@dudousxd/nestjs-durable-core 0.18.0 → 0.21.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/engine.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type EntityConfig } from './entities';
2
2
  import { type EventBatchConfig } from './event-accumulators';
3
- import type { ControlPlane, EngineListener, NamedTransport, RunDispatcher, RunResult, SearchAttributes, StateStore, StepError, StepInterceptor, Transport, UpdateResult, UpdateValidator, WorkflowCtx, WorkflowRun } from './interfaces';
3
+ import type { ControlPlane, EngineListener, NamedTransport, RunDispatcher, RunResult, SearchAttributes, StateStore, StepError, StepInterceptor, Transport, UpdateResult, UpdateValidator, WorkflowCtx, WorkflowExecutor, WorkflowRun } from './interfaces';
4
4
  import { type QueueConfig } from './queue';
5
5
  import { type WorkflowClass, type WorkflowInputOf, type WorkflowRef } from './workflow-ref';
6
6
  type WorkflowFn = (ctx: WorkflowCtx, input: unknown) => Promise<unknown>;
@@ -68,6 +68,15 @@ export interface WorkflowEngineDeps {
68
68
  * hiccup — gets another try). Default 1 (no retry). Compensations must be idempotent.
69
69
  */
70
70
  compensationRetries?: number;
71
+ /**
72
+ * Persist a `running` checkpoint when a local step's body begins, so an in-flight step shows up
73
+ * in the dashboard (and a fresh page load / REST query) the moment it starts — not only once it
74
+ * completes. The `step.started` lifecycle event is emitted either way (the live SSE view always
75
+ * sees the start); this flag only controls the extra checkpoint write. Default `true`. Set
76
+ * `false` on hot paths with many short local steps to halve their checkpoint writes — you keep
77
+ * the live event but lose reload-survivable in-flight visibility.
78
+ */
79
+ trackStepStart?: boolean;
71
80
  /**
72
81
  * Where a freshly-`start`ed run executes (see {@link RunDispatcher}). Defaults to in-process: the
73
82
  * run executes on this instance asynchronously (a microtask), so `start` returns without blocking.
@@ -94,6 +103,8 @@ export declare class WorkflowEngine {
94
103
  private readonly webhookUrl?;
95
104
  private readonly traceparent?;
96
105
  private readonly compensationRetries;
106
+ /** Persist a `running` checkpoint at the start of a local step body (see {@link WorkflowEngineDeps.trackStepStart}). */
107
+ private readonly trackStepStart;
97
108
  /** Where a freshly-started run executes — in-process by default (see {@link RunDispatcher}). */
98
109
  private readonly runDispatcher;
99
110
  /** Every registered workflow, keyed by `name@version` — so old versions stay runnable. */
@@ -162,6 +173,20 @@ export declare class WorkflowEngine {
162
173
  onEvent?: string[];
163
174
  eventBatch?: EventBatchConfig;
164
175
  }): void;
176
+ /**
177
+ * Register a workflow whose body runs in another SDK (e.g. Python). The engine owns the run exactly
178
+ * as for a TS workflow — it persists checkpoints, recovers, runs timers — but advances it by handing
179
+ * the run's history to `executor` (which dispatches a {@link WorkflowTask} to the worker) and applying
180
+ * the {@link WorkflowDecision} the worker's replay returns. The worker never touches the store.
181
+ */
182
+ registerRemote(name: string, version: string, opts: {
183
+ group: string;
184
+ executor: WorkflowExecutor;
185
+ tags?: string[];
186
+ singleton?: SingletonConfig;
187
+ executionTimeout?: string | number;
188
+ validateInput?: (input: unknown) => void | Promise<void>;
189
+ }): void;
165
190
  /**
166
191
  * Register a flow-control queue referenced by `ctx.call(step, input, { queue })`. Caps concurrent
167
192
  * in-flight steps and/or the admission rate; blocked calls re-suspend and retry, so the limit is
@@ -344,6 +369,14 @@ export declare class WorkflowEngine {
344
369
  * canonical parent→children edge, used for both cancellation cascades and the dashboard run-tree.
345
370
  */
346
371
  getRunChildren(parentRunId: string): Promise<string[]>;
372
+ /**
373
+ * Announce a local step's body has begun and (when `trackStepStart`) checkpoint it as `running`,
374
+ * so it's visible in flight rather than appearing only on completion. The checkpoint is a
375
+ * placeholder overwritten by {@link completeStep}/{@link failStep}; it never short-circuits replay
376
+ * (only `completed` does), so a crash mid-body just re-runs the step. The `step.started` event
377
+ * fires regardless — the live SSE view sees the start even with persistence off.
378
+ */
379
+ private startStep;
347
380
  /** Checkpoint a finished step and announce it — the two things that must always happen together. */
348
381
  private completeStep;
349
382
  /** Checkpoint a step that failed terminally, so the failure point is visible (not just the run). */
@@ -355,6 +388,18 @@ export declare class WorkflowEngine {
355
388
  */
356
389
  private admitSingleton;
357
390
  private execute;
391
+ /**
392
+ * Advance a remote (cross-SDK) workflow one turn: hand its history to the executor (which dispatches
393
+ * a workflow task to the worker and awaits its replay) and apply the decision. Mirrors
394
+ * {@link runExecution}'s settle/suspend; the lease is held by {@link execute}. The result that lands
395
+ * us back here (a remote step finished, a timer fired) goes through `resume` like any TS workflow.
396
+ */
397
+ private runRemoteExecution;
398
+ /** The run's resolved durable ops as replay inputs: completed/failed steps + elapsed timers. */
399
+ private remoteHistory;
400
+ /** Apply a turn's commands: persist recorded local steps, dispatch remote calls, schedule timers.
401
+ * Returns the earliest timer deadline to suspend on (or undefined — suspended on a result). */
402
+ private applyCommands;
358
403
  /** The run body, lease held + renewed by {@link execute}. */
359
404
  private runExecution;
360
405
  /**
@@ -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
@@ -59,6 +59,8 @@ class WorkflowEngine {
59
59
  webhookUrl;
60
60
  traceparent;
61
61
  compensationRetries;
62
+ /** Persist a `running` checkpoint at the start of a local step body (see {@link WorkflowEngineDeps.trackStepStart}). */
63
+ trackStepStart;
62
64
  /** Where a freshly-started run executes — in-process by default (see {@link RunDispatcher}). */
63
65
  runDispatcher;
64
66
  /** Every registered workflow, keyed by `name@version` — so old versions stay runnable. */
@@ -106,6 +108,7 @@ class WorkflowEngine {
106
108
  this.webhookUrl = deps.webhookUrl;
107
109
  this.traceparent = deps.traceparent;
108
110
  this.compensationRetries = Math.max(1, deps.compensationRetries ?? 1);
111
+ this.trackStepStart = deps.trackStepStart ?? true;
109
112
  // Default: execute the run on this instance, asynchronously, so `start` never blocks on the body.
110
113
  // A failed pickup is swallowed here (the run stays `pending` for a `runPending` poll to retry);
111
114
  // run failures themselves are handled inside `execute` and surfaced as the run's `failed` state.
@@ -229,6 +232,31 @@ class WorkflowEngine {
229
232
  this.eventTriggers.set(event, subscribers);
230
233
  }
231
234
  }
235
+ /**
236
+ * Register a workflow whose body runs in another SDK (e.g. Python). The engine owns the run exactly
237
+ * as for a TS workflow — it persists checkpoints, recovers, runs timers — but advances it by handing
238
+ * the run's history to `executor` (which dispatches a {@link WorkflowTask} to the worker) and applying
239
+ * the {@link WorkflowDecision} the worker's replay returns. The worker never touches the store.
240
+ */
241
+ registerRemote(name, version, opts) {
242
+ const registered = {
243
+ name,
244
+ version,
245
+ // A remote workflow has no in-process body; execute() branches on `remote` before this is read.
246
+ fn: () => {
247
+ throw new Error(`workflow ${name} is remote — it has no in-process body`);
248
+ },
249
+ tags: opts.tags,
250
+ singleton: opts.singleton,
251
+ executionTimeoutMs: opts.executionTimeout != null ? (0, duration_1.parseDuration)(opts.executionTimeout) : undefined,
252
+ validateInput: opts.validateInput,
253
+ remote: { group: opts.group, executor: opts.executor },
254
+ };
255
+ this.workflows.set(versionKey(name, version), registered);
256
+ const current = this.latest.get(name);
257
+ if (!current || isNewerVersion(version, current.version))
258
+ this.latest.set(name, registered);
259
+ }
232
260
  /**
233
261
  * Register a flow-control queue referenced by `ctx.call(step, input, { queue })`. Caps concurrent
234
262
  * in-flight steps and/or the admission rate; blocked calls re-suspend and retry, so the limit is
@@ -788,6 +816,39 @@ class WorkflowEngine {
788
816
  }
789
817
  return [...childIds];
790
818
  }
819
+ /**
820
+ * Announce a local step's body has begun and (when `trackStepStart`) checkpoint it as `running`,
821
+ * so it's visible in flight rather than appearing only on completion. The checkpoint is a
822
+ * placeholder overwritten by {@link completeStep}/{@link failStep}; it never short-circuits replay
823
+ * (only `completed` does), so a crash mid-body just re-runs the step. The `step.started` event
824
+ * fires regardless — the live SSE view sees the start even with persistence off.
825
+ */
826
+ async startStep(step) {
827
+ if (this.trackStepStart) {
828
+ await this.store.saveCheckpoint({
829
+ runId: step.runId,
830
+ seq: step.seq,
831
+ name: step.name,
832
+ kind: step.kind,
833
+ stepId: (0, protocol_1.stepId)(step.runId, step.seq),
834
+ status: 'running',
835
+ input: step.input,
836
+ events: step.events && step.events.length > 0 ? step.events : undefined,
837
+ attempts: step.attempts,
838
+ workerGroup: step.workerGroup,
839
+ enqueuedAt: step.enqueuedAt,
840
+ startedAt: step.startedAt,
841
+ finishedAt: step.startedAt, // placeholder until the body settles
842
+ });
843
+ }
844
+ this.emit({
845
+ type: 'step.started',
846
+ runId: step.runId,
847
+ seq: step.seq,
848
+ name: step.name,
849
+ kind: step.kind,
850
+ });
851
+ }
791
852
  /** Checkpoint a finished step and announce it — the two things that must always happen together. */
792
853
  async completeStep(step) {
793
854
  await this.store.saveCheckpoint({
@@ -880,12 +941,248 @@ class WorkflowEngine {
880
941
  }, Math.max(50, Math.floor(this.leaseMs / 2)));
881
942
  renew.unref?.();
882
943
  try {
944
+ // A remote (e.g. Python) workflow is advanced by dispatching workflow tasks, not by running an
945
+ // in-process body — but everything around it (lease, recovery, timers, the resume that lands us
946
+ // here on a step result) is identical, so it branches here under the same lease.
947
+ if (registered?.remote)
948
+ return await this.runRemoteExecution(run, registered);
883
949
  return await this.runExecution(run, fn, registered);
884
950
  }
885
951
  finally {
886
952
  clearInterval(renew);
887
953
  }
888
954
  }
955
+ /**
956
+ * Advance a remote (cross-SDK) workflow one turn: hand its history to the executor (which dispatches
957
+ * a workflow task to the worker and awaits its replay) and apply the decision. Mirrors
958
+ * {@link runExecution}'s settle/suspend; the lease is held by {@link execute}. The result that lands
959
+ * us back here (a remote step finished, a timer fired) goes through `resume` like any TS workflow.
960
+ */
961
+ async runRemoteExecution(run, registered) {
962
+ const remote = registered.remote;
963
+ if (run.status === 'pending') {
964
+ await this.store.updateRun(run.id, { status: 'running', updatedAt: new Date() });
965
+ run.status = 'running';
966
+ this.emit({ type: 'run.started', runId: run.id, workflow: run.workflow });
967
+ }
968
+ if (registered.singleton && !(await this.admitSingleton(run, registered.singleton))) {
969
+ const wakeAt = this.clock() + SINGLETON_RETRY_MS;
970
+ await this.store.updateRun(run.id, { status: 'suspended', wakeAt, updatedAt: new Date() });
971
+ this.emit({ type: 'run.suspended', runId: run.id, workflow: run.workflow });
972
+ await this.store.releaseRunLock(run.id);
973
+ return { runId: run.id, status: 'suspended' };
974
+ }
975
+ const history = await this.remoteHistory(run.id);
976
+ let decision;
977
+ try {
978
+ decision = await remote.executor.advance(run, history);
979
+ }
980
+ catch (err) {
981
+ const error = {
982
+ message: err instanceof Error ? err.message : String(err),
983
+ stack: err instanceof Error ? err.stack : undefined,
984
+ };
985
+ await this.store.updateRun(run.id, { status: 'failed', error, updatedAt: new Date() });
986
+ this.emit({ type: 'run.failed', runId: run.id, workflow: run.workflow, error });
987
+ return { runId: run.id, status: 'failed', error };
988
+ }
989
+ if (decision.status === 'completed') {
990
+ await this.store.updateRun(run.id, {
991
+ status: 'completed',
992
+ output: decision.output,
993
+ error: undefined,
994
+ updatedAt: new Date(),
995
+ });
996
+ this.emit({
997
+ type: 'run.completed',
998
+ runId: run.id,
999
+ workflow: run.workflow,
1000
+ output: decision.output,
1001
+ });
1002
+ void this.notifyParent(run.id, { ok: true, value: decision.output });
1003
+ return { runId: run.id, status: 'completed', output: decision.output };
1004
+ }
1005
+ if (decision.status === 'failed') {
1006
+ const error = decision.error ?? { message: 'workflow failed' };
1007
+ await this.store.updateRun(run.id, { status: 'failed', error, updatedAt: new Date() });
1008
+ this.emit({ type: 'run.failed', runId: run.id, workflow: run.workflow, error });
1009
+ void this.notifyParent(run.id, { ok: false, error: error.message });
1010
+ return { runId: run.id, status: 'failed', error };
1011
+ }
1012
+ // continue: persist any local steps the replay ran, dispatch the blocking ops, then suspend. When
1013
+ // those resolve (a result lands, a timer fires) `resume` brings us back for the next turn.
1014
+ const wakeAt = await this.applyCommands(run, decision.commands);
1015
+ await this.store.updateRun(run.id, { status: 'suspended', wakeAt, updatedAt: new Date() });
1016
+ this.emit({ type: 'run.suspended', runId: run.id, workflow: run.workflow });
1017
+ return { runId: run.id, status: 'suspended' };
1018
+ }
1019
+ /** The run's resolved durable ops as replay inputs: completed/failed steps + elapsed timers. */
1020
+ async remoteHistory(runId) {
1021
+ const checkpoints = await this.store.listCheckpoints(runId);
1022
+ const kindOf = {
1023
+ remote: 'call',
1024
+ local: 'step',
1025
+ sleep: 'timer',
1026
+ signal: 'signal',
1027
+ };
1028
+ const events = [];
1029
+ for (const cp of checkpoints) {
1030
+ if (cp.status === 'completed' || cp.status === 'failed') {
1031
+ // A child run resolves THROUGH the signal machinery (a `child:<id>` waiter notified on the
1032
+ // child's terminal state), so its checkpoint is kind `signal` with a `signal:child:` name and
1033
+ // a Completion payload. Surface it as a `child` event with the value/error unwrapped.
1034
+ if (cp.kind === 'signal' && cp.name.startsWith('signal:child:')) {
1035
+ const completion = cp.output;
1036
+ events.push({
1037
+ seq: cp.seq,
1038
+ kind: 'child',
1039
+ output: completion?.ok ? completion.value : undefined,
1040
+ error: completion && completion.ok === false ? { message: completion.error } : undefined,
1041
+ });
1042
+ continue;
1043
+ }
1044
+ events.push({
1045
+ seq: cp.seq,
1046
+ kind: kindOf[cp.kind] ?? 'step',
1047
+ // A signal checkpoint's name is the internal `signal:<token>`, not the workflow-level signal
1048
+ // name the replay used — omit it so the replay matches on seq + kind (its determinism anchor).
1049
+ name: cp.kind === 'signal' ? undefined : cp.name,
1050
+ output: cp.status === 'completed' ? cp.output : undefined,
1051
+ error: cp.status === 'failed' ? cp.error : undefined,
1052
+ });
1053
+ }
1054
+ else if (cp.kind === 'sleep' && cp.wakeAt != null && cp.wakeAt <= this.clock()) {
1055
+ // a still-`pending` sleep whose deadline has passed reads as a resolved timer on replay.
1056
+ events.push({ seq: cp.seq, kind: 'timer', name: cp.name });
1057
+ }
1058
+ }
1059
+ return events.sort((a, b) => a.seq - b.seq);
1060
+ }
1061
+ /** Apply a turn's commands: persist recorded local steps, dispatch remote calls, schedule timers.
1062
+ * Returns the earliest timer deadline to suspend on (or undefined — suspended on a result). */
1063
+ async applyCommands(run, commands) {
1064
+ let wakeAt;
1065
+ for (const cmd of commands) {
1066
+ const at = new Date();
1067
+ const id = (0, protocol_1.stepId)(run.id, cmd.seq);
1068
+ if (cmd.kind === 'recordStep') {
1069
+ await this.store.saveCheckpoint({
1070
+ runId: run.id,
1071
+ seq: cmd.seq,
1072
+ name: cmd.name,
1073
+ kind: 'local',
1074
+ stepId: id,
1075
+ status: cmd.error ? 'failed' : 'completed',
1076
+ output: cmd.output,
1077
+ error: cmd.error,
1078
+ attempts: 1,
1079
+ enqueuedAt: at,
1080
+ startedAt: at,
1081
+ finishedAt: at,
1082
+ });
1083
+ this.emit({
1084
+ type: cmd.error ? 'step.failed' : 'step.completed',
1085
+ runId: run.id,
1086
+ seq: cmd.seq,
1087
+ name: cmd.name,
1088
+ kind: 'local',
1089
+ output: cmd.output,
1090
+ error: cmd.error,
1091
+ });
1092
+ }
1093
+ else if (cmd.kind === 'call') {
1094
+ await this.store.saveCheckpoint({
1095
+ runId: run.id,
1096
+ seq: cmd.seq,
1097
+ name: cmd.name,
1098
+ kind: 'remote',
1099
+ stepId: id,
1100
+ status: 'pending',
1101
+ input: cmd.input,
1102
+ attempts: 1,
1103
+ workerGroup: cmd.group,
1104
+ enqueuedAt: at,
1105
+ startedAt: at,
1106
+ finishedAt: at,
1107
+ });
1108
+ await this.pool.dispatch({
1109
+ runId: run.id,
1110
+ seq: cmd.seq,
1111
+ name: cmd.name,
1112
+ stepId: id,
1113
+ group: cmd.group,
1114
+ input: cmd.input,
1115
+ traceparent: this.traceparent?.(),
1116
+ attempt: 1,
1117
+ }, undefined);
1118
+ this.emit({
1119
+ type: 'step.started',
1120
+ runId: run.id,
1121
+ seq: cmd.seq,
1122
+ name: cmd.name,
1123
+ kind: 'remote',
1124
+ });
1125
+ }
1126
+ else if (cmd.kind === 'sleep') {
1127
+ const deadline = this.clock() + cmd.ms;
1128
+ await this.store.saveCheckpoint({
1129
+ runId: run.id,
1130
+ seq: cmd.seq,
1131
+ name: `sleep:${cmd.seq}`,
1132
+ kind: 'sleep',
1133
+ stepId: id,
1134
+ status: 'pending',
1135
+ attempts: 1,
1136
+ wakeAt: deadline,
1137
+ enqueuedAt: at,
1138
+ startedAt: at,
1139
+ finishedAt: at,
1140
+ });
1141
+ wakeAt = wakeAt == null ? deadline : Math.min(wakeAt, deadline);
1142
+ }
1143
+ else if (cmd.kind === 'waitSignal') {
1144
+ // Park on a signal: register a waiter at this seq so engine.signal(token) lands the resolving
1145
+ // `signal` checkpoint here and resumes the run. The token is the signal name, so an external
1146
+ // engine.signal(name, payload) delivers it. If the signal was already delivered (buffered
1147
+ // before the workflow reached this point — e.g. signalWithStart), resolve it now and re-drive
1148
+ // on a macrotask, AFTER this turn suspends and frees the run lock (a re-entrant resume bails).
1149
+ const buffered = await this.store.takeBufferedSignal(cmd.signal);
1150
+ if (buffered) {
1151
+ await this.store.saveCheckpoint((0, checkpoints_1.instantCheckpoint)({
1152
+ runId: run.id,
1153
+ seq: cmd.seq,
1154
+ name: `signal:${cmd.signal}`,
1155
+ kind: 'signal',
1156
+ output: buffered.payload,
1157
+ }));
1158
+ setTimeout(() => void this.resume(run.id).catch(() => undefined), 0);
1159
+ }
1160
+ else {
1161
+ await this.store.putSignalWaiter({ token: cmd.signal, runId: run.id, seq: cmd.seq });
1162
+ }
1163
+ }
1164
+ else if (cmd.kind === 'startChild') {
1165
+ // Start a child run and await it (the worker's ctx.start_child suspends until the child's
1166
+ // result is in history). Mirror the in-process `ctx.child`: register a `child:<id>` waiter at
1167
+ // this seq — the child notifies it on its terminal state (engine.notifyParent) — then start the
1168
+ // child once, deferred so a fast child can't reentrantly resume this still-suspending parent,
1169
+ // and id-idempotent so replay/recovery never double-starts it.
1170
+ const childId = `${run.id}.child.${cmd.seq}`;
1171
+ await this.store.putSignalWaiter({
1172
+ token: `child:${childId}`,
1173
+ runId: run.id,
1174
+ seq: cmd.seq,
1175
+ });
1176
+ if (!(await this.store.getRun(childId))) {
1177
+ queueMicrotask(() => void this.start(cmd.workflow, cmd.input, childId).catch(() => undefined));
1178
+ }
1179
+ }
1180
+ else {
1181
+ throw new Error(`remote workflow command '${cmd.kind}' is not supported yet`);
1182
+ }
1183
+ }
1184
+ return wakeAt;
1185
+ }
889
1186
  /** The run body, lease held + renewed by {@link execute}. */
890
1187
  async runExecution(run, fn, registered) {
891
1188
  // First execution of an enqueued run: mark it running and announce the start, BEFORE the singleton
@@ -1032,6 +1329,7 @@ class WorkflowEngine {
1032
1329
  store: this.store,
1033
1330
  clock: this.clock,
1034
1331
  webhookUrl: this.webhookUrl,
1332
+ startStep: (s) => this.startStep(s),
1035
1333
  completeStep: (s) => this.completeStep(s),
1036
1334
  failStep: (s) => this.failStep(s),
1037
1335
  callRemote: (runId, seq, step, input, queue, transport) => this.callRemote(runId, seq, step, input, queue, transport),