Skip to content

BehavioralFsm

Defined in: behavioral-fsm.ts:53

Defines FSM behavior (states + transitions) while tracking per-client state in a WeakMap. A single BehavioralFsm instance can drive any number of independent client objects simultaneously — each gets its own state, deferred queue, and lifecycle.

Prefer createBehavioralFsm() over constructing this directly — the factory infers all generic parameters from the config object.

All public methods silently no-op after dispose() is called.

TClient extends object

The client object type. Must be an object (non-primitive) so it can serve as a WeakMap key.

TStateNames extends string

String literal union of valid state names.

TInputNames extends string

String literal union of valid input names.

TBubbles extends string = never

String literal union of inputs this FSM declares via bubbles. Type-only — carried so BubblesOfInstance can extract it from a constructed instance; nothing at runtime reads this generic.

new BehavioralFsm<TClient, TStateNames, TInputNames, TBubbles>(config): BehavioralFsm<TClient, TStateNames, TInputNames, TBubbles>

Defined in: behavioral-fsm.ts:80

FsmConfig<TClient, Record<string, Record<string, unknown>>>

BehavioralFsm<TClient, TStateNames, TInputNames, TBubbles>

readonly [MACHINA_TYPE]: "BehavioralFsm"

Defined in: behavioral-fsm.ts:63


readonly id: string

Defined in: behavioral-fsm.ts:60


readonly initialState: TStateNames

Defined in: behavioral-fsm.ts:61


readonly states: Record<string, Record<string, unknown>>

Defined in: behavioral-fsm.ts:67

canHandle(client, inputName): boolean

Defined in: behavioral-fsm.ts:140

Returns true if the client’s current state has a handler for inputName (or a catch-all "*" handler), or if the current state’s _child chain can handle it — the check recurses to the same depth handle()’s delegation can actually reach, so a grandchild-only input answers true from the root. Does NOT initialize the client — no _onEnter, no events, no side effects. Unseen clients are treated as if they were already in initialState. Returns false when disposed.

TClient

string

boolean


compositeState(client): string

Defined in: behavioral-fsm.ts:284

Returns the client’s state as a dot-delimited path including any active child FSM states (e.g. "active.connecting.retrying"). Returns just the current state name when no child is active. Returns "" for clients that have never been initialized (unlike currentState() which returns undefined).

TClient

string


currentState(client): TStateNames | undefined

Defined in: behavioral-fsm.ts:178

Returns the client’s current state, or undefined if the client has never been initialized (i.e. handle(), transition(), or reset() have never been called for it). Does NOT trigger initialization.

TClient

TStateNames | undefined


dispose(options?): void

Defined in: behavioral-fsm.ts:664

Permanently shut down this FSM. Irreversible — all subsequent method calls become silent no-ops. Tears down child subscriptions, clears all listeners, and cascades disposal to child FSMs (unless preserveChildren is set). The same child appearing in multiple states is disposed once.

DisposeOptions

void


emit(eventName, data?): void

Defined in: behavioral-fsm.ts:649

Emit a custom event through the FSM. Built-in lifecycle events are emitted automatically — this is for user-defined events from handlers. No-ops when disposed.

string

unknown

void


handle(client, inputName, …args): void

Defined in: behavioral-fsm.ts:102

Dispatch an input to the given client’s current state handler.

Delegation order: if the current state has a _child FSM that can handle the input, it is dispatched there. If the child emits nohandler, the input bubbles up to this FSM’s local handler. If no handler exists here either, nohandler is emitted on this FSM’s emitter.

No-ops silently when disposed.

TClient

TInputNames

unknown[]

void


on<K>(eventName, callback): Subscription

Defined in: behavioral-fsm.ts:632

Subscribe to a built-in lifecycle event or the wildcard.

Named overload: typed payload includes { client: TClient } so you can identify which client the event pertains to. Wildcard ("*") receives (eventName, data) for every event. Returns a no-op Subscription when disposed.

K extends "transitioning" | "transitioned" | "handling" | "handled" | "nohandler" | "invalidstate" | "deferred"

K

(data) => void

Subscription

on(eventName, callback): Subscription

Defined in: behavioral-fsm.ts:636

Subscribe to a built-in lifecycle event or the wildcard.

Named overload: typed payload includes { client: TClient } so you can identify which client the event pertains to. Wildcard ("*") receives (eventName, data) for every event. Returns a no-op Subscription when disposed.

"*"

(eventName, data) => void

Subscription


rehydrate(client, compositeState): void

Defined in: behavioral-fsm.ts:323

Silently place client at compositeState with no lifecycle activity — no _onEnter, no _onExit, no transitioning/transitioned events. Designed to work with compositeState(), which produces the dot-path string that the string form consumes.

The snapshot form (pass a ClientSnapshot from dehydrate()) additionally requeues each level’s pending deferred inputs, so a client resumes with the same replay-on-next-transition behavior it had when dehydrated. Both forms validate the ENTIRE hierarchy before writing anything — a throw at any level leaves every level, including this one, unwritten.

Throws synchronously for unknown state names, missing _child at an inner level, or Fsm children in the hierarchy (Fsm owns its own context; nothing per-client to rehydrate there).

No-ops silently when disposed.

TClient

string

void

rehydrate(client, snapshot): void

Defined in: behavioral-fsm.ts:324

Silently place client at compositeState with no lifecycle activity — no _onEnter, no _onExit, no transitioning/transitioned events. Designed to work with compositeState(), which produces the dot-path string that the string form consumes.

The snapshot form (pass a ClientSnapshot from dehydrate()) additionally requeues each level’s pending deferred inputs, so a client resumes with the same replay-on-next-transition behavior it had when dehydrated. Both forms validate the ENTIRE hierarchy before writing anything — a throw at any level leaves every level, including this one, unwritten.

Throws synchronously for unknown state names, missing _child at an inner level, or Fsm children in the hierarchy (Fsm owns its own context; nothing per-client to rehydrate there).

No-ops silently when disposed.

TClient

ClientSnapshot

void


reset(client): void

Defined in: behavioral-fsm.ts:166

Transition the client back to initialState, firing _onEnter and lifecycle events as if entering it fresh. No-ops when disposed.

TClient

void


transition(client, toState): void

Defined in: behavioral-fsm.ts:196

Directly transition client to toState, running the full lifecycle: _onExit for the current state → transitioning event → update state → _onEnter for new state → transitioned event → child reset → deferred queue replay → bounce (if _onEnter returned a state name).

Same-state transitions are silently ignored. Transitions to unknown state names emit invalidstate instead of throwing. Throws if the transition depth exceeds MAX_TRANSITION_DEPTH (likely an _onEnter → transition loop).

No-ops when disposed.

TClient

TStateNames

void