@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 +46 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +298 -0
- 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 +136 -6
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/remote-workflow-executor.d.ts +24 -0
- package/dist/remote-workflow-executor.d.ts.map +1 -0
- package/dist/remote-workflow-executor.js +73 -0
- package/dist/remote-workflow-executor.js.map +1 -0
- package/dist/step-logger.d.ts.map +1 -1
- package/dist/step-logger.js +11 -0
- package/dist/step-logger.js.map +1 -1
- package/dist/workflow-ctx.d.ts +2 -0
- package/dist/workflow-ctx.d.ts.map +1 -1
- package/dist/workflow-ctx.js +12 -0
- package/dist/workflow-ctx.js.map +1 -1
- package/package.json +1 -1
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
|
/**
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAIA,OAAO,EAAY,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AASzD,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEhF,OAAO,KAAK,EACV,YAAY,EAEZ,cAAc,EACd,cAAc,EAGd,aAAa,EACb,SAAS,EAET,gBAAgB,EAChB,UAAU,EACV,SAAS,EAET,eAAe,
|
|
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),
|