Extras
Additional useful features, not required.
Promises
Check it:
104 lines,
2684 chars
async function promiseUsage() {
const machine = withApi(
createPromiseMachine(async (x: number) => {
console.log("sleeping for", x);
await delay(x);
return `slept for ${x}ms`;
}),
);
const it = machine.getState().match({
Rejected: () => ({ kablamo: false }),
_: () => ({ kablamo: true }),
});
console.log(it);
const checkState = () =>
console.log(
machine.getState().match({
Resolved: (res) => `DONE: ${res}`,
Rejected: (err) => `Error! ${err}`,
_: () => `Not yet: ${machine.getState().key}`,
}),
);
checkState();
machine.api.execute(1000);
checkState();
// const pendingPromise = machine.promise;
// if (pendingPromise !== machine.promise) {
// console.log("Got valid result BUT promise changed!");
// }
machine.api.execute(1);
// const donePromise = machine.done;
const beforeDoneState = machine.getState();
checkState();
console.log("rejecting");
machine.api.reject(new Error("error"));
checkState();
// await donePromise;
const doneState = machine.getState();
// if (machine.done === donePromise && doneState === beforeDoneState) {
// console.log("changed", doneState.data);
// } else {
// console.log(
// `state changed from ${beforeDoneState.key} to ${doneState.key}`,
// );
// }
// machine.send2('execute', 1000)
// machine.send<"execute">("execute", 1);
machine.send("execute", 1000);
machine.send("reject", new Error("error"));
// machine.debugParams<any, 'Pending'>('resolve')('ok')
// machine.debugParams<'execute', 'Idle'>('execute')(123)
await delay(2);
checkState();
await delay(1);
checkState();
await delay(1);
checkState();
const fetchMachine = withApi(
createPromiseMachine((id: number) =>
fetch(`.data/${id}`).then((response) => response.json()),
),
);
const logState = () =>
fetchMachine.getState().match({
Resolved: (data) => console.log(data),
Rejected: (error) => console.log(error.message),
_: () => console.log("not yet"),
});
logState(); // not yet
fetchMachine.api.execute(123);
logState(); // not yet
logState(); // result or error
const change = machine.getChange();
if (
matchChange(change, {
to: "Pending",
from: "Idle",
type: "execute",
})
) {
change.from.key = "Idle";
change.type = "execute";
change.to.key = "Pending";
}
}
Type Guards
Type Safety Everywhere
Cool autocomplete and type safety scenarios:
import { PromiseMachine, PromiseMachineEvent, createPromiseMachine } from 'matchina'
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
const slowAddingMachine = createPromiseMachine(async (a: number, b: number, t: number = 1000) => {
await delay(t)
return a + b
})
slowAddingMachine.send('execute', 1, 2)
const lastChange = slowAddingMachine.getChange()
if (lastChange.to.key === 'Resolved') {
const sum = lastChange.to.data
}
lastChange.to.match({
Pending: ([a, b, t]) => {
console.log(a, b, a + b)
},
Rejected: (err) => {
console.log(err.name, err.message)
}
}, false)
lastChange.from.as('Rejected').key === 'Rejected'
if (lastChange.to.is('Pending')) {
const [a, b, t] = lastChange.to.data
}
lastChange.match({
execute: (a, b, t=1000) => {
console.log(a, b, t)
},
reject: (err) => {
console.log(err.name, err.message)
}
}, false)
On states
- Factory autocompletes
states
types - State Creator autocompletes parameters
- State autocompletes
key
- State autocompletes
data
match
autocompletes state typesmatch
handler autocompletes state properties
On state machines
- transition config autocompletes state keys
- transition config autocompletes transition type and from properties
- autocomplete available states
- api autocompletes state types
- api autocompletes transition params
On state machine events
event
autocompletes eventtype
,to
,from
, andparams
event.match
autocompletes event typeevent.match
handler autocompletes event params- type assert matching
to.key
,from.key
and eventtype
On hooks and typeguards
Code Snippets
114 lines,
2385 chars
type type MyStateData = {
Idle: undefined;
Foo: (foo: string) => {
foo: string;
};
Bar: (bar: number) => {
bar: number;
};
}MyStateData = {
type Idle: undefinedIdle: undefined;
type Foo: (foo: string) => {
foo: string;
}Foo: (foo: stringfoo: string) => {
foo: stringfoo: string;
};
type Bar: (bar: number) => {
bar: number;
}Bar: (bar: numberbar: number) => {
bar: numberbar: number;
};
}
type type MyStates = {
Idle: () => StateMatchbox<"Idle", MyStateData>;
Foo: (foo: string) => StateMatchbox<"Foo", MyStateData>;
Bar: (bar: number) => StateMatchbox<"Bar", MyStateData>;
}MyStates = type States<Specs extends SpecRecord> = { [T in string & keyof Specs]: CreateState<Specs, T>; }States<type MyStateData = {
Idle: undefined;
Foo: (foo: string) => {
foo: string;
};
Bar: (bar: number) => {
bar: number;
};
}MyStateData>
const const states: MyStatesstates: type MyStates = {
Idle: () => StateMatchbox<"Idle", MyStateData>;
Foo: (foo: string) => StateMatchbox<"Foo", MyStateData>;
Bar: (bar: number) => StateMatchbox<"Bar", MyStateData>;
}MyStates = defineStates<MyStateData>(config: MyStateData): States<MyStateData>defineStates<type MyStateData = {
Idle: undefined;
Foo: (foo: string) => {
foo: string;
};
Bar: (bar: number) => {
bar: number;
};
}MyStateData>( {
type Idle: undefinedIdle: var undefinedundefined,
type Foo: (foo: string) => {
foo: string;
}Foo: (foo: stringfoo: string) => ({ foo: stringfoo }),
type Bar: (bar: number) => {
bar: number;
}Bar: (bar: numberbar: number) => ({ bar: numberbar })
})
type type Transitions = object & {
Idle?: {
[x: string]: FactoryMachineTransition<MyStates, "Idle", string> | undefined;
} | undefined;
Foo?: {
[x: string]: FactoryMachineTransition<MyStates, "Foo", string> | undefined;
} | undefined;
Bar?: {
...;
} | undefined;
} & {
...;
}Transitions =
type FactoryMachineTransitions<SF extends AnyStatesFactory> = object & { [FromStateKey in string & keyof SF]?: {
[x: string]: FactoryMachineTransition<SF, FromStateKey, string> | undefined;
} | undefined; }FactoryMachineTransitions<type MyStates = {
Idle: () => StateMatchbox<"Idle", MyStateData>;
Foo: (foo: string) => StateMatchbox<"Foo", MyStateData>;
Bar: (bar: number) => StateMatchbox<"Bar", MyStateData>;
}MyStates> &
{
readonly type Idle: {
readonly start: "Foo";
}Idle: {
readonly start: "Foo"start: "Foo";
};
readonly type Foo: {
readonly toggle: "Bar";
readonly foo: (foo?: string) => MyStates["Foo"];
}Foo: {
readonly toggle: "Bar"toggle: "Bar";
readonly foo: (foo?: string) => MyStates["Foo"]foo: (foo: string | undefinedfoo?: string) => type MyStates = {
Idle: () => StateMatchbox<"Idle", MyStateData>;
Foo: (foo: string) => StateMatchbox<"Foo", MyStateData>;
Bar: (bar: number) => StateMatchbox<"Bar", MyStateData>;
}MyStates['Foo']
};
readonly type Bar: {
readonly toggle: "Foo";
readonly increment: (bar: number) => (ev: any) => MyStates["Bar"];
}Bar: {
readonly toggle: "Foo"toggle: "Foo";
readonly increment: (bar: number) => (ev: any) => MyStates["Bar"]increment: (bar: numberbar: number) => (ev: anyev: any) => type MyStates = {
Idle: () => StateMatchbox<"Idle", MyStateData>;
Foo: (foo: string) => StateMatchbox<"Foo", MyStateData>;
Bar: (bar: number) => StateMatchbox<"Bar", MyStateData>;
}MyStates['Bar'];
}
}
const const transitions: Transitionstransitions: type Transitions = object & {
Idle?: {
[x: string]: FactoryMachineTransition<MyStates, "Idle", string> | undefined;
} | undefined;
Foo?: {
[x: string]: FactoryMachineTransition<MyStates, "Foo", string> | undefined;
} | undefined;
Bar?: {
...;
} | undefined;
} & {
...;
}Transitions = {
type Idle: {
[x: string]: FactoryMachineTransition<MyStates, "Idle", string> | undefined;
} & {
readonly start: "Foo";
}Idle: {
start: "Foo"start: 'Foo'
},
type Foo: {
[x: string]: FactoryMachineTransition<MyStates, "Foo", string> | undefined;
} & {
readonly toggle: "Bar";
readonly foo: (foo?: string) => MyStates["Foo"];
}Foo: {
toggle: "Bar"toggle: 'Bar',
foo: (foo?: string) => MyStates["Foo"]foo: (foo: string | undefinedfoo = 'foo') => const states: MyStatesstates.type Foo: (foo: string) => StateMatchbox<"Foo", MyStateData>Foo(`my-${foo: stringfoo}`) as any,
},
type Bar: {
[x: string]: FactoryMachineTransition<MyStates, "Bar", string> | undefined;
} & {
readonly toggle: "Foo";
readonly increment: (bar: number) => (ev: any) => MyStates["Bar"];
}Bar: {
toggle: "Foo"toggle: 'Foo',
increment: (bar: number) => (ev: any) => MyStates["Bar"]increment: (bar: numberbar: number) => (ev: anyev: any) =>
const states: MyStatesstates.type Bar: (bar: number) => StateMatchbox<"Bar", MyStateData>Barev: any(ev.anyfrom.anydata.anybar + bar: numberbar) as any
}
}
const const machine: FactoryMachine<{
states: MyStates;
transitions: Transitions;
}>machine = createFactoryMachine<MyStates, Transitions, {
states: MyStates;
transitions: Transitions;
}, FactoryMachineEvent<{
states: MyStates;
transitions: Transitions;
}>>(states: MyStates, transitions: Transitions, init: "Idle" | ... 2 more ... | StateMatchbox<...>): FactoryMachine<{
...;
}>createFactoryMachine<type MyStates = {
Idle: () => StateMatchbox<"Idle", MyStateData>;
Foo: (foo: string) => StateMatchbox<"Foo", MyStateData>;
Bar: (bar: number) => StateMatchbox<"Bar", MyStateData>;
}MyStates, type Transitions = object & {
Idle?: {
[x: string]: FactoryMachineTransition<MyStates, "Idle", string> | undefined;
} | undefined;
Foo?: {
[x: string]: FactoryMachineTransition<MyStates, "Foo", string> | undefined;
} | undefined;
Bar?: {
...;
} | undefined;
} & {
...;
}Transitions>(
const states: MyStatesstates,
const transitions: Transitionstransitions,
'Idle'
)
const machine: FactoryMachine<{
states: MyStates;
transitions: Transitions;
}>machine.FactoryMachine<{ states: MyStates; transitions: Transitions; }>.states: MyStatesstates.B- Bar
ar(1).data: {
bar: number;
}data.bar: numberbar === 1
// const change = machine.getChange()
// const { type, from: {key: fromKey}, to: { key: toKey } } = change
const const slowAddingMachine: PromiseMachine<(a: number, b: number, t?: number) => Promise<number>>slowAddingMachine = createPromiseMachine<(a: number, b: number, t?: number) => Promise<number>>(makePromise?: ((a: number, b: number, t?: number) => Promise<number>) | undefined): PromiseMachine<(a: number, b: number, t?: number) => Promise<number>>createPromiseMachine(async (a: numbera: number, b: numberb: number, t: numbert: number = 1000) => {
await function delay(ms: number): Promise<unknown>delay(t: numbert)
return a: numbera + b: numberb
})
const slowAddingMachine: PromiseMachine<(a: number, b: number, t?: number) => Promise<number>>slowAddingMachine.StateMachine<FactoryMachineEvent<{ states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>; transitions: PromiseTransitions; }>>.send: (type: "execute" | "resolve" | "reject", ...params: (any[] & [a: number, b: number, t?: number | undefined]) | (any[] & [data: number]) | (any[] & [error: Error])) => voidsend('execute', 1, 2)
const const api: {
readonly execute: (...args: any[] & [a: number, b: number, t?: number | undefined]) => void;
readonly resolve: (...args: any[] & [data: number]) => void;
readonly reject: (...args: any[] & [error: Error]) => void;
} & objectapi = createApi<PromiseMachine<(a: number, b: number, t?: number) => Promise<number>>, keyof PromiseTransitions>(machine: PromiseMachine<(a: number, b: number, t?: number) => Promise<...>>, filterStateKey?: keyof PromiseTransitions | undefined): FactoryMachineApi<PromiseMachine<...>>createApiconst slowAddingMachine: PromiseMachine<(a: number, b: number, t?: number) => Promise<number>>(slowAddingMachine)
const api: {
readonly execute: (...args: any[] & [a: number, b: number, t?: number | undefined]) => void;
readonly resolve: (...args: any[] & [data: number]) => void;
readonly reject: (...args: any[] & [error: Error]) => void;
} & objectapi.r- reject
- resolve
eject(new var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)Error('test'))
const const lastChange: FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>lastChange = const slowAddingMachine: PromiseMachine<(a: number, b: number, t?: number) => Promise<number>>slowAddingMachine.StateMachine<FactoryMachineEvent<{ states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>; transitions: PromiseTransitions; }>>.getChange(): FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>getChange()
const lastChange: FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>lastChange.to: ({
key: "Pending";
data: [a: number, b: number, t?: number | undefined];
} & MemberExtensionsFromDataSpecs<PromiseStateCreators<(a: number, b: number, t?: number) => Promise<number>, Error>, "key">) | ({
...;
} & MemberExtensionsFromDataSpecs<...>) | ({
...;
} & MemberExtensionsFromDataSpecs<...>)to.MemberExtensionsFromDataSpecs<DataSpecs, TagProp extends string>.match: MatchMemberData
<void, false>(cases: Partial<Cases<MemberData<PromiseStateCreators<(a: number, b: number, t?: number) => Promise<number>, Error>>, void> & {
...;
}>, exhaustive?: false | undefined) => voidmatch({
Pending?: ((value: [a: number, b: number, t?: number | undefined]) => void) | undefinedPending: ([a: numbera, b: numberb, t: number | undefinedt]) => {
},
Rejected?: ((value: Error) => void) | undefinedRejected: (err: Errorerr) => {
var console: Consoleconsole.Console.log(...data: any[]): void[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(err: Errorerr.Error.name: stringname, err: Errorerr.Error.message: stringmessage)
}
}, false)
const { const from: ({
key: "Pending";
data: [a: number, b: number, t?: number | undefined];
} & MemberExtensionsFromDataSpecs<PromiseStateCreators<(a: number, b: number, t?: number) => Promise<number>, Error>, "key">) | ({
...;
} & MemberExtensionsFromDataSpecs<...>)from } = const lastChange: FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>lastChange
if (matchChange<FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>, "execute" | ... 2 more ... | undefined, "Idle" | ... 1 more ... | undefined, "Rejected">(changeEvent: FactoryMachineEvent<...>, ...rest: [type?: ...] | [filter: ...]): changeEvent is StateMachineEvent<...> & ... 2 more ... & {
...;
}matchChange(
const lastChange: FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>lastChange,
{
to?: "Rejected" | undefinedto: 'Rejected'
})) {
const lastChange: FactoryMachineTransitionEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}, "Pending", "reject", "Rejected">lastChange.type: "reject"type = 'reject'
}
const lastChange: FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>lastChange.match: MatchInvocation
<void, false>(cases: Partial<InvocationCases<FlatMemberUnion<StateEventTransitionSenders<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>>, void> & {
...;
}>, exhaustive?: false | undefined) => voidmatch({
execute?: ((...params: any[] & [a: number, b: number, t?: number | undefined]) => void) | undefinedexecute: (a: numbera, b: numberb, t: number | undefinedt=1000) => {
var console: Consoleconsole.Console.log(...data: any[]): void[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(a: numbera, b: numberb, t: numbert)
},
reject?: ((...params: any[] & [error: Error]) => void) | undefinedreject: (err: Errorerr) => {
var console: Consoleconsole.Console.log(...data: any[]): void[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(err: Errorerr.Error.name: stringname, err: Errorerr.Error.message: stringmessage)
}
}, false)
if const lastChange: FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>(lastChange.to: ({
key: "Pending";
data: [a: number, b: number, t?: number | undefined];
} & MemberExtensionsFromDataSpecs<PromiseStateCreators<(a: number, b: number, t?: number) => Promise<number>, Error>, "key">) | ({
...;
} & MemberExtensionsFromDataSpecs<...>) | ({
...;
} & MemberExtensionsFromDataSpecs<...>)to.MemberExtensionsFromDataSpecs<DataSpecs, TagProp extends string>.is: <"Pending">(key: "Pending") => this is MemberFromDataSpecs<T, DataSpecs, TagProp>is('Pending')) {
const [const a: numbera, const b: numberb, const t: number | undefinedt] = const lastChange: FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>lastChange.to: {
key: "Pending";
data: [a: number, b: number, t?: number | undefined];
} & MemberExtensionsFromDataSpecs<PromiseStateCreators<(a: number, b: number, t?: number) => Promise<number>, Error>, "key">to.data: [a: number, b: number, t?: number | undefined]data
}
const slowAddingMachine: PromiseMachine<(a: number, b: number, t?: number) => Promise<number>>slowAddingMachine.FactoryMachine<{ states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>; transitions: PromiseTransitions; }>.states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>states.type Idle: () => StateMatchbox<"Idle", PromiseStateCreators<(a: number, b: number, t?: number) => Promise<number>, Error>>Idle().key: "Idle"key
const { const type: "execute" | "resolve" | "reject"type, from: ({
key: "Pending";
data: [a: number, b: number, t?: number | undefined];
} & MemberExtensionsFromDataSpecs<PromiseStateCreators<(a: number, b: number, t?: number) => Promise<number>, Error>, "key">) | ({
...;
} & MemberExtensionsFromDataSpecs<...>)from: {key: "Idle" | "Pending"key: const fromKey: "Idle" | "Pending"fromKey}, to: ({
key: "Pending";
data: [a: number, b: number, t?: number | undefined];
} & MemberExtensionsFromDataSpecs<PromiseStateCreators<(a: number, b: number, t?: number) => Promise<number>, Error>, "key">) | ({
...;
} & MemberExtensionsFromDataSpecs<...>) | ({
...;
} & MemberExtensionsFromDataSpecs<...>)to: { key: "Pending" | "Rejected" | "Resolved"key: const toKey: "Pending" | "Rejected" | "Resolved"toKey } } = const lastChange: FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>lastChange