cl-pipelines is infrastructure-agnostic by design. It never talks to a database or job queue directly — instead, it calls two narrow interfaces that you implement for your stack. This means the same pipeline logic works with Convex, PostgreSQL, Redis, or any other backend without changing a line of phase code.
StorageAdapter
TheStorageAdapter persists job status, checkpoint state, and log entries. You implement five methods:
(jobId: string) => Promise<JobRecord | null>
Returns the current job record, or
null if the job doesn’t exist yet. The library calls this at the start of every advancePhase to read the current checkpoint.(jobId: string, status: PipelineStatus, error?: string) => Promise<void>
Updates the job’s status. When called with an error (e.g.
"error" status), the error string should be persisted. When called without an error (e.g. "complete"), any previously stored error should be cleared.(jobId: string, checkpoint: Checkpoint<TState> | null) => Promise<void>
Writes or clears the checkpoint. Called with
null when the pipeline completes.(jobId: string, entry: LogEntry) => Promise<void>
Appends a single log entry to the job’s log. Called each time a phase calls
ctx.log().(jobId: string) => Promise<void>
Clears all log entries for a job. The library exposes this method for consumers to call (for example, before a full retry), but does not call it automatically.
SchedulerAdapter
TheSchedulerAdapter triggers advancePhase after each phase completes. It has one method:
(jobId: string, delayMs: number) => Promise<void>
Enqueue a call to
advancePhase(jobId) to run after delayMs milliseconds. The library always calls this with delayMs: 0 in v0.1. Your implementation should delegate to your scheduler’s equivalent of “run as soon as possible.”In-memory adapters
For local development and unit tests,cl-pipelines ships two fully-functional in-memory adapters:
scheduler._bind() and storage._inspect() are test-only hooks (prefixed with _ to signal this). In production, your SchedulerAdapter.scheduleAdvance delegates to a real durable scheduler, and you read job state through your StorageAdapter.getJob.Implementing your own adapter
To implement aStorageAdapter for a custom backend, create a plain object (or class) that satisfies the interface: