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,83 @@
# FileSourceAdapter
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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<Uint8Array>`
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<AudioDocument>`
- `loadMany(paths, opts?): Promise<AudioDocument[]>`
- `createReadStream(path): ReadableStream<Uint8Array>`
## 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.