# toAsyncIterable _(no submodules)_ ## Purpose `toAsyncIterable` adapts a `ReadableStream` into a standard `AsyncIterable`, 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`. - 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( stream: ReadableStream, deadlineMs: number, onItem: (item: T) => Promise ): Promise { 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(stream, signal?): AsyncIterable` - **`stream: ReadableStream`** — 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` 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.