Files
suaveplan/packages/foundation/flow/workflow
2026-05-27 23:47:53 +01:00
..
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00

@suaveplan/workflow - Workflow / pipeline orchestration: sequential + parallel + conditional steps with saga compensation, retry, timeout, lifecycle hooks, and OTel instrumentation

Workflow and pipeline orchestration: sequential and parallel step execution, conditional branching with guard functions, retry strategies, timeouts, saga-pattern compensation, lifecycle hooks, and OpenTelemetry instrumentation. Type-safe via TypeScript generics, tree-shakeable, and zero external runtime dependencies beyond @suaveplan/error, @suaveplan/events, @suaveplan/types, @opentelemetry/api, tslib, and zod.

Documentation tree

  • compensator — Compensator is the explicit, registry-based rollback engine used by @suaveplan/workflow. The workflow engine and th…
    • compensator — Compensator is the explicit, registry-based rollback engine used by @suaveplan/workflow. The workflow engine and th…
  • saga — Saga is the dedicated, opinionated orchestrator for the textbook saga pattern: sequential business steps where every…
    • saga — Saga is the dedicated, opinionated orchestrator for the textbook saga pattern: sequential business steps where every…
  • context — WorkflowContext is the per-run state container that the workflow engine threads through every step. It owns the workf…
    • context — WorkflowContext is the per-run state container that the workflow engine threads through every step. It owns the workf…
  • workflow-builder — WorkflowBuilder is the fluent author-facing API for assembling workflows. Most consumers use it to chain together ste…
    • workflow-builder — WorkflowBuilder is the fluent author-facing API for assembling workflows. Most consumers use it to chain together ste…
  • workflow-engine — WorkflowEngine is the runtime orchestrator at the centre of @suaveplan/workflow. It accepts a Zod-validated `Workfl…
    • transition-hooks — TransitionHooks is the internal data-pipeline registry the WorkflowEngine uses to expose the `workflow:transitionin…
    • workflow-engine — WorkflowEngine is the runtime orchestrator at the centre of @suaveplan/workflow. It accepts a Zod-validated `Workfl…
  • errors — @suaveplan/workflow raises a single typed error class — WorkflowError — for every failure mode the engine can surfa…
    • workflow-error — @suaveplan/workflow raises a single typed error class — WorkflowError — for every failure mode the engine can surfa…
  • hook — HookRegistry is the lifecycle-event dispatcher for @suaveplan/workflow. It is the seam between the engine's interna…
    • hook — HookRegistry is the lifecycle-event dispatcher for @suaveplan/workflow. It is the seam between the engine's interna…
  • retry — withRetry is the standalone, reusable retry executor that @suaveplan/workflow exposes alongside the Step.retry co…
    • retry — withRetry is the standalone, reusable retry executor that @suaveplan/workflow exposes alongside the Step.retry co…
  • otel — This module is the only place in @suaveplan/workflow that talks to the OpenTelemetry API directly. Every other module…
    • instruments — This module is the only place in @suaveplan/workflow that talks to the OpenTelemetry API directly. Every other module…
  • conditional-step — ConditionalStep is the branching primitive in @suaveplan/workflow. It evaluates a GuardFn against the context and…
    • conditional-step — ConditionalStep is the branching primitive in @suaveplan/workflow. It evaluates a GuardFn against the context and…
  • parallel-step — ParallelStep groups multiple Step instances and runs them concurrently against a shared context. It is the only pla…
    • parallel-step — ParallelStep groups multiple Step instances and runs them concurrently against a shared context. It is the only pla…
  • step — Step is the atomic unit of execution in @suaveplan/workflow. Every sequential branch of a workflow ultimately runs…
    • step — Step is the atomic unit of execution in @suaveplan/workflow. Every sequential branch of a workflow ultimately runs…
  • common — This module is the canonical home for every shared structural type in @suaveplan/workflow. The WorkflowEngine, buil…
  • workflow-hooks — @suaveplan/workflow exposes two distinct hook surfaces. The first is the fire-and-forget HookRegistry (`workflow:st…
  • utils — Pure helpers shared across every other module in @suaveplan/workflow. Centralising them here keeps Step, the engine…
    • utils — Pure helpers shared across every other module in @suaveplan/workflow. Centralising them here keeps Step, the engine…

Install

bun add @suaveplan/workflow

Quick Start

import { createWorkflow } from "@suaveplan/workflow";

interface OrderData {
	orderId: string;
	items: string[];
	total: number;
}

const result = await createWorkflow<OrderData>("process-order")
	.addStep("validate", (ctx) => {
		if (ctx.items.length === 0) throw new Error("No items");
	})
	.build()
	.run({
		initialData: { orderId: "ord-1", items: ["widget"], total: 29.99 },
	});

Modules

Error Handling Strategies

builder.onError("stop");        // halt immediately on first failure
builder.onError("compensate");  // run reverse-order compensations, then stop
builder.onError("continue");    // log + skip the failure, run the rest

Retry Configuration

import { RetryPresets } from "@suaveplan/workflow";

builder
	.addStep("flaky-api", async (ctx) => callExternalApi(ctx))
	.withRetry(RetryPresets.standard());

builder.addStep("custom", (ctx) => callApi(ctx)).withRetry({
	maxAttempts: 5,
	strategy: "exponential",
	delayMs: 500,
	maxDelayMs: 10_000,
	backoffMultiplier: 2,
});

Lifecycle Hooks

const engine = createWorkflowEngine(config);

engine.hooks.on("workflow:start", (p) =>
	console.log(`Workflow ${p.workflowId} started`),
);
engine.hooks.on("step:complete", (p) =>
	console.log(`Step ${p.stepId} completed`),
);
engine.hooks.on("step:fail", (p) =>
	console.error(`Step ${p.stepId} failed:`, p.error),
);

Saga Pattern

import { createSaga, createStep } from "@suaveplan/workflow";

const saga = createSaga({ id: "booking", name: "Booking Saga" });

saga
	.addStep(createStep({
		id: "reserve",
		name: "Reserve Seats",
		execute: (ctx) => reserveSeats(ctx),
		compensate: (ctx) => releaseSeats(ctx),
	}))
	.addStep(createStep({
		id: "charge",
		name: "Charge Payment",
		execute: (ctx) => chargeCard(ctx),
		compensate: (ctx) => refundCard(ctx),
	}));

const result = await saga.execute(context);
// If "charge" fails, "reserve" is automatically compensated.

License

MIT