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,91 @@
# round-trip (streamToUint8Array / uint8ArrayToStream)
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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<Uint8Array, Uint8Array>,
ms: number
): Promise<Uint8Array> {
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<Uint8Array>` that closes after the last chunk.
### `streamToUint8Array(stream, signal?)`
- **`stream: ReadableStream<Uint8Array>`** — source stream.
- **`signal?: AbortSignal`** — cancellation token; cancelling rejects the call
with `StreamAbortedError` and cancels the reader.
- **Returns** — `Promise<Uint8Array>` 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.