@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.d.cts
CHANGED
|
@@ -1162,6 +1162,15 @@ interface Transport {
|
|
|
1162
1162
|
* transports that don't partition simply omit it.
|
|
1163
1163
|
*/
|
|
1164
1164
|
useNamespace?(namespace: string): void;
|
|
1165
|
+
/**
|
|
1166
|
+
* A SIBLING transport on the same broker/connection, pinned to `namespace` (explicit — never
|
|
1167
|
+
* re-scoped by a later {@link useNamespace}). Powers the tenant-worker bridge preset
|
|
1168
|
+
* (`topology: { role: 'control-plane', tenant, tenantWorkers: 'bridge' }`): a tenant-scoped
|
|
1169
|
+
* operator pairs its namespaced primary with `withNamespace('default')` — a bare-prefix sibling —
|
|
1170
|
+
* so operator-convention tenant workers (`<group>@<tenant>` under the bare prefix) are
|
|
1171
|
+
* discoverable and dispatchable. Optional — transports that don't partition simply omit it.
|
|
1172
|
+
*/
|
|
1173
|
+
withNamespace?(namespace: string): Transport;
|
|
1165
1174
|
/** Release the transport's resources (broker workers, queues, connections) for a clean shutdown.
|
|
1166
1175
|
* Optional — an in-process transport has nothing to close. Called on `onApplicationShutdown`
|
|
1167
1176
|
* after the engine drains, so a deploy hands off instead of leaving the broker to time out. */
|
|
@@ -2618,6 +2627,19 @@ declare class WorkflowEngine {
|
|
|
2618
2627
|
* knowing about the child feature.
|
|
2619
2628
|
*/
|
|
2620
2629
|
private notifyParent;
|
|
2630
|
+
/**
|
|
2631
|
+
* Deferred child start shared by the in-process ctx host and the remote `startChild` command.
|
|
2632
|
+
* Deferred (microtask) so a fast child can't reentrantly resume a still-suspending parent. A start
|
|
2633
|
+
* that THROWS (unregistered/unroutable workflow, input validation, singleton back-pressure, store
|
|
2634
|
+
* failure) must NOT be swallowed: the parent is already suspended on `child:<childId>` (the waiter
|
|
2635
|
+
* is put BEFORE start on every path), so a silent drop parks it in suspended-forever, invisibly
|
|
2636
|
+
* re-attempting on every recovery wake — a misconfigured remote child looks exactly like a healthy
|
|
2637
|
+
* long wait. Instead the failure is delivered to that waiter like a failed child (notifyParent):
|
|
2638
|
+
* the parent resumes and fails loudly with the cause. For a fire-and-forget `ctx.startChild`
|
|
2639
|
+
* (spawn — no waiter) the completion is buffered; a later join by the same id consumes it and
|
|
2640
|
+
* correctly observes the failed start.
|
|
2641
|
+
*/
|
|
2642
|
+
private startChildDeferred;
|
|
2621
2643
|
/**
|
|
2622
2644
|
* Cancel a run (e.g. from the dashboard). Returns null if the run does not exist. Pass
|
|
2623
2645
|
* `{ compensate: true }` to undo the saga first: the suspended run is resumed so its completed
|
package/dist/index.d.ts
CHANGED
|
@@ -1162,6 +1162,15 @@ interface Transport {
|
|
|
1162
1162
|
* transports that don't partition simply omit it.
|
|
1163
1163
|
*/
|
|
1164
1164
|
useNamespace?(namespace: string): void;
|
|
1165
|
+
/**
|
|
1166
|
+
* A SIBLING transport on the same broker/connection, pinned to `namespace` (explicit — never
|
|
1167
|
+
* re-scoped by a later {@link useNamespace}). Powers the tenant-worker bridge preset
|
|
1168
|
+
* (`topology: { role: 'control-plane', tenant, tenantWorkers: 'bridge' }`): a tenant-scoped
|
|
1169
|
+
* operator pairs its namespaced primary with `withNamespace('default')` — a bare-prefix sibling —
|
|
1170
|
+
* so operator-convention tenant workers (`<group>@<tenant>` under the bare prefix) are
|
|
1171
|
+
* discoverable and dispatchable. Optional — transports that don't partition simply omit it.
|
|
1172
|
+
*/
|
|
1173
|
+
withNamespace?(namespace: string): Transport;
|
|
1165
1174
|
/** Release the transport's resources (broker workers, queues, connections) for a clean shutdown.
|
|
1166
1175
|
* Optional — an in-process transport has nothing to close. Called on `onApplicationShutdown`
|
|
1167
1176
|
* after the engine drains, so a deploy hands off instead of leaving the broker to time out. */
|
|
@@ -2618,6 +2627,19 @@ declare class WorkflowEngine {
|
|
|
2618
2627
|
* knowing about the child feature.
|
|
2619
2628
|
*/
|
|
2620
2629
|
private notifyParent;
|
|
2630
|
+
/**
|
|
2631
|
+
* Deferred child start shared by the in-process ctx host and the remote `startChild` command.
|
|
2632
|
+
* Deferred (microtask) so a fast child can't reentrantly resume a still-suspending parent. A start
|
|
2633
|
+
* that THROWS (unregistered/unroutable workflow, input validation, singleton back-pressure, store
|
|
2634
|
+
* failure) must NOT be swallowed: the parent is already suspended on `child:<childId>` (the waiter
|
|
2635
|
+
* is put BEFORE start on every path), so a silent drop parks it in suspended-forever, invisibly
|
|
2636
|
+
* re-attempting on every recovery wake — a misconfigured remote child looks exactly like a healthy
|
|
2637
|
+
* long wait. Instead the failure is delivered to that waiter like a failed child (notifyParent):
|
|
2638
|
+
* the parent resumes and fails loudly with the cause. For a fire-and-forget `ctx.startChild`
|
|
2639
|
+
* (spawn — no waiter) the completion is buffered; a later join by the same id consumes it and
|
|
2640
|
+
* correctly observes the failed start.
|
|
2641
|
+
*/
|
|
2642
|
+
private startChildDeferred;
|
|
2621
2643
|
/**
|
|
2622
2644
|
* Cancel a run (e.g. from the dashboard). Returns null if the run does not exist. Pass
|
|
2623
2645
|
* `{ compensate: true }` to undo the saga first: the suspended run is resumed so its completed
|
package/dist/index.js
CHANGED
|
@@ -1099,6 +1099,21 @@ var TransportPool = class {
|
|
|
1099
1099
|
];
|
|
1100
1100
|
}
|
|
1101
1101
|
/**
|
|
1102
|
+
* The first transport (in pool order) reporting a LIVE heartbeat for `group` — the one a
|
|
1103
|
+
* convention-routed remote workflow must dispatch on. In a mixed pool (e.g. a tenant-scoped
|
|
1104
|
+
* control plane pairing its namespaced transport with a bare-prefix one for operator-convention
|
|
1105
|
+
* tenant workers), each transport sees a DIFFERENT keyspace: dispatching on `primary` while the
|
|
1106
|
+
* live worker heartbeats on another transport parks the task on a queue nobody consumes. Returns
|
|
1107
|
+
* `undefined` when no transport reports the group (mirrors the merged `listWorkerGroups` miss).
|
|
1108
|
+
*/
|
|
1109
|
+
async transportWithLiveGroup(group) {
|
|
1110
|
+
for (const { transport } of this.transports) {
|
|
1111
|
+
if (!transport.listWorkerGroups) continue;
|
|
1112
|
+
if ((await transport.listWorkerGroups()).includes(group)) return transport;
|
|
1113
|
+
}
|
|
1114
|
+
return void 0;
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1102
1117
|
* Propagate the engine's `namespace` to every transport that partitions by it — so the same
|
|
1103
1118
|
* namespace that scopes the store also scopes each transport's queues/keys. A transport that
|
|
1104
1119
|
* doesn't partition (no `useNamespace`) is skipped. Idempotent; a transport given an explicit
|
|
@@ -2414,10 +2429,10 @@ var WorkflowEngine = class {
|
|
|
2414
2429
|
* liveness changes between calls, exactly as {@link synthesizeRemoteChild} does.
|
|
2415
2430
|
*/
|
|
2416
2431
|
async resolveRemoteByConvention(run) {
|
|
2417
|
-
const liveGroups = await this.pool.listWorkerGroups();
|
|
2418
2432
|
const group = tenantGroup(sanitizeQueueToken(run.workflow), run.namespace);
|
|
2419
|
-
|
|
2420
|
-
|
|
2433
|
+
const liveTransport = await this.pool.transportWithLiveGroup(group);
|
|
2434
|
+
if (!liveTransport) return void 0;
|
|
2435
|
+
const executor = new RemoteWorkflowExecutor(liveTransport, run.workflow, run.namespace);
|
|
2421
2436
|
return {
|
|
2422
2437
|
name: run.workflow,
|
|
2423
2438
|
version: run.workflowVersion,
|
|
@@ -3036,6 +3051,27 @@ var WorkflowEngine = class {
|
|
|
3036
3051
|
void this.signal(`child:${runId}`, completion).catch(() => void 0);
|
|
3037
3052
|
}
|
|
3038
3053
|
/**
|
|
3054
|
+
* Deferred child start shared by the in-process ctx host and the remote `startChild` command.
|
|
3055
|
+
* Deferred (microtask) so a fast child can't reentrantly resume a still-suspending parent. A start
|
|
3056
|
+
* that THROWS (unregistered/unroutable workflow, input validation, singleton back-pressure, store
|
|
3057
|
+
* failure) must NOT be swallowed: the parent is already suspended on `child:<childId>` (the waiter
|
|
3058
|
+
* is put BEFORE start on every path), so a silent drop parks it in suspended-forever, invisibly
|
|
3059
|
+
* re-attempting on every recovery wake — a misconfigured remote child looks exactly like a healthy
|
|
3060
|
+
* long wait. Instead the failure is delivered to that waiter like a failed child (notifyParent):
|
|
3061
|
+
* the parent resumes and fails loudly with the cause. For a fire-and-forget `ctx.startChild`
|
|
3062
|
+
* (spawn — no waiter) the completion is buffered; a later join by the same id consumes it and
|
|
3063
|
+
* correctly observes the failed start.
|
|
3064
|
+
*/
|
|
3065
|
+
startChildDeferred(workflow, input, childId, opts) {
|
|
3066
|
+
queueMicrotask(() => void this.start(workflow, input, childId, opts).catch((err) => {
|
|
3067
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3068
|
+
this.notifyParent(childId, {
|
|
3069
|
+
ok: false,
|
|
3070
|
+
error: `child workflow "${workflow}" failed to start: ${message}`
|
|
3071
|
+
});
|
|
3072
|
+
}));
|
|
3073
|
+
}
|
|
3074
|
+
/**
|
|
3039
3075
|
* Cancel a run (e.g. from the dashboard). Returns null if the run does not exist. Pass
|
|
3040
3076
|
* `{ compensate: true }` to undo the saga first: the suspended run is resumed so its completed
|
|
3041
3077
|
* steps' compensations run in reverse (visible as `compensate:<step>` events), then it's marked
|
|
@@ -3859,9 +3895,9 @@ var WorkflowEngine = class {
|
|
|
3859
3895
|
parallelGroup: cmd.parallelGroup
|
|
3860
3896
|
});
|
|
3861
3897
|
if (!await this.store.getRun(childId)) {
|
|
3862
|
-
|
|
3898
|
+
this.startChildDeferred(cmd.workflow, cmd.input, childId, {
|
|
3863
3899
|
namespace: run.namespace
|
|
3864
|
-
})
|
|
3900
|
+
});
|
|
3865
3901
|
}
|
|
3866
3902
|
} else {
|
|
3867
3903
|
throw new Error(`remote workflow command '${cmd.kind}' is not supported yet`);
|
|
@@ -4240,10 +4276,10 @@ var WorkflowEngine = class {
|
|
|
4240
4276
|
// `davi-local` pipeline would stamp its `processing` child `default` and it would leak off the
|
|
4241
4277
|
// tenant's worker pool onto the shared/dev workers.
|
|
4242
4278
|
startChild: /* @__PURE__ */ __name((workflow, input, id, priority) => {
|
|
4243
|
-
|
|
4279
|
+
this.startChildDeferred(workflow, input, id, {
|
|
4244
4280
|
priority,
|
|
4245
4281
|
namespace: parentNamespace
|
|
4246
|
-
})
|
|
4282
|
+
});
|
|
4247
4283
|
}, "startChild"),
|
|
4248
4284
|
// Deferred for the same reentrancy reason as `startChild` above. `cancel()` is already
|
|
4249
4285
|
// idempotent on a terminal/cancelled run (returns its existing status without side effects), so
|