first commit
This commit is contained in:
106
docs/foundation/flow/fsm/src/errors/fsm-error.md
Normal file
106
docs/foundation/flow/fsm/src/errors/fsm-error.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# fsm-error
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Defines the stable error-code registry and typed error class for the `@suaveplan/fsm` package. Every error thrown by the FSM interpreter, definition validator, and context helpers extends `FSMError`, giving callers a single `instanceof` check and a machine-readable `code` property drawn from `FSM_CODES`.
|
||||
|
||||
The registry is built with `defineCodes` from `@suaveplan/error`, which guarantees that each code string is unique across the entire `@suaveplan/*` ecosystem and can be compared with `===` without string literals.
|
||||
|
||||
## Features
|
||||
|
||||
- Five stable error codes covering all failure scenarios: `INVALID_DEFINITION`, `INVALID_TRANSITION`, `INVALID_STATE`, `GUARD_FAILED`, `ACTION_ERROR`
|
||||
- `FSMError` extends `SuaveplanError<FSMCode>` for narrow discriminated-union typing
|
||||
- Supports optional `cause` and `context` metadata on every error instance
|
||||
- Fully compatible with the `@suaveplan/error` wire-format serialization
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { FSMError, FSM_CODES } from '@suaveplan/fsm/errors';
|
||||
|
||||
try {
|
||||
machine.send('UNKNOWN_EVENT');
|
||||
} catch (err) {
|
||||
if (err instanceof FSMError) {
|
||||
console.error(err.code, err.message);
|
||||
// "FSM.INVALID_TRANSITION" "No valid transition for event in current state"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { FSMError, FSM_CODES } from '@suaveplan/fsm/errors';
|
||||
|
||||
function handleFSMError(err: unknown): void {
|
||||
if (!(err instanceof FSMError)) throw err;
|
||||
|
||||
switch (err.code) {
|
||||
case FSM_CODES.INVALID_DEFINITION:
|
||||
console.error('Machine definition is malformed:', err.message);
|
||||
break;
|
||||
case FSM_CODES.INVALID_TRANSITION:
|
||||
console.warn('Event not handled in current state');
|
||||
break;
|
||||
case FSM_CODES.GUARD_FAILED:
|
||||
console.info('Transition blocked by guard');
|
||||
break;
|
||||
case FSM_CODES.ACTION_ERROR:
|
||||
console.error('Action threw during transition:', err.cause);
|
||||
break;
|
||||
default:
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `FSM_CODES`
|
||||
|
||||
```ts
|
||||
const FSM_CODES: {
|
||||
INVALID_DEFINITION: 'FSM.INVALID_DEFINITION';
|
||||
INVALID_TRANSITION: 'FSM.INVALID_TRANSITION';
|
||||
INVALID_STATE: 'FSM.INVALID_STATE';
|
||||
GUARD_FAILED: 'FSM.GUARD_FAILED';
|
||||
ACTION_ERROR: 'FSM.ACTION_ERROR';
|
||||
}
|
||||
```
|
||||
|
||||
Stable string constants for each FSM error domain. Use these instead of raw string comparisons to benefit from TypeScript narrowing.
|
||||
|
||||
### `type FSMCode`
|
||||
|
||||
Union type of all values in `FSM_CODES`. Used as the generic parameter of `FSMError`.
|
||||
|
||||
### `class FSMError`
|
||||
|
||||
```ts
|
||||
class FSMError extends SuaveplanError<FSMCode> {
|
||||
constructor(
|
||||
code: FSMCode,
|
||||
message: string,
|
||||
options?: { cause?: Error; context?: Record<string, unknown> }
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `code` — one of the `FSM_CODES` values; stored as `this.code`
|
||||
- `message` — human-readable description of the failure
|
||||
- `options.cause` — underlying error that triggered this one (chained)
|
||||
- `options.context` — arbitrary key/value metadata (state name, event type, etc.)
|
||||
|
||||
**Throws:** Never (constructor only).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The `defineCodes` helper from `@suaveplan/error` namespaces all codes under `"FSM."` to prevent collisions with codes from other packages sharing the same registry. The `as const` assertion on the descriptor object ensures the resulting type is a fully-discriminated union rather than `string`.
|
||||
|
||||
The `noUselessConstructor` biome lint suppression is intentional: the constructor is required to widen the `protected` base constructor to `public`, which is a documented pattern in this repository for all `SuaveplanError` subclasses.
|
||||
Reference in New Issue
Block a user