@dudousxd/nestjs-durable-core 0.44.1 → 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 +214 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +373 -73
- package/dist/index.d.ts +373 -73
- package/dist/index.js +208 -57
- 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();
|
|
@@ -913,6 +935,17 @@ var TransportPool = class {
|
|
|
913
935
|
...groups
|
|
914
936
|
];
|
|
915
937
|
}
|
|
938
|
+
/**
|
|
939
|
+
* Propagate the engine's `namespace` to every transport that partitions by it — so the same
|
|
940
|
+
* namespace that scopes the store also scopes each transport's queues/keys. A transport that
|
|
941
|
+
* doesn't partition (no `useNamespace`) is skipped. Idempotent; a transport given an explicit
|
|
942
|
+
* namespace at construction ignores this (see {@link Transport.useNamespace}).
|
|
943
|
+
*/
|
|
944
|
+
useNamespace(namespace) {
|
|
945
|
+
for (const { transport } of this.transports) {
|
|
946
|
+
transport.useNamespace?.(namespace);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
916
949
|
/** Pinned `preferId` first, then the rest (failover order). */
|
|
917
950
|
ordered(preferId) {
|
|
918
951
|
if (!preferId) return this.transports;
|
|
@@ -935,6 +968,26 @@ function unwrapCompletion(payload, label) {
|
|
|
935
968
|
}
|
|
936
969
|
__name(unwrapCompletion, "unwrapCompletion");
|
|
937
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
|
+
|
|
938
991
|
// src/workflow-ref.ts
|
|
939
992
|
var WORKFLOW_NAME_KEY = Symbol.for("nestjs-durable:workflow-name");
|
|
940
993
|
function workflowName(ref) {
|
|
@@ -954,6 +1007,23 @@ function normalizeChildOptions(options) {
|
|
|
954
1007
|
} : options ?? {};
|
|
955
1008
|
}
|
|
956
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");
|
|
957
1027
|
var Position = class Position2 {
|
|
958
1028
|
static {
|
|
959
1029
|
__name(this, "Position");
|
|
@@ -979,7 +1049,7 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
|
|
|
979
1049
|
await store.saveCheckpoint(cp);
|
|
980
1050
|
replay?.set(cp.seq, cp);
|
|
981
1051
|
}, "writeCheckpoint");
|
|
982
|
-
const
|
|
1052
|
+
const localStep = /* @__PURE__ */ __name(async (name, fn, options) => {
|
|
983
1053
|
const current = pos.next();
|
|
984
1054
|
const existing = await readCheckpoint(current);
|
|
985
1055
|
if (existing && existing.name !== name) {
|
|
@@ -1053,7 +1123,7 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
|
|
|
1053
1123
|
if (wait > 0) await new Promise((resolve) => setTimeout(resolve, wait));
|
|
1054
1124
|
}
|
|
1055
1125
|
}
|
|
1056
|
-
}, "
|
|
1126
|
+
}, "localStep");
|
|
1057
1127
|
const transaction = /* @__PURE__ */ __name(async (name, fn) => {
|
|
1058
1128
|
if (!store.transaction) {
|
|
1059
1129
|
throw new Error("ctx.transaction needs a store that supports transactions (the SQL adapters do). Use ctx.step for non-transactional work.");
|
|
@@ -1200,7 +1270,7 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
|
|
|
1200
1270
|
throw new WorkflowSuspended(deadline);
|
|
1201
1271
|
}, "waitForEvent");
|
|
1202
1272
|
const task = /* @__PURE__ */ __name(async (name, dispatch, options) => {
|
|
1203
|
-
await
|
|
1273
|
+
await localStep(`task:dispatch:${name}`, dispatch, options);
|
|
1204
1274
|
return unwrapCompletion(await waitForSignal(`task:${runId}:${name}`), `task "${name}"`);
|
|
1205
1275
|
}, "task");
|
|
1206
1276
|
const child = /* @__PURE__ */ __name(async (workflow2, input, options) => {
|
|
@@ -1413,9 +1483,8 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
|
|
|
1413
1483
|
wait
|
|
1414
1484
|
};
|
|
1415
1485
|
}, "webhook");
|
|
1416
|
-
const
|
|
1417
|
-
const
|
|
1418
|
-
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");
|
|
1419
1488
|
const upsertSearchAttributes = /* @__PURE__ */ __name(async (attrs) => {
|
|
1420
1489
|
const current = pos.next();
|
|
1421
1490
|
const existing = await readCheckpoint(current);
|
|
@@ -1434,9 +1503,26 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
|
|
|
1434
1503
|
output: attrs
|
|
1435
1504
|
}));
|
|
1436
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");
|
|
1437
1522
|
return {
|
|
1438
1523
|
runId,
|
|
1439
1524
|
step,
|
|
1525
|
+
localStep,
|
|
1440
1526
|
upsertSearchAttributes,
|
|
1441
1527
|
transaction,
|
|
1442
1528
|
callEntity,
|
|
@@ -1456,12 +1542,7 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
|
|
|
1456
1542
|
onUpdate,
|
|
1457
1543
|
patched,
|
|
1458
1544
|
now,
|
|
1459
|
-
|
|
1460
|
-
uuid,
|
|
1461
|
-
call: /* @__PURE__ */ __name((remote, input, opts) => host.callRemote(runId, pos.next(), remote, input, opts?.queue, opts?.transport, {
|
|
1462
|
-
priority: opts?.priority,
|
|
1463
|
-
fairnessKey: opts?.fairnessKey
|
|
1464
|
-
}), "call")
|
|
1545
|
+
sideEffect
|
|
1465
1546
|
};
|
|
1466
1547
|
}
|
|
1467
1548
|
__name(createWorkflowCtx, "createWorkflowCtx");
|
|
@@ -1579,7 +1660,10 @@ var WorkflowEngine = class {
|
|
|
1579
1660
|
this.admission = deps.admission ?? new InMemoryAdmissionBackend(this.clock);
|
|
1580
1661
|
this.admission.onFreed?.((queue) => this.wakeQueueWaiters(queue));
|
|
1581
1662
|
this.instanceId = deps.instanceId ?? globalThis.crypto.randomUUID();
|
|
1582
|
-
this.namespace = deps.namespace
|
|
1663
|
+
this.namespace = deps.namespace;
|
|
1664
|
+
if (this.namespace !== void 0) {
|
|
1665
|
+
this.pool.useNamespace(this.namespace);
|
|
1666
|
+
}
|
|
1583
1667
|
this.leaseMs = deps.leaseMs ?? 3e4;
|
|
1584
1668
|
this.maxRecoveryAttempts = deps.maxRecoveryAttempts;
|
|
1585
1669
|
this.remoteAdvanceSilenceMs = deps.remoteAdvanceSilenceMs;
|
|
@@ -1644,6 +1728,13 @@ var WorkflowEngine = class {
|
|
|
1644
1728
|
await this.completeRemoteDecision(decision);
|
|
1645
1729
|
}
|
|
1646
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
|
+
});
|
|
1647
1738
|
this.controlPlane?.onControl((msg) => {
|
|
1648
1739
|
if (msg.from === this.instanceId) return;
|
|
1649
1740
|
if (msg.kind === "event") {
|
|
@@ -1808,7 +1899,7 @@ var WorkflowEngine = class {
|
|
|
1808
1899
|
});
|
|
1809
1900
|
}
|
|
1810
1901
|
/**
|
|
1811
|
-
* Register a flow-control queue referenced by `ctx.
|
|
1902
|
+
* Register a flow-control queue referenced by `ctx.step(step, input, { queue })`. Caps concurrent
|
|
1812
1903
|
* in-flight steps and/or the admission rate; blocked calls re-suspend and retry, so the limit is
|
|
1813
1904
|
* durable. Per engine instance (see {@link QueueConfig}). Registering the same name replaces it.
|
|
1814
1905
|
*/
|
|
@@ -1911,6 +2002,23 @@ var WorkflowEngine = class {
|
|
|
1911
2002
|
const ancestor = await this.findRemoteAncestor(runId);
|
|
1912
2003
|
if (ancestor) registered = this.synthesizeRemoteChild(name, ancestor.version, ancestor);
|
|
1913
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
|
+
}
|
|
1914
2022
|
if (!registered) throw new Error(`workflow ${name} is not registered`);
|
|
1915
2023
|
await registered.validateInput?.(input);
|
|
1916
2024
|
const prior = await this.store.getRun(runId);
|
|
@@ -1935,7 +2043,7 @@ var WorkflowEngine = class {
|
|
|
1935
2043
|
workflow: name,
|
|
1936
2044
|
workflowVersion: registered.version,
|
|
1937
2045
|
status: "pending",
|
|
1938
|
-
namespace: this.namespace,
|
|
2046
|
+
namespace: opts?.namespace ?? this.namespace,
|
|
1939
2047
|
input,
|
|
1940
2048
|
tags,
|
|
1941
2049
|
searchAttributes: opts?.searchAttributes,
|
|
@@ -1972,11 +2080,11 @@ var WorkflowEngine = class {
|
|
|
1972
2080
|
error: run.error
|
|
1973
2081
|
};
|
|
1974
2082
|
}
|
|
1975
|
-
if (run.namespace !== void 0 && run.namespace !== this.namespace) {
|
|
2083
|
+
if (this.namespace !== void 0 && run.namespace !== void 0 && run.namespace !== this.namespace) {
|
|
1976
2084
|
await this.store.releaseRunLock(runId);
|
|
1977
2085
|
throw new NamespaceMismatch();
|
|
1978
2086
|
}
|
|
1979
|
-
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);
|
|
1980
2088
|
if (!registered) {
|
|
1981
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)`);
|
|
1982
2090
|
}
|
|
@@ -2022,6 +2130,34 @@ var WorkflowEngine = class {
|
|
|
2022
2130
|
};
|
|
2023
2131
|
}
|
|
2024
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
|
+
/**
|
|
2025
2161
|
* Walk the parent chain of `childRunId` to the nearest ancestor registered as REMOTE, returning its
|
|
2026
2162
|
* registration (or `undefined` if there is none). The parent of an awaited child is found via its
|
|
2027
2163
|
* live `child:<childId>` signal waiter — present for as long as the parent is suspended awaiting the
|
|
@@ -2118,6 +2254,7 @@ var WorkflowEngine = class {
|
|
|
2118
2254
|
type: "run.failed",
|
|
2119
2255
|
runId: run.id,
|
|
2120
2256
|
workflow: run.workflow,
|
|
2257
|
+
namespace: run.namespace,
|
|
2121
2258
|
error
|
|
2122
2259
|
});
|
|
2123
2260
|
}
|
|
@@ -2175,6 +2312,7 @@ var WorkflowEngine = class {
|
|
|
2175
2312
|
type: "run.failed",
|
|
2176
2313
|
runId: run.id,
|
|
2177
2314
|
workflow: run.workflow,
|
|
2315
|
+
namespace: run.namespace,
|
|
2178
2316
|
error
|
|
2179
2317
|
});
|
|
2180
2318
|
this.notifyDead({
|
|
@@ -2249,7 +2387,8 @@ var WorkflowEngine = class {
|
|
|
2249
2387
|
if (!run) return null;
|
|
2250
2388
|
const id = newRunId ?? `${runId}~retry~${globalThis.crypto.randomUUID().slice(0, 8)}`;
|
|
2251
2389
|
await this.start(run.workflow, input, id, {
|
|
2252
|
-
tags: run.tags
|
|
2390
|
+
tags: run.tags,
|
|
2391
|
+
namespace: run.namespace
|
|
2253
2392
|
});
|
|
2254
2393
|
return {
|
|
2255
2394
|
runId: id
|
|
@@ -2483,6 +2622,7 @@ var WorkflowEngine = class {
|
|
|
2483
2622
|
type: "run.failed",
|
|
2484
2623
|
runId,
|
|
2485
2624
|
workflow: run.workflow,
|
|
2625
|
+
namespace: run.namespace,
|
|
2486
2626
|
error
|
|
2487
2627
|
});
|
|
2488
2628
|
await this.cancelChildren(runId, opts);
|
|
@@ -2759,7 +2899,7 @@ var WorkflowEngine = class {
|
|
|
2759
2899
|
});
|
|
2760
2900
|
}
|
|
2761
2901
|
async execute(run, fn) {
|
|
2762
|
-
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);
|
|
2763
2903
|
const lockNow = this.clock();
|
|
2764
2904
|
if (run.lockedBy !== this.instanceId || (run.lockedUntil ?? 0) <= lockNow) {
|
|
2765
2905
|
if (!await this.store.tryLockRun(run.id, this.instanceId, lockNow + this.leaseMs, lockNow)) {
|
|
@@ -2809,6 +2949,7 @@ var WorkflowEngine = class {
|
|
|
2809
2949
|
type: "run.completed",
|
|
2810
2950
|
runId: run.id,
|
|
2811
2951
|
workflow: run.workflow,
|
|
2952
|
+
namespace: run.namespace,
|
|
2812
2953
|
output: outcome.output
|
|
2813
2954
|
});
|
|
2814
2955
|
void this.notifyParent(run.id, {
|
|
@@ -2831,6 +2972,7 @@ var WorkflowEngine = class {
|
|
|
2831
2972
|
type: "run.failed",
|
|
2832
2973
|
runId: run.id,
|
|
2833
2974
|
workflow: run.workflow,
|
|
2975
|
+
namespace: run.namespace,
|
|
2834
2976
|
error: outcome.error
|
|
2835
2977
|
});
|
|
2836
2978
|
void this.notifyParent(run.id, {
|
|
@@ -2851,7 +2993,8 @@ var WorkflowEngine = class {
|
|
|
2851
2993
|
this.emit({
|
|
2852
2994
|
type: "run.suspended",
|
|
2853
2995
|
runId: run.id,
|
|
2854
|
-
workflow: run.workflow
|
|
2996
|
+
workflow: run.workflow,
|
|
2997
|
+
namespace: run.namespace
|
|
2855
2998
|
});
|
|
2856
2999
|
return {
|
|
2857
3000
|
runId: run.id,
|
|
@@ -2872,6 +3015,7 @@ var WorkflowEngine = class {
|
|
|
2872
3015
|
type: "run.failed",
|
|
2873
3016
|
runId: run.id,
|
|
2874
3017
|
workflow: run.workflow,
|
|
3018
|
+
namespace: run.namespace,
|
|
2875
3019
|
error
|
|
2876
3020
|
});
|
|
2877
3021
|
this.notifyCancelled(run.id);
|
|
@@ -2899,7 +3043,8 @@ var WorkflowEngine = class {
|
|
|
2899
3043
|
this.emit({
|
|
2900
3044
|
type: "run.started",
|
|
2901
3045
|
runId: run.id,
|
|
2902
|
-
workflow: run.workflow
|
|
3046
|
+
workflow: run.workflow,
|
|
3047
|
+
namespace: run.namespace
|
|
2903
3048
|
});
|
|
2904
3049
|
}
|
|
2905
3050
|
if (registered.singleton && !await this.singletons.admit(run, registered.singleton)) {
|
|
@@ -2912,7 +3057,8 @@ var WorkflowEngine = class {
|
|
|
2912
3057
|
this.emit({
|
|
2913
3058
|
type: "run.suspended",
|
|
2914
3059
|
runId: run.id,
|
|
2915
|
-
workflow: run.workflow
|
|
3060
|
+
workflow: run.workflow,
|
|
3061
|
+
namespace: run.namespace
|
|
2916
3062
|
});
|
|
2917
3063
|
await this.store.releaseRunLock(run.id);
|
|
2918
3064
|
return {
|
|
@@ -2934,7 +3080,8 @@ var WorkflowEngine = class {
|
|
|
2934
3080
|
this.emit({
|
|
2935
3081
|
type: "run.suspended",
|
|
2936
3082
|
runId: run.id,
|
|
2937
|
-
workflow: run.workflow
|
|
3083
|
+
workflow: run.workflow,
|
|
3084
|
+
namespace: run.namespace
|
|
2938
3085
|
});
|
|
2939
3086
|
await this.store.releaseRunLock(run.id);
|
|
2940
3087
|
await remote.executor.dispatch(run, history, taskId);
|
|
@@ -2973,6 +3120,7 @@ var WorkflowEngine = class {
|
|
|
2973
3120
|
type: "run.failed",
|
|
2974
3121
|
runId: run.id,
|
|
2975
3122
|
workflow: run.workflow,
|
|
3123
|
+
namespace: run.namespace,
|
|
2976
3124
|
error
|
|
2977
3125
|
});
|
|
2978
3126
|
return {
|
|
@@ -3123,13 +3271,16 @@ var WorkflowEngine = class {
|
|
|
3123
3271
|
status: "pending",
|
|
3124
3272
|
input: cmd.input,
|
|
3125
3273
|
attempts: 1,
|
|
3126
|
-
|
|
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),
|
|
3127
3278
|
enqueuedAt: at,
|
|
3128
3279
|
startedAt: at,
|
|
3129
3280
|
finishedAt: at,
|
|
3130
3281
|
// A `ctx.gather_calls([...])` fan-out stamps every dispatched `call` in the fan with the
|
|
3131
|
-
// same group, so the dashboard renders the
|
|
3132
|
-
// 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`.
|
|
3133
3284
|
parallelGroup: cmd.parallelGroup
|
|
3134
3285
|
}));
|
|
3135
3286
|
await this.pool.dispatch({
|
|
@@ -3137,7 +3288,7 @@ var WorkflowEngine = class {
|
|
|
3137
3288
|
seq: cmd.seq,
|
|
3138
3289
|
name: cmd.name,
|
|
3139
3290
|
stepId: id,
|
|
3140
|
-
group: cmd.group,
|
|
3291
|
+
group: tenantGroup(sanitizeQueueToken(cmd.name), cmd.group),
|
|
3141
3292
|
input: cmd.input,
|
|
3142
3293
|
traceparent: this.traceparent?.(),
|
|
3143
3294
|
context: this.context?.(),
|
|
@@ -3228,6 +3379,7 @@ var WorkflowEngine = class {
|
|
|
3228
3379
|
type: "run.failed",
|
|
3229
3380
|
runId: run.id,
|
|
3230
3381
|
workflow: run.workflow,
|
|
3382
|
+
namespace: run.namespace,
|
|
3231
3383
|
error
|
|
3232
3384
|
});
|
|
3233
3385
|
return {
|
|
@@ -3248,7 +3400,8 @@ var WorkflowEngine = class {
|
|
|
3248
3400
|
this.emit({
|
|
3249
3401
|
type: "run.started",
|
|
3250
3402
|
runId: run.id,
|
|
3251
|
-
workflow: run.workflow
|
|
3403
|
+
workflow: run.workflow,
|
|
3404
|
+
namespace: run.namespace
|
|
3252
3405
|
});
|
|
3253
3406
|
}
|
|
3254
3407
|
if (run.status !== "cancelling" && registered?.singleton && !await this.singletons.admit(run, registered.singleton)) {
|
|
@@ -3261,7 +3414,8 @@ var WorkflowEngine = class {
|
|
|
3261
3414
|
this.emit({
|
|
3262
3415
|
type: "run.suspended",
|
|
3263
3416
|
runId: run.id,
|
|
3264
|
-
workflow: run.workflow
|
|
3417
|
+
workflow: run.workflow,
|
|
3418
|
+
namespace: run.namespace
|
|
3265
3419
|
});
|
|
3266
3420
|
await this.store.releaseRunLock(run.id);
|
|
3267
3421
|
return {
|
|
@@ -3293,7 +3447,8 @@ var WorkflowEngine = class {
|
|
|
3293
3447
|
this.emit({
|
|
3294
3448
|
type: "run.completed",
|
|
3295
3449
|
runId: run.id,
|
|
3296
|
-
workflow: run.workflow
|
|
3450
|
+
workflow: run.workflow,
|
|
3451
|
+
namespace: run.namespace
|
|
3297
3452
|
});
|
|
3298
3453
|
void this.notifyParent(run.id, {
|
|
3299
3454
|
ok: true,
|
|
@@ -3512,7 +3667,6 @@ var WorkflowEngine = class {
|
|
|
3512
3667
|
this.queueWaiters.get(queue)?.delete(runId);
|
|
3513
3668
|
this.stepQueue.set(id, queue);
|
|
3514
3669
|
}
|
|
3515
|
-
const validInput = step.input.parse(input);
|
|
3516
3670
|
const enqueuedAt = /* @__PURE__ */ new Date();
|
|
3517
3671
|
await this.store.saveCheckpoint({
|
|
3518
3672
|
runId,
|
|
@@ -3521,9 +3675,9 @@ var WorkflowEngine = class {
|
|
|
3521
3675
|
kind: "remote",
|
|
3522
3676
|
stepId: id,
|
|
3523
3677
|
status: "pending",
|
|
3524
|
-
input
|
|
3678
|
+
input,
|
|
3525
3679
|
attempts: attempt,
|
|
3526
|
-
workerGroup: step.
|
|
3680
|
+
workerGroup: tenantGroup(sanitizeQueueToken(step.name), step.partition),
|
|
3527
3681
|
enqueuedAt,
|
|
3528
3682
|
startedAt: enqueuedAt,
|
|
3529
3683
|
finishedAt: enqueuedAt
|
|
@@ -3533,8 +3687,8 @@ var WorkflowEngine = class {
|
|
|
3533
3687
|
seq,
|
|
3534
3688
|
name: step.name,
|
|
3535
3689
|
stepId: id,
|
|
3536
|
-
group: step.
|
|
3537
|
-
input
|
|
3690
|
+
group: tenantGroup(sanitizeQueueToken(step.name), step.partition),
|
|
3691
|
+
input,
|
|
3538
3692
|
traceparent: this.traceparent?.(),
|
|
3539
3693
|
context: this.context?.(),
|
|
3540
3694
|
priority: admission?.priority,
|
|
@@ -3599,7 +3753,9 @@ var WorkflowEngine = class {
|
|
|
3599
3753
|
const run = await this.store.getRun(decision.runId);
|
|
3600
3754
|
if (!run || run.awaitingDecisionTaskId !== decision.taskId) return;
|
|
3601
3755
|
if (run.status === "cancelled" || run.status === "completed" || run.status === "dead") return;
|
|
3602
|
-
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
|
+
}
|
|
3603
3759
|
const nowMs = this.clock();
|
|
3604
3760
|
if (!await this.store.tryLockRun(decision.runId, this.instanceId, nowMs + this.leaseMs, nowMs)) {
|
|
3605
3761
|
return;
|
|
@@ -3665,7 +3821,7 @@ var WorkflowEngine = class {
|
|
|
3665
3821
|
/** In-memory await path for a remote step with a liveness `timeoutMs` (re-dispatch on timeout). */
|
|
3666
3822
|
async callRemoteInMemory(runId, seq, step, input, transport) {
|
|
3667
3823
|
if (this.pool.size === 0) throw new Error("remote steps require a Transport");
|
|
3668
|
-
const validInput = step.input.parse(input);
|
|
3824
|
+
const validInput = step.input ? step.input.parse(input) : input;
|
|
3669
3825
|
const id = stepId(runId, seq);
|
|
3670
3826
|
const enqueuedAt = /* @__PURE__ */ new Date();
|
|
3671
3827
|
this.emit({
|
|
@@ -3688,7 +3844,7 @@ var WorkflowEngine = class {
|
|
|
3688
3844
|
seq,
|
|
3689
3845
|
name: step.name,
|
|
3690
3846
|
stepId: id,
|
|
3691
|
-
group: step.
|
|
3847
|
+
group: tenantGroup(sanitizeQueueToken(step.name), step.partition),
|
|
3692
3848
|
input: validInput,
|
|
3693
3849
|
traceparent: this.traceparent?.(),
|
|
3694
3850
|
context: this.context?.(),
|
|
@@ -3696,7 +3852,7 @@ var WorkflowEngine = class {
|
|
|
3696
3852
|
}, transport);
|
|
3697
3853
|
try {
|
|
3698
3854
|
const resolution = step.timeoutMs ? await this.awaitWithHeartbeat(id, resultPromise, step.timeoutMs) : await resultPromise;
|
|
3699
|
-
const output = step.output.parse(resolution.output);
|
|
3855
|
+
const output = step.output ? step.output.parse(resolution.output) : resolution.output;
|
|
3700
3856
|
const startedAt = resolution.startedAt ? new Date(resolution.startedAt) : enqueuedAt;
|
|
3701
3857
|
await this.completeStep({
|
|
3702
3858
|
runId,
|
|
@@ -3707,7 +3863,7 @@ var WorkflowEngine = class {
|
|
|
3707
3863
|
output,
|
|
3708
3864
|
events: resolution.events,
|
|
3709
3865
|
attempts: attempt,
|
|
3710
|
-
workerGroup: step.
|
|
3866
|
+
workerGroup: tenantGroup(sanitizeQueueToken(step.name), step.partition),
|
|
3711
3867
|
enqueuedAt,
|
|
3712
3868
|
startedAt
|
|
3713
3869
|
});
|
|
@@ -3783,16 +3939,6 @@ var TERMINAL_RUN_STATUSES = [
|
|
|
3783
3939
|
"dead"
|
|
3784
3940
|
];
|
|
3785
3941
|
|
|
3786
|
-
// src/remote-step-factory.ts
|
|
3787
|
-
function remoteStep(config) {
|
|
3788
|
-
return {
|
|
3789
|
-
...config,
|
|
3790
|
-
group: config.group ?? config.name.split(".")[0] ?? config.name,
|
|
3791
|
-
__remote: true
|
|
3792
|
-
};
|
|
3793
|
-
}
|
|
3794
|
-
__name(remoteStep, "remoteStep");
|
|
3795
|
-
|
|
3796
3942
|
// src/codec-state-store.ts
|
|
3797
3943
|
var CodecStateStore = class {
|
|
3798
3944
|
static {
|
|
@@ -4516,6 +4662,8 @@ export {
|
|
|
4516
4662
|
ContinueAsNew,
|
|
4517
4663
|
DURABLE_OPTIONS,
|
|
4518
4664
|
DURABLE_OPTIONS_CANONICAL,
|
|
4665
|
+
DURABLE_STEP_CONFIG,
|
|
4666
|
+
DURABLE_STEP_NAME,
|
|
4519
4667
|
Entities,
|
|
4520
4668
|
EventAccumulators,
|
|
4521
4669
|
FatalError,
|
|
@@ -4553,12 +4701,15 @@ export {
|
|
|
4553
4701
|
normalizeAttributeRows,
|
|
4554
4702
|
parseDuration,
|
|
4555
4703
|
prevCronFireMs,
|
|
4556
|
-
remoteStep,
|
|
4557
4704
|
runSchedules,
|
|
4558
4705
|
runStepHandler,
|
|
4706
|
+
sanitizeQueueToken,
|
|
4559
4707
|
scheduledRunId,
|
|
4560
4708
|
sqlComparator,
|
|
4709
|
+
stepConfigOf,
|
|
4561
4710
|
stepId,
|
|
4711
|
+
stepNameOf,
|
|
4712
|
+
tenantGroup,
|
|
4562
4713
|
workflowName
|
|
4563
4714
|
};
|
|
4564
4715
|
//# sourceMappingURL=index.js.map
|