Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,178 @@
# OpenSpec Change: enterprise-audio-engine-server
- **Change ID**: enterprise-audio-engine-server
- **Package**: `@suaveplan/audio-engine-server`
- **Category**: server
- **Target path**: `packages/media/audio/audio-engine-server`
- **Status**: PROPOSED
- **Date**: 2026-05-14
- **Design**: `./design.md`
- **Spec delta**: `./specs/audio-engine-server/spec.md`
---
## Summary
Deliver `@suaveplan/audio-engine-server` on the genesis branch from scratch at enterprise quality. Server-side file I/O adapters for the genesis audio ecosystem. This package is **intentionally thin** — all DSP, planning, streaming, and timeline logic lives in universal packages (`audio-core`, `audio-timeline`, `audio-transport`). This package only provides file source adapters, file sink adapters, and a server timeline adapter that renders to file output instead of speakers.
Batch processing is universal (uses `@suaveplan/queue` + `@suaveplan/streams`) and is documented in the master spec as a cross-cutting pattern, not a server-specific feature.
---
## Current-state audit (genesis)
The package does not exist on genesis yet — this is a fresh build. No prior source, no test results, no coverage data. The old `enterprise-browser-audio-engine` was browser-only and had no server equivalent.
---
## Available workspace deps on genesis
| Package | Role |
|---|---|
| `@suaveplan/audio-core` (`workspace:^`) | `AudioDocument`, `PlanEngine`, `ProcessPipeline` |
| `@suaveplan/audio-timeline` (`workspace:^`) | `TimelineEngine`, `Track`, `Clip` |
| `@suaveplan/audio-transport` (`workspace:^`) | `Transport` |
| `@suaveplan/audio-codecs` (`workspace:^`) | `CodecRegistry`, `encodeTo`, `decodeSource` |
| `@suaveplan/streams` (`workspace:^`) | `TransformStream`, `ReadableStream`, `WritableStream` primitives |
| `@suaveplan/error` (`workspace:^`) | `SuaveplanError` + `defineCodes`; the package exports `AudioEngineServerError` (extending `AudioError` from `@suaveplan/audio-core`) carrying `AUDIO_ENGINE_SERVER.*` codes. |
| `@suaveplan/types` (`workspace:^`) | `LoggerLike` |
| `@suaveplan/opentelemetry` (`workspace:^`) | OTel spans, counters, histograms |
| `zod` (`catalog:forms`) | Zod schemas for option types |
Dev deps: `vitest`, `@vitest/coverage-istanbul`, `fast-check`, `mitata` (all `catalog:testing`).
---
## Public API
### Core types
```ts
interface FileSourceOptions {
fs?: FsLike // injectable fs adapter for testing
fetch?: typeof fetch // injectable fetch for URLs
}
class FileSourceAdapter {
constructor(options?: FileSourceOptions)
async load(path: string, opts?: LoadOptions): Promise<AudioDocument>
createReadStream(path: string): ReadableStream<Uint8Array>
async loadMany(paths: string[], opts?: LoadOptions): Promise<AudioDocument[]>
}
interface FileSinkOptions {
fs?: FsLike
}
class FileSinkAdapter {
constructor(options?: FileSinkOptions)
async save(doc: AudioDocument, path: string, format: string, opts?: SaveOptions): Promise<void>
createWriteStream(path: string): WritableStream<Uint8Array>
async saveMany(docs: AudioDocument[], pathFormat: string): Promise<void>
}
interface ServerAdapterOptions {
outputPath?: string
format?: string
sampleRate?: number
ppqn?: number
}
class ServerTimelineAdapter implements TimelineAdapter {
constructor(options?: ServerAdapterOptions)
init(options?: { signal?: AbortSignal }): Promise<void>
setTracks(tracks: Track[]): void
addTrack(track: Track): void
removeTrack(trackId: string): void
updateTrack(trackId: string, track: Track): void
play(time?: number): void
pause(time?: number): void
stop(time?: number): void
seek(time: number): void
getCurrentTime(): number
setLoop(start: number, end: number, enabled: boolean): void
setMasterVolume(volume: number): void
setTrackVolume(trackId: string, volume: number): void
setTrackMute(trackId: string, muted: boolean): void
setTrackSolo(trackId: string, soloed: boolean): void
setTrackPan(trackId: string, pan: number): void
setTempo(bpm: number, atTick?: number): void
ticksToSeconds(ticks: number): number
secondsToTicks(seconds: number): number
readonly lookAhead: 0
readonly ppqn: number
dispose(): void
/** Extra non-interface method: render the timeline to a finite output, useful for non-realtime export. */
render(options?: { startTime?: number; endTime?: number; signal?: AbortSignal }): Promise<void>
}
```
---
## New additions for genesis
1. **Injectable fs adapter** — `FileSourceAdapter` and `FileSinkAdapter` accept an `FsLike` interface for testing without touching the real filesystem.
2. **Streaming load/save** — `createReadStream` / `createWriteStream` return WHATWG streams compatible with `@suaveplan/streams`.
3. **Batch helpers** — `loadMany` / `saveMany` for bulk operations.
4. **Server playback = render** — `ServerTimelineAdapter.play()` renders as fast as possible via `audio-core.streamPlan()` piped to codec encoder and file sink.
5. **OTel instrumentation** — spans for `file.load`, `file.save`, `server.render`, counters for bytes read/written.
6. **Property-based tests** — load then save → file exists, round-trip preserves sample count.
7. **Benchmark suite** — load 100MB WAV, save 100MB FLAC.
---
## File layout
```text
packages/media/audio/audio-engine-server/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
source/
file-source.ts
file-source.md
file-source.types.ts
file-source.test.ts
index.ts
sink/
file-sink.ts
file-sink.md
file-sink.types.ts
file-sink.test.ts
index.ts
adapter/
server-adapter.ts
server-adapter.md
server-adapter.types.ts
server-adapter.test.ts
index.ts
__benchmarks__/
io.bench.ts
tests/
integration.test.ts
property.test.ts
```
---
## Impact
**Breaking changes**: none — fresh build, package does not yet exist on genesis.
**Prerequisites**: `@suaveplan/audio-core`, `@suaveplan/audio-timeline`, `@suaveplan/audio-transport`, `@suaveplan/audio-codecs`, `@suaveplan/streams`, `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/opentelemetry` published to Verdaccio.
**No universal package may depend on this.** It is a leaf node in the dependency graph.
### Affected dependents
`@suaveplan/audio-engine-server` is a leaf node — its only downstream consumer is the audio CLI,
which does not exist on genesis yet (it lands in a later change in this same batch). It is created
against this package's spec delta, not retrofitted.
- `@suaveplan/audio-cli` — [ ] tests run / [ ] docs reviewed / [x] no action needed (reason: does not exist yet; created in `enterprise-audio-cli`)
Once landed, any subsequent change to this package's public surface MUST run
`bunx turbo test --filter=...@suaveplan/audio-engine-server` per `openspec/AGENTS.md` §13 step 10.