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