# round-trip (streamToUint8Array / uint8ArrayToStream) _(no submodules)_ ## Purpose The round-trip module provides the two halves of byte ↔ stream conversion that underpin most file/network adapters in `@suaveplan/streams`. `uint8ArrayToStream` turns an in-memory buffer into a backpressure-respecting `ReadableStream`, and `streamToUint8Array` drains an arbitrary readable stream back into a single `Uint8Array`. They are deliberately co-located because most consumers reach for both in the same call path (e.g. test fixtures that load a buffer, push it through a transform pipeline, then re-collect the output for assertion). The drain side is `AbortSignal`-aware and rejects with `StreamAbortedError` when cancellation is requested, cancelling the underlying reader so upstream producers can release resources promptly. ## Features - `uint8ArrayToStream`: configurable chunk size; defaults to a single emission. - Per-chunk copy emission so consumers can safely retain references. - `streamToUint8Array`: total-length pre-sum, single allocation for output. - Honours `AbortSignal` both before and during read; cancels the reader. - Pure WHATWG Streams API — no Node-only or DOM-only deps. ## Usage: Basic Example ```ts import { streamToUint8Array, uint8ArrayToStream, } from "@suaveplan/streams/utils/stream/round-trip/round-trip"; const original = new Uint8Array([1, 2, 3, 4]); const drained = await streamToUint8Array(uint8ArrayToStream(original)); // drained equals original ``` ## Usage: Advanced Example ```ts import { streamToUint8Array, uint8ArrayToStream, } from "@suaveplan/streams/utils/stream/round-trip/round-trip"; async function pipeWithTimeout( data: Uint8Array, transform: TransformStream, ms: number ): Promise { const ac = new AbortController(); const timer = setTimeout(() => ac.abort(new Error("timeout")), ms); try { const piped = uint8ArrayToStream(data, { chunkSize: 4096 }).pipeThrough( transform ); return await streamToUint8Array(piped, ac.signal); } finally { clearTimeout(timer); } } ``` ## API Reference ### `uint8ArrayToStream(data, options?)` - **`data: Uint8Array`** — bytes to emit. - **`options.chunkSize?: number`** — bytes per chunk; defaults to entire array. - **Returns** — `ReadableStream` that closes after the last chunk. ### `streamToUint8Array(stream, signal?)` - **`stream: ReadableStream`** — source stream. - **`signal?: AbortSignal`** — cancellation token; cancelling rejects the call with `StreamAbortedError` and cancels the reader. - **Returns** — `Promise` of the concatenated bytes. - **Throws** — `StreamAbortedError` on cancellation. ## Implementation Notes - The drain pre-collects chunks and sums lengths so the final `Uint8Array` is allocated exactly once — avoids the quadratic-copy trap of repeatedly appending into a growing buffer. - Each emission of `uint8ArrayToStream` is a copy via `data.slice`, so the source `Uint8Array` may be mutated independently after the stream is built. - The abort listener is removed in `finally` to keep the signal listener count bounded across long-lived signals.