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));