Files
skills/plugins/document-navigator/docs/foundation/flow/fsm/README.md
admin 9bf56a806b Move docs/ package mirror into document-navigator plugin
Relocate the ~6k-file docs tree from the repo root into
plugins/document-navigator/docs/ so the plugin is self-contained and
portable. docs/superpowers/ (specs and plans) stays at the repo root.

Moved via cp+rm rather than git mv because directory rename() fails with
EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the
identical-content files at commit time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 01:10:00 +01:00

7.9 KiB

@suaveplan/fsm - Typed finite state machine with guards, actions, history tracking, hierarchical states, parallel regions, deferred events, and OTel instrumentation

Summary

Typed, generic finite state machine (FSM) for TypeScript. Ships a synchronous-by-default interpreter with guard predicates, ordered transition actions, context threading, entry/exit lifecycle hooks, history recording, and OTel instrumentation. Zero peer dependencies beyond @suaveplan/error, @suaveplan/events, and @suaveplan/types.

Documentation tree

  • interpreter — (no submodules)
  • errors — (no submodules)
  • context-helpers — Provides ergonomic factory helpers for building context-update and side-effect actions that can be slotted directly int…
  • definition-validator — Validates an FSMDefinition object before the interpreter begins executing. Early structural validation prevents subtl…
- [fsm-interpreter](./src/core/interpreter/fsm-interpreter.md) — _(no submodules)_- [errors](./src/errors/fsm-error.md) — _(no submodules)_- [fsm-error](./src/errors/fsm-error.md) — _(no submodules)_- [context-helpers](./src/utils/context-helpers.md) — Provides ergonomic factory helpers for building context-update and side-effect actions that can be slotted directly int…
  • definition-validator — Validates an FSMDefinition object before the interpreter begins executing. Early structural validation prevents subtl…
  • fsm-interpreter — (no submodules)- errors — (no submodules)- fsm-error — (no submodules)- context-helpers — Provides ergonomic factory helpers for building context-update and side-effect actions that can be slotted directly int…
  • definition-validator — Validates an FSMDefinition object before the interpreter begins executing. Early structural validation prevents subtl…
  • fsm-interpreter — (no submodules)- errors — (no submodules)- fsm-error — (no submodules)- context-helpers — Provides ergonomic factory helpers for building context-update and side-effect actions that can be slotted directly int…
  • definition-validator — Validates an FSMDefinition object before the interpreter begins executing. Early structural validation prevents subtl…
  • fsm-interpreter — (no submodules)- errors — (no submodules) - fsm-error — (no submodules)- context-helpers — Provides ergonomic factory helpers for building context-update and side-effect actions that can be slotted directly int…
  • definition-validator — Validates an FSMDefinition object before the interpreter begins executing. Early structural validation prevents subtl…

Install

bun add @suaveplan/fsm

Quick Start

import { createMachine, assign } from '@suaveplan/fsm';

interface TrafficCtx { count: number }

const traffic = createMachine<TrafficCtx>({
  id: 'traffic',
  initial: 'red',
  context: { count: 0 },
  states: {
    red:    { on: { NEXT: { target: 'green' } } },
    green:  { on: { NEXT: { target: 'yellow' } } },
    yellow: {
      on: {
        NEXT: {
          target: 'red',
          actions: [assign<TrafficCtx>(ctx => ({ count: ctx.count + 1 }))],
        },
      },
    },
  },
});

traffic.send('NEXT'); // → green
traffic.send('NEXT'); // → yellow
const state = traffic.send('NEXT'); // → red
console.log(state.context.count); // 1
console.log(state.history.length); // 3

Modules

Module Path Description
Root @suaveplan/fsm createMachine, types, assign, pure, validateDefinition
Interpreter @suaveplan/fsm/interpreter FSMInterpreter, createMachine
Errors @suaveplan/fsm/errors FSMError, FSM_CODES

Core Concepts

createMachine(definition)

Factory function returning an FSMInterpreter instance. The definition is validated eagerly — malformed definitions throw FSMError(INVALID_DEFINITION) before any events are processed.

interface FSMDefinition<TContext> {
  id?: string;         // used in OTel metric labels
  initial: string;     // must exist in states
  context: TContext;   // initial extended context
  states: Record<string, StateDefinition<TContext>>;
}

Guards

Guard functions on a transition prevent it from firing when they return false. If multiple transitions are declared for the same event (as an array), the first one whose guard returns true is taken.

states: {
  locked: {
    on: {
      UNLOCK: [
        { target: 'open',   guard: (ctx, evt) => evt.payload === ctx.pin },
        { target: 'locked', guard: () => true },   // fallthrough (stay locked)
      ],
    },
  },
}

Actions

Transition actions are called with the accumulated context. Returning a new context replaces it; returning void leaves it unchanged.

import { assign, pure } from '@suaveplan/fsm';

const bump   = assign<Ctx>(ctx => ({ ...ctx, count: ctx.count + 1 }));
const logEvt = pure<Ctx>((ctx, evt) => console.log(evt.type, ctx));

Entry / Exit lifecycle

states: {
  loading: {
    entry: ctx => ({ ...ctx, loading: true }),
    exit:  ctx => ({ ...ctx, loading: false }),
    on: { DONE: { target: 'idle' } },
  },
}

Final states

Entering a state with type: 'final' sets state.done = true.

Subscriptions

const unsub = machine.onTransition(state => {
  console.log('now in', state.value, 'done?', state.done);
});
// later:
unsub();

can() and matches()

machine.can('NEXT');      // true if the event is declared in current state
machine.matches('green'); // true if currently in 'green'

reset()

Resets to initial state, original context, and clears history.

OTel Metrics

Name Type Attributes
fsm.transition.total Counter id, from, to, event
fsm.transition.duration_ms Histogram id, from, to

Error Codes

Code Meaning
FSM.INVALID_DEFINITION Structural error in definition (missing initial, unknown target, etc.)
FSM.INVALID_TRANSITION No matching transition for event in current state
FSM.INVALID_STATE Current state not found in definition
FSM.GUARD_FAILED All guards for an event returned false
FSM.ACTION_ERROR An action or lifecycle function threw

Module Docs