@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.
@@ -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":";;;AAYA;;;;;;;;;GASG;AACH,MAAa,eAAe;IAEP;IACA;IAFnB,YACmB,KAAiB,EACjB,KAAmB;QADnB,UAAK,GAAL,KAAK,CAAY;QACjB,UAAK,GAAL,KAAK,CAAc;IACnC,CAAC;IAEI,GAAG,CAAC,CAAU;QACpB,OAAO,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC5D,CAAC;IACO,GAAG,CAAC,CAAU;QACpB,OAAO,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC5D,CAAC;IAEO,MAAM,CAAC,GAAgB;QAC7B,OAAO,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;IAC9E,CAAC;IACO,MAAM,CAA+B,GAAM;QACjD,OAAO,CAAC,GAAG,IAAI,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAM,CAAC;IAC5F,CAAC;IACO,KAAK,CAAC,EAAkB;QAC9B,OAAO,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;IAC3E,CAAC;IACO,KAAK,CAAkC,EAAK;QAClD,OAAO,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAM,CAAC;IACxF,CAAC;IAED,YAAY;QACV,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAC1D,CAAC;IAED,SAAS,CAAC,GAAgB;QACxB,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,SAAS,CAAC,KAAa,EAAE,KAA2B;QAClD,MAAM,IAAI,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;QAC1B,IAAI,OAAO,IAAI,KAAK;YAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACzD,IAAI,QAAQ,IAAI,KAAK;YAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC5D,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC3C,CAAC;IACD,KAAK,CAAC,MAAM,CAAC,KAAa;QACxB,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACrD,CAAC;IACD,KAAK,CAAC,aAAa,CAAC,KAAa,EAAE,GAAW;QAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;IAChE,CAAC;IACD,cAAc,CAAC,UAA0B;QACvC,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3D,CAAC;IACD,KAAK,CAAC,kBAAkB;QACtB,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5E,CAAC;IACD,KAAK,CAAC,eAAe,CAAC,KAAa;QACjC,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,CAAC;IACD,KAAK,CAAC,aAAa,CAAC,KAAa;QAC/B,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5E,CAAC;IACD,UAAU,CAAC,KAAa,EAAE,KAAa,EAAE,YAAoB,EAAE,KAAa;QAC1E,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;IAClE,CAAC;IACD,cAAc,CAAC,KAAa;QAC1B,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC;IACD,YAAY,CAAC,KAAa,EAAE,KAAa,EAAE,YAAoB;QAC7D,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;IAC7D,CAAC;IACD,eAAe,CAAC,MAAoB;QAClC,OAAO,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;IAC5C,CAAC;IACD,gBAAgB,CAAC,KAAa;QAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC;IACD,iBAAiB,CAAC,MAAc;QAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC9C,CAAC;IACD,KAAK,CAAC,QAAQ,CAAC,KAAe;QAC5B,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC;IACD,KAAK,CAAC,eAAe,CAAC,KAAa;QACjC,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7E,CAAC;CACF;AAjFD,0CAiFC"}
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 to every run waiting on it via `ctx.waitForEvent(name, { match })` whose
198
- * match the payload satisfies. Name-based pub/sub (fan-out), vs `signal`'s point-to-point token.
199
- * Returns how many runs it resumed.
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): Promise<number>;
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
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
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: 'running',
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
- this.emit({ type: 'run.started', runId, workflow: name });
262
- return this.track(this.execute(run, registered.fn));
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 to every run waiting on it via `ctx.waitForEvent(name, { match })` whose
390
- * match the payload satisfies. Name-based pub/sub (fan-out), vs `signal`'s point-to-point token.
391
- * Returns how many runs it resumed.
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
- delivered += 1;
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 delivered;
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. Replay re-registers the
451
- // saga, and at the run's suspension point execute() runs the undo and marks it cancelled.
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
- return result;
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
  /**