@dudousxd/nestjs-durable-core 0.54.0 → 0.55.1
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 +43 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +43 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1190,6 +1190,21 @@ var TransportPool = class {
|
|
|
1190
1190
|
];
|
|
1191
1191
|
}
|
|
1192
1192
|
/**
|
|
1193
|
+
* The first transport (in pool order) reporting a LIVE heartbeat for `group` — the one a
|
|
1194
|
+
* convention-routed remote workflow must dispatch on. In a mixed pool (e.g. a tenant-scoped
|
|
1195
|
+
* control plane pairing its namespaced transport with a bare-prefix one for operator-convention
|
|
1196
|
+
* tenant workers), each transport sees a DIFFERENT keyspace: dispatching on `primary` while the
|
|
1197
|
+
* live worker heartbeats on another transport parks the task on a queue nobody consumes. Returns
|
|
1198
|
+
* `undefined` when no transport reports the group (mirrors the merged `listWorkerGroups` miss).
|
|
1199
|
+
*/
|
|
1200
|
+
async transportWithLiveGroup(group) {
|
|
1201
|
+
for (const { transport } of this.transports) {
|
|
1202
|
+
if (!transport.listWorkerGroups) continue;
|
|
1203
|
+
if ((await transport.listWorkerGroups()).includes(group)) return transport;
|
|
1204
|
+
}
|
|
1205
|
+
return void 0;
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1193
1208
|
* Propagate the engine's `namespace` to every transport that partitions by it — so the same
|
|
1194
1209
|
* namespace that scopes the store also scopes each transport's queues/keys. A transport that
|
|
1195
1210
|
* doesn't partition (no `useNamespace`) is skipped. Idempotent; a transport given an explicit
|
|
@@ -2505,10 +2520,10 @@ var WorkflowEngine = class {
|
|
|
2505
2520
|
* liveness changes between calls, exactly as {@link synthesizeRemoteChild} does.
|
|
2506
2521
|
*/
|
|
2507
2522
|
async resolveRemoteByConvention(run) {
|
|
2508
|
-
const liveGroups = await this.pool.listWorkerGroups();
|
|
2509
2523
|
const group = tenantGroup(sanitizeQueueToken(run.workflow), run.namespace);
|
|
2510
|
-
|
|
2511
|
-
|
|
2524
|
+
const liveTransport = await this.pool.transportWithLiveGroup(group);
|
|
2525
|
+
if (!liveTransport) return void 0;
|
|
2526
|
+
const executor = new RemoteWorkflowExecutor(liveTransport, run.workflow, run.namespace);
|
|
2512
2527
|
return {
|
|
2513
2528
|
name: run.workflow,
|
|
2514
2529
|
version: run.workflowVersion,
|
|
@@ -3127,6 +3142,27 @@ var WorkflowEngine = class {
|
|
|
3127
3142
|
void this.signal(`child:${runId}`, completion).catch(() => void 0);
|
|
3128
3143
|
}
|
|
3129
3144
|
/**
|
|
3145
|
+
* Deferred child start shared by the in-process ctx host and the remote `startChild` command.
|
|
3146
|
+
* Deferred (microtask) so a fast child can't reentrantly resume a still-suspending parent. A start
|
|
3147
|
+
* that THROWS (unregistered/unroutable workflow, input validation, singleton back-pressure, store
|
|
3148
|
+
* failure) must NOT be swallowed: the parent is already suspended on `child:<childId>` (the waiter
|
|
3149
|
+
* is put BEFORE start on every path), so a silent drop parks it in suspended-forever, invisibly
|
|
3150
|
+
* re-attempting on every recovery wake — a misconfigured remote child looks exactly like a healthy
|
|
3151
|
+
* long wait. Instead the failure is delivered to that waiter like a failed child (notifyParent):
|
|
3152
|
+
* the parent resumes and fails loudly with the cause. For a fire-and-forget `ctx.startChild`
|
|
3153
|
+
* (spawn — no waiter) the completion is buffered; a later join by the same id consumes it and
|
|
3154
|
+
* correctly observes the failed start.
|
|
3155
|
+
*/
|
|
3156
|
+
startChildDeferred(workflow, input, childId, opts) {
|
|
3157
|
+
queueMicrotask(() => void this.start(workflow, input, childId, opts).catch((err) => {
|
|
3158
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3159
|
+
this.notifyParent(childId, {
|
|
3160
|
+
ok: false,
|
|
3161
|
+
error: `child workflow "${workflow}" failed to start: ${message}`
|
|
3162
|
+
});
|
|
3163
|
+
}));
|
|
3164
|
+
}
|
|
3165
|
+
/**
|
|
3130
3166
|
* Cancel a run (e.g. from the dashboard). Returns null if the run does not exist. Pass
|
|
3131
3167
|
* `{ compensate: true }` to undo the saga first: the suspended run is resumed so its completed
|
|
3132
3168
|
* steps' compensations run in reverse (visible as `compensate:<step>` events), then it's marked
|
|
@@ -3950,9 +3986,9 @@ var WorkflowEngine = class {
|
|
|
3950
3986
|
parallelGroup: cmd.parallelGroup
|
|
3951
3987
|
});
|
|
3952
3988
|
if (!await this.store.getRun(childId)) {
|
|
3953
|
-
|
|
3989
|
+
this.startChildDeferred(cmd.workflow, cmd.input, childId, {
|
|
3954
3990
|
namespace: run.namespace
|
|
3955
|
-
})
|
|
3991
|
+
});
|
|
3956
3992
|
}
|
|
3957
3993
|
} else {
|
|
3958
3994
|
throw new Error(`remote workflow command '${cmd.kind}' is not supported yet`);
|
|
@@ -4331,10 +4367,10 @@ var WorkflowEngine = class {
|
|
|
4331
4367
|
// `davi-local` pipeline would stamp its `processing` child `default` and it would leak off the
|
|
4332
4368
|
// tenant's worker pool onto the shared/dev workers.
|
|
4333
4369
|
startChild: /* @__PURE__ */ __name((workflow, input, id, priority) => {
|
|
4334
|
-
|
|
4370
|
+
this.startChildDeferred(workflow, input, id, {
|
|
4335
4371
|
priority,
|
|
4336
4372
|
namespace: parentNamespace
|
|
4337
|
-
})
|
|
4373
|
+
});
|
|
4338
4374
|
}, "startChild"),
|
|
4339
4375
|
// Deferred for the same reentrancy reason as `startChild` above. `cancel()` is already
|
|
4340
4376
|
// idempotent on a terminal/cancelled run (returns its existing status without side effects), so
|