@dudousxd/nestjs-durable-core 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/codec-state-store.d.ts +2 -0
- package/dist/codec-state-store.d.ts.map +1 -1
- package/dist/codec-state-store.js +6 -0
- package/dist/codec-state-store.js.map +1 -1
- package/dist/engine.d.ts +68 -5
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +214 -17
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +57 -1
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/search-attributes.d.ts +17 -0
- package/dist/search-attributes.d.ts.map +1 -0
- package/dist/search-attributes.js +47 -0
- package/dist/search-attributes.js.map +1 -0
- package/dist/test-helpers.d.ts +11 -0
- package/dist/test-helpers.d.ts.map +1 -0
- package/dist/test-helpers.js +15 -0
- package/dist/test-helpers.js.map +1 -0
- package/dist/testing/in-memory-state-store.d.ts +2 -0
- package/dist/testing/in-memory-state-store.d.ts.map +1 -1
- package/dist/testing/in-memory-state-store.js +17 -0
- package/dist/testing/in-memory-state-store.js.map +1 -1
- package/dist/workflow-ctx.d.ts +4 -2
- package/dist/workflow-ctx.d.ts.map +1 -1
- package/dist/workflow-ctx.js +12 -2
- package/dist/workflow-ctx.js.map +1 -1
- package/package.json +1 -1
|
@@ -35,9 +35,11 @@ export declare class CodecStateStore implements StateStore {
|
|
|
35
35
|
getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null>;
|
|
36
36
|
saveCheckpoint(checkpoint: StepCheckpoint): Promise<void>;
|
|
37
37
|
listIncompleteRuns(): Promise<WorkflowRun[]>;
|
|
38
|
+
listPendingRuns(limit: number): Promise<WorkflowRun[]>;
|
|
38
39
|
listDueTimers(nowMs: number): Promise<WorkflowRun[]>;
|
|
39
40
|
tryLockRun(runId: string, owner: string, leaseUntilMs: number, nowMs: number): Promise<boolean>;
|
|
40
41
|
releaseRunLock(runId: string): Promise<void>;
|
|
42
|
+
renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean>;
|
|
41
43
|
putSignalWaiter(waiter: SignalWaiter): Promise<void>;
|
|
42
44
|
takeSignalWaiter(token: string): Promise<SignalWaiter | null>;
|
|
43
45
|
listSignalWaiters(prefix: string): Promise<SignalWaiter[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec-state-store.d.ts","sourceRoot":"","sources":["../src/codec-state-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEpG;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;IAChC,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;CACjC;AAED;;;;;;;;;GASG;AACH,qBAAa,eAAgB,YAAW,UAAU;IAE9C,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBADL,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,YAAY;IAGtC,OAAO,CAAC,GAAG;IAGX,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,MAAM;IAGd,OAAO,CAAC,MAAM;IAGd,OAAO,CAAC,KAAK;IAGb,OAAO,CAAC,KAAK;IAIb,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7B,SAAS,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAG1C,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAM9D,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAGlD,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAG/E,cAAc,CAAC,UAAU,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAGnD,kBAAkB,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAG5C,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAG1D,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAG/F,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAG5C,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAGpD,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAG7D,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAGpD,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAGjD,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;CAGhE"}
|
|
1
|
+
{"version":3,"file":"codec-state-store.d.ts","sourceRoot":"","sources":["../src/codec-state-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEpG;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;IAChC,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;CACjC;AAED;;;;;;;;;GASG;AACH,qBAAa,eAAgB,YAAW,UAAU;IAE9C,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBADL,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,YAAY;IAGtC,OAAO,CAAC,GAAG;IAGX,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,MAAM;IAGd,OAAO,CAAC,MAAM;IAGd,OAAO,CAAC,KAAK;IAGb,OAAO,CAAC,KAAK;IAIb,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7B,SAAS,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAG1C,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAM9D,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAGlD,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAG/E,cAAc,CAAC,UAAU,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAGnD,kBAAkB,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAG5C,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAGtD,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAG1D,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAG/F,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAG5C,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAGlF,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAGpD,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAG7D,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAGpD,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAGjD,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;CAGhE"}
|
|
@@ -62,6 +62,9 @@ class CodecStateStore {
|
|
|
62
62
|
async listIncompleteRuns() {
|
|
63
63
|
return (await this.inner.listIncompleteRuns()).map((r) => this.decRun(r));
|
|
64
64
|
}
|
|
65
|
+
async listPendingRuns(limit) {
|
|
66
|
+
return (await this.inner.listPendingRuns(limit)).map((r) => this.decRun(r));
|
|
67
|
+
}
|
|
65
68
|
async listDueTimers(nowMs) {
|
|
66
69
|
return (await this.inner.listDueTimers(nowMs)).map((r) => this.decRun(r));
|
|
67
70
|
}
|
|
@@ -71,6 +74,9 @@ class CodecStateStore {
|
|
|
71
74
|
releaseRunLock(runId) {
|
|
72
75
|
return this.inner.releaseRunLock(runId);
|
|
73
76
|
}
|
|
77
|
+
renewRunLock(runId, owner, leaseUntilMs) {
|
|
78
|
+
return this.inner.renewRunLock(runId, owner, leaseUntilMs);
|
|
79
|
+
}
|
|
74
80
|
putSignalWaiter(waiter) {
|
|
75
81
|
return this.inner.putSignalWaiter(waiter);
|
|
76
82
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec-state-store.js","sourceRoot":"","sources":["../src/codec-state-store.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"codec-state-store.js","sourceRoot":"","sources":["../src/codec-state-store.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ControlPlane, EngineListener, NamedTransport, RunResult, StateStore, StepError, Transport, UpdateResult, UpdateValidator, WorkflowCtx, WorkflowRun } from './interfaces';
|
|
1
|
+
import type { ControlPlane, EngineListener, NamedTransport, RunDispatcher, RunResult, SearchAttributes, StateStore, StepError, StepInterceptor, Transport, UpdateResult, UpdateValidator, WorkflowCtx, WorkflowRun } from './interfaces';
|
|
2
2
|
import { type QueueConfig } from './queue';
|
|
3
3
|
import { type WorkflowClass, type WorkflowInputOf } from './workflow-ref';
|
|
4
4
|
type WorkflowFn = (ctx: WorkflowCtx, input: unknown) => Promise<unknown>;
|
|
@@ -6,6 +6,8 @@ type WorkflowFn = (ctx: WorkflowCtx, input: unknown) => Promise<unknown>;
|
|
|
6
6
|
export interface StartOptions {
|
|
7
7
|
/** Run-scoped tags, merged with the workflow's static `@Workflow({ tags })` onto the run. */
|
|
8
8
|
tags?: string[];
|
|
9
|
+
/** Typed, queryable run data stamped on the run (e.g. `{ amount: 200, tier: 'pro' }`). */
|
|
10
|
+
searchAttributes?: SearchAttributes;
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
11
13
|
* Serialize runs of a workflow that share a key — a durable, FIFO mutex (e.g. one pipeline per base).
|
|
@@ -64,6 +66,13 @@ export interface WorkflowEngineDeps {
|
|
|
64
66
|
* hiccup — gets another try). Default 1 (no retry). Compensations must be idempotent.
|
|
65
67
|
*/
|
|
66
68
|
compensationRetries?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Where a freshly-`start`ed run executes (see {@link RunDispatcher}). Defaults to in-process: the
|
|
71
|
+
* run executes on this instance asynchronously (a microtask), so `start` returns without blocking.
|
|
72
|
+
* Pass a no-op dispatcher on a caller that must NOT run workflows (e.g. an API/dashboard pod), and
|
|
73
|
+
* run `runPending` on a worker pod to pick those up; or a broker-backed one for a worker pool.
|
|
74
|
+
*/
|
|
75
|
+
runDispatcher?: RunDispatcher;
|
|
67
76
|
}
|
|
68
77
|
/**
|
|
69
78
|
* The orchestrator. Owns workflow state and replays runs deterministically: each step's
|
|
@@ -83,15 +92,21 @@ export declare class WorkflowEngine {
|
|
|
83
92
|
private readonly webhookUrl?;
|
|
84
93
|
private readonly traceparent?;
|
|
85
94
|
private readonly compensationRetries;
|
|
95
|
+
/** Where a freshly-started run executes — in-process by default (see {@link RunDispatcher}). */
|
|
96
|
+
private readonly runDispatcher;
|
|
86
97
|
/** Every registered workflow, keyed by `name@version` — so old versions stay runnable. */
|
|
87
98
|
private readonly workflows;
|
|
88
99
|
/** The newest registered version per workflow name — used to `start` new runs. */
|
|
89
100
|
private readonly latest;
|
|
101
|
+
/** Event name → workflow names started when that event is published (see `onEvent`). */
|
|
102
|
+
private readonly eventTriggers;
|
|
90
103
|
/** In-flight remote steps awaiting a worker result, keyed by stepId. */
|
|
91
104
|
private readonly pending;
|
|
92
105
|
/** Per-step "reset the liveness timer" callbacks, called when a heartbeat arrives. */
|
|
93
106
|
private readonly heartbeatResets;
|
|
94
107
|
private readonly listeners;
|
|
108
|
+
/** Step interceptors (onion middleware around real local-step execution), first = outermost. */
|
|
109
|
+
private readonly interceptors;
|
|
95
110
|
/** Callbacks notified (on any instance) when a run is cancelled — for cooperative cancellation. */
|
|
96
111
|
private readonly cancelListeners;
|
|
97
112
|
/** Notified when a run is dead-lettered (moved to `dead`) — a hook for a DLQ handler. */
|
|
@@ -119,6 +134,8 @@ export declare class WorkflowEngine {
|
|
|
119
134
|
tags?: string[];
|
|
120
135
|
singleton?: SingletonConfig;
|
|
121
136
|
executionTimeout?: string | number;
|
|
137
|
+
validateInput?: (input: unknown) => void | Promise<void>;
|
|
138
|
+
onEvent?: string[];
|
|
122
139
|
}): void;
|
|
123
140
|
/**
|
|
124
141
|
* Register a flow-control queue referenced by `ctx.call(step, input, { queue })`. Caps concurrent
|
|
@@ -128,6 +145,15 @@ export declare class WorkflowEngine {
|
|
|
128
145
|
registerQueue(config: QueueConfig): void;
|
|
129
146
|
/** Subscribe to lifecycle events. Returns an unsubscribe function. */
|
|
130
147
|
subscribe(listener: EngineListener): () => void;
|
|
148
|
+
/**
|
|
149
|
+
* Register a {@link StepInterceptor} — onion middleware run around the real execution of every
|
|
150
|
+
* local `ctx.step` (timing, logging, tracing, error enrichment, context propagation). First
|
|
151
|
+
* registered is outermost; interceptors fire only when a step executes, never on replay. Returns
|
|
152
|
+
* an unsubscribe function.
|
|
153
|
+
*/
|
|
154
|
+
use(interceptor: StepInterceptor): () => void;
|
|
155
|
+
/** Fold the registered interceptors around a local step body (identity when there are none). */
|
|
156
|
+
private interceptStep;
|
|
131
157
|
/**
|
|
132
158
|
* Be notified when a run is cancelled — on ANY instance, via the transport control plane. A
|
|
133
159
|
* worker bridge can use this for cooperative cancellation: abort the in-flight work for `runId`
|
|
@@ -184,6 +210,36 @@ export declare class WorkflowEngine {
|
|
|
184
210
|
* boot. A run still not due re-suspends cheaply without running new work.
|
|
185
211
|
*/
|
|
186
212
|
resumeDueTimers(nowMs?: number): Promise<RunResult[]>;
|
|
213
|
+
/**
|
|
214
|
+
* Lease and execute one run by id — the worker side of dispatch. Acquires the recovery lease (so
|
|
215
|
+
* exactly one instance runs it), then resumes the body. Returns the result, or null if another
|
|
216
|
+
* instance holds the lease or the engine is draining. The default in-process dispatcher calls this;
|
|
217
|
+
* a broker-backed worker calls it per consumed run id.
|
|
218
|
+
*/
|
|
219
|
+
runOne(runId: string): Promise<RunResult | null>;
|
|
220
|
+
/**
|
|
221
|
+
* Re-enqueue a run for a worker to (re-)execute — the dispatch-model **retry**. Sets it back to
|
|
222
|
+
* `pending`, clears any stale lease, and dispatches; a worker resumes it (replaying its checkpoints,
|
|
223
|
+
* re-attempting the failed step). Returns the enqueued state immediately — never runs the body
|
|
224
|
+
* inline — or null if the run is unknown. The dashboard "retry" goes through here so it can't block
|
|
225
|
+
* the HTTP request on workflow execution.
|
|
226
|
+
*/
|
|
227
|
+
requeue(runId: string): Promise<RunResult | null>;
|
|
228
|
+
/**
|
|
229
|
+
* Pick up and execute every `pending` run — the poll-based side of dispatch for a worker pod with
|
|
230
|
+
* no broker. Runs enqueued by other pods (or by a caller using a no-op dispatcher) sit `pending`
|
|
231
|
+
* in the store until polled; leasing ensures exactly one pod runs each. Call periodically alongside
|
|
232
|
+
* {@link resumeDueTimers}.
|
|
233
|
+
*/
|
|
234
|
+
runPending(nowMs?: number): Promise<RunResult[]>;
|
|
235
|
+
/**
|
|
236
|
+
* Resolve once `runId` reaches a settled state — terminal (completed/failed/cancelled/dead) or
|
|
237
|
+
* suspended (handed off to a timer/signal/event). The async counterpart to dispatch: pair it with
|
|
238
|
+
* `start` when a call site needs the outcome — `await start(...); const r = await waitForRun(id)`.
|
|
239
|
+
*/
|
|
240
|
+
waitForRun(runId: string, opts?: {
|
|
241
|
+
timeoutMs?: number;
|
|
242
|
+
}): Promise<RunResult>;
|
|
187
243
|
/**
|
|
188
244
|
* Resume each run only if this instance can acquire its recovery lease — so when several
|
|
189
245
|
* replicas recover or poll at once, each run is picked up by exactly one of them.
|
|
@@ -194,11 +250,16 @@ export declare class WorkflowEngine {
|
|
|
194
250
|
* Returns the run result, or null if no run is waiting for that token.
|
|
195
251
|
*/
|
|
196
252
|
/**
|
|
197
|
-
* Publish a named event
|
|
198
|
-
*
|
|
199
|
-
*
|
|
253
|
+
* Publish a named event. It does two things, and returns how many runs it touched (the sum):
|
|
254
|
+
* 1. **Resumes** every in-flight run waiting on it via `ctx.waitForEvent(name, { match })` whose
|
|
255
|
+
* match the payload satisfies (fan-out, vs `signal`'s point-to-point token).
|
|
256
|
+
* 2. **Starts** a fresh run of every workflow registered with `onEvent: [name]`, passing the
|
|
257
|
+
* payload as input. Idempotent by `evt:<id>:<workflow>` — pass `opts.id` to dedupe redeliveries
|
|
258
|
+
* of the same logical event (default: a fresh uuid, so each publish triggers once).
|
|
200
259
|
*/
|
|
201
|
-
publishEvent(name: string, payload: unknown
|
|
260
|
+
publishEvent(name: string, payload: unknown, opts?: {
|
|
261
|
+
id?: string;
|
|
262
|
+
}): Promise<number>;
|
|
202
263
|
signal(token: string, payload: unknown): Promise<RunResult | null>;
|
|
203
264
|
/**
|
|
204
265
|
* Report the result of a `ctx.task(name, …)` back to its run (async completion). The external
|
|
@@ -234,6 +295,8 @@ export declare class WorkflowEngine {
|
|
|
234
295
|
*/
|
|
235
296
|
private admitSingleton;
|
|
236
297
|
private execute;
|
|
298
|
+
/** The run body, lease held + renewed by {@link execute}. */
|
|
299
|
+
private runExecution;
|
|
237
300
|
/**
|
|
238
301
|
* Run one saga compensation, retried up to `compensationRetries`, emitting a `compensate:<step>`
|
|
239
302
|
* step event for its outcome so a stranded undo is visible. Never throws — a permanently-failing
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,YAAY,EAEZ,cAAc,EACd,cAAc,EAGd,SAAS,
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.js
CHANGED
|
@@ -57,15 +57,21 @@ class WorkflowEngine {
|
|
|
57
57
|
webhookUrl;
|
|
58
58
|
traceparent;
|
|
59
59
|
compensationRetries;
|
|
60
|
+
/** Where a freshly-started run executes — in-process by default (see {@link RunDispatcher}). */
|
|
61
|
+
runDispatcher;
|
|
60
62
|
/** Every registered workflow, keyed by `name@version` — so old versions stay runnable. */
|
|
61
63
|
workflows = new Map();
|
|
62
64
|
/** The newest registered version per workflow name — used to `start` new runs. */
|
|
63
65
|
latest = new Map();
|
|
66
|
+
/** Event name → workflow names started when that event is published (see `onEvent`). */
|
|
67
|
+
eventTriggers = new Map();
|
|
64
68
|
/** In-flight remote steps awaiting a worker result, keyed by stepId. */
|
|
65
69
|
pending = new Map();
|
|
66
70
|
/** Per-step "reset the liveness timer" callbacks, called when a heartbeat arrives. */
|
|
67
71
|
heartbeatResets = new Map();
|
|
68
72
|
listeners = new Set();
|
|
73
|
+
/** Step interceptors (onion middleware around real local-step execution), first = outermost. */
|
|
74
|
+
interceptors = [];
|
|
69
75
|
/** Callbacks notified (on any instance) when a run is cancelled — for cooperative cancellation. */
|
|
70
76
|
cancelListeners = new Set();
|
|
71
77
|
/** Notified when a run is dead-lettered (moved to `dead`) — a hook for a DLQ handler. */
|
|
@@ -92,6 +98,12 @@ class WorkflowEngine {
|
|
|
92
98
|
this.webhookUrl = deps.webhookUrl;
|
|
93
99
|
this.traceparent = deps.traceparent;
|
|
94
100
|
this.compensationRetries = Math.max(1, deps.compensationRetries ?? 1);
|
|
101
|
+
// Default: execute the run on this instance, asynchronously, so `start` never blocks on the body.
|
|
102
|
+
// A failed pickup is swallowed here (the run stays `pending` for a `runPending` poll to retry);
|
|
103
|
+
// run failures themselves are handled inside `execute` and surfaced as the run's `failed` state.
|
|
104
|
+
this.runDispatcher = deps.runDispatcher ?? {
|
|
105
|
+
dispatch: (runId) => queueMicrotask(() => void this.runOne(runId).catch(() => { })),
|
|
106
|
+
};
|
|
95
107
|
this.pool.bind(async (result) => {
|
|
96
108
|
// In-memory path (a `timeoutMs` step awaiting on THIS instance): resolve its pending promise.
|
|
97
109
|
const waiter = this.pending.get(result.stepId);
|
|
@@ -156,11 +168,18 @@ class WorkflowEngine {
|
|
|
156
168
|
tags: opts?.tags,
|
|
157
169
|
singleton: opts?.singleton,
|
|
158
170
|
executionTimeoutMs: opts?.executionTimeout != null ? (0, duration_1.parseDuration)(opts.executionTimeout) : undefined,
|
|
171
|
+
validateInput: opts?.validateInput,
|
|
172
|
+
onEvent: opts?.onEvent,
|
|
159
173
|
};
|
|
160
174
|
this.workflows.set(versionKey(name, version), registered);
|
|
161
175
|
const current = this.latest.get(name);
|
|
162
176
|
if (!current || isNewerVersion(version, current.version))
|
|
163
177
|
this.latest.set(name, registered);
|
|
178
|
+
for (const event of opts?.onEvent ?? []) {
|
|
179
|
+
const subscribers = this.eventTriggers.get(event) ?? new Set();
|
|
180
|
+
subscribers.add(name);
|
|
181
|
+
this.eventTriggers.set(event, subscribers);
|
|
182
|
+
}
|
|
164
183
|
}
|
|
165
184
|
/**
|
|
166
185
|
* Register a flow-control queue referenced by `ctx.call(step, input, { queue })`. Caps concurrent
|
|
@@ -175,6 +194,27 @@ class WorkflowEngine {
|
|
|
175
194
|
this.listeners.add(listener);
|
|
176
195
|
return () => this.listeners.delete(listener);
|
|
177
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Register a {@link StepInterceptor} — onion middleware run around the real execution of every
|
|
199
|
+
* local `ctx.step` (timing, logging, tracing, error enrichment, context propagation). First
|
|
200
|
+
* registered is outermost; interceptors fire only when a step executes, never on replay. Returns
|
|
201
|
+
* an unsubscribe function.
|
|
202
|
+
*/
|
|
203
|
+
use(interceptor) {
|
|
204
|
+
this.interceptors.push(interceptor);
|
|
205
|
+
return () => {
|
|
206
|
+
const i = this.interceptors.indexOf(interceptor);
|
|
207
|
+
if (i >= 0)
|
|
208
|
+
this.interceptors.splice(i, 1);
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/** Fold the registered interceptors around a local step body (identity when there are none). */
|
|
212
|
+
interceptStep(invocation, body) {
|
|
213
|
+
if (this.interceptors.length === 0)
|
|
214
|
+
return body();
|
|
215
|
+
const chain = this.interceptors.reduceRight((next, interceptor) => () => interceptor(invocation, next), body);
|
|
216
|
+
return chain();
|
|
217
|
+
}
|
|
178
218
|
/**
|
|
179
219
|
* Be notified when a run is cancelled — on ANY instance, via the transport control plane. A
|
|
180
220
|
* worker bridge can use this for cooperative cancellation: abort the in-flight work for `runId`
|
|
@@ -235,6 +275,8 @@ class WorkflowEngine {
|
|
|
235
275
|
const registered = this.latest.get(name);
|
|
236
276
|
if (!registered)
|
|
237
277
|
throw new Error(`workflow ${name} is not registered`);
|
|
278
|
+
// Validate the input up front — a bad payload is rejected before any run is created.
|
|
279
|
+
await registered.validateInput?.(input);
|
|
238
280
|
// Idempotent by run id: a redelivered trigger (at-least-once queues) or a scheduler re-tick for
|
|
239
281
|
// the same id is a no-op, returning the existing run's state instead of starting a duplicate.
|
|
240
282
|
const prior = await this.store.getRun(runId);
|
|
@@ -251,15 +293,18 @@ class WorkflowEngine {
|
|
|
251
293
|
id: runId,
|
|
252
294
|
workflow: name,
|
|
253
295
|
workflowVersion: registered.version,
|
|
254
|
-
status: '
|
|
296
|
+
status: 'pending',
|
|
255
297
|
input,
|
|
256
298
|
tags,
|
|
299
|
+
searchAttributes: opts?.searchAttributes,
|
|
257
300
|
createdAt: now,
|
|
258
301
|
updatedAt: now,
|
|
259
302
|
};
|
|
260
303
|
await this.store.createRun(run);
|
|
261
|
-
|
|
262
|
-
|
|
304
|
+
// The run is durably enqueued; a dispatcher (in-process by default) executes it — `start` does
|
|
305
|
+
// NOT run the body inline. Await the terminal/suspended state with `waitForRun(runId)` if needed.
|
|
306
|
+
await this.runDispatcher.dispatch(runId);
|
|
307
|
+
return { runId, status: 'pending' };
|
|
263
308
|
}
|
|
264
309
|
/** Read a run's current persisted state (or null if unknown). A thin pass-through to the store. */
|
|
265
310
|
getRun(runId) {
|
|
@@ -363,6 +408,99 @@ class WorkflowEngine {
|
|
|
363
408
|
async resumeDueTimers(nowMs = this.clock()) {
|
|
364
409
|
return this.resumeLeased(await this.store.listDueTimers(nowMs), nowMs);
|
|
365
410
|
}
|
|
411
|
+
/**
|
|
412
|
+
* Lease and execute one run by id — the worker side of dispatch. Acquires the recovery lease (so
|
|
413
|
+
* exactly one instance runs it), then resumes the body. Returns the result, or null if another
|
|
414
|
+
* instance holds the lease or the engine is draining. The default in-process dispatcher calls this;
|
|
415
|
+
* a broker-backed worker calls it per consumed run id.
|
|
416
|
+
*/
|
|
417
|
+
async runOne(runId) {
|
|
418
|
+
if (this.draining)
|
|
419
|
+
return null;
|
|
420
|
+
const nowMs = this.clock();
|
|
421
|
+
const acquired = await this.store.tryLockRun(runId, this.instanceId, nowMs + this.leaseMs, nowMs);
|
|
422
|
+
if (!acquired)
|
|
423
|
+
return null;
|
|
424
|
+
return this.resume(runId);
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Re-enqueue a run for a worker to (re-)execute — the dispatch-model **retry**. Sets it back to
|
|
428
|
+
* `pending`, clears any stale lease, and dispatches; a worker resumes it (replaying its checkpoints,
|
|
429
|
+
* re-attempting the failed step). Returns the enqueued state immediately — never runs the body
|
|
430
|
+
* inline — or null if the run is unknown. The dashboard "retry" goes through here so it can't block
|
|
431
|
+
* the HTTP request on workflow execution.
|
|
432
|
+
*/
|
|
433
|
+
async requeue(runId) {
|
|
434
|
+
const run = await this.store.getRun(runId);
|
|
435
|
+
if (!run)
|
|
436
|
+
return null;
|
|
437
|
+
await this.store.releaseRunLock(runId);
|
|
438
|
+
await this.store.updateRun(runId, { status: 'pending', updatedAt: new Date() });
|
|
439
|
+
await this.runDispatcher.dispatch(runId);
|
|
440
|
+
return { runId, status: 'pending' };
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Pick up and execute every `pending` run — the poll-based side of dispatch for a worker pod with
|
|
444
|
+
* no broker. Runs enqueued by other pods (or by a caller using a no-op dispatcher) sit `pending`
|
|
445
|
+
* in the store until polled; leasing ensures exactly one pod runs each. Call periodically alongside
|
|
446
|
+
* {@link resumeDueTimers}.
|
|
447
|
+
*/
|
|
448
|
+
async runPending(nowMs = this.clock()) {
|
|
449
|
+
// Oldest-first (FIFO), capped per call so a backlog drains over several polls without one sweep
|
|
450
|
+
// fetching unboundedly. A run not picked up this tick is picked up the next.
|
|
451
|
+
return this.resumeLeased(await this.store.listPendingRuns(100), nowMs);
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Resolve once `runId` reaches a settled state — terminal (completed/failed/cancelled/dead) or
|
|
455
|
+
* suspended (handed off to a timer/signal/event). The async counterpart to dispatch: pair it with
|
|
456
|
+
* `start` when a call site needs the outcome — `await start(...); const r = await waitForRun(id)`.
|
|
457
|
+
*/
|
|
458
|
+
waitForRun(runId, opts) {
|
|
459
|
+
const isSettled = (s) => s !== 'pending' && s !== 'running';
|
|
460
|
+
const toResult = (run) => ({
|
|
461
|
+
runId,
|
|
462
|
+
status: run.status,
|
|
463
|
+
output: run.output,
|
|
464
|
+
error: run.error,
|
|
465
|
+
});
|
|
466
|
+
return new Promise((resolve, reject) => {
|
|
467
|
+
let done = false;
|
|
468
|
+
let off = () => { };
|
|
469
|
+
const timer = opts?.timeoutMs != null
|
|
470
|
+
? setTimeout(() => {
|
|
471
|
+
if (done)
|
|
472
|
+
return;
|
|
473
|
+
done = true;
|
|
474
|
+
off();
|
|
475
|
+
reject(new Error(`waitForRun(${runId}) timed out after ${opts.timeoutMs}ms`));
|
|
476
|
+
}, opts.timeoutMs)
|
|
477
|
+
: undefined;
|
|
478
|
+
const finish = (run) => {
|
|
479
|
+
if (done)
|
|
480
|
+
return;
|
|
481
|
+
done = true;
|
|
482
|
+
if (timer)
|
|
483
|
+
clearTimeout(timer);
|
|
484
|
+
off();
|
|
485
|
+
resolve(toResult(run));
|
|
486
|
+
};
|
|
487
|
+
const check = () => {
|
|
488
|
+
void this.store.getRun(runId).then((run) => {
|
|
489
|
+
if (run && isSettled(run.status))
|
|
490
|
+
finish(run);
|
|
491
|
+
});
|
|
492
|
+
};
|
|
493
|
+
// React only to this run's settling events (not its every step event), and subscribe BEFORE the
|
|
494
|
+
// initial read so a run that settles in between isn't missed.
|
|
495
|
+
off = this.subscribe((ev) => {
|
|
496
|
+
if (ev.runId === runId &&
|
|
497
|
+
(ev.type === 'run.completed' || ev.type === 'run.failed' || ev.type === 'run.suspended')) {
|
|
498
|
+
check();
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
check();
|
|
502
|
+
});
|
|
503
|
+
}
|
|
366
504
|
/**
|
|
367
505
|
* Resume each run only if this instance can acquire its recovery lease — so when several
|
|
368
506
|
* replicas recover or poll at once, each run is picked up by exactly one of them.
|
|
@@ -386,20 +524,38 @@ class WorkflowEngine {
|
|
|
386
524
|
* Returns the run result, or null if no run is waiting for that token.
|
|
387
525
|
*/
|
|
388
526
|
/**
|
|
389
|
-
* Publish a named event
|
|
390
|
-
*
|
|
391
|
-
*
|
|
527
|
+
* Publish a named event. It does two things, and returns how many runs it touched (the sum):
|
|
528
|
+
* 1. **Resumes** every in-flight run waiting on it via `ctx.waitForEvent(name, { match })` whose
|
|
529
|
+
* match the payload satisfies (fan-out, vs `signal`'s point-to-point token).
|
|
530
|
+
* 2. **Starts** a fresh run of every workflow registered with `onEvent: [name]`, passing the
|
|
531
|
+
* payload as input. Idempotent by `evt:<id>:<workflow>` — pass `opts.id` to dedupe redeliveries
|
|
532
|
+
* of the same logical event (default: a fresh uuid, so each publish triggers once).
|
|
392
533
|
*/
|
|
393
|
-
async publishEvent(name, payload) {
|
|
534
|
+
async publishEvent(name, payload, opts) {
|
|
535
|
+
let touched = 0;
|
|
394
536
|
const waiters = await this.store.listSignalWaiters((0, events_1.eventPrefix)(name));
|
|
395
|
-
let delivered = 0;
|
|
396
537
|
for (const w of waiters) {
|
|
397
538
|
if ((0, events_1.eventMatches)(payload, (0, events_1.eventMatchOf)(w.token))) {
|
|
398
539
|
await this.signal(w.token, payload);
|
|
399
|
-
|
|
540
|
+
touched += 1;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
const subscribers = this.eventTriggers.get(name);
|
|
544
|
+
if (subscribers?.size) {
|
|
545
|
+
const eventId = opts?.id ?? globalThis.crypto.randomUUID();
|
|
546
|
+
for (const workflow of subscribers) {
|
|
547
|
+
// A subscriber that rejects the payload (validateInput) must not block the others or the
|
|
548
|
+
// waiters — its run simply never starts, mirroring fire-and-forget dead-letter routing.
|
|
549
|
+
try {
|
|
550
|
+
await this.start(workflow, payload, `evt:${eventId}:${workflow}`);
|
|
551
|
+
touched += 1;
|
|
552
|
+
}
|
|
553
|
+
catch {
|
|
554
|
+
// skip this subscriber
|
|
555
|
+
}
|
|
400
556
|
}
|
|
401
557
|
}
|
|
402
|
-
return
|
|
558
|
+
return touched;
|
|
403
559
|
}
|
|
404
560
|
async signal(token, payload) {
|
|
405
561
|
const waiter = await this.store.takeSignalWaiter(token);
|
|
@@ -447,18 +603,24 @@ class WorkflowEngine {
|
|
|
447
603
|
const run = await this.store.getRun(runId);
|
|
448
604
|
if (!run)
|
|
449
605
|
return null;
|
|
450
|
-
// Compensating cancel: resume the run with a cancellation pending
|
|
451
|
-
//
|
|
606
|
+
// Compensating cancel: resume the run with a cancellation pending — replay re-registers the saga,
|
|
607
|
+
// and at the suspension point execute() runs the undo and marks it cancelled. Run that resume in
|
|
608
|
+
// the BACKGROUND so the caller (e.g. an HTTP request) never blocks on replaying the workflow +
|
|
609
|
+
// compensations. `execute` holds the run's lease, so this can't double-run one a live worker owns
|
|
610
|
+
// (its lease acquire fails and it no-ops); the broadcast tells that worker to abort cooperatively.
|
|
452
611
|
if (opts?.compensate && (run.status === 'suspended' || run.status === 'running')) {
|
|
453
612
|
this.cancelRequested.add(runId);
|
|
454
|
-
const result = await this.resume(runId);
|
|
455
|
-
this.notifyCancelled(runId);
|
|
456
613
|
if (this.controlPlane) {
|
|
457
614
|
void this.controlPlane
|
|
458
615
|
.publishControl({ kind: 'cancel', runId, from: this.instanceId })
|
|
459
616
|
.catch(() => undefined);
|
|
460
617
|
}
|
|
461
|
-
|
|
618
|
+
queueMicrotask(() => {
|
|
619
|
+
void this.resume(runId)
|
|
620
|
+
.then(() => this.notifyCancelled(runId))
|
|
621
|
+
.catch(() => undefined);
|
|
622
|
+
});
|
|
623
|
+
return { runId, status: run.status };
|
|
462
624
|
}
|
|
463
625
|
const error = { message: 'cancelled' };
|
|
464
626
|
await this.store.updateRun(runId, { status: 'cancelled', error, updatedAt: new Date() });
|
|
@@ -547,9 +709,43 @@ class WorkflowEngine {
|
|
|
547
709
|
return idx >= 0 && idx < (cfg.limit ?? 1);
|
|
548
710
|
}
|
|
549
711
|
async execute(run, fn) {
|
|
712
|
+
const registered = this.workflows.get(versionKey(run.workflow, run.workflowVersion));
|
|
713
|
+
// Hold the lease for the WHOLE execution — whatever path got us here (leased sweep, a signal, a
|
|
714
|
+
// remote result, a dashboard action). The leased sweeps already own it; the event-driven paths
|
|
715
|
+
// don't, so acquire it here. If another instance owns it, don't double-run. While we run, renew
|
|
716
|
+
// the lease periodically so a long run keeps it (a crashed worker's lease still expires and is
|
|
717
|
+
// reclaimed by periodic recovery).
|
|
718
|
+
const lockNow = this.clock();
|
|
719
|
+
if (run.lockedBy !== this.instanceId || (run.lockedUntil ?? 0) <= lockNow) {
|
|
720
|
+
if (!(await this.store.tryLockRun(run.id, this.instanceId, lockNow + this.leaseMs, lockNow))) {
|
|
721
|
+
return { runId: run.id, status: run.status };
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
const renew = setInterval(() => {
|
|
725
|
+
void this.store
|
|
726
|
+
.renewRunLock(run.id, this.instanceId, this.clock() + this.leaseMs)
|
|
727
|
+
.catch(() => undefined);
|
|
728
|
+
}, Math.max(50, Math.floor(this.leaseMs / 2)));
|
|
729
|
+
renew.unref?.();
|
|
730
|
+
try {
|
|
731
|
+
return await this.runExecution(run, fn, registered);
|
|
732
|
+
}
|
|
733
|
+
finally {
|
|
734
|
+
clearInterval(renew);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
/** The run body, lease held + renewed by {@link execute}. */
|
|
738
|
+
async runExecution(run, fn, registered) {
|
|
739
|
+
// First execution of an enqueued run: mark it running and announce the start, BEFORE the singleton
|
|
740
|
+
// gate — `admitSingleton` only counts `running`/`suspended` runs, so a still-`pending` run could
|
|
741
|
+
// never be admitted. A resumed run is already past `pending`, so this fires exactly once.
|
|
742
|
+
if (run.status === 'pending') {
|
|
743
|
+
await this.store.updateRun(run.id, { status: 'running', updatedAt: new Date() });
|
|
744
|
+
run.status = 'running';
|
|
745
|
+
this.emit({ type: 'run.started', runId: run.id, workflow: run.workflow });
|
|
746
|
+
}
|
|
550
747
|
// Singleton admission gate: if this run shares its key with `limit` older in-flight runs, wait
|
|
551
748
|
// (suspend on a short timer) until a slot frees instead of running now. Re-checked on each resume.
|
|
552
|
-
const registered = this.workflows.get(versionKey(run.workflow, run.workflowVersion));
|
|
553
749
|
if (registered?.singleton && !(await this.admitSingleton(run, registered.singleton))) {
|
|
554
750
|
const wakeAt = this.clock() + SINGLETON_RETRY_MS;
|
|
555
751
|
await this.store.updateRun(run.id, { status: 'suspended', wakeAt, updatedAt: new Date() });
|
|
@@ -559,7 +755,7 @@ class WorkflowEngine {
|
|
|
559
755
|
}
|
|
560
756
|
// Saga compensations registered by completed steps; run in reverse if the run later fails.
|
|
561
757
|
const compensations = [];
|
|
562
|
-
const ctx = (0, workflow_ctx_1.createWorkflowCtx)(this.ctxHost, run.id, compensations);
|
|
758
|
+
const ctx = (0, workflow_ctx_1.createWorkflowCtx)(this.ctxHost, run.id, compensations, run.workflow);
|
|
563
759
|
try {
|
|
564
760
|
const output = await fn(ctx, run.input);
|
|
565
761
|
// Clear any error from an earlier failed-then-retried attempt: a completed run is a success
|
|
@@ -691,6 +887,7 @@ class WorkflowEngine {
|
|
|
691
887
|
startChild: (workflow, input, id) => {
|
|
692
888
|
queueMicrotask(() => void this.start(workflow, input, id).catch(() => undefined));
|
|
693
889
|
},
|
|
890
|
+
interceptStep: (invocation, body) => this.interceptStep(invocation, body),
|
|
694
891
|
};
|
|
695
892
|
}
|
|
696
893
|
/**
|