# bunny-cdn-adapter.streams ## Purpose Builds the `ReadableStream` and `WritableStream` instances `BunnyCdnAdapter` returns from `createReadStream` / `createWriteStream`. Splits the stream plumbing out of the main class body so each piece can be unit-tested without instantiating the full adapter. `buildReadStream` lazily resolves the `StorageClientCtx` promise on the first `pull` (the adapter passes a promise that resolves once headers are computed) then pumps the `Response.body` chunks 1:1 to the consumer. `buildWriteStream` buffers chunks until `close` and dispatches via the caller-supplied `commit` callback — the adapter passes `(body, signal) => this.set(safe, body, undefined, signal)` so the write goes through the OTel-wrapped `set` path. ## Features - Native streaming: `buildReadStream` returns `Response.body` chunks without buffering. - Error propagation: mid-stream errors surface via `controller.error`; aborts propagate through the underlying fetch's signal. - `buildWriteStream` accepts an injectable `commit` callback so the adapter (or test fixtures) controls how the buffered body is shipped. - `abort()` handler drops buffered chunks promptly so memory does not linger after cancellation. ## Usage: Basic ```ts import { buildReadStream, buildWriteStream } from "@suaveplan/storage-cdn-bunnycdn"; const stream = buildReadStream(ctxPromise, "k", signal); for await (const chunk of stream) processChunk(chunk); ``` ## Usage: Advanced ```ts import { buildWriteStream } from "@suaveplan/storage-cdn-bunnycdn"; const ws = buildWriteStream({ key: "k", signal, commit: async (body, s) => storage.set("k", body, undefined, s), }); const writer = ws.getWriter(); await writer.write(chunk); await writer.close(); // triggers commit ``` ## API Reference ### `buildReadStream(ctxPromise, key, signal?): ReadableStream` Starts the GET on first read; pumps the response body until `done`. `Response.body === null` closes the stream cleanly without enqueueing. ### `interface WriteStreamArgs` `{ key, signal, commit }` — `commit(body, signal)` is invoked once at `close` time with the concatenated buffer. ### `buildWriteStream(args): WritableStream` Buffers chunks, concatenates on `close`, dispatches via `commit`. `abort()` clears the buffer without committing. ## Implementation Notes - Buffered-then-flush is the right tradeoff for BunnyCDN Storage PUT: their API expects a known `Content-Length`, which means chunked transfers without orchestration aren't supported. Provider-specific streaming uploads (the documented multi-part upload pattern) belong in a separate helper. - `abort()` clears `chunks` rather than reusing the array — clearing length forces garbage collection of every enqueued buffer once the writer releases its reference. - `releaseLock()` on the response body reader runs in `finally` so a mid-stream error doesn't leak the lock. - `ctxPromise` is awaited inside `start`; if header resolution fails (e.g. a caller-supplied async `headers` callback rejects), the rejection surfaces via `controller.error`, which is what consumers expect for stream initialisation failures.