@statewalker/fsm 0.30.0 → 0.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/utils/newFsmStateHandler.ts +0 -199
package/package.json
CHANGED
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
import { FsmState } from "../FsmState";
|
|
2
|
-
import { isStateTransitionEnabled } from "./transitions";
|
|
3
|
-
|
|
4
|
-
export function newModuleLoader<T>(baseUrl: string) {
|
|
5
|
-
const cache: Record<string, Promise<T>> = {};
|
|
6
|
-
return async (path: string) => {
|
|
7
|
-
return (cache[path] =
|
|
8
|
-
cache[path] ||
|
|
9
|
-
(async () => {
|
|
10
|
-
const suffixes = ["/", "", ".ts", "/index.ts"];
|
|
11
|
-
for (const suffix of suffixes) {
|
|
12
|
-
const url = new URL(path + suffix, baseUrl).pathname;
|
|
13
|
-
try {
|
|
14
|
-
return (await import(url)) as T;
|
|
15
|
-
} catch (error) {
|
|
16
|
-
continue;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
})());
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export async function newHandlerLoader(
|
|
24
|
-
baseUrl: string,
|
|
25
|
-
loader: (
|
|
26
|
-
path: string,
|
|
27
|
-
) => Promise<undefined | FsmStateModule> = newModuleLoader(baseUrl),
|
|
28
|
-
): Promise<(state: FsmState) => Promise<FsmStateModule[]>> {
|
|
29
|
-
return async (state: FsmState) => {
|
|
30
|
-
const stack: string[] = [];
|
|
31
|
-
for (let s: FsmState | undefined = state; s; s = s.parent) {
|
|
32
|
-
stack.unshift(s.key);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const handlers: FsmStateModule[] = [];
|
|
36
|
-
let topName: undefined | string;
|
|
37
|
-
while (stack.length > 0) {
|
|
38
|
-
const lastSegment = stack.pop();
|
|
39
|
-
if (!topName) {
|
|
40
|
-
topName = lastSegment;
|
|
41
|
-
if (topName === undefined) break;
|
|
42
|
-
}
|
|
43
|
-
const path = [...stack, topName].join("/");
|
|
44
|
-
const handler = await loader(path);
|
|
45
|
-
handler && handlers.push(handler);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return handlers;
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/*
|
|
53
|
-
* StateContext:
|
|
54
|
-
- Each context is a function or a class
|
|
55
|
-
- It recieves the stack of state objects
|
|
56
|
-
- The returned value is used to pass to the handler function
|
|
57
|
-
* Trigger function
|
|
58
|
-
- recieves the context instance
|
|
59
|
-
- notifies about changes using the given callback
|
|
60
|
-
* Handler
|
|
61
|
-
- recieves the context
|
|
62
|
-
- starts activities on call
|
|
63
|
-
- optionally returns a cleanup function
|
|
64
|
-
- could return a promise with optional cleanup function; the state is not
|
|
65
|
-
activated until all handlers return the control
|
|
66
|
-
*/
|
|
67
|
-
export type FsmStateModule<
|
|
68
|
-
FsmStateContext = Record<string, unknown>,
|
|
69
|
-
FsmStateStore = Record<string, unknown>,
|
|
70
|
-
> = {
|
|
71
|
-
context?:
|
|
72
|
-
| ((...stack: FsmStateStore[]) => FsmStateContext)
|
|
73
|
-
| (new (
|
|
74
|
-
...stack: FsmStateStore[]
|
|
75
|
-
) => FsmStateContext);
|
|
76
|
-
// trigger?: (context: FsmStateContext) => AsyncGenerator<string>;
|
|
77
|
-
trigger?: (context: FsmStateContext) => AsyncIterator<string>;
|
|
78
|
-
handler?: (
|
|
79
|
-
context: FsmStateContext,
|
|
80
|
-
) => void | (() => void) | Promise<void | (() => void)>;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
export function newModuleContext<
|
|
84
|
-
C = Record<string, unknown>,
|
|
85
|
-
S = Record<string, unknown>,
|
|
86
|
-
>(module: FsmStateModule<C, S>, ...stack: S[]): C {
|
|
87
|
-
if (isClass<S[], C>(module.context)) {
|
|
88
|
-
return new module.context(...stack) as unknown as C;
|
|
89
|
-
} else if (isFunction<S[], C>(module.context)) {
|
|
90
|
-
return module.context(...stack);
|
|
91
|
-
} else {
|
|
92
|
-
return stack[0] as unknown as C;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
function isFunction<T extends Array<unknown>, R = unknown>(
|
|
96
|
-
value: unknown,
|
|
97
|
-
): value is (...args: T) => R {
|
|
98
|
-
return typeof value === "function";
|
|
99
|
-
}
|
|
100
|
-
function isClass<T extends Array<unknown>, R = unknown>(
|
|
101
|
-
value: unknown,
|
|
102
|
-
): value is new (
|
|
103
|
-
...args: T
|
|
104
|
-
) => R {
|
|
105
|
-
if (!isFunction(value)) return false;
|
|
106
|
-
return value?.prototype?.constructor?.toString().match(/^class/);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export function newStateHandlers<
|
|
110
|
-
FsmStateContext = Record<string, unknown>,
|
|
111
|
-
FsmStateStore = Record<string, unknown>,
|
|
112
|
-
>(
|
|
113
|
-
loader: (
|
|
114
|
-
state: FsmState,
|
|
115
|
-
) => Promise<FsmStateModule<FsmStateContext, FsmStateStore>[]>,
|
|
116
|
-
newContext: (state: FsmState, ...stack: FsmStateStore[]) => FsmStateStore = (
|
|
117
|
-
state,
|
|
118
|
-
) =>
|
|
119
|
-
({
|
|
120
|
-
state: state.key,
|
|
121
|
-
event: state.process.event,
|
|
122
|
-
}) as unknown as FsmStateStore,
|
|
123
|
-
contextCache: Map<unknown, FsmStateContext> = new Map<
|
|
124
|
-
unknown,
|
|
125
|
-
FsmStateContext
|
|
126
|
-
>(),
|
|
127
|
-
) {
|
|
128
|
-
const triggers = new Map<unknown, () => void>();
|
|
129
|
-
return (state: FsmState) => {
|
|
130
|
-
const stack: FsmStateStore[] = [];
|
|
131
|
-
for (let s: FsmState | undefined = state.parent; !!s; s = s.parent) {
|
|
132
|
-
const store = s.getData<FsmStateStore>("context");
|
|
133
|
-
store && stack.push(store);
|
|
134
|
-
}
|
|
135
|
-
const store = newContext(state, ...stack);
|
|
136
|
-
stack.unshift(store);
|
|
137
|
-
state.setData("context", store);
|
|
138
|
-
|
|
139
|
-
let registrations: (void | (() => void) | Promise<void | (() => void)>)[] =
|
|
140
|
-
[];
|
|
141
|
-
state.onExit(async () => {
|
|
142
|
-
for (const r of registrations) {
|
|
143
|
-
const registration = await r;
|
|
144
|
-
registration?.();
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
state.onEnter(async () => {
|
|
148
|
-
const modules = await loader(state);
|
|
149
|
-
if (!modules?.length) return;
|
|
150
|
-
for (const module of modules) {
|
|
151
|
-
const contextKey: unknown = module.context;
|
|
152
|
-
// Create state context or get it from the local cache
|
|
153
|
-
let stateContext = contextCache.get(contextKey);
|
|
154
|
-
if (!stateContext) {
|
|
155
|
-
stateContext = newModuleContext(module, ...stack);
|
|
156
|
-
contextCache.set(contextKey, stateContext);
|
|
157
|
-
registrations.push(() => {
|
|
158
|
-
contextCache.delete(contextKey);
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Initialize triggers
|
|
163
|
-
if (module.trigger && !triggers.has(module.trigger)) {
|
|
164
|
-
const process = state.process;
|
|
165
|
-
const iterator = module.trigger(stateContext);
|
|
166
|
-
let finalize: undefined | (() => void);
|
|
167
|
-
const end = new Promise<{ done: boolean; value?: string }>(
|
|
168
|
-
(r) => (finalize = () => r({ done: true })),
|
|
169
|
-
);
|
|
170
|
-
registrations.push(() => triggers.delete(module.trigger));
|
|
171
|
-
registrations.push(finalize);
|
|
172
|
-
registrations.push(() => iterator?.return?.(void 0));
|
|
173
|
-
(async () => {
|
|
174
|
-
try {
|
|
175
|
-
while (true) {
|
|
176
|
-
const { done, value: event } = await Promise.race([
|
|
177
|
-
iterator.next(),
|
|
178
|
-
end,
|
|
179
|
-
]);
|
|
180
|
-
if (done) break;
|
|
181
|
-
if (event && isStateTransitionEnabled(process, event)) {
|
|
182
|
-
process.dispatch(event);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
} finally {
|
|
186
|
-
finalize?.();
|
|
187
|
-
}
|
|
188
|
-
})();
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Run actions associated with this event
|
|
192
|
-
if (module.handler) {
|
|
193
|
-
const cleanup = module.handler(stateContext);
|
|
194
|
-
registrations.push(cleanup);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
};
|
|
199
|
-
}
|