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,105 @@
# `partition`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`partition` splits a single `ReadableStream<T>` 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<boolean>`)
- 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<number>;
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<Event>;
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<T>(source, predicate): { truthy, falsy }`
- **`source: ReadableStream<T>`** — the input stream. `partition` acquires a
single default reader and drives it from the output pulls.
- **`predicate: (chunk: T) => boolean | Promise<boolean>`** — 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<T>; falsy: ReadableStream<T> }`** — 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.