API Reference

Everything below is exported from the package root:

import {
  FsmProcess, FsmState,
  startProcess, KEY_DISPATCH, KEY_TERMINATE, KEY_STATES, KEY_EVENT,
  getStateTransitions, isStateTransitionEnabled,
  setProcessTracer, setStateTracer, setProcessPrinter,
} from "@statewalker/fsm";

There are two entry points: the low-level FsmProcess engine and the higher-level startProcess runner built on top of it. Types are exported alongside their values.


Configuration

FsmStateConfig

The declarative shape you pass to FsmProcess or startProcess. One plain object describes an entire hierarchical machine, so it stays serializable and inspectable.

type FsmStateConfig = {
  key: string;
  transitions?: [from: string, event: string, to: string][];
  states?: FsmStateConfig[];
} & Record<string, unknown>;

The index signature lets you attach arbitrary metadata (a description, a label, anything). The engine ignores extra fields; they travel with the config for your own tooling. Named constants for the sentinels are exported too: STATE_INITIAL / STATE_FINAL (""), STATE_ANY / EVENT_ANY ("*"), EVENT_EMPTY ("").

Transition resolution. For a (state, event) pair the engine tries the most specific rule first, then falls back through the wildcards: (state, event)(*, event)(state, *)(*, *). If nothing matches, the state exits to its parent (as if the target were ""). Within a running machine, an event unhandled by the current leaf bubbles up the parent chain.


FsmProcess

The running machine — owner of the active-state stack and the traversal.

const process = new FsmProcess(config);

Properties

Methods

Callbacks

Each registration returns a disposer that unregisters it.


FsmState

One live node in the active-state stack. Created by the engine on entry, discarded on exit, so handlers can capture per-activation closures instead of sharing machine-wide state.

Properties

Callbacks

Each returns a disposer.

There is no getData / setData on FsmState. Per-state data that must survive a dump lives in the dump / restore handlers’ data bag; transient per-activation data lives in your handler closures.


startProcess

The ergonomic runner. Builds an FsmProcess, installs behaviour from one load callback, and binds the machine into a shared context.

const handle = await startProcess(context, config, load, startEvent = "");

It returns a ProcessHandle. startFsmProcess is an exported alias of startProcess.

StageHandler

type StageHandler<C> = (context: C) =>
  | void
  | (() => void | Promise<void>)                 // cleanup, registered as onExit
  | AsyncGenerator<string> | Generator<string>   // events to dispatch back
  | Promise<void | (() => void | Promise<void>)>;

A handler runs on entry with the shared context. Its return value wires up the rest:

Context keys

startProcess binds the machine into context under these exported keys, so any handler reaches the running machine uniformly:

ProcessHandle

interface ProcessHandle {
  shutdown(): Promise<void>;
  dump(...args): Promise<FsmProcessDump>;
  restore(dump, ...args): Promise<void>;
}

The caller’s remote control: stop and snapshot/rehydrate the machine without holding the underlying FsmProcess.


Transition queries

Read-only helpers over the graph — for a UI that needs to know which events can fire right now.


Tracing

See Tracing & Logging for the full guide.