Skip to content

Pipeline

function Pipeline<E, Ctx extends object>(
ctx: Ctx,
steps: ReadonlyArray<Step<E, Ctx>>,
options: PipelineOptions<E, Ctx>,
): EitherAsync<E, Readonly<Ctx>>;
Parameter Description
ctx The initial context. The pipeline works on a frozen copy of it.
steps The steps, run one after the other in array order.
options The hooks: onLeft (optional), onException and onConflictingKeys. See Hooks.
  1. The pipeline freezes a shallow copy of ctx. That is the current context.
  2. Each step receives the current context. Depending on what it returns:
    • right(next): if next changed or removed a value that was already written, onConflictingKeys runs. Otherwise a frozen copy of next becomes the current context.
    • left(error): onLeft runs. Without onLeft, the pipeline stops with left(error).
    • it throws, or its promise rejects: onException runs.
  3. A hook that returns right(ctx) makes the pipeline continue with the next step, using that context. A hook that returns left(error) stops the pipeline: later steps do not run and the result is that left.
  4. After the last step the result is right of the current context. With no steps it is right of the copy of ctx, and no hook runs.

Pipeline returns a mini-fp EitherAsync<E, Readonly<Ctx>>. You can await it to get the Either, or keep chaining (map, mapLeft, onLeft, match, …) before awaiting:

import { left, right } from "@ngwes/mini-fp";
import { Pipeline } from "@ngwes/groucho-fp";
import type { PipelineOptions, Step } from "@ngwes/groucho-fp";
type Ctx = { input: string; shout?: string };
type Err = { message: string };
const shout: Step<Err, Ctx> = (ctx) => right({ ...ctx, shout: ctx.input.toUpperCase() });
const options: PipelineOptions<Err, Ctx> = {
onException: (exception) => left({ message: `crashed: ${String(exception)}` }),
onConflictingKeys: (keys) => left({ message: `conflict on ${keys.join(", ")}` }),
};
// await it: an Either
const result = await Pipeline({ input: "hello" }, [shout], options);
console.log(result.isRight()); // true
// or chain first
const message = await Pipeline({ input: "hello" }, [shout], options)
.map((ctx) => ctx.shout)
.onLeft((error) => console.error(error.message))
.match(
(shouted) => `got ${shouted}`,
(error) => `failed: ${error.message}`,
);
console.log(message); // "got HELLO"

onLeft is left out here, so a step’s left would stop the pipeline as is. The next section explains the hooks.