import { Container, useContainer, createPipeline, Middleware, } from 'farrow-pipeline'; const WATERFALL_SYMBOL = Symbol('WATERFALL_SYMBOL'); export type Brook = (I: I) => I; export type BrookInput = Brook | { middleware: Brook }; export type Brooks = Brook[]; export type BrookInputs = BrookInput[]; export const getBrook = (input: BrookInput) => { if (typeof input === 'function') { return input; } else if (input && typeof input.middleware === 'function') { return input.middleware; } // eslint-disable-next-line @typescript-eslint/no-base-to-string,@typescript-eslint/restrict-template-expressions throw new Error(`${input} is not a Brook or { brook: Brook }`); }; export type RunWaterfallOptions = { container?: Container; onLast?: Brook; }; // eslint-disable-next-line @typescript-eslint/no-invalid-void-type export type Waterfall = { run: (input: I, options?: RunWaterfallOptions) => I; use: (...I: BrookInputs) => Waterfall; middleware: Brook; [WATERFALL_SYMBOL]: true; }; export type Waterfall2Brook

> = P extends Waterfall< infer I > ? Brook : never; export type WaterfallRecord = Record>; // eslint-disable-next-line @typescript-eslint/no-invalid-void-type export type Waterfalls2Brooks = { [K in keyof PS]: PS[K] extends Waterfall ? Waterfall2Brook : // 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 RunnerFromWaterfall> = M extends Waterfall< infer VS > ? Waterfall['run'] : never; // eslint-disable-next-line @typescript-eslint/no-invalid-void-type export type Waterfalls2Runners = { [K in keyof PS]: PS[K] extends Waterfall ? RunnerFromWaterfall : // 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 createWaterfall = (): Waterfall => { const pipeline = createPipeline(); const use: Waterfall['use'] = (...brooks) => { pipeline.use(...brooks.map(getBrook).map(mapBrookToMiddleware)); return waterfall; }; const run: Waterfall['run'] = (input, options) => // eslint-disable-next-line @typescript-eslint/no-shadow pipeline.run(input, { ...options, onLast: input => input }); const middleware: Waterfall['middleware'] = 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: Waterfall = { ...pipeline, use, run, middleware, [WATERFALL_SYMBOL]: true, }; return waterfall; }; export const isWaterfall = (input: any): input is Waterfall => Boolean(input?.[WATERFALL_SYMBOL]); const mapBrookToMiddleware = (brook: Brook): Middleware => (input, next) => next(brook(input));