@dudousxd/nestjs-durable-core 0.49.0 → 0.49.2
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 +25 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +25 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1113,6 +1113,16 @@ interface GroupHealth {
|
|
|
1113
1113
|
depth: number;
|
|
1114
1114
|
/** Workers with a non-expired heartbeat for this group. */
|
|
1115
1115
|
liveWorkers: WorkerHeartbeat[];
|
|
1116
|
+
/**
|
|
1117
|
+
* Whether this group serves a `@Workflow` body or a `@Step`/handler — route-by-handler gives each
|
|
1118
|
+
* its own queue, so a health list mixes both. Classified by the CONTROL PLANE from its authoritative
|
|
1119
|
+
* registry (`workerHealth()`): a group whose base name is a registered workflow is `'workflow'`,
|
|
1120
|
+
* everything else (in-process steps, remote `handle_*`) is `'step'`. `undefined` only when no
|
|
1121
|
+
* control-plane registry was available to classify (e.g. a transport that reports health with no
|
|
1122
|
+
* engine). Lets a dashboard summarise the fleet in domain terms ("N workflows · M steps") instead of
|
|
1123
|
+
* leaking the raw queue count.
|
|
1124
|
+
*/
|
|
1125
|
+
kind?: 'workflow' | 'step';
|
|
1116
1126
|
}
|
|
1117
1127
|
/**
|
|
1118
1128
|
* The **control plane** — a broadcast pub/sub across ALL engine instances (every pod), separate
|
|
@@ -1774,6 +1784,19 @@ interface WorkflowEngineDeps {
|
|
|
1774
1784
|
* one (this rearmed window for heartbeat-aware workers, or the fixed `timeoutMs` otherwise).
|
|
1775
1785
|
*/
|
|
1776
1786
|
remoteAdvanceSilenceMs?: number | undefined;
|
|
1787
|
+
/**
|
|
1788
|
+
* Fallback re-drive window (ms) for a run that SUSPENDS with no natural timer — i.e. it's waiting on
|
|
1789
|
+
* an EVENT to wake it (a child's completion, a signal, a remote step with no `timeoutMs`) rather than
|
|
1790
|
+
* a `ctx.sleep`. Those events re-drive the run directly; but if the wake is LOST (the delivering pod
|
|
1791
|
+
* crashed / rolled mid-handoff), the run would otherwise sit `suspended` forever with `wakeAt: null` —
|
|
1792
|
+
* invisible to both the timer poller (no `wakeAt`) and crash-recovery (no lease). This stamps a
|
|
1793
|
+
* fallback `wakeAt` so {@link resumeDueTimers} re-drives it after the window; the re-drive is an
|
|
1794
|
+
* idempotent replay (a still-pending dependency re-suspends via the checkpoint guard, a settled one
|
|
1795
|
+
* advances), so it's a safe self-heal, not a retry that can double-dispatch. The primary event path
|
|
1796
|
+
* still wins first for a healthy run (it re-drives long before this fires), so this only ever fires
|
|
1797
|
+
* for a genuinely-orphaned run. Default 5min; set `0` to disable (the prior wake-forever behavior).
|
|
1798
|
+
*/
|
|
1799
|
+
reconcileMs?: number | undefined;
|
|
1777
1800
|
/**
|
|
1778
1801
|
* Build the public callback URL for a `ctx.webhook()` token (e.g.
|
|
1779
1802
|
* ``(t) => `https://api.example.com/durable/webhooks/${t}` ``). Populates
|
|
@@ -1849,6 +1872,7 @@ declare class WorkflowEngine {
|
|
|
1849
1872
|
private readonly leaseMs;
|
|
1850
1873
|
private readonly maxRecoveryAttempts?;
|
|
1851
1874
|
private readonly remoteAdvanceSilenceMs?;
|
|
1875
|
+
private readonly reconcileMs;
|
|
1852
1876
|
private readonly webhookUrl?;
|
|
1853
1877
|
private readonly traceparent?;
|
|
1854
1878
|
private readonly context?;
|
|
@@ -2314,6 +2338,13 @@ declare class WorkflowEngine {
|
|
|
2314
2338
|
* keeps its own lease handling (the TS executor's `finally`, the remote path's caller).
|
|
2315
2339
|
*/
|
|
2316
2340
|
private settleRun;
|
|
2341
|
+
/**
|
|
2342
|
+
* A wakeAt to persist on a suspend: the natural timer if the turn produced one (a `ctx.sleep`, a
|
|
2343
|
+
* remote-step deadline), else the {@link reconcileMs} fallback so an event-waiting run (child/signal/
|
|
2344
|
+
* timeout-less remote step) can't be orphaned forever if its wake is lost. `undefined` only when the
|
|
2345
|
+
* fallback is disabled (`reconcileMs: 0`). See {@link WorkflowEngineDeps.reconcileMs}.
|
|
2346
|
+
*/
|
|
2347
|
+
private reconcileWakeAt;
|
|
2317
2348
|
private runRemoteExecution;
|
|
2318
2349
|
/**
|
|
2319
2350
|
* Apply a remote workflow turn's {@link WorkflowDecision}: persist the local steps it ran, then
|
package/dist/index.d.ts
CHANGED
|
@@ -1113,6 +1113,16 @@ interface GroupHealth {
|
|
|
1113
1113
|
depth: number;
|
|
1114
1114
|
/** Workers with a non-expired heartbeat for this group. */
|
|
1115
1115
|
liveWorkers: WorkerHeartbeat[];
|
|
1116
|
+
/**
|
|
1117
|
+
* Whether this group serves a `@Workflow` body or a `@Step`/handler — route-by-handler gives each
|
|
1118
|
+
* its own queue, so a health list mixes both. Classified by the CONTROL PLANE from its authoritative
|
|
1119
|
+
* registry (`workerHealth()`): a group whose base name is a registered workflow is `'workflow'`,
|
|
1120
|
+
* everything else (in-process steps, remote `handle_*`) is `'step'`. `undefined` only when no
|
|
1121
|
+
* control-plane registry was available to classify (e.g. a transport that reports health with no
|
|
1122
|
+
* engine). Lets a dashboard summarise the fleet in domain terms ("N workflows · M steps") instead of
|
|
1123
|
+
* leaking the raw queue count.
|
|
1124
|
+
*/
|
|
1125
|
+
kind?: 'workflow' | 'step';
|
|
1116
1126
|
}
|
|
1117
1127
|
/**
|
|
1118
1128
|
* The **control plane** — a broadcast pub/sub across ALL engine instances (every pod), separate
|
|
@@ -1774,6 +1784,19 @@ interface WorkflowEngineDeps {
|
|
|
1774
1784
|
* one (this rearmed window for heartbeat-aware workers, or the fixed `timeoutMs` otherwise).
|
|
1775
1785
|
*/
|
|
1776
1786
|
remoteAdvanceSilenceMs?: number | undefined;
|
|
1787
|
+
/**
|
|
1788
|
+
* Fallback re-drive window (ms) for a run that SUSPENDS with no natural timer — i.e. it's waiting on
|
|
1789
|
+
* an EVENT to wake it (a child's completion, a signal, a remote step with no `timeoutMs`) rather than
|
|
1790
|
+
* a `ctx.sleep`. Those events re-drive the run directly; but if the wake is LOST (the delivering pod
|
|
1791
|
+
* crashed / rolled mid-handoff), the run would otherwise sit `suspended` forever with `wakeAt: null` —
|
|
1792
|
+
* invisible to both the timer poller (no `wakeAt`) and crash-recovery (no lease). This stamps a
|
|
1793
|
+
* fallback `wakeAt` so {@link resumeDueTimers} re-drives it after the window; the re-drive is an
|
|
1794
|
+
* idempotent replay (a still-pending dependency re-suspends via the checkpoint guard, a settled one
|
|
1795
|
+
* advances), so it's a safe self-heal, not a retry that can double-dispatch. The primary event path
|
|
1796
|
+
* still wins first for a healthy run (it re-drives long before this fires), so this only ever fires
|
|
1797
|
+
* for a genuinely-orphaned run. Default 5min; set `0` to disable (the prior wake-forever behavior).
|
|
1798
|
+
*/
|
|
1799
|
+
reconcileMs?: number | undefined;
|
|
1777
1800
|
/**
|
|
1778
1801
|
* Build the public callback URL for a `ctx.webhook()` token (e.g.
|
|
1779
1802
|
* ``(t) => `https://api.example.com/durable/webhooks/${t}` ``). Populates
|
|
@@ -1849,6 +1872,7 @@ declare class WorkflowEngine {
|
|
|
1849
1872
|
private readonly leaseMs;
|
|
1850
1873
|
private readonly maxRecoveryAttempts?;
|
|
1851
1874
|
private readonly remoteAdvanceSilenceMs?;
|
|
1875
|
+
private readonly reconcileMs;
|
|
1852
1876
|
private readonly webhookUrl?;
|
|
1853
1877
|
private readonly traceparent?;
|
|
1854
1878
|
private readonly context?;
|
|
@@ -2314,6 +2338,13 @@ declare class WorkflowEngine {
|
|
|
2314
2338
|
* keeps its own lease handling (the TS executor's `finally`, the remote path's caller).
|
|
2315
2339
|
*/
|
|
2316
2340
|
private settleRun;
|
|
2341
|
+
/**
|
|
2342
|
+
* A wakeAt to persist on a suspend: the natural timer if the turn produced one (a `ctx.sleep`, a
|
|
2343
|
+
* remote-step deadline), else the {@link reconcileMs} fallback so an event-waiting run (child/signal/
|
|
2344
|
+
* timeout-less remote step) can't be orphaned forever if its wake is lost. `undefined` only when the
|
|
2345
|
+
* fallback is disabled (`reconcileMs: 0`). See {@link WorkflowEngineDeps.reconcileMs}.
|
|
2346
|
+
*/
|
|
2347
|
+
private reconcileWakeAt;
|
|
2317
2348
|
private runRemoteExecution;
|
|
2318
2349
|
/**
|
|
2319
2350
|
* Apply a remote workflow turn's {@link WorkflowDecision}: persist the local steps it ran, then
|
package/dist/index.js
CHANGED
|
@@ -1720,6 +1720,7 @@ var WorkflowEngine = class {
|
|
|
1720
1720
|
leaseMs;
|
|
1721
1721
|
maxRecoveryAttempts;
|
|
1722
1722
|
remoteAdvanceSilenceMs;
|
|
1723
|
+
reconcileMs;
|
|
1723
1724
|
webhookUrl;
|
|
1724
1725
|
traceparent;
|
|
1725
1726
|
context;
|
|
@@ -1790,6 +1791,7 @@ var WorkflowEngine = class {
|
|
|
1790
1791
|
this.leaseMs = deps.leaseMs ?? 3e4;
|
|
1791
1792
|
this.maxRecoveryAttempts = deps.maxRecoveryAttempts;
|
|
1792
1793
|
this.remoteAdvanceSilenceMs = deps.remoteAdvanceSilenceMs;
|
|
1794
|
+
this.reconcileMs = deps.reconcileMs === void 0 ? 3e5 : deps.reconcileMs || void 0;
|
|
1793
1795
|
this.webhookUrl = deps.webhookUrl;
|
|
1794
1796
|
this.traceparent = deps.traceparent;
|
|
1795
1797
|
this.context = deps.context;
|
|
@@ -2879,10 +2881,21 @@ var WorkflowEngine = class {
|
|
|
2879
2881
|
...this.knownGroups(extra),
|
|
2880
2882
|
...await this.pool.listWorkerGroups()
|
|
2881
2883
|
]);
|
|
2884
|
+
const workflowTokens = /* @__PURE__ */ new Set();
|
|
2885
|
+
for (const def of this.workflows.values()) {
|
|
2886
|
+
workflowTokens.add(sanitizeQueueToken(def.name));
|
|
2887
|
+
if (def.remote?.group) workflowTokens.add(def.remote.group);
|
|
2888
|
+
}
|
|
2882
2889
|
const out = [];
|
|
2883
2890
|
for (const group of groups) {
|
|
2884
2891
|
const health = await this.pool.groupHealth(group);
|
|
2885
|
-
if (health)
|
|
2892
|
+
if (!health) continue;
|
|
2893
|
+
const at = group.lastIndexOf("@");
|
|
2894
|
+
const baseToken = at === -1 ? group : group.slice(0, at);
|
|
2895
|
+
out.push({
|
|
2896
|
+
...health,
|
|
2897
|
+
kind: workflowTokens.has(baseToken) ? "workflow" : "step"
|
|
2898
|
+
});
|
|
2886
2899
|
}
|
|
2887
2900
|
return out;
|
|
2888
2901
|
}
|
|
@@ -3123,7 +3136,7 @@ var WorkflowEngine = class {
|
|
|
3123
3136
|
}
|
|
3124
3137
|
await this.store.updateRun(run.id, {
|
|
3125
3138
|
status: "suspended",
|
|
3126
|
-
wakeAt: outcome.wakeAt,
|
|
3139
|
+
wakeAt: this.reconcileWakeAt(outcome.wakeAt),
|
|
3127
3140
|
updatedAt
|
|
3128
3141
|
});
|
|
3129
3142
|
this.emit({
|
|
@@ -3137,6 +3150,16 @@ var WorkflowEngine = class {
|
|
|
3137
3150
|
status: "suspended"
|
|
3138
3151
|
};
|
|
3139
3152
|
}
|
|
3153
|
+
/**
|
|
3154
|
+
* A wakeAt to persist on a suspend: the natural timer if the turn produced one (a `ctx.sleep`, a
|
|
3155
|
+
* remote-step deadline), else the {@link reconcileMs} fallback so an event-waiting run (child/signal/
|
|
3156
|
+
* timeout-less remote step) can't be orphaned forever if its wake is lost. `undefined` only when the
|
|
3157
|
+
* fallback is disabled (`reconcileMs: 0`). See {@link WorkflowEngineDeps.reconcileMs}.
|
|
3158
|
+
*/
|
|
3159
|
+
reconcileWakeAt(wakeAt) {
|
|
3160
|
+
if (wakeAt != null) return wakeAt;
|
|
3161
|
+
return this.reconcileMs != null ? this.clock() + this.reconcileMs : void 0;
|
|
3162
|
+
}
|
|
3140
3163
|
async runRemoteExecution(run, registered) {
|
|
3141
3164
|
if (run.status === "cancelling") {
|
|
3142
3165
|
const error = {
|