@dudousxd/nestjs-durable-core 0.33.0 → 0.34.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/index.cjs +34 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -6
- package/dist/index.d.ts +35 -6
- package/dist/index.js +34 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1502,6 +1502,7 @@ var WorkflowEngine = class {
|
|
|
1502
1502
|
instanceId;
|
|
1503
1503
|
leaseMs;
|
|
1504
1504
|
maxRecoveryAttempts;
|
|
1505
|
+
remoteAdvanceSilenceMs;
|
|
1505
1506
|
webhookUrl;
|
|
1506
1507
|
traceparent;
|
|
1507
1508
|
context;
|
|
@@ -1526,7 +1527,9 @@ var WorkflowEngine = class {
|
|
|
1526
1527
|
accumulators;
|
|
1527
1528
|
/** In-flight remote steps awaiting a worker result, keyed by stepId. */
|
|
1528
1529
|
pending = /* @__PURE__ */ new Map();
|
|
1529
|
-
/**
|
|
1530
|
+
/** "Reset the liveness timer" callbacks keyed by the beat's target — a stepId (in-flight remote step)
|
|
1531
|
+
* or a runId (in-flight remote workflow turn) — invoked when a matching heartbeat arrives. The two id
|
|
1532
|
+
* shapes don't collide in practice: a stepId is `${runId}:${seq}`, a run key is the bare runId. */
|
|
1530
1533
|
heartbeatResets = /* @__PURE__ */ new Map();
|
|
1531
1534
|
listeners = /* @__PURE__ */ new Set();
|
|
1532
1535
|
/** Step interceptors (onion middleware around real local-step execution), first = outermost. */
|
|
@@ -1565,6 +1568,7 @@ var WorkflowEngine = class {
|
|
|
1565
1568
|
this.instanceId = deps.instanceId ?? globalThis.crypto.randomUUID();
|
|
1566
1569
|
this.leaseMs = deps.leaseMs ?? 3e4;
|
|
1567
1570
|
this.maxRecoveryAttempts = deps.maxRecoveryAttempts;
|
|
1571
|
+
this.remoteAdvanceSilenceMs = deps.remoteAdvanceSilenceMs;
|
|
1568
1572
|
this.webhookUrl = deps.webhookUrl;
|
|
1569
1573
|
this.traceparent = deps.traceparent;
|
|
1570
1574
|
this.context = deps.context;
|
|
@@ -1601,9 +1605,11 @@ var WorkflowEngine = class {
|
|
|
1601
1605
|
}
|
|
1602
1606
|
await this.completeRemoteResult(result);
|
|
1603
1607
|
},
|
|
1604
|
-
// A heartbeat for an in-flight long
|
|
1608
|
+
// A heartbeat resets the liveness window for whatever it targets: an in-flight long STEP (keyed
|
|
1609
|
+
// by stepId, see callRemote) or — when stepId is absent — the in-flight workflow TURN (keyed by
|
|
1610
|
+
// runId, see runRemoteExecution's heartbeat-rearmed advance).
|
|
1605
1611
|
async (beat) => {
|
|
1606
|
-
this.heartbeatResets.get(beat.stepId)?.();
|
|
1612
|
+
this.heartbeatResets.get(beat.stepId ?? beat.runId)?.();
|
|
1607
1613
|
},
|
|
1608
1614
|
// A remote workflow worker streams each local step's lifecycle (running → completed/failed) so
|
|
1609
1615
|
// it's checkpointed live, not all-at-once when the long turn ends.
|
|
@@ -2695,7 +2701,9 @@ var WorkflowEngine = class {
|
|
|
2695
2701
|
const history = await this.remoteHistory(run.id);
|
|
2696
2702
|
let decision;
|
|
2697
2703
|
try {
|
|
2698
|
-
|
|
2704
|
+
const silenceMs = this.remoteAdvanceSilenceMs;
|
|
2705
|
+
const advance = remote.executor.advance(run, history);
|
|
2706
|
+
decision = silenceMs != null ? await this.awaitWithLivenessDeadline(run.id, advance, silenceMs, () => new RemoteWorkflowTimeout(run.id, silenceMs)) : await advance;
|
|
2699
2707
|
} catch (err) {
|
|
2700
2708
|
if (err instanceof RemoteWorkflowTimeout) {
|
|
2701
2709
|
await this.store.releaseRunLock(run.id);
|
|
@@ -3356,11 +3364,13 @@ var WorkflowEngine = class {
|
|
|
3356
3364
|
}
|
|
3357
3365
|
}
|
|
3358
3366
|
/**
|
|
3359
|
-
* Await
|
|
3360
|
-
* arrives within `timeoutMs`. Each heartbeat (delivered via `transport.onHeartbeat
|
|
3361
|
-
* window, so a worker that keeps beating stays alive past
|
|
3367
|
+
* Await `resultPromise`, but reject with `onTimeout()` if neither it settles nor a heartbeat for `id`
|
|
3368
|
+
* arrives within `timeoutMs`. Each heartbeat (delivered via `transport.onHeartbeat`, looked up in
|
|
3369
|
+
* `heartbeatResets` by `id`) REARMS the window, so a worker that keeps beating stays alive past
|
|
3370
|
+
* `timeoutMs`. Shared by the remote-STEP liveness (keyed by stepId) and the remote-WORKFLOW advance
|
|
3371
|
+
* liveness (keyed by runId); the caller supplies the timeout error and any extra cleanup via `onTimeout`.
|
|
3362
3372
|
*/
|
|
3363
|
-
|
|
3373
|
+
awaitWithLivenessDeadline(id, resultPromise, timeoutMs, onTimeout) {
|
|
3364
3374
|
return new Promise((resolve, reject) => {
|
|
3365
3375
|
let timer;
|
|
3366
3376
|
const cleanup = /* @__PURE__ */ __name(() => {
|
|
@@ -3370,8 +3380,7 @@ var WorkflowEngine = class {
|
|
|
3370
3380
|
const arm = /* @__PURE__ */ __name(() => {
|
|
3371
3381
|
timer = setTimeout(() => {
|
|
3372
3382
|
cleanup();
|
|
3373
|
-
|
|
3374
|
-
reject(new RemoteStepTimeout(id, timeoutMs));
|
|
3383
|
+
reject(onTimeout());
|
|
3375
3384
|
}, timeoutMs);
|
|
3376
3385
|
timer.unref?.();
|
|
3377
3386
|
}, "arm");
|
|
@@ -3389,6 +3398,14 @@ var WorkflowEngine = class {
|
|
|
3389
3398
|
});
|
|
3390
3399
|
});
|
|
3391
3400
|
}
|
|
3401
|
+
/** Remote-STEP liveness: await a worker result, timing out (with the step's pending-map cleanup) to
|
|
3402
|
+
* `RemoteStepTimeout` if it neither lands nor beats within `timeoutMs`. See {@link awaitWithLivenessDeadline}. */
|
|
3403
|
+
awaitWithHeartbeat(id, resultPromise, timeoutMs) {
|
|
3404
|
+
return this.awaitWithLivenessDeadline(id, resultPromise, timeoutMs, () => {
|
|
3405
|
+
this.pending.delete(id);
|
|
3406
|
+
return new RemoteStepTimeout(id, timeoutMs);
|
|
3407
|
+
});
|
|
3408
|
+
}
|
|
3392
3409
|
};
|
|
3393
3410
|
var RemoteStepError = class extends Error {
|
|
3394
3411
|
static {
|
|
@@ -4154,6 +4171,7 @@ var InMemoryTransport = class {
|
|
|
4154
4171
|
}
|
|
4155
4172
|
handlers = /* @__PURE__ */ new Map();
|
|
4156
4173
|
resultHandler;
|
|
4174
|
+
heartbeatHandler;
|
|
4157
4175
|
stepEventHandler;
|
|
4158
4176
|
controlHandlers = /* @__PURE__ */ new Set();
|
|
4159
4177
|
/** Register a fake worker handler for a step name. */
|
|
@@ -4168,7 +4186,12 @@ var InMemoryTransport = class {
|
|
|
4168
4186
|
onResult(handler) {
|
|
4169
4187
|
this.resultHandler = handler;
|
|
4170
4188
|
}
|
|
4171
|
-
onHeartbeat(
|
|
4189
|
+
onHeartbeat(handler) {
|
|
4190
|
+
this.heartbeatHandler = handler;
|
|
4191
|
+
}
|
|
4192
|
+
/** Test hook: deliver a liveness heartbeat to the engine, as a real broker would on its channel. */
|
|
4193
|
+
async emitHeartbeat(beat) {
|
|
4194
|
+
await this.heartbeatHandler?.(beat);
|
|
4172
4195
|
}
|
|
4173
4196
|
/** Workflow step lifecycle: deliver to the engine's handler (async, mirroring a real broker). */
|
|
4174
4197
|
async dispatchStepEvent(event) {
|