first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View 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.