Skip to content

Engine methods

Methods on the Engine returned by createEngine.

engine.start(pollIntervalMs?)

Initializes storage and starts the polling loop, running tick() every pollIntervalMs (default 1000). Call once at startup. Returns Promise<void>.

engine.stop()

Stops the polling loop, ends all open streams, aborts in-flight steps, and waits for any in-flight tick to finish. In-flight runs are left running so they can be reclaimed. Registered schedules are deliberately left in place — they belong to the storage, not to this instance. Returns Promise<void>.

engine.tick()

Claims up to concurrency pending or stale runs and executes them in parallel. Useful for CLI tools and tests. If you use tick() without start(), call storage.initialize() first. Returns Promise<void>.

engine.enqueue(name, input, options?)

Submits a run. Type-safe — only accepts registered workflow names with matching input. Returns the created WorkflowRun (use run.id to track it).

OptionTypeDescription
idempotencyKeystringSame key + same input returns the existing run; same key + different input throws IdempotencyConflictError

Throws WorkflowNotFoundError for an unknown name and ValidationError if input fails the schema.

engine.stream(options?)

Returns a pull-based ResultStream — an AsyncIterableIterator<EngineEvent> (and AsyncDisposable) of execution events. Each call returns an independent stream.

OptionTypeDefaultDescription
bufferSizenumberInfinityMax events buffered before the engine pauses (backpressure). 0 = strict rendezvous; any non-negative integer allows that many buffered events.

Invalid bufferSize (negative, or a non-integer that isn't Infinity) throws ConfigError. Breaking out of the loop, await using, or engine.stop() unsubscribes automatically. See Streaming Results.

engine.cancel(runId)

Cancels a pending or running workflow. Returns true if cancelled, false if it already completed / failed / cancelled. Aborts the current step's AbortSignal immediately. See Cancellation.

engine.sendEvent(runId, eventName, payload)

Delivers an external event to a run that is (or will be) waiting on waitForEvent(eventName). The payload — validated against that wait's schema, if any — becomes the wait's result and the next step's prev.

typescript
await engine.sendEvent(run.id, 'approved', { approver: 'alice' })

Delivery is durable and order-independent: an event sent before the run reaches the wait is buffered and consumed when it gets there. Returns false if the run does not exist or has already finished (completed / failed / cancelled); throws ConfigError if the workflow has no such event step, or ValidationError if the payload fails the wait's schema. See Waiting for Events.

engine.schedule(name, input, recurrence, options?)

Registers a durable recurring schedule. Returns Promise<string> resolving to its key. Validates name, input, and the recurrence immediately.

recurrence is a number of milliseconds, { every } (milliseconds or a duration string like '1h'), or { cron } (a five-field expression, evaluated in UTC). A malformed or unsatisfiable cron expression throws ConfigError here rather than on the first tick.

The schedule is persisted rather than held as an in-process timer, so it survives restarts; registering the same key again updates it in place, preserving the cadence unless the interval changed. Due firings are claimed atomically, so N instances sharing a schedule produce one run per interval. options.key sets the identity explicitly (it otherwise defaults to a hash of the name, interval, and input). See Scheduled Workflows.

engine.unschedule(key)

Removes a durable schedule. Returns Promise<boolean>false if no such schedule existed. Because schedules are shared, this stops it for every instance.

engine.listSchedules()

Returns Promise<readonly WorkflowSchedule[]> — every registered schedule, ordered by key, each carrying its nextRunAt.

engine.getRunStatus(runId)

Returns { run, steps } — the run's current status and all its step results — or null if the run is not found.

typescript
const info = await engine.getRunStatus(run.id)
info?.run.status // 'pending' | 'running' | 'sleeping' | 'waiting' | 'completed' | 'failed' | 'cancelled'
info?.steps      // StepResult[]

engine.listRuns(filter?)

Lists runs ordered by createdAt descending, then id descending (a total, stable order even when runs share a createdAt). For inspection or dead-letter visibility. All filter fields are optional:

typescript
await engine.listRuns({
  status: 'failed',   // only this RunStatus
  workflow: 'order',  // only this workflow
  limit: 50,          // max rows (default 100; must be a positive integer)
  before: cursor,     // keyset cursor (createdAt), paired with beforeId
  beforeId: cursorId, // keyset cursor tie-break (id)
})

Paginate with a keyset cursor — pass the last row's createdAt and id. Using both is exact even when timestamps collide; before alone is a coarse "created before T" filter that can drop runs tied on that millisecond:

typescript
const page1 = await engine.listRuns({ limit: 50 })
const last = page1.at(-1)
const page2 = last
  ? await engine.listRuns({ limit: 50, before: last.createdAt, beforeId: last.id })
  : []

engine.resume(runId)

Re-queues a failed or cancelled run so the engine picks it up again. Completed steps are preserved and skipped on replay, so execution resumes at the step that failed; the failed step's result is discarded and re-run. Returns true if the run was resumable, false if it does not exist or is in any other state (pending / running / completed).

typescript
const failed = await engine.listRuns({ status: 'failed' })
for (const run of failed) {
  await engine.resume(run.id)
}

Released under the MIT License.