Files
2026-05-27 23:47:53 +01:00

163 lines
8.8 KiB
Markdown

# @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`.
<!-- begin:auto-docs -->
## 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…
<!-- end:auto-docs -->
## 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<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
- [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