first commit
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user