@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.
Files changed (41) hide show
  1. package/README.md +112 -0
  2. package/bin/cli.js +1 -1
  3. package/dist/bin/node-runner-UJp8T_oP.d.ts +16 -0
  4. package/dist/bin/node-runner-UJp8T_oP.d.ts.map +1 -0
  5. package/dist/bin/node-runner.js +40 -0
  6. package/dist/bin/node-runner.js.map +1 -0
  7. package/dist/index-vTjnkbGW.d.ts +129 -0
  8. package/dist/index-vTjnkbGW.d.ts.map +1 -0
  9. package/dist/index.js +38 -779
  10. package/dist/index.js.map +1 -1
  11. package/dist/launcher-6VUDviTj.d.ts +48 -0
  12. package/dist/launcher-6VUDviTj.d.ts.map +1 -0
  13. package/dist/launcher-CASrMAOa.js +443 -0
  14. package/dist/launcher-CASrMAOa.js.map +1 -0
  15. package/package.json +8 -8
  16. package/src/bin/node-runner.ts +57 -0
  17. package/src/core/fsm-base-class.ts +1 -26
  18. package/src/core/fsm-process.ts +14 -11
  19. package/src/core/fsm-state.ts +1 -21
  20. package/src/core/index.ts +1 -1
  21. package/src/index.ts +0 -3
  22. package/src/orchestrator/handler-registry.ts +108 -0
  23. package/src/orchestrator/index.ts +15 -9
  24. package/src/orchestrator/launcher.ts +42 -141
  25. package/src/orchestrator/start-process.ts +141 -26
  26. package/src/utils/index.ts +0 -2
  27. package/src/utils/printer.ts +5 -11
  28. package/src/utils/tracer.ts +1 -1
  29. package/CHANGELOG.md +0 -59
  30. package/dist/index.d.ts +0 -335
  31. package/src/core/new-fsm-process.ts +0 -83
  32. package/src/orchestrator/constants.ts +0 -130
  33. package/src/orchestrator/is-generator.ts +0 -12
  34. package/src/orchestrator/process-config-manager.ts +0 -70
  35. package/src/orchestrator/resolve-module-refs.ts +0 -52
  36. package/src/orchestrator/start-node-processes.ts +0 -19
  37. package/src/orchestrator/start-processes.ts +0 -32
  38. package/src/orchestrator/types.ts +0 -34
  39. package/src/utils/handlers.ts +0 -35
  40. package/src/utils/process.ts +0 -26
  41. 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.36.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.2.2",
41
- "tsup": "^8.5.0",
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": "yarn test && tsup",
51
- "build": "yarn dist",
52
- "watch": "tsup --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": "yarn format && biome check . --fix --unsafe",
56
+ "fix": "pnpm format && biome check . --fix --unsafe",
57
57
  "test": "vitest --run",
58
58
  "test:watch": "vitest",
59
- "prepublish": "yarn dist"
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; // Function;
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) {
@@ -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._runHandlerParallel("onStateCreate", state);
164
+ await this._runHandler("onStateCreate", state);
165
165
  return state;
166
166
  }
167
167
 
168
- _getSubstate(parent: FsmState | undefined, prevStateKey: string | undefined) {
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;
@@ -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
@@ -1,5 +1,5 @@
1
+ export * from "./fsm-base-class.ts";
1
2
  export * from "./fsm-process.ts";
2
3
  export * from "./fsm-state.ts";
3
4
  export * from "./fsm-state-config.ts";
4
5
  export * from "./fsm-state-descriptor.ts";
5
- export * from "./new-fsm-process.ts";
package/src/index.ts CHANGED
@@ -1,6 +1,3 @@
1
1
  export * from "./core/index.ts";
2
2
  export * from "./orchestrator/index.ts";
3
3
  export * from "./utils/index.ts";
4
-
5
- import { startNodeProcesses } from "./orchestrator/start-node-processes.ts";
6
- export default startNodeProcesses;
@@ -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 * from "./constants.ts";
2
- export * from "./index.ts";
3
- export * from "./launcher.ts";
4
- export * from "./process-config-manager.ts";
5
- export * from "./resolve-module-refs.ts";
6
- export * from "./start-node-processes.ts";
7
- export * from "./start-process.ts";
8
- export * from "./start-processes.ts";
9
- export * from "./types.ts";
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";