# FileSourceAdapter _(no submodules)_ ## Purpose `FileSourceAdapter` is the server-tier entry point for reading audio files into the universal `AudioDocument` model. It owns the path-sanitisation boundary, dispatches to `@suaveplan/audio-codecs` `decodeSource` for format detection and decode, and exposes both buffered (`load`, `loadMany`) and streaming (`createReadStream`) modes through a single injectable `FsLike` seam. The adapter implements no DSP itself — every byte ends up routed through the universal codec layer. ## Features - Buffered `load(path, opts?)` returns an `AudioDocument` from a local path or an `http`/`https` URL. - `loadMany(paths, opts?)` resolves a `Promise.all` over `load`, preserving input order regardless of decode completion order. - `createReadStream(path)` returns a WHATWG `ReadableStream` backed by the injected `FsLike` (or `node:fs.createReadStream`). - Zod-validated options at every entry point; failures throw `AUDIO_ENGINE_SERVER.INVALID_OPTIONS`. - `sanitizePath` rejects empty / null-byte / `..`-traversal paths against the optional `rootDir` boundary before any filesystem call is issued. - `AbortSignal` propagation — pre-aborted signals short-circuit with `AUDIO_ENGINE_SERVER.ABORTED`; mid-load aborts surface the same code via the codec layer. - OpenTelemetry instrumentation — span `file.load`, counter `audio_engine_bytes_read_total`, histogram `audio_engine_load_duration_ms`. Inert without an exporter. ## Usage: Basic ```ts import { FileSourceAdapter } from "@suaveplan/audio-engine-server"; const source = new FileSourceAdapter(); const doc = await source.load("/tmp/sample.wav"); console.log(doc.sampleRate, doc.channels, doc.sourceLength); ``` ## Usage: Advanced ```ts import { FileSourceAdapter, defaultFs } from "@suaveplan/audio-engine-server"; const source = new FileSourceAdapter({ fs: defaultFs, rootDir: "/var/audio", registry: customCodecRegistry, }); const ctrl = new AbortController(); setTimeout(() => ctrl.abort(), 5000); const docs = await source.loadMany( ["a.wav", "b.flac", "c.mp3"], { signal: ctrl.signal } ); ``` ## API Reference - `new FileSourceAdapter(options?: FileSourceOptions)` — `fs`, `fetch`, `registry`, `rootDir` all optional. - `load(path, opts?): Promise` - `loadMany(paths, opts?): Promise` - `createReadStream(path): ReadableStream` ## Implementation Notes `load` distinguishes URL inputs via `isHttpUrl`; URL inputs are fetched through the injected `fetch` and skip filesystem sanitisation. Local paths pass through `sanitizePath(path, rootDir)` before any `FsLike` call, so a path-traversal attempt throws `INVALID_PATH` before the filesystem is touched. `ENOENT` from the underlying `readFile` is mapped to `FILE_NOT_FOUND`; any other read error becomes `IO_FAILED`. Decode failures from the codec layer are re-thrown as `DECODE_FAILED` unless the inner error reports a stream abort, in which case the engine maps it to `AUDIO_ENGINE_SERVER.ABORTED` to preserve the signal-aborted contract.