stream-chain
Version:
Chain functions, generators, Node streams, and Web streams into a pipeline with backpressure support.
360 lines (336 loc) • 11.6 kB
TypeScript
/**
* Special symbol that indicates that a function produced no value.
* Effectively, the rest of the pipeline is not used.
*/
export declare const none: unique symbol;
/**
* Special symbol that indicates that the pipeline should be stopped.
* Just like {@link none}, it produces no value.
*/
export declare const stop: unique symbol;
/** Used internally to mark a value as a final value. */
export declare const finalSymbol: unique symbol;
/** Used internally to mark a value as multiple values. */
export declare const manySymbol: unique symbol;
/** Used internally to mark a function as capable of being flushed. */
export declare const flushSymbol: unique symbol;
/** Used internally to mark a function as being derived from a function list. */
export declare const fListSymbol: unique symbol;
/**
* An exception that indicates that the pipeline should be stopped.
*/
export declare class Stop extends Error {}
/**
* Interface for a value that has been marked as a final value.
*/
export interface FinalValue<T = unknown> {
[finalSymbol]: 1;
value: T;
}
/**
* Type predicate for `FinalValue`.
* @param o object to test
* @returns `true` if `o` is a `FinalValue`
*/
export declare function isFinalValue(o: unknown): o is FinalValue;
/**
* Creates a `FinalValue`
* @param value the wrapped value
* @returns a `FinalValue`
*/
export declare function finalValue<T>(value: T): FinalValue<T>;
/**
* Retrieves the value of a `FinalValue`
* @param o a `FinalValue` object
* @returns the wrapped value
*/
export declare function getFinalValue<T>(o: FinalValue<T>): T;
/**
* Alias for {@link finalValue}
*/
export declare const final: typeof finalValue;
/**
* Interface for a value that has been marked as multiple values.
* It is used to return multiple values from a regular (non-generator) function.
*/
export interface Many<T = unknown> {
[manySymbol]: 1;
values: T[];
}
/**
* Type predicate for `Many`.
* @param o object to test
* @returns `true` if `o` is a `Many`
*/
export declare function isMany(o: unknown): o is Many;
/**
* Creates a `Many`
* @param values the wrapped values
* @returns a `Many`
*/
export declare function many<T>(values: T[]): Many<T>;
/**
* Retrieves the values of a `Many`
* @param o a `Many` object
* @returns the wrapped values
*/
export declare function getManyValues<T>(o: Many<T>): T[];
/**
* Interface for a function that can be flushed.
* If it is marked as flushable, it will be called with the special {@link none} value
* when the pipeline is stopped so it can produce the last value.
*/
export interface Flushable<I = unknown, O = unknown> {
(value: I, ...rest: any[]): O;
[flushSymbol]: 1;
}
/**
* Type predicate for `Flushable`.
* @param o function to test
* @returns `true` if `o` is a `Flushable`
*/
export declare function isFlushable<I, O>(o: (value: I, ...rest: any[]) => O): o is Flushable<I, O>;
/**
* Creates a `Flushable`
* @param write function to be marked as flushable
* @param final an optional function to be called when the pipeline is stopped
* @returns a `Flushable`
* @remarks If `final` is not provided, `write` will be called with {@link none} when the pipeline is stopped
*/
export declare function flushable<I, O>(
write: (value: I, ...rest: any[]) => O,
final?: () => O
): Flushable<I, O>;
/**
* Interface for a function that can be derived from a function list.
* `chain` can use the list instead of the original function.
*/
export interface FunctionList<
T extends (...args: readonly any[]) => unknown,
I = unknown,
O = unknown
> {
(value: I, ...rest: any[]): O;
[fListSymbol]: 1;
fList: T[];
}
/**
* Type predicate for `FunctionList`.
* @param o function to test
* @returns `true` if `o` is a `FunctionList`
*/
export declare function isFunctionList<I, O>(
o: (value: I, ...rest: readonly any[]) => O
): o is FunctionList<(...args: readonly any[]) => unknown, I, O>;
/**
* Sets a function list creating a `FunctionList` structure.
* @param o function to be marked as a function list
* @param fns function list
* @returns `o` as a `FunctionList`
*/
export declare function setFunctionList<
T extends (...args: readonly any[]) => unknown,
F extends (...args: readonly any[]) => unknown
>(
o: F,
fns: T[]
): F extends (value: infer I, ...rest: any[]) => infer O ? FunctionList<T, I, O> : never;
/**
* Retrieves the function list of a `FunctionList`
* @param o a `FunctionList` object
* @returns the function list
*/
export declare function getFunctionList<
T extends (...args: readonly any[]) => unknown,
I = unknown,
O = unknown
>(o: FunctionList<T, I, O>): T[];
/**
* Clears the function list from a `FunctionList`
* @param o a `FunctionList` object
* @returns `o` as a `FunctionList`
*/
export declare function clearFunctionList<
T extends (...args: readonly any[]) => unknown,
I = unknown,
O = unknown
>(o: FunctionList<T, I, O>): (value: I, ...rest: any[]) => O;
/**
* Convert a value to `Many`.
* @param value the value to convert
* @returns a `Many` containing the value
* @remarks `Many` is used to return multiple values from a regular (non-generator) function.
*/
export declare function toMany<T>(value: Many<T>): Many<T>;
export declare function toMany(value: typeof none): Many<never>;
export declare function toMany<T>(value: T): Many<T>;
/**
* Normalize a value by unbundling it if it is a `Many`.
* @param value the value to normalize
* @returns the normalized value
*/
export declare function normalizeMany(value: unknown): unknown;
/**
* Extracts the element type from a `combineMany`/`combineManyMut` argument.
* `none` contributes nothing, `Many<T>` contributes `T`, anything else contributes itself.
*/
export type CombineManyItem<T> = T extends typeof none ? never : T extends Many<infer U> ? U : T;
/**
* Combine values into a `Many`.
* @param args values to combine
* @returns a `Many` containing all values
*/
export declare function combineMany<T extends unknown[]>(
...args: T
): Many<CombineManyItem<T[number]>>;
/**
* Combine values into a `Many` mutably.
* @param a the first value (may be modified in-place if it is a `Many`)
* @param args additional values to combine
* @returns a `Many` containing all values
* @remarks only the first argument may be modified in-place
*/
export declare function combineManyMut<A, T extends unknown[]>(
a: A,
...args: T
): Many<CombineManyItem<A> | CombineManyItem<T[number]>>;
// Stream type guards (Web + Node, shape-based — neither imports `node:stream`)
/**
* Type predicate for a Web Streams `ReadableStream`. Shape-based — checks for
* `getReader` and `pipeTo` methods, which Node Streams do not expose.
*/
export declare function isReadableWebStream(x: unknown): x is ReadableStream;
/**
* Type predicate for a Web Streams `WritableStream`. Shape-based — checks for
* `getWriter` and `abort` methods, which Node Streams do not expose.
*/
export declare function isWritableWebStream(x: unknown): x is WritableStream;
/**
* Type predicate for a Web Streams duplex pair `{readable, writable}` (the
* shape returned by `TransformStream` and `asWebStream`).
*/
export declare function isDuplexWebStream(
x: unknown
): x is {readable: ReadableStream; writable: WritableStream};
/**
* Type predicate for a Node `Readable` (including `Duplex`/`Transform`).
* Shape-based — checks for `.pipe` / `.on` and `_readableState`. Does not
* import `node:stream`, so browser-safe to call.
*/
export declare function isReadableNodeStream(x: unknown): boolean;
/**
* Type predicate for a Node `Writable` (including `Duplex`/`Transform`).
* Shape-based — checks for `.write` / `.on` and `_writableState`.
*/
export declare function isWritableNodeStream(x: unknown): boolean;
/**
* Type predicate for a Node `Duplex`/`Transform`. Shape-based — combines the
* readable and writable checks plus the `_readableState` marker.
*/
export declare function isDuplexNodeStream(x: unknown): boolean;
// generic utilities: unpacking types
/**
* Generic utility for getting the return type of a function including async and generators.
* Naked-T conditional so it distributes over unions — needed when a function returns a
* shape like `Many<R> | Promise<Many<R>>` (the surface of `fun(...)`'s output). Without
* distribution the whole-union `extends Promise<unknown>` test fails on `Many<R>` alone,
* `Promise<Many<R>>` falls through unwrapped, and the inner `Many` leaks downstream.
*/
export type UnpackReturnType<F extends (...args: readonly any[]) => unknown> = UnpackReturnTypeOf<
ReturnType<F>
>;
type UnpackReturnTypeOf<T> =
T extends AsyncGenerator<infer O, unknown, unknown>
? O
: T extends Generator<infer O, unknown, unknown>
? O
: T extends Promise<infer U>
? UnpackReturnTypeOf<U>
: T;
/**
* `stream-chain`-specific utility for getting the type from functions used in a function list.
*/
export type UnpackType<T> =
T extends Many<infer U>
? U
: T extends FinalValue<infer U>
? U
: Exclude<T, typeof none | typeof stop>;
/**
* Unpacking the return type of a function as a combination of {@link UnpackType} and {@link UnpackReturnType}.
*/
export type OutputType<F extends (...args: readonly any[]) => unknown> = UnpackType<
UnpackReturnType<F>
>;
// generic utilities: working with tuples
/**
* Returns the first element of a tuple or `never`.
*/
export type First<L extends readonly unknown[]> = L extends readonly [
infer T,
...(readonly unknown[])
]
? T
: never;
/**
* Returns the last element of a tuple or `never`.
*/
export type Last<L extends readonly unknown[]> = L extends readonly [
...(readonly unknown[]),
infer T
]
? T
: never;
/**
* Flattens a tuple of tuples recursively returning a flat tuple.
*/
export type Flatten<L extends readonly unknown[]> = L extends readonly [infer T, ...infer R]
? T extends readonly unknown[]
? readonly [...Flatten<T>, ...Flatten<R>]
: readonly [T, ...Flatten<R>]
: L;
/**
* Filters a tuple removing all elements of a specified type returning a filtered tuple.
*/
export type Filter<L extends readonly unknown[], X> = L extends readonly [infer T, ...infer R]
? T extends X
? Filter<R, X>
: readonly [T, ...Filter<R, X>]
: L;
/**
* Flattens and filters a tuple. See {@link Flatten} and {@link Filter}.
*/
export type AsFlatList<L extends readonly unknown[]> = Filter<Flatten<L>, null | undefined>;
// generic utilities: working with functions
/**
* A generic one-argument function. Used internally.
*/
export type Fn = (arg: any, ...args: readonly unknown[]) => unknown;
/**
* Returns the first argument of a function or a function list or `never`.
*/
export type Arg0<F> = 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 unpacked return type of a function or a function list or `never`.
*/
export type Ret<F, Default = unknown> = 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;