# FileSinkAdapter _(no submodules)_ ## Purpose `FileSinkAdapter` is the server-tier exit point for `AudioDocument` instances: it encodes a document through `@suaveplan/audio-codecs` for the requested format and writes the resulting bytes through the injected `FsLike` seam. Like `FileSourceAdapter`, the sink owns the path-sanitisation boundary and exposes both buffered (`save`, `saveMany`) and streaming (`createWriteStream`) modes. ## Features - Buffered `save(doc, path, format, opts?)` runs the codec encoder via `encodeTo`, concatenates the resulting chunks, and writes the file — creating parent directories implicitly. - `saveMany(docs, pathFormat, format, opts?)` parallel-encodes a list of documents, expanding `%d` in the path template per index. - `createWriteStream(path)` returns a WHATWG `WritableStream` backed by `node:fs.createWriteStream` (or the injected `FsLike`). - Zod-validated options; failures throw `AUDIO_ENGINE_SERVER.INVALID_OPTIONS`. - `sanitizePath` runs before any filesystem call. - Unknown / unregistered format → `AUDIO_ENGINE_SERVER.ENCODE_FAILED`. - Underlying write errors → `AUDIO_ENGINE_SERVER.IO_FAILED`. - OpenTelemetry instrumentation — span `file.save`, counter `audio_engine_bytes_written_total`, histogram `audio_engine_save_duration_ms`. ## Usage: Basic ```ts import { FileSinkAdapter } from "@suaveplan/audio-engine-server"; const sink = new FileSinkAdapter(); await sink.save(doc, "/tmp/out.flac", "flac"); ``` ## Usage: Advanced ```ts const sink = new FileSinkAdapter({ rootDir: "/var/exports" }); const writer = sink.createWriteStream("session.wav"); await pcmStream.pipeTo(writer); await sink.saveMany( [trackA, trackB, trackC], "stem-%d.wav", "wav", { bitDepth: 24 } ); ``` ## API Reference - `new FileSinkAdapter(options?: FileSinkOptions)` — `fs`, `registry`, `rootDir` optional. - `save(doc, path, format, opts?): Promise` - `saveMany(docs, pathFormat, format?, opts?): Promise` - `createWriteStream(path): WritableStream` ## Implementation Notes The buffered `save` collects encoder chunks into an array, computes the total length, and writes a single concatenated buffer; the codec layer already streams internally so this is a small one-time copy. `saveMany` substitutes the literal `%d` token in `pathFormat` with the document's index. Unknown codec formats surface from the codec layer as `AUDIO_CODECS.NOT_SUPPORTED`; the sink re-throws them as `AUDIO_ENGINE_SERVER.ENCODE_FAILED` with the offending format in `context`. Underlying `node:fs` write failures (`EACCES`, `ENOSPC`, etc.) become `AUDIO_ENGINE_SERVER.IO_FAILED` with the resolved absolute path in `context`.