# `partition` _(no submodules)_ ## Purpose `partition` splits a single `ReadableStream` into two output streams using a boolean predicate. Unlike `tee` or `fanOut`, every input chunk is routed to _exactly one_ output — the truthy stream or the falsy stream — so the union of the two outputs equals the source with per-side ordering preserved. This is the right tool when you want to fork a stream by classification (errors vs successes, even vs odd, hot vs cold) without paying the buffering cost of a full duplication. ## Features - Single-pass split via one underlying reader on the source - Synchronous or asynchronous predicate (`(chunk: T) => boolean | Promise`) - Backpressure flows from either output back to the source — pulls on each side drive the reader on demand - Predicate exceptions error _both_ output streams and cancel the source - Per-side `cancel()` cooperates: the source is cancelled only when both sides have been cancelled - One-slot per-side buffer absorbs the off-routing case (a pull on truthy yielding a falsy chunk and vice versa) - Zero workspace runtime dependencies — uses only WHATWG Streams primitives ## Usage: Basic Example ```ts import { partition } from "@suaveplan/streams/pipeline/partition/partition"; declare const numbers: ReadableStream; const { truthy: evens, falsy: odds } = partition( numbers, (n) => n % 2 === 0 ); ``` ## Usage: Advanced Example ```ts import { partition } from "@suaveplan/streams/pipeline/partition/partition"; interface Event { level: "info" | "warn" | "error"; payload: unknown; } declare const events: ReadableStream; const { truthy: errors, falsy: rest } = partition(events, async (e) => { // async predicates are awaited in routing order await Promise.resolve(); return e.level === "error"; }); // Each side is an independent ReadableStream — pipe through unrelated // pipelines without worrying about duplication or fan-out cost. const errorsToSink = errors.pipeTo(errorSink); const restToSink = rest.pipeTo(analyticsSink); await Promise.all([errorsToSink, restToSink]); ``` ## API Reference ### `partition(source, predicate): { truthy, falsy }` - **`source: ReadableStream`** — the input stream. `partition` acquires a single default reader and drives it from the output pulls. - **`predicate: (chunk: T) => boolean | Promise`** — routes each chunk: `true` enqueues to `truthy`, `false` enqueues to `falsy`. Predicate exceptions are caught, both outputs are errored with the original cause, and the source reader is cancelled. - **Returns `{ truthy: ReadableStream; falsy: ReadableStream }`** — two independent readable streams. Both close when the source is exhausted. Cancelling one side does _not_ cancel the source; cancelling both does. ### Throws - Synchronous: none — invalid arguments propagate as TypeErrors from the underlying `ReadableStream` machinery on first use. - Asynchronous: predicate exceptions surface as stream errors on both outputs; their `.cause` is the original thrown value. ## Implementation Notes The implementation owns a single `ReadableStreamDefaultReader` on the source and exposes two consumer streams whose `pull()` callbacks both drain from that reader. Each `pull` loops until it produces a chunk _for its own side_; chunks for the other side are stashed in a one-slot buffer that the other side claims on its next pull. Because pulls only fire under backpressure (when the consumer side has space), the source is read at most as fast as the _currently-pulling_ consumer demands — backpressure on either side is preserved end-to-end. Cancellation is cooperative. A `cancel()` on a single output marks that side closed; the source reader is only cancelled once both sides are closed. This mirrors `tee` semantics and lets the live side continue draining the source. Predicate exceptions are routed once: the first throw errors both outputs and cancels the reader, after which `pullFor` short-circuits via the `errored` flag.