-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcreate.ts
108 lines (90 loc) · 2.87 KB
/
create.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
type Obj = Record<string, unknown>;
type TransitionArgs<C extends Obj> = {
context: C;
};
type DFA<S extends Obj> = {
type: "D";
initial: keyof S;
states: {
[K in keyof S]: {
[ON in keyof S[K]]: {
[E in keyof S[K][ON]]: () => { target: keyof S } | keyof S;
};
};
};
};
type NDFA<S extends Obj, C extends Obj> = {
type: "ND";
context: C;
initial: keyof S;
states: {
[K in keyof S]: {
[ON in keyof S[K]]: {
[E in keyof S[K][ON]]: (args: TransitionArgs<C>) => { target: keyof S } | keyof S;
};
};
};
};
type MachineDefinition<S extends Obj, C extends Obj> = DFA<S> | NDFA<S, C>;
type GenericMachine<States = any, Args extends Obj = any> = {
state: () => States;
send: (args: Args) => void;
};
type Machine<S extends Obj> = GenericMachine<
keyof S,
{ event: ExtractEvents<MachineDefinition<S, any>> }
>;
type ExtractEvents<M extends MachineDefinition<any, any>> = {
[K in keyof M["states"]]: keyof M["states"][K]["on"];
}[keyof M["states"]];
const isString = (v: unknown): v is string => typeof v === "string";
function createDFA<S extends Obj, C extends Obj>(machine: DFA<S>): Machine<S> {
let state = machine.initial;
function send(action: { event: ExtractEvents<MachineDefinition<S, C>> }) {
// @ts-ignore correctness ensure in function signature
const transitionFn = machine["states"][state]["on"][action.event as any];
if (transitionFn !== undefined) {
const newState = transitionFn();
if (isString(newState)) {
state = newState;
} else {
state = newState.target;
}
}
}
function stateGetter() {
return state;
}
return {
send,
state: stateGetter,
};
}
function createNDFA<S extends Obj, C extends Obj>(machine: NDFA<S, C>): Machine<S> {
let state = machine.initial;
let context = {...machine.context};
function send(action: { event: ExtractEvents<MachineDefinition<S, C>> }) {
// @ts-ignore correctness ensure in function signature
const transitionFn = machine["states"][state]["on"][action.event as any];
if (transitionFn !== undefined) {
const newState = transitionFn({ context });
if (isString(newState)) {
state = newState;
} else {
state = newState.target;
}
}
}
function stateGetter() {
return state;
}
return {
send,
state: stateGetter,
};
}
function createMachine<S extends Obj, C extends Obj>(machine: MachineDefinition<S, C>): Machine<S> {
return machine.type === "D" ? createDFA(machine) : createNDFA(machine);
}
export type { GenericMachine, Machine, MachineDefinition };
export { createMachine };