88 lines
8.8 KiB
Markdown
88 lines
8.8 KiB
Markdown
# @suaveplan/api-saga - Saga orchestrator for the Suaveplan API plugin system. Drives manifest-declared multi-plugin workflows in declared order with reverse-order compensation on failure, per-step idempotency keyed by {sagaId, stepName}, persisted state across host restarts via the host's ledger DB (suaveplan_saga_instances + suaveplan_saga_steps), capability-gated cross-tenant dispatch, typed event-bus emission of every step + instance transition, and an in-memory MeterProvider-friendly OTel surface (saga.instance span, saga.step span, saga.compensate span, saga.instances_total counter, saga.step_duration_ms histogram, saga.compensations_total counter, saga.in_flight gauge). Exposes a SagaParticipantRegistry the host populates from plugin manifests' sagas.owns[] and sagas.participatesIn[] declarations, refuses install on duplicate owners or unknown participant wiring, and ships runSagaParticipantConformance on the ./testing subpath so every step + compensate handler authored against @suaveplan/api-types' SagaParticipant contract is pinned to the same invariants.
|
|
|
|
Saga orchestrator for the Suaveplan API plugin system. Drives
|
|
manifest-declared multi-plugin workflows in declared order with
|
|
reverse-order compensation on failure, per-step idempotency keyed by
|
|
`{sagaId, stepName}`, persisted state across host restarts via the
|
|
host's ledger DB, a capability-gated cross-tenant guard, typed
|
|
event-bus emission for every step + instance transition, and an
|
|
OpenTelemetry instrument surface covering `saga.instances_total`,
|
|
`saga.step_duration_ms`, `saga.compensations_total`, and `saga.in_flight`.
|
|
|
|
API plugin system — see the umbrella change
|
|
the design document and the spec at
|
|
`api-saga/spec.md`.
|
|
|
|
<!-- begin:auto-docs -->
|
|
## Documentation tree
|
|
|
|
- [compensation](./src/compensation/compensation.md) — On step failure the saga orchestrator walks completed steps in reverse order and invokes each step's compensation handl…
|
|
- [compensation](./src/compensation/compensation.md) — On step failure the saga orchestrator walks completed steps in reverse order and invokes each step's compensation handl…
|
|
- [error-codes](./src/error-codes/error-codes.md) — Stable registry for every failure raised by `@suaveplan/api-saga`. The saga orchestrator surfaces ten distinct fault mo…
|
|
- [error-codes](./src/error-codes/error-codes.md) — Stable registry for every failure raised by `@suaveplan/api-saga`. The saga orchestrator surfaces ten distinct fault mo…
|
|
- [events](./src/events/events.md) — The saga orchestrator publishes every step + instance transition onto the host's `EventBus<SagaEventMap>`. Sibling plug…
|
|
- [events](./src/events/events.md) — The saga orchestrator publishes every step + instance transition onto the host's `EventBus<SagaEventMap>`. Sibling plug…
|
|
- [idempotency](./src/idempotency/idempotency.md) — Per design doc §9.1 the saga orchestrator MUST wrap every step invocation in an idempotency guard keyed by `{sagaId, st…
|
|
- [idempotency](./src/idempotency/idempotency.md) — Per design doc §9.1 the saga orchestrator MUST wrap every step invocation in an idempotency guard keyed by `{sagaId, st…
|
|
- [instruments](./src/instruments/instruments.md) — The saga orchestrator emits an OTel surface matching design doc §9.1: three spans (`saga.instance`, `saga.step`, `saga.…
|
|
- [instruments](./src/instruments/instruments.md) — The saga orchestrator emits an OTel surface matching design doc §9.1: three spans (`saga.instance`, `saga.step`, `saga.…
|
|
- [orchestrator](./src/orchestrator/orchestrator.md) — `createSagaOrchestrator(deps)` is the top-level factory plugin authors and host integrators consume. It binds together…
|
|
- [compensate-runner](./src/orchestrator/compensate-runner.md) — `runFullCompensation(...)` is the orchestrator's per-instance compensation driver. It bridges the generic `compensation…
|
|
- [orchestrator](./src/orchestrator/orchestrator.md) — `createSagaOrchestrator(deps)` is the top-level factory plugin authors and host integrators consume. It binds together…
|
|
- [step-runner](./src/orchestrator/step-runner.md) — `runStep(...)` is the orchestrator's per-step engine. It bundles every cross-cutting concern that fires around an actio…
|
|
- [persistence](./src/persistence/persistence.md) — `SagaPersistence` is the sole path through which the orchestrator mutates the host's saga tables (`suaveplan_saga_insta…
|
|
- [persistence](./src/persistence/persistence.md) — `SagaPersistence` is the sole path through which the orchestrator mutates the host's saga tables (`suaveplan_saga_insta…
|
|
- [recovery](./src/recovery/recovery.md) — When a host restarts, the saga orchestrator's in-memory state goes with it — but the saga itself is durable because eve…
|
|
- [recovery](./src/recovery/recovery.md) — When a host restarts, the saga orchestrator's in-memory state goes with it — but the saga itself is durable because eve…
|
|
- [registry](./src/registry/participant-registry.md) — The participant registry is the host-side compendium of every plugin that owns a saga and every plugin that participate…
|
|
- [participant-registry](./src/registry/participant-registry.md) — The participant registry is the host-side compendium of every plugin that owns a saga and every plugin that participate…
|
|
- [state](./src/state/state-machine.md) — The saga lifecycle has two coupled finite-state machines: the instance state (overall saga health) and the per-step sta…
|
|
- [state-machine](./src/state/state-machine.md) — The saga lifecycle has two coupled finite-state machines: the instance state (overall saga health) and the per-step sta…
|
|
- [tenant](./src/tenant/cross-tenant.md) — Design doc §9.1 (final paragraph) requires that a saga step whose target tenant differs from the saga initiator's tenan…
|
|
- [cross-tenant](./src/tenant/cross-tenant.md) — Design doc §9.1 (final paragraph) requires that a saga step whose target tenant differs from the saga initiator's tenan…
|
|
- [testing](./src/testing/testing.md) — `runSagaParticipantConformance(factory)` is the canonical contract suite every saga step + compensate handler in the mo…
|
|
- [testing](./src/testing/testing.md) — `runSagaParticipantConformance(factory)` is the canonical contract suite every saga step + compensate handler in the mo…
|
|
<!-- end:auto-docs -->
|
|
|
|
## Modules
|
|
|
|
- [orchestrator](./src/orchestrator/orchestrator.md) — `createSagaOrchestrator(deps)` factory + `SagaOrchestrator` public surface (`start`, `resume`, `cancel`, `listInstances`, `getInstance`).
|
|
- [orchestrator/step-runner](./src/orchestrator/step-runner.md) — per-step engine.
|
|
- [orchestrator/compensate-runner](./src/orchestrator/compensate-runner.md) — reverse-order compensation driver.
|
|
- [registry/participant-registry](./src/registry/participant-registry.md) — owner vs participant registration; validates manifest wiring.
|
|
- [persistence](./src/persistence/persistence.md) — state-machine-guarded CRUD against `suaveplan_saga_instances` + `suaveplan_saga_steps`.
|
|
- [recovery](./src/recovery/recovery.md) — boot-time replay of non-terminal instances.
|
|
- [idempotency](./src/idempotency/idempotency.md) — `@suaveplan/idempotency` wrapper keyed by `{sagaId, stepName}` with manual opt-out.
|
|
- [events](./src/events/events.md) — typed `SagaEventMap` and Zod schemas for every emitted payload.
|
|
- [compensation](./src/compensation/compensation.md) — generic reverse-walk retry runner.
|
|
- [tenant/cross-tenant](./src/tenant/cross-tenant.md) — `saga.cross-tenant` capability gate.
|
|
- [state/state-machine](./src/state/state-machine.md) — instance + step state machines.
|
|
- [instruments](./src/instruments/instruments.md) — OTel surface.
|
|
- [error-codes](./src/error-codes/error-codes.md) — 10 `SAGA.*` codes via `SagaError`.
|
|
- [testing](./src/testing/testing.md) — `runSagaParticipantConformance` published on the `./testing` subpath.
|
|
|
|
## Owner vs participant
|
|
|
|
A plugin **owns** a saga when its manifest declares `sagas.owns[]` —
|
|
the entry pins the saga name and the ordered list of `{plugin,
|
|
action, compensate}` steps. The owner is solely responsible for
|
|
choosing the step sequence.
|
|
|
|
A plugin **participates in** a saga when its manifest declares
|
|
`sagas.participatesIn: ["owner-plugin/saga-name"]` AND it exports
|
|
named `actions[name]` + `compensations[name]` handlers. The
|
|
orchestrator dispatches step events into the participant's handlers
|
|
via the `SagaParticipantRegistry`.
|
|
|
|
Two plugins MAY NOT both own the same saga name — the registry
|
|
refuses the second registration with `SAGA.DUPLICATE_OWNER`. A
|
|
participant whose `actions[name]` is missing fails at install with
|
|
`SAGA.UNKNOWN_PARTICIPANT`, before any saga can be started.
|
|
|
|
## Status
|
|
|
|
`0.1.0` — bottom-up rebuild stage. Will graduate to `1.0.0` per the
|
|
foundation umbrella's Stage 6 acceptance gate (`a multi-step saga in
|
|
api-plugin-example runs end-to-end including a compensation path`).
|
|
|