first commit
This commit is contained in:
105
docs/foundation/flow/streams/src/pipeline/partition/partition.md
Normal file
105
docs/foundation/flow/streams/src/pipeline/partition/partition.md
Normal 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.
|
||||
Reference in New Issue
Block a user