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

11 KiB

Tasks: enterprise-audio-codec-ffmpeg-native

Implementation checklist for @suaveplan/audio-codec-ffmpeg-native. Tasks are ordered; each section must be complete before the next begins. Every ### Requirement and #### Scenario from specs/audio-codec-ffmpeg-native/spec.md is enumerated in §0c working notes and checked off as implementation lands.


1. Scaffold

  • Create packages/media/audio/audio-codec-ffmpeg-native/ with package.json, tsconfig.json, vitest.config.ts (use createTestConfig() from @suaveplan/testing/config; pool: 'forks').
  • package.json declares "engines": { "node": ">=20" }, no "browser" field, "sideEffects": false, "type": "module", and version: "0.1.0".
  • dependencies: @suaveplan/audio-codecs workspace:^, @suaveplan/audio-core workspace:^, @suaveplan/streams workspace:^, @suaveplan/error workspace:^, @suaveplan/types workspace:^, fluent-ffmpeg catalog:audio, zod catalog:forms, @opentelemetry/api catalog:observability, tslib catalog:build.
  • optionalDependencies: @ffmpeg-installer/ffmpeg catalog:audio.
  • devDependencies: @suaveplan/testing workspace:^, vitest catalog:testing, @vitest/coverage-istanbul catalog:testing, fast-check catalog:testing, mitata catalog:testing, @types/fluent-ffmpeg catalog:types.
  • If fluent-ffmpeg, @ffmpeg-installer/ffmpeg, or @types/fluent-ffmpeg are not yet in the root package.json catalog block, add them under catalog.audio / catalog.types and re-run bun install.
  • Declare subpath exports for every feature module listed in proposal.md "Module layout" plus the root barrel — every entry resolves to a typed import condition under dist/.
  • Add per-package tsconfig.json extending the shared preset.

2. Types + error codes

  • src/error-codes.ts — defineCodes with the eight AUDIO_CODEC_FFMPEG_NATIVE.* codes; export AudioCodecFfmpegNativeError extends SuaveplanError, AUDIO_CODEC_FFMPEG_NATIVE_ERROR_METADATA, registerAudioCodecFfmpegNativeErrorCodes().
  • src/error-codes.test.ts — assert each code is thrown by at least one call site (this test cross-references the per-feature tests once they exist).
  • src/error-codes.md — Purpose / Features / Basic / Advanced / API / Notes.
  • src/constants/index.ts — supported format lists, MIME types, default timeouts, default concurrency.

3. Binary resolver

  • src/binary/binary.ts — resolveBinary(opts): Promise<string> implementing the 4-step discovery order; cache result on success.
  • src/binary/binary.types.ts — BinaryResolverOptions, ResolvedBinary shapes.
  • src/binary/binary.test.ts — covers binaryProvider precedence, explicit binaryPath, dynamic-import fallback, and BINARY_MISSING throw path.
  • src/binary/binary.md.
  • src/binary/index.ts — explicit named exports.

4. Semaphore

  • src/semaphore/semaphore.ts — ~30-line FIFO semaphore with acquire / release; no external dep.
  • src/semaphore/semaphore.types.ts.
  • src/semaphore/semaphore.test.ts — concurrency cap, FIFO ordering, slot-release on error.
  • src/semaphore/semaphore.md, index.ts.

5. Subprocess runner (the only place node:child_process / fluent-ffmpeg is imported)

  • src/runner/runner.ts — runDecode(opts) and runEncode(opts). Owns spawn, stdin pipe, stdout drain (chunk-by-chunk, no full-output Buffer.concat), stderr ring buffer, exit handling, timeout enforcement, semaphore release.
  • src/runner/runner.types.ts — RunDecodeOptions, RunEncodeOptions, RunnerCallbacks, RunnerHandles.
  • src/runner/runner.test.ts — happy path, non-zero exit → _DECODE_FAILED with stderr context, spawn failure → _SPAWN_FAILED, timeout → _TIMEOUT with SIGTERM/SIGKILL escalation observed.
  • src/runner/runner.md, index.ts.

6. Decode pipeline

  • src/decode/decode.ts — decodeOneShot(bytes, opts) orchestrating runDecode + per-channel Float32Array accumulators + interleave splitter.
  • src/decode/decode.types.ts.
  • src/decode/decode.test.ts — well-formed DecodeResult, chunk-by-chunk assertion (instrument runner to count Buffer.concat), _DECODE_FAILED with stderr in context, canDecode delegation.
  • src/decode/decode.md, index.ts.

7. Encode pipeline

  • src/encode/encode.ts — encodeOneShot(pcm, opts) with PCM-shape validation, interleaver writing into a 64 KiB working buffer, runner invocation.
  • src/encode/encode.types.ts.
  • src/encode/encode.test.ts — round-trip through decode, _INVALID_PCM for ragged channels, _UNSUPPORTED_CODEC for unknown format and for WMA encode.
  • src/encode/encode.md, index.ts.

8. Streaming adapters

  • src/stream/stream-decoder.ts — createDecoder(opts) returning StreamingDecoder; createDecodeTransform(opts) returning a @suaveplan/streams TransformStream<Uint8Array, AudioPage>.
  • src/stream/stream-encoder.ts — createEncoder(opts) returning StreamingEncoder; createEncodeTransform(opts) returning a TransformStream<Float32Array[], Uint8Array>.
  • Sibling .types.ts, .test.ts, .md for each; tests cover chunked-write equivalence, TransformStream composition, encoder yielding before flush.
  • src/stream/index.ts — explicit named exports.

9. AbortSignal wiring

  • src/abort/abort.ts — attachAbort(child, signal, semaphoreRelease): sends SIGTERM, starts 1 s timer, escalates to SIGKILL, releases slot, rejects with _ABORTED. Short-circuit path for pre-aborted signals.
  • src/abort/abort.types.ts.
  • src/abort/abort.test.ts — pre-aborted short-circuit (no spawn observed), mid-decode SIGTERM → SIGKILL escalation under a child that ignores SIGTERM.
  • src/abort/abort.md, index.ts.

10. OpenTelemetry instrumentation

  • src/otel/otel.ts — span / counter / histogram / inflight-gauge helpers; inert without exporter; no import-time side effects.
  • src/otel/otel.types.ts, otel.test.ts (in-memory MeterProvider + TracerProvider), otel.md, index.ts.

11. Codec class + register helper

  • src/codec/codec.ts — FfmpegNativeCodec implementing AudioCodec. Owns the per-instance semaphore, the cached binary path, the OTel handles, and delegates to decode/, encode/, stream/ modules.
  • src/codec/codec.schemas.ts — FfmpegNativeCodecOptionsSchema, EncodeOptsSchema (Zod).
  • src/codec/codec.types.ts, codec.test.ts (constructor Zod validation, canDecode rejection without spawn, WMA encode short-circuit, full DecodeResult shape), codec.md, index.ts.
  • src/register/register.ts — register(registry, options?) idempotent installer.
  • src/register/register.test.ts, register.types.ts, register.md, index.ts.

12. Root barrel

  • src/index.ts — explicit named re-exports of every public symbol from each feature module. No export * (rule 10 / §6a).

13. Tests — purity, integration, exports

  • tests/purity.test.ts — built dist has no browser globals; child_process imported only from src/runner/runner.ts; package import mutates no global OTel state.
  • tests/integration.test.ts — full round-trip across every declared encodable format using the bundled @ffmpeg-installer/ffmpeg binary.
  • tests/exports.test.ts — every feature module has a working subpath export entry and is independently importable.
  • Every test file imports runner primitives from @suaveplan/testing/runner (rule 11 / §7).

14. Benchmarks

  • src/__benchmarks__/codec.bench.ts — mitata suite: decode 10 s WAV, decode 10 s MP3, encode 10 s WAV→MP3, concurrent-decode at concurrencyLimit = 4. Use --cpu-prof-md / --heap-prof-md for LLM-assisted analysis.
  • Publish baseline numbers in README.md.

15. Documentation

  • Every .ts (excluding .test.ts, .types.ts, .schemas.ts, barrel index.ts) has a sibling .md meeting §4: ≥200 words, Purpose / Features / Basic / Advanced / API Reference / Implementation Notes.
  • README.md at package root: name, one-paragraph description, install, quick-start, Modules section linking every src/<feature>/<feature>.md, format support matrix from design.md, baseline benchmark numbers.

16. Pre-publish gates (§13)

  • Step 0 — Spec reconciliation: walk every requirement and Verified by from the spec delta; confirm every symbol, span, counter, error code, and test name exists verbatim in src/. STOP if any item is unmatched.
  • bunx turbo lint typecheck test --filter=@suaveplan/audio-codec-ffmpeg-native.
  • bun run check (biome).
  • bunx turbo test:coverage --filter=@suaveplan/audio-codec-ffmpeg-native reports 100 / 100 / 100 / 100 (istanbul).
  • bunx turbo build --filter=@suaveplan/audio-codec-ffmpeg-native.
  • Banned-pattern scan: zero hits for @deprecated, TODO, FIXME, ts-ignore, ts-expect-error, .skip, .only, bare any, console.log.
  • Barrel scan (§13 step 4a): zero export * in feature barrels.
  • Test-runner import scan (§13 step 4b): zero direct from "vitest" / from "bun:test" imports.
  • Missing-.md scan returns zero output.
  • README.md present.
  • Bare-version scan returns zero output.
  • Subpath-export count matches feature-module count.
  • Security scan for hardcoded secrets returns zero output.
  • (Not a foundation package — dependent sweep optional; run it anyway since this is a fresh public surface.)

17. Root-doc refresh + markdownlint (§13b)

  • bun scripts/folder-reorg/generate-manifest.ts → refresh MANIFEST.md.
  • bun scripts/generate-missing.ts → refresh MISSING.yml.
  • Move the package row from Pending → Completed in STATUS.md.
  • Add the per-package paragraph in SUMMARY.md.
  • bunx markdownlint-cli2 --fix; resolve any remaining violations by hand.

18. Fold spec into canonical location

  • Fold openspec/changes/enterprise-audio-codec-ffmpeg-native/specs/audio-codec-ffmpeg-native/spec.md into openspec/specs/audio-codec-ffmpeg-native/spec.md per §14.

19. Archive

  • Move openspec/changes/enterprise-audio-codec-ffmpeg-native/ to openspec/archive/2026-05-15-enterprise-audio-codec-ffmpeg-native/.