Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
# connection-fsm
## Purpose
Builds the controller connection-state machine consumed by every
`MidiControllerPlugin`. The FSM models the lifecycle of a hardware
controller's MIDI access: a fresh controller starts `disconnected`, moves
through `connecting`, settles in `connected`, and falls into `error` on
any access failure or rejected transition. Because the FSM is driven from
both the adapter (real connect/disconnect) and from `handleStateChange`
(hot-plug events from `navigator.requestMIDIAccess().onstatechange`-shaped
sources), centralising the rules here keeps every device subclass
ignorant of state-machine details.
## Features
- `createConnectionMachine()` returns an `@suaveplan/fsm` interpreter with
the four canonical states pre-wired.
- `sendOrThrow(machine, event)` converts the FSM's underlying
`INVALID_TRANSITION` into a domain-stable
`AudioControllersError("AUDIO_CONTROLLERS.INVALID_STATE")` with the
offending `from` and `event` carried in `context`.
- Transition table mirrors the spec exactly: `DISCONNECT`, `CONNECT`,
`CONNECTED`, `ERROR`, `RESET`.
## Usage: Basic
```ts
import { createConnectionMachine, sendOrThrow } from "@suaveplan/audio-controllers";
const m = createConnectionMachine();
sendOrThrow(m, "CONNECT");
sendOrThrow(m, "CONNECTED");
```
## Usage: Advanced
```ts
const m = createConnectionMachine();
m.onTransition((state) => log.debug({ value: state.value }, "controller-fsm"));
try {
sendOrThrow(m, "CONNECTED"); // illegal from `disconnected`
} catch (err) {
if (err instanceof AudioControllersError) {
expect(err.code).toBe(AUDIO_CONTROLLERS_CODES.INVALID_STATE);
}
}
```
## API Reference
- `createConnectionMachine()` → `FSMInterpreter<{ lastError?: string }>`.
- `sendOrThrow(machine, event)` → `void`.
- `ConnectionMachine` — re-exported interpreter type alias.
## Implementation Notes
A local `TRANSITIONS` table is consulted before delegating to the FSM's
own dispatcher so the package surface throws an `AUDIO_CONTROLLERS.*`
error rather than the raw `FSM.INVALID_TRANSITION` code. The state
graph deliberately omits transitions out of `error` other than `RESET`
to force callers to explicitly acknowledge recovery — accidental
re-`CONNECT` from `error` would mask the original failure.