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>
This commit is contained in:
173
plugins/document-navigator/docs/foundation/flow/fsm/README.md
Normal file
173
plugins/document-navigator/docs/foundation/flow/fsm/README.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# @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`.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [interpreter](./src/core/interpreter/fsm-interpreter.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [fsm-interpreter](./src/core/interpreter/fsm-interpreter.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [errors](./src/errors/fsm-error.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [fsm-error](./src/errors/fsm-error.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [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](./src/utils/definition-validator.md) — Validates an `FSMDefinition` object before the interpreter begins executing. Early structural validation prevents subtl…
|
||||
<!-- end:auto-docs --> - [fsm-interpreter](./src/core/interpreter/fsm-interpreter.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/fsm-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [fsm-error](./src/errors/fsm-error.md) — <!-- begin:auto-docs --> _(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](./src/utils/definition-validator.md) — Validates an `FSMDefinition` object before the interpreter begins executing. Early structural validation prevents subtl…
|
||||
- [fsm-interpreter](./src/core/interpreter/fsm-interpreter.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/fsm-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [fsm-error](./src/errors/fsm-error.md) — <!-- begin:auto-docs --> _(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](./src/utils/definition-validator.md) — Validates an `FSMDefinition` object before the interpreter begins executing. Early structural validation prevents subtl…
|
||||
- [fsm-interpreter](./src/core/interpreter/fsm-interpreter.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/fsm-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [fsm-error](./src/errors/fsm-error.md) — <!-- begin:auto-docs --> _(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](./src/utils/definition-validator.md) — Validates an `FSMDefinition` object before the interpreter begins executing. Early structural validation prevents subtl…
|
||||
- [fsm-interpreter](./src/core/interpreter/fsm-interpreter.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/fsm-error.md) — <!-- begin:auto-docs --> _(no submodules)_ - [fsm-error](./src/errors/fsm-error.md) — <!-- begin:auto-docs --> _(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](./src/utils/definition-validator.md) — Validates an `FSMDefinition` object before the interpreter begins executing. Early structural validation prevents subtl…
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
bun add @suaveplan/fsm
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```ts
|
||||
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.
|
||||
|
||||
```ts
|
||||
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.
|
||||
|
||||
```ts
|
||||
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.
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
const unsub = machine.onTransition(state => {
|
||||
console.log('now in', state.value, 'done?', state.done);
|
||||
});
|
||||
// later:
|
||||
unsub();
|
||||
```
|
||||
|
||||
### `can()` and `matches()`
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
- [`src/core/interpreter/fsm-interpreter.md`](src/core/interpreter/fsm-interpreter.md)
|
||||
- [`src/errors/fsm-error.md`](src/errors/fsm-error.md)
|
||||
- [`src/utils/definition-validator.md`](src/utils/definition-validator.md)
|
||||
- [`src/utils/context-helpers.md`](src/utils/context-helpers.md)
|
||||
Reference in New Issue
Block a user