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,89 @@
# `provider/audio-provider`
## Purpose
`<AudioProvider>` is the single React component that owns the audio
engine bundle for a React subtree. It constructs exactly one
`BrowserTimelineAdapter`, one `TimelineEngine`, one `Transport`, one
`ProviderBusRegistry`, and one `PluginRegistry`, publishes the
resolved bundle through `AudioReactContext`, and tears every one of
them down on unmount.
The provider is React 19 Strict Mode safe: under the development
double-invoke pattern (mount → unmount → re-mount) it never leaks an
engine. The async init pipeline is gated by a cancellation flag and an
`AbortController`; if the effect tears down before init resolves the
flag suppresses the publish step and any partially-constructed adapter
is force-disposed immediately.
## Features
- Strict-mode-safe async init with `AbortSignal` cancellation.
- Synchronous unmount path that disposes `adapter`, then `transport`,
then `buses` — each at most once.
- Zod-validated options (`AudioProviderOptions`) — invalid input
throws `ReactAudioError(REACT_AUDIO.INVALID_OPTIONS)` at construct
time.
- Auto-registers consumer-supplied plugin factories into the package
registry.
- Publishes `engineState` (`'pending' | 'ready' | 'error'`) for the
`useEngineState` hook and any consumer that wants to render a
loading state.
## Usage: Basic
```tsx
import { AudioProvider } from "@suaveplan/react-audio";
function App() {
return (
<AudioProvider>
<Workspace />
</AudioProvider>
);
}
```
## Usage: Advanced
```tsx
import { AudioProvider } from "@suaveplan/react-audio";
function App() {
return (
<AudioProvider
options={{
adapter: { sampleRate: 48000, latencyHint: "interactive" },
transport: { bpm: 128 },
plugins: [
{ pluginType: "delay", factory: () => makeDelay() },
],
sampleRateHz: 30,
}}
>
<Workspace />
</AudioProvider>
);
}
```
## API Reference
- `<AudioProvider options? children?>` — renders a context provider.
- `AudioProviderProps` — `{ options?: AudioProviderOptions, children?: ReactNode }`.
## Implementation Notes
The async init pipeline lives entirely inside `useEffect`'s async
IIFE so React's render path stays synchronous. A `PendingInit` value
carries the in-flight adapter, transport, and bus-registry references
so the cleanup callback can dispose anything already constructed if
the effect is torn down mid-init — this is the linchpin of the
Strict Mode guarantee. The `AbortController` is also passed to
`adapter.init({ signal })` so the engine package's own cancellable
work paths short-circuit. State updates (`setState`, `setEngineState`,
`setVersion`) are skipped when `cancelled` is true so a torn-down
effect cannot mutate React state. The teardown path is unconditional
and synchronous: every dispose is wrapped in nothing but a null
check, no try/catch — disposing a class that hasn't fully initialised
is the responsibility of the owning package's `dispose()` to handle.