@dudousxd/nestjs-durable-core 0.71.1 → 0.72.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 +976 -171
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +514 -2
- package/dist/index.d.ts +514 -2
- package/dist/index.js +961 -171
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -25,7 +25,9 @@ __export(index_exports, {
|
|
|
25
25
|
CONTROL_FLOW_SIGNAL: () => CONTROL_FLOW_SIGNAL,
|
|
26
26
|
CURRENT_PROTOCOL_VERSION: () => CURRENT_PROTOCOL_VERSION,
|
|
27
27
|
CodecStateStore: () => CodecStateStore,
|
|
28
|
+
ConcurrencyLimitError: () => ConcurrencyLimitError,
|
|
28
29
|
ContinueAsNew: () => ContinueAsNew,
|
|
30
|
+
DEFAULT_CONCURRENCY_STATUSES: () => DEFAULT_CONCURRENCY_STATUSES,
|
|
29
31
|
DEFAULT_PARTITION: () => DEFAULT_PARTITION,
|
|
30
32
|
DURABLE_OPTIONS: () => DURABLE_OPTIONS,
|
|
31
33
|
DURABLE_OPTIONS_CANONICAL: () => DURABLE_OPTIONS_CANONICAL,
|
|
@@ -44,6 +46,7 @@ __export(index_exports, {
|
|
|
44
46
|
InMemoryTransport: () => InMemoryTransport,
|
|
45
47
|
LEGACY_V1_CAPABILITIES: () => LEGACY_V1_CAPABILITIES,
|
|
46
48
|
LEGACY_V1_PROTOCOL: () => LEGACY_V1_PROTOCOL,
|
|
49
|
+
NestedWorkflowCallError: () => NestedWorkflowCallError,
|
|
47
50
|
NonDeterminismError: () => NonDeterminismError,
|
|
48
51
|
QueueController: () => QueueController,
|
|
49
52
|
RUN_VALUE_FACET_LIMIT: () => RUN_VALUE_FACET_LIMIT,
|
|
@@ -55,6 +58,9 @@ __export(index_exports, {
|
|
|
55
58
|
RunGateway: () => RunGateway,
|
|
56
59
|
STATE_STORE: () => STATE_STORE,
|
|
57
60
|
STATE_STORE_CANONICAL: () => STATE_STORE_CANONICAL,
|
|
61
|
+
ScheduleAlreadyExistsError: () => ScheduleAlreadyExistsError,
|
|
62
|
+
ScheduleClient: () => ScheduleClient,
|
|
63
|
+
ScheduleNotFoundError: () => ScheduleNotFoundError,
|
|
58
64
|
SignalTimeoutError: () => SignalTimeoutError,
|
|
59
65
|
SingletonQueueFullError: () => SingletonQueueFullError,
|
|
60
66
|
TERMINAL_RUN_STATUSES: () => TERMINAL_RUN_STATUSES,
|
|
@@ -68,6 +74,7 @@ __export(index_exports, {
|
|
|
68
74
|
aggregateAnnouncements: () => aggregateAnnouncements,
|
|
69
75
|
announcementsOf: () => announcementsOf,
|
|
70
76
|
applyAttributeQuery: () => applyAttributeQuery,
|
|
77
|
+
assertConcurrency: () => assertConcurrency,
|
|
71
78
|
attributeColumnFor: () => attributeColumnFor,
|
|
72
79
|
attributeOperand: () => attributeOperand,
|
|
73
80
|
attributePredicateOperands: () => attributePredicateOperands,
|
|
@@ -80,10 +87,13 @@ __export(index_exports, {
|
|
|
80
87
|
classifyWaiterToken: () => classifyWaiterToken,
|
|
81
88
|
clearDurableExecutionWrappers: () => clearDurableExecutionWrappers,
|
|
82
89
|
collectMetrics: () => collectMetrics,
|
|
90
|
+
concurrencyTag: () => concurrencyTag,
|
|
83
91
|
controlPlaneDescriptor: () => controlPlaneDescriptor,
|
|
92
|
+
countRuns: () => countRuns,
|
|
84
93
|
coveredTokens: () => coveredTokens,
|
|
85
94
|
createStepLogger: () => createStepLogger,
|
|
86
95
|
currentStep: () => currentStep,
|
|
96
|
+
currentStepScope: () => currentStepScope,
|
|
87
97
|
currentWorkflowCtx: () => currentWorkflowCtx,
|
|
88
98
|
debug: () => debug,
|
|
89
99
|
describeWorker: () => describeWorker,
|
|
@@ -102,6 +112,7 @@ __export(index_exports, {
|
|
|
102
112
|
isEngineMintedTag: () => isEngineMintedTag,
|
|
103
113
|
isEventToken: () => isEventToken,
|
|
104
114
|
isLegacyDescriptor: () => isLegacyDescriptor,
|
|
115
|
+
isNonEmptyRunScope: () => isNonEmptyRunScope,
|
|
105
116
|
isWorkflowControlFlowSignal: () => isWorkflowControlFlowSignal,
|
|
106
117
|
log: () => log,
|
|
107
118
|
matchesAttributes: () => matchesAttributes,
|
|
@@ -109,6 +120,7 @@ __export(index_exports, {
|
|
|
109
120
|
mergeRunValueFacetRows: () => mergeRunValueFacetRows,
|
|
110
121
|
missingCapabilities: () => missingCapabilities,
|
|
111
122
|
negotiate: () => negotiate,
|
|
123
|
+
nextCronFireMs: () => nextCronFireMs,
|
|
112
124
|
normalizeAttributeRows: () => normalizeAttributeRows,
|
|
113
125
|
normalizeDescriptor: () => normalizeDescriptor,
|
|
114
126
|
observedAnnouncements: () => observedAnnouncements,
|
|
@@ -118,11 +130,14 @@ __export(index_exports, {
|
|
|
118
130
|
prevCronFireMs: () => prevCronFireMs,
|
|
119
131
|
readSearchAttributes: () => readSearchAttributes,
|
|
120
132
|
requiredCapabilities: () => requiredCapabilities,
|
|
133
|
+
resolveConcurrency: () => resolveConcurrency,
|
|
121
134
|
resolveRouting: () => resolveRouting,
|
|
122
135
|
resolveRunWaiting: () => resolveRunWaiting,
|
|
123
136
|
runDurableExecution: () => runDurableExecution,
|
|
124
137
|
runInStepLogger: () => runInStepLogger,
|
|
138
|
+
runInStepScope: () => runInStepScope,
|
|
125
139
|
runInWorkflowCtx: () => runInWorkflowCtx,
|
|
140
|
+
runOutsideWorkflowCtx: () => runOutsideWorkflowCtx,
|
|
126
141
|
runSchedules: () => runSchedules,
|
|
127
142
|
runStepHandler: () => runStepHandler,
|
|
128
143
|
runValueFacetsFromRuns: () => runValueFacetsFromRuns,
|
|
@@ -313,6 +328,27 @@ function currentWorkflowCtx() {
|
|
|
313
328
|
return storage.getStore();
|
|
314
329
|
}
|
|
315
330
|
__name(currentWorkflowCtx, "currentWorkflowCtx");
|
|
331
|
+
var STEP_SCOPE_KEY = Symbol.for("nestjs-durable:ambient-step-scope");
|
|
332
|
+
var stepScopeRef = globalThis;
|
|
333
|
+
if (!stepScopeRef[STEP_SCOPE_KEY]) {
|
|
334
|
+
stepScopeRef[STEP_SCOPE_KEY] = new import_node_async_hooks.AsyncLocalStorage();
|
|
335
|
+
}
|
|
336
|
+
var stepScopeStorage = stepScopeRef[STEP_SCOPE_KEY];
|
|
337
|
+
function runInStepScope(owner, step, fn) {
|
|
338
|
+
return stepScopeStorage.run({
|
|
339
|
+
owner,
|
|
340
|
+
step
|
|
341
|
+
}, fn);
|
|
342
|
+
}
|
|
343
|
+
__name(runInStepScope, "runInStepScope");
|
|
344
|
+
function currentStepScope() {
|
|
345
|
+
return stepScopeStorage.getStore();
|
|
346
|
+
}
|
|
347
|
+
__name(currentStepScope, "currentStepScope");
|
|
348
|
+
function runOutsideWorkflowCtx(fn) {
|
|
349
|
+
return storage.exit(() => stepScopeStorage.exit(fn));
|
|
350
|
+
}
|
|
351
|
+
__name(runOutsideWorkflowCtx, "runOutsideWorkflowCtx");
|
|
316
352
|
|
|
317
353
|
// src/ambient-step.ts
|
|
318
354
|
var import_node_async_hooks2 = require("async_hooks");
|
|
@@ -391,6 +427,23 @@ var FatalError = class extends Error {
|
|
|
391
427
|
this.code = code;
|
|
392
428
|
}
|
|
393
429
|
};
|
|
430
|
+
var ConcurrencyLimitError = class extends Error {
|
|
431
|
+
static {
|
|
432
|
+
__name(this, "ConcurrencyLimitError");
|
|
433
|
+
}
|
|
434
|
+
workflow;
|
|
435
|
+
key;
|
|
436
|
+
limit;
|
|
437
|
+
active;
|
|
438
|
+
constructor(workflow, key, limit, active) {
|
|
439
|
+
super(`concurrency limit reached for key "${key}" (${active}/${limit} in flight); ${workflow} was not started \u2014 retry later`);
|
|
440
|
+
this.name = "ConcurrencyLimitError";
|
|
441
|
+
this.workflow = workflow;
|
|
442
|
+
this.key = key;
|
|
443
|
+
this.limit = limit;
|
|
444
|
+
this.active = active;
|
|
445
|
+
}
|
|
446
|
+
};
|
|
394
447
|
var SingletonQueueFullError = class extends Error {
|
|
395
448
|
static {
|
|
396
449
|
__name(this, "SingletonQueueFullError");
|
|
@@ -458,6 +511,21 @@ var NonDeterminismError = class extends Error {
|
|
|
458
511
|
this.seq = seq;
|
|
459
512
|
}
|
|
460
513
|
};
|
|
514
|
+
var NestedWorkflowCallError = class extends FatalError {
|
|
515
|
+
static {
|
|
516
|
+
__name(this, "NestedWorkflowCallError");
|
|
517
|
+
}
|
|
518
|
+
runId;
|
|
519
|
+
primitive;
|
|
520
|
+
step;
|
|
521
|
+
constructor(runId, primitive, step) {
|
|
522
|
+
super(`ctx.${primitive}() was called inside the body of step "${step}" (run ${runId}). Workflow primitives (child/startChild/step/sleep/signals/sideEffect/\u2026, and MyWorkflow.start()/.execute() via the ambient ctx) must be called from the workflow body, never from inside a step body: the step's checkpoint replays without re-running it, so the nested call would shift every later journal position and corrupt the run on replay. Move the call out of the step.`, "nested_workflow_call");
|
|
523
|
+
this.name = "NestedWorkflowCallError";
|
|
524
|
+
this.runId = runId;
|
|
525
|
+
this.primitive = primitive;
|
|
526
|
+
this.step = step;
|
|
527
|
+
}
|
|
528
|
+
};
|
|
461
529
|
var GatherError = class extends Error {
|
|
462
530
|
static {
|
|
463
531
|
__name(this, "GatherError");
|
|
@@ -814,14 +882,19 @@ async function runStepHandler(task, handler) {
|
|
|
814
882
|
} : result, "withEvents");
|
|
815
883
|
try {
|
|
816
884
|
const logger = createStepLogger(events, Date.now);
|
|
817
|
-
const output = await runDurableExecution(
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
885
|
+
const output = await runDurableExecution(
|
|
886
|
+
{
|
|
887
|
+
unit: "step",
|
|
888
|
+
runId: task.runId,
|
|
889
|
+
workflow: void 0,
|
|
890
|
+
name: task.name,
|
|
891
|
+
seq: task.seq,
|
|
892
|
+
attempt: task.attempt
|
|
893
|
+
},
|
|
894
|
+
// Outside any ambient workflow ctx: an in-process transport runs the handler on the parent
|
|
895
|
+
// body's async path, but a handler is not the body (see `runOutsideWorkflowCtx`).
|
|
896
|
+
async () => runOutsideWorkflowCtx(() => runInStepLogger(logger, () => handler(task.input, logger)))
|
|
897
|
+
);
|
|
825
898
|
return withEvents({
|
|
826
899
|
...base,
|
|
827
900
|
status: "completed",
|
|
@@ -904,6 +977,58 @@ function instantCheckpoint(p) {
|
|
|
904
977
|
}
|
|
905
978
|
__name(instantCheckpoint, "instantCheckpoint");
|
|
906
979
|
|
|
980
|
+
// src/concurrency.ts
|
|
981
|
+
var DEFAULT_CONCURRENCY_STATUSES = [
|
|
982
|
+
"pending",
|
|
983
|
+
"running",
|
|
984
|
+
"suspended",
|
|
985
|
+
"cancelling",
|
|
986
|
+
"blocked"
|
|
987
|
+
];
|
|
988
|
+
function concurrencyTag(key) {
|
|
989
|
+
return `concurrency:${key}`;
|
|
990
|
+
}
|
|
991
|
+
__name(concurrencyTag, "concurrencyTag");
|
|
992
|
+
async function resolveConcurrency(override, config, input) {
|
|
993
|
+
if (override) return override;
|
|
994
|
+
if (!config) return void 0;
|
|
995
|
+
const key = config.key(input);
|
|
996
|
+
if (key === void 0) return void 0;
|
|
997
|
+
const limit = typeof config.limit === "function" ? await config.limit(key) : config.limit;
|
|
998
|
+
return {
|
|
999
|
+
key,
|
|
1000
|
+
limit,
|
|
1001
|
+
countStatuses: config.countStatuses
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
__name(resolveConcurrency, "resolveConcurrency");
|
|
1005
|
+
async function countRuns(store, query) {
|
|
1006
|
+
if (store.countRuns) return store.countRuns(query);
|
|
1007
|
+
if (store.runFacets && !query.status && !query.statuses && query.origin === void 0) {
|
|
1008
|
+
const cells = await store.runFacets(query);
|
|
1009
|
+
return cells.reduce((sum, c) => sum + c.count, 0);
|
|
1010
|
+
}
|
|
1011
|
+
if (store.runFacets && query.statuses && !query.status && query.origin === void 0) {
|
|
1012
|
+
const { statuses, ...rest } = query;
|
|
1013
|
+
const cells = await store.runFacets(rest);
|
|
1014
|
+
return cells.filter((c) => statuses.includes(c.status)).reduce((sum, c) => sum + c.count, 0);
|
|
1015
|
+
}
|
|
1016
|
+
return (await store.listRuns(query)).length;
|
|
1017
|
+
}
|
|
1018
|
+
__name(countRuns, "countRuns");
|
|
1019
|
+
async function assertConcurrency(store, workflow, quota) {
|
|
1020
|
+
const active = quota.limit <= 0 ? 0 : await countRuns(store, {
|
|
1021
|
+
tag: concurrencyTag(quota.key),
|
|
1022
|
+
statuses: [
|
|
1023
|
+
...quota.countStatuses ?? DEFAULT_CONCURRENCY_STATUSES
|
|
1024
|
+
]
|
|
1025
|
+
});
|
|
1026
|
+
if (quota.limit <= 0 || active >= quota.limit) {
|
|
1027
|
+
throw new ConcurrencyLimitError(workflow, quota.key, quota.limit, active);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
__name(assertConcurrency, "assertConcurrency");
|
|
1031
|
+
|
|
907
1032
|
// src/entities.ts
|
|
908
1033
|
var ENTITY_WORKFLOW = "__entity";
|
|
909
1034
|
var entityRunId = /* @__PURE__ */ __name((name, key) => `entity:${name}:${key}`, "entityRunId");
|
|
@@ -1736,6 +1861,19 @@ function planDispatch(requires, liveDescriptors, cp, token) {
|
|
|
1736
1861
|
}
|
|
1737
1862
|
__name(planDispatch, "planDispatch");
|
|
1738
1863
|
|
|
1864
|
+
// src/interfaces.ts
|
|
1865
|
+
var TERMINAL_RUN_STATUSES = [
|
|
1866
|
+
"completed",
|
|
1867
|
+
"failed",
|
|
1868
|
+
"cancelled",
|
|
1869
|
+
"dead"
|
|
1870
|
+
];
|
|
1871
|
+
function isNonEmptyRunScope(scope) {
|
|
1872
|
+
if (!scope) return false;
|
|
1873
|
+
return scope.workflow !== void 0 || scope.workflows !== void 0 || scope.tag !== void 0 || scope.tags !== void 0 || scope.namespace !== void 0 || scope.namespaces !== void 0 || scope.attributes !== void 0 && scope.attributes.length > 0;
|
|
1874
|
+
}
|
|
1875
|
+
__name(isNonEmptyRunScope, "isNonEmptyRunScope");
|
|
1876
|
+
|
|
1739
1877
|
// src/tenant-group.ts
|
|
1740
1878
|
function tenantGroup(baseGroup, tenant) {
|
|
1741
1879
|
return tenant !== void 0 && tenant !== "" && tenant !== "default" ? `${baseGroup}@${tenant}` : baseGroup;
|
|
@@ -1826,6 +1964,486 @@ function indexWaitersByRun(waiters) {
|
|
|
1826
1964
|
}
|
|
1827
1965
|
__name(indexWaitersByRun, "indexWaitersByRun");
|
|
1828
1966
|
|
|
1967
|
+
// src/scheduler.ts
|
|
1968
|
+
var import_node_module = require("module");
|
|
1969
|
+
|
|
1970
|
+
// src/cron-compat.ts
|
|
1971
|
+
function isIndexable(value) {
|
|
1972
|
+
return typeof value === "object" && value !== null || typeof value === "function";
|
|
1973
|
+
}
|
|
1974
|
+
__name(isIndexable, "isIndexable");
|
|
1975
|
+
function unwrapDefault(module_) {
|
|
1976
|
+
if (!isIndexable(module_)) return module_;
|
|
1977
|
+
if (Reflect.get(module_, "parseExpression") !== void 0) return module_;
|
|
1978
|
+
if (Reflect.get(module_, "CronExpressionParser") !== void 0) return module_;
|
|
1979
|
+
const inner = Reflect.get(module_, "default");
|
|
1980
|
+
return inner === void 0 ? module_ : inner;
|
|
1981
|
+
}
|
|
1982
|
+
__name(unwrapDefault, "unwrapDefault");
|
|
1983
|
+
function v4Entry(module_) {
|
|
1984
|
+
if (!isIndexable(module_)) return void 0;
|
|
1985
|
+
const parse = Reflect.get(module_, "parseExpression");
|
|
1986
|
+
if (typeof parse !== "function") return void 0;
|
|
1987
|
+
return (expr, options) => parse.call(module_, expr, options);
|
|
1988
|
+
}
|
|
1989
|
+
__name(v4Entry, "v4Entry");
|
|
1990
|
+
function v5Entry(module_) {
|
|
1991
|
+
if (!isIndexable(module_)) return void 0;
|
|
1992
|
+
const parser = Reflect.get(module_, "CronExpressionParser");
|
|
1993
|
+
if (!isIndexable(parser)) return void 0;
|
|
1994
|
+
const parse = Reflect.get(parser, "parse");
|
|
1995
|
+
if (typeof parse !== "function") return void 0;
|
|
1996
|
+
return (expr, options) => parse.call(parser, expr, options);
|
|
1997
|
+
}
|
|
1998
|
+
__name(v5Entry, "v5Entry");
|
|
1999
|
+
function resolveCronParse(module_) {
|
|
2000
|
+
const resolved = unwrapDefault(module_);
|
|
2001
|
+
return v4Entry(resolved) ?? v5Entry(resolved);
|
|
2002
|
+
}
|
|
2003
|
+
__name(resolveCronParse, "resolveCronParse");
|
|
2004
|
+
|
|
2005
|
+
// src/scheduler.ts
|
|
2006
|
+
var nodeRequire = (0, import_node_module.createRequire)(__importMetaUrl);
|
|
2007
|
+
var defaultSleep = /* @__PURE__ */ __name((ms) => new Promise((r) => setTimeout(r, ms)), "defaultSleep");
|
|
2008
|
+
function scheduledRunId(key, everyMs, nowMs) {
|
|
2009
|
+
return `sched:${key}:${Math.floor(nowMs / everyMs)}`;
|
|
2010
|
+
}
|
|
2011
|
+
__name(scheduledRunId, "scheduledRunId");
|
|
2012
|
+
var cronParser;
|
|
2013
|
+
function loadCronParser() {
|
|
2014
|
+
if (cronParser !== void 0) return cronParser;
|
|
2015
|
+
try {
|
|
2016
|
+
cronParser = nodeRequire("cron-parser");
|
|
2017
|
+
} catch {
|
|
2018
|
+
throw new Error('cron schedules need the optional peer dependency "cron-parser" \u2014 install it (e.g. `npm i cron-parser`).');
|
|
2019
|
+
}
|
|
2020
|
+
return cronParser;
|
|
2021
|
+
}
|
|
2022
|
+
__name(loadCronParser, "loadCronParser");
|
|
2023
|
+
function prevCronFireMs(expr, nowMs, timezone = "UTC") {
|
|
2024
|
+
const parse = resolveCronParse(loadCronParser());
|
|
2025
|
+
if (!parse) {
|
|
2026
|
+
throw new Error(`the installed "cron-parser" exposes neither v4's parseExpression nor v5's CronExpressionParser.parse \u2014 the supported range is ^4.0.0 || ^5.0.0.`);
|
|
2027
|
+
}
|
|
2028
|
+
return parse(expr, {
|
|
2029
|
+
currentDate: new Date(nowMs + 1),
|
|
2030
|
+
tz: timezone
|
|
2031
|
+
}).prev().toDate().getTime();
|
|
2032
|
+
}
|
|
2033
|
+
__name(prevCronFireMs, "prevCronFireMs");
|
|
2034
|
+
function nextCronFireMs(expr, afterMs, timezone = "UTC") {
|
|
2035
|
+
const parse = resolveCronParse(loadCronParser());
|
|
2036
|
+
if (!parse) {
|
|
2037
|
+
throw new Error(`the installed "cron-parser" exposes neither v4's parseExpression nor v5's CronExpressionParser.parse \u2014 the supported range is ^4.0.0 || ^5.0.0.`);
|
|
2038
|
+
}
|
|
2039
|
+
return parse(expr, {
|
|
2040
|
+
currentDate: new Date(afterMs),
|
|
2041
|
+
tz: timezone
|
|
2042
|
+
}).next().toDate().getTime();
|
|
2043
|
+
}
|
|
2044
|
+
__name(nextCronFireMs, "nextCronFireMs");
|
|
2045
|
+
function scheduleRunIdAt(s, nowMs) {
|
|
2046
|
+
if (s.cron != null) return `sched:${s.key}:${prevCronFireMs(s.cron, nowMs, s.timezone)}`;
|
|
2047
|
+
if (s.everyMs != null) return scheduledRunId(s.key, s.everyMs, nowMs);
|
|
2048
|
+
throw new Error(`schedule "${s.key}" needs either "everyMs" or "cron"`);
|
|
2049
|
+
}
|
|
2050
|
+
__name(scheduleRunIdAt, "scheduleRunIdAt");
|
|
2051
|
+
function scheduleRunIdsAt(s, nowMs) {
|
|
2052
|
+
const current = scheduleRunIdAt(s, nowMs);
|
|
2053
|
+
if (!s.backfill || s.backfill.maxCatchup <= 0) return [
|
|
2054
|
+
current
|
|
2055
|
+
];
|
|
2056
|
+
const ids = [];
|
|
2057
|
+
if (s.everyMs != null) {
|
|
2058
|
+
const bucket = Math.floor(nowMs / s.everyMs);
|
|
2059
|
+
const oldest = Math.max(0, bucket - s.backfill.maxCatchup);
|
|
2060
|
+
for (let b = oldest; b < bucket; b += 1) ids.push(`sched:${s.key}:${b}`);
|
|
2061
|
+
} else if (s.cron != null) {
|
|
2062
|
+
const prior = [];
|
|
2063
|
+
let cursor = prevCronFireMs(s.cron, nowMs, s.timezone);
|
|
2064
|
+
for (let i = 0; i < s.backfill.maxCatchup; i += 1) {
|
|
2065
|
+
const before = prevCronFireMs(s.cron, cursor - 1, s.timezone);
|
|
2066
|
+
prior.push(`sched:${s.key}:${before}`);
|
|
2067
|
+
cursor = before;
|
|
2068
|
+
}
|
|
2069
|
+
ids.push(...prior.reverse());
|
|
2070
|
+
}
|
|
2071
|
+
ids.push(current);
|
|
2072
|
+
return ids;
|
|
2073
|
+
}
|
|
2074
|
+
__name(scheduleRunIdsAt, "scheduleRunIdsAt");
|
|
2075
|
+
async function runSchedules(engine, schedules, nowMs, opts) {
|
|
2076
|
+
const random = opts?.random ?? Math.random;
|
|
2077
|
+
const sleep = opts?.sleep ?? defaultSleep;
|
|
2078
|
+
const ids = [];
|
|
2079
|
+
for (const s of schedules) {
|
|
2080
|
+
if (s.paused) continue;
|
|
2081
|
+
if (s.overlap === "skip" && s.everyMs) {
|
|
2082
|
+
const prevBucket = Math.floor(nowMs / s.everyMs) - 1;
|
|
2083
|
+
if (prevBucket >= 0) {
|
|
2084
|
+
const prev = await engine.getRun(`sched:${s.key}:${prevBucket}`);
|
|
2085
|
+
if (prev && (prev.status === "running" || prev.status === "suspended")) continue;
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
if (s.jitter && s.jitter > 0) await sleep(Math.floor(random() * s.jitter));
|
|
2089
|
+
for (const runId of scheduleRunIdsAt(s, nowMs)) {
|
|
2090
|
+
await engine.start(s.workflow, s.input, runId);
|
|
2091
|
+
ids.push(runId);
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
return ids;
|
|
2095
|
+
}
|
|
2096
|
+
__name(runSchedules, "runSchedules");
|
|
2097
|
+
|
|
2098
|
+
// src/schedules.ts
|
|
2099
|
+
var ScheduleAlreadyExistsError = class extends Error {
|
|
2100
|
+
static {
|
|
2101
|
+
__name(this, "ScheduleAlreadyExistsError");
|
|
2102
|
+
}
|
|
2103
|
+
scheduleId;
|
|
2104
|
+
constructor(scheduleId) {
|
|
2105
|
+
super(`schedule "${scheduleId}" already exists`), this.scheduleId = scheduleId;
|
|
2106
|
+
this.name = "ScheduleAlreadyExistsError";
|
|
2107
|
+
}
|
|
2108
|
+
};
|
|
2109
|
+
var ScheduleNotFoundError = class extends Error {
|
|
2110
|
+
static {
|
|
2111
|
+
__name(this, "ScheduleNotFoundError");
|
|
2112
|
+
}
|
|
2113
|
+
scheduleId;
|
|
2114
|
+
constructor(scheduleId) {
|
|
2115
|
+
super(`schedule "${scheduleId}" not found`), this.scheduleId = scheduleId;
|
|
2116
|
+
this.name = "ScheduleNotFoundError";
|
|
2117
|
+
}
|
|
2118
|
+
};
|
|
2119
|
+
var DEFAULT_NAMESPACE = "default";
|
|
2120
|
+
var ON_TIME_GRACE_MS = 6e4;
|
|
2121
|
+
var TICK_BATCH = 100;
|
|
2122
|
+
var ScheduleClient = class {
|
|
2123
|
+
static {
|
|
2124
|
+
__name(this, "ScheduleClient");
|
|
2125
|
+
}
|
|
2126
|
+
store;
|
|
2127
|
+
host;
|
|
2128
|
+
options;
|
|
2129
|
+
constructor(store, host, options = {}) {
|
|
2130
|
+
this.store = store;
|
|
2131
|
+
this.host = host;
|
|
2132
|
+
this.options = options;
|
|
2133
|
+
}
|
|
2134
|
+
now() {
|
|
2135
|
+
return (this.options.clock ?? Date.now)();
|
|
2136
|
+
}
|
|
2137
|
+
/** Whether the store persists schedules (implements the schedule methods). */
|
|
2138
|
+
get supported() {
|
|
2139
|
+
const s = this.store;
|
|
2140
|
+
return !!(s.saveSchedule && s.getSchedule && s.updateSchedule && s.deleteSchedule && s.listSchedules);
|
|
2141
|
+
}
|
|
2142
|
+
requireStore() {
|
|
2143
|
+
if (!this.supported) {
|
|
2144
|
+
throw new Error("persisted schedules need a store that implements saveSchedule/getSchedule/updateSchedule/deleteSchedule/listSchedules \u2014 every bundled store adapter does; this one does not");
|
|
2145
|
+
}
|
|
2146
|
+
const s = this.store;
|
|
2147
|
+
return {
|
|
2148
|
+
saveSchedule: /* @__PURE__ */ __name((r) => s.saveSchedule.call(s, r), "saveSchedule"),
|
|
2149
|
+
getSchedule: /* @__PURE__ */ __name((id) => s.getSchedule.call(s, id), "getSchedule"),
|
|
2150
|
+
updateSchedule: /* @__PURE__ */ __name((id, patch, expected) => s.updateSchedule.call(s, id, patch, expected), "updateSchedule"),
|
|
2151
|
+
deleteSchedule: /* @__PURE__ */ __name((id) => s.deleteSchedule.call(s, id), "deleteSchedule"),
|
|
2152
|
+
listSchedules: /* @__PURE__ */ __name((q) => s.listSchedules.call(s, q), "listSchedules")
|
|
2153
|
+
};
|
|
2154
|
+
}
|
|
2155
|
+
/** Create a schedule. Throws {@link ScheduleAlreadyExistsError} if the id is taken. */
|
|
2156
|
+
async create(opts) {
|
|
2157
|
+
const store = this.requireStore();
|
|
2158
|
+
if (await store.getSchedule(opts.id)) throw new ScheduleAlreadyExistsError(opts.id);
|
|
2159
|
+
return this.write(opts, null);
|
|
2160
|
+
}
|
|
2161
|
+
/**
|
|
2162
|
+
* Create the schedule, or replace the definition of an existing one (its history — last fire, last
|
|
2163
|
+
* run, fire count — is kept). The next fire is recomputed from now under the new definition, so a
|
|
2164
|
+
* window that already fired is never fired again by an upsert.
|
|
2165
|
+
*/
|
|
2166
|
+
async upsert(opts) {
|
|
2167
|
+
const store = this.requireStore();
|
|
2168
|
+
return this.write(opts, await store.getSchedule(opts.id));
|
|
2169
|
+
}
|
|
2170
|
+
/** The schedule `id`, or null. */
|
|
2171
|
+
async get(id) {
|
|
2172
|
+
const record = await this.requireStore().getSchedule(id);
|
|
2173
|
+
return record ? describe(record) : null;
|
|
2174
|
+
}
|
|
2175
|
+
/** Schedules matching `query`, soonest-due first. */
|
|
2176
|
+
async list(query = {}) {
|
|
2177
|
+
return (await this.requireStore().listSchedules(query)).map(describe);
|
|
2178
|
+
}
|
|
2179
|
+
/** Delete the schedule. Runs it already started are left alone. Returns whether it existed. */
|
|
2180
|
+
delete(id) {
|
|
2181
|
+
return this.requireStore().deleteSchedule(id);
|
|
2182
|
+
}
|
|
2183
|
+
/** Stop firing until {@link resume}. `note` records why (shown by `get`/`list`). */
|
|
2184
|
+
async pause(id, note) {
|
|
2185
|
+
const store = this.requireStore();
|
|
2186
|
+
const record = await this.mustGet(id);
|
|
2187
|
+
const spec = {
|
|
2188
|
+
...record.spec
|
|
2189
|
+
};
|
|
2190
|
+
if (note !== void 0) spec.note = note;
|
|
2191
|
+
await store.updateSchedule(id, {
|
|
2192
|
+
paused: true,
|
|
2193
|
+
spec,
|
|
2194
|
+
updatedAt: new Date(this.now())
|
|
2195
|
+
});
|
|
2196
|
+
return describe(await this.mustGet(id));
|
|
2197
|
+
}
|
|
2198
|
+
/**
|
|
2199
|
+
* Fire again from the next window after now — the windows skipped while paused are NOT caught up.
|
|
2200
|
+
*/
|
|
2201
|
+
async resume(id) {
|
|
2202
|
+
const store = this.requireStore();
|
|
2203
|
+
const record = await this.mustGet(id);
|
|
2204
|
+
const spec = record.spec;
|
|
2205
|
+
const window = nextWindowAfter(spec, this.now());
|
|
2206
|
+
await store.updateSchedule(id, {
|
|
2207
|
+
paused: false,
|
|
2208
|
+
nextFireAt: window === null ? null : window + jitterFor(record.id, window, spec),
|
|
2209
|
+
state: {
|
|
2210
|
+
...record.state,
|
|
2211
|
+
window
|
|
2212
|
+
},
|
|
2213
|
+
updatedAt: new Date(this.now())
|
|
2214
|
+
});
|
|
2215
|
+
return describe(await this.mustGet(id));
|
|
2216
|
+
}
|
|
2217
|
+
/**
|
|
2218
|
+
* Start one run of the schedule right now, outside its cadence (paused or not). The run id is
|
|
2219
|
+
* `sched:<id>:manual:<nowMs>`; it doesn't move the schedule's next fire.
|
|
2220
|
+
*/
|
|
2221
|
+
async trigger(id) {
|
|
2222
|
+
const record = await this.mustGet(id);
|
|
2223
|
+
const runId = `sched:${id}:manual:${this.now()}`;
|
|
2224
|
+
return this.host.start(record.workflow, specOf(record).input, runId, startOptions(record));
|
|
2225
|
+
}
|
|
2226
|
+
/**
|
|
2227
|
+
* Fire every due schedule (in this engine's namespace; all namespaces for an operator engine):
|
|
2228
|
+
* start its window's run and advance it to the next window. Called by the NestJS timer poller on
|
|
2229
|
+
* each tick when `persistedSchedules` is on; call it yourself from any other driver. Returns the
|
|
2230
|
+
* run ids started (or re-found, for an idempotent re-fire).
|
|
2231
|
+
*/
|
|
2232
|
+
async tick(nowMs = this.now()) {
|
|
2233
|
+
const store = this.requireStore();
|
|
2234
|
+
const started = [];
|
|
2235
|
+
for (let round = 0; round < 50; round++) {
|
|
2236
|
+
const due = await store.listSchedules({
|
|
2237
|
+
dueBy: nowMs,
|
|
2238
|
+
namespace: this.options.namespace,
|
|
2239
|
+
limit: TICK_BATCH
|
|
2240
|
+
});
|
|
2241
|
+
for (const record of due) {
|
|
2242
|
+
const runId = await this.fire(record, nowMs);
|
|
2243
|
+
if (runId) started.push(runId);
|
|
2244
|
+
}
|
|
2245
|
+
if (due.length < TICK_BATCH) break;
|
|
2246
|
+
}
|
|
2247
|
+
return started;
|
|
2248
|
+
}
|
|
2249
|
+
/** Fire one due schedule and advance it. Returns the run id started, if any. */
|
|
2250
|
+
async fire(record, nowMs) {
|
|
2251
|
+
const store = this.requireStore();
|
|
2252
|
+
const spec = specOf(record);
|
|
2253
|
+
const state = record.state;
|
|
2254
|
+
let latest;
|
|
2255
|
+
let next;
|
|
2256
|
+
try {
|
|
2257
|
+
latest = latestWindowAtOrBefore(spec, nowMs);
|
|
2258
|
+
next = nextWindowAfter(spec, latest);
|
|
2259
|
+
} catch (error2) {
|
|
2260
|
+
await store.updateSchedule(record.id, {
|
|
2261
|
+
paused: true,
|
|
2262
|
+
nextFireAt: null,
|
|
2263
|
+
state: {
|
|
2264
|
+
...state,
|
|
2265
|
+
lastError: `invalid schedule: ${errorMessage(error2)}`
|
|
2266
|
+
},
|
|
2267
|
+
updatedAt: new Date(nowMs)
|
|
2268
|
+
}, record.nextFireAt);
|
|
2269
|
+
return void 0;
|
|
2270
|
+
}
|
|
2271
|
+
const window = state.window ?? latest;
|
|
2272
|
+
const jitter = spec.jitterMs ?? 0;
|
|
2273
|
+
const onTime = nowMs - latest <= ON_TIME_GRACE_MS + jitter;
|
|
2274
|
+
let runId;
|
|
2275
|
+
let lastError;
|
|
2276
|
+
let fired = false;
|
|
2277
|
+
const skipMissed = spec.catchup === "skip" && !onTime;
|
|
2278
|
+
const beforeWindow = latest < window;
|
|
2279
|
+
if (!skipMissed && !beforeWindow && !await this.overlapping(spec, state)) {
|
|
2280
|
+
runId = `sched:${record.id}:${latest}`;
|
|
2281
|
+
try {
|
|
2282
|
+
await this.host.start(record.workflow, spec.input, runId, startOptions(record));
|
|
2283
|
+
fired = true;
|
|
2284
|
+
} catch (error2) {
|
|
2285
|
+
if (await this.host.getRun(runId)) fired = true;
|
|
2286
|
+
else lastError = errorMessage(error2);
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
const nextState = {
|
|
2290
|
+
...state,
|
|
2291
|
+
window: next,
|
|
2292
|
+
...fired ? {
|
|
2293
|
+
lastFireAt: latest,
|
|
2294
|
+
lastRunId: runId,
|
|
2295
|
+
fires: (state.fires ?? 0) + 1
|
|
2296
|
+
} : {},
|
|
2297
|
+
lastError: fired ? void 0 : lastError ?? state.lastError
|
|
2298
|
+
};
|
|
2299
|
+
await store.updateSchedule(record.id, {
|
|
2300
|
+
nextFireAt: next === null ? null : next + jitterFor(record.id, next, spec),
|
|
2301
|
+
state: stripUndefined(nextState),
|
|
2302
|
+
updatedAt: new Date(nowMs)
|
|
2303
|
+
}, record.nextFireAt);
|
|
2304
|
+
return fired ? runId : void 0;
|
|
2305
|
+
}
|
|
2306
|
+
/** `overlap: 'skip'` and the previous run of this schedule is still in flight. */
|
|
2307
|
+
async overlapping(spec, state) {
|
|
2308
|
+
if (spec.overlap !== "skip" || !state.lastRunId) return false;
|
|
2309
|
+
const prev = await this.host.getRun(state.lastRunId);
|
|
2310
|
+
return !!prev && !TERMINAL_RUN_STATUSES.includes(prev.status);
|
|
2311
|
+
}
|
|
2312
|
+
async mustGet(id) {
|
|
2313
|
+
const record = await this.requireStore().getSchedule(id);
|
|
2314
|
+
if (!record) throw new ScheduleNotFoundError(id);
|
|
2315
|
+
return record;
|
|
2316
|
+
}
|
|
2317
|
+
async write(opts, existing) {
|
|
2318
|
+
const store = this.requireStore();
|
|
2319
|
+
const spec = toSpec(opts);
|
|
2320
|
+
const now = this.now();
|
|
2321
|
+
const paused = opts.paused ?? existing?.paused ?? false;
|
|
2322
|
+
const window = nextWindowAfter(spec, now);
|
|
2323
|
+
const record = {
|
|
2324
|
+
id: opts.id,
|
|
2325
|
+
namespace: opts.namespace ?? this.options.namespace ?? DEFAULT_NAMESPACE,
|
|
2326
|
+
workflow: workflowName(opts.workflow),
|
|
2327
|
+
paused,
|
|
2328
|
+
nextFireAt: window === null ? null : window + jitterFor(opts.id, window, spec),
|
|
2329
|
+
tags: opts.tags,
|
|
2330
|
+
spec,
|
|
2331
|
+
state: stripUndefined({
|
|
2332
|
+
...existing?.state ?? {},
|
|
2333
|
+
window
|
|
2334
|
+
}),
|
|
2335
|
+
createdAt: existing?.createdAt ?? new Date(now),
|
|
2336
|
+
updatedAt: new Date(now)
|
|
2337
|
+
};
|
|
2338
|
+
await store.saveSchedule(record);
|
|
2339
|
+
return describe(record);
|
|
2340
|
+
}
|
|
2341
|
+
};
|
|
2342
|
+
function toSpec(opts) {
|
|
2343
|
+
if (opts.cron == null === (opts.every == null)) {
|
|
2344
|
+
throw new Error(`schedule "${opts.id}" needs exactly one of "cron" or "every"`);
|
|
2345
|
+
}
|
|
2346
|
+
const everyMs = opts.every != null ? parseDuration(opts.every) : void 0;
|
|
2347
|
+
if (everyMs !== void 0 && !(everyMs > 0)) {
|
|
2348
|
+
throw new Error(`schedule "${opts.id}": "every" must be a positive duration`);
|
|
2349
|
+
}
|
|
2350
|
+
const jitterMs = opts.jitter != null ? parseDuration(opts.jitter) : void 0;
|
|
2351
|
+
return stripUndefined({
|
|
2352
|
+
v: 1,
|
|
2353
|
+
input: opts.input,
|
|
2354
|
+
cron: opts.cron,
|
|
2355
|
+
everyMs,
|
|
2356
|
+
timezone: opts.timezone,
|
|
2357
|
+
jitterMs: jitterMs && jitterMs > 0 ? jitterMs : void 0,
|
|
2358
|
+
overlap: opts.overlap ?? "allow",
|
|
2359
|
+
catchup: opts.catchup ?? "latest",
|
|
2360
|
+
note: opts.note,
|
|
2361
|
+
searchAttributes: opts.searchAttributes,
|
|
2362
|
+
priority: opts.priority,
|
|
2363
|
+
concurrency: opts.concurrency
|
|
2364
|
+
});
|
|
2365
|
+
}
|
|
2366
|
+
__name(toSpec, "toSpec");
|
|
2367
|
+
function specOf(record) {
|
|
2368
|
+
return record.spec;
|
|
2369
|
+
}
|
|
2370
|
+
__name(specOf, "specOf");
|
|
2371
|
+
function latestWindowAtOrBefore(spec, atMs) {
|
|
2372
|
+
if (spec.cron != null) return prevCronFireMs(spec.cron, atMs, spec.timezone);
|
|
2373
|
+
const every = spec.everyMs;
|
|
2374
|
+
return Math.floor(atMs / every) * every;
|
|
2375
|
+
}
|
|
2376
|
+
__name(latestWindowAtOrBefore, "latestWindowAtOrBefore");
|
|
2377
|
+
function nextWindowAfter(spec, afterMs) {
|
|
2378
|
+
if (spec.cron != null) return nextCronFireMs(spec.cron, afterMs, spec.timezone);
|
|
2379
|
+
const every = spec.everyMs;
|
|
2380
|
+
return (Math.floor(afterMs / every) + 1) * every;
|
|
2381
|
+
}
|
|
2382
|
+
__name(nextWindowAfter, "nextWindowAfter");
|
|
2383
|
+
function jitterFor(id, window, spec) {
|
|
2384
|
+
const max = spec.jitterMs ?? 0;
|
|
2385
|
+
if (max <= 0) return 0;
|
|
2386
|
+
let h = 2166136261;
|
|
2387
|
+
for (const ch of `${id}@${window}`) {
|
|
2388
|
+
h ^= ch.charCodeAt(0);
|
|
2389
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
2390
|
+
}
|
|
2391
|
+
return h % max;
|
|
2392
|
+
}
|
|
2393
|
+
__name(jitterFor, "jitterFor");
|
|
2394
|
+
function startOptions(record) {
|
|
2395
|
+
const spec = specOf(record);
|
|
2396
|
+
return stripUndefined({
|
|
2397
|
+
tags: [
|
|
2398
|
+
...record.tags ?? [],
|
|
2399
|
+
`schedule:${record.id}`
|
|
2400
|
+
],
|
|
2401
|
+
namespace: record.namespace,
|
|
2402
|
+
searchAttributes: spec.searchAttributes,
|
|
2403
|
+
priority: spec.priority,
|
|
2404
|
+
concurrency: spec.concurrency
|
|
2405
|
+
});
|
|
2406
|
+
}
|
|
2407
|
+
__name(startOptions, "startOptions");
|
|
2408
|
+
function describe(record) {
|
|
2409
|
+
const spec = specOf(record);
|
|
2410
|
+
const state = record.state;
|
|
2411
|
+
return stripUndefined({
|
|
2412
|
+
id: record.id,
|
|
2413
|
+
workflow: record.workflow,
|
|
2414
|
+
input: spec.input,
|
|
2415
|
+
namespace: record.namespace,
|
|
2416
|
+
cron: spec.cron,
|
|
2417
|
+
everyMs: spec.everyMs,
|
|
2418
|
+
timezone: spec.timezone,
|
|
2419
|
+
jitterMs: spec.jitterMs,
|
|
2420
|
+
overlap: spec.overlap ?? "allow",
|
|
2421
|
+
catchup: spec.catchup ?? "latest",
|
|
2422
|
+
paused: record.paused,
|
|
2423
|
+
note: spec.note,
|
|
2424
|
+
tags: record.tags,
|
|
2425
|
+
searchAttributes: spec.searchAttributes,
|
|
2426
|
+
priority: spec.priority,
|
|
2427
|
+
concurrency: spec.concurrency,
|
|
2428
|
+
nextFireAt: record.paused || record.nextFireAt == null ? null : new Date(record.nextFireAt),
|
|
2429
|
+
lastFireAt: state.lastFireAt != null ? new Date(state.lastFireAt) : void 0,
|
|
2430
|
+
lastRunId: state.lastRunId,
|
|
2431
|
+
fires: state.fires ?? 0,
|
|
2432
|
+
lastError: state.lastError,
|
|
2433
|
+
createdAt: record.createdAt,
|
|
2434
|
+
updatedAt: record.updatedAt
|
|
2435
|
+
});
|
|
2436
|
+
}
|
|
2437
|
+
__name(describe, "describe");
|
|
2438
|
+
function stripUndefined(o) {
|
|
2439
|
+
return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
|
|
2440
|
+
}
|
|
2441
|
+
__name(stripUndefined, "stripUndefined");
|
|
2442
|
+
function errorMessage(error2) {
|
|
2443
|
+
return error2 instanceof Error ? error2.message : String(error2);
|
|
2444
|
+
}
|
|
2445
|
+
__name(errorMessage, "errorMessage");
|
|
2446
|
+
|
|
1829
2447
|
// src/singleton-gate.ts
|
|
1830
2448
|
var SINGLETON_RETRY_MS = 1e3;
|
|
1831
2449
|
var SINGLETON_RETRY_JITTER_MS = 250;
|
|
@@ -2180,10 +2798,15 @@ var Position = class Position2 {
|
|
|
2180
2798
|
rewind() {
|
|
2181
2799
|
this.seq -= 1;
|
|
2182
2800
|
}
|
|
2801
|
+
/** The position the NEXT primitive would claim, without claiming it. */
|
|
2802
|
+
peek() {
|
|
2803
|
+
return this.seq + 1;
|
|
2804
|
+
}
|
|
2183
2805
|
};
|
|
2184
2806
|
function createWorkflowCtx(host, runId, compensations, workflow = "", searchAttributesSchema) {
|
|
2185
2807
|
const { store, replay } = host;
|
|
2186
2808
|
const pos = new Position();
|
|
2809
|
+
const scopeOwner = {};
|
|
2187
2810
|
const readCheckpoint = /* @__PURE__ */ __name((seq) => {
|
|
2188
2811
|
const hit = replay?.get(seq);
|
|
2189
2812
|
if (hit !== void 0) return hit;
|
|
@@ -2229,7 +2852,7 @@ function createWorkflowCtx(host, runId, compensations, workflow = "", searchAttr
|
|
|
2229
2852
|
};
|
|
2230
2853
|
const body = /* @__PURE__ */ __name(() => {
|
|
2231
2854
|
const logger = createStepLogger(events, host.clock);
|
|
2232
|
-
return runInStepLogger(logger, () => fn(logger));
|
|
2855
|
+
return runInStepScope(scopeOwner, name, () => runInStepLogger(logger, () => fn(logger)));
|
|
2233
2856
|
}, "body");
|
|
2234
2857
|
const output = host.interceptStep ? await host.interceptStep(invocation, body) : await body();
|
|
2235
2858
|
await host.completeStep({
|
|
@@ -2282,7 +2905,7 @@ function createWorkflowCtx(host, runId, compensations, workflow = "", searchAttr
|
|
|
2282
2905
|
}
|
|
2283
2906
|
if (existing && existing.status === "completed") return existing.output;
|
|
2284
2907
|
return store.transaction(async (tx) => {
|
|
2285
|
-
const output = await fn(tx.raw);
|
|
2908
|
+
const output = await runInStepScope(scopeOwner, name, () => fn(tx.raw));
|
|
2286
2909
|
const cp = instantCheckpoint({
|
|
2287
2910
|
runId,
|
|
2288
2911
|
seq: current,
|
|
@@ -2791,30 +3414,49 @@ function createWorkflowCtx(host, runId, compensations, workflow = "", searchAttr
|
|
|
2791
3414
|
});
|
|
2792
3415
|
}
|
|
2793
3416
|
__name(step, "step");
|
|
3417
|
+
const nestedStep = /* @__PURE__ */ __name(() => {
|
|
3418
|
+
const scope = currentStepScope();
|
|
3419
|
+
return scope !== void 0 && scope.owner === scopeOwner ? scope.step : void 0;
|
|
3420
|
+
}, "nestedStep");
|
|
3421
|
+
const guard = /* @__PURE__ */ __name((primitive, fn) => (...args) => {
|
|
3422
|
+
const stepName = nestedStep();
|
|
3423
|
+
if (stepName === void 0) return fn(...args);
|
|
3424
|
+
return (async () => {
|
|
3425
|
+
if (await readCheckpoint(pos.peek())) return fn(...args);
|
|
3426
|
+
throw new NestedWorkflowCallError(runId, primitive, stepName);
|
|
3427
|
+
})();
|
|
3428
|
+
}, "guard");
|
|
3429
|
+
const guardedWebhook = /* @__PURE__ */ __name(() => {
|
|
3430
|
+
const stepName = nestedStep();
|
|
3431
|
+
if (stepName !== void 0 && !replay?.get(pos.peek())) {
|
|
3432
|
+
throw new NestedWorkflowCallError(runId, "webhook", stepName);
|
|
3433
|
+
}
|
|
3434
|
+
return webhook();
|
|
3435
|
+
}, "guardedWebhook");
|
|
2794
3436
|
return {
|
|
2795
3437
|
runId,
|
|
2796
|
-
step,
|
|
2797
|
-
localStep,
|
|
2798
|
-
upsertSearchAttributes,
|
|
2799
|
-
transaction,
|
|
2800
|
-
callEntity,
|
|
2801
|
-
signalEntity,
|
|
2802
|
-
sleep,
|
|
2803
|
-
sleepUntil,
|
|
3438
|
+
step: guard("step", step),
|
|
3439
|
+
localStep: guard("localStep", localStep),
|
|
3440
|
+
upsertSearchAttributes: guard("upsertSearchAttributes", upsertSearchAttributes),
|
|
3441
|
+
transaction: guard("transaction", transaction),
|
|
3442
|
+
callEntity: guard("callEntity", callEntity),
|
|
3443
|
+
signalEntity: guard("signalEntity", signalEntity),
|
|
3444
|
+
sleep: guard("sleep", sleep),
|
|
3445
|
+
sleepUntil: guard("sleepUntil", sleepUntil),
|
|
2804
3446
|
continueAsNew,
|
|
2805
|
-
waitForSignal,
|
|
2806
|
-
waitForEvent,
|
|
2807
|
-
task,
|
|
2808
|
-
child,
|
|
2809
|
-
all,
|
|
2810
|
-
startChild,
|
|
2811
|
-
breakpoint,
|
|
2812
|
-
webhook,
|
|
2813
|
-
setEvent,
|
|
2814
|
-
onUpdate,
|
|
2815
|
-
patched,
|
|
2816
|
-
now,
|
|
2817
|
-
sideEffect
|
|
3447
|
+
waitForSignal: guard("waitForSignal", waitForSignal),
|
|
3448
|
+
waitForEvent: guard("waitForEvent", waitForEvent),
|
|
3449
|
+
task: guard("task", task),
|
|
3450
|
+
child: guard("child", child),
|
|
3451
|
+
all: guard("all", all),
|
|
3452
|
+
startChild: guard("startChild", startChild),
|
|
3453
|
+
breakpoint: guard("breakpoint", breakpoint),
|
|
3454
|
+
webhook: guardedWebhook,
|
|
3455
|
+
setEvent: guard("setEvent", setEvent),
|
|
3456
|
+
onUpdate: guard("onUpdate", onUpdate),
|
|
3457
|
+
patched: guard("patched", patched),
|
|
3458
|
+
now: guard("now", now),
|
|
3459
|
+
sideEffect: guard("sideEffect", sideEffect)
|
|
2818
3460
|
};
|
|
2819
3461
|
}
|
|
2820
3462
|
__name(createWorkflowCtx, "createWorkflowCtx");
|
|
@@ -2894,6 +3536,12 @@ var WorkflowEngine = class {
|
|
|
2894
3536
|
runDispatcher;
|
|
2895
3537
|
/** Per-key serialization for singleton workflows (admission, back-pressure, notify-on-release). */
|
|
2896
3538
|
singletons;
|
|
3539
|
+
/**
|
|
3540
|
+
* Persisted, runtime-managed schedules: create/upsert/pause/resume/delete/list/trigger them, and
|
|
3541
|
+
* `tick()` fires the due ones (the NestJS timer poller does it when `persistedSchedules` is on).
|
|
3542
|
+
* Needs a store that persists schedules — every bundled adapter does. See {@link ScheduleClient}.
|
|
3543
|
+
*/
|
|
3544
|
+
schedules;
|
|
2897
3545
|
/** Every registered workflow, keyed by `name@version` — so old versions stay runnable. */
|
|
2898
3546
|
workflows = /* @__PURE__ */ new Map();
|
|
2899
3547
|
/** The newest registered version per workflow name — used to `start` new runs. */
|
|
@@ -2978,6 +3626,13 @@ var WorkflowEngine = class {
|
|
|
2978
3626
|
}, "dispatch"),
|
|
2979
3627
|
configFor: /* @__PURE__ */ __name((run) => (this.workflows.get(versionKey(run.workflow, run.workflowVersion)) ?? this.latest.get(run.workflow))?.singleton, "configFor")
|
|
2980
3628
|
});
|
|
3629
|
+
this.schedules = new ScheduleClient(this.store, {
|
|
3630
|
+
start: /* @__PURE__ */ __name((workflow, input, runId, opts) => this.start(workflow, input, runId, opts), "start"),
|
|
3631
|
+
getRun: /* @__PURE__ */ __name((runId) => this.store.getRun(runId), "getRun")
|
|
3632
|
+
}, {
|
|
3633
|
+
namespace: this.namespace,
|
|
3634
|
+
clock: this.clock
|
|
3635
|
+
});
|
|
2981
3636
|
this.pool.bind(
|
|
2982
3637
|
async (result) => {
|
|
2983
3638
|
const waiter = this.pending.get(result.stepId);
|
|
@@ -3113,6 +3768,7 @@ var WorkflowEngine = class {
|
|
|
3113
3768
|
fn,
|
|
3114
3769
|
tags: opts?.tags,
|
|
3115
3770
|
singleton: opts?.singleton,
|
|
3771
|
+
concurrency: opts?.concurrency,
|
|
3116
3772
|
executionTimeoutMs: opts?.executionTimeout != null ? parseDuration(opts.executionTimeout) : void 0,
|
|
3117
3773
|
validateInput: opts?.validateInput,
|
|
3118
3774
|
searchAttributesSchema: opts?.searchAttributesSchema,
|
|
@@ -3175,6 +3831,7 @@ var WorkflowEngine = class {
|
|
|
3175
3831
|
}, "fn"),
|
|
3176
3832
|
tags: opts.tags,
|
|
3177
3833
|
singleton: opts.singleton,
|
|
3834
|
+
concurrency: opts.concurrency,
|
|
3178
3835
|
executionTimeoutMs: opts.executionTimeout != null ? parseDuration(opts.executionTimeout) : void 0,
|
|
3179
3836
|
validateInput: opts.validateInput,
|
|
3180
3837
|
requires: opts.requires,
|
|
@@ -3346,12 +4003,17 @@ var WorkflowEngine = class {
|
|
|
3346
4003
|
error: prior.error
|
|
3347
4004
|
};
|
|
3348
4005
|
}
|
|
4006
|
+
const quota = await resolveConcurrency(opts?.concurrency, registered.concurrency, input);
|
|
4007
|
+
if (quota) await assertConcurrency(this.store, name, quota);
|
|
3349
4008
|
const now = /* @__PURE__ */ new Date();
|
|
3350
4009
|
const stampedTags = [
|
|
3351
4010
|
...opts?.tags ?? [],
|
|
3352
4011
|
...registered.singleton ? [
|
|
3353
4012
|
this.singletons.tag(registered.singleton, input)
|
|
3354
4013
|
] : [],
|
|
4014
|
+
...quota ? [
|
|
4015
|
+
concurrencyTag(quota.key)
|
|
4016
|
+
] : [],
|
|
3355
4017
|
...registered.versionDeclared === false ? [
|
|
3356
4018
|
VERSION_UNDECLARED_TAG
|
|
3357
4019
|
] : []
|
|
@@ -3396,10 +4058,30 @@ var WorkflowEngine = class {
|
|
|
3396
4058
|
getRun(runId) {
|
|
3397
4059
|
return this.store.getRun(runId);
|
|
3398
4060
|
}
|
|
3399
|
-
|
|
4061
|
+
/**
|
|
4062
|
+
* Re-execute a run's body (replaying its checkpoints). This is the EXPLICIT entry point: besides
|
|
4063
|
+
* driving a live run, it re-drives a `failed` run — the manual retry (prefer {@link requeue}, which
|
|
4064
|
+
* also resets the failure state so replay re-attempts the failed parts). Completed / cancelled /
|
|
4065
|
+
* dead runs are never re-executed. The engine's own implicit wake-ups (signals, step results,
|
|
4066
|
+
* timers, redelivered dispatches) go through {@link resumeImplicit}, which treats `failed` as
|
|
4067
|
+
* terminal too.
|
|
4068
|
+
*/
|
|
4069
|
+
resume(runId) {
|
|
4070
|
+
return this.resumeRun(runId, true);
|
|
4071
|
+
}
|
|
4072
|
+
/**
|
|
4073
|
+
* {@link resume} for every IMPLICIT wake-up — a delivered signal/child completion, a late step
|
|
4074
|
+
* result, a due timer, a redelivered dispatch, a queue-slot wake. A `failed` run is terminal to
|
|
4075
|
+
* these: only an explicit retry ({@link requeue} / {@link resume}) may re-drive it. Otherwise a
|
|
4076
|
+
* stale waiter or a late result left behind by a failed run silently re-executes its body.
|
|
4077
|
+
*/
|
|
4078
|
+
resumeImplicit(runId) {
|
|
4079
|
+
return this.resumeRun(runId, false);
|
|
4080
|
+
}
|
|
4081
|
+
async resumeRun(runId, allowFailed) {
|
|
3400
4082
|
const run = await this.store.getRun(runId);
|
|
3401
4083
|
if (!run) throw new Error(`run ${runId} not found`);
|
|
3402
|
-
if (run.status === "cancelled" || run.status === "completed" || run.status === "dead") {
|
|
4084
|
+
if (run.status === "cancelled" || run.status === "completed" || run.status === "dead" || run.status === "failed" && !allowFailed) {
|
|
3403
4085
|
return {
|
|
3404
4086
|
runId,
|
|
3405
4087
|
status: run.status,
|
|
@@ -3848,7 +4530,7 @@ var WorkflowEngine = class {
|
|
|
3848
4530
|
const nowMs = this.clock();
|
|
3849
4531
|
const acquired = await this.store.tryLockRun(runId, this.instanceId, nowMs + this.leaseMs, nowMs);
|
|
3850
4532
|
if (!acquired) return null;
|
|
3851
|
-
return this.
|
|
4533
|
+
return this.resumeImplicit(runId).catch((err) => {
|
|
3852
4534
|
if (err instanceof NamespaceMismatch) return null;
|
|
3853
4535
|
throw err;
|
|
3854
4536
|
});
|
|
@@ -4069,7 +4751,7 @@ var WorkflowEngine = class {
|
|
|
4069
4751
|
if (!acquired) continue;
|
|
4070
4752
|
try {
|
|
4071
4753
|
const settled = onLocked ? await onLocked(run) : void 0;
|
|
4072
|
-
results.push(settled ?? await this.
|
|
4754
|
+
results.push(settled ?? await this.resumeImplicit(run.id));
|
|
4073
4755
|
} catch (error2) {
|
|
4074
4756
|
console.error(`[nestjs-durable] could not resume run ${run.id} (${run.workflow}@${run.workflowVersion}); leaving it for another instance:`, error2);
|
|
4075
4757
|
}
|
|
@@ -4166,7 +4848,7 @@ var WorkflowEngine = class {
|
|
|
4166
4848
|
// for an ordinary (non-fan) signal/child await.
|
|
4167
4849
|
parallelGroup: waiter.parallelGroup
|
|
4168
4850
|
}));
|
|
4169
|
-
return this.
|
|
4851
|
+
return this.resumeImplicit(waiter.runId).catch((err) => {
|
|
4170
4852
|
if (err instanceof NamespaceMismatch) return null;
|
|
4171
4853
|
throw err;
|
|
4172
4854
|
});
|
|
@@ -4282,7 +4964,7 @@ var WorkflowEngine = class {
|
|
|
4282
4964
|
async cancel(runId, opts) {
|
|
4283
4965
|
const run = await this.store.getRun(runId);
|
|
4284
4966
|
if (!run) return null;
|
|
4285
|
-
if (
|
|
4967
|
+
if (TERMINAL_RUN_STATUSES.includes(run.status)) {
|
|
4286
4968
|
return {
|
|
4287
4969
|
runId,
|
|
4288
4970
|
status: run.status,
|
|
@@ -4311,7 +4993,7 @@ var WorkflowEngine = class {
|
|
|
4311
4993
|
}).catch(() => void 0);
|
|
4312
4994
|
}
|
|
4313
4995
|
queueMicrotask(() => {
|
|
4314
|
-
void this.
|
|
4996
|
+
void this.resumeImplicit(runId).then(() => this.notifyCancelled(runId)).catch(() => void 0);
|
|
4315
4997
|
});
|
|
4316
4998
|
await this.cancelChildren(runId, opts);
|
|
4317
4999
|
return {
|
|
@@ -4389,6 +5071,123 @@ var WorkflowEngine = class {
|
|
|
4389
5071
|
return deleted + 1;
|
|
4390
5072
|
}
|
|
4391
5073
|
/**
|
|
5074
|
+
* Hard-delete EVERY run matching `scope` — plus, by default, each one's whole child subtree — in
|
|
5075
|
+
* bounded batches. The "forget this tenant" / "drop this feature's history" operation: where
|
|
5076
|
+
* {@link deleteRun} removes one tree you already know the id of, this drains everything a scope
|
|
5077
|
+
* selects (`{ namespace: 'acme' }`, `{ tag: 'tenant:acme' }`, `{ workflows: [...] }`, attribute
|
|
5078
|
+
* predicates …) through the store API, so no caller has to reach into the `durable_*` tables.
|
|
5079
|
+
*
|
|
5080
|
+
* - **Live runs** (not yet terminal) are {@link cancel cancelled} first (plain cancel, no saga
|
|
5081
|
+
* undo), which tells any worker holding one to stop, and then deleted with the rest. Pass
|
|
5082
|
+
* `cancelLive: false` to leave live runs (and live descendants of finished ones) alone and
|
|
5083
|
+
* delete terminal history only.
|
|
5084
|
+
* - **Children** are found through the same parent→child edges as {@link getRunChildren} and deleted
|
|
5085
|
+
* with their root even when they don't match `scope` themselves (children inherit `namespace`,
|
|
5086
|
+
* but not `tags`). `children: false` deletes exactly the matching runs.
|
|
5087
|
+
* - **Batched**: `batchSize` runs (default 500) per round, each round one bulk
|
|
5088
|
+
* {@link StateStore.deleteRuns} (falling back to per-run {@link StateStore.deleteRun}).
|
|
5089
|
+
*
|
|
5090
|
+
* An EMPTY scope is rejected — it would delete every run in the store. Returns how many runs were
|
|
5091
|
+
* deleted (children included).
|
|
5092
|
+
*/
|
|
5093
|
+
async purgeRuns(scope, opts) {
|
|
5094
|
+
if (!isNonEmptyRunScope(scope)) {
|
|
5095
|
+
throw new Error("purgeRuns: refusing an empty scope (it would delete every run) \u2014 pass at least one predicate, e.g. { namespace }");
|
|
5096
|
+
}
|
|
5097
|
+
const batchSize = Math.max(1, opts?.batchSize ?? 500);
|
|
5098
|
+
const cancelLive = opts?.cancelLive ?? true;
|
|
5099
|
+
const withChildren = opts?.children ?? true;
|
|
5100
|
+
const statuses = cancelLive ? void 0 : [
|
|
5101
|
+
...TERMINAL_RUN_STATUSES
|
|
5102
|
+
];
|
|
5103
|
+
let deleted = 0;
|
|
5104
|
+
let previous = "";
|
|
5105
|
+
for (; ; ) {
|
|
5106
|
+
const batch = await this.store.listRuns({
|
|
5107
|
+
...scope,
|
|
5108
|
+
statuses,
|
|
5109
|
+
limit: batchSize
|
|
5110
|
+
});
|
|
5111
|
+
if (batch.length === 0) break;
|
|
5112
|
+
const fingerprint = batch.map((r) => r.id).join("\0");
|
|
5113
|
+
if (fingerprint === previous) {
|
|
5114
|
+
throw new Error("purgeRuns: the store returned the same runs after deleting them");
|
|
5115
|
+
}
|
|
5116
|
+
previous = fingerprint;
|
|
5117
|
+
const known = new Map(batch.map((r) => [
|
|
5118
|
+
r.id,
|
|
5119
|
+
r.status
|
|
5120
|
+
]));
|
|
5121
|
+
const ids = withChildren ? await this.collectSubtrees(batch.map((r) => r.id)) : batch.map((r) => r.id);
|
|
5122
|
+
const doomed = [];
|
|
5123
|
+
for (const id of ids) {
|
|
5124
|
+
const status = known.has(id) ? known.get(id) : (await this.store.getRun(id))?.status;
|
|
5125
|
+
if (status === void 0) continue;
|
|
5126
|
+
if (!TERMINAL_RUN_STATUSES.includes(status)) {
|
|
5127
|
+
if (!cancelLive) continue;
|
|
5128
|
+
await this.cancel(id).catch(() => void 0);
|
|
5129
|
+
}
|
|
5130
|
+
doomed.push(id);
|
|
5131
|
+
}
|
|
5132
|
+
await this.deleteRunIds(doomed);
|
|
5133
|
+
deleted += doomed.length;
|
|
5134
|
+
if (batch.length < batchSize) break;
|
|
5135
|
+
}
|
|
5136
|
+
return deleted;
|
|
5137
|
+
}
|
|
5138
|
+
/**
|
|
5139
|
+
* {@link purgeRuns} for one namespace — every run (and child) a tenant/partition owns. Children
|
|
5140
|
+
* inherit their parent's namespace, so a namespace purge is complete by construction.
|
|
5141
|
+
*/
|
|
5142
|
+
purgeNamespace(namespace, opts) {
|
|
5143
|
+
return this.purgeRuns({
|
|
5144
|
+
namespace
|
|
5145
|
+
}, opts);
|
|
5146
|
+
}
|
|
5147
|
+
/** The given runs plus every descendant, children before parents. One waiter scan for the batch. */
|
|
5148
|
+
async collectSubtrees(rootIds) {
|
|
5149
|
+
const waitersByParent = /* @__PURE__ */ new Map();
|
|
5150
|
+
for (const w of await this.store.listSignalWaiters("child:")) {
|
|
5151
|
+
const list = waitersByParent.get(w.runId) ?? [];
|
|
5152
|
+
list.push(w.token.slice("child:".length));
|
|
5153
|
+
waitersByParent.set(w.runId, list);
|
|
5154
|
+
}
|
|
5155
|
+
const prefixes = [
|
|
5156
|
+
"signal:child:",
|
|
5157
|
+
"spawn:"
|
|
5158
|
+
];
|
|
5159
|
+
const childrenOf = /* @__PURE__ */ __name(async (parent) => {
|
|
5160
|
+
const ids = new Set(waitersByParent.get(parent) ?? []);
|
|
5161
|
+
const cps = this.store.listCheckpointsByNamePrefix ? await this.store.listCheckpointsByNamePrefix(parent, prefixes) : (await this.store.listCheckpoints(parent)).filter((cp) => prefixes.some((p) => cp.name.startsWith(p)));
|
|
5162
|
+
for (const cp of cps) {
|
|
5163
|
+
if (cp.name.startsWith("signal:child:")) ids.add(cp.name.slice("signal:child:".length));
|
|
5164
|
+
if (cp.name.startsWith("spawn:") && typeof cp.output === "string") ids.add(cp.output);
|
|
5165
|
+
}
|
|
5166
|
+
return [
|
|
5167
|
+
...ids
|
|
5168
|
+
];
|
|
5169
|
+
}, "childrenOf");
|
|
5170
|
+
const ordered = [];
|
|
5171
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5172
|
+
const visit = /* @__PURE__ */ __name(async (id) => {
|
|
5173
|
+
if (seen.has(id)) return;
|
|
5174
|
+
seen.add(id);
|
|
5175
|
+
for (const child of await childrenOf(id)) await visit(child);
|
|
5176
|
+
ordered.push(id);
|
|
5177
|
+
}, "visit");
|
|
5178
|
+
for (const id of rootIds) await visit(id);
|
|
5179
|
+
return ordered;
|
|
5180
|
+
}
|
|
5181
|
+
/** Bulk-delete through the store's `deleteRuns`, or one `deleteRun` per id when it has none. */
|
|
5182
|
+
async deleteRunIds(ids) {
|
|
5183
|
+
if (ids.length === 0) return;
|
|
5184
|
+
if (this.store.deleteRuns) {
|
|
5185
|
+
await this.store.deleteRuns(ids);
|
|
5186
|
+
return;
|
|
5187
|
+
}
|
|
5188
|
+
for (const id of ids) await this.store.deleteRun(id);
|
|
5189
|
+
}
|
|
5190
|
+
/**
|
|
4392
5191
|
* Cascade cancellation to a run's children — both awaited (`ctx.child`, found via its live
|
|
4393
5192
|
* `child:<id>` waiter) and fire-and-forget (`ctx.startChild`, found via its `spawn:<id>`
|
|
4394
5193
|
* checkpoint). Recursive, so a whole subtree is cancelled; the terminal guard in `cancel` stops it
|
|
@@ -5029,7 +5828,7 @@ var WorkflowEngine = class {
|
|
|
5029
5828
|
const checkpoint = await this.store.getCheckpoint(runId, cmd.seq);
|
|
5030
5829
|
if (checkpoint && checkpoint.status !== "pending") {
|
|
5031
5830
|
setTimeout(() => {
|
|
5032
|
-
void this.
|
|
5831
|
+
void this.resumeImplicit(runId).catch(() => void 0);
|
|
5033
5832
|
}, 0).unref?.();
|
|
5034
5833
|
return;
|
|
5035
5834
|
}
|
|
@@ -5371,7 +6170,7 @@ var WorkflowEngine = class {
|
|
|
5371
6170
|
kind: "signal",
|
|
5372
6171
|
output: payload
|
|
5373
6172
|
}));
|
|
5374
|
-
setTimeout(() => void this.
|
|
6173
|
+
setTimeout(() => void this.resumeImplicit(run.id).catch(() => void 0), 0);
|
|
5375
6174
|
}, "deliverBuffered");
|
|
5376
6175
|
const buffered = await this.store.takeBufferedSignal(cmd.signal);
|
|
5377
6176
|
if (buffered) {
|
|
@@ -6185,7 +6984,7 @@ var WorkflowEngine = class {
|
|
|
6185
6984
|
queueMs: startedAt.getTime() - cp.enqueuedAt.getTime(),
|
|
6186
6985
|
durationMs: finishedAt.getTime() - startedAt.getTime()
|
|
6187
6986
|
});
|
|
6188
|
-
await this.
|
|
6987
|
+
await this.resumeImplicit(result.runId).catch((err) => {
|
|
6189
6988
|
if (err instanceof NamespaceMismatch) return null;
|
|
6190
6989
|
throw err;
|
|
6191
6990
|
});
|
|
@@ -6286,7 +7085,7 @@ var WorkflowEngine = class {
|
|
|
6286
7085
|
...waiters
|
|
6287
7086
|
];
|
|
6288
7087
|
waiters.clear();
|
|
6289
|
-
for (const runId of runIds) void this.
|
|
7088
|
+
for (const runId of runIds) void this.resumeImplicit(runId).catch(() => void 0);
|
|
6290
7089
|
}
|
|
6291
7090
|
/** Release the flow-control slot a dispatched step held (if any), by its stepId. */
|
|
6292
7091
|
async releaseQueueSlot(id) {
|
|
@@ -6409,14 +7208,6 @@ var RemoteStepError = class extends Error {
|
|
|
6409
7208
|
}
|
|
6410
7209
|
};
|
|
6411
7210
|
|
|
6412
|
-
// src/interfaces.ts
|
|
6413
|
-
var TERMINAL_RUN_STATUSES = [
|
|
6414
|
-
"completed",
|
|
6415
|
-
"failed",
|
|
6416
|
-
"cancelled",
|
|
6417
|
-
"dead"
|
|
6418
|
-
];
|
|
6419
|
-
|
|
6420
7211
|
// src/run-facets.ts
|
|
6421
7212
|
function facetOrigin(origin) {
|
|
6422
7213
|
return origin?.trim() ? origin : null;
|
|
@@ -6448,7 +7239,8 @@ __name(mergeRunFacetRows, "mergeRunFacetRows");
|
|
|
6448
7239
|
var RUN_VALUE_FACET_LIMIT = 100;
|
|
6449
7240
|
var RUN_VALUE_FACET_SCAN = 5e3;
|
|
6450
7241
|
var ENGINE_MINTED_TAG_PREFIXES = [
|
|
6451
|
-
"singleton:"
|
|
7242
|
+
"singleton:",
|
|
7243
|
+
"concurrency:"
|
|
6452
7244
|
];
|
|
6453
7245
|
function isEngineMintedTag(value) {
|
|
6454
7246
|
return value !== null && ENGINE_MINTED_TAG_PREFIXES.some((prefix) => value.startsWith(prefix));
|
|
@@ -6553,9 +7345,42 @@ var CodecStateStore = class {
|
|
|
6553
7345
|
* Bound the same conditional way as {@link runFacets} so absence is reported, not faked.
|
|
6554
7346
|
*/
|
|
6555
7347
|
runValueFacets;
|
|
7348
|
+
/** Forwarded verbatim (deletes touch no payload). Bound only when the inner store has it, so the
|
|
7349
|
+
* engine's per-run fallback still kicks in through the wrapper. */
|
|
7350
|
+
deleteRuns;
|
|
7351
|
+
/** Forwarded verbatim — retention selects by status/updatedAt/scope, never by payload. Bound only
|
|
7352
|
+
* when the inner store prunes, so the retention poller still sees (and warns about) its absence. */
|
|
7353
|
+
pruneTerminalRuns;
|
|
7354
|
+
/** Forwarded verbatim — a count reads no payload. Bound only when the inner store counts, so the
|
|
7355
|
+
* engine's facet/listing fallback still applies through the wrapper. */
|
|
7356
|
+
countRuns;
|
|
7357
|
+
/** Schedules persist their run `input` inside `spec` — encoded like any other payload. Bound only
|
|
7358
|
+
* when the inner store persists schedules, so `engine.schedules` reports absence faithfully. */
|
|
7359
|
+
saveSchedule;
|
|
7360
|
+
getSchedule;
|
|
7361
|
+
updateSchedule;
|
|
7362
|
+
deleteSchedule;
|
|
7363
|
+
listSchedules;
|
|
6556
7364
|
constructor(inner, codec) {
|
|
6557
7365
|
this.inner = inner;
|
|
6558
7366
|
this.codec = codec;
|
|
7367
|
+
const deleteRuns = inner.deleteRuns;
|
|
7368
|
+
if (deleteRuns) this.deleteRuns = (ids) => deleteRuns.call(inner, ids);
|
|
7369
|
+
const prune = inner.pruneTerminalRuns;
|
|
7370
|
+
if (prune) this.pruneTerminalRuns = (policy, now, limit) => prune.call(inner, policy, now, limit);
|
|
7371
|
+
const count = inner.countRuns;
|
|
7372
|
+
if (count) this.countRuns = (query) => count.call(inner, query);
|
|
7373
|
+
const { saveSchedule, getSchedule, updateSchedule, deleteSchedule, listSchedules } = inner;
|
|
7374
|
+
if (saveSchedule && getSchedule && updateSchedule && deleteSchedule && listSchedules) {
|
|
7375
|
+
this.saveSchedule = (r) => saveSchedule.call(inner, this.encSchedule(r));
|
|
7376
|
+
this.getSchedule = async (id) => this.decSchedule(await getSchedule.call(inner, id));
|
|
7377
|
+
this.updateSchedule = (id, patch, expected) => updateSchedule.call(inner, id, patch.spec ? {
|
|
7378
|
+
...patch,
|
|
7379
|
+
spec: this.encSpec(patch.spec)
|
|
7380
|
+
} : patch, expected);
|
|
7381
|
+
this.deleteSchedule = (id) => deleteSchedule.call(inner, id);
|
|
7382
|
+
this.listSchedules = async (q) => (await listSchedules.call(inner, q)).map((r) => this.decSchedule(r));
|
|
7383
|
+
}
|
|
6559
7384
|
const facets = inner.runFacets;
|
|
6560
7385
|
if (facets) this.runFacets = (query) => facets.call(inner, query);
|
|
6561
7386
|
const valueFacets = inner.runValueFacets;
|
|
@@ -6597,6 +7422,28 @@ var CodecStateStore = class {
|
|
|
6597
7422
|
output: this.dec(cp.output)
|
|
6598
7423
|
};
|
|
6599
7424
|
}
|
|
7425
|
+
encSpec(spec) {
|
|
7426
|
+
return "input" in spec ? {
|
|
7427
|
+
...spec,
|
|
7428
|
+
input: this.enc(spec.input)
|
|
7429
|
+
} : spec;
|
|
7430
|
+
}
|
|
7431
|
+
encSchedule(r) {
|
|
7432
|
+
return {
|
|
7433
|
+
...r,
|
|
7434
|
+
spec: this.encSpec(r.spec)
|
|
7435
|
+
};
|
|
7436
|
+
}
|
|
7437
|
+
decSchedule(r) {
|
|
7438
|
+
if (!r || !("input" in r.spec)) return r;
|
|
7439
|
+
return {
|
|
7440
|
+
...r,
|
|
7441
|
+
spec: {
|
|
7442
|
+
...r.spec,
|
|
7443
|
+
input: this.dec(r.spec.input)
|
|
7444
|
+
}
|
|
7445
|
+
};
|
|
7446
|
+
}
|
|
6600
7447
|
ensureSchema() {
|
|
6601
7448
|
return this.inner.ensureSchema?.() ?? Promise.resolve();
|
|
6602
7449
|
}
|
|
@@ -6795,126 +7642,6 @@ function collectMetrics(engine) {
|
|
|
6795
7642
|
}
|
|
6796
7643
|
__name(collectMetrics, "collectMetrics");
|
|
6797
7644
|
|
|
6798
|
-
// src/scheduler.ts
|
|
6799
|
-
var import_node_module = require("module");
|
|
6800
|
-
|
|
6801
|
-
// src/cron-compat.ts
|
|
6802
|
-
function isIndexable(value) {
|
|
6803
|
-
return typeof value === "object" && value !== null || typeof value === "function";
|
|
6804
|
-
}
|
|
6805
|
-
__name(isIndexable, "isIndexable");
|
|
6806
|
-
function unwrapDefault(module_) {
|
|
6807
|
-
if (!isIndexable(module_)) return module_;
|
|
6808
|
-
if (Reflect.get(module_, "parseExpression") !== void 0) return module_;
|
|
6809
|
-
if (Reflect.get(module_, "CronExpressionParser") !== void 0) return module_;
|
|
6810
|
-
const inner = Reflect.get(module_, "default");
|
|
6811
|
-
return inner === void 0 ? module_ : inner;
|
|
6812
|
-
}
|
|
6813
|
-
__name(unwrapDefault, "unwrapDefault");
|
|
6814
|
-
function v4Entry(module_) {
|
|
6815
|
-
if (!isIndexable(module_)) return void 0;
|
|
6816
|
-
const parse = Reflect.get(module_, "parseExpression");
|
|
6817
|
-
if (typeof parse !== "function") return void 0;
|
|
6818
|
-
return (expr, options) => parse.call(module_, expr, options);
|
|
6819
|
-
}
|
|
6820
|
-
__name(v4Entry, "v4Entry");
|
|
6821
|
-
function v5Entry(module_) {
|
|
6822
|
-
if (!isIndexable(module_)) return void 0;
|
|
6823
|
-
const parser = Reflect.get(module_, "CronExpressionParser");
|
|
6824
|
-
if (!isIndexable(parser)) return void 0;
|
|
6825
|
-
const parse = Reflect.get(parser, "parse");
|
|
6826
|
-
if (typeof parse !== "function") return void 0;
|
|
6827
|
-
return (expr, options) => parse.call(parser, expr, options);
|
|
6828
|
-
}
|
|
6829
|
-
__name(v5Entry, "v5Entry");
|
|
6830
|
-
function resolveCronParse(module_) {
|
|
6831
|
-
const resolved = unwrapDefault(module_);
|
|
6832
|
-
return v4Entry(resolved) ?? v5Entry(resolved);
|
|
6833
|
-
}
|
|
6834
|
-
__name(resolveCronParse, "resolveCronParse");
|
|
6835
|
-
|
|
6836
|
-
// src/scheduler.ts
|
|
6837
|
-
var nodeRequire = (0, import_node_module.createRequire)(__importMetaUrl);
|
|
6838
|
-
var defaultSleep = /* @__PURE__ */ __name((ms) => new Promise((r) => setTimeout(r, ms)), "defaultSleep");
|
|
6839
|
-
function scheduledRunId(key, everyMs, nowMs) {
|
|
6840
|
-
return `sched:${key}:${Math.floor(nowMs / everyMs)}`;
|
|
6841
|
-
}
|
|
6842
|
-
__name(scheduledRunId, "scheduledRunId");
|
|
6843
|
-
var cronParser;
|
|
6844
|
-
function loadCronParser() {
|
|
6845
|
-
if (cronParser !== void 0) return cronParser;
|
|
6846
|
-
try {
|
|
6847
|
-
cronParser = nodeRequire("cron-parser");
|
|
6848
|
-
} catch {
|
|
6849
|
-
throw new Error('cron schedules need the optional peer dependency "cron-parser" \u2014 install it (e.g. `npm i cron-parser`).');
|
|
6850
|
-
}
|
|
6851
|
-
return cronParser;
|
|
6852
|
-
}
|
|
6853
|
-
__name(loadCronParser, "loadCronParser");
|
|
6854
|
-
function prevCronFireMs(expr, nowMs, timezone = "UTC") {
|
|
6855
|
-
const parse = resolveCronParse(loadCronParser());
|
|
6856
|
-
if (!parse) {
|
|
6857
|
-
throw new Error(`the installed "cron-parser" exposes neither v4's parseExpression nor v5's CronExpressionParser.parse \u2014 the supported range is ^4.0.0 || ^5.0.0.`);
|
|
6858
|
-
}
|
|
6859
|
-
return parse(expr, {
|
|
6860
|
-
currentDate: new Date(nowMs + 1),
|
|
6861
|
-
tz: timezone
|
|
6862
|
-
}).prev().toDate().getTime();
|
|
6863
|
-
}
|
|
6864
|
-
__name(prevCronFireMs, "prevCronFireMs");
|
|
6865
|
-
function scheduleRunIdAt(s, nowMs) {
|
|
6866
|
-
if (s.cron != null) return `sched:${s.key}:${prevCronFireMs(s.cron, nowMs, s.timezone)}`;
|
|
6867
|
-
if (s.everyMs != null) return scheduledRunId(s.key, s.everyMs, nowMs);
|
|
6868
|
-
throw new Error(`schedule "${s.key}" needs either "everyMs" or "cron"`);
|
|
6869
|
-
}
|
|
6870
|
-
__name(scheduleRunIdAt, "scheduleRunIdAt");
|
|
6871
|
-
function scheduleRunIdsAt(s, nowMs) {
|
|
6872
|
-
const current = scheduleRunIdAt(s, nowMs);
|
|
6873
|
-
if (!s.backfill || s.backfill.maxCatchup <= 0) return [
|
|
6874
|
-
current
|
|
6875
|
-
];
|
|
6876
|
-
const ids = [];
|
|
6877
|
-
if (s.everyMs != null) {
|
|
6878
|
-
const bucket = Math.floor(nowMs / s.everyMs);
|
|
6879
|
-
const oldest = Math.max(0, bucket - s.backfill.maxCatchup);
|
|
6880
|
-
for (let b = oldest; b < bucket; b += 1) ids.push(`sched:${s.key}:${b}`);
|
|
6881
|
-
} else if (s.cron != null) {
|
|
6882
|
-
const prior = [];
|
|
6883
|
-
let cursor = prevCronFireMs(s.cron, nowMs, s.timezone);
|
|
6884
|
-
for (let i = 0; i < s.backfill.maxCatchup; i += 1) {
|
|
6885
|
-
const before = prevCronFireMs(s.cron, cursor - 1, s.timezone);
|
|
6886
|
-
prior.push(`sched:${s.key}:${before}`);
|
|
6887
|
-
cursor = before;
|
|
6888
|
-
}
|
|
6889
|
-
ids.push(...prior.reverse());
|
|
6890
|
-
}
|
|
6891
|
-
ids.push(current);
|
|
6892
|
-
return ids;
|
|
6893
|
-
}
|
|
6894
|
-
__name(scheduleRunIdsAt, "scheduleRunIdsAt");
|
|
6895
|
-
async function runSchedules(engine, schedules, nowMs, opts) {
|
|
6896
|
-
const random = opts?.random ?? Math.random;
|
|
6897
|
-
const sleep = opts?.sleep ?? defaultSleep;
|
|
6898
|
-
const ids = [];
|
|
6899
|
-
for (const s of schedules) {
|
|
6900
|
-
if (s.paused) continue;
|
|
6901
|
-
if (s.overlap === "skip" && s.everyMs) {
|
|
6902
|
-
const prevBucket = Math.floor(nowMs / s.everyMs) - 1;
|
|
6903
|
-
if (prevBucket >= 0) {
|
|
6904
|
-
const prev = await engine.getRun(`sched:${s.key}:${prevBucket}`);
|
|
6905
|
-
if (prev && (prev.status === "running" || prev.status === "suspended")) continue;
|
|
6906
|
-
}
|
|
6907
|
-
}
|
|
6908
|
-
if (s.jitter && s.jitter > 0) await sleep(Math.floor(random() * s.jitter));
|
|
6909
|
-
for (const runId of scheduleRunIdsAt(s, nowMs)) {
|
|
6910
|
-
await engine.start(s.workflow, s.input, runId);
|
|
6911
|
-
ids.push(runId);
|
|
6912
|
-
}
|
|
6913
|
-
}
|
|
6914
|
-
return ids;
|
|
6915
|
-
}
|
|
6916
|
-
__name(runSchedules, "runSchedules");
|
|
6917
|
-
|
|
6918
7645
|
// src/search-attributes.ts
|
|
6919
7646
|
function compare(actual, filter) {
|
|
6920
7647
|
if (filter.op === "in") return filter.values.some((v) => actual === v);
|
|
@@ -7122,6 +7849,38 @@ var InMemoryStateStore = class {
|
|
|
7122
7849
|
}
|
|
7123
7850
|
this.reindexAttributes(runId, void 0);
|
|
7124
7851
|
}
|
|
7852
|
+
async deleteRuns(runIds) {
|
|
7853
|
+
for (const runId of runIds) await this.deleteRun(runId);
|
|
7854
|
+
}
|
|
7855
|
+
async pruneTerminalRuns(policy, nowMs, limit) {
|
|
7856
|
+
const statuses = policy.statuses.filter((s) => TERMINAL_RUN_STATUSES.includes(s));
|
|
7857
|
+
if (statuses.length === 0 || limit <= 0) return 0;
|
|
7858
|
+
if (policy.maxAge == null && policy.maxCount == null) return 0;
|
|
7859
|
+
const inScope = this.matching({
|
|
7860
|
+
...policy.scope ?? {},
|
|
7861
|
+
statuses
|
|
7862
|
+
});
|
|
7863
|
+
const ids = /* @__PURE__ */ new Set();
|
|
7864
|
+
if (policy.maxAge != null) {
|
|
7865
|
+
const cutoff = nowMs - parseDuration(policy.maxAge);
|
|
7866
|
+
const expired = inScope.filter((r) => r.updatedAt.getTime() < cutoff).sort((a, b) => a.updatedAt.getTime() - b.updatedAt.getTime()).slice(0, limit);
|
|
7867
|
+
for (const r of expired) ids.add(r.id);
|
|
7868
|
+
}
|
|
7869
|
+
if (policy.maxCount != null && ids.size < limit) {
|
|
7870
|
+
const byRecency = [
|
|
7871
|
+
...inScope
|
|
7872
|
+
].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime() || b.id.localeCompare(a.id));
|
|
7873
|
+
for (const r of byRecency.slice(policy.maxCount)) {
|
|
7874
|
+
if (ids.size >= limit) break;
|
|
7875
|
+
ids.add(r.id);
|
|
7876
|
+
}
|
|
7877
|
+
}
|
|
7878
|
+
const doomed = [
|
|
7879
|
+
...ids
|
|
7880
|
+
].slice(0, limit);
|
|
7881
|
+
await this.deleteRuns(doomed);
|
|
7882
|
+
return doomed.length;
|
|
7883
|
+
}
|
|
7125
7884
|
async getCheckpoint(runId, seq) {
|
|
7126
7885
|
const cp = this.checkpoints.get(this.key(runId, seq));
|
|
7127
7886
|
return cp ? {
|
|
@@ -7329,6 +8088,9 @@ var InMemoryStateStore = class {
|
|
|
7329
8088
|
...r
|
|
7330
8089
|
}));
|
|
7331
8090
|
}
|
|
8091
|
+
async countRuns(query) {
|
|
8092
|
+
return this.matching(query).length;
|
|
8093
|
+
}
|
|
7332
8094
|
async runFacets(query) {
|
|
7333
8095
|
return mergeRunFacetRows(this.matching(query).map((run) => ({
|
|
7334
8096
|
status: run.status,
|
|
@@ -7346,6 +8108,34 @@ var InMemoryStateStore = class {
|
|
|
7346
8108
|
});
|
|
7347
8109
|
return runValueFacetsFromRuns(runs, axis, opts);
|
|
7348
8110
|
}
|
|
8111
|
+
schedules = /* @__PURE__ */ new Map();
|
|
8112
|
+
async saveSchedule(record) {
|
|
8113
|
+
this.schedules.set(record.id, structuredClone(record));
|
|
8114
|
+
}
|
|
8115
|
+
async getSchedule(id) {
|
|
8116
|
+
const record = this.schedules.get(id);
|
|
8117
|
+
return record ? structuredClone(record) : null;
|
|
8118
|
+
}
|
|
8119
|
+
async updateSchedule(id, patch, expectedNextFireAt) {
|
|
8120
|
+
const record = this.schedules.get(id);
|
|
8121
|
+
if (!record) return false;
|
|
8122
|
+
if (expectedNextFireAt !== void 0 && record.nextFireAt !== expectedNextFireAt) return false;
|
|
8123
|
+
this.schedules.set(id, {
|
|
8124
|
+
...record,
|
|
8125
|
+
...structuredClone(patch)
|
|
8126
|
+
});
|
|
8127
|
+
return true;
|
|
8128
|
+
}
|
|
8129
|
+
async deleteSchedule(id) {
|
|
8130
|
+
return this.schedules.delete(id);
|
|
8131
|
+
}
|
|
8132
|
+
async listSchedules(query) {
|
|
8133
|
+
const rows = [
|
|
8134
|
+
...this.schedules.values()
|
|
8135
|
+
].filter((s) => query.namespace === void 0 || s.namespace === query.namespace).filter((s) => query.workflow === void 0 || s.workflow === query.workflow).filter((s) => query.tag === void 0 || (s.tags ?? []).includes(query.tag)).filter((s) => query.paused === void 0 || s.paused === query.paused).filter((s) => query.dueBy === void 0 || !s.paused && s.nextFireAt !== null && s.nextFireAt <= query.dueBy).sort((a, b) => (a.nextFireAt ?? Number.POSITIVE_INFINITY) - (b.nextFireAt ?? Number.POSITIVE_INFINITY) || a.id.localeCompare(b.id));
|
|
8136
|
+
const offset = query.offset ?? 0;
|
|
8137
|
+
return rows.slice(offset, query.limit != null ? offset + query.limit : void 0).map((s) => structuredClone(s));
|
|
8138
|
+
}
|
|
7349
8139
|
async listCheckpoints(runId) {
|
|
7350
8140
|
const rows = [
|
|
7351
8141
|
...this.checkpoints.values()
|
|
@@ -7427,7 +8217,9 @@ var InMemoryTransport = class {
|
|
|
7427
8217
|
CONTROL_FLOW_SIGNAL,
|
|
7428
8218
|
CURRENT_PROTOCOL_VERSION,
|
|
7429
8219
|
CodecStateStore,
|
|
8220
|
+
ConcurrencyLimitError,
|
|
7430
8221
|
ContinueAsNew,
|
|
8222
|
+
DEFAULT_CONCURRENCY_STATUSES,
|
|
7431
8223
|
DEFAULT_PARTITION,
|
|
7432
8224
|
DURABLE_OPTIONS,
|
|
7433
8225
|
DURABLE_OPTIONS_CANONICAL,
|
|
@@ -7446,6 +8238,7 @@ var InMemoryTransport = class {
|
|
|
7446
8238
|
InMemoryTransport,
|
|
7447
8239
|
LEGACY_V1_CAPABILITIES,
|
|
7448
8240
|
LEGACY_V1_PROTOCOL,
|
|
8241
|
+
NestedWorkflowCallError,
|
|
7449
8242
|
NonDeterminismError,
|
|
7450
8243
|
QueueController,
|
|
7451
8244
|
RUN_VALUE_FACET_LIMIT,
|
|
@@ -7457,6 +8250,9 @@ var InMemoryTransport = class {
|
|
|
7457
8250
|
RunGateway,
|
|
7458
8251
|
STATE_STORE,
|
|
7459
8252
|
STATE_STORE_CANONICAL,
|
|
8253
|
+
ScheduleAlreadyExistsError,
|
|
8254
|
+
ScheduleClient,
|
|
8255
|
+
ScheduleNotFoundError,
|
|
7460
8256
|
SignalTimeoutError,
|
|
7461
8257
|
SingletonQueueFullError,
|
|
7462
8258
|
TERMINAL_RUN_STATUSES,
|
|
@@ -7470,6 +8266,7 @@ var InMemoryTransport = class {
|
|
|
7470
8266
|
aggregateAnnouncements,
|
|
7471
8267
|
announcementsOf,
|
|
7472
8268
|
applyAttributeQuery,
|
|
8269
|
+
assertConcurrency,
|
|
7473
8270
|
attributeColumnFor,
|
|
7474
8271
|
attributeOperand,
|
|
7475
8272
|
attributePredicateOperands,
|
|
@@ -7482,10 +8279,13 @@ var InMemoryTransport = class {
|
|
|
7482
8279
|
classifyWaiterToken,
|
|
7483
8280
|
clearDurableExecutionWrappers,
|
|
7484
8281
|
collectMetrics,
|
|
8282
|
+
concurrencyTag,
|
|
7485
8283
|
controlPlaneDescriptor,
|
|
8284
|
+
countRuns,
|
|
7486
8285
|
coveredTokens,
|
|
7487
8286
|
createStepLogger,
|
|
7488
8287
|
currentStep,
|
|
8288
|
+
currentStepScope,
|
|
7489
8289
|
currentWorkflowCtx,
|
|
7490
8290
|
debug,
|
|
7491
8291
|
describeWorker,
|
|
@@ -7504,6 +8304,7 @@ var InMemoryTransport = class {
|
|
|
7504
8304
|
isEngineMintedTag,
|
|
7505
8305
|
isEventToken,
|
|
7506
8306
|
isLegacyDescriptor,
|
|
8307
|
+
isNonEmptyRunScope,
|
|
7507
8308
|
isWorkflowControlFlowSignal,
|
|
7508
8309
|
log,
|
|
7509
8310
|
matchesAttributes,
|
|
@@ -7511,6 +8312,7 @@ var InMemoryTransport = class {
|
|
|
7511
8312
|
mergeRunValueFacetRows,
|
|
7512
8313
|
missingCapabilities,
|
|
7513
8314
|
negotiate,
|
|
8315
|
+
nextCronFireMs,
|
|
7514
8316
|
normalizeAttributeRows,
|
|
7515
8317
|
normalizeDescriptor,
|
|
7516
8318
|
observedAnnouncements,
|
|
@@ -7520,11 +8322,14 @@ var InMemoryTransport = class {
|
|
|
7520
8322
|
prevCronFireMs,
|
|
7521
8323
|
readSearchAttributes,
|
|
7522
8324
|
requiredCapabilities,
|
|
8325
|
+
resolveConcurrency,
|
|
7523
8326
|
resolveRouting,
|
|
7524
8327
|
resolveRunWaiting,
|
|
7525
8328
|
runDurableExecution,
|
|
7526
8329
|
runInStepLogger,
|
|
8330
|
+
runInStepScope,
|
|
7527
8331
|
runInWorkflowCtx,
|
|
8332
|
+
runOutsideWorkflowCtx,
|
|
7528
8333
|
runSchedules,
|
|
7529
8334
|
runStepHandler,
|
|
7530
8335
|
runValueFacetsFromRuns,
|