@statewalker/fsm 0.37.0 → 0.38.0

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.
@@ -1,443 +0,0 @@
1
- //#region src/core/fsm-base-class.ts
2
- var FsmBaseClass = class {
3
- constructor() {
4
- this.handlers = {};
5
- }
6
- _addHandler(type, handler, direct = true) {
7
- let list = this.handlers[type];
8
- if (!list) list = this.handlers[type] = [];
9
- const h = handler;
10
- direct ? list.push(h) : list.unshift(h);
11
- return () => this._removeHandler(type, h);
12
- }
13
- _removeHandler(type, handler) {
14
- let list = this.handlers[type];
15
- if (!list) return;
16
- list = list.filter((h) => h !== handler);
17
- if (list.length > 0) this.handlers[type] = list;
18
- else delete this.handlers[type];
19
- }
20
- async _runHandler(type, ...args) {
21
- const list = this.handlers[type] || [];
22
- for (const handler of list) try {
23
- await handler(...args);
24
- } catch (error) {
25
- await this._handleError(error);
26
- }
27
- }
28
- async _handleError(error) {
29
- console.error(error);
30
- }
31
- };
32
- function bindMethods(obj, ...methods) {
33
- const o = obj;
34
- for (const methodName of methods) {
35
- const method = o[methodName];
36
- if (typeof method !== "function") continue;
37
- o[methodName] = method.bind(o);
38
- }
39
- return obj;
40
- }
41
- //#endregion
42
- //#region src/core/fsm-state.ts
43
- var FsmState = class extends FsmBaseClass {
44
- constructor(process, parent, key, descriptor) {
45
- super();
46
- this.process = process;
47
- this.key = key;
48
- this.parent = parent;
49
- this.descriptor = descriptor;
50
- bindMethods(this, "onEnter", "onExit", "dump", "restore", "onStateError");
51
- }
52
- onEnter(handler) {
53
- return this._addHandler("onEnter", handler, true);
54
- }
55
- onExit(handler) {
56
- return this._addHandler("onExit", handler, false);
57
- }
58
- onStateError(handler) {
59
- return this._addHandler("onStateError", handler);
60
- }
61
- dump(handler) {
62
- return this._addHandler("dump", handler, true);
63
- }
64
- restore(handler) {
65
- return this._addHandler("restore", handler, true);
66
- }
67
- async _handleError(error) {
68
- await this._runHandler("onStateError", this, error);
69
- await this.process._handleStateError(this, error);
70
- }
71
- };
72
- //#endregion
73
- //#region src/core/fsm-state-config.ts
74
- const STATE_ANY = "*";
75
- const STATE_INITIAL = "";
76
- const STATE_FINAL = "";
77
- const EVENT_ANY = "*";
78
- const EVENT_EMPTY = "";
79
- //#endregion
80
- //#region src/core/fsm-state-descriptor.ts
81
- var FsmStateDescriptor = class FsmStateDescriptor {
82
- constructor() {
83
- this.transitions = {};
84
- this.states = {};
85
- }
86
- static build(config) {
87
- const descriptor = new FsmStateDescriptor();
88
- for (const [from, event, to] of config.transitions || []) {
89
- let index = descriptor.transitions[from];
90
- if (!index) index = descriptor.transitions[from] = {};
91
- index[event] = to;
92
- }
93
- if (config.states) for (const substateConfig of config.states) descriptor.states[substateConfig.key] = FsmStateDescriptor.build(substateConfig);
94
- return descriptor;
95
- }
96
- getTargetStateKey(stateKey, eventKey) {
97
- const pairs = [
98
- [stateKey, eventKey],
99
- ["*", eventKey],
100
- [stateKey, "*"],
101
- ["*", "*"]
102
- ];
103
- let targetKey;
104
- for (let i = 0, len = pairs.length; targetKey === void 0 && i < len; i++) {
105
- const [stateKey, eventKey] = pairs[i];
106
- const stateTransitions = this.transitions[stateKey];
107
- if (!stateTransitions) continue;
108
- targetKey = stateTransitions[eventKey];
109
- }
110
- return targetKey !== void 0 ? targetKey : "";
111
- }
112
- };
113
- //#endregion
114
- //#region src/core/fsm-process.ts
115
- const STATUS_NONE = 0;
116
- const STATUS_FIRST = 1;
117
- const STATUS_NEXT = 2;
118
- const STATUS_LEAF = 4;
119
- const STATUS_LAST = 8;
120
- const STATUS_FINISHED = 16;
121
- const STATUS_ENTER = 3;
122
- const STATUS_EXIT = 12;
123
- var FsmProcess = class extends FsmBaseClass {
124
- constructor(config) {
125
- super();
126
- this.running = false;
127
- this.mask = 4;
128
- this.status = 0;
129
- this.rootDescriptor = FsmStateDescriptor.build(config);
130
- this.config = config;
131
- bindMethods(this, "dispatch", "dump", "restore");
132
- }
133
- async shutdown(event) {
134
- while (this.state) {
135
- this.event = event;
136
- this.status = 16;
137
- await this.state?._runHandler("onExit", this.state);
138
- this.state = this.state.parent;
139
- }
140
- }
141
- async dispatch(event) {
142
- this.nextEvent = event;
143
- if (!this.running && !(this.status & 16)) {
144
- this.running = true;
145
- try {
146
- this.event = this.nextEvent;
147
- this.nextEvent = void 0;
148
- while (true) {
149
- if (this.status & 12) await this.state?._runHandler("onExit", this.state);
150
- if (!await this._update()) break;
151
- if (this.status & 3) await this.state?._runHandler("onEnter", this.state);
152
- if (this.status & this.mask) break;
153
- }
154
- } finally {
155
- this.running = false;
156
- }
157
- const nextEvent = this.nextEvent;
158
- if (nextEvent !== void 0) return Promise.resolve().then(() => this.dispatch(nextEvent));
159
- }
160
- return !(this.status & 16);
161
- }
162
- async dump(...args) {
163
- const dumpState = async (state) => {
164
- const stateDump = {
165
- key: state.key,
166
- data: {}
167
- };
168
- await state._runHandler("dump", state, stateDump.data, ...args);
169
- return stateDump;
170
- };
171
- const dumpStates = async (state, stack = []) => {
172
- if (!state) return stack;
173
- state.parent && await dumpStates(state.parent, stack);
174
- stack.push(await dumpState(state));
175
- return stack;
176
- };
177
- return {
178
- status: this.status,
179
- event: this.event,
180
- stack: await dumpStates(this.state)
181
- };
182
- }
183
- async restore(dump, ...args) {
184
- this.status = dump.status || 0;
185
- this.event = dump.event;
186
- this.state = void 0;
187
- for (let i = 0; i < dump.stack.length; i++) {
188
- const stateDump = dump.stack[i];
189
- this.state = this.state ? await this._newSubstate(this.state, stateDump.key) : await this._newState(void 0, stateDump.key, this.rootDescriptor);
190
- await this.state._runHandler("restore", this.state, stateDump.data, ...args);
191
- }
192
- return this;
193
- }
194
- onStateCreate(handler) {
195
- return this._addHandler("onStateCreate", handler, true);
196
- }
197
- onStateError(handler) {
198
- return this._addHandler("onStateError", handler);
199
- }
200
- async _handleStateError(state, error) {
201
- await this._runHandler("onStateError", state, error);
202
- this._handleError(error);
203
- }
204
- async _newState(parent, key, descriptor) {
205
- const state = new FsmState(this, parent, key, descriptor);
206
- await this._runHandler("onStateCreate", state);
207
- return state;
208
- }
209
- async _getSubstate(parent, prevStateKey) {
210
- if (!parent) return;
211
- const toState = parent.descriptor?.getTargetStateKey(prevStateKey || "", this.event || "") || "";
212
- if (!toState) return;
213
- return this._newSubstate(parent, toState);
214
- }
215
- async _newSubstate(parent, toState) {
216
- let descriptor;
217
- for (let state = parent; !descriptor && state; state = state.parent) descriptor = state.descriptor?.states[toState];
218
- return this._newState(parent, toState, descriptor);
219
- }
220
- async _update() {
221
- if (this.status & 16) return false;
222
- const nextState = this.status !== 0 ? this.status & 3 ? await this._getSubstate(this.state, "") : await this._getSubstate(this.state?.parent, this.state?.key) : await this._newState(void 0, this.config.key, this.rootDescriptor);
223
- if (nextState !== void 0) {
224
- this.state = nextState;
225
- this.status = this.status & 12 ? 2 : 1;
226
- } else {
227
- if (this.status & 12) {
228
- this.state = this.state?.parent;
229
- this.status = 8;
230
- } else this.status = 4;
231
- if (!this.state) this.status = 16;
232
- }
233
- return !(this.status & 16);
234
- }
235
- };
236
- //#endregion
237
- //#region src/orchestrator/handler-registry.ts
238
- const DEFAULT_PATTERNS = [
239
- (state) => state,
240
- (state) => `${state}Controller`,
241
- (state) => `${state}View`,
242
- (state) => `${state}Trigger`
243
- ];
244
- function createHandlerRegistry() {
245
- const configs = {};
246
- const handlers = {};
247
- return {
248
- register(processName, config) {
249
- configs[processName] = config;
250
- return () => {
251
- delete configs[processName];
252
- };
253
- },
254
- registerHandlers(processName, ...modules) {
255
- let list = handlers[processName];
256
- if (!list) list = handlers[processName] = [];
257
- list.push(modules);
258
- return () => {
259
- const l = handlers[processName];
260
- if (!l) return;
261
- const idx = l.indexOf(modules);
262
- if (idx !== -1) l.splice(idx, 1);
263
- if (l.length === 0) delete handlers[processName];
264
- };
265
- },
266
- getConfig(processName) {
267
- const config = configs[processName];
268
- if (!config) throw new Error(`No config registered for process "${processName}"`);
269
- return config;
270
- },
271
- getLoader(processName, patterns) {
272
- const config = this.getConfig(processName);
273
- const pats = patterns ?? DEFAULT_PATTERNS;
274
- return (stateKey) => {
275
- const modulesKeys = [];
276
- if (stateKey === config.key) modulesKeys.push("default");
277
- for (const pattern of pats) modulesKeys.push(pattern(stateKey));
278
- const keysSet = new Set(modulesKeys);
279
- return toStageHandlers(handlers[processName] ?? [], (key) => keysSet.has(key));
280
- };
281
- }
282
- };
283
- }
284
- function toStageHandlers(value, accept) {
285
- return visit(value);
286
- function visit(val) {
287
- if (!val) return [];
288
- if (typeof val === "function") return [val];
289
- if (typeof val !== "object") return [];
290
- if (Array.isArray(val)) return val.flatMap(visit);
291
- return Object.entries(val).flatMap(([key, value]) => {
292
- return accept(key, value) ? visit(value) : [];
293
- });
294
- }
295
- }
296
- //#endregion
297
- //#region src/orchestrator/start-process.ts
298
- const KEY_DISPATCH = "fsm:dispatch";
299
- const KEY_TERMINATE = "fsm:terminate";
300
- const KEY_STATES = "fsm:states";
301
- const KEY_EVENT = "fsm:event";
302
- function getStateTransitions(state) {
303
- const result = [];
304
- const index = {};
305
- if (state) {
306
- let prevStateKey = state.key;
307
- for (let parent = state.parent; parent; parent = parent.parent) {
308
- if (!parent.descriptor) continue;
309
- result.push(...getTransitionsFromDescriptor(parent.descriptor, prevStateKey, index));
310
- prevStateKey = parent.key;
311
- }
312
- }
313
- return result.reverse();
314
- }
315
- function getTransitionsFromDescriptor(descriptor, prevStateKey, index) {
316
- const result = [];
317
- const prevStateKeys = [prevStateKey, "*"];
318
- for (const prevKey of prevStateKeys) {
319
- const targets = descriptor.transitions[prevKey];
320
- if (targets) for (const [event, target] of Object.entries(targets)) {
321
- if (index[event]) continue;
322
- if (target) index[event] = true;
323
- result.push([
324
- prevStateKey,
325
- event,
326
- target
327
- ]);
328
- }
329
- }
330
- return result;
331
- }
332
- function isStateTransitionEnabled(process, event) {
333
- const transitions = getStateTransitions(process.state);
334
- for (const [, ev] of transitions) if (ev === event) return true;
335
- return false;
336
- }
337
- function isGenerator(value) {
338
- return typeof value === "object" && value !== null && "next" in value && typeof value.next === "function";
339
- }
340
- async function startProcess(context, config, load, startEvent = "") {
341
- let terminated = false;
342
- const ctx = context;
343
- const process = new FsmProcess(config);
344
- const statesStack = [];
345
- process.onStateCreate((state) => {
346
- state.onEnter(async () => {
347
- statesStack.push(state.key);
348
- ctx[KEY_STATES] = [...statesStack];
349
- ctx[KEY_EVENT] = process.event;
350
- const modules = await load(state.key, process.event) ?? [];
351
- for (const module of Array.isArray(modules) ? modules : [modules]) {
352
- const result = await module?.(context);
353
- if (isGenerator(result)) {
354
- let stateExited = false;
355
- state.onExit(() => {
356
- stateExited = true;
357
- result.return?.(void 0);
358
- });
359
- (async () => {
360
- try {
361
- for await (const event of result) {
362
- if (stateExited || terminated) break;
363
- await dispatch(event);
364
- if (stateExited || terminated) break;
365
- }
366
- } catch {}
367
- })();
368
- } else if (typeof result === "function") state.onExit(result);
369
- }
370
- });
371
- state.onExit(() => {
372
- statesStack.pop();
373
- ctx[KEY_STATES] = [...statesStack];
374
- ctx[KEY_EVENT] = process.event;
375
- });
376
- });
377
- async function dispatch(event) {
378
- if (event !== void 0 && isStateTransitionEnabled(process, event)) await process.dispatch(event);
379
- }
380
- async function terminate() {
381
- terminated = true;
382
- await process.shutdown();
383
- }
384
- async function dumpProcess(...args) {
385
- return process.dump(...args);
386
- }
387
- async function restoreProcess(dumpData, ...args) {
388
- statesStack.length = 0;
389
- terminated = false;
390
- await process.restore(dumpData, ...args);
391
- for (let state = process.state; state; state = state.parent) statesStack.unshift(state.key);
392
- ctx[KEY_STATES] = [...statesStack];
393
- ctx[KEY_EVENT] = process.event;
394
- }
395
- ctx[KEY_DISPATCH] = dispatch;
396
- ctx[KEY_TERMINATE] = terminate;
397
- await process.dispatch(startEvent);
398
- return {
399
- shutdown: terminate,
400
- dump: dumpProcess,
401
- restore: restoreProcess
402
- };
403
- }
404
- /** @deprecated Use `startProcess` instead */
405
- const startFsmProcess = startProcess;
406
- //#endregion
407
- //#region src/orchestrator/launcher.ts
408
- const KEY_LAUNCH_PROCESS = "fsm:launch";
409
- async function launcher(config) {
410
- const registry = createHandlerRegistry();
411
- async function launch(processName, context, startEvent = "start") {
412
- return startProcess(context, registry.getConfig(processName), registry.getLoader(processName), startEvent);
413
- }
414
- const rootContext = { [KEY_LAUNCH_PROCESS]: launch };
415
- const initContext = config.context ?? ((ctx) => ctx);
416
- const startSet = config.start ? new Set(config.start) : void 0;
417
- for (const def of config.processes) {
418
- registry.register(def.name, def.config);
419
- if (def.handlers?.length) registry.registerHandlers(def.name, ...def.handlers);
420
- }
421
- const shutdowns = [];
422
- const seen = /* @__PURE__ */ new Set();
423
- for (const def of config.processes) {
424
- if (seen.has(def.name)) continue;
425
- seen.add(def.name);
426
- if (def.start === false) continue;
427
- if (startSet && !startSet.has(def.name)) continue;
428
- let context = {
429
- parent: rootContext,
430
- "fsm:name": def.name
431
- };
432
- context = initContext(context) ?? context;
433
- const handle = await launch(def.name, context);
434
- shutdowns.push(handle.shutdown);
435
- }
436
- return async () => {
437
- for (const shutdown of shutdowns) await shutdown();
438
- };
439
- }
440
- //#endregion
441
- export { bindMethods as A, EVENT_ANY as C, STATE_INITIAL as D, STATE_FINAL as E, FsmState as O, FsmStateDescriptor as S, STATE_ANY as T, STATUS_FIRST as _, KEY_STATES as a, STATUS_NEXT as b, isStateTransitionEnabled as c, createHandlerRegistry as d, toStageHandlers as f, STATUS_FINISHED as g, STATUS_EXIT as h, KEY_EVENT as i, FsmBaseClass as k, startFsmProcess as l, STATUS_ENTER as m, launcher as n, KEY_TERMINATE as o, FsmProcess as p, KEY_DISPATCH as r, getStateTransitions as s, KEY_LAUNCH_PROCESS as t, startProcess as u, STATUS_LAST as v, EVENT_EMPTY as w, STATUS_NONE as x, STATUS_LEAF as y };
442
-
443
- //# sourceMappingURL=launcher-CASrMAOa.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"launcher-CASrMAOa.js","names":[],"sources":["../src/core/fsm-base-class.ts","../src/core/fsm-state.ts","../src/core/fsm-state-config.ts","../src/core/fsm-state-descriptor.ts","../src/core/fsm-process.ts","../src/orchestrator/handler-registry.ts","../src/orchestrator/start-process.ts","../src/orchestrator/launcher.ts"],"sourcesContent":["type Handler<P extends unknown[] = unknown[], R = unknown> = (...args: P) => R;\n\nexport class FsmBaseClass {\n handlers: Record<string, Handler[]> = {};\n\n // ----------------------------------------------\n // internal methods\n protected _addHandler<T>(type: string, handler: T, direct: boolean = true) {\n let list = this.handlers[type];\n if (!list) {\n list = this.handlers[type] = [];\n }\n const h = handler as Handler;\n direct ? list.push(h) : list.unshift(h);\n return () => this._removeHandler(type, h);\n }\n\n protected _removeHandler<T>(type: string, handler: T) {\n let list = this.handlers[type];\n if (!list) return;\n list = list.filter((h) => h !== handler);\n if (list.length > 0) {\n this.handlers[type] = list;\n } else {\n delete this.handlers[type];\n }\n }\n\n async _runHandler(type: string, ...args: unknown[]) {\n const list = this.handlers[type] || [];\n for (const handler of list) {\n try {\n await handler(...args);\n } catch (error) {\n await this._handleError(error);\n }\n }\n }\n\n async _handleError(error: Error | unknown) {\n console.error(error);\n }\n}\n\nexport function bindMethods<T>(obj: T, ...methods: (string | symbol)[]) {\n const o = obj as Record<string | symbol, unknown>;\n for (const methodName of methods) {\n const method = o[methodName];\n if (typeof method !== \"function\") continue;\n o[methodName] = method.bind(o);\n }\n return obj;\n}\n","import { bindMethods, FsmBaseClass } from \"./fsm-base-class.ts\";\nimport type { FsmProcess } from \"./fsm-process.ts\";\nimport type { FsmStateDescriptor } from \"./fsm-state-descriptor.ts\";\n\nexport type FsmStateDump = Record<string, unknown> & {\n key: string;\n data: Record<string, unknown>;\n};\nexport type FsmStateHandler = (\n state: FsmState,\n ...args: unknown[]\n) => void | Promise<void>;\n\nexport type FsmStateDumpHandler = (\n state: FsmState,\n dump: FsmStateDump,\n) => void | Promise<void>;\n\nexport type FsmStateErrorHandler = (\n state: FsmState,\n error: unknown,\n) => void | Promise<void>;\n\nexport class FsmState extends FsmBaseClass {\n process: FsmProcess;\n key: string;\n parent?: FsmState;\n descriptor?: FsmStateDescriptor;\n\n constructor(\n process: FsmProcess,\n parent: FsmState | undefined,\n key: string,\n descriptor?: FsmStateDescriptor,\n ) {\n super();\n this.process = process;\n this.key = key;\n this.parent = parent;\n this.descriptor = descriptor;\n bindMethods(this, \"onEnter\", \"onExit\", \"dump\", \"restore\", \"onStateError\");\n }\n\n onEnter(handler: FsmStateHandler) {\n return this._addHandler(\"onEnter\", handler, true);\n }\n onExit(handler: FsmStateHandler) {\n return this._addHandler(\"onExit\", handler, false);\n }\n onStateError(handler: FsmStateErrorHandler) {\n return this._addHandler(\"onStateError\", handler);\n }\n dump(handler: FsmStateDumpHandler) {\n return this._addHandler(\"dump\", handler, true);\n }\n restore(handler: FsmStateDumpHandler) {\n return this._addHandler(\"restore\", handler, true);\n }\n\n async _handleError(error: Error | unknown) {\n await this._runHandler(\"onStateError\", this, error);\n await this.process._handleStateError(this, error);\n }\n}\n","export const STATE_ANY = \"*\";\nexport const STATE_INITIAL = \"\";\nexport const STATE_FINAL = \"\";\nexport const EVENT_ANY = \"*\";\nexport const EVENT_EMPTY = \"\";\n\nexport type FsmStateKey = string;\nexport type FsmEventKey = string;\nexport type FsmStateConfig = {\n key: FsmStateKey;\n transitions?: [from: FsmStateKey, event: FsmEventKey, to: FsmStateKey][];\n states?: FsmStateConfig[];\n} & Record<string, unknown>;\n","import {\n EVENT_ANY,\n type FsmStateConfig,\n STATE_ANY,\n STATE_FINAL,\n} from \"./fsm-state-config.ts\";\n\nexport class FsmStateDescriptor {\n transitions: Record<string, Record<string, string>> = {};\n states: Record<string, FsmStateDescriptor> = {};\n\n static build(config: FsmStateConfig) {\n const descriptor = new FsmStateDescriptor();\n for (const [from, event, to] of config.transitions || []) {\n let index = descriptor.transitions[from];\n if (!index) {\n index = descriptor.transitions[from] = {};\n }\n index[event] = to;\n }\n if (config.states) {\n for (const substateConfig of config.states) {\n descriptor.states[substateConfig.key] =\n FsmStateDescriptor.build(substateConfig);\n }\n }\n return descriptor;\n }\n\n getTargetStateKey(stateKey: string, eventKey: string) {\n const pairs = [\n [stateKey, eventKey],\n [STATE_ANY, eventKey],\n [stateKey, EVENT_ANY],\n [STATE_ANY, EVENT_ANY],\n ];\n let targetKey: string | undefined;\n for (\n let i = 0, len = pairs.length;\n targetKey === undefined && i < len;\n i++\n ) {\n const [stateKey, eventKey] = pairs[i];\n const stateTransitions = this.transitions[stateKey];\n if (!stateTransitions) continue;\n targetKey = stateTransitions[eventKey];\n }\n return targetKey !== undefined ? targetKey : STATE_FINAL;\n }\n}\n","import { bindMethods, FsmBaseClass } from \"./fsm-base-class.ts\";\nimport {\n FsmState,\n type FsmStateDump,\n type FsmStateHandler,\n} from \"./fsm-state.ts\";\nimport {\n EVENT_EMPTY,\n type FsmStateConfig,\n STATE_FINAL,\n STATE_INITIAL,\n} from \"./fsm-state-config.ts\";\nimport { FsmStateDescriptor } from \"./fsm-state-descriptor.ts\";\n\nexport const STATUS_NONE = 0;\nexport const STATUS_FIRST = 1;\nexport const STATUS_NEXT = 2;\nexport const STATUS_LEAF = 4;\nexport const STATUS_LAST = 8;\nexport const STATUS_FINISHED = 16;\n\n//\nexport const STATUS_ENTER = STATUS_FIRST | STATUS_NEXT;\nexport const STATUS_EXIT = STATUS_LEAF | STATUS_LAST;\n\nexport type FsmProcessHandler = (\n process: FsmProcess,\n ...args: unknown[]\n) => void | Promise<void>;\n\nexport type FsmProcessDump = Record<string, unknown> & {\n status: number;\n event?: string;\n stack: FsmStateDump[];\n};\n\nexport type FsmProcessDumpHandler = (\n process: FsmProcess,\n dump: FsmProcessDump,\n) => void | Promise<void>;\n\nexport class FsmProcess extends FsmBaseClass {\n state?: FsmState;\n event?: string;\n nextEvent?: string;\n running: boolean = false;\n mask: number = STATUS_LEAF;\n status: number = 0;\n config: FsmStateConfig;\n rootDescriptor: FsmStateDescriptor;\n\n constructor(config: FsmStateConfig) {\n super();\n this.rootDescriptor = FsmStateDescriptor.build(config);\n this.config = config;\n bindMethods(this, \"dispatch\", \"dump\", \"restore\");\n }\n\n async shutdown(event?: string) {\n while (this.state) {\n this.event = event;\n this.status = STATUS_FINISHED;\n await this.state?._runHandler(\"onExit\", this.state);\n this.state = this.state.parent;\n }\n }\n\n async dispatch(event: string): Promise<boolean> {\n this.nextEvent = event;\n if (!this.running && !(this.status & STATUS_FINISHED)) {\n this.running = true;\n try {\n this.event = this.nextEvent;\n this.nextEvent = undefined;\n while (true) {\n // ---\n if (this.status & STATUS_EXIT) {\n await this.state?._runHandler(\"onExit\", this.state);\n }\n if (!(await this._update())) break;\n if (this.status & STATUS_ENTER) {\n await this.state?._runHandler(\"onEnter\", this.state);\n }\n if (this.status & this.mask) break;\n // ---\n }\n } finally {\n this.running = false;\n }\n const nextEvent = this.nextEvent;\n if (nextEvent !== undefined) {\n return Promise.resolve().then(() => this.dispatch(nextEvent));\n }\n }\n return !(this.status & STATUS_FINISHED);\n }\n\n async dump(...args: unknown[]): Promise<FsmProcessDump> {\n const dumpState = async (state: FsmState) => {\n const stateDump: FsmStateDump = {\n key: state.key,\n data: {},\n };\n await state._runHandler(\"dump\", state, stateDump.data, ...args);\n return stateDump;\n };\n const dumpStates = async (\n state: FsmState | undefined,\n stack: FsmStateDump[] = [],\n ) => {\n if (!state) return stack;\n state.parent && (await dumpStates(state.parent, stack));\n stack.push(await dumpState(state));\n return stack;\n };\n const dump: FsmProcessDump = {\n status: this.status,\n event: this.event,\n stack: await dumpStates(this.state),\n };\n return dump;\n }\n\n async restore(dump: FsmProcessDump, ...args: unknown[]) {\n this.status = dump.status || 0;\n this.event = dump.event;\n this.state = undefined;\n for (let i = 0; i < dump.stack.length; i++) {\n const stateDump = dump.stack[i];\n this.state = this.state\n ? await this._newSubstate(this.state, stateDump.key)\n : await this._newState(undefined, stateDump.key, this.rootDescriptor);\n await this.state._runHandler(\n \"restore\",\n this.state,\n stateDump.data,\n ...args,\n );\n }\n return this;\n }\n\n onStateCreate(handler: FsmStateHandler) {\n return this._addHandler(\"onStateCreate\", handler, true);\n }\n\n onStateError(\n handler: (state: FsmState, error: unknown) => void | Promise<void>,\n ) {\n return this._addHandler(\"onStateError\", handler);\n }\n\n async _handleStateError(state: FsmState, error: Error | unknown) {\n await this._runHandler(\"onStateError\", state, error);\n this._handleError(error);\n }\n\n async _newState(\n parent: FsmState | undefined,\n key: string,\n descriptor: FsmStateDescriptor | undefined,\n ) {\n const state = new FsmState(this, parent, key, descriptor);\n await this._runHandler(\"onStateCreate\", state);\n return state;\n }\n\n async _getSubstate(\n parent: FsmState | undefined,\n prevStateKey: string | undefined,\n ) {\n if (!parent) return;\n const toState =\n parent.descriptor?.getTargetStateKey(\n prevStateKey || STATE_INITIAL,\n this.event || EVENT_EMPTY,\n ) || STATE_FINAL;\n if (!toState) return;\n return this._newSubstate(parent, toState);\n }\n\n async _newSubstate(parent: FsmState | undefined, toState: string) {\n let descriptor: FsmStateDescriptor | undefined;\n for (\n let state: FsmState | undefined = parent;\n !descriptor && state;\n state = state.parent\n ) {\n descriptor = state.descriptor?.states[toState];\n }\n return this._newState(parent, toState, descriptor);\n }\n\n async _update() {\n if (this.status & STATUS_FINISHED) return false;\n const nextState =\n this.status !== STATUS_NONE\n ? this.status & STATUS_ENTER\n ? await this._getSubstate(this.state, STATE_INITIAL)\n : await this._getSubstate(this.state?.parent, this.state?.key)\n : await this._newState(undefined, this.config.key, this.rootDescriptor);\n if (nextState !== undefined) {\n this.state = nextState;\n this.status = this.status & STATUS_EXIT ? STATUS_NEXT : STATUS_FIRST;\n } else {\n if (this.status & STATUS_EXIT) {\n this.state = this.state?.parent;\n this.status = STATUS_LAST;\n } else {\n this.status = STATUS_LEAF;\n }\n if (!this.state) this.status = STATUS_FINISHED;\n }\n return !(this.status & STATUS_FINISHED);\n }\n}\n","import type { FsmStateConfig } from \"../core/fsm-state-config.ts\";\n\n/** A handler invoked when a state is entered. */\nexport type StageHandler<C = Record<string, unknown>> = (\n context: C,\n) =>\n | void\n | (() => void | Promise<void>)\n | Promise<void | (() => void | Promise<void>)>\n | AsyncGenerator<string, void, unknown>\n | Generator<string, void, unknown>;\n\n/** Maps a state key to a handler module key for auto-discovery. */\ntype PatternFn = (state: string) => string;\n\nconst DEFAULT_PATTERNS: PatternFn[] = [\n (state) => state,\n (state) => `${state}Controller`,\n (state) => `${state}View`,\n (state) => `${state}Trigger`,\n];\n\nexport interface HandlerRegistry {\n register(processName: string, config: FsmStateConfig): () => void;\n registerHandlers(processName: string, ...modules: unknown[]): () => void;\n getConfig(processName: string): FsmStateConfig;\n getLoader<C>(\n processName: string,\n patterns?: PatternFn[],\n ): (state: string, event?: string) => StageHandler<C>[];\n}\n\nexport function createHandlerRegistry(): HandlerRegistry {\n const configs: Record<string, FsmStateConfig> = {};\n const handlers: Record<string, unknown[]> = {};\n\n return {\n register(processName: string, config: FsmStateConfig): () => void {\n configs[processName] = config;\n return () => {\n delete configs[processName];\n };\n },\n\n registerHandlers(processName: string, ...modules: unknown[]): () => void {\n let list = handlers[processName];\n if (!list) {\n list = handlers[processName] = [];\n }\n list.push(modules);\n return () => {\n const l = handlers[processName];\n if (!l) return;\n const idx = l.indexOf(modules);\n if (idx !== -1) l.splice(idx, 1);\n if (l.length === 0) delete handlers[processName];\n };\n },\n\n getConfig(processName: string): FsmStateConfig {\n const config = configs[processName];\n if (!config) {\n throw new Error(`No config registered for process \"${processName}\"`);\n }\n return config;\n },\n\n getLoader<C>(\n processName: string,\n patterns?: PatternFn[],\n ): (state: string, event?: string) => StageHandler<C>[] {\n const config = this.getConfig(processName);\n const pats = patterns ?? DEFAULT_PATTERNS;\n return (stateKey: string) => {\n const modulesKeys: string[] = [];\n if (stateKey === config.key) {\n modulesKeys.push(\"default\");\n }\n for (const pattern of pats) {\n modulesKeys.push(pattern(stateKey));\n }\n const keysSet = new Set(modulesKeys);\n const processHandlers = handlers[processName] ?? [];\n return toStageHandlers<C>(processHandlers, (key: string) =>\n keysSet.has(key),\n );\n };\n },\n };\n}\n\nexport function toStageHandlers<C>(\n value: unknown,\n accept: (key: string, value: unknown) => boolean,\n): StageHandler<C>[] {\n return visit(value);\n\n function visit(val: unknown): StageHandler<C>[] {\n if (!val) return [];\n if (typeof val === \"function\") return [val] as StageHandler<C>[];\n if (typeof val !== \"object\") return [];\n if (Array.isArray(val)) return val.flatMap(visit);\n const obj = val as Record<string, unknown>;\n return Object.entries(obj).flatMap(([key, value]) => {\n return accept(key, value) ? visit(value) : [];\n });\n }\n}\n","import { FsmProcess, type FsmProcessDump } from \"../core/fsm-process.ts\";\nimport type { FsmState } from \"../core/fsm-state.ts\";\nimport type { FsmStateConfig } from \"../core/fsm-state-config.ts\";\nimport type { FsmStateDescriptor } from \"../core/fsm-state-descriptor.ts\";\nimport type { StageHandler } from \"./handler-registry.ts\";\n\nexport interface ProcessHandle {\n shutdown(): Promise<void>;\n dump(...args: unknown[]): Promise<FsmProcessDump>;\n restore(dump: FsmProcessDump, ...args: unknown[]): Promise<void>;\n}\n\n// Context keys for binding FSM functions into the shared context object.\nexport const KEY_DISPATCH = \"fsm:dispatch\";\nexport const KEY_TERMINATE = \"fsm:terminate\";\nexport const KEY_STATES = \"fsm:states\";\nexport const KEY_EVENT = \"fsm:event\";\n\n// ---------------------------------------------------------------------------\n// Transition introspection\n// ---------------------------------------------------------------------------\n\nexport function getStateTransitions(\n state?: FsmState,\n): [from: string, event: string, to: string][] {\n const result: [from: string, event: string, to: string][] = [];\n const index: Record<string, boolean> = {};\n if (state) {\n let prevStateKey = state.key;\n for (let parent = state.parent; parent; parent = parent.parent) {\n if (!parent.descriptor) continue;\n result.push(\n ...getTransitionsFromDescriptor(parent.descriptor, prevStateKey, index),\n );\n prevStateKey = parent.key;\n }\n }\n return result.reverse();\n}\n\nfunction getTransitionsFromDescriptor(\n descriptor: FsmStateDescriptor,\n prevStateKey: string,\n index: Record<string, boolean>,\n): [from: string, event: string, to: string][] {\n const result: [from: string, event: string, to: string][] = [];\n const prevStateKeys = [prevStateKey, \"*\"];\n for (const prevKey of prevStateKeys) {\n const targets = descriptor.transitions[prevKey];\n if (targets) {\n for (const [event, target] of Object.entries(targets)) {\n if (index[event]) continue;\n if (target) index[event] = true;\n result.push([prevStateKey, event, target]);\n }\n }\n }\n return result;\n}\n\nexport function isStateTransitionEnabled(\n process: FsmProcess,\n event: string,\n): boolean {\n const transitions = getStateTransitions(process.state);\n for (const [, ev] of transitions) {\n if (ev === event) return true;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Generator detection\n// ---------------------------------------------------------------------------\n\nfunction isGenerator(\n value: unknown,\n): value is\n | Generator<string, void, unknown>\n | AsyncGenerator<string, void, unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"next\" in value &&\n typeof (value as { next: unknown }).next === \"function\"\n );\n}\n\n// ---------------------------------------------------------------------------\n// startProcess (also exported as startFsmProcess for backward compat)\n// ---------------------------------------------------------------------------\n\nexport async function startProcess<C = unknown>(\n context: C,\n config: FsmStateConfig,\n load: (\n state: string,\n event: string | undefined,\n ) => StageHandler<C>[] | Promise<StageHandler<C>[]>,\n startEvent = \"\",\n): Promise<ProcessHandle> {\n let terminated = false;\n const ctx = context as Record<string, unknown>;\n const process = new FsmProcess(config);\n const statesStack: string[] = [];\n\n process.onStateCreate((state: FsmState) => {\n state.onEnter(async () => {\n statesStack.push(state.key);\n ctx[KEY_STATES] = [...statesStack];\n ctx[KEY_EVENT] = process.event;\n\n const modules = (await load(state.key, process.event)) ?? [];\n for (const module of Array.isArray(modules) ? modules : [modules]) {\n const result = await module?.(context);\n if (isGenerator(result)) {\n let stateExited = false;\n state.onExit(() => {\n stateExited = true;\n result.return?.(undefined as never);\n });\n (async () => {\n try {\n for await (const event of result) {\n if (stateExited || terminated) break;\n await dispatch(event);\n if (stateExited || terminated) break;\n }\n } catch {\n // Swallow generator errors after return()\n }\n })();\n } else if (typeof result === \"function\") {\n state.onExit(result as () => void | Promise<void>);\n }\n }\n });\n state.onExit(() => {\n statesStack.pop();\n ctx[KEY_STATES] = [...statesStack];\n ctx[KEY_EVENT] = process.event;\n });\n });\n\n async function dispatch(event: string): Promise<void> {\n if (event !== undefined && isStateTransitionEnabled(process, event)) {\n await process.dispatch(event);\n }\n }\n async function terminate(): Promise<void> {\n terminated = true;\n await process.shutdown();\n }\n\n async function dumpProcess(...args: unknown[]): Promise<FsmProcessDump> {\n return process.dump(...args);\n }\n\n async function restoreProcess(\n dumpData: FsmProcessDump,\n ...args: unknown[]\n ): Promise<void> {\n statesStack.length = 0;\n terminated = false;\n await process.restore(dumpData, ...args);\n for (\n let state: FsmState | undefined = process.state;\n state;\n state = state.parent\n ) {\n statesStack.unshift(state.key);\n }\n ctx[KEY_STATES] = [...statesStack];\n ctx[KEY_EVENT] = process.event;\n }\n\n ctx[KEY_DISPATCH] = dispatch;\n ctx[KEY_TERMINATE] = terminate;\n await process.dispatch(startEvent);\n return {\n shutdown: terminate,\n dump: dumpProcess,\n restore: restoreProcess,\n };\n}\n\n/** @deprecated Use `startProcess` instead */\nexport const startFsmProcess = startProcess;\n","import type { FsmStateConfig } from \"../core/fsm-state-config.ts\";\nimport type { StageHandler } from \"./handler-registry.ts\";\nimport { createHandlerRegistry } from \"./handler-registry.ts\";\nimport { startProcess } from \"./start-process.ts\";\n\nexport const KEY_LAUNCH_PROCESS = \"fsm:launch\";\n\nexport interface LauncherConfig {\n processes: ProcessDef[];\n start?: string[];\n context?: (parent: Record<string, unknown>) => Record<string, unknown>;\n}\n\nexport interface ProcessDef {\n name: string;\n config: FsmStateConfig;\n handlers?: (StageHandler | Record<string, StageHandler | StageHandler[]>)[];\n start?: boolean;\n}\n\nexport async function launcher(\n config: LauncherConfig,\n): Promise<() => Promise<void>> {\n const registry = createHandlerRegistry();\n\n async function launch(\n processName: string,\n context: Record<string, unknown>,\n startEvent = \"start\",\n ) {\n const cfg = registry.getConfig(processName);\n const load = registry.getLoader(processName);\n return startProcess(context, cfg, load, startEvent);\n }\n\n const rootContext: Record<string, unknown> = {\n [KEY_LAUNCH_PROCESS]: launch,\n };\n const initContext = config.context ?? ((ctx: Record<string, unknown>) => ctx);\n\n const startSet = config.start ? new Set(config.start) : undefined;\n\n for (const def of config.processes) {\n registry.register(def.name, def.config);\n if (def.handlers?.length) {\n registry.registerHandlers(def.name, ...def.handlers);\n }\n }\n\n const shutdowns: (() => Promise<void>)[] = [];\n const seen = new Set<string>();\n for (const def of config.processes) {\n if (seen.has(def.name)) continue;\n seen.add(def.name);\n\n if (def.start === false) continue;\n if (startSet && !startSet.has(def.name)) continue;\n\n let context: Record<string, unknown> = {\n parent: rootContext,\n \"fsm:name\": def.name,\n };\n context = initContext(context) ?? context;\n const handle = await launch(def.name, context);\n shutdowns.push(handle.shutdown);\n }\n\n return async () => {\n for (const shutdown of shutdowns) {\n await shutdown();\n }\n };\n}\n"],"mappings":"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"}
@@ -1,57 +0,0 @@
1
- import type { LauncherConfig, ProcessDef } from "../orchestrator/launcher.ts";
2
- import { launcher } from "../orchestrator/launcher.ts";
3
-
4
- function resolveModulesPaths(baseUrl: string, ...refs: string[]): string[] {
5
- return refs.map((ref) => new URL(ref, baseUrl).pathname);
6
- }
7
-
8
- export async function startProcesses({
9
- onExit,
10
- modules,
11
- context,
12
- }: {
13
- onExit?: () => Promise<void> | void;
14
- modules: unknown[];
15
- context?: LauncherConfig["context"];
16
- }) {
17
- const processDefs: ProcessDef[] = [];
18
- for (const mod of modules) {
19
- const imported =
20
- typeof mod === "string" || mod instanceof URL
21
- ? await import(String(mod))
22
- : mod;
23
- if (imported && typeof imported === "object") {
24
- processDefs.push(imported as ProcessDef);
25
- }
26
- }
27
-
28
- if (onExit) {
29
- processDefs.push({
30
- name: "Exit",
31
- config: { key: "Exit" },
32
- handlers: [() => onExit],
33
- });
34
- }
35
-
36
- return await launcher({ processes: processDefs, context });
37
- }
38
-
39
- export async function startNodeProcesses(modules?: string[]): Promise<void> {
40
- try {
41
- if (!modules) {
42
- modules = process.argv.slice(2);
43
- }
44
- const terminate = await startProcesses({
45
- modules: resolveModulesPaths(`file://${process.cwd()}/`, ...modules),
46
- onExit: () => process.exit(0),
47
- });
48
- process.on("SIGINT", terminate);
49
- process.on("SIGTERM", terminate);
50
- process.stdin.resume();
51
- } catch (err) {
52
- console.error("Fatal error:", err);
53
- process.exit(1);
54
- }
55
- }
56
-
57
- export default startNodeProcesses;
@@ -1,108 +0,0 @@
1
- import type { FsmStateConfig } from "../core/fsm-state-config.ts";
2
-
3
- /** A handler invoked when a state is entered. */
4
- export type StageHandler<C = Record<string, unknown>> = (
5
- context: C,
6
- ) =>
7
- | void
8
- | (() => void | Promise<void>)
9
- | Promise<void | (() => void | Promise<void>)>
10
- | AsyncGenerator<string, void, unknown>
11
- | Generator<string, void, unknown>;
12
-
13
- /** Maps a state key to a handler module key for auto-discovery. */
14
- type PatternFn = (state: string) => string;
15
-
16
- const DEFAULT_PATTERNS: PatternFn[] = [
17
- (state) => state,
18
- (state) => `${state}Controller`,
19
- (state) => `${state}View`,
20
- (state) => `${state}Trigger`,
21
- ];
22
-
23
- export interface HandlerRegistry {
24
- register(processName: string, config: FsmStateConfig): () => void;
25
- registerHandlers(processName: string, ...modules: unknown[]): () => void;
26
- getConfig(processName: string): FsmStateConfig;
27
- getLoader<C>(
28
- processName: string,
29
- patterns?: PatternFn[],
30
- ): (state: string, event?: string) => StageHandler<C>[];
31
- }
32
-
33
- export function createHandlerRegistry(): HandlerRegistry {
34
- const configs: Record<string, FsmStateConfig> = {};
35
- const handlers: Record<string, unknown[]> = {};
36
-
37
- return {
38
- register(processName: string, config: FsmStateConfig): () => void {
39
- configs[processName] = config;
40
- return () => {
41
- delete configs[processName];
42
- };
43
- },
44
-
45
- registerHandlers(processName: string, ...modules: unknown[]): () => void {
46
- let list = handlers[processName];
47
- if (!list) {
48
- list = handlers[processName] = [];
49
- }
50
- list.push(modules);
51
- return () => {
52
- const l = handlers[processName];
53
- if (!l) return;
54
- const idx = l.indexOf(modules);
55
- if (idx !== -1) l.splice(idx, 1);
56
- if (l.length === 0) delete handlers[processName];
57
- };
58
- },
59
-
60
- getConfig(processName: string): FsmStateConfig {
61
- const config = configs[processName];
62
- if (!config) {
63
- throw new Error(`No config registered for process "${processName}"`);
64
- }
65
- return config;
66
- },
67
-
68
- getLoader<C>(
69
- processName: string,
70
- patterns?: PatternFn[],
71
- ): (state: string, event?: string) => StageHandler<C>[] {
72
- const config = this.getConfig(processName);
73
- const pats = patterns ?? DEFAULT_PATTERNS;
74
- return (stateKey: string) => {
75
- const modulesKeys: string[] = [];
76
- if (stateKey === config.key) {
77
- modulesKeys.push("default");
78
- }
79
- for (const pattern of pats) {
80
- modulesKeys.push(pattern(stateKey));
81
- }
82
- const keysSet = new Set(modulesKeys);
83
- const processHandlers = handlers[processName] ?? [];
84
- return toStageHandlers<C>(processHandlers, (key: string) =>
85
- keysSet.has(key),
86
- );
87
- };
88
- },
89
- };
90
- }
91
-
92
- export function toStageHandlers<C>(
93
- value: unknown,
94
- accept: (key: string, value: unknown) => boolean,
95
- ): StageHandler<C>[] {
96
- return visit(value);
97
-
98
- function visit(val: unknown): StageHandler<C>[] {
99
- if (!val) return [];
100
- if (typeof val === "function") return [val] as StageHandler<C>[];
101
- if (typeof val !== "object") return [];
102
- if (Array.isArray(val)) return val.flatMap(visit);
103
- const obj = val as Record<string, unknown>;
104
- return Object.entries(obj).flatMap(([key, value]) => {
105
- return accept(key, value) ? visit(value) : [];
106
- });
107
- }
108
- }