first commit
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
# `schemas`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
The `eq.schemas` module owns every Zod schema that guards `EQPlugin`'s boundaries: construction options, single-band records, and preset data payloads. Per the spec's "Zod boundary validation" requirement, every public options object on every plugin SHALL be validated against a sibling `*.schemas.ts` before any state is mutated, and `EQPlugin` is no exception — `setBand`, `setBandType`, constructor options, and `applyPreset` all funnel their input through these schemas and throw `AUDIO_FX.INVALID_OPTIONS` or `AUDIO_FX.INVALID_PRESET` with the Zod issue list in `context.issues` on failure.
|
||||
|
||||
Keeping the schemas in a sibling file (rather than inline in `eq.ts`) is the project-wide convention: external consumers can import the schema, run their own `safeParse` against UI input, and surface field-level errors before the plugin sees the data.
|
||||
|
||||
## Features
|
||||
|
||||
- `EQ_BAND_COUNT` — the fixed 8-band count enforced by both `EQPluginOptionsSchema` and `EQPresetDataSchema`; preset payloads with anything other than exactly eight bands are rejected at the boundary so the plugin's per-block inner loop can hoist the count as a literal.
|
||||
- `EQBandTypeSchema` — discriminated enum over the five shipped response shapes.
|
||||
- `EQBandSchema` — full per-band record with `frequency > 0`, `gain` finite, `q > 0`, and `type` from the band-type enum.
|
||||
- `EQPluginOptionsSchema` — constructor options; the `initialBands` array is optional but, when present, must have exactly `EQ_BAND_COUNT` entries.
|
||||
- `EQPresetDataSchema` — plugin-specific preset payload; same length constraint on `bands`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { EQBandSchema, EQPluginOptionsSchema } from "@suaveplan/audio-fx-plugin-eq";
|
||||
|
||||
const result = EQBandSchema.safeParse(userInput);
|
||||
if (!result.success) {
|
||||
return showFieldErrors(result.error.issues);
|
||||
}
|
||||
const band = result.data;
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
EQ_BAND_COUNT,
|
||||
EQBandSchema,
|
||||
EQPluginOptionsSchema,
|
||||
EQPresetDataSchema,
|
||||
} from "@suaveplan/audio-fx-plugin-eq";
|
||||
|
||||
// 1. Bulk-validate a UI form's eight rows before constructing the plugin.
|
||||
const rows = readUIForm();
|
||||
if (rows.length !== EQ_BAND_COUNT) {
|
||||
return showError(`EQ requires exactly ${EQ_BAND_COUNT} bands; got ${rows.length}.`);
|
||||
}
|
||||
const validated = EQPluginOptionsSchema.parse({ initialBands: rows });
|
||||
|
||||
// 2. Re-validate a deserialised preset against EQPresetDataSchema before
|
||||
// handing it to the plugin's applyPreset. This duplicates the plugin's
|
||||
// own check, but lets the host surface user-friendly errors *before*
|
||||
// a plugin instance exists.
|
||||
const preset = EQPresetDataSchema.parse(JSON.parse(savedJson));
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `EQ_BAND_COUNT: number` — exported constant; always `8`.
|
||||
- `EQBandTypeSchema` — `z.ZodEnum<['peaking', 'low-shelf', 'high-shelf', 'low-pass', 'high-pass']>`.
|
||||
- `EQBandSchema` — `z.ZodObject` matching `EQBand`. Rejects non-positive `frequency` / `q`, non-finite values, and unknown `type` strings.
|
||||
- `EQPluginOptionsSchema` — `z.ZodObject` matching `EQPluginOptions`. `initialBands` is optional; when present, length must equal `EQ_BAND_COUNT`.
|
||||
- `EQPresetDataSchema` — `z.ZodObject` matching `EQPresetData`. `bands` length must equal `EQ_BAND_COUNT`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The schemas use `.finite()` on numeric fields so `NaN` and `±Infinity` are rejected at the boundary; the biquad coefficient formulas would propagate non-finite values into IIR state otherwise and contaminate every subsequent block. Q is `.positive()` rather than `.nonnegative()` because the cookbook formulas divide by `Q` in the `alpha` calculation; allowing `Q === 0` would produce `Infinity` coefficients.
|
||||
|
||||
`EQBandSchema` does not validate `frequency` against the Nyquist limit because the plugin instance does not yet know its target `sampleRate` at construction time — the engine's `setup()` call carries the rate. Out-of-Nyquist frequencies still produce numerically stable (if musically meaningless) coefficients, and the cost of a per-band check at every coefficient recompute is not worth the protection.
|
||||
|
||||
The schemas are exported from the package's `./eq` subpath so consumers can reuse them in form validation, server-side payload guards, and integration tests without pulling in the full plugin runtime.
|
||||
Reference in New Issue
Block a user