@dudousxd/nestjs-durable-core 0.38.0 → 0.40.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 +165 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +94 -9
- package/dist/index.d.ts +94 -9
- package/dist/index.js +165 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1512,6 +1512,14 @@ function isNewerVersion(a, b) {
|
|
|
1512
1512
|
}) > 0;
|
|
1513
1513
|
}
|
|
1514
1514
|
__name(isNewerVersion, "isNewerVersion");
|
|
1515
|
+
var NamespaceMismatch = class NamespaceMismatch2 extends Error {
|
|
1516
|
+
static {
|
|
1517
|
+
__name(this, "NamespaceMismatch");
|
|
1518
|
+
}
|
|
1519
|
+
constructor() {
|
|
1520
|
+
super("namespace-mismatch");
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
1515
1523
|
var WorkflowEngine = class {
|
|
1516
1524
|
static {
|
|
1517
1525
|
__name(this, "WorkflowEngine");
|
|
@@ -1522,6 +1530,7 @@ var WorkflowEngine = class {
|
|
|
1522
1530
|
controlPlane;
|
|
1523
1531
|
clock;
|
|
1524
1532
|
instanceId;
|
|
1533
|
+
namespace;
|
|
1525
1534
|
leaseMs;
|
|
1526
1535
|
maxRecoveryAttempts;
|
|
1527
1536
|
remoteAdvanceSilenceMs;
|
|
@@ -1588,6 +1597,7 @@ var WorkflowEngine = class {
|
|
|
1588
1597
|
this.admission = deps.admission ?? new InMemoryAdmissionBackend(this.clock);
|
|
1589
1598
|
this.admission.onFreed?.((queue) => this.wakeQueueWaiters(queue));
|
|
1590
1599
|
this.instanceId = deps.instanceId ?? globalThis.crypto.randomUUID();
|
|
1600
|
+
this.namespace = deps.namespace ?? "default";
|
|
1591
1601
|
this.leaseMs = deps.leaseMs ?? 3e4;
|
|
1592
1602
|
this.maxRecoveryAttempts = deps.maxRecoveryAttempts;
|
|
1593
1603
|
this.remoteAdvanceSilenceMs = deps.remoteAdvanceSilenceMs;
|
|
@@ -1698,8 +1708,21 @@ var WorkflowEngine = class {
|
|
|
1698
1708
|
* Register a workflow version. Register multiple versions of the same name to keep in-flight
|
|
1699
1709
|
* runs working across a breaking change: old runs resume on the version they started on, new
|
|
1700
1710
|
* runs start on the newest registered version.
|
|
1711
|
+
*
|
|
1712
|
+
* GROUP-SERVED (uniform dispatch, opt-in): pass `{ group, executor }` to have the engine DISPATCH
|
|
1713
|
+
* this workflow's turns to a worker `group` (via `executor.advance`) instead of running `fn`
|
|
1714
|
+
* inline — exactly as {@link registerRemote} does for a cross-SDK (e.g. Python) body, EXCEPT the
|
|
1715
|
+
* body is a TS `fn` the engine retains so an IN-APP worker on `group` can fetch it by name
|
|
1716
|
+
* ({@link workflowBody}) and run it. This is "one app, both roles": the same process owns the
|
|
1717
|
+
* engine AND consumes its own group. Recovery, timers, singleton, cancel and dead-lettering stay
|
|
1718
|
+
* engine concerns, identical to a remote run. OMIT `group`/`executor` (the default) to keep the
|
|
1719
|
+
* inline fast path — `fn` runs in-process and the run pays zero dispatch round-trips. The first
|
|
1720
|
+
* step toward routing every run through a group with no local/remote flag (Phase 3).
|
|
1701
1721
|
*/
|
|
1702
1722
|
register(name, version, fn, opts) {
|
|
1723
|
+
if (opts?.group == null !== (opts?.executor == null)) {
|
|
1724
|
+
throw new Error(`workflow ${name}@${version}: register({ group, executor }) needs BOTH group and executor (or neither)`);
|
|
1725
|
+
}
|
|
1703
1726
|
const registered = {
|
|
1704
1727
|
name,
|
|
1705
1728
|
version,
|
|
@@ -1709,8 +1732,17 @@ var WorkflowEngine = class {
|
|
|
1709
1732
|
executionTimeoutMs: opts?.executionTimeout != null ? parseDuration(opts.executionTimeout) : void 0,
|
|
1710
1733
|
validateInput: opts?.validateInput,
|
|
1711
1734
|
onEvent: opts?.onEvent,
|
|
1712
|
-
eventBatch: opts?.eventBatch
|
|
1735
|
+
eventBatch: opts?.eventBatch,
|
|
1736
|
+
// A real, retained in-process body — so `workflowBody` can hand it to an in-app worker, and so
|
|
1737
|
+
// it is NOT confused with the throwing stub `registerRemote` installs for a body-less remote.
|
|
1738
|
+
hasBody: true
|
|
1713
1739
|
};
|
|
1740
|
+
if (opts?.group != null && opts?.executor != null) {
|
|
1741
|
+
registered.remote = {
|
|
1742
|
+
group: opts.group,
|
|
1743
|
+
executor: opts.executor
|
|
1744
|
+
};
|
|
1745
|
+
}
|
|
1714
1746
|
this.workflows.set(versionKey(name, version), registered);
|
|
1715
1747
|
const current = this.latest.get(name);
|
|
1716
1748
|
if (!current || isNewerVersion(version, current.version)) this.latest.set(name, registered);
|
|
@@ -1721,6 +1753,17 @@ var WorkflowEngine = class {
|
|
|
1721
1753
|
}
|
|
1722
1754
|
}
|
|
1723
1755
|
/**
|
|
1756
|
+
* The retained in-process body for `name@version`, for an IN-APP worker that consumes this engine's
|
|
1757
|
+
* own group to SERVE a group-served `register(..., { group, executor })` workflow (uniform dispatch):
|
|
1758
|
+
* the worker fetches the body by name when it picks up the dispatched {@link WorkflowTask}, replays
|
|
1759
|
+
* it, and returns the {@link WorkflowDecision}. `undefined` for an unregistered name or a body-less
|
|
1760
|
+
* remote (a `registerRemote` / synthesized child carries only a throwing stub, never handed out).
|
|
1761
|
+
*/
|
|
1762
|
+
workflowBody(name, version) {
|
|
1763
|
+
const reg = this.workflows.get(versionKey(name, version));
|
|
1764
|
+
return reg?.hasBody ? reg.fn : void 0;
|
|
1765
|
+
}
|
|
1766
|
+
/**
|
|
1724
1767
|
* Register a workflow whose body runs in another SDK (e.g. Python). The engine owns the run exactly
|
|
1725
1768
|
* as for a TS workflow — it persists checkpoints, recovers, runs timers — but advances it by handing
|
|
1726
1769
|
* the run's history to `executor` (which dispatches a {@link WorkflowTask} to the worker) and applying
|
|
@@ -1846,7 +1889,11 @@ var WorkflowEngine = class {
|
|
|
1846
1889
|
}
|
|
1847
1890
|
async start(workflow, input, runId, opts) {
|
|
1848
1891
|
const name = workflowName(workflow);
|
|
1849
|
-
|
|
1892
|
+
let registered = this.latest.get(name);
|
|
1893
|
+
if (!registered) {
|
|
1894
|
+
const ancestor = await this.findRemoteAncestor(runId);
|
|
1895
|
+
if (ancestor) registered = this.synthesizeRemoteChild(name, ancestor.version, ancestor);
|
|
1896
|
+
}
|
|
1850
1897
|
if (!registered) throw new Error(`workflow ${name} is not registered`);
|
|
1851
1898
|
await registered.validateInput?.(input);
|
|
1852
1899
|
const prior = await this.store.getRun(runId);
|
|
@@ -1871,6 +1918,7 @@ var WorkflowEngine = class {
|
|
|
1871
1918
|
workflow: name,
|
|
1872
1919
|
workflowVersion: registered.version,
|
|
1873
1920
|
status: "pending",
|
|
1921
|
+
namespace: this.namespace,
|
|
1874
1922
|
input,
|
|
1875
1923
|
tags,
|
|
1876
1924
|
searchAttributes: opts?.searchAttributes,
|
|
@@ -1907,12 +1955,91 @@ var WorkflowEngine = class {
|
|
|
1907
1955
|
error: run.error
|
|
1908
1956
|
};
|
|
1909
1957
|
}
|
|
1910
|
-
|
|
1958
|
+
if (run.namespace !== void 0 && run.namespace !== this.namespace) {
|
|
1959
|
+
await this.store.releaseRunLock(runId);
|
|
1960
|
+
throw new NamespaceMismatch();
|
|
1961
|
+
}
|
|
1962
|
+
const registered = this.workflows.get(versionKey(run.workflow, run.workflowVersion)) ?? await this.findInheritedRegistration(run);
|
|
1911
1963
|
if (!registered) {
|
|
1912
1964
|
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)`);
|
|
1913
1965
|
}
|
|
1914
1966
|
return this.track(this.execute(run, registered.fn));
|
|
1915
1967
|
}
|
|
1968
|
+
/**
|
|
1969
|
+
* The registration for an UNREGISTERED run, INHERITED from its nearest REMOTE ancestor — or
|
|
1970
|
+
* `undefined` if it has none (the skew-protection error case in {@link resume}). Callers check the
|
|
1971
|
+
* flat registry FIRST (so an explicit `registerRemote(name, …)` ALWAYS wins, and a registered run
|
|
1972
|
+
* never reaches here); this resolves only the new case: a child spawned by a remote workflow (e.g. a
|
|
1973
|
+
* Python `gather_children` / `start_child` fan-out) of a name the host never `registerRemote`-ed. It
|
|
1974
|
+
* is driven as a remote run on the ancestor's group + the SAME executor instance, so the host needs
|
|
1975
|
+
* no redundant registration per child name.
|
|
1976
|
+
*
|
|
1977
|
+
* RECOMPUTED per resume rather than memoized into `this.workflows`: a synthesized child is not a real
|
|
1978
|
+
* registration and must not leak into `latest`, `knownGroups`, or `sweepTimeouts` (which iterate the
|
|
1979
|
+
* registry); recomputing also stays correct if the ancestor's remote registration is re-pointed at a
|
|
1980
|
+
* new executor across a deploy. Only unregistered runs pay it, so registered workflows pay nothing.
|
|
1981
|
+
*/
|
|
1982
|
+
async findInheritedRegistration(run) {
|
|
1983
|
+
const ancestor = await this.findRemoteAncestor(run.id);
|
|
1984
|
+
if (!ancestor) return void 0;
|
|
1985
|
+
return this.synthesizeRemoteChild(run.workflow, run.workflowVersion, ancestor);
|
|
1986
|
+
}
|
|
1987
|
+
/**
|
|
1988
|
+
* A throwaway {@link RegisteredWorkflow} that routes `name@version` as a remote run on the ancestor's
|
|
1989
|
+
* group via the ancestor's SAME executor instance. The executor is group-scoped (it dispatches a
|
|
1990
|
+
* {@link WorkflowTask} carrying `run.workflow`, and the worker picks the body by name), so reusing it
|
|
1991
|
+
* is correct AND required — a second executor on the same group would race the worker for results.
|
|
1992
|
+
* Only `remote` is inherited; the child gets none of the ancestor's singleton/timeout/validator, so
|
|
1993
|
+
* its identity stays its own.
|
|
1994
|
+
*/
|
|
1995
|
+
synthesizeRemoteChild(name, version, ancestor) {
|
|
1996
|
+
const remote = ancestor.remote;
|
|
1997
|
+
if (!remote) throw new Error(`workflow ${ancestor.name} is not a remote workflow`);
|
|
1998
|
+
return {
|
|
1999
|
+
name,
|
|
2000
|
+
version,
|
|
2001
|
+
fn: /* @__PURE__ */ __name(() => {
|
|
2002
|
+
throw new Error(`workflow ${name} is remote \u2014 it has no in-process body`);
|
|
2003
|
+
}, "fn"),
|
|
2004
|
+
remote
|
|
2005
|
+
};
|
|
2006
|
+
}
|
|
2007
|
+
/**
|
|
2008
|
+
* Walk the parent chain of `childRunId` to the nearest ancestor registered as REMOTE, returning its
|
|
2009
|
+
* registration (or `undefined` if there is none). The parent of an awaited child is found via its
|
|
2010
|
+
* live `child:<childId>` signal waiter — present for as long as the parent is suspended awaiting the
|
|
2011
|
+
* child, i.e. throughout the child's executable lifetime (start, every resume, crash recovery, cancel
|
|
2012
|
+
* cascade), which is exactly when we resolve. A registered-but-LOCAL parent stops the walk (an
|
|
2013
|
+
* unregistered child of a TS workflow is a genuine misconfiguration → skew error); only an
|
|
2014
|
+
* unregistered parent keeps walking, so a child of an inherited child still reaches the remote root.
|
|
2015
|
+
* `visited` guards against a pathological cyclic id graph.
|
|
2016
|
+
*/
|
|
2017
|
+
async findRemoteAncestor(childRunId, visited = /* @__PURE__ */ new Set()) {
|
|
2018
|
+
if (visited.has(childRunId)) return void 0;
|
|
2019
|
+
visited.add(childRunId);
|
|
2020
|
+
const parentRunId = await this.findParentRunId(childRunId);
|
|
2021
|
+
if (!parentRunId) return void 0;
|
|
2022
|
+
const parent = await this.store.getRun(parentRunId);
|
|
2023
|
+
if (!parent) return void 0;
|
|
2024
|
+
const parentReg = this.workflows.get(versionKey(parent.workflow, parent.workflowVersion));
|
|
2025
|
+
if (parentReg?.remote) return parentReg;
|
|
2026
|
+
if (parentReg) return void 0;
|
|
2027
|
+
return this.findRemoteAncestor(parent.id, visited);
|
|
2028
|
+
}
|
|
2029
|
+
/**
|
|
2030
|
+
* The run that spawned `childRunId`, via its live `child:<childRunId>` signal waiter — every awaited
|
|
2031
|
+
* child (`ctx.child`, `ctx.all`, a remote `start_child`) suspends its parent on exactly this waiter,
|
|
2032
|
+
* and the waiter is written BEFORE the child is started. Only reached for an UNREGISTERED child, so
|
|
2033
|
+
* registered runs never pay the scan; returns `undefined` once the parent has settled the child (the
|
|
2034
|
+
* waiter is consumed), but by then the child is terminal and no longer resolved.
|
|
2035
|
+
*/
|
|
2036
|
+
async findParentRunId(childRunId) {
|
|
2037
|
+
const token = `child:${childRunId}`;
|
|
2038
|
+
for (const waiter of await this.store.listSignalWaiters("child:")) {
|
|
2039
|
+
if (waiter.token === token) return waiter.runId;
|
|
2040
|
+
}
|
|
2041
|
+
return void 0;
|
|
2042
|
+
}
|
|
1916
2043
|
/** Track an in-flight execution so {@link drain} can wait for it. */
|
|
1917
2044
|
track(p) {
|
|
1918
2045
|
this.inflight.add(p);
|
|
@@ -1950,11 +2077,13 @@ var WorkflowEngine = class {
|
|
|
1950
2077
|
const inflight = [
|
|
1951
2078
|
...await this.store.listRuns({
|
|
1952
2079
|
workflow: reg.name,
|
|
1953
|
-
status: "running"
|
|
2080
|
+
status: "running",
|
|
2081
|
+
namespace: this.namespace
|
|
1954
2082
|
}),
|
|
1955
2083
|
...await this.store.listRuns({
|
|
1956
2084
|
workflow: reg.name,
|
|
1957
|
-
status: "suspended"
|
|
2085
|
+
status: "suspended",
|
|
2086
|
+
namespace: this.namespace
|
|
1958
2087
|
})
|
|
1959
2088
|
];
|
|
1960
2089
|
for (const run of inflight) {
|
|
@@ -1984,7 +2113,7 @@ var WorkflowEngine = class {
|
|
|
1984
2113
|
async recoverIncomplete(nowMs = this.clock()) {
|
|
1985
2114
|
if (this.draining) return [];
|
|
1986
2115
|
const results = [];
|
|
1987
|
-
for (const run of await this.store.listIncompleteRuns()) {
|
|
2116
|
+
for (const run of await this.store.listIncompleteRuns(this.namespace)) {
|
|
1988
2117
|
const acquired = await this.store.tryLockRun(run.id, this.instanceId, nowMs + this.leaseMs, nowMs);
|
|
1989
2118
|
if (!acquired) continue;
|
|
1990
2119
|
const settled = await this.countRecovery(run);
|
|
@@ -2054,7 +2183,7 @@ var WorkflowEngine = class {
|
|
|
2054
2183
|
* boot. A run still not due re-suspends cheaply without running new work.
|
|
2055
2184
|
*/
|
|
2056
2185
|
async resumeDueTimers(nowMs = this.clock()) {
|
|
2057
|
-
return this.resumeLeased(await this.store.listDueTimers(nowMs), nowMs);
|
|
2186
|
+
return this.resumeLeased(await this.store.listDueTimers(nowMs, this.namespace), nowMs);
|
|
2058
2187
|
}
|
|
2059
2188
|
/**
|
|
2060
2189
|
* Lease and execute one run by id — the worker side of dispatch. Acquires the recovery lease (so
|
|
@@ -2067,7 +2196,10 @@ var WorkflowEngine = class {
|
|
|
2067
2196
|
const nowMs = this.clock();
|
|
2068
2197
|
const acquired = await this.store.tryLockRun(runId, this.instanceId, nowMs + this.leaseMs, nowMs);
|
|
2069
2198
|
if (!acquired) return null;
|
|
2070
|
-
return this.resume(runId)
|
|
2199
|
+
return this.resume(runId).catch((err) => {
|
|
2200
|
+
if (err instanceof NamespaceMismatch) return null;
|
|
2201
|
+
throw err;
|
|
2202
|
+
});
|
|
2071
2203
|
}
|
|
2072
2204
|
/**
|
|
2073
2205
|
* Re-enqueue a run for a worker to (re-)execute — the dispatch-model **retry**. Sets it back to
|
|
@@ -2113,7 +2245,7 @@ var WorkflowEngine = class {
|
|
|
2113
2245
|
* {@link resumeDueTimers}.
|
|
2114
2246
|
*/
|
|
2115
2247
|
async runPending(nowMs = this.clock()) {
|
|
2116
|
-
return this.resumeLeased(await this.store.listPendingRuns(100), nowMs);
|
|
2248
|
+
return this.resumeLeased(await this.store.listPendingRuns(100, this.namespace), nowMs);
|
|
2117
2249
|
}
|
|
2118
2250
|
/**
|
|
2119
2251
|
* Resolve once `runId` reaches a settled state — terminal (completed/failed/cancelled/dead) or
|
|
@@ -2225,7 +2357,10 @@ var WorkflowEngine = class {
|
|
|
2225
2357
|
kind: "signal",
|
|
2226
2358
|
output: payload
|
|
2227
2359
|
}));
|
|
2228
|
-
return this.resume(waiter.runId)
|
|
2360
|
+
return this.resume(waiter.runId).catch((err) => {
|
|
2361
|
+
if (err instanceof NamespaceMismatch) return null;
|
|
2362
|
+
throw err;
|
|
2363
|
+
});
|
|
2229
2364
|
}
|
|
2230
2365
|
/**
|
|
2231
2366
|
* Ensure a run exists for `runId`, then deliver a signal to it — atomically race-free thanks to
|
|
@@ -2602,7 +2737,7 @@ var WorkflowEngine = class {
|
|
|
2602
2737
|
});
|
|
2603
2738
|
}
|
|
2604
2739
|
async execute(run, fn) {
|
|
2605
|
-
const registered = this.workflows.get(versionKey(run.workflow, run.workflowVersion));
|
|
2740
|
+
const registered = this.workflows.get(versionKey(run.workflow, run.workflowVersion)) ?? await this.findInheritedRegistration(run);
|
|
2606
2741
|
const lockNow = this.clock();
|
|
2607
2742
|
if (run.lockedBy !== this.instanceId || (run.lockedUntil ?? 0) <= lockNow) {
|
|
2608
2743
|
if (!await this.store.tryLockRun(run.id, this.instanceId, lockNow + this.leaseMs, lockNow)) {
|
|
@@ -3379,7 +3514,10 @@ var WorkflowEngine = class {
|
|
|
3379
3514
|
queueMs: startedAt.getTime() - cp.enqueuedAt.getTime(),
|
|
3380
3515
|
durationMs: finishedAt.getTime() - startedAt.getTime()
|
|
3381
3516
|
});
|
|
3382
|
-
await this.resume(result.runId)
|
|
3517
|
+
await this.resume(result.runId).catch((err) => {
|
|
3518
|
+
if (err instanceof NamespaceMismatch) return null;
|
|
3519
|
+
throw err;
|
|
3520
|
+
});
|
|
3383
3521
|
}
|
|
3384
3522
|
/**
|
|
3385
3523
|
* A slot freed on `queue` (a fleet-wide signal): resume this instance's runs blocked on it so they
|
|
@@ -3677,14 +3815,14 @@ var CodecStateStore = class {
|
|
|
3677
3815
|
saveCheckpoint: /* @__PURE__ */ __name((cp) => tx.saveCheckpoint(this.encCp(cp)), "saveCheckpoint")
|
|
3678
3816
|
}));
|
|
3679
3817
|
}
|
|
3680
|
-
async listIncompleteRuns() {
|
|
3681
|
-
return (await this.inner.listIncompleteRuns()).map((r) => this.decRun(r));
|
|
3818
|
+
async listIncompleteRuns(namespace) {
|
|
3819
|
+
return (await this.inner.listIncompleteRuns(namespace)).map((r) => this.decRun(r));
|
|
3682
3820
|
}
|
|
3683
|
-
async listPendingRuns(limit) {
|
|
3684
|
-
return (await this.inner.listPendingRuns(limit)).map((r) => this.decRun(r));
|
|
3821
|
+
async listPendingRuns(limit, namespace) {
|
|
3822
|
+
return (await this.inner.listPendingRuns(limit, namespace)).map((r) => this.decRun(r));
|
|
3685
3823
|
}
|
|
3686
|
-
async listDueTimers(nowMs) {
|
|
3687
|
-
return (await this.inner.listDueTimers(nowMs)).map((r) => this.decRun(r));
|
|
3824
|
+
async listDueTimers(nowMs, namespace) {
|
|
3825
|
+
return (await this.inner.listDueTimers(nowMs, namespace)).map((r) => this.decRun(r));
|
|
3688
3826
|
}
|
|
3689
3827
|
tryLockRun(runId, owner, leaseUntilMs, nowMs) {
|
|
3690
3828
|
return this.inner.tryLockRun(runId, owner, leaseUntilMs, nowMs);
|
|
@@ -4039,7 +4177,8 @@ var InMemoryStateStore = class {
|
|
|
4039
4177
|
}
|
|
4040
4178
|
async createRun(run) {
|
|
4041
4179
|
this.runs.set(run.id, {
|
|
4042
|
-
...run
|
|
4180
|
+
...run,
|
|
4181
|
+
namespace: run.namespace ?? "default"
|
|
4043
4182
|
});
|
|
4044
4183
|
this.reindexAttributes(run.id, run.searchAttributes);
|
|
4045
4184
|
}
|
|
@@ -4080,24 +4219,24 @@ var InMemoryStateStore = class {
|
|
|
4080
4219
|
...checkpoint
|
|
4081
4220
|
});
|
|
4082
4221
|
}
|
|
4083
|
-
async listIncompleteRuns() {
|
|
4222
|
+
async listIncompleteRuns(namespace) {
|
|
4084
4223
|
return [
|
|
4085
4224
|
...this.runs.values()
|
|
4086
|
-
].filter((r) => r.status === "running" || r.status === "cancelling").map((r) => ({
|
|
4225
|
+
].filter((r) => (r.status === "running" || r.status === "cancelling") && (namespace === void 0 || r.namespace === namespace)).map((r) => ({
|
|
4087
4226
|
...r
|
|
4088
4227
|
}));
|
|
4089
4228
|
}
|
|
4090
|
-
async listPendingRuns(limit) {
|
|
4229
|
+
async listPendingRuns(limit, namespace) {
|
|
4091
4230
|
return [
|
|
4092
4231
|
...this.runs.values()
|
|
4093
|
-
].filter((r) => r.status === "pending").sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime() || a.id.localeCompare(b.id)).slice(0, limit).map((r) => ({
|
|
4232
|
+
].filter((r) => r.status === "pending" && (namespace === void 0 || r.namespace === namespace)).sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime() || a.id.localeCompare(b.id)).slice(0, limit).map((r) => ({
|
|
4094
4233
|
...r
|
|
4095
4234
|
}));
|
|
4096
4235
|
}
|
|
4097
|
-
async listDueTimers(nowMs) {
|
|
4236
|
+
async listDueTimers(nowMs, namespace) {
|
|
4098
4237
|
return [
|
|
4099
4238
|
...this.runs.values()
|
|
4100
|
-
].filter((r) => r.status === "suspended" && r.wakeAt !== void 0 && r.wakeAt <= nowMs).map((r) => ({
|
|
4239
|
+
].filter((r) => r.status === "suspended" && r.wakeAt !== void 0 && r.wakeAt <= nowMs && (namespace === void 0 || r.namespace === namespace)).map((r) => ({
|
|
4101
4240
|
...r
|
|
4102
4241
|
}));
|
|
4103
4242
|
}
|
|
@@ -4212,6 +4351,7 @@ var InMemoryStateStore = class {
|
|
|
4212
4351
|
if (query.status) runs = runs.filter((r) => r.status === query.status);
|
|
4213
4352
|
if (query.statuses) runs = runs.filter((r) => query.statuses?.includes(r.status));
|
|
4214
4353
|
if (query.tag) runs = runs.filter((r) => r.tags?.includes(query.tag));
|
|
4354
|
+
if (query.namespace !== void 0) runs = runs.filter((r) => r.namespace === query.namespace);
|
|
4215
4355
|
if (query.attributes?.length) {
|
|
4216
4356
|
let candidates = null;
|
|
4217
4357
|
for (const f of query.attributes) {
|