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,61 @@
# `schemas`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The `compressor.schemas` module owns the Zod boundary guards for `CompressorPlugin` — both the constructor options and the plugin-specific preset payload. Per the package's "Zod boundary validation" requirement, every public options object on every plugin SHALL be validated against a sibling `*.schemas.ts` before the plugin touches any state, and these schemas are how `CompressorPlugin` keeps that promise. A malformed `initialRatio`, an out-of-range `initialAttackMs`, or a missing field on `applyPreset` data all surface as `AUDIO_FX.INVALID_OPTIONS` / `AUDIO_FX.INVALID_PRESET` with the Zod issue list in `context.issues`.
Keeping the schemas in a sibling file (rather than inline in `compressor.ts`) is the package-wide convention; consumers can import the schemas directly and run their own `safeParse` against UI input, surfacing field-level errors before the plugin instance exists.
## Features
- `CompressorPluginOptionsSchema` — every constructor field is optional but, when present, must be finite and inside the per-parameter `[min, max]` range that the plugin's `parameters` declare (`threshold ∈ [−60, 0]`, `knee ∈ [0, 24]`, `ratio ∈ [1, 20]`, `attack ∈ [0.1, 100] ms`, `release ∈ [1, 1000] ms`).
- `CompressorPresetDataSchema` — the plugin-specific preset payload: just `midSide` and `sidechainEnabled` booleans. The five continuous parameters round-trip through the universal `parameters` map on the `PresetSnapshot` envelope, so they do not need to appear here.
## Usage: Basic Example
```ts
import { CompressorPluginOptionsSchema } from "@suaveplan/audio-fx-plugin-compressor";
const result = CompressorPluginOptionsSchema.safeParse(formValues);
if (!result.success) {
return showFieldErrors(result.error.issues);
}
const compressor = new CompressorPlugin(result.data);
```
## Usage: Advanced Example
```ts
import {
CompressorPluginOptionsSchema,
CompressorPresetDataSchema,
} from "@suaveplan/audio-fx-plugin-compressor";
// Server-side validation of an untrusted preset payload before it
// hits the plugin's applyPreset (which would throw with the same
// code, but the host wants to surface a friendlier error first).
const envelope = parseChainSnapshot(payload); // base envelope check
for (const entry of envelope.plugins.filter((p) => p.pluginType === "compressor")) {
const data = CompressorPresetDataSchema.parse(entry.preset.data);
if (data.sidechainEnabled && !allowSidechain) {
return reject("Sidechain compression is disabled for this tier.");
}
}
```
## API Reference
- `CompressorPluginOptionsSchema` — `z.ZodObject` matching `CompressorPluginOptions`. Every field is optional and ranged.
- `CompressorPresetDataSchema` — `z.ZodObject` matching `CompressorPresetData`. Both fields required.
## Implementation Notes
The schemas use `.finite()` so `NaN` and `±Infinity` are rejected at the boundary; the envelope follower's `exp(-1 / (τ · sr))` calculation would otherwise propagate non-finite alphas into the smoothing state and contaminate every subsequent block. Time constants use `.min(0.1)` for attack and `.min(1)` for release — values smaller than this break the one-pole smoothing assumption (alpha approaches 1 from below) and produce numerical artefacts in single-precision float.
Ratio is `.min(1)` (rather than `.positive()`) because a ratio below 1 would expand rather than compress, which the plugin's curve does not model. A future expander plugin would live as a separate plugin with its own schema. The maxima (`.max(20)` for ratio, `.max(24)` for knee, etc.) are the values the plugin's parameter declarations advertise — keeping the schema and the declaration in sync means consumers can safely take the parameter's `min` / `max` as authoritative without re-deriving them from the schema.
The preset data deliberately omits the continuous parameters because they live in `PresetSnapshot.parameters` (the universal map). Duplicating them here would risk drift if a future refactor changes one source but not the other; keeping the data slim makes the preset round-trip obviously correct.