Class-based HFSM with nested states, event-driven transitions, lifecycle hooks, and dump/restore serialization.
Declarative state tree definition:
const config: FsmStateConfig = {
key: "Main",
transitions: [
["", "start", "Active"], // initial → Active
["Active", "done", ""], // Active → final
["*", "reset", "Active"], // any → Active
],
states: [
{ key: "Active", states: [
{ key: "Step1" },
{ key: "Step2" },
], transitions: [
["", "", "Step1"], // initial → Step1 (eventless)
["Step1", "next", "Step2"],
]},
],
};Runtime state machine. Maintains a state stack (root → ... → leaf), dispatches events, manages lifecycle.
dispatch(event)— trigger a transitionshutdown()— exit all states gracefullystate— current leaf statestatus— bitmask tracking enter/exit cycleonStateCreate(handler)— called for every new state (primary extension point)dump()/restore(data)— serialization hooks
Individual node in the state hierarchy.
key— state nameparent— parent stateonEnter(handler)— run when enteringonExit(handler)— run when exiting (reverse order)onStateError(handler)— error handlingdump()/restore(data)— per-state serialization
High-level entry point: creates FsmProcess, wires handlers, binds FSM into context.
Context keys bound:
fsm:dispatch— dispatch functionfsm:terminate— shutdown functionfsm:states— current state stackfsm:event— last event
load(stateKey) returns handler(s) for each state. Handlers can return:
void— no cleanupFunction— registered as onExit cleanupAsyncGenerator— yielded events are dispatched to FSM
Convention-based handler discovery via createHandlerRegistry():
const registry = createHandlerRegistry();
registry.addConfig("MyProcess", config);
registry.addHandlers("MyProcess", { "Active": activeHandler, "Step1": step1Handler });
const load = registry.getLoader("MyProcess");Multi-process launcher with strict types:
interface LauncherConfig {
processes: ProcessDef[];
start?: string[];
context?: (parent: Record<string, unknown>) => Record<string, unknown>;
}
interface ProcessDef {
name: string;
config: FsmStateConfig;
handlers?: (StageHandler | Record<string, StageHandler | StageHandler[]>)[];
start?: boolean;
}printer(process)— log state transitions to consoletracer(process)— collect transition trace for testing
Removed in 0.35:
FsmBaseClass.data,.setData(),.getData()— use closures or context insteadFsmState.getData(key, recursive),.useData(key)— use closuresFsmBaseClass._runHandlerParallel()— handlers run sequentially nownewFsmProcess()— usestartProcess()from orchestratorutils/handlers.ts(addSubstateHandlers,callStateHandlers) — use HandlerRegistryutils/process.ts— usestartProcess()directly