Durability Store
For the conceptual overview and patterns, see the durability guide.
DurabilityStore
Section titled “DurabilityStore”Pluggable persistence layer for flow checkpoints. Adapters may live in separate packages.
interface DurabilityStore { load(runId: string): Promise<FlowCheckpoint | null>; save(checkpoint: FlowCheckpoint): Promise<void>; delete(runId: string): Promise<void>;}| Method | Semantics |
|---|---|
load(runId) | Return the checkpoint for runId, or null if none exists. Must not throw on unknown ids. |
save(checkpoint) | Persist the checkpoint, overwriting any existing entry for the same runId. Must be atomic — partial writes defeat checkpointing. |
delete(runId) | Remove the checkpoint. No-op for an unknown runId — must not throw. |
Implementations are responsible for serialization. JSON is the typical choice; whatever you pick must round-trip Date objects (createdAt, updatedAt) and support undefined for breakValue slots that are absent.
FlowCheckpoint
Section titled “FlowCheckpoint”The shape persisted to the store. Adapters store and return this verbatim.
interface FlowCheckpoint { flowName: string; runId: string; input: unknown; state: unknown; completedSteps: string[]; status: 'running' | 'completed' | 'failed'; breakValue?: unknown; failedStep?: { name: string; error: string }; createdAt: Date; updatedAt: Date;}| Field | Description |
|---|---|
flowName | The flow’s name (from createFlow('name')). Useful for diagnostics; not used to route resume. |
runId | The DurabilityOptions.runId of this run. |
input | The argument the flow was first started with. Used verbatim on resume — the caller’s second input argument is ignored. |
state | Accumulated state at the moment of the last successful step. |
completedSteps | Names of finished or condition-skipped steps, in execution order. |
status | 'running' (in progress or crashed), 'completed' (saved result is final), 'failed' (errored after exhausting retries). |
breakValue | Only present when the flow short-circuited via breakIf. The field’s presence distinguishes “broke” from “completed normally” — the value itself may be undefined. |
failedStep | Only present when status === 'failed'. The step that errored, plus its error message. |
createdAt | When the run first started. Preserved across resumes. |
updatedAt | When the checkpoint was last written. |
DurabilityOptions
Section titled “DurabilityOptions”The shape passed via FlowExecutionOptions.durability.
interface DurabilityOptions { store: DurabilityStore; runId: string;}| Field | Description |
|---|---|
store | The store to read and write checkpoints through. |
runId | Stable identifier for this run. Same runId across execute() calls → resume or replay. New runId → fresh run. Typically derived from a business identifier. |
MemoryDurabilityStore
Section titled “MemoryDurabilityStore”A reference DurabilityStore that holds checkpoints in a Map.
import { MemoryDurabilityStore } from '@celom/prose';
const store = new MemoryDurabilityStore();Test helpers (not part of the DurabilityStore interface):
| Method | Purpose |
|---|---|
size() | Number of stored runs. |
snapshot(runId) | Synchronous read of a single checkpoint (returns a clone). |
clear() | Drop all stored checkpoints. |
MemoryDurabilityStore clones on both load() and save() so the executor cannot mutate stored state. Adapters that serialize to bytes get this property for free.
Implementation contract
Section titled “Implementation contract”Conformance tests for adapter authors live at packages/prose/src/lib/__tests__/store-conformance.ts. The file is not re-exported from the package entry point and is not included in the published dist/. Copy or vendor it into your adapter package — or, for adapters in this monorepo, import it directly from source.
// Once copied or vendored, run the suite in your adapter's spec file:import { storeConformanceSuite } from './store-conformance.js';import { MyStore } from './my-store.js';
storeConformanceSuite('MyStore', () => new MyStore());The suite covers the public DurabilityStore contract — round-tripping checkpoints, overwriting on second save(), preserving the failedStep / breakValue fields, no-op delete() for unknown ids, and isolation between runIds. Adapter-specific invariants (clone-on-read for in-memory adapters, transaction semantics for SQL adapters) belong in your adapter’s own spec.
Implementation notes:
- Atomic writes. A reader observing a partial checkpoint defeats the entire point of durability. Wrap the write in a transaction, or write to a temp row and swap.
load()returnsnull, not throws. UnknownrunIds are normal — they mean “fresh run.”delete()of an unknown id is a no-op. No error.- Clone on read for non-serializing stores. If your store keeps the checkpoint in memory (or shares an object reference with the caller), clone on
load()andsave()to prevent accidental mutation.
Related
Section titled “Related”- Durability guide — concepts, patterns, and feature interactions.
- Order processing with durability — end-to-end example with a crash scenario.
- Execution options — the
durabilityoption onflow.execute().