-
Notifications
You must be signed in to change notification settings - Fork 1
/
interpreter.ts
73 lines (64 loc) · 2.06 KB
/
interpreter.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
import {
State,
DefaultContext,
StateSchema,
EventObject,
Typestate,
StateMachine,
Event,
interpret as xstateInterpret,
} from 'xstate';
import type { Storage } from './storage';
function patchStateDefinitionTagsBug(stateDefinition: any) {
if (Array.isArray(stateDefinition.tags)) {
stateDefinition.tags = new Set(stateDefinition.tags);
}
}
export function createInterpreter<
TContext = DefaultContext,
TStateSchema extends StateSchema = any,
TEvent extends EventObject = EventObject,
TTypestate extends Typestate<TContext> = {
value: any;
context: TContext;
}
>(
key: string,
machine: StateMachine<TContext, TStateSchema, TEvent, TTypestate>,
storage: Storage
) {
const settleMachine = async (event: Event<TEvent>) => {
const service = xstateInterpret(machine);
const stateDefinition = (await storage.get(key)) ?? machine.initialState;
// Temporary hack while waiting on xstate fix
patchStateDefinitionTagsBug(stateDefinition);
const previousState = machine.resolveState(State.create(stateDefinition));
let hasTransitioned = false;
const nextState = await new Promise<
State<TContext, TEvent, TStateSchema, TTypestate>
>((resolve, reject) => {
service
.onTransition((state) => {
// When we resume a state machine the initial state with always have either a resolve or a reject tag.
// This ensures that we don't immediately exit when entering into the inital state.
if (!hasTransitioned) {
hasTransitioned = true;
return;
}
// TODO: Not sure about stopping then resolving/rejecting, maybe we should set some mutbale state just stop?
if (state.hasTag('resolve')) {
service.stop();
return resolve(state);
} else if (state.hasTag('reject')) {
service.stop();
return reject(state);
}
})
.start(previousState)
.send(event);
});
await storage.set(key, nextState);
return nextState;
};
return { settleMachine };
}