import {isError, isOk} from '../internal/result'; import type {GenericCallback} from '../models'; import type {UnwrapValue} from '../result/models'; import {assert, type Asserter} from './assert'; // #region Types /** * A synchronous _Flow_, a function that pipe a value through a series of functions */ export type Flow = ( ...args: Parameters ) => UnwrapValue; /** * An asynchronous _Flow_, a function that pipes a value through a series of functions */ export type FlowPromise = ( ...args: Parameters ) => Promise>; // #endregion // #region Functions // #region Flow // #region Asynchronous /** * Create an asynchronous _Flow_, a function that pipes values through a function * * _Available as `asyncFlow` and `flow.async`_ * * @returns _Flow_ function */ export function asyncFlow(fn: Fn): FlowPromise>; /** * Create an asynchronous _Flow_, a function that pipes values through a series of functions * * _Available as `asyncFlow` and `flow.async`_ * * @returns _Flow_ function */ export function asyncFlow( first: First, second: (value: Awaited>>) => Second, ): FlowPromise; /** * Create an asynchronous _Flow_, a function that pipes values through a series of functions * * _Available as `asyncFlow` and `flow.async`_ * * @returns _Flow_ function */ export function asyncFlow( first: First, second: (value: Awaited>>) => Second, third: (value: Awaited>) => Third, ): FlowPromise; /** * Create an asynchronous _Flow_, a function that pipes values through a series of functions * * _Available as `asyncFlow` and `flow.async`_ * * @returns _Flow_ function */ export function asyncFlow( first: First, second: (value: Awaited>>) => Second, third: (value: Awaited>) => Third, fourth: (value: Awaited>) => Fourth, ): FlowPromise; /** * Create an asynchronous _Flow_, a function that pipes values through a series of functions * * _Available as `asyncFlow` and `flow.async`_ * * @returns _Flow_ function */ export function asyncFlow( first: First, second: (value: Awaited>>) => Second, third: (value: Awaited>) => Third, fourth: (value: Awaited>) => Fourth, fifth: (value: Awaited>) => Fifth, ): FlowPromise; /** * Create an asynchronous _Flow_, a function that pipes values through a series of functions * * _Available as `asyncFlow` and `flow.async`_ * * @returns _Flow_ function */ export function asyncFlow( first: First, second: (value: Awaited>>) => Second, third: (value: Awaited>) => Third, fourth: (value: Awaited>) => Fourth, fifth: (value: Awaited>) => Fifth, sixth: (value: Awaited>) => Sixth, ): FlowPromise; /** * Create an asynchronous _Flow_, a function that pipes values through a series of functions * * _Available as `asyncFlow` and `flow.async`_ * * @returns _Flow_ function */ export function asyncFlow< First extends GenericCallback, Second, Third, Fourth, Fifth, Sixth, Seventh, >( first: First, second: (value: Awaited>>) => Second, third: (value: Awaited>) => Third, fourth: (value: Awaited>) => Fourth, fifth: (value: Awaited>) => Fifth, sixth: (value: Awaited>) => Sixth, seventh: (value: Awaited>) => Seventh, ): FlowPromise; /** * Create an asynchronous _Flow_, a function that pipes values through a series of functions * * _Available as `asyncFlow` and `flow.async`_ * * @returns _Flow_ function */ export function asyncFlow< First extends GenericCallback, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, >( first: First, second: (value: Awaited>>) => Second, third: (value: Awaited>) => Third, fourth: (value: Awaited>) => Fourth, fifth: (value: Awaited>) => Fifth, sixth: (value: Awaited>) => Sixth, seventh: (value: Awaited>) => Seventh, eighth: (value: Awaited>) => Eighth, ): FlowPromise; /** * Create an asynchronous _Flow_, a function that pipes values through a series of functions * * _Available as `asyncFlow` and `flow.async`_ * * @returns _Flow_ function */ export function asyncFlow< First extends GenericCallback, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, Ninth, >( first: First, second: (value: Awaited>>) => Second, third: (value: Awaited>) => Third, fourth: (value: Awaited>) => Fourth, fifth: (value: Awaited>) => Fifth, sixth: (value: Awaited>) => Sixth, seventh: (value: Awaited>) => Seventh, eighth: (value: Awaited>) => Eighth, ninth: (value: Awaited>) => Ninth, ): FlowPromise; /** * Create an asynchronous _Flow_, a function that pipes values through a series of functions * * _Available as `asyncFlow` and `flow.async`_ * * @returns _Flow_ function */ export function asyncFlow< First extends GenericCallback, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, Ninth, Tenth, >( first: First, second: (value: Awaited>>) => Second, third: (value: Awaited>) => Third, fourth: (value: Awaited>) => Fourth, fifth: (value: Awaited>) => Fifth, sixth: (value: Awaited>) => Sixth, seventh: (value: Awaited>) => Seventh, eighth: (value: Awaited>) => Eighth, ninth: (value: Awaited>) => Ninth, tenth: (value: Awaited>) => Tenth, ): FlowPromise; /** * Create an asynchronous _Flow_, a function that pipes values through a series of functions * * _Available as `asyncFlow` and `flow.async`_ * * @returns _Flow_ function */ export function asyncFlow( fn: Fn, ...fns: Array<(value: Awaited>>) => unknown> ): FlowPromise>; /** * Create an asynchronous _Flow_, a function that pipes values through a series of functions * * _Available as `asyncFlow` and `flow.async`_ * * @returns _Flow_ function */ export function asyncFlow(...fns: GenericCallback[]): (...args: unknown[]) => Promise; export function asyncFlow(...fns: GenericCallback[]): (...args: unknown[]) => Promise { assertFlowFunctions(fns); return (...args: unknown[]): Promise => asyncWork( args.map(value => { if (isError(value)) { throw value.error; } return isOk(value) ? value.value : value; }), fns, true, ); } // #endregion // #region Synchronous /** * Create a _Flow_, a function that pipes values through a function * * @returns _Flow_ function */ export function flow(fn: Fn): Flow>; /** * Create a _Flow_, a function that pipes values through a series of functions * * @returns _Flow_ function */ export function flow( first: First, second: (value: UnwrapValue>) => Second, ): Flow; /** * Create a _Flow_, a function that pipes values through a series of functions * * @returns _Flow_ function */ export function flow( first: First, second: (value: UnwrapValue>) => Second, third: (value: UnwrapValue) => Third, ): Flow; /** * Create a _Flow_, a function that pipes values through a series of functions * * @returns _Flow_ function */ export function flow( first: First, second: (value: UnwrapValue>) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, ): Flow; /** * Create a _Flow_, a function that pipes values through a series of functions * * @returns _Flow_ function */ export function flow( first: First, second: (value: UnwrapValue>) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, fifth: (value: UnwrapValue) => Fifth, ): Flow; /** * Create a _Flow_, a function that pipes values through a series of functions * * @returns _Flow_ function */ export function flow( first: First, second: (value: UnwrapValue>) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, fifth: (value: UnwrapValue) => Fifth, sixth: (value: UnwrapValue) => Sixth, ): Flow; /** * Create a _Flow_, a function that pipes values through a series of functions * * @returns _Flow_ function */ export function flow( first: First, second: (value: UnwrapValue>) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, fifth: (value: UnwrapValue) => Fifth, sixth: (value: UnwrapValue) => Sixth, seventh: (value: UnwrapValue) => Seventh, ): Flow; /** * Create a _Flow_, a function that pipes values through a series of functions * * @returns _Flow_ function */ export function flow< First extends GenericCallback, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, >( first: First, second: (value: UnwrapValue>) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, fifth: (value: UnwrapValue) => Fifth, sixth: (value: UnwrapValue) => Sixth, seventh: (value: UnwrapValue) => Seventh, eighth: (value: UnwrapValue) => Eighth, ): Flow; /** * Create a _Flow_, a function that pipes values through a series of functions * * @returns _Flow_ function */ export function flow< First extends GenericCallback, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, Ninth, >( first: First, second: (value: UnwrapValue>) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, fifth: (value: UnwrapValue) => Fifth, sixth: (value: UnwrapValue) => Sixth, seventh: (value: UnwrapValue) => Seventh, eighth: (value: UnwrapValue) => Eighth, ninth: (value: UnwrapValue) => Ninth, ): Flow; /** * Create a _Flow_, a function that pipes values through a series of functions * * @returns _Flow_ function */ export function flow< First extends GenericCallback, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, Ninth, Tenth, >( first: First, second: (value: UnwrapValue>) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, fifth: (value: UnwrapValue) => Fifth, sixth: (value: UnwrapValue) => Sixth, seventh: (value: UnwrapValue) => Seventh, eighth: (value: UnwrapValue) => Eighth, ninth: (value: UnwrapValue) => Ninth, tenth: (value: UnwrapValue) => Tenth, ): Flow; /** * Create a _Flow_, a function that pipes values through a series of functions * * @returns _Flow_ function */ export function flow( first: First, ...fns: Array<(value: UnwrapValue>) => unknown> ): Flow>; /** * Create a _Flow_, a function that pipes values through a series of functions * * @returns _Flow_ function */ export function flow(...fns: GenericCallback[]): (...args: unknown[]) => unknown; export function flow(...fns: GenericCallback[]): (...args: unknown[]) => unknown { assertFlowFunctions(fns); return (...args: unknown[]): unknown => work( args.map(value => { if (isError(value)) { throw value.error; } return isOk(value) ? value.value : value; }), fns, true, ); } flow.async = asyncFlow; // #endregion // #endregion // #region Pipe // #region Asynchronous /** * Pipe a value through a function * * _Available as `asyncPipe` and `pipe.async`_ * * @param value Initial value * @returns _Piped_ result */ export async function asyncPipe( value: Initial, pipe: (value: UnwrapValue) => Piped, ): Promise>; /** * Pipe a value through a series of functions * * _Available as `asyncPipe` and `pipe.async`_ * * @param value Initial value * @returns _Piped_ result */ export async function asyncPipe( value: Initial, first: (value: UnwrapValue) => First, second: (value: UnwrapValue) => Second, ): Promise>; /** * Pipe a value through a series of functions * * _Available as `asyncPipe` and `pipe.async`_ * * @param value Initial value * @returns _Piped_ result */ export async function asyncPipe( value: Initial, first: (value: UnwrapValue) => First, second: (value: UnwrapValue) => Second, third: (value: UnwrapValue) => Third, ): Promise>; /** * Pipe a value through a series of functions * * _Available as `asyncPipe` and `pipe.async`_ * * @param value Initial value * @returns _Piped_ result */ export async function asyncPipe( value: Initial, first: (value: UnwrapValue) => First, second: (value: UnwrapValue) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, ): Promise>; /** * Pipe a value through a series of functions * * _Available as `asyncPipe` and `pipe.async`_ * * @param value Initial value * @returns _Piped_ result */ export async function asyncPipe( value: Initial, first: (value: UnwrapValue) => First, second: (value: UnwrapValue) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, fifth: (value: UnwrapValue) => Fifth, ): Promise>; /** * Pipe a value through a series of functions * * _Available as `asyncPipe` and `pipe.async`_ * * @param value Initial value * @returns _Piped_ result */ export async function asyncPipe( value: Initial, first: (value: UnwrapValue) => First, second: (value: UnwrapValue) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, fifth: (value: UnwrapValue) => Fifth, sixth: (value: UnwrapValue) => Sixth, ): Promise>; /** * Pipe a value through a series of functions * * _Available as `asyncPipe` and `pipe.async`_ * * @param value Initial value * @returns _Piped_ result */ export async function asyncPipe( value: Initial, first: (value: UnwrapValue) => First, second: (value: UnwrapValue) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, fifth: (value: UnwrapValue) => Fifth, sixth: (value: UnwrapValue) => Sixth, seventh: (value: UnwrapValue) => Seventh, ): Promise>; /** * Pipe a value through a series of functions * * _Available as `asyncPipe` and `pipe.async`_ * * @param value Initial value * @returns _Piped_ result */ export async function asyncPipe< Initial, First, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, >( value: Initial, first: (value: UnwrapValue) => First, second: (value: UnwrapValue) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, fifth: (value: UnwrapValue) => Fifth, sixth: (value: UnwrapValue) => Sixth, seventh: (value: UnwrapValue) => Seventh, eighth: (value: UnwrapValue) => Eighth, ): Promise>; /** * Pipe a value through a series of functions * * _Available as `asyncPipe` and `pipe.async`_ * * @param value Initial value * @returns _Piped_ result */ export async function asyncPipe< Initial, First, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, Ninth, >( value: Initial, first: (value: UnwrapValue) => First, second: (value: UnwrapValue) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, fifth: (value: UnwrapValue) => Fifth, sixth: (value: UnwrapValue) => Sixth, seventh: (value: UnwrapValue) => Seventh, eighth: (value: UnwrapValue) => Eighth, ninth: (value: UnwrapValue) => Ninth, ): Promise>; /** * Pipe a value through a series of functions * * _Available as `asyncPipe` and `pipe.async`_ * * @param value Initial value * @returns _Piped_ result */ export async function asyncPipe< Initial, First, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, Ninth, Tenth, >( value: Initial, first: (value: UnwrapValue) => First, second: (value: UnwrapValue) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, fifth: (value: UnwrapValue) => Fifth, sixth: (value: UnwrapValue) => Sixth, seventh: (value: UnwrapValue) => Seventh, eighth: (value: UnwrapValue) => Eighth, ninth: (value: UnwrapValue) => Ninth, tenth: (value: UnwrapValue) => Tenth, ): Promise>; /** * Pipe a value through a series of functions * * _Available as `asyncPipe` and `pipe.async`_ * * @param value Initial value * @returns _Piped_ result */ export async function asyncPipe( value: Value, ...pipes: Array<(value: Value) => Value> ): Promise>; /** * Pipe a value through a series of functions * * _Available as `asyncPipe` and `pipe.async`_ * * @param value Initial value * @returns _Piped_ result */ export async function asyncPipe( value: unknown, ...pipes: Array<(value: unknown) => unknown> ): Promise; export async function asyncPipe( value: unknown, ...pipes: Array<(value: unknown) => unknown> ): Promise { assertPipeFunctions(pipes); return asyncWork(value, pipes, false); } // #endregion // #region Synchronous /** * Pipe a value through a function * * @param value Initial value * @returns _Piped_ result */ export function pipe( value: Initial, pipe: (value: UnwrapValue) => Piped, ): UnwrapValue; /** * Pipe a value through a series of functions * * @param value Initial value * @returns _Piped_ result */ export function pipe( value: Initial, first: (value: UnwrapValue) => First, second: (value: UnwrapValue) => Second, ): UnwrapValue; /** * Pipe a value through a series of functions * * @param value Initial value * @returns _Piped_ result */ export function pipe( value: Initial, first: (value: UnwrapValue) => First, second: (value: UnwrapValue) => Second, third: (value: UnwrapValue) => Third, ): UnwrapValue; /** * Pipe a value through a series of functions * * @param value Initial value * @returns _Piped_ result */ export function pipe( value: Initial, first: (value: UnwrapValue) => First, second: (value: UnwrapValue) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, ): UnwrapValue; /** * Pipe a value through a series of functions * * @param value Initial value * @returns _Piped_ result */ export function pipe( value: Initial, first: (value: UnwrapValue) => First, second: (value: UnwrapValue) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, fifth: (value: UnwrapValue) => Fifth, ): UnwrapValue; /** * Pipe a value through a series of functions * * @param value Initial value * @returns _Piped_ result */ export function pipe( value: Initial, first: (value: UnwrapValue) => First, second: (value: UnwrapValue) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, fifth: (value: UnwrapValue) => Fifth, sixth: (value: UnwrapValue) => Sixth, ): UnwrapValue; /** * Pipe a value through a series of functions * * @param value Initial value * @returns _Piped_ result */ export function pipe( value: Initial, first: (value: UnwrapValue) => First, second: (value: UnwrapValue) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, fifth: (value: UnwrapValue) => Fifth, sixth: (value: UnwrapValue) => Sixth, seventh: (value: UnwrapValue) => Seventh, ): UnwrapValue; /** * Pipe a value through a series of functions * * @param value Initial value * @returns _Piped_ result */ export function pipe( value: Initial, first: (value: UnwrapValue) => First, second: (value: UnwrapValue) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, fifth: (value: UnwrapValue) => Fifth, sixth: (value: UnwrapValue) => Sixth, seventh: (value: UnwrapValue) => Seventh, eighth: (value: UnwrapValue) => Eighth, ): UnwrapValue; /** * Pipe a value through a series of functions * * @param value Initial value * @returns _Piped_ result */ export function pipe( value: Initial, first: (value: UnwrapValue) => First, second: (value: UnwrapValue) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, fifth: (value: UnwrapValue) => Fifth, sixth: (value: UnwrapValue) => Sixth, seventh: (value: UnwrapValue) => Seventh, eighth: (value: UnwrapValue) => Eighth, ninth: (value: UnwrapValue) => Ninth, ): UnwrapValue; /** * Pipe a value through a series of functions * * @param value Initial value * @returns _Piped_ result */ export function pipe< Initial, First, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, Ninth, Tenth, >( value: Initial, first: (value: UnwrapValue) => First, second: (value: UnwrapValue) => Second, third: (value: UnwrapValue) => Third, fourth: (value: UnwrapValue) => Fourth, fifth: (value: UnwrapValue) => Fifth, sixth: (value: UnwrapValue) => Sixth, seventh: (value: UnwrapValue) => Seventh, eighth: (value: UnwrapValue) => Eighth, ninth: (value: UnwrapValue) => Ninth, tenth: (value: UnwrapValue) => Tenth, ): UnwrapValue; /** * Pipe a value through a series of functions * * @param value Initial value * @returns _Piped_ result */ export function pipe( value: Value, ...pipes: Array<(value: Value) => Value> ): UnwrapValue; /** * Pipe a value through a series of functions * * @param value Initial value * @returns _Piped_ result */ export function pipe(value: unknown, ...pipes: Array<(value: unknown) => unknown>): unknown; export function pipe(value: unknown, ...pipes: GenericCallback[]): unknown { assertPipeFunctions(pipes); return work(value, pipes, false); } pipe.async = asyncPipe; // #endregion // #endregion // #region Work async function asyncWork( initial: unknown, functions: GenericCallback[], flow: boolean, ): Promise { const {length} = functions; let transformed = unwrapValue(initial); for (let index = 0; index < length; index += 1) { const fn = functions[index]; const value = flow && index === 0 && Array.isArray(initial) ? await fn(...(initial as unknown[])) : await fn(transformed); transformed = unwrapValue(value); } return transformed; } function unwrapValue(value: unknown, flow?: boolean, nested?: boolean): unknown { if (typeof value === 'function') { if (nested != null) { throw new TypeError(MESSAGE_NESTING); } return unwrapValue(value(), flow, true); } if (flow != null && value instanceof Promise) { throw new TypeError(flow ? MESSAGE_FLOW_PROMISE : MESSAGE_PIPE_PROMISE); } if (isError(value)) { throw value.error; } return isOk(value) ? value.value : value; } function work(initial: unknown, functions: GenericCallback[], flow: boolean): unknown { const {length} = functions; let transformed = unwrapValue(initial, flow); for (let index = 0; index < length; index += 1) { const fn = functions[index]; const value = flow && index === 0 && Array.isArray(initial) ? fn(...(initial as unknown[])) : fn(transformed); transformed = unwrapValue(value, flow); } return transformed; } // #endregion // #endregion // #region Variables const MESSAGE_FLOW_ARRAY = 'Flow expected to receive an array of functions'; const MESSAGE_FLOW_PROMISE = 'Synchronous Flow received a promise. Use `flow.async` instead.'; const MESSAGE_NESTING = 'Return values are too deeply nested.'; const MESSAGE_PIPE_ARRAY = 'Pipe expected to receive an array of functions'; const MESSAGE_PIPE_PROMISE = 'Synchronous Pipe received a promise. Use `pipe.async` instead.'; const assertFlowFunctions: Asserter = assert.condition( value => Array.isArray(value) && value.every(item => typeof item === 'function'), MESSAGE_FLOW_ARRAY, TypeError, ); const assertPipeFunctions: Asserter = assert.condition( value => Array.isArray(value) && value.every(item => typeof item === 'function'), MESSAGE_PIPE_ARRAY, TypeError, ); // #endregion