@dudousxd/nestjs-durable-core 0.81.0 → 0.83.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 +361 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +271 -23
- package/dist/index.d.ts +271 -23
- package/dist/index.js +356 -38
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,7 @@ __export(index_exports, {
|
|
|
30
30
|
ContinueAsNew: () => ContinueAsNew,
|
|
31
31
|
DEFAULT_CONCURRENCY_STATUSES: () => DEFAULT_CONCURRENCY_STATUSES,
|
|
32
32
|
DEFAULT_PARTITION: () => DEFAULT_PARTITION,
|
|
33
|
+
DEFAULT_STALLED_AFTER_MS: () => DEFAULT_STALLED_AFTER_MS,
|
|
33
34
|
DURABLE_OPTIONS: () => DURABLE_OPTIONS,
|
|
34
35
|
DURABLE_OPTIONS_CANONICAL: () => DURABLE_OPTIONS_CANONICAL,
|
|
35
36
|
DURABLE_STEP_CONFIG: () => DURABLE_STEP_CONFIG,
|
|
@@ -53,6 +54,7 @@ __export(index_exports, {
|
|
|
53
54
|
NestedWorkflowCallError: () => NestedWorkflowCallError,
|
|
54
55
|
NonDeterminismError: () => NonDeterminismError,
|
|
55
56
|
QueueController: () => QueueController,
|
|
57
|
+
RUN_HISTORY_FIXTURE_VERSION: () => RUN_HISTORY_FIXTURE_VERSION,
|
|
56
58
|
RUN_VALUE_FACET_LIMIT: () => RUN_VALUE_FACET_LIMIT,
|
|
57
59
|
RUN_VALUE_FACET_SCAN: () => RUN_VALUE_FACET_SCAN,
|
|
58
60
|
RemoteStepError: () => RemoteStepError,
|
|
@@ -70,6 +72,7 @@ __export(index_exports, {
|
|
|
70
72
|
ScheduleAlreadyExistsError: () => ScheduleAlreadyExistsError,
|
|
71
73
|
ScheduleClient: () => ScheduleClient,
|
|
72
74
|
ScheduleNotFoundError: () => ScheduleNotFoundError,
|
|
75
|
+
SchemaValidationError: () => SchemaValidationError,
|
|
73
76
|
SignalTimeoutError: () => SignalTimeoutError,
|
|
74
77
|
SingletonQueueFullError: () => SingletonQueueFullError,
|
|
75
78
|
TERMINAL_RUN_STATUSES: () => TERMINAL_RUN_STATUSES,
|
|
@@ -126,6 +129,7 @@ __export(index_exports, {
|
|
|
126
129
|
facetOrigin: () => facetOrigin,
|
|
127
130
|
fanPageBounds: () => fanPageBounds,
|
|
128
131
|
fanPageEtag: () => fanPageEtag,
|
|
132
|
+
findStalledRuns: () => findStalledRuns,
|
|
129
133
|
foldedCount: () => foldedCount,
|
|
130
134
|
handoffHandlerId: () => handoffHandlerId,
|
|
131
135
|
handoffName: () => handoffName,
|
|
@@ -180,6 +184,7 @@ __export(index_exports, {
|
|
|
180
184
|
runStepHandler: () => runStepHandler,
|
|
181
185
|
runValueFacetsFromRuns: () => runValueFacetsFromRuns,
|
|
182
186
|
sanitizeQueueToken: () => sanitizeQueueToken,
|
|
187
|
+
scheduleRunIdAt: () => scheduleRunIdAt,
|
|
183
188
|
scheduledRunId: () => scheduledRunId,
|
|
184
189
|
singletonAdmissionDenialPatch: () => singletonAdmissionDenialPatch,
|
|
185
190
|
singletonAdmissionTags: () => singletonAdmissionTags,
|
|
@@ -478,6 +483,31 @@ var FatalError = class extends Error {
|
|
|
478
483
|
this.details = details;
|
|
479
484
|
}
|
|
480
485
|
};
|
|
486
|
+
var SchemaValidationError = class extends FatalError {
|
|
487
|
+
static {
|
|
488
|
+
__name(this, "SchemaValidationError");
|
|
489
|
+
}
|
|
490
|
+
static code = "schema_validation";
|
|
491
|
+
issues;
|
|
492
|
+
/**
|
|
493
|
+
* @param label What was validated, e.g. `workflow "checkout" input`; the message is
|
|
494
|
+
* `invalid <label>: <path>: <message>; …`.
|
|
495
|
+
* @param summary Overrides the `<path>: <message>; …` part, for issues whose messages already
|
|
496
|
+
* name their field (class-validator's `orderId must be a string`).
|
|
497
|
+
*/
|
|
498
|
+
constructor(label, issues, code = "schema_validation", summary) {
|
|
499
|
+
const normalized = issues.map((issue) => ({
|
|
500
|
+
message: issue.message,
|
|
501
|
+
path: (issue.path ?? []).map((segment) => String(typeof segment === "object" && segment !== null ? segment.key : segment))
|
|
502
|
+
}));
|
|
503
|
+
const text = summary ?? normalized.map((issue) => issue.path.length > 0 ? `${issue.path.join(".")}: ${issue.message}` : issue.message).join("; ");
|
|
504
|
+
super(`invalid ${label}: ${text}`, code, {
|
|
505
|
+
issues: normalized
|
|
506
|
+
});
|
|
507
|
+
this.name = "SchemaValidationError";
|
|
508
|
+
this.issues = normalized;
|
|
509
|
+
}
|
|
510
|
+
};
|
|
481
511
|
var ConcurrencyLimitError = class extends Error {
|
|
482
512
|
static {
|
|
483
513
|
__name(this, "ConcurrencyLimitError");
|
|
@@ -2209,9 +2239,14 @@ function liveHandoff(runId, checkpoints) {
|
|
|
2209
2239
|
return live;
|
|
2210
2240
|
}
|
|
2211
2241
|
__name(liveHandoff, "liveHandoff");
|
|
2242
|
+
var SCHEMA_VALIDATION_CODES = /* @__PURE__ */ new Set([
|
|
2243
|
+
"invalid_input",
|
|
2244
|
+
"invalid_output"
|
|
2245
|
+
]);
|
|
2212
2246
|
function routeFor(routes, error2) {
|
|
2213
2247
|
if (!routes?.length || error2.code && UNROUTABLE_CODES.has(error2.code)) return void 0;
|
|
2214
|
-
|
|
2248
|
+
const { code } = error2;
|
|
2249
|
+
return (code ? routes.find((r) => r.code === code) : void 0) ?? (code && SCHEMA_VALIDATION_CODES.has(code) ? routes.find((r) => r.code === "schema_validation") : void 0) ?? routes.find((r) => r.code === void 0);
|
|
2215
2250
|
}
|
|
2216
2251
|
__name(routeFor, "routeFor");
|
|
2217
2252
|
function assertRoutes(workflow, routes) {
|
|
@@ -2483,9 +2518,13 @@ __name(scheduleRunIdsAt, "scheduleRunIdsAt");
|
|
|
2483
2518
|
async function runSchedules(engine, schedules, nowMs, opts) {
|
|
2484
2519
|
const random = opts?.random ?? Math.random;
|
|
2485
2520
|
const sleep = opts?.sleep ?? defaultSleep;
|
|
2521
|
+
const settled = settledIdsFor(engine);
|
|
2486
2522
|
const ids = [];
|
|
2487
2523
|
for (const s of schedules) {
|
|
2524
|
+
if (engine.isDraining?.()) break;
|
|
2488
2525
|
if (s.paused) continue;
|
|
2526
|
+
const candidates = scheduleRunIdsAt(s, nowMs).filter((id) => !settled.get(s.key)?.has(id));
|
|
2527
|
+
if (candidates.length === 0) continue;
|
|
2489
2528
|
if (s.overlap === "skip" && s.everyMs) {
|
|
2490
2529
|
const prevBucket = Math.floor(nowMs / s.everyMs) - 1;
|
|
2491
2530
|
if (prevBucket >= 0) {
|
|
@@ -2494,14 +2533,44 @@ async function runSchedules(engine, schedules, nowMs, opts) {
|
|
|
2494
2533
|
}
|
|
2495
2534
|
}
|
|
2496
2535
|
if (s.jitter && s.jitter > 0) await sleep(Math.floor(random() * s.jitter));
|
|
2497
|
-
for (const runId of
|
|
2498
|
-
|
|
2536
|
+
for (const runId of candidates) {
|
|
2537
|
+
if (engine.isDraining?.()) break;
|
|
2538
|
+
const existing = await engine.getRun(runId);
|
|
2539
|
+
remember(settled, s.key, runId);
|
|
2540
|
+
if (existing) continue;
|
|
2541
|
+
await engine.start(workflowName(s.workflow), s.input, runId, {
|
|
2542
|
+
namespace: s.namespace
|
|
2543
|
+
});
|
|
2499
2544
|
ids.push(runId);
|
|
2500
2545
|
}
|
|
2501
2546
|
}
|
|
2502
2547
|
return ids;
|
|
2503
2548
|
}
|
|
2504
2549
|
__name(runSchedules, "runSchedules");
|
|
2550
|
+
var settledRunIds = /* @__PURE__ */ new WeakMap();
|
|
2551
|
+
function settledIdsFor(engine) {
|
|
2552
|
+
let memo = settledRunIds.get(engine);
|
|
2553
|
+
if (memo === void 0) {
|
|
2554
|
+
memo = /* @__PURE__ */ new Map();
|
|
2555
|
+
settledRunIds.set(engine, memo);
|
|
2556
|
+
}
|
|
2557
|
+
return memo;
|
|
2558
|
+
}
|
|
2559
|
+
__name(settledIdsFor, "settledIdsFor");
|
|
2560
|
+
function remember(memo, key, runId) {
|
|
2561
|
+
const seen = memo.get(key);
|
|
2562
|
+
if (seen === void 0) {
|
|
2563
|
+
memo.set(key, /* @__PURE__ */ new Set([
|
|
2564
|
+
runId
|
|
2565
|
+
]));
|
|
2566
|
+
return;
|
|
2567
|
+
}
|
|
2568
|
+
seen.add(runId);
|
|
2569
|
+
if (seen.size > 64) memo.set(key, /* @__PURE__ */ new Set([
|
|
2570
|
+
runId
|
|
2571
|
+
]));
|
|
2572
|
+
}
|
|
2573
|
+
__name(remember, "remember");
|
|
2505
2574
|
|
|
2506
2575
|
// src/schedules.ts
|
|
2507
2576
|
var ScheduleAlreadyExistsError = class extends Error {
|
|
@@ -3017,6 +3086,66 @@ var SingletonGate = class {
|
|
|
3017
3086
|
}
|
|
3018
3087
|
};
|
|
3019
3088
|
|
|
3089
|
+
// src/stalled-runs.ts
|
|
3090
|
+
var DEFAULT_STALLED_AFTER_MS = 15 * 6e4;
|
|
3091
|
+
async function findStalledRuns(store, options = {}) {
|
|
3092
|
+
const now = options.now ?? Date.now();
|
|
3093
|
+
const thresholdMs = options.thresholdMs ?? DEFAULT_STALLED_AFTER_MS;
|
|
3094
|
+
const scan = Math.max(1, options.scan ?? 1e3);
|
|
3095
|
+
const pageSize = Math.min(200, scan);
|
|
3096
|
+
const stalled = [];
|
|
3097
|
+
for (let offset = 0; offset < scan; offset += pageSize) {
|
|
3098
|
+
const limit = Math.min(pageSize, scan - offset);
|
|
3099
|
+
const page = await store.listRuns({
|
|
3100
|
+
statuses: [
|
|
3101
|
+
"running",
|
|
3102
|
+
"suspended"
|
|
3103
|
+
],
|
|
3104
|
+
...options.namespace !== void 0 ? {
|
|
3105
|
+
namespace: options.namespace
|
|
3106
|
+
} : {},
|
|
3107
|
+
limit,
|
|
3108
|
+
offset
|
|
3109
|
+
});
|
|
3110
|
+
for (const run of page) {
|
|
3111
|
+
const ageMs = now - run.updatedAt.getTime();
|
|
3112
|
+
if (ageMs < thresholdMs) continue;
|
|
3113
|
+
const info2 = await stalledInfo(store, run, now, thresholdMs, ageMs);
|
|
3114
|
+
if (info2) stalled.push(info2);
|
|
3115
|
+
}
|
|
3116
|
+
if (page.length < limit) break;
|
|
3117
|
+
}
|
|
3118
|
+
return stalled;
|
|
3119
|
+
}
|
|
3120
|
+
__name(findStalledRuns, "findStalledRuns");
|
|
3121
|
+
async function stalledInfo(store, run, now, thresholdMs, ageMs) {
|
|
3122
|
+
let stalePending = null;
|
|
3123
|
+
for (const cp of await store.listCheckpoints(run.id)) {
|
|
3124
|
+
if (cp.kind !== "remote" || cp.status !== "pending") continue;
|
|
3125
|
+
const stepAgeMs = now - cp.enqueuedAt.getTime();
|
|
3126
|
+
if (stepAgeMs < thresholdMs) continue;
|
|
3127
|
+
if (cp.wakeAt != null && cp.wakeAt > now) continue;
|
|
3128
|
+
if (!stalePending || stepAgeMs > stalePending.ageMs) {
|
|
3129
|
+
stalePending = {
|
|
3130
|
+
seq: cp.seq,
|
|
3131
|
+
name: cp.name,
|
|
3132
|
+
attempts: cp.attempts,
|
|
3133
|
+
ageMs: stepAgeMs,
|
|
3134
|
+
leaseUntil: cp.wakeAt ?? null
|
|
3135
|
+
};
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
3138
|
+
const wakeForever = run.status === "suspended" && run.wakeAt == null;
|
|
3139
|
+
if (!wakeForever && !stalePending) return null;
|
|
3140
|
+
return {
|
|
3141
|
+
run,
|
|
3142
|
+
ageMs,
|
|
3143
|
+
wakeForever,
|
|
3144
|
+
stalePending
|
|
3145
|
+
};
|
|
3146
|
+
}
|
|
3147
|
+
__name(stalledInfo, "stalledInfo");
|
|
3148
|
+
|
|
3020
3149
|
// src/transport-pool.ts
|
|
3021
3150
|
var TransportPool = class {
|
|
3022
3151
|
static {
|
|
@@ -3240,11 +3369,6 @@ function normalizeChildOptions(options) {
|
|
|
3240
3369
|
} : options ?? {};
|
|
3241
3370
|
}
|
|
3242
3371
|
__name(normalizeChildOptions, "normalizeChildOptions");
|
|
3243
|
-
function formatIssuePath(path) {
|
|
3244
|
-
if (!path || path.length === 0) return "(root)";
|
|
3245
|
-
return path.map((segment) => String(typeof segment === "object" ? segment.key : segment)).join(".");
|
|
3246
|
-
}
|
|
3247
|
-
__name(formatIssuePath, "formatIssuePath");
|
|
3248
3372
|
function resolveStepPolicy(config, opts) {
|
|
3249
3373
|
const policy = {};
|
|
3250
3374
|
const retries = opts?.retries ?? config?.retries;
|
|
@@ -4076,11 +4200,7 @@ function createWorkflowCtx(host, runId, compensations, workflow = "", searchAttr
|
|
|
4076
4200
|
throw new Error(`workflow "${workflow}": ctx.upsertSearchAttributes schema validation must be synchronous (the searchAttributes schema's \`~standard.validate\` returned a Promise) \u2014 replay-determinism requires a sync validator; drop any async refinement from the schema passed to @Workflow({ searchAttributes }).`);
|
|
4077
4201
|
}
|
|
4078
4202
|
if (result.issues) {
|
|
4079
|
-
|
|
4080
|
-
...new Set(result.issues.map((issue) => formatIssuePath(issue.path)))
|
|
4081
|
-
];
|
|
4082
|
-
const details = result.issues.map((issue) => `${formatIssuePath(issue.path)}: ${issue.message}`).join("; ");
|
|
4083
|
-
throw new Error(`workflow "${workflow}": ctx.upsertSearchAttributes rejected by its searchAttributes schema \u2014 offending key(s) ${offendingKeys.join(", ")} \u2014 ${details}`);
|
|
4203
|
+
throw new SchemaValidationError(`workflow "${workflow}" ctx.upsertSearchAttributes`, result.issues);
|
|
4084
4204
|
}
|
|
4085
4205
|
}
|
|
4086
4206
|
await host.upsertSearchAttributes(runId, attrs);
|
|
@@ -4374,6 +4494,18 @@ var WorkflowEngine = class {
|
|
|
4374
4494
|
stepQueue = /* @__PURE__ */ new Map();
|
|
4375
4495
|
/** Executions currently in flight, so a graceful shutdown can wait for them to settle. */
|
|
4376
4496
|
inflight = /* @__PURE__ */ new Set();
|
|
4497
|
+
/**
|
|
4498
|
+
* The run lease of each execution in flight on THIS instance, by run id — renewed while it runs
|
|
4499
|
+
* and checked by every settle (the zombie fence, see {@link ExecutionLease}). Also what `drain()`
|
|
4500
|
+
* releases when its budget runs out.
|
|
4501
|
+
*/
|
|
4502
|
+
executionLeases = /* @__PURE__ */ new Map();
|
|
4503
|
+
/** Pager hooks for stranded runs (see {@link onStalled}). */
|
|
4504
|
+
stalledListeners = /* @__PURE__ */ new Set();
|
|
4505
|
+
/** Runs already paged about, by id → the `updatedAt` of the paged episode (see {@link sweepStalled}). */
|
|
4506
|
+
notifiedStalled = /* @__PURE__ */ new Map();
|
|
4507
|
+
lastStalledSweepAt = Number.NEGATIVE_INFINITY;
|
|
4508
|
+
stalledAfterMs;
|
|
4377
4509
|
draining = false;
|
|
4378
4510
|
/** Wait already spent on this shutdown before `drain()` (see `beginDrain`). */
|
|
4379
4511
|
drainSpentMs = 0;
|
|
@@ -4398,6 +4530,8 @@ var WorkflowEngine = class {
|
|
|
4398
4530
|
this.maxRecoveryAttempts = deps.maxRecoveryAttempts;
|
|
4399
4531
|
this.remoteAdvanceSilenceMs = deps.remoteAdvanceSilenceMs;
|
|
4400
4532
|
this.reconcileMs = deps.reconcileMs === void 0 ? 3e5 : deps.reconcileMs || void 0;
|
|
4533
|
+
const stalledAfter = deps.stalledAfterMs;
|
|
4534
|
+
this.stalledAfterMs = stalledAfter !== void 0 && Number.isFinite(stalledAfter) ? Math.max(6e4, stalledAfter) : DEFAULT_STALLED_AFTER_MS;
|
|
4401
4535
|
const batch = deps.timerBatchSize;
|
|
4402
4536
|
this.timerBatchSize = batch !== void 0 && Number.isFinite(batch) && batch >= 1 ? Math.floor(batch) : 500;
|
|
4403
4537
|
this.eventBufferTtlMs = deps.eventBufferTtlMs || void 0;
|
|
@@ -4741,6 +4875,64 @@ var WorkflowEngine = class {
|
|
|
4741
4875
|
this.deadListeners.add(listener);
|
|
4742
4876
|
return () => this.deadListeners.delete(listener);
|
|
4743
4877
|
}
|
|
4878
|
+
/**
|
|
4879
|
+
* Register a pager hook for STRANDED runs: an in-flight run untouched past `stalledAfterMs` that is
|
|
4880
|
+
* either suspended with NO wake timer (wake-forever: nothing will ever re-drive it) or parked on an
|
|
4881
|
+
* old pending remote step with no live lease (dispatched and, most likely, lost). The timer poller
|
|
4882
|
+
* sweeps for these ({@link sweepStalled}, throttled to one pass a minute) and notifies each episode
|
|
4883
|
+
* ONCE per instance: a run is paged again only after it makes progress and strands again. Wire your
|
|
4884
|
+
* alerting here like {@link onDead}. A listener that throws never breaks the sweep. Returns an
|
|
4885
|
+
* unsubscribe function.
|
|
4886
|
+
*/
|
|
4887
|
+
onStalled(listener) {
|
|
4888
|
+
this.stalledListeners.add(listener);
|
|
4889
|
+
return () => this.stalledListeners.delete(listener);
|
|
4890
|
+
}
|
|
4891
|
+
/**
|
|
4892
|
+
* Detect stranded in-flight runs and notify the {@link onStalled} listeners. Self-throttled to one
|
|
4893
|
+
* pass a minute, skipped while draining, and a no-op without listeners — call it every tick (the
|
|
4894
|
+
* NestJS timer poller does). Scoped to this engine's `namespace` (an operator engine with no
|
|
4895
|
+
* namespace sweeps every namespace). Returns the runs notified THIS pass.
|
|
4896
|
+
*/
|
|
4897
|
+
async sweepStalled(now = this.clock()) {
|
|
4898
|
+
if (this.stalledListeners.size === 0 || this.draining) return [];
|
|
4899
|
+
if (now - this.lastStalledSweepAt < 6e4) return [];
|
|
4900
|
+
this.lastStalledSweepAt = now;
|
|
4901
|
+
const notified = [];
|
|
4902
|
+
for (const info2 of await this.listStalledRuns({
|
|
4903
|
+
now
|
|
4904
|
+
})) {
|
|
4905
|
+
const episode = info2.run.updatedAt.getTime();
|
|
4906
|
+
if (this.notifiedStalled.get(info2.run.id) === episode) continue;
|
|
4907
|
+
this.notifiedStalled.set(info2.run.id, episode);
|
|
4908
|
+
if (this.notifiedStalled.size > 1e3) this.notifiedStalled.clear();
|
|
4909
|
+
for (const listener of this.stalledListeners) {
|
|
4910
|
+
try {
|
|
4911
|
+
listener(info2);
|
|
4912
|
+
} catch {
|
|
4913
|
+
}
|
|
4914
|
+
}
|
|
4915
|
+
notified.push(info2);
|
|
4916
|
+
}
|
|
4917
|
+
return notified;
|
|
4918
|
+
}
|
|
4919
|
+
/**
|
|
4920
|
+
* The stranded runs right now, without notifying anyone: the query behind {@link sweepStalled} and
|
|
4921
|
+
* the CLI's `durable inspect --stale`. A `running`/`suspended` run qualifies when it has been
|
|
4922
|
+
* untouched for `thresholdMs` (default `stalledAfterMs`) AND either it is suspended with no wake
|
|
4923
|
+
* timer, or its oldest pending remote step is older than the threshold with no lease in the future
|
|
4924
|
+
* (a step with a `timeoutMs`, a heartbeat or `remoteRedispatchMs` keeps a lease; a long
|
|
4925
|
+
* `ctx.sleep` has a future `wakeAt`; neither is stranded). Scans up to `scan` candidates (default
|
|
4926
|
+
* 1000), newest first, in this engine's namespace.
|
|
4927
|
+
*/
|
|
4928
|
+
async listStalledRuns(options = {}) {
|
|
4929
|
+
return findStalledRuns(this.store, {
|
|
4930
|
+
now: options.now ?? this.clock(),
|
|
4931
|
+
thresholdMs: options.thresholdMs ?? this.stalledAfterMs,
|
|
4932
|
+
scan: options.scan,
|
|
4933
|
+
namespace: this.namespace
|
|
4934
|
+
});
|
|
4935
|
+
}
|
|
4744
4936
|
notifyDead(run) {
|
|
4745
4937
|
for (const fn of this.deadListeners) {
|
|
4746
4938
|
try {
|
|
@@ -4899,6 +5091,11 @@ var WorkflowEngine = class {
|
|
|
4899
5091
|
getRun(runId) {
|
|
4900
5092
|
return this.store.getRun(runId);
|
|
4901
5093
|
}
|
|
5094
|
+
/** A run's full checkpoint timeline, ordered by seq. A thin pass-through to the store (it is what
|
|
5095
|
+
* the testing kit's `captureHistory` reads, next to {@link getRun}). */
|
|
5096
|
+
listCheckpoints(runId) {
|
|
5097
|
+
return this.store.listCheckpoints(runId);
|
|
5098
|
+
}
|
|
4902
5099
|
/**
|
|
4903
5100
|
* Re-execute a run's body (replaying its checkpoints). This is the EXPLICIT entry point: besides
|
|
4904
5101
|
* driving a live run, it re-drives a `failed` run — the manual retry (prefer {@link requeue}, which
|
|
@@ -4984,6 +5181,8 @@ var WorkflowEngine = class {
|
|
|
4984
5181
|
* diagnosis.
|
|
4985
5182
|
*/
|
|
4986
5183
|
async releaseLeaseBestEffort(runId) {
|
|
5184
|
+
if (this.isFenced(runId)) return;
|
|
5185
|
+
this.handOffLease(runId);
|
|
4987
5186
|
try {
|
|
4988
5187
|
await this.store.releaseRunLock(runId);
|
|
4989
5188
|
} catch (err) {
|
|
@@ -5207,6 +5406,10 @@ var WorkflowEngine = class {
|
|
|
5207
5406
|
for (const rearm of this.drainRearms) rearm();
|
|
5208
5407
|
}
|
|
5209
5408
|
}
|
|
5409
|
+
/** True once {@link beginDrain} (or {@link drain}) has been called. */
|
|
5410
|
+
isDraining() {
|
|
5411
|
+
return this.draining;
|
|
5412
|
+
}
|
|
5210
5413
|
/**
|
|
5211
5414
|
* Graceful shutdown: stop picking up new runs (recovery/timer become no-ops) and wait for
|
|
5212
5415
|
* in-flight executions to settle, up to `timeoutMs`. Call from your app's shutdown hook so a
|
|
@@ -5233,17 +5436,47 @@ var WorkflowEngine = class {
|
|
|
5233
5436
|
rearm();
|
|
5234
5437
|
});
|
|
5235
5438
|
this.drainRearms.add(rearm);
|
|
5439
|
+
let settled = false;
|
|
5236
5440
|
try {
|
|
5237
5441
|
await Promise.race([
|
|
5238
5442
|
Promise.allSettled([
|
|
5239
5443
|
...this.inflight
|
|
5240
|
-
])
|
|
5444
|
+
]).then(() => {
|
|
5445
|
+
settled = true;
|
|
5446
|
+
}),
|
|
5241
5447
|
timer
|
|
5242
5448
|
]);
|
|
5243
5449
|
} finally {
|
|
5244
5450
|
this.drainRearms.delete(rearm);
|
|
5245
5451
|
clearTimeout(timeout);
|
|
5246
5452
|
}
|
|
5453
|
+
if (!settled) await this.releaseInflightLeases();
|
|
5454
|
+
}
|
|
5455
|
+
/**
|
|
5456
|
+
* The drain budget ran out with executions still in flight: release their recovery leases so
|
|
5457
|
+
* another instance re-drives those runs on its next recovery pass, instead of waiting out
|
|
5458
|
+
* `leaseMs` after this process exits. Each still-running turn is FENCED first, so whatever it
|
|
5459
|
+
* computes after this point is never written over the new owner's work (see settleRun). Its
|
|
5460
|
+
* completed steps are checkpointed, so the new owner replays them; a step still executing here
|
|
5461
|
+
* may run again there, exactly as after a crash. The release is owner-scoped where the store
|
|
5462
|
+
* supports it (only a lease this instance still holds is cleared). Best effort: never throws.
|
|
5463
|
+
*/
|
|
5464
|
+
async releaseInflightLeases() {
|
|
5465
|
+
const released = [];
|
|
5466
|
+
for (const [runId, lease] of [
|
|
5467
|
+
...this.executionLeases
|
|
5468
|
+
]) {
|
|
5469
|
+
if (lease.lost || lease.handedOff || lease.done) continue;
|
|
5470
|
+
lease.lost = true;
|
|
5471
|
+
try {
|
|
5472
|
+
await this.store.releaseRunLock(runId, this.instanceId);
|
|
5473
|
+
released.push(runId);
|
|
5474
|
+
} catch {
|
|
5475
|
+
}
|
|
5476
|
+
}
|
|
5477
|
+
if (released.length > 0) {
|
|
5478
|
+
console.warn(`[nestjs-durable] drain timed out with ${released.length} run(s) still executing; released their leases so another instance recovers them now: ${released.join(", ")}`);
|
|
5479
|
+
}
|
|
5247
5480
|
}
|
|
5248
5481
|
/**
|
|
5249
5482
|
* Cancel in-flight runs that have outlived their workflow's `executionTimeout`. Call it from the
|
|
@@ -6733,7 +6966,10 @@ var WorkflowEngine = class {
|
|
|
6733
6966
|
}
|
|
6734
6967
|
async singletonWaitResult(run) {
|
|
6735
6968
|
const current = await this.store.getRun(run.id);
|
|
6736
|
-
|
|
6969
|
+
if (!this.isFenced(run.id)) {
|
|
6970
|
+
this.handOffLease(run.id);
|
|
6971
|
+
await this.store.releaseRunLock(run.id);
|
|
6972
|
+
}
|
|
6737
6973
|
if (!current) throw new Error(`run ${run.id} was deleted during singleton admission`);
|
|
6738
6974
|
if (current.status === "suspended") {
|
|
6739
6975
|
this.emit({
|
|
@@ -6761,10 +6997,7 @@ var WorkflowEngine = class {
|
|
|
6761
6997
|
};
|
|
6762
6998
|
}
|
|
6763
6999
|
}
|
|
6764
|
-
const
|
|
6765
|
-
void this.store.renewRunLock(run.id, this.instanceId, this.clock() + this.leaseMs).catch(() => void 0);
|
|
6766
|
-
}, Math.max(50, Math.floor(this.leaseMs / 2)));
|
|
6767
|
-
renew.unref?.();
|
|
7000
|
+
const held = this.holdExecutionLease(run.id);
|
|
6768
7001
|
try {
|
|
6769
7002
|
const result = registered?.remote ? await this.runRemoteExecution(run, registered) : await this.runExecution(run, fn, registered);
|
|
6770
7003
|
if (registered?.singleton && (result.status === "completed" || result.status === "failed" || result.status === "cancelled" || result.status === "dead")) {
|
|
@@ -6772,10 +7005,59 @@ var WorkflowEngine = class {
|
|
|
6772
7005
|
}
|
|
6773
7006
|
return result;
|
|
6774
7007
|
} finally {
|
|
6775
|
-
|
|
7008
|
+
held.stop();
|
|
6776
7009
|
}
|
|
6777
7010
|
}
|
|
6778
7011
|
/**
|
|
7012
|
+
* Renew `runId`'s lease for as long as this execution holds it, and register it in
|
|
7013
|
+
* {@link executionLeases} so settles can check the zombie fence and `drain()` can release it.
|
|
7014
|
+
*
|
|
7015
|
+
* A renewal that returns `false` means the lease is gone: this turn stalled past `leaseMs` and
|
|
7016
|
+
* another instance reclaimed the run, so this executor is now a zombie. It is FENCED (`lost`): its
|
|
7017
|
+
* settles degrade to an echo of the store's state instead of clobbering the new owner's writes, and
|
|
7018
|
+
* its own lease release is skipped (it would free the new owner's lease). A renewal that THROWS (a
|
|
7019
|
+
* transient store error) is not evidence of a takeover, so it stays swallowed and the next
|
|
7020
|
+
* interval retries. A lease this execution released on purpose (`handedOff`) is not renewed, and a
|
|
7021
|
+
* renewal resolving after the execution ended (`done`) fences nothing.
|
|
7022
|
+
*/
|
|
7023
|
+
holdExecutionLease(runId) {
|
|
7024
|
+
const lease = {
|
|
7025
|
+
lost: false,
|
|
7026
|
+
handedOff: false,
|
|
7027
|
+
done: false
|
|
7028
|
+
};
|
|
7029
|
+
this.executionLeases.set(runId, lease);
|
|
7030
|
+
const renew = setInterval(() => {
|
|
7031
|
+
if (lease.handedOff || lease.lost || lease.done) return;
|
|
7032
|
+
void this.store.renewRunLock(runId, this.instanceId, this.clock() + this.leaseMs).then((renewed) => {
|
|
7033
|
+
if (renewed || lease.handedOff || lease.lost || lease.done) return;
|
|
7034
|
+
lease.lost = true;
|
|
7035
|
+
console.warn(`[nestjs-durable] lost the lease of run ${runId} while executing it (another instance took it over after it expired); this instance will not write the run's outcome`);
|
|
7036
|
+
}).catch(() => void 0);
|
|
7037
|
+
}, Math.max(50, Math.floor(this.leaseMs / 2)));
|
|
7038
|
+
renew.unref?.();
|
|
7039
|
+
return {
|
|
7040
|
+
lease,
|
|
7041
|
+
stop: /* @__PURE__ */ __name(() => {
|
|
7042
|
+
clearInterval(renew);
|
|
7043
|
+
lease.done = true;
|
|
7044
|
+
if (this.executionLeases.get(runId) === lease) this.executionLeases.delete(runId);
|
|
7045
|
+
}, "stop")
|
|
7046
|
+
};
|
|
7047
|
+
}
|
|
7048
|
+
/** Whether `runId`'s execution on this instance lost its lease (see {@link holdExecutionLease}). */
|
|
7049
|
+
isFenced(runId) {
|
|
7050
|
+
return this.executionLeases.get(runId)?.lost === true;
|
|
7051
|
+
}
|
|
7052
|
+
/**
|
|
7053
|
+
* Mark `runId`'s lease as given away on purpose by its own execution (released mid-turn), so a
|
|
7054
|
+
* renewal that now fails is not read as a takeover.
|
|
7055
|
+
*/
|
|
7056
|
+
handOffLease(runId) {
|
|
7057
|
+
const lease = this.executionLeases.get(runId);
|
|
7058
|
+
if (lease) lease.handedOff = true;
|
|
7059
|
+
}
|
|
7060
|
+
/**
|
|
6779
7061
|
* Advance a remote (cross-SDK) workflow one turn: hand its history to the executor (which dispatches
|
|
6780
7062
|
* a workflow task to the worker and awaits its replay) and apply the decision. Mirrors
|
|
6781
7063
|
* {@link runExecution}'s settle/suspend; the lease is held by {@link execute}. The result that lands
|
|
@@ -6992,6 +7274,7 @@ ${run.workflowVersion}`;
|
|
|
6992
7274
|
* keeps its own lease handling (the TS executor's `finally`, the remote path's caller).
|
|
6993
7275
|
*/
|
|
6994
7276
|
async settleRun(run, outcome) {
|
|
7277
|
+
if (this.isFenced(run.id)) return this.echoCurrentStatus(run.id);
|
|
6995
7278
|
const updatedAt = /* @__PURE__ */ new Date();
|
|
6996
7279
|
if (outcome.kind === "completed") {
|
|
6997
7280
|
const applied2 = await (this.store.updateRunIf ?? this.updateRunIfFallback).call(this.store, run.id, SETTLE_ELIGIBLE_STATUSES, {
|
|
@@ -7072,6 +7355,7 @@ ${run.workflowVersion}`;
|
|
|
7072
7355
|
}
|
|
7073
7356
|
async runRemoteExecution(run, registered) {
|
|
7074
7357
|
if (run.status === "cancelling") {
|
|
7358
|
+
if (this.isFenced(run.id)) return this.echoCurrentStatus(run.id);
|
|
7075
7359
|
const error2 = {
|
|
7076
7360
|
message: "cancelled"
|
|
7077
7361
|
};
|
|
@@ -7080,6 +7364,7 @@ ${run.workflowVersion}`;
|
|
|
7080
7364
|
error: error2,
|
|
7081
7365
|
updatedAt: /* @__PURE__ */ new Date()
|
|
7082
7366
|
});
|
|
7367
|
+
if (this.isFenced(run.id)) return this.echoCurrentStatus(run.id);
|
|
7083
7368
|
this.emit({
|
|
7084
7369
|
type: "run.failed",
|
|
7085
7370
|
runId: run.id,
|
|
@@ -7126,6 +7411,7 @@ ${run.workflowVersion}`;
|
|
|
7126
7411
|
}
|
|
7127
7412
|
const history = await this.remoteHistory(run.id, checkpoints);
|
|
7128
7413
|
if (remote.executor.dispatch) {
|
|
7414
|
+
if (this.isFenced(run.id)) return this.echoCurrentStatus(run.id);
|
|
7129
7415
|
const taskId = `${run.id}:wf:${globalThis.crypto.randomUUID().slice(0, 8)}`;
|
|
7130
7416
|
const silenceMs = this.remoteAdvanceSilenceMs;
|
|
7131
7417
|
const wakeAt = silenceMs != null ? this.clock() + silenceMs : void 0;
|
|
@@ -7141,6 +7427,7 @@ ${run.workflowVersion}`;
|
|
|
7141
7427
|
workflow: run.workflow,
|
|
7142
7428
|
namespace: run.namespace
|
|
7143
7429
|
});
|
|
7430
|
+
this.handOffLease(run.id);
|
|
7144
7431
|
await this.store.releaseRunLock(run.id);
|
|
7145
7432
|
await remote.executor.dispatch(run, history, taskId);
|
|
7146
7433
|
return {
|
|
@@ -7169,6 +7456,7 @@ ${run.workflowVersion}`;
|
|
|
7169
7456
|
message: err instanceof Error ? err.message : String(err),
|
|
7170
7457
|
stack: err instanceof Error ? err.stack : void 0
|
|
7171
7458
|
};
|
|
7459
|
+
if (this.isFenced(run.id)) return this.echoCurrentStatus(run.id);
|
|
7172
7460
|
await this.store.updateRun(run.id, {
|
|
7173
7461
|
status: "failed",
|
|
7174
7462
|
error: error2,
|
|
@@ -7197,6 +7485,7 @@ ${run.workflowVersion}`;
|
|
|
7197
7485
|
* — the caller has already ensured this is the decision for the currently-awaited turn.
|
|
7198
7486
|
*/
|
|
7199
7487
|
async applyDecision(run, decision) {
|
|
7488
|
+
if (this.isFenced(run.id)) return this.echoCurrentStatus(run.id);
|
|
7200
7489
|
if (decision.status === "completed") {
|
|
7201
7490
|
await this.applyCommands(run, decision.commands);
|
|
7202
7491
|
return this.settleRun(run, {
|
|
@@ -7666,6 +7955,7 @@ ${run.workflowVersion}`;
|
|
|
7666
7955
|
* cancel flag stays too, re-derived from the status on any later re-drive regardless.
|
|
7667
7956
|
*/
|
|
7668
7957
|
async compensateAndCancel(run, compensations, replay) {
|
|
7958
|
+
if (this.isFenced(run.id)) return this.echoCurrentStatus(run.id);
|
|
7669
7959
|
const pending = await this.unwindCompensations(run, compensations, replay);
|
|
7670
7960
|
if (pending) return {
|
|
7671
7961
|
runId: run.id,
|
|
@@ -7757,6 +8047,7 @@ ${run.workflowVersion}`;
|
|
|
7757
8047
|
});
|
|
7758
8048
|
} catch (err) {
|
|
7759
8049
|
if (err instanceof ContinueAsNew) {
|
|
8050
|
+
if (this.isFenced(run.id)) return await this.echoCurrentStatus(run.id);
|
|
7760
8051
|
if (this.cancelRequested.has(run.id)) {
|
|
7761
8052
|
return await this.compensateAndCancel(run, compensations, replay);
|
|
7762
8053
|
}
|
|
@@ -8208,6 +8499,7 @@ ${run.workflowVersion}`;
|
|
|
8208
8499
|
* own `finally` also releases (the run-execution path) is safe.
|
|
8209
8500
|
*/
|
|
8210
8501
|
async parkBlocked(run, blocked) {
|
|
8502
|
+
if (this.isFenced(run.id)) return this.echoCurrentStatus(run.id);
|
|
8211
8503
|
const error2 = {
|
|
8212
8504
|
message: blocked.reason,
|
|
8213
8505
|
code: blocked.code,
|
|
@@ -8794,10 +9086,7 @@ ${run.workflowVersion}`;
|
|
|
8794
9086
|
if (!await this.store.tryLockRun(decision.runId, this.instanceId, nowMs + this.leaseMs, nowMs)) {
|
|
8795
9087
|
return;
|
|
8796
9088
|
}
|
|
8797
|
-
const
|
|
8798
|
-
void this.store.renewRunLock(decision.runId, this.instanceId, this.clock() + this.leaseMs).catch(() => void 0);
|
|
8799
|
-
}, Math.max(50, Math.floor(this.leaseMs / 2)));
|
|
8800
|
-
renew.unref?.();
|
|
9089
|
+
const held = this.holdExecutionLease(decision.runId);
|
|
8801
9090
|
try {
|
|
8802
9091
|
const fresh = await this.store.getRun(decision.runId);
|
|
8803
9092
|
if (!fresh || fresh.awaitingDecisionTaskId !== decision.taskId) return;
|
|
@@ -8812,8 +9101,9 @@ ${run.workflowVersion}`;
|
|
|
8812
9101
|
fresh.awaitingDecisionTaskId = void 0;
|
|
8813
9102
|
await this.applyDecision(fresh, decision);
|
|
8814
9103
|
} finally {
|
|
8815
|
-
|
|
8816
|
-
|
|
9104
|
+
const fenced = held.lease.lost;
|
|
9105
|
+
held.stop();
|
|
9106
|
+
if (!fenced) await this.store.releaseRunLock(decision.runId);
|
|
8817
9107
|
}
|
|
8818
9108
|
}
|
|
8819
9109
|
/** Earliest clock time a suspended turn's deadline is worth LOOKING at again (`runId` → clock ms) —
|
|
@@ -9009,6 +9299,9 @@ function childIdsOf(checkpoints) {
|
|
|
9009
9299
|
}
|
|
9010
9300
|
__name(childIdsOf, "childIdsOf");
|
|
9011
9301
|
|
|
9302
|
+
// src/run-history.ts
|
|
9303
|
+
var RUN_HISTORY_FIXTURE_VERSION = 1;
|
|
9304
|
+
|
|
9012
9305
|
// src/run-facets.ts
|
|
9013
9306
|
function facetOrigin(origin) {
|
|
9014
9307
|
return origin?.trim() ? origin : null;
|
|
@@ -9562,13 +9855,14 @@ var CodecStateStore = class {
|
|
|
9562
9855
|
}
|
|
9563
9856
|
inner;
|
|
9564
9857
|
codec;
|
|
9858
|
+
extended;
|
|
9565
9859
|
/** Atomic claims touch only searchable metadata and bypass payload encoding. */
|
|
9566
9860
|
tryAdmitSingleton;
|
|
9567
9861
|
/**
|
|
9568
9862
|
* Forwarded verbatim — a facet cell counts `status`/`origin`, neither of which the codec touches
|
|
9569
|
-
* (it encodes payloads: input/output/error). Bound as a
|
|
9570
|
-
* actually aggregates, so `store.runFacets?.()` still
|
|
9571
|
-
* of resolving to a silently wrong empty result.
|
|
9863
|
+
* (it encodes payloads: input/output, plus events/error under `extended` coverage). Bound as a
|
|
9864
|
+
* PROPERTY, and only when the inner store actually aggregates, so `store.runFacets?.()` still
|
|
9865
|
+
* reports absence through the wrapper instead of resolving to a silently wrong empty result.
|
|
9572
9866
|
*/
|
|
9573
9867
|
runFacets;
|
|
9574
9868
|
/**
|
|
@@ -9620,9 +9914,10 @@ var CodecStateStore = class {
|
|
|
9620
9914
|
updateSchedule;
|
|
9621
9915
|
deleteSchedule;
|
|
9622
9916
|
listSchedules;
|
|
9623
|
-
constructor(inner, codec) {
|
|
9917
|
+
constructor(inner, codec, options = {}) {
|
|
9624
9918
|
this.inner = inner;
|
|
9625
9919
|
this.codec = codec;
|
|
9920
|
+
this.extended = options.coverage === "extended";
|
|
9626
9921
|
if (inner.tryAdmitSingleton) this.tryAdmitSingleton = inner.tryAdmitSingleton.bind(inner);
|
|
9627
9922
|
const deleteRuns = inner.deleteRuns;
|
|
9628
9923
|
if (deleteRuns) this.deleteRuns = (ids) => deleteRuns.call(inner, ids);
|
|
@@ -9680,32 +9975,52 @@ var CodecStateStore = class {
|
|
|
9680
9975
|
return v === void 0 ? void 0 : this.codec.decode(v);
|
|
9681
9976
|
}
|
|
9682
9977
|
encRun(run) {
|
|
9683
|
-
|
|
9978
|
+
const next = {
|
|
9684
9979
|
...run,
|
|
9685
9980
|
input: this.enc(run.input),
|
|
9686
9981
|
output: this.enc(run.output)
|
|
9687
9982
|
};
|
|
9983
|
+
if (this.extended && run.error !== void 0) {
|
|
9984
|
+
next.error = this.enc(run.error);
|
|
9985
|
+
}
|
|
9986
|
+
return next;
|
|
9688
9987
|
}
|
|
9689
9988
|
decRun(run) {
|
|
9690
|
-
|
|
9989
|
+
if (!run) return run;
|
|
9990
|
+
const next = {
|
|
9691
9991
|
...run,
|
|
9692
9992
|
input: this.dec(run.input),
|
|
9693
9993
|
output: this.dec(run.output)
|
|
9694
9994
|
};
|
|
9995
|
+
if (this.extended && run.error !== void 0) {
|
|
9996
|
+
next.error = this.dec(run.error);
|
|
9997
|
+
}
|
|
9998
|
+
return next;
|
|
9695
9999
|
}
|
|
9696
10000
|
encCp(cp) {
|
|
9697
|
-
|
|
10001
|
+
const next = {
|
|
9698
10002
|
...cp,
|
|
9699
10003
|
input: this.enc(cp.input),
|
|
9700
10004
|
output: this.enc(cp.output)
|
|
9701
10005
|
};
|
|
10006
|
+
if (this.extended) {
|
|
10007
|
+
if (cp.events !== void 0) next.events = this.enc(cp.events);
|
|
10008
|
+
if (cp.error !== void 0) next.error = this.enc(cp.error);
|
|
10009
|
+
}
|
|
10010
|
+
return next;
|
|
9702
10011
|
}
|
|
9703
10012
|
decCp(cp) {
|
|
9704
|
-
|
|
10013
|
+
if (!cp) return cp;
|
|
10014
|
+
const next = {
|
|
9705
10015
|
...cp,
|
|
9706
10016
|
input: this.dec(cp.input),
|
|
9707
10017
|
output: this.dec(cp.output)
|
|
9708
10018
|
};
|
|
10019
|
+
if (this.extended) {
|
|
10020
|
+
if (cp.events !== void 0) next.events = this.dec(cp.events);
|
|
10021
|
+
if (cp.error !== void 0) next.error = this.dec(cp.error);
|
|
10022
|
+
}
|
|
10023
|
+
return next;
|
|
9709
10024
|
}
|
|
9710
10025
|
encSpec(spec) {
|
|
9711
10026
|
return "input" in spec ? {
|
|
@@ -9744,6 +10059,9 @@ var CodecStateStore = class {
|
|
|
9744
10059
|
};
|
|
9745
10060
|
if ("input" in patch) next.input = this.enc(patch.input);
|
|
9746
10061
|
if ("output" in patch) next.output = this.enc(patch.output);
|
|
10062
|
+
if (this.extended && patch.error !== void 0) {
|
|
10063
|
+
next.error = this.enc(patch.error);
|
|
10064
|
+
}
|
|
9747
10065
|
return next;
|
|
9748
10066
|
}
|
|
9749
10067
|
async getRun(runId) {
|
|
@@ -9779,8 +10097,8 @@ var CodecStateStore = class {
|
|
|
9779
10097
|
tryLockRun(runId, owner, leaseUntilMs, nowMs) {
|
|
9780
10098
|
return this.inner.tryLockRun(runId, owner, leaseUntilMs, nowMs);
|
|
9781
10099
|
}
|
|
9782
|
-
releaseRunLock(runId) {
|
|
9783
|
-
return this.inner.releaseRunLock(runId);
|
|
10100
|
+
releaseRunLock(runId, owner) {
|
|
10101
|
+
return owner === void 0 ? this.inner.releaseRunLock(runId) : this.inner.releaseRunLock(runId, owner);
|
|
9784
10102
|
}
|
|
9785
10103
|
renewRunLock(runId, owner, leaseUntilMs) {
|
|
9786
10104
|
return this.inner.renewRunLock(runId, owner, leaseUntilMs);
|
|
@@ -10288,9 +10606,9 @@ var InMemoryStateStore = class {
|
|
|
10288
10606
|
run.lockedUntil = leaseUntilMs;
|
|
10289
10607
|
return true;
|
|
10290
10608
|
}
|
|
10291
|
-
async releaseRunLock(runId) {
|
|
10609
|
+
async releaseRunLock(runId, owner) {
|
|
10292
10610
|
const run = this.runs.get(runId);
|
|
10293
|
-
if (run) {
|
|
10611
|
+
if (run && (owner === void 0 || run.lockedBy === owner)) {
|
|
10294
10612
|
run.lockedBy = void 0;
|
|
10295
10613
|
run.lockedUntil = void 0;
|
|
10296
10614
|
}
|
|
@@ -10641,6 +10959,7 @@ var InMemoryTransport = class {
|
|
|
10641
10959
|
ContinueAsNew,
|
|
10642
10960
|
DEFAULT_CONCURRENCY_STATUSES,
|
|
10643
10961
|
DEFAULT_PARTITION,
|
|
10962
|
+
DEFAULT_STALLED_AFTER_MS,
|
|
10644
10963
|
DURABLE_OPTIONS,
|
|
10645
10964
|
DURABLE_OPTIONS_CANONICAL,
|
|
10646
10965
|
DURABLE_STEP_CONFIG,
|
|
@@ -10664,6 +10983,7 @@ var InMemoryTransport = class {
|
|
|
10664
10983
|
NestedWorkflowCallError,
|
|
10665
10984
|
NonDeterminismError,
|
|
10666
10985
|
QueueController,
|
|
10986
|
+
RUN_HISTORY_FIXTURE_VERSION,
|
|
10667
10987
|
RUN_VALUE_FACET_LIMIT,
|
|
10668
10988
|
RUN_VALUE_FACET_SCAN,
|
|
10669
10989
|
RemoteStepError,
|
|
@@ -10681,6 +11001,7 @@ var InMemoryTransport = class {
|
|
|
10681
11001
|
ScheduleAlreadyExistsError,
|
|
10682
11002
|
ScheduleClient,
|
|
10683
11003
|
ScheduleNotFoundError,
|
|
11004
|
+
SchemaValidationError,
|
|
10684
11005
|
SignalTimeoutError,
|
|
10685
11006
|
SingletonQueueFullError,
|
|
10686
11007
|
TERMINAL_RUN_STATUSES,
|
|
@@ -10737,6 +11058,7 @@ var InMemoryTransport = class {
|
|
|
10737
11058
|
facetOrigin,
|
|
10738
11059
|
fanPageBounds,
|
|
10739
11060
|
fanPageEtag,
|
|
11061
|
+
findStalledRuns,
|
|
10740
11062
|
foldedCount,
|
|
10741
11063
|
handoffHandlerId,
|
|
10742
11064
|
handoffName,
|
|
@@ -10791,6 +11113,7 @@ var InMemoryTransport = class {
|
|
|
10791
11113
|
runStepHandler,
|
|
10792
11114
|
runValueFacetsFromRuns,
|
|
10793
11115
|
sanitizeQueueToken,
|
|
11116
|
+
scheduleRunIdAt,
|
|
10794
11117
|
scheduledRunId,
|
|
10795
11118
|
singletonAdmissionDenialPatch,
|
|
10796
11119
|
singletonAdmissionTags,
|