import { createAsyncPipeline, Middleware, MaybeAsync, Container, useContainer, } from 'farrow-pipeline'; const ASYNC_WATERFALL_SYMBOL = Symbol('ASYNC_WATERFALL_SYMBOL'); export type AsyncBrook = (I: I) => MaybeAsync; export type AsyncBrookInput = | AsyncBrook | { middlware: AsyncBrook }; export type AsyncBrooks = AsyncBrook[]; export type AsyncBrookInputs = AsyncBrookInput[]; export const getAsyncBrook = (input: AsyncBrookInput) => { if (typeof input === 'function') { return input; } else if (input && typeof input.middlware === 'function') { return input.middlware; } // eslint-disable-next-line @typescript-eslint/no-base-to-string,@typescript-eslint/restrict-template-expressions throw new Error(`${input} is not a AsyncBrook or { brook: AsyncBrook }`); }; export type RunAsyncWaterfallOptions = { container?: Container; onLast?: AsyncBrook; }; export type AsyncWaterfall = { run: (input: I, options?: RunAsyncWaterfallOptions) => MaybeAsync; use: (...I: AsyncBrookInputs) => AsyncWaterfall; middlware: AsyncBrook; [ASYNC_WATERFALL_SYMBOL]: true; }; export type AsyncWaterfall2AsyncBrook

> = P extends AsyncWaterfall ? AsyncBrook : never; export type AsyncWaterfallRecord = Record>; // eslint-disable-next-line @typescript-eslint/no-invalid-void-type export type AsyncWaterfalls2Brooks = { [K in keyof PS]: PS[K] extends AsyncWaterfall ? AsyncWaterfall2AsyncBrook : // eslint-disable-next-line @typescript-eslint/no-invalid-void-type PS[K] extends void ? // eslint-disable-next-line @typescript-eslint/no-invalid-void-type void : never; }; export type RunnerFromAsyncWaterfall> = M extends AsyncWaterfall ? AsyncWaterfall['run'] : never; // eslint-disable-next-line @typescript-eslint/no-invalid-void-type export type AsyncWaterfalls2Runners = { [K in keyof PS]: PS[K] extends AsyncWaterfall ? RunnerFromAsyncWaterfall : // eslint-disable-next-line @typescript-eslint/no-invalid-void-type PS[K] extends void ? // eslint-disable-next-line @typescript-eslint/no-invalid-void-type void : never; }; // eslint-disable-next-line @typescript-eslint/no-invalid-void-type export const createAsyncWaterfall = (): AsyncWaterfall => { const pipeline = createAsyncPipeline(); const use: AsyncWaterfall['use'] = (...input) => { pipeline.use( ...input.map(getAsyncBrook).map(mapAsyncBrookToAsyncMiddleware), ); return waterfall; }; const run: AsyncWaterfall['run'] = (input, options) => // eslint-disable-next-line @typescript-eslint/no-shadow pipeline.run(input, { ...options, onLast: input => input }); const middlware: AsyncWaterfall['middlware'] = input => { // eslint-disable-next-line react-hooks/rules-of-hooks const container = useContainer(); // eslint-disable-next-line @typescript-eslint/no-shadow return pipeline.run(input, { container, onLast: input => input }); }; const waterfall: AsyncWaterfall = { ...pipeline, use, run, middlware, [ASYNC_WATERFALL_SYMBOL]: true as const, }; return waterfall; }; export const isAsyncWaterfall = (input: any): input is AsyncWaterfall => Boolean(input?.[ASYNC_WATERFALL_SYMBOL]); const mapAsyncBrookToAsyncMiddleware = (brook: AsyncBrook): Middleware> => async (input, next) => next(await brook(input));