repos / starfx

supercharged async flow control library.
git clone https://github.com/neurosnap/starfx.git

Eric Bower · 19 Jan 24

compose.ts

 1import { Instruction, Operation } from "./deps.ts";
 2import type { Next } from "./types.ts";
 3
 4export interface BaseCtx {
 5  // deno-lint-ignore no-explicit-any
 6  [key: string]: any;
 7}
 8
 9export type BaseMiddleware<Ctx extends BaseCtx = BaseCtx, T = unknown> = (
10  ctx: Ctx,
11  next: Next,
12) => Operation<T | undefined>;
13
14export function compose<Ctx extends BaseCtx = BaseCtx, T = unknown>(
15  middleware: BaseMiddleware<Ctx, T>[],
16) {
17  if (!Array.isArray(middleware)) {
18    throw new TypeError("Middleware stack must be an array!");
19  }
20
21  for (const fn of middleware) {
22    if (typeof fn !== "function") {
23      throw new TypeError("Middleware must be composed of functions!");
24    }
25  }
26
27  return function* composeFn(context: Ctx, mdw?: BaseMiddleware<Ctx, T>) {
28    // last called middleware #
29    let index = -1;
30
31    function* dispatch(i: number): Generator<Instruction, void, void> {
32      if (i <= index) {
33        throw new Error("next() called multiple times");
34      }
35      index = i;
36      let fn: BaseMiddleware<Ctx, T> | undefined = middleware[i];
37      if (i === middleware.length) {
38        fn = mdw;
39      }
40      if (!fn) {
41        return;
42      }
43      const nxt = dispatch.bind(null, i + 1);
44      yield* fn(context, nxt);
45    }
46
47    yield* dispatch(0);
48  };
49}