@statewalker/fsm 0.36.0 → 0.37.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.
- package/README.md +112 -0
- package/bin/cli.js +1 -1
- package/dist/bin/node-runner-UJp8T_oP.d.ts +16 -0
- package/dist/bin/node-runner-UJp8T_oP.d.ts.map +1 -0
- package/dist/bin/node-runner.js +40 -0
- package/dist/bin/node-runner.js.map +1 -0
- package/dist/index-vTjnkbGW.d.ts +129 -0
- package/dist/index-vTjnkbGW.d.ts.map +1 -0
- package/dist/index.js +38 -779
- package/dist/index.js.map +1 -1
- package/dist/launcher-6VUDviTj.d.ts +48 -0
- package/dist/launcher-6VUDviTj.d.ts.map +1 -0
- package/dist/launcher-CASrMAOa.js +443 -0
- package/dist/launcher-CASrMAOa.js.map +1 -0
- package/package.json +8 -8
- package/src/bin/node-runner.ts +57 -0
- package/src/core/fsm-base-class.ts +1 -26
- package/src/core/fsm-process.ts +14 -11
- package/src/core/fsm-state.ts +1 -21
- package/src/core/index.ts +1 -1
- package/src/index.ts +0 -3
- package/src/orchestrator/handler-registry.ts +108 -0
- package/src/orchestrator/index.ts +15 -9
- package/src/orchestrator/launcher.ts +42 -141
- package/src/orchestrator/start-process.ts +141 -26
- package/src/utils/index.ts +0 -2
- package/src/utils/printer.ts +5 -11
- package/src/utils/tracer.ts +1 -1
- package/CHANGELOG.md +0 -59
- package/dist/index.d.ts +0 -335
- package/src/core/new-fsm-process.ts +0 -83
- package/src/orchestrator/constants.ts +0 -130
- package/src/orchestrator/is-generator.ts +0 -12
- package/src/orchestrator/process-config-manager.ts +0 -70
- package/src/orchestrator/resolve-module-refs.ts +0 -52
- package/src/orchestrator/start-node-processes.ts +0 -19
- package/src/orchestrator/start-processes.ts +0 -32
- package/src/orchestrator/types.ts +0 -34
- package/src/utils/handlers.ts +0 -35
- package/src/utils/process.ts +0 -26
- package/src/utils/transitions.ts +0 -56
|
@@ -0,0 +1 @@
|
|
|
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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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statewalker/fsm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "HFSM Implementation",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"homepage": "https://github.com/statewalker/statewalker-fsm",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@biomejs/biome": "^2.
|
|
41
|
-
"
|
|
40
|
+
"@biomejs/biome": "^2.3.0",
|
|
41
|
+
"tsdown": "^0.12.4",
|
|
42
42
|
"typescript": "^5.9.2",
|
|
43
43
|
"vitest": "^3.2.4"
|
|
44
44
|
},
|
|
@@ -47,16 +47,16 @@
|
|
|
47
47
|
"url": "git@github.com:statewalker/statewalker-fsm.git"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
|
-
"dist": "
|
|
51
|
-
"build": "
|
|
52
|
-
"watch": "
|
|
50
|
+
"dist": "pnpm test && tsdown",
|
|
51
|
+
"build": "pnpm dist",
|
|
52
|
+
"watch": "tsdown --watch",
|
|
53
53
|
"clean": "rm -rf dist",
|
|
54
54
|
"format": "biome format . --write",
|
|
55
55
|
"check": "biome check .",
|
|
56
|
-
"fix": "
|
|
56
|
+
"fix": "pnpm format && biome check . --fix --unsafe",
|
|
57
57
|
"test": "vitest --run",
|
|
58
58
|
"test:watch": "vitest",
|
|
59
|
-
"prepublish": "
|
|
59
|
+
"prepublish": "pnpm dist"
|
|
60
60
|
},
|
|
61
61
|
"sideEffects": false,
|
|
62
62
|
"publishConfig": {
|
|
@@ -0,0 +1,57 @@
|
|
|
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,19 +1,7 @@
|
|
|
1
|
-
type Handler<P extends unknown[] = unknown[], R = unknown> = (...args: P) => R;
|
|
2
|
-
// type Handler = Function;
|
|
1
|
+
type Handler<P extends unknown[] = unknown[], R = unknown> = (...args: P) => R;
|
|
3
2
|
|
|
4
3
|
export class FsmBaseClass {
|
|
5
4
|
handlers: Record<string, Handler[]> = {};
|
|
6
|
-
data: Record<string, unknown> = {};
|
|
7
|
-
constructor() {
|
|
8
|
-
bindMethods(this, "setData", "getData");
|
|
9
|
-
}
|
|
10
|
-
setData<T>(key: string, value: T) {
|
|
11
|
-
this.data[key] = value;
|
|
12
|
-
return this;
|
|
13
|
-
}
|
|
14
|
-
getData<T>(key: string): T | undefined {
|
|
15
|
-
return this.data[key] as T;
|
|
16
|
-
}
|
|
17
5
|
|
|
18
6
|
// ----------------------------------------------
|
|
19
7
|
// internal methods
|
|
@@ -38,19 +26,6 @@ export class FsmBaseClass {
|
|
|
38
26
|
}
|
|
39
27
|
}
|
|
40
28
|
|
|
41
|
-
async _runHandlerParallel(type: string, ...args: unknown[]) {
|
|
42
|
-
const list = this.handlers[type] || [];
|
|
43
|
-
await Promise.all(
|
|
44
|
-
list.map(async (handler) => {
|
|
45
|
-
try {
|
|
46
|
-
await handler(...args);
|
|
47
|
-
} catch (error) {
|
|
48
|
-
this._handleError(error);
|
|
49
|
-
}
|
|
50
|
-
}),
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
29
|
async _runHandler(type: string, ...args: unknown[]) {
|
|
55
30
|
const list = this.handlers[type] || [];
|
|
56
31
|
for (const handler of list) {
|
package/src/core/fsm-process.ts
CHANGED
|
@@ -77,7 +77,7 @@ export class FsmProcess extends FsmBaseClass {
|
|
|
77
77
|
if (this.status & STATUS_EXIT) {
|
|
78
78
|
await this.state?._runHandler("onExit", this.state);
|
|
79
79
|
}
|
|
80
|
-
if (!this._update()) break;
|
|
80
|
+
if (!(await this._update())) break;
|
|
81
81
|
if (this.status & STATUS_ENTER) {
|
|
82
82
|
await this.state?._runHandler("onEnter", this.state);
|
|
83
83
|
}
|
|
@@ -128,8 +128,8 @@ export class FsmProcess extends FsmBaseClass {
|
|
|
128
128
|
for (let i = 0; i < dump.stack.length; i++) {
|
|
129
129
|
const stateDump = dump.stack[i];
|
|
130
130
|
this.state = this.state
|
|
131
|
-
? this._newSubstate(this.state, stateDump.key)
|
|
132
|
-
: this._newState(undefined, stateDump.key, this.rootDescriptor);
|
|
131
|
+
? await this._newSubstate(this.state, stateDump.key)
|
|
132
|
+
: await this._newState(undefined, stateDump.key, this.rootDescriptor);
|
|
133
133
|
await this.state._runHandler(
|
|
134
134
|
"restore",
|
|
135
135
|
this.state,
|
|
@@ -155,17 +155,20 @@ export class FsmProcess extends FsmBaseClass {
|
|
|
155
155
|
this._handleError(error);
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
_newState(
|
|
158
|
+
async _newState(
|
|
159
159
|
parent: FsmState | undefined,
|
|
160
160
|
key: string,
|
|
161
161
|
descriptor: FsmStateDescriptor | undefined,
|
|
162
162
|
) {
|
|
163
163
|
const state = new FsmState(this, parent, key, descriptor);
|
|
164
|
-
this.
|
|
164
|
+
await this._runHandler("onStateCreate", state);
|
|
165
165
|
return state;
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
_getSubstate(
|
|
168
|
+
async _getSubstate(
|
|
169
|
+
parent: FsmState | undefined,
|
|
170
|
+
prevStateKey: string | undefined,
|
|
171
|
+
) {
|
|
169
172
|
if (!parent) return;
|
|
170
173
|
const toState =
|
|
171
174
|
parent.descriptor?.getTargetStateKey(
|
|
@@ -176,7 +179,7 @@ export class FsmProcess extends FsmBaseClass {
|
|
|
176
179
|
return this._newSubstate(parent, toState);
|
|
177
180
|
}
|
|
178
181
|
|
|
179
|
-
_newSubstate(parent: FsmState | undefined, toState: string) {
|
|
182
|
+
async _newSubstate(parent: FsmState | undefined, toState: string) {
|
|
180
183
|
let descriptor: FsmStateDescriptor | undefined;
|
|
181
184
|
for (
|
|
182
185
|
let state: FsmState | undefined = parent;
|
|
@@ -188,14 +191,14 @@ export class FsmProcess extends FsmBaseClass {
|
|
|
188
191
|
return this._newState(parent, toState, descriptor);
|
|
189
192
|
}
|
|
190
193
|
|
|
191
|
-
_update() {
|
|
194
|
+
async _update() {
|
|
192
195
|
if (this.status & STATUS_FINISHED) return false;
|
|
193
196
|
const nextState =
|
|
194
197
|
this.status !== STATUS_NONE
|
|
195
198
|
? this.status & STATUS_ENTER
|
|
196
|
-
? this._getSubstate(this.state, STATE_INITIAL)
|
|
197
|
-
: this._getSubstate(this.state?.parent, this.state?.key)
|
|
198
|
-
: this._newState(undefined, this.config.key, this.rootDescriptor);
|
|
199
|
+
? await this._getSubstate(this.state, STATE_INITIAL)
|
|
200
|
+
: await this._getSubstate(this.state?.parent, this.state?.key)
|
|
201
|
+
: await this._newState(undefined, this.config.key, this.rootDescriptor);
|
|
199
202
|
if (nextState !== undefined) {
|
|
200
203
|
this.state = nextState;
|
|
201
204
|
this.status = this.status & STATUS_EXIT ? STATUS_NEXT : STATUS_FIRST;
|
package/src/core/fsm-state.ts
CHANGED
|
@@ -38,15 +38,7 @@ export class FsmState extends FsmBaseClass {
|
|
|
38
38
|
this.key = key;
|
|
39
39
|
this.parent = parent;
|
|
40
40
|
this.descriptor = descriptor;
|
|
41
|
-
bindMethods(
|
|
42
|
-
this,
|
|
43
|
-
"onEnter",
|
|
44
|
-
"onExit",
|
|
45
|
-
"dump",
|
|
46
|
-
"restore",
|
|
47
|
-
"useData",
|
|
48
|
-
"onStateError",
|
|
49
|
-
);
|
|
41
|
+
bindMethods(this, "onEnter", "onExit", "dump", "restore", "onStateError");
|
|
50
42
|
}
|
|
51
43
|
|
|
52
44
|
onEnter(handler: FsmStateHandler) {
|
|
@@ -64,18 +56,6 @@ export class FsmState extends FsmBaseClass {
|
|
|
64
56
|
restore(handler: FsmStateDumpHandler) {
|
|
65
57
|
return this._addHandler("restore", handler, true);
|
|
66
58
|
}
|
|
67
|
-
getData<T>(key: string, recursive: boolean = true): T | undefined {
|
|
68
|
-
return (
|
|
69
|
-
(this.data[key] as T) ??
|
|
70
|
-
(recursive ? this.parent?.getData<T>(key, recursive) : undefined)
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
useData<T>(key: string) {
|
|
74
|
-
return [
|
|
75
|
-
(recursive: boolean = true) => this.getData<T>(key, recursive),
|
|
76
|
-
(value: T) => this.setData(key, value),
|
|
77
|
-
];
|
|
78
|
-
}
|
|
79
59
|
|
|
80
60
|
async _handleError(error: Error | unknown) {
|
|
81
61
|
await this._runHandler("onStateError", this, error);
|
package/src/core/index.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -0,0 +1,108 @@
|
|
|
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
|
+
}
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
5
|
-
export
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
export type { HandlerRegistry, StageHandler } from "./handler-registry.ts";
|
|
2
|
+
export { createHandlerRegistry, toStageHandlers } from "./handler-registry.ts";
|
|
3
|
+
export type { LauncherConfig, ProcessDef } from "./launcher.ts";
|
|
4
|
+
export { KEY_LAUNCH_PROCESS, launcher } from "./launcher.ts";
|
|
5
|
+
export type { ProcessHandle } from "./start-process.ts";
|
|
6
|
+
export {
|
|
7
|
+
getStateTransitions,
|
|
8
|
+
isStateTransitionEnabled,
|
|
9
|
+
KEY_DISPATCH,
|
|
10
|
+
KEY_EVENT,
|
|
11
|
+
KEY_STATES,
|
|
12
|
+
KEY_TERMINATE,
|
|
13
|
+
startFsmProcess,
|
|
14
|
+
startProcess,
|
|
15
|
+
} from "./start-process.ts";
|