# @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](./src/compensation/compensator/compensator.md) — `Compensator` is the explicit, registry-based rollback engine used by `@suaveplan/workflow`. The workflow engine and th… - [compensator](./src/compensation/compensator/compensator.md) — `Compensator` is the explicit, registry-based rollback engine used by `@suaveplan/workflow`. The workflow engine and th… - [saga](./src/compensation/saga/saga.md) — `Saga` is the dedicated, opinionated orchestrator for the textbook saga pattern: sequential business steps where every… - [saga](./src/compensation/saga/saga.md) — `Saga` is the dedicated, opinionated orchestrator for the textbook saga pattern: sequential business steps where every… - [context](./src/core/context/context.md) — `WorkflowContext` is the per-run state container that the workflow engine threads through every step. It owns the workf… - [context](./src/core/context/context.md) — `WorkflowContext` is the per-run state container that the workflow engine threads through every step. It owns the workf… - [workflow-builder](./src/core/workflow-builder/workflow-builder.md) — `WorkflowBuilder` is the fluent author-facing API for assembling workflows. Most consumers use it to chain together ste… - [workflow-builder](./src/core/workflow-builder/workflow-builder.md) — `WorkflowBuilder` is the fluent author-facing API for assembling workflows. Most consumers use it to chain together ste… - [workflow-engine](./src/core/workflow-engine/workflow-engine.md) — `WorkflowEngine` is the runtime orchestrator at the centre of `@suaveplan/workflow`. It accepts a Zod-validated `Workfl… - [transition-hooks](./src/core/workflow-engine/transition-hooks.md) — `TransitionHooks` is the internal data-pipeline registry the `WorkflowEngine` uses to expose the `workflow:transitionin… - [workflow-engine](./src/core/workflow-engine/workflow-engine.md) — `WorkflowEngine` is the runtime orchestrator at the centre of `@suaveplan/workflow`. It accepts a Zod-validated `Workfl… - [errors](./src/errors/workflow-error.md) — `@suaveplan/workflow` raises a single typed error class — `WorkflowError` — for every failure mode the engine can surfa… - [workflow-error](./src/errors/workflow-error.md) — `@suaveplan/workflow` raises a single typed error class — `WorkflowError` — for every failure mode the engine can surfa… - [hook](./src/middleware/hook/hook.md) — `HookRegistry` is the lifecycle-event dispatcher for `@suaveplan/workflow`. It is the seam between the engine's interna… - [hook](./src/middleware/hook/hook.md) — `HookRegistry` is the lifecycle-event dispatcher for `@suaveplan/workflow`. It is the seam between the engine's interna… - [retry](./src/middleware/retry/retry.md) — `withRetry` is the standalone, reusable retry executor that `@suaveplan/workflow` exposes alongside the `Step.retry` co… - [retry](./src/middleware/retry/retry.md) — `withRetry` is the standalone, reusable retry executor that `@suaveplan/workflow` exposes alongside the `Step.retry` co… - [otel](./src/otel/instruments.md) — This module is the only place in `@suaveplan/workflow` that talks to the OpenTelemetry API directly. Every other module… - [instruments](./src/otel/instruments.md) — This module is the only place in `@suaveplan/workflow` that talks to the OpenTelemetry API directly. Every other module… - [conditional-step](./src/steps/conditional-step/conditional-step.md) — `ConditionalStep` is the branching primitive in `@suaveplan/workflow`. It evaluates a `GuardFn` against the context and… - [conditional-step](./src/steps/conditional-step/conditional-step.md) — `ConditionalStep` is the branching primitive in `@suaveplan/workflow`. It evaluates a `GuardFn` against the context and… - [parallel-step](./src/steps/parallel-step/parallel-step.md) — `ParallelStep` groups multiple `Step` instances and runs them concurrently against a shared context. It is the only pla… - [parallel-step](./src/steps/parallel-step/parallel-step.md) — `ParallelStep` groups multiple `Step` instances and runs them concurrently against a shared context. It is the only pla… - [step](./src/steps/step/step.md) — `Step` is the atomic unit of execution in `@suaveplan/workflow`. Every sequential branch of a workflow ultimately runs… - [step](./src/steps/step/step.md) — `Step` is the atomic unit of execution in `@suaveplan/workflow`. Every sequential branch of a workflow ultimately runs… - [common](./src/types/common.md) — This module is the canonical home for every shared structural type in `@suaveplan/workflow`. The `WorkflowEngine`, buil… - [workflow-hooks](./src/types/workflow-hooks.md) — `@suaveplan/workflow` exposes two distinct hook surfaces. The first is the fire-and-forget `HookRegistry` (`workflow:st… - [utils](./src/utils/utils.md) — Pure helpers shared across every other module in `@suaveplan/workflow`. Centralising them here keeps `Step`, the engine… - [utils](./src/utils/utils.md) — Pure helpers shared across every other module in `@suaveplan/workflow`. Centralising them here keeps `Step`, the engine… ## Install ```bash bun add @suaveplan/workflow ``` ## Quick Start ```ts import { createWorkflow } from "@suaveplan/workflow"; interface OrderData { orderId: string; items: string[]; total: number; } const result = await createWorkflow("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 - [Errors](./src/errors/workflow-error.md) — `WORKFLOW_CODES` and `WorkflowError` (`STEP_FAILED`, `TIMEOUT`, `CANCELLED`, `COMPENSATION_FAILED`, `GUARD_REJECTED`, `RETRY_EXHAUSTED`). - [OTel instruments](./src/otel/instruments.md) — counters + histogram. - [Types](./src/types/common.md) and [transition hooks](./src/types/workflow-hooks.md). - [Utils](./src/utils/utils.md) — `generateId`, `delay`, `withTimeout`, `calculateRetryDelay`, `now`, `defaultClock`. - [Step](./src/steps/step/step.md), [Parallel step](./src/steps/parallel-step/parallel-step.md), [Conditional step](./src/steps/conditional-step/conditional-step.md). - [Compensator](./src/compensation/compensator/compensator.md) and [Saga](./src/compensation/saga/saga.md). - [Hook registry](./src/middleware/hook/hook.md) and [Retry middleware](./src/middleware/retry/retry.md). - [Workflow context](./src/core/context/context.md). - [Workflow builder](./src/core/workflow-builder/workflow-builder.md). - [Workflow engine](./src/core/workflow-engine/workflow-engine.md) and [Transition hooks](./src/core/workflow-engine/transition-hooks.md). ## Error Handling Strategies ```ts 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 ```ts 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 ```ts 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 ```ts 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