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,94 @@
# toAsyncIterable
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`toAsyncIterable` adapts a `ReadableStream<T>` into a standard
`AsyncIterable<T>`, letting consumers use ergonomic `for await ... of` loops
to drain a stream rather than driving the WHATWG reader API by hand. While
modern Node and recent browser engines expose `ReadableStream[Symbol.asyncIterator]`
natively, the streams package ships its own implementation so that:
- behaviour is identical across runtimes regardless of native support;
- abort semantics are pinned — aborts always surface as `StreamAbortedError`;
- early-break cleanup is guaranteed (the underlying reader is cancelled, the
lock is released, and abort listeners are removed) so upstream producers
can promptly free resources.
## Features
- `for await ... of` support for any `ReadableStream<T>`.
- Optional `AbortSignal` input — abort throws `StreamAbortedError`.
- Cancels the underlying reader on early-break (via the iterator's
`return()` hook).
- Idempotent cleanup — releasing an already-released lock is swallowed.
- Generic over `T`; works equally for `Uint8Array` byte streams and
arbitrary object streams.
## Usage: Basic Example
```ts
import { toAsyncIterable } from "@suaveplan/streams/sinks/to-async-iterable/to-async-iterable";
for await (const chunk of toAsyncIterable(stream)) {
console.log(chunk);
}
```
## Usage: Advanced Example
```ts
import { toAsyncIterable } from "@suaveplan/streams/sinks/to-async-iterable/to-async-iterable";
import { StreamAbortedError } from "@suaveplan/streams/errors/stream-error";
async function consumeWithDeadline<T>(
stream: ReadableStream<T>,
deadlineMs: number,
onItem: (item: T) => Promise<void>
): Promise<void> {
const ac = new AbortController();
const timer = setTimeout(() => ac.abort(new Error("deadline")), deadlineMs);
try {
for await (const item of toAsyncIterable(stream, ac.signal)) {
await onItem(item);
}
} catch (err) {
if (err instanceof StreamAbortedError) {
console.warn("hit deadline before stream completed");
return;
}
throw err;
} finally {
clearTimeout(timer);
}
}
```
## API Reference
### `toAsyncIterable<T>(stream, signal?): AsyncIterable<T>`
- **`stream: ReadableStream<T>`** — source stream to adapt.
- **`signal?: AbortSignal`** — optional cancellation token. Aborting causes
the active or next `next()` call to reject with `StreamAbortedError` and
cancels the underlying reader.
- **Returns** — an `AsyncIterable<T>` that yields chunks until the stream
closes or aborts.
- **Throws** — `StreamAbortedError` on abort; otherwise propagates upstream
stream errors verbatim.
## Implementation Notes
- The iterator implements `return()` so `break`/`throw` inside the
`for await` loop both trigger reader cancel + lock release. Without this,
upstream producers would keep filling buffers for chunks no consumer will
ever read.
- Both pre-iteration abort and mid-`read()` abort paths produce
`StreamAbortedError`. The mid-read path uses the same `aborted` flag plus
a defensive re-check after `read()` resolves to handle the race where the
signal fires after the read promise was already in flight.
- Abort listeners are registered with `{ once: true }` and explicitly
removed in cleanup — long-lived signals shared across many iterators do
not accumulate listeners.