2.2 KiB
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/fsminterpreter with the four canonical states pre-wired.sendOrThrow(machine, event)converts the FSM's underlyingINVALID_TRANSITIONinto a domain-stableAudioControllersError("AUDIO_CONTROLLERS.INVALID_STATE")with the offendingfromandeventcarried incontext.- Transition table mirrors the spec exactly:
DISCONNECT,CONNECT,CONNECTED,ERROR,RESET.
Usage: Basic
import { createConnectionMachine, sendOrThrow } from "@suaveplan/audio-controllers";
const m = createConnectionMachine();
sendOrThrow(m, "CONNECT");
sendOrThrow(m, "CONNECTED");
Usage: Advanced
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.