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,70 @@
# `hooks/use-provider-state`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Internal helpers shared across every public hook. They centralise the
two structured-error throws every engine-dependent hook must perform:
`REACT_AUDIO.NO_PROVIDER` when the consumer renders outside an
`<AudioProvider>` ancestor, and `REACT_AUDIO.PROVIDER_PENDING` when
the engine has not yet finished its async init. Keeping the throw
logic in one file means there is exactly one place to audit the error
contract.
## Features
- `useProviderStateOrThrow()` — returns the resolved `ProviderState`
or throws.
- `useAudioContextOrThrow()` — returns the raw `AudioContextValue`
(without the pending check) for hooks like `useEngineState` that
need to *report* the lifecycle phase rather than throw on it.
## Usage: Basic
```ts
import { useProviderStateOrThrow } from "@suaveplan/react-audio";
function MyHook() {
const state = useProviderStateOrThrow();
return state.transport;
}
```
## Usage: Advanced
```ts
import { useAudioContextOrThrow } from "@suaveplan/react-audio";
function useReadyOrLoading(): "ready" | "loading" {
const ctx = useAudioContextOrThrow();
return ctx.engineState === "ready" ? "ready" : "loading";
}
```
## API Reference
- `useProviderStateOrThrow(): ProviderState` — throws
`REACT_AUDIO.NO_PROVIDER` outside provider; throws
`REACT_AUDIO.PROVIDER_PENDING` while engine init is pending.
- `useAudioContextOrThrow(): AudioContextValue` — throws
`REACT_AUDIO.NO_PROVIDER` outside provider.
## Implementation Notes
`useContext` returns `null` as the "no provider" sentinel because the
context's default value is `null`. The two helpers use a single
discriminator check (`ctx === null`) for that path so the throw site
is canonical. `useProviderStateOrThrow` additionally checks for
`ctx.state === null` (the async init window) and throws
`PROVIDER_PENDING` so engine-dependent hooks fail fast — consumers
typically gate on `useEngineState() === 'ready'` before rendering
those hooks' subtrees. The split into two helpers exists so
`useEngineState` itself can read the phase without throwing on it —
otherwise the consumer would have no way to discover "we're still
initialising" without crashing the component tree. Both helpers
register exactly one React hook (`useContext`) so any downstream hook
can register additional `useCallback` / `useSyncExternalStore` calls
unconditionally and stay compliant with the Rules of Hooks.