Files
2026-05-27 23:29:53 +01:00

3.4 KiB
Raw Permalink Blame History

Add @suaveplan/audio-ir-core

Package: @suaveplan/audio-ir-core Status: PROPOSED Type: new-package

Why

The IR ecosystem (audio-ir-loader, audio-ir-{mic,speaker,room}-plugin-<name> packages) needs a single foundation that owns the IR contract, manifest schema, and in-memory registry. Without it, every IR content package would reinvent the entry shape, the registry collision rule, and the ID-derivation convention — guaranteed drift. This package is the foundation every sibling depends on.

What changes

  • ADDED: ImpulseResponse type — { id: string; channelData: readonly Float32Array[]; channels: number; sampleRate: number; lengthFrames: number; durationSec: number; gainDb: number; family: "mic" | "speaker" | "room"; manifest: IrManifestEntry }. Frozen, validated.
  • ADDED: IrManifestEntry Zod schema — { id, family, uri, channels, sampleRate, gainDb, license, source }. id matches /^[a-z][a-z0-9-]*$/ (no consecutive -, starts with a letter, length 1–128). uri is any non-empty string (deferred validation to storage-uri at load time).
  • ADDED: IrManifest Zod schema — { packageSlug: <slug grammar>, family: "mic" | "speaker" | "room", entries: readonly IrManifestEntry[] }. Every entry.id SHALL begin with <packageSlug>- (enforced by schema refinement). entry.family SHALL equal manifest-level family.
  • ADDED: IrRegistry — in-memory Map-backed registry with register(entry), has(id), get(id), list({ family? }), clear(). register throws AUDIO_IR.DUPLICATE_ID when an ID is registered twice.
  • ADDED: createIrRegistry() factory.
  • ADDED: AUDIO_IR.* codes via defineCodes: DUPLICATE_ID, NOT_REGISTERED, INVALID_MANIFEST, ID_PREFIX_MISMATCH, FAMILY_MISMATCH.
  • ADDED: OTel — audio_ir.registry_size{family} observable gauge; audio_ir.registrations_total{family, outcome} counter; audio_ir.lookups_total{family, outcome} counter; audio_ir.errors_total{code} counter.

Out of scope

Nothing functional within the foundation's scope. The following are explicitly NOT here, because they belong to siblings:

  • Loading + decoding — audio-ir-loader owns the resolver-driven walk.
  • Concrete IR data — audio-ir-{mic,speaker,room}-plugin-<name> packages own the files and the per-package register(registry, resolver) call.

Source mapping

Genesis-original. No upstream enterprise/audio-ir-core.md exists.

Requirements with no source line

Requirement Justification
every spec requirement Genesis-original foundation; no upstream baseline

Verification approach

  • src/types/manifest-entry.test.ts — Zod parsing + refinement (id grammar, id-prefix rule, family rule).
  • src/types/manifest.test.ts — manifest-level Zod validation.
  • src/registry/registry.test.ts — register / has / get / list / clear semantics; DUPLICATE_ID throw.
  • src/registry/registry.spec-conformance.test.ts — strict-mode behaviors, lookup invariants under concurrency.
  • src/otel/otel.test.ts — counter + gauge assertions with real in-memory MeterProvider.
  • src/error-codes/error-codes.test.ts — exhaustion.

Impact

Affected dependents

No dependents yet (the package is brand new). Verified by bunx turbo ls --filter=...@suaveplan/audio-ir-core returning only the package itself.

The first downstream consumers will be @suaveplan/audio-ir-loader and every @suaveplan/audio-ir-{family}-plugin-<name> package, each landing in its own change.