stream-chain
Version:
Chain functions, generators, Node streams, and Web streams into a pipeline with backpressure support.
313 lines (294 loc) • 11.5 kB
TypeScript
/// <reference types="node" />
import {Duplex, DuplexOptions, Readable, Transform, Writable} from 'node:stream';
import {TypedDuplex, TypedReadable, TypedTransform, TypedWritable} from './typed-streams.js';
import {
none,
stop,
Stop,
finalSymbol,
finalValue,
final,
isFinalValue,
getFinalValue,
manySymbol,
many,
isMany,
getManyValues,
getFunctionList,
flushSymbol,
flushable,
isFlushable,
fListSymbol,
isFunctionList,
setFunctionList,
clearFunctionList,
toMany,
normalizeMany,
combineMany,
combineManyMut,
type AsFlatList,
type Fn,
type OutputType
} from './defs.js';
import gen from './gen.js';
import asStream from './asStream.js';
import asWebStream from './asWebStream.js';
/**
* Creates a stream object out of a list of functions and streams.
* @param fns array of functions, streams, or arrays (flattened recursively). Falsy items are ignored.
* @param options optional `Duplex` options plus `{noGrouping?, skipEvents?}`. See {@link ChainOptions}. Defaults to `{writableObjectMode: true, readableObjectMode: true}`.
* @returns a `Duplex` stream extended with `.streams`, `.input`, and `.output` properties.
* @remarks This is the main function of this library.
*/
declare function chain<L extends readonly unknown[]>(
fns: chain.ChainList<chain.Arg0<L>, L>,
options?: chain.ChainOptions
): chain.ChainOutput<chain.Arg0<L>, chain.Ret<L>>;
/**
* Same as {@link chain} but bypasses TypeScript type checking on `fns`. Use when
* you need an escape hatch for inputs the strict signature can't express.
* @param fns array of functions, streams, or nested arrays (flattened recursively). Type checking is intentionally not applied.
* @param options optional `Duplex` options plus `{noGrouping?, skipEvents?}`. See {@link ChainOptions}.
* @returns a `Duplex` stream extended with `.streams`, `.input`, and `.output` properties, typed as `ChainOutput<W, R>` (caller-supplied type parameters).
*/
declare function chainUnchecked<W = any, R = any>(
fns: readonly any[],
options?: chain.ChainOptions
): chain.ChainOutput<W, R>;
declare namespace chain {
/**
* Represents a typed duplex stream as a pair of readable and writable streams.
*/
export type DuplexStream<W = unknown, R = unknown> = {
readable: ReadableStream<R>;
writable: WritableStream<W>;
};
/**
* Options for the chain function, which is based on `DuplexOptions`.
*/
export interface ChainOptions extends DuplexOptions {
/** If `true`, no groupings will be done. Each function will be a separate stream object. */
noGrouping?: boolean;
/** If `true`, event bindings to the chain stream object will be skipped. */
skipEvents?: boolean;
}
/**
* The tuple type for a chain function with one item.
*/
type ChainStreams1 = [Readable | Writable | Duplex | Transform];
/**
* The tuple type for a chain function with multiple items.
*/
type ChainStreams = [
Readable | Duplex | Transform,
...(Duplex | Transform)[],
Writable | Duplex | Transform
];
/**
* Read-side overrides added on top of `Duplex` for chain outputs.
* Notes on what's NOT here:
* - `write`/`end` are not narrowed to `W` because doing so breaks structural
* compatibility with `NodeJS.WritableStream` (which fixes `chunk: string | Uint8Array`
* for non-objectMode streams) and consequently breaks `readable.pipe(chain)`.
* `W` rides via the phantom `__streamTypeW` (mirroring `TypedDuplex`) so
* `Arg0<ChainOutput<W, R>>` recovers `W` for chain-of-chain inference.
*/
interface ChainOutputExtensions<W, R> {
/** Phantom carrier for the writable-side type. Type-only — never callable at runtime. */
__streamTypeW(): W;
/** Phantom carrier for the readable-side type. Type-only — never callable at runtime. */
__streamTypeR(): R;
/** Internal list of streams. */
streams: ChainStreams1 | ChainStreams;
/** The first stream, which can be used to feed the chain and to attach event handlers. */
input: Readable | Writable | Duplex | Transform;
/** The last stream, which can be used to consume results and to attach event handlers. */
output: Readable | Writable | Duplex | Transform;
[Symbol.asyncIterator](): NodeJS.AsyncIterator<R>;
read(size?: number): R;
push(chunk: R | null, encoding?: BufferEncoding): boolean;
on(event: 'data', listener: (chunk: R) => void): this;
once(event: 'data', listener: (chunk: R) => void): this;
addListener(event: 'data', listener: (chunk: R) => void): this;
off(event: 'data', listener: (chunk: R) => void): this;
removeListener(event: 'data', listener: (chunk: R) => void): this;
emit(event: 'data', chunk: R): boolean;
}
/**
* Represents the output of the chain function. It is based on `Duplex` with extra properties.
* `Omit` strips the `any`-returning read/push/asyncIterator members so the R-typed
* overrides in `ChainOutputExtensions` win; the remaining `Duplex` members (including
* the typed event-emitter overloads and `write`/`end`/`pipe` compatibility) survive via
* intersection. `W` rides via the `__streamTypeW` phantom in `ChainOutputExtensions`
* so `Arg0`/`Ret` can recover both type parameters when a chain output appears inside
* another chain.
*/
export type ChainOutput<W, R> = Omit<Duplex, 'read' | 'push' | typeof Symbol.asyncIterator> &
ChainOutputExtensions<W, R>;
/**
* Returns the first argument of a chain, a stream, or a function.
*/
export type Arg0<F> =
F extends ChainOutput<infer W, any>
? W
: F extends TypedTransform<infer W, any>
? W
: F extends TypedDuplex<infer W, any>
? W
: F extends TypedReadable<any>
? never
: F extends TypedWritable<infer W>
? W
: F extends Writable | Transform | Duplex
? any
: F extends Readable
? never
: F extends TransformStream<infer W, any>
? W
: F extends DuplexStream<infer W, any>
? W
: F extends WritableStream<infer W>
? W
: F extends ReadableStream<any>
? never
: F extends readonly unknown[]
? AsFlatList<F> extends readonly [infer F1, ...(readonly unknown[])]
? Arg0<F1>
: AsFlatList<F> extends readonly []
? unknown
: AsFlatList<F> extends readonly (infer F1)[]
? Arg0<F1>
: never
: F extends (...args: readonly any[]) => unknown
? Parameters<F>[0]
: never;
/**
* Returns the return type of a chain, a stream, or a function.
*/
export type Ret<F, Default = unknown> =
F extends ChainOutput<any, infer R>
? R
: F extends TypedTransform<any, infer R>
? R
: F extends TypedDuplex<any, infer R>
? R
: F extends TypedReadable<infer R>
? R
: F extends TypedWritable<any>
? never
: F extends Readable | Transform | Duplex
? any
: F extends Writable
? never
: F extends TransformStream<any, infer R>
? R
: F extends DuplexStream<any, infer R>
? R
: F extends ReadableStream<infer R>
? R
: F extends WritableStream<any>
? never
: F extends readonly unknown[]
? AsFlatList<F> extends readonly [...unknown[], infer F1]
? Ret<F1, Default>
: AsFlatList<F> extends readonly []
? Default
: AsFlatList<F> extends readonly (infer F1)[]
? Ret<F1, Default>
: never
: F extends Fn
? OutputType<F>
: never;
/**
* Represents an item in the chain function.
* It is used to highlight mismatches between argument types and return types in a list.
*/
export type ChainItem<I, F> =
F extends TypedTransform<infer W, infer R>
? I extends W
? F
: TypedTransform<I, R>
: F extends TypedDuplex<infer W, infer R>
? I extends W
? F
: TypedDuplex<I, R>
: F extends TypedReadable<any>
? [I] extends [never]
? F
: never
: F extends TypedWritable<infer W>
? I extends W
? F
: TypedWritable<I>
: F extends Writable | Transform | Duplex
? F
: F extends Readable
? [I] extends [never]
? F
: never
: F extends TransformStream<infer W, infer R>
? I extends W
? F
: TransformStream<I, R>
: F extends DuplexStream<infer W, infer R>
? I extends W
? F
: DuplexStream<I, R>
: F extends ReadableStream<any>
? [I] extends [never]
? F
: never
: F extends WritableStream<infer W>
? I extends W
? F
: WritableStream<I>
: F extends readonly [infer F1, ...infer R]
? F1 extends null | undefined
? readonly [F1, ...ChainList<I, R>]
: readonly [ChainItem<I, F1>, ...ChainList<Ret<F1, I>, R>]
: F extends readonly unknown[]
? readonly [ChainItem<I, any>]
: F extends Fn
? I extends Arg0<F>
? F
: (arg: I, ...rest: readonly unknown[]) => ReturnType<F>
: never;
/**
* Replicates a tuple verifying the types of the list items so arguments match returns.
* The replicated tuple is used to highlight mismatches between list items.
*/
export type ChainList<I, L> = L extends readonly [infer F1, ...infer R]
? F1 extends null | undefined
? readonly [F1, ...ChainList<I, R>]
: readonly [ChainItem<I, F1>, ...ChainList<Ret<F1, I>, R>]
: L;
}
export default chain;
export {chain, chainUnchecked, gen, asStream, asWebStream};
export {dataSource} from './dataSource.js';
export {
none,
stop,
Stop,
finalSymbol,
finalValue,
final,
isFinalValue,
getFinalValue,
manySymbol,
many,
isMany,
getManyValues,
getFunctionList,
flushSymbol,
flushable,
isFlushable,
fListSymbol,
isFunctionList,
setFunctionList,
clearFunctionList,
toMany,
normalizeMany,
combineMany,
combineManyMut
};