The exported value: builders (Stream.from, Stream.unfold, Stream.lines) are the only
entry points — there is no public constructor and no call form.
-
asyncStream<U, R>(): StreamClass<source: Source<U>,fn: () => R | PromiseLike<R>,value: Exclude<U, AnyFailure>,signal: AbortSignaloptions?: { maxConcurrency?: number; timeoutMs?: number; ordered?: boolean; }>Exclude<R, AnyFailure>,Extract<U | R, AnyFailure> | AnyFailure
Elixir's
Task.async_stream/3, living at its JS-idiomatic home: mapsfnover the source with at mostmaxConcurrencyinvocations in flight, lazily — nothing starts until a consumer pulls, and the window refills only as results are taken. Results keep source order by default (ordered: falseyields completion order — faster when element durations vary). Each element may deadline (timeoutMs) into a declaredFailure('AsyncTimeout')element — the railway, not a crash; the element's signal fires so cancellation-aware work stops. A throw fromfnstays a bug. Failure elements from the source pass through unmapped. -
channel<U, F = never>(options: ChannelOptions<U, F> & { overflow: "fail"; }): Channel<U, F | ChannelOverflowFailure>
The one push source: a handle whose producer emits whenever it likes, and a Stream that consumes what it emitted. Every other builder pulls —
unfold,resourceanditerateall ask for the next element — but the most common real source in JS cannot be asked: an EventEmitter, a WebSocket,fs.watch, SSE, a browser running tests. -
chunkBy<S>(): StreamClass<Exclude<S, AnyFailure>[], Extract<S, AnyFailure>>source: Source<S>,key: (value: Exclude<S, AnyFailure>) => unknown
StreamClass#chunkBy as a static:
Stream.chunkBy(source, …)isStream.from(source).chunkBy(…). -
chunkEvery<S>(): StreamClass<Exclude<S, AnyFailure>[], Extract<S, AnyFailure>>source: Source<S>,count: number,step?,leftover?: Exclude<S, AnyFailure>[] | "discard"
StreamClass#chunkEvery as a static:
Stream.chunkEvery(source, …)isStream.from(source).chunkEvery(…). -
chunkWhile<S, A, C>(): StreamClass<C, Extract<S, AnyFailure>>source: Source<S>,initial: A,step: () => { acc: A; emit?: C; halt?: boolean; },value: Exclude<S, AnyFailure>,accumulator: Aflush?: (accumulator: A) => C | undefined
StreamClass#chunkWhile as a static:
Stream.chunkWhile(source, …)isStream.from(source).chunkWhile(…). -
collect<S>(source: Source<S>): Task<Exclude<S, AnyFailure>[], Extract<S, AnyFailure>>
StreamClass#collect as a static:
Stream.collect(source, …)isStream.from(source).collect(…). -
concat<U>(...sources: Source<U>[]): StreamClass<Exclude<U, AnyFailure>, Extract<U, AnyFailure>>
Concatenates sources in order — Elixir's
Stream.concat, variadic. -
cycle<U>(source: Source<U>): StreamClass<Exclude<U, AnyFailure>, Extract<U, AnyFailure>>
Repeats a re-iterable source forever — always bound it (
take,takeWhile). -
dedup<S>(source: Source<S>): StreamClass<Exclude<S, AnyFailure>, Extract<S, AnyFailure>>
StreamClass#dedup as a static:
Stream.dedup(source, …)isStream.from(source).dedup(…). -
dedupBy<S>(): StreamClass<Exclude<S, AnyFailure>, Extract<S, AnyFailure>>source: Source<S>,key: (value: Exclude<S, AnyFailure>) => unknown
StreamClass#dedupBy as a static:
Stream.dedupBy(source, …)isStream.from(source).dedupBy(…). -
drop<S>(): StreamClass<Exclude<S, AnyFailure>, Extract<S, AnyFailure>>source: Source<S>,count: number
StreamClass#drop as a static:
Stream.drop(source, …)isStream.from(source).drop(…). -
dropEvery<S>(): StreamClass<Exclude<S, AnyFailure>, Extract<S, AnyFailure>>source: Source<S>,every: number
StreamClass#dropEvery as a static:
Stream.dropEvery(source, …)isStream.from(source).dropEvery(…). -
dropWhile<S>(): StreamClass<Exclude<S, AnyFailure>, Extract<S, AnyFailure>>source: Source<S>,predicate: () => booleanvalue: Exclude<S, AnyFailure>,index: number
StreamClass#dropWhile as a static:
Stream.dropWhile(source, …)isStream.from(source).dropWhile(…). -
duplicate<U>(): StreamClass<U>value: U,count: number
countcopies of one value. -
every<S>(): Task<boolean, Extract<S, AnyFailure>>source: Source<S>,predicate: () => boolean | PromiseLike<boolean>value: Exclude<S, AnyFailure>,index: number
StreamClass#every as a static:
Stream.every(source, …)isStream.from(source).every(…). -
filter<S>(): StreamClass<Exclude<S, AnyFailure>, Extract<S, AnyFailure>>source: Source<S>,predicate: () => booleanvalue: Exclude<S, AnyFailure>,index: number
StreamClass#filter as a static:
Stream.filter(source, …)isStream.from(source).filter(…). -
find<S>(): Task<Exclude<S, AnyFailure> | undefined, Extract<S, AnyFailure>>source: Source<S>,predicate: () => boolean | PromiseLike<boolean>value: Exclude<S, AnyFailure>,index: number
StreamClass#find as a static:
Stream.find(source, …)isStream.from(source).find(…). -
flatMap<S, U>(): StreamClass<source: Source<S>,fn: () => Source<U>value: Exclude<S, AnyFailure>,index: number>Exclude<U, AnyFailure>,Extract<S, AnyFailure> | Extract<U, AnyFailure>
StreamClass#flatMap as a static:
Stream.flatMap(source, …)isStream.from(source).flatMap(…). -
forEach<S>(): Task<void, Extract<S, AnyFailure>>source: Source<S>,fn: () => void | PromiseLike<void>value: Exclude<S, AnyFailure>,index: number
StreamClass#forEach as a static:
Stream.forEach(source, …)isStream.from(source).forEach(…). -
from<U>(source: Source<U>): StreamClass<Exclude<U, AnyFailure>, Extract<U, AnyFailure>>
Lifts a source into a Stream. Elements that are Failures are the
Echannel from the start; everything else isT. -
fromIndex(offset?: number): StreamClass<number>
The infinite integers
offset, offset + 1, …— Elixir'sStream.from_index/1. -
intersperse<S, Sep>(): StreamClass<Exclude<S, AnyFailure> | Sep, Extract<S, AnyFailure>>source: Source<S>,separator: Sep
StreamClass#intersperse as a static:
Stream.intersperse(source, …)isStream.from(source).intersperse(…). -
interval(ms: number): StreamClass<number>
Emits
0, 1, 2, …everymsmilliseconds, forever — Elixir'sStream.interval/1, onsetTimeout(universal). Infinite: always bound it. -
into<S>(): StreamClass<Exclude<S, AnyFailure>, Extract<S, AnyFailure>>source: Source<S>,sink: WritableStream<Exclude<S, AnyFailure>>
StreamClass#into as a static:
Stream.into(source, …)isStream.from(source).into(…). -
iterate<U>(): StreamClass<U>seed: U,fn: (previous: U) => U
seed, fn(seed), fn(fn(seed)), …— Elixir'sStream.iterate/2, infinite. -
lines(bytes: Source<Uint8Array>): StreamClass<string>
Decodes a byte stream into text lines — the front door for NDJSON, logs, and SSE-ish feeds. Handles multi-byte characters and lines split across chunk boundaries; the final unterminated line is flushed.
-
map<S, U>(): StreamClass<source: Source<S>,fn: () => U | PromiseLike<U>value: Exclude<S, AnyFailure>,index: number>Exclude<U, AnyFailure>,Extract<S, AnyFailure> | Extract<U, AnyFailure>
StreamClass#map as a static:
Stream.map(source, …)isStream.from(source).map(…). -
mapConcurrent<S, R>(): StreamClass<source: Source<S>,fn: () => R | PromiseLike<R>,value: Exclude<S, AnyFailure>,signal: AbortSignaloptions?: { maxConcurrency?: number; timeoutMs?: number; ordered?: boolean; }>Exclude<R, AnyFailure>,Extract<S, AnyFailure> | Extract<R, AnyFailure> | AnyFailure
StreamClass#mapConcurrent as a static:
Stream.mapConcurrent(source, …)isStream.from(source).mapConcurrent(…). -
mapEvery<S>(): StreamClass<Exclude<S, AnyFailure>, Extract<S, AnyFailure>>source: Source<S>,every: number,fn: (value: Exclude<S, AnyFailure>) => Exclude<S, AnyFailure> | PromiseLike<Exclude<S, AnyFailure>>
StreamClass#mapEvery as a static:
Stream.mapEvery(source, …)isStream.from(source).mapEvery(…). -
partition<S>(source: Source<S>): Task<>{ values: Exclude<S, AnyFailure>[]; errors: Extract<S, AnyFailure>[]; },never
StreamClass#partition as a static:
Stream.partition(source, …)isStream.from(source).partition(…). -
reduce<S, A>(): Task<A, Extract<S, AnyFailure>>source: Source<S>,fn: () => A | PromiseLike<A>,accumulator: A,value: Exclude<S, AnyFailure>,index: numberinitial: A
StreamClass#reduce as a static:
Stream.reduce(source, …)isStream.from(source).reduce(…). -
reject<S>(): StreamClass<Exclude<S, AnyFailure>, Extract<S, AnyFailure>>source: Source<S>,predicate: () => booleanvalue: Exclude<S, AnyFailure>,index: number
StreamClass#reject as a static:
Stream.reject(source, …)isStream.from(source).reject(…). -
repeatedly<U>(fn: () => U | PromiseLike<U>): StreamClass<U>
Calls
fnfor each pull, forever — Elixir'sStream.repeatedly/1. -
resource<S, U>(): StreamClass<Exclude<U, AnyFailure>, Extract<U, AnyFailure>>start: () => S | PromiseLike<S>,next: (state: S) =>Promise<readonly [U, S | null] | null>
| readonly [U, S | null]
| null,after: (state: S) => void | PromiseLike<void>unfoldwith lifecycle hooks — Elixir'sStream.resource/3:startruns on first pull,nextis unfold's step, andafterALWAYS runs — normal end, earlytake, or a throw. (Plain generators already get this viatry/finally;resourceis for when the setup/teardown pair deserves to be explicit.) -
results<S>(source: Source<S>): Task<>Result<Exclude<S, AnyFailure>, Extract<S, AnyFailure>>[],never
StreamClass#results as a static:
Stream.results(source, …)isStream.from(source).results(…). -
run<S>(source: Source<S>): Task<void, Extract<S, AnyFailure>>
StreamClass#run as a static:
Stream.run(source, …)isStream.from(source).run(…). -
scan(fn: () => T | PromiseLike<T>): StreamClass<T, E>accumulator: T,value: T
Emits the running fold of the values — Elixir's
Stream.scan; withoutinitialthe first value seeds the accumulator. Failures pass through and leave the accumulator untouched. -
some<S>(): Task<boolean, Extract<S, AnyFailure>>source: Source<S>,predicate: () => boolean | PromiseLike<boolean>value: Exclude<S, AnyFailure>,index: number
StreamClass#some as a static:
Stream.some(source, …)isStream.from(source).some(…). -
take<S>(): StreamClass<Exclude<S, AnyFailure>, Extract<S, AnyFailure>>source: Source<S>,count: number
StreamClass#take as a static:
Stream.take(source, …)isStream.from(source).take(…). -
takeEvery<S>(): StreamClass<Exclude<S, AnyFailure>, Extract<S, AnyFailure>>source: Source<S>,every: number
StreamClass#takeEvery as a static:
Stream.takeEvery(source, …)isStream.from(source).takeEvery(…). -
takeWhile<S>(): StreamClass<Exclude<S, AnyFailure>, Extract<S, AnyFailure>>source: Source<S>,predicate: () => booleanvalue: Exclude<S, AnyFailure>,index: number
StreamClass#takeWhile as a static:
Stream.takeWhile(source, …)isStream.from(source).takeWhile(…). -
tap<S>(): StreamClass<Exclude<S, AnyFailure>, Extract<S, AnyFailure>>source: Source<S>,fn: () => void | PromiseLike<void>value: Exclude<S, AnyFailure>,index: number
StreamClass#tap as a static:
Stream.tap(source, …)isStream.from(source).tap(…). -
through<S, U>(): StreamClass<Exclude<U, AnyFailure>, Extract<U, AnyFailure>>source: Source<S>,fn: (elements: AsyncIterable<S>) => Source<U>
StreamClass#through as a static:
Stream.through(source, …)isStream.from(source).through(…). -
timer(ms: number): StreamClass<number>
Emits a single
0aftermsmilliseconds, then ends — Elixir'sStream.timer/1. -
unfold<S, U>(): StreamClass<Exclude<U, AnyFailure>, Extract<U, AnyFailure>>seed: S,next: (state: S) =>Promise<readonly [U, S | null] | null>
| readonly [U, S | null]
| nullElixir's
Stream.unfold/2: grow a lazy stream from a seed.next(state)returns[element, nextState]to emit and continue, ornullto end; return[failure, null]to emit a declared failure and stop. Nothing runs until a consumer pulls — pagination only fetches the pages the consumer actually reaches. -
uniq<S>(source: Source<S>): StreamClass<Exclude<S, AnyFailure>, Extract<S, AnyFailure>>
StreamClass#uniq as a static:
Stream.uniq(source, …)isStream.from(source).uniq(…). -
uniqBy<S>(): StreamClass<Exclude<S, AnyFailure>, Extract<S, AnyFailure>>source: Source<S>,key: (value: Exclude<S, AnyFailure>) => unknown
StreamClass#uniqBy as a static:
Stream.uniqBy(source, …)isStream.from(source).uniqBy(…). -
withIndex<S>(): StreamClass<source: Source<S>,offset?: number>readonly [Exclude<S, AnyFailure>, number],Extract<S, AnyFailure>
StreamClass#withIndex as a static:
Stream.withIndex(source, …)isStream.from(source).withIndex(…). -
zip<U extends readonly unknown[]>(...sources: [K in keyof U]: Source<U[K]>): StreamClass<>[K in keyof U]: Exclude<U[K], AnyFailure>,Extract<U[number], AnyFailure>
Zips sources in lockstep, ending at the shortest (Elixir's
Stream.zip) and closing the longer sources so their cleanup runs. JS divergences: sources are pulled sequentially per slot, and a failure element passes through bare without consuming from the other sources — failures cannot pair. -
zipWith<U extends readonly unknown[], R>(): StreamClass<Exclude<R, AnyFailure>, Extract<U[number] | R, AnyFailure>>sources: [K in keyof U]: Source<U[K]>,fn: (...values: [K in keyof U]: Exclude<U[K], AnyFailure>) => R
zipplus a combiner — Elixir'sStream.zip_with; the tuple arrives spread.
The producer half of StreamClass.channel: emit into it, consume stream out of it.
-
abort(reason: unknown): void
Rejects the consuming Task with
reason— the two-tier rule's bug tier. Idempotent. -
buffered: number
Elements buffered for a consumer that has not taken them yet.
-
close(): void
Ends the stream once the buffer drains. Idempotent.
-
closed: boolean
Whether the channel has been closed, aborted, or abandoned by its consumer.
-
dropped: number
How many elements the buffer has lost to overflow.
0unless a consumer fell behind. -
emit(value: T): boolean
Offers a value. Returns
falsewhen there is no room left — Node'swrite()convention: advisory for a producer that can slow down, ignorable for one that cannot. -
fail(error: E): boolean
Offers a declared failure as an element — the railway, not a rejection.
-
ready(): Promise<void>
Resolves once there is room to emit again — Web Streams'
writer.ready, and the promise form of Node's'drain'. -
stream: Stream<T, E>
The consuming half. One consumer only; a second pass throws.
How a Channel behaves when its consumer cannot keep up.
-
capacity: number
How many elements to buffer for a consumer that has not taken them yet. Default
10_000. -
onDemand: () => void
Called once, when a consumer first attaches. A producer that can defer starting should start here: it is the difference between a buffer that stays near empty and one that races ahead of a consumer that has not arrived.
-
onDiscard: () => voiddropped: T | E,buffered: number
Called with each element the buffer actually lost, and the depth after the loss. The only place overflow is observable — make it fatal from here by calling
failorabort. -
overflow: Overflow
What to do once
capacityis reached: drop from either end, or'fail'— end the stream with a ChannelOverflowFailure element instead of losing anything quietly. Default'dropOldest'.
The failure element a 'fail' channel ends with.
What a full channel does — the two GenStage :buffer_keep choices, named for the element that
goes rather than the one that stays, plus the option of refusing to lose anything silently.
Anything a Stream can be built from or flattened into: sync or async iterables (a web ReadableStream is async-iterable on every modern runtime).
A channel with overflow: 'fail' filled up: the consumer fell far enough behind that the
buffer could not hold the difference, and dropping was not on the table.
Usage
import * as mod from "lib/stream/stream.ts";