@dudousxd/nestjs-durable-core 0.45.0 → 0.46.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 +203 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +366 -73
- package/dist/index.d.ts +366 -73
- package/dist/index.js +197 -58
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -574,7 +574,7 @@ var Entities = class {
|
|
|
574
574
|
let state = config.initialState();
|
|
575
575
|
for (let i = 0; ; i += 1) {
|
|
576
576
|
const msg = await ctx.waitForSignal(token);
|
|
577
|
-
const out = await ctx.
|
|
577
|
+
const out = await ctx.localStep(`op:${i}`, async () => {
|
|
578
578
|
const handler = config.handlers[msg.op];
|
|
579
579
|
if (!handler) throw new FatalError(`entity "${name}" has no handler for op "${msg.op}"`);
|
|
580
580
|
const result = await handler(state, msg.arg);
|
|
@@ -586,7 +586,7 @@ var Entities = class {
|
|
|
586
586
|
state = out.state;
|
|
587
587
|
await ctx.setEvent("state", state);
|
|
588
588
|
if (msg.reply) {
|
|
589
|
-
await ctx.
|
|
589
|
+
await ctx.localStep(`reply:${i}`, async () => {
|
|
590
590
|
await this.host.signal(msg.reply, out.result);
|
|
591
591
|
});
|
|
592
592
|
}
|
|
@@ -700,16 +700,28 @@ function eventMatches(payload, match) {
|
|
|
700
700
|
}
|
|
701
701
|
__name(eventMatches, "eventMatches");
|
|
702
702
|
|
|
703
|
+
// src/tenant-group.ts
|
|
704
|
+
function tenantGroup(baseGroup, tenant) {
|
|
705
|
+
return tenant !== void 0 && tenant !== "" && tenant !== "default" ? `${baseGroup}@${tenant}` : baseGroup;
|
|
706
|
+
}
|
|
707
|
+
__name(tenantGroup, "tenantGroup");
|
|
708
|
+
function sanitizeQueueToken(name) {
|
|
709
|
+
return name.replace(/:/g, "-");
|
|
710
|
+
}
|
|
711
|
+
__name(sanitizeQueueToken, "sanitizeQueueToken");
|
|
712
|
+
|
|
703
713
|
// src/remote-workflow-executor.ts
|
|
704
714
|
var RemoteWorkflowExecutor = class {
|
|
705
715
|
static {
|
|
706
716
|
__name(this, "RemoteWorkflowExecutor");
|
|
707
717
|
}
|
|
708
718
|
transport;
|
|
709
|
-
|
|
710
|
-
|
|
719
|
+
name;
|
|
720
|
+
partition;
|
|
721
|
+
constructor(transport, name, partition, _opts = {}) {
|
|
711
722
|
this.transport = transport;
|
|
712
|
-
this.
|
|
723
|
+
this.name = name;
|
|
724
|
+
this.partition = partition;
|
|
713
725
|
}
|
|
714
726
|
/**
|
|
715
727
|
* Enqueue one workflow turn under the engine-supplied `taskId`. Does NOT await the decision — the
|
|
@@ -729,7 +741,7 @@ var RemoteWorkflowExecutor = class {
|
|
|
729
741
|
...pendingSignals ? {
|
|
730
742
|
pendingSignals
|
|
731
743
|
} : {},
|
|
732
|
-
group: this.
|
|
744
|
+
group: tenantGroup(sanitizeQueueToken(this.name), this.partition),
|
|
733
745
|
priority: run.priority,
|
|
734
746
|
attempt: 1
|
|
735
747
|
};
|
|
@@ -901,6 +913,16 @@ var TransportPool = class {
|
|
|
901
913
|
liveWorkers: reports.flatMap((r) => r.liveWorkers)
|
|
902
914
|
};
|
|
903
915
|
}
|
|
916
|
+
/**
|
|
917
|
+
* Register a control-plane `start-run` consumer on every transport that carries the channel. Only
|
|
918
|
+
* control-plane-side transports implement {@link Transport.onStartRun}; a transport without it is
|
|
919
|
+
* skipped, so a plain worker/in-process pool is a no-op. Mirrors {@link bind}'s per-transport wiring.
|
|
920
|
+
*/
|
|
921
|
+
onStartRun(handler) {
|
|
922
|
+
for (const { transport } of this.transports) {
|
|
923
|
+
transport.onStartRun?.(handler);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
904
926
|
/** Distinct worker groups with a live heartbeat, merged across every transport that can report it. */
|
|
905
927
|
async listWorkerGroups() {
|
|
906
928
|
const groups = /* @__PURE__ */ new Set();
|
|
@@ -946,6 +968,26 @@ function unwrapCompletion(payload, label) {
|
|
|
946
968
|
}
|
|
947
969
|
__name(unwrapCompletion, "unwrapCompletion");
|
|
948
970
|
|
|
971
|
+
// src/step-name-symbol.ts
|
|
972
|
+
var DURABLE_STEP_NAME = Symbol.for("durable.step.name");
|
|
973
|
+
var DURABLE_STEP_CONFIG = Symbol.for("durable.step.config");
|
|
974
|
+
function isStepConfig(value) {
|
|
975
|
+
return typeof value === "object" && value !== null;
|
|
976
|
+
}
|
|
977
|
+
__name(isStepConfig, "isStepConfig");
|
|
978
|
+
function stepNameOf(ref) {
|
|
979
|
+
if (typeof ref !== "function") return void 0;
|
|
980
|
+
const stamped = ref[DURABLE_STEP_NAME];
|
|
981
|
+
return typeof stamped === "string" ? stamped : void 0;
|
|
982
|
+
}
|
|
983
|
+
__name(stepNameOf, "stepNameOf");
|
|
984
|
+
function stepConfigOf(ref) {
|
|
985
|
+
if (typeof ref !== "function") return void 0;
|
|
986
|
+
const stamped = ref[DURABLE_STEP_CONFIG];
|
|
987
|
+
return isStepConfig(stamped) ? stamped : void 0;
|
|
988
|
+
}
|
|
989
|
+
__name(stepConfigOf, "stepConfigOf");
|
|
990
|
+
|
|
949
991
|
// src/workflow-ref.ts
|
|
950
992
|
var WORKFLOW_NAME_KEY = Symbol.for("nestjs-durable:workflow-name");
|
|
951
993
|
function workflowName(ref) {
|
|
@@ -965,6 +1007,23 @@ function normalizeChildOptions(options) {
|
|
|
965
1007
|
} : options ?? {};
|
|
966
1008
|
}
|
|
967
1009
|
__name(normalizeChildOptions, "normalizeChildOptions");
|
|
1010
|
+
function resolveStepPolicy(config, opts) {
|
|
1011
|
+
const policy = {};
|
|
1012
|
+
const retries = opts?.retries ?? config?.retries;
|
|
1013
|
+
if (retries !== void 0) policy.retries = retries;
|
|
1014
|
+
const backoff = opts?.backoff ?? config?.backoff;
|
|
1015
|
+
if (backoff !== void 0) policy.backoff = backoff;
|
|
1016
|
+
const backoffMs = opts?.backoffMs ?? config?.backoffMs;
|
|
1017
|
+
if (backoffMs !== void 0) policy.backoffMs = backoffMs;
|
|
1018
|
+
const backoffMaxMs = opts?.backoffMaxMs ?? config?.backoffMaxMs;
|
|
1019
|
+
if (backoffMaxMs !== void 0) policy.backoffMaxMs = backoffMaxMs;
|
|
1020
|
+
const jitter = opts?.jitter ?? config?.jitter;
|
|
1021
|
+
if (jitter !== void 0) policy.jitter = jitter;
|
|
1022
|
+
const timeoutMs = opts?.timeoutMs ?? config?.timeoutMs;
|
|
1023
|
+
if (timeoutMs !== void 0) policy.timeoutMs = timeoutMs;
|
|
1024
|
+
return policy;
|
|
1025
|
+
}
|
|
1026
|
+
__name(resolveStepPolicy, "resolveStepPolicy");
|
|
968
1027
|
var Position = class Position2 {
|
|
969
1028
|
static {
|
|
970
1029
|
__name(this, "Position");
|
|
@@ -990,7 +1049,7 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
|
|
|
990
1049
|
await store.saveCheckpoint(cp);
|
|
991
1050
|
replay?.set(cp.seq, cp);
|
|
992
1051
|
}, "writeCheckpoint");
|
|
993
|
-
const
|
|
1052
|
+
const localStep = /* @__PURE__ */ __name(async (name, fn, options) => {
|
|
994
1053
|
const current = pos.next();
|
|
995
1054
|
const existing = await readCheckpoint(current);
|
|
996
1055
|
if (existing && existing.name !== name) {
|
|
@@ -1064,7 +1123,7 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
|
|
|
1064
1123
|
if (wait > 0) await new Promise((resolve) => setTimeout(resolve, wait));
|
|
1065
1124
|
}
|
|
1066
1125
|
}
|
|
1067
|
-
}, "
|
|
1126
|
+
}, "localStep");
|
|
1068
1127
|
const transaction = /* @__PURE__ */ __name(async (name, fn) => {
|
|
1069
1128
|
if (!store.transaction) {
|
|
1070
1129
|
throw new Error("ctx.transaction needs a store that supports transactions (the SQL adapters do). Use ctx.step for non-transactional work.");
|
|
@@ -1211,7 +1270,7 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
|
|
|
1211
1270
|
throw new WorkflowSuspended(deadline);
|
|
1212
1271
|
}, "waitForEvent");
|
|
1213
1272
|
const task = /* @__PURE__ */ __name(async (name, dispatch, options) => {
|
|
1214
|
-
await
|
|
1273
|
+
await localStep(`task:dispatch:${name}`, dispatch, options);
|
|
1215
1274
|
return unwrapCompletion(await waitForSignal(`task:${runId}:${name}`), `task "${name}"`);
|
|
1216
1275
|
}, "task");
|
|
1217
1276
|
const child = /* @__PURE__ */ __name(async (workflow2, input, options) => {
|
|
@@ -1424,9 +1483,8 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
|
|
|
1424
1483
|
wait
|
|
1425
1484
|
};
|
|
1426
1485
|
}, "webhook");
|
|
1427
|
-
const
|
|
1428
|
-
const
|
|
1429
|
-
const uuid = /* @__PURE__ */ __name(() => step("uuid", async () => globalThis.crypto.randomUUID()), "uuid");
|
|
1486
|
+
const sideEffect = /* @__PURE__ */ __name((fn) => localStep("sideEffect", async () => fn()), "sideEffect");
|
|
1487
|
+
const now = /* @__PURE__ */ __name(() => localStep("now", async () => host.clock()), "now");
|
|
1430
1488
|
const upsertSearchAttributes = /* @__PURE__ */ __name(async (attrs) => {
|
|
1431
1489
|
const current = pos.next();
|
|
1432
1490
|
const existing = await readCheckpoint(current);
|
|
@@ -1445,9 +1503,26 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
|
|
|
1445
1503
|
output: attrs
|
|
1446
1504
|
}));
|
|
1447
1505
|
}, "upsertSearchAttributes");
|
|
1506
|
+
function step(handlerOrName, input, opts) {
|
|
1507
|
+
const name = typeof handlerOrName === "string" ? handlerOrName : stepNameOf(handlerOrName);
|
|
1508
|
+
if (name === void 0) {
|
|
1509
|
+
throw new Error("ctx.step: handler is not a @Step reference (no stamped step name) \u2014 pass a @Step-decorated method or a step name string");
|
|
1510
|
+
}
|
|
1511
|
+
const config = typeof handlerOrName === "string" ? void 0 : stepConfigOf(handlerOrName);
|
|
1512
|
+
const def = {
|
|
1513
|
+
name,
|
|
1514
|
+
...resolveStepPolicy(config, opts)
|
|
1515
|
+
};
|
|
1516
|
+
return host.callRemote(runId, pos.next(), def, input, opts?.queue, opts?.transport, {
|
|
1517
|
+
priority: opts?.priority,
|
|
1518
|
+
fairnessKey: opts?.fairnessKey
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
__name(step, "step");
|
|
1448
1522
|
return {
|
|
1449
1523
|
runId,
|
|
1450
1524
|
step,
|
|
1525
|
+
localStep,
|
|
1451
1526
|
upsertSearchAttributes,
|
|
1452
1527
|
transaction,
|
|
1453
1528
|
callEntity,
|
|
@@ -1467,12 +1542,7 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
|
|
|
1467
1542
|
onUpdate,
|
|
1468
1543
|
patched,
|
|
1469
1544
|
now,
|
|
1470
|
-
|
|
1471
|
-
uuid,
|
|
1472
|
-
call: /* @__PURE__ */ __name((remote, input, opts) => host.callRemote(runId, pos.next(), remote, input, opts?.queue, opts?.transport, {
|
|
1473
|
-
priority: opts?.priority,
|
|
1474
|
-
fairnessKey: opts?.fairnessKey
|
|
1475
|
-
}), "call")
|
|
1545
|
+
sideEffect
|
|
1476
1546
|
};
|
|
1477
1547
|
}
|
|
1478
1548
|
__name(createWorkflowCtx, "createWorkflowCtx");
|
|
@@ -1590,8 +1660,10 @@ var WorkflowEngine = class {
|
|
|
1590
1660
|
this.admission = deps.admission ?? new InMemoryAdmissionBackend(this.clock);
|
|
1591
1661
|
this.admission.onFreed?.((queue) => this.wakeQueueWaiters(queue));
|
|
1592
1662
|
this.instanceId = deps.instanceId ?? globalThis.crypto.randomUUID();
|
|
1593
|
-
this.namespace = deps.namespace
|
|
1594
|
-
|
|
1663
|
+
this.namespace = deps.namespace;
|
|
1664
|
+
if (this.namespace !== void 0) {
|
|
1665
|
+
this.pool.useNamespace(this.namespace);
|
|
1666
|
+
}
|
|
1595
1667
|
this.leaseMs = deps.leaseMs ?? 3e4;
|
|
1596
1668
|
this.maxRecoveryAttempts = deps.maxRecoveryAttempts;
|
|
1597
1669
|
this.remoteAdvanceSilenceMs = deps.remoteAdvanceSilenceMs;
|
|
@@ -1656,6 +1728,13 @@ var WorkflowEngine = class {
|
|
|
1656
1728
|
await this.completeRemoteDecision(decision);
|
|
1657
1729
|
}
|
|
1658
1730
|
);
|
|
1731
|
+
this.pool.onStartRun(async (message) => {
|
|
1732
|
+
await this.start(message.workflow, message.input, message.runId ?? globalThis.crypto.randomUUID(), {
|
|
1733
|
+
namespace: message.tenant,
|
|
1734
|
+
tags: message.tags,
|
|
1735
|
+
searchAttributes: message.searchAttributes
|
|
1736
|
+
});
|
|
1737
|
+
});
|
|
1659
1738
|
this.controlPlane?.onControl((msg) => {
|
|
1660
1739
|
if (msg.from === this.instanceId) return;
|
|
1661
1740
|
if (msg.kind === "event") {
|
|
@@ -1820,7 +1899,7 @@ var WorkflowEngine = class {
|
|
|
1820
1899
|
});
|
|
1821
1900
|
}
|
|
1822
1901
|
/**
|
|
1823
|
-
* Register a flow-control queue referenced by `ctx.
|
|
1902
|
+
* Register a flow-control queue referenced by `ctx.step(step, input, { queue })`. Caps concurrent
|
|
1824
1903
|
* in-flight steps and/or the admission rate; blocked calls re-suspend and retry, so the limit is
|
|
1825
1904
|
* durable. Per engine instance (see {@link QueueConfig}). Registering the same name replaces it.
|
|
1826
1905
|
*/
|
|
@@ -1923,6 +2002,23 @@ var WorkflowEngine = class {
|
|
|
1923
2002
|
const ancestor = await this.findRemoteAncestor(runId);
|
|
1924
2003
|
if (ancestor) registered = this.synthesizeRemoteChild(name, ancestor.version, ancestor);
|
|
1925
2004
|
}
|
|
2005
|
+
if (!registered) {
|
|
2006
|
+
const conventionRun = {
|
|
2007
|
+
id: runId,
|
|
2008
|
+
workflow: name,
|
|
2009
|
+
workflowVersion: "1",
|
|
2010
|
+
status: "pending",
|
|
2011
|
+
// Stamp the namespace the REAL run will carry (:893) so convention routing resolves the same
|
|
2012
|
+
// tenant-suffixed group the run will dispatch to — an operator starting a tenant's run via the
|
|
2013
|
+
// `onStartRun` wire (opts.namespace = tenant) must find the live `<workflow>@<tenant>` group,
|
|
2014
|
+
// not the bare group. Without this the start-run entry path can't reach a suffixed-only worker.
|
|
2015
|
+
namespace: opts?.namespace ?? this.namespace,
|
|
2016
|
+
input,
|
|
2017
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
2018
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2019
|
+
};
|
|
2020
|
+
registered = await this.resolveRemoteByConvention(conventionRun);
|
|
2021
|
+
}
|
|
1926
2022
|
if (!registered) throw new Error(`workflow ${name} is not registered`);
|
|
1927
2023
|
await registered.validateInput?.(input);
|
|
1928
2024
|
const prior = await this.store.getRun(runId);
|
|
@@ -1947,7 +2043,7 @@ var WorkflowEngine = class {
|
|
|
1947
2043
|
workflow: name,
|
|
1948
2044
|
workflowVersion: registered.version,
|
|
1949
2045
|
status: "pending",
|
|
1950
|
-
namespace: this.namespace,
|
|
2046
|
+
namespace: opts?.namespace ?? this.namespace,
|
|
1951
2047
|
input,
|
|
1952
2048
|
tags,
|
|
1953
2049
|
searchAttributes: opts?.searchAttributes,
|
|
@@ -1984,11 +2080,11 @@ var WorkflowEngine = class {
|
|
|
1984
2080
|
error: run.error
|
|
1985
2081
|
};
|
|
1986
2082
|
}
|
|
1987
|
-
if (run.namespace !== void 0 && run.namespace !== this.namespace) {
|
|
2083
|
+
if (this.namespace !== void 0 && run.namespace !== void 0 && run.namespace !== this.namespace) {
|
|
1988
2084
|
await this.store.releaseRunLock(runId);
|
|
1989
2085
|
throw new NamespaceMismatch();
|
|
1990
2086
|
}
|
|
1991
|
-
const registered = this.workflows.get(versionKey(run.workflow, run.workflowVersion)) ?? await this.findInheritedRegistration(run);
|
|
2087
|
+
const registered = this.workflows.get(versionKey(run.workflow, run.workflowVersion)) ?? await this.findInheritedRegistration(run) ?? await this.resolveRemoteByConvention(run);
|
|
1992
2088
|
if (!registered) {
|
|
1993
2089
|
throw new Error(`workflow ${run.workflow}@${run.workflowVersion} is not registered \u2014 keep the prior version deployed so in-flight runs can drain (skew protection)`);
|
|
1994
2090
|
}
|
|
@@ -2034,6 +2130,34 @@ var WorkflowEngine = class {
|
|
|
2034
2130
|
};
|
|
2035
2131
|
}
|
|
2036
2132
|
/**
|
|
2133
|
+
* Attempt to route an unregistered workflow to the live worker group of the same name: if the
|
|
2134
|
+
* transport reports a live heartbeat for group `run.workflow`, synthesize a throwaway
|
|
2135
|
+
* {@link RegisteredWorkflow} that dispatches to that group over the primary transport. Returns
|
|
2136
|
+
* `undefined` when the group is not live. Always attempted — there is no opt-in flag; an explicit
|
|
2137
|
+
* `register`/`registerRemote` still takes precedence (this only runs when nothing is registered).
|
|
2138
|
+
*
|
|
2139
|
+
* Recomputed per call (never memoized into `this.workflows`) so it stays correct if group
|
|
2140
|
+
* liveness changes between calls, exactly as {@link synthesizeRemoteChild} does.
|
|
2141
|
+
*/
|
|
2142
|
+
async resolveRemoteByConvention(run) {
|
|
2143
|
+
const liveGroups = await this.pool.listWorkerGroups();
|
|
2144
|
+
const group = tenantGroup(sanitizeQueueToken(run.workflow), run.namespace);
|
|
2145
|
+
if (!liveGroups.includes(group)) return void 0;
|
|
2146
|
+
const executor = new RemoteWorkflowExecutor(this.pool.primary, run.workflow, run.namespace);
|
|
2147
|
+
return {
|
|
2148
|
+
name: run.workflow,
|
|
2149
|
+
version: run.workflowVersion,
|
|
2150
|
+
fn: /* @__PURE__ */ __name(() => {
|
|
2151
|
+
throw new Error(`remote workflow ${run.workflow} has no local body`);
|
|
2152
|
+
}, "fn"),
|
|
2153
|
+
hasBody: false,
|
|
2154
|
+
remote: {
|
|
2155
|
+
group,
|
|
2156
|
+
executor
|
|
2157
|
+
}
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
/**
|
|
2037
2161
|
* Walk the parent chain of `childRunId` to the nearest ancestor registered as REMOTE, returning its
|
|
2038
2162
|
* registration (or `undefined` if there is none). The parent of an awaited child is found via its
|
|
2039
2163
|
* live `child:<childId>` signal waiter — present for as long as the parent is suspended awaiting the
|
|
@@ -2130,6 +2254,7 @@ var WorkflowEngine = class {
|
|
|
2130
2254
|
type: "run.failed",
|
|
2131
2255
|
runId: run.id,
|
|
2132
2256
|
workflow: run.workflow,
|
|
2257
|
+
namespace: run.namespace,
|
|
2133
2258
|
error
|
|
2134
2259
|
});
|
|
2135
2260
|
}
|
|
@@ -2187,6 +2312,7 @@ var WorkflowEngine = class {
|
|
|
2187
2312
|
type: "run.failed",
|
|
2188
2313
|
runId: run.id,
|
|
2189
2314
|
workflow: run.workflow,
|
|
2315
|
+
namespace: run.namespace,
|
|
2190
2316
|
error
|
|
2191
2317
|
});
|
|
2192
2318
|
this.notifyDead({
|
|
@@ -2261,7 +2387,8 @@ var WorkflowEngine = class {
|
|
|
2261
2387
|
if (!run) return null;
|
|
2262
2388
|
const id = newRunId ?? `${runId}~retry~${globalThis.crypto.randomUUID().slice(0, 8)}`;
|
|
2263
2389
|
await this.start(run.workflow, input, id, {
|
|
2264
|
-
tags: run.tags
|
|
2390
|
+
tags: run.tags,
|
|
2391
|
+
namespace: run.namespace
|
|
2265
2392
|
});
|
|
2266
2393
|
return {
|
|
2267
2394
|
runId: id
|
|
@@ -2495,6 +2622,7 @@ var WorkflowEngine = class {
|
|
|
2495
2622
|
type: "run.failed",
|
|
2496
2623
|
runId,
|
|
2497
2624
|
workflow: run.workflow,
|
|
2625
|
+
namespace: run.namespace,
|
|
2498
2626
|
error
|
|
2499
2627
|
});
|
|
2500
2628
|
await this.cancelChildren(runId, opts);
|
|
@@ -2771,7 +2899,7 @@ var WorkflowEngine = class {
|
|
|
2771
2899
|
});
|
|
2772
2900
|
}
|
|
2773
2901
|
async execute(run, fn) {
|
|
2774
|
-
const registered = this.workflows.get(versionKey(run.workflow, run.workflowVersion)) ?? await this.findInheritedRegistration(run);
|
|
2902
|
+
const registered = this.workflows.get(versionKey(run.workflow, run.workflowVersion)) ?? await this.findInheritedRegistration(run) ?? await this.resolveRemoteByConvention(run);
|
|
2775
2903
|
const lockNow = this.clock();
|
|
2776
2904
|
if (run.lockedBy !== this.instanceId || (run.lockedUntil ?? 0) <= lockNow) {
|
|
2777
2905
|
if (!await this.store.tryLockRun(run.id, this.instanceId, lockNow + this.leaseMs, lockNow)) {
|
|
@@ -2821,6 +2949,7 @@ var WorkflowEngine = class {
|
|
|
2821
2949
|
type: "run.completed",
|
|
2822
2950
|
runId: run.id,
|
|
2823
2951
|
workflow: run.workflow,
|
|
2952
|
+
namespace: run.namespace,
|
|
2824
2953
|
output: outcome.output
|
|
2825
2954
|
});
|
|
2826
2955
|
void this.notifyParent(run.id, {
|
|
@@ -2843,6 +2972,7 @@ var WorkflowEngine = class {
|
|
|
2843
2972
|
type: "run.failed",
|
|
2844
2973
|
runId: run.id,
|
|
2845
2974
|
workflow: run.workflow,
|
|
2975
|
+
namespace: run.namespace,
|
|
2846
2976
|
error: outcome.error
|
|
2847
2977
|
});
|
|
2848
2978
|
void this.notifyParent(run.id, {
|
|
@@ -2863,7 +2993,8 @@ var WorkflowEngine = class {
|
|
|
2863
2993
|
this.emit({
|
|
2864
2994
|
type: "run.suspended",
|
|
2865
2995
|
runId: run.id,
|
|
2866
|
-
workflow: run.workflow
|
|
2996
|
+
workflow: run.workflow,
|
|
2997
|
+
namespace: run.namespace
|
|
2867
2998
|
});
|
|
2868
2999
|
return {
|
|
2869
3000
|
runId: run.id,
|
|
@@ -2884,6 +3015,7 @@ var WorkflowEngine = class {
|
|
|
2884
3015
|
type: "run.failed",
|
|
2885
3016
|
runId: run.id,
|
|
2886
3017
|
workflow: run.workflow,
|
|
3018
|
+
namespace: run.namespace,
|
|
2887
3019
|
error
|
|
2888
3020
|
});
|
|
2889
3021
|
this.notifyCancelled(run.id);
|
|
@@ -2911,7 +3043,8 @@ var WorkflowEngine = class {
|
|
|
2911
3043
|
this.emit({
|
|
2912
3044
|
type: "run.started",
|
|
2913
3045
|
runId: run.id,
|
|
2914
|
-
workflow: run.workflow
|
|
3046
|
+
workflow: run.workflow,
|
|
3047
|
+
namespace: run.namespace
|
|
2915
3048
|
});
|
|
2916
3049
|
}
|
|
2917
3050
|
if (registered.singleton && !await this.singletons.admit(run, registered.singleton)) {
|
|
@@ -2924,7 +3057,8 @@ var WorkflowEngine = class {
|
|
|
2924
3057
|
this.emit({
|
|
2925
3058
|
type: "run.suspended",
|
|
2926
3059
|
runId: run.id,
|
|
2927
|
-
workflow: run.workflow
|
|
3060
|
+
workflow: run.workflow,
|
|
3061
|
+
namespace: run.namespace
|
|
2928
3062
|
});
|
|
2929
3063
|
await this.store.releaseRunLock(run.id);
|
|
2930
3064
|
return {
|
|
@@ -2946,7 +3080,8 @@ var WorkflowEngine = class {
|
|
|
2946
3080
|
this.emit({
|
|
2947
3081
|
type: "run.suspended",
|
|
2948
3082
|
runId: run.id,
|
|
2949
|
-
workflow: run.workflow
|
|
3083
|
+
workflow: run.workflow,
|
|
3084
|
+
namespace: run.namespace
|
|
2950
3085
|
});
|
|
2951
3086
|
await this.store.releaseRunLock(run.id);
|
|
2952
3087
|
await remote.executor.dispatch(run, history, taskId);
|
|
@@ -2985,6 +3120,7 @@ var WorkflowEngine = class {
|
|
|
2985
3120
|
type: "run.failed",
|
|
2986
3121
|
runId: run.id,
|
|
2987
3122
|
workflow: run.workflow,
|
|
3123
|
+
namespace: run.namespace,
|
|
2988
3124
|
error
|
|
2989
3125
|
});
|
|
2990
3126
|
return {
|
|
@@ -3135,13 +3271,16 @@ var WorkflowEngine = class {
|
|
|
3135
3271
|
status: "pending",
|
|
3136
3272
|
input: cmd.input,
|
|
3137
3273
|
attempts: 1,
|
|
3138
|
-
|
|
3274
|
+
// `cmd.group` is the routing declaration the worker's replay emitted (cross-SDK, or a
|
|
3275
|
+
// group-served TS body) — treated as an isolation partition on top of the step's own
|
|
3276
|
+
// name, mirroring the StepDef formula below (identical expression everywhere).
|
|
3277
|
+
workerGroup: tenantGroup(sanitizeQueueToken(cmd.name), cmd.group),
|
|
3139
3278
|
enqueuedAt: at,
|
|
3140
3279
|
startedAt: at,
|
|
3141
3280
|
finishedAt: at,
|
|
3142
3281
|
// A `ctx.gather_calls([...])` fan-out stamps every dispatched `call` in the fan with the
|
|
3143
|
-
// same group, so the dashboard renders the
|
|
3144
|
-
// the gathered `recordStep`/`startChild` tags). Undefined for a lone sequential `ctx.
|
|
3282
|
+
// same group, so the dashboard renders the dispatched steps as one parallel fan (parity
|
|
3283
|
+
// with the gathered `recordStep`/`startChild` tags). Undefined for a lone sequential `ctx.step`.
|
|
3145
3284
|
parallelGroup: cmd.parallelGroup
|
|
3146
3285
|
}));
|
|
3147
3286
|
await this.pool.dispatch({
|
|
@@ -3149,7 +3288,7 @@ var WorkflowEngine = class {
|
|
|
3149
3288
|
seq: cmd.seq,
|
|
3150
3289
|
name: cmd.name,
|
|
3151
3290
|
stepId: id,
|
|
3152
|
-
group: cmd.group,
|
|
3291
|
+
group: tenantGroup(sanitizeQueueToken(cmd.name), cmd.group),
|
|
3153
3292
|
input: cmd.input,
|
|
3154
3293
|
traceparent: this.traceparent?.(),
|
|
3155
3294
|
context: this.context?.(),
|
|
@@ -3240,6 +3379,7 @@ var WorkflowEngine = class {
|
|
|
3240
3379
|
type: "run.failed",
|
|
3241
3380
|
runId: run.id,
|
|
3242
3381
|
workflow: run.workflow,
|
|
3382
|
+
namespace: run.namespace,
|
|
3243
3383
|
error
|
|
3244
3384
|
});
|
|
3245
3385
|
return {
|
|
@@ -3260,7 +3400,8 @@ var WorkflowEngine = class {
|
|
|
3260
3400
|
this.emit({
|
|
3261
3401
|
type: "run.started",
|
|
3262
3402
|
runId: run.id,
|
|
3263
|
-
workflow: run.workflow
|
|
3403
|
+
workflow: run.workflow,
|
|
3404
|
+
namespace: run.namespace
|
|
3264
3405
|
});
|
|
3265
3406
|
}
|
|
3266
3407
|
if (run.status !== "cancelling" && registered?.singleton && !await this.singletons.admit(run, registered.singleton)) {
|
|
@@ -3273,7 +3414,8 @@ var WorkflowEngine = class {
|
|
|
3273
3414
|
this.emit({
|
|
3274
3415
|
type: "run.suspended",
|
|
3275
3416
|
runId: run.id,
|
|
3276
|
-
workflow: run.workflow
|
|
3417
|
+
workflow: run.workflow,
|
|
3418
|
+
namespace: run.namespace
|
|
3277
3419
|
});
|
|
3278
3420
|
await this.store.releaseRunLock(run.id);
|
|
3279
3421
|
return {
|
|
@@ -3305,7 +3447,8 @@ var WorkflowEngine = class {
|
|
|
3305
3447
|
this.emit({
|
|
3306
3448
|
type: "run.completed",
|
|
3307
3449
|
runId: run.id,
|
|
3308
|
-
workflow: run.workflow
|
|
3450
|
+
workflow: run.workflow,
|
|
3451
|
+
namespace: run.namespace
|
|
3309
3452
|
});
|
|
3310
3453
|
void this.notifyParent(run.id, {
|
|
3311
3454
|
ok: true,
|
|
@@ -3524,7 +3667,6 @@ var WorkflowEngine = class {
|
|
|
3524
3667
|
this.queueWaiters.get(queue)?.delete(runId);
|
|
3525
3668
|
this.stepQueue.set(id, queue);
|
|
3526
3669
|
}
|
|
3527
|
-
const validInput = step.input.parse(input);
|
|
3528
3670
|
const enqueuedAt = /* @__PURE__ */ new Date();
|
|
3529
3671
|
await this.store.saveCheckpoint({
|
|
3530
3672
|
runId,
|
|
@@ -3533,9 +3675,9 @@ var WorkflowEngine = class {
|
|
|
3533
3675
|
kind: "remote",
|
|
3534
3676
|
stepId: id,
|
|
3535
3677
|
status: "pending",
|
|
3536
|
-
input
|
|
3678
|
+
input,
|
|
3537
3679
|
attempts: attempt,
|
|
3538
|
-
workerGroup: step.
|
|
3680
|
+
workerGroup: tenantGroup(sanitizeQueueToken(step.name), step.partition),
|
|
3539
3681
|
enqueuedAt,
|
|
3540
3682
|
startedAt: enqueuedAt,
|
|
3541
3683
|
finishedAt: enqueuedAt
|
|
@@ -3545,8 +3687,8 @@ var WorkflowEngine = class {
|
|
|
3545
3687
|
seq,
|
|
3546
3688
|
name: step.name,
|
|
3547
3689
|
stepId: id,
|
|
3548
|
-
group: step.
|
|
3549
|
-
input
|
|
3690
|
+
group: tenantGroup(sanitizeQueueToken(step.name), step.partition),
|
|
3691
|
+
input,
|
|
3550
3692
|
traceparent: this.traceparent?.(),
|
|
3551
3693
|
context: this.context?.(),
|
|
3552
3694
|
priority: admission?.priority,
|
|
@@ -3611,7 +3753,9 @@ var WorkflowEngine = class {
|
|
|
3611
3753
|
const run = await this.store.getRun(decision.runId);
|
|
3612
3754
|
if (!run || run.awaitingDecisionTaskId !== decision.taskId) return;
|
|
3613
3755
|
if (run.status === "cancelled" || run.status === "completed" || run.status === "dead") return;
|
|
3614
|
-
if (run.namespace !== void 0 && run.namespace !== this.namespace)
|
|
3756
|
+
if (this.namespace !== void 0 && run.namespace !== void 0 && run.namespace !== this.namespace) {
|
|
3757
|
+
return;
|
|
3758
|
+
}
|
|
3615
3759
|
const nowMs = this.clock();
|
|
3616
3760
|
if (!await this.store.tryLockRun(decision.runId, this.instanceId, nowMs + this.leaseMs, nowMs)) {
|
|
3617
3761
|
return;
|
|
@@ -3677,7 +3821,7 @@ var WorkflowEngine = class {
|
|
|
3677
3821
|
/** In-memory await path for a remote step with a liveness `timeoutMs` (re-dispatch on timeout). */
|
|
3678
3822
|
async callRemoteInMemory(runId, seq, step, input, transport) {
|
|
3679
3823
|
if (this.pool.size === 0) throw new Error("remote steps require a Transport");
|
|
3680
|
-
const validInput = step.input.parse(input);
|
|
3824
|
+
const validInput = step.input ? step.input.parse(input) : input;
|
|
3681
3825
|
const id = stepId(runId, seq);
|
|
3682
3826
|
const enqueuedAt = /* @__PURE__ */ new Date();
|
|
3683
3827
|
this.emit({
|
|
@@ -3700,7 +3844,7 @@ var WorkflowEngine = class {
|
|
|
3700
3844
|
seq,
|
|
3701
3845
|
name: step.name,
|
|
3702
3846
|
stepId: id,
|
|
3703
|
-
group: step.
|
|
3847
|
+
group: tenantGroup(sanitizeQueueToken(step.name), step.partition),
|
|
3704
3848
|
input: validInput,
|
|
3705
3849
|
traceparent: this.traceparent?.(),
|
|
3706
3850
|
context: this.context?.(),
|
|
@@ -3708,7 +3852,7 @@ var WorkflowEngine = class {
|
|
|
3708
3852
|
}, transport);
|
|
3709
3853
|
try {
|
|
3710
3854
|
const resolution = step.timeoutMs ? await this.awaitWithHeartbeat(id, resultPromise, step.timeoutMs) : await resultPromise;
|
|
3711
|
-
const output = step.output.parse(resolution.output);
|
|
3855
|
+
const output = step.output ? step.output.parse(resolution.output) : resolution.output;
|
|
3712
3856
|
const startedAt = resolution.startedAt ? new Date(resolution.startedAt) : enqueuedAt;
|
|
3713
3857
|
await this.completeStep({
|
|
3714
3858
|
runId,
|
|
@@ -3719,7 +3863,7 @@ var WorkflowEngine = class {
|
|
|
3719
3863
|
output,
|
|
3720
3864
|
events: resolution.events,
|
|
3721
3865
|
attempts: attempt,
|
|
3722
|
-
workerGroup: step.
|
|
3866
|
+
workerGroup: tenantGroup(sanitizeQueueToken(step.name), step.partition),
|
|
3723
3867
|
enqueuedAt,
|
|
3724
3868
|
startedAt
|
|
3725
3869
|
});
|
|
@@ -3795,16 +3939,6 @@ var TERMINAL_RUN_STATUSES = [
|
|
|
3795
3939
|
"dead"
|
|
3796
3940
|
];
|
|
3797
3941
|
|
|
3798
|
-
// src/remote-step-factory.ts
|
|
3799
|
-
function remoteStep(config) {
|
|
3800
|
-
return {
|
|
3801
|
-
...config,
|
|
3802
|
-
group: config.group ?? config.name.split(".")[0] ?? config.name,
|
|
3803
|
-
__remote: true
|
|
3804
|
-
};
|
|
3805
|
-
}
|
|
3806
|
-
__name(remoteStep, "remoteStep");
|
|
3807
|
-
|
|
3808
3942
|
// src/codec-state-store.ts
|
|
3809
3943
|
var CodecStateStore = class {
|
|
3810
3944
|
static {
|
|
@@ -4528,6 +4662,8 @@ export {
|
|
|
4528
4662
|
ContinueAsNew,
|
|
4529
4663
|
DURABLE_OPTIONS,
|
|
4530
4664
|
DURABLE_OPTIONS_CANONICAL,
|
|
4665
|
+
DURABLE_STEP_CONFIG,
|
|
4666
|
+
DURABLE_STEP_NAME,
|
|
4531
4667
|
Entities,
|
|
4532
4668
|
EventAccumulators,
|
|
4533
4669
|
FatalError,
|
|
@@ -4565,12 +4701,15 @@ export {
|
|
|
4565
4701
|
normalizeAttributeRows,
|
|
4566
4702
|
parseDuration,
|
|
4567
4703
|
prevCronFireMs,
|
|
4568
|
-
remoteStep,
|
|
4569
4704
|
runSchedules,
|
|
4570
4705
|
runStepHandler,
|
|
4706
|
+
sanitizeQueueToken,
|
|
4571
4707
|
scheduledRunId,
|
|
4572
4708
|
sqlComparator,
|
|
4709
|
+
stepConfigOf,
|
|
4573
4710
|
stepId,
|
|
4711
|
+
stepNameOf,
|
|
4712
|
+
tenantGroup,
|
|
4574
4713
|
workflowName
|
|
4575
4714
|
};
|
|
4576
4715
|
//# sourceMappingURL=index.js.map
|