@dudousxd/nestjs-durable-core 0.29.1 → 0.31.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.js CHANGED
@@ -1,6 +1,161 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
+ // src/queue.ts
5
+ var QueueController = class {
6
+ static {
7
+ __name(this, "QueueController");
8
+ }
9
+ config;
10
+ clock;
11
+ inFlight = 0;
12
+ windowStart = 0;
13
+ windowCount = 0;
14
+ /** Registered waiters by id (only used when priority/fairness ordering is in play). */
15
+ waiters = /* @__PURE__ */ new Map();
16
+ /** Monotonic counter stamping each newly-seen waiter for a stable FIFO tiebreak. */
17
+ waiterSeq = 0;
18
+ /** Per-key "last served" order for round-robin fairness — higher = more recently served. */
19
+ keyServed = /* @__PURE__ */ new Map();
20
+ servedTick = 0;
21
+ constructor(config, clock) {
22
+ this.config = config;
23
+ this.clock = clock;
24
+ }
25
+ /** Whether this controller does any priority/fairness ordering (else it's a plain FIFO gate). */
26
+ get ordered() {
27
+ return this.config.fairness === "key" || this.config.order === "lifo";
28
+ }
29
+ /**
30
+ * Try to admit one unit of work. On `ok`, the caller has taken a concurrency slot and must call
31
+ * {@link release} when the step settles. On block, `retryAt` is when admission may next succeed.
32
+ *
33
+ * `item` carries optional priority/fairness hints; omitting it is the legacy FIFO path. When
34
+ * ordering is configured (or a priority is given), a blocked call REGISTERS as a waiter and is only
35
+ * admitted on a later retry once it is the rightful next under (priority desc, fairness, FIFO).
36
+ */
37
+ tryAdmit(item) {
38
+ const now = this.clock();
39
+ const rl = this.config.rateLimit;
40
+ if (rl) {
41
+ if (now - this.windowStart >= rl.periodMs) {
42
+ this.windowStart = now;
43
+ this.windowCount = 0;
44
+ }
45
+ if (this.windowCount >= rl.limit) return {
46
+ ok: false,
47
+ retryAt: this.windowStart + rl.periodMs
48
+ };
49
+ }
50
+ const usingOrder = this.ordered || item?.priority != null || item?.waiterId != null;
51
+ const waiterId = item?.waiterId;
52
+ const full = this.config.concurrency != null && this.inFlight >= this.config.concurrency;
53
+ if (full) {
54
+ if (usingOrder && waiterId != null) this.register(waiterId, item);
55
+ return {
56
+ ok: false,
57
+ retryAt: now + (this.config.retryMs ?? 1e3)
58
+ };
59
+ }
60
+ if (usingOrder && this.waiters.size > 0) {
61
+ if (waiterId != null) this.register(waiterId, item);
62
+ if (!this.isNext(waiterId, item)) {
63
+ return {
64
+ ok: false,
65
+ retryAt: now + (this.config.retryMs ?? 1e3)
66
+ };
67
+ }
68
+ }
69
+ if (rl) this.windowCount += 1;
70
+ this.inFlight += 1;
71
+ if (waiterId != null) this.waiters.delete(waiterId);
72
+ if (item?.key != null) this.keyServed.set(item.key, ++this.servedTick);
73
+ return {
74
+ ok: true
75
+ };
76
+ }
77
+ /** Release a concurrency slot taken by a successful {@link tryAdmit}. */
78
+ release() {
79
+ if (this.inFlight > 0) this.inFlight -= 1;
80
+ }
81
+ /** Track (or refresh) a blocked call as a waiter; its FIFO seq is stamped only on first sight. */
82
+ register(waiterId, item) {
83
+ const existing = this.waiters.get(waiterId);
84
+ if (existing) {
85
+ existing.priority = item?.priority ?? 0;
86
+ existing.key = item?.key;
87
+ return;
88
+ }
89
+ this.waiters.set(waiterId, {
90
+ priority: item?.priority ?? 0,
91
+ key: item?.key,
92
+ seq: this.waiterSeq++
93
+ });
94
+ }
95
+ /**
96
+ * Is the calling item the best candidate among all registered waiters? Ordering, best first:
97
+ * 1. higher `priority`;
98
+ * 2. fairness: with `fairness: 'key'`, the least-recently-served key wins (round-robin);
99
+ * 3. earliest registration (FIFO) — stable tiebreak.
100
+ * A bare item (no waiterId) is compared on its hints directly.
101
+ */
102
+ isNext(waiterId, item) {
103
+ const self = (waiterId != null ? this.waiters.get(waiterId) : void 0) ?? {
104
+ priority: item?.priority ?? 0,
105
+ key: item?.key,
106
+ seq: Number.POSITIVE_INFINITY
107
+ };
108
+ let best = self;
109
+ for (const w of this.waiters.values()) {
110
+ if (this.better(w, best)) best = w;
111
+ }
112
+ return best === self;
113
+ }
114
+ /** Strict "a should be admitted before b" comparison under the active policy. */
115
+ better(a, b) {
116
+ if (a.priority !== b.priority) return a.priority > b.priority;
117
+ if (this.config.fairness === "key") {
118
+ const sa = this.servedOf(a.key);
119
+ const sb = this.servedOf(b.key);
120
+ if (sa !== sb) return sa < sb;
121
+ }
122
+ return this.config.order === "lifo" ? a.seq > b.seq : a.seq < b.seq;
123
+ }
124
+ /** Last-served tick for a key (0 = never served, so an unseen key is admitted before a busy one). */
125
+ servedOf(key) {
126
+ if (key == null) return 0;
127
+ return this.keyServed.get(key) ?? 0;
128
+ }
129
+ };
130
+
131
+ // src/admission.ts
132
+ var InMemoryAdmissionBackend = class {
133
+ static {
134
+ __name(this, "InMemoryAdmissionBackend");
135
+ }
136
+ clock;
137
+ controllers = /* @__PURE__ */ new Map();
138
+ constructor(clock) {
139
+ this.clock = clock;
140
+ }
141
+ register(config) {
142
+ this.controllers.set(config.name, new QueueController(config, this.clock));
143
+ }
144
+ handles(queue) {
145
+ return this.controllers.has(queue);
146
+ }
147
+ async tryAdmit(queue, item) {
148
+ const controller = this.controllers.get(queue);
149
+ if (!controller) return {
150
+ ok: true
151
+ };
152
+ return controller.tryAdmit(item);
153
+ }
154
+ async release(queue, _slotId) {
155
+ this.controllers.get(queue)?.release();
156
+ }
157
+ };
158
+
4
159
  // src/duration.ts
5
160
  var UNIT_MS = {
6
161
  ms: 1,
@@ -517,133 +672,6 @@ function eventMatches(payload, match) {
517
672
  }
518
673
  __name(eventMatches, "eventMatches");
519
674
 
520
- // src/queue.ts
521
- var QueueController = class {
522
- static {
523
- __name(this, "QueueController");
524
- }
525
- config;
526
- clock;
527
- inFlight = 0;
528
- windowStart = 0;
529
- windowCount = 0;
530
- /** Registered waiters by id (only used when priority/fairness ordering is in play). */
531
- waiters = /* @__PURE__ */ new Map();
532
- /** Monotonic counter stamping each newly-seen waiter for a stable FIFO tiebreak. */
533
- waiterSeq = 0;
534
- /** Per-key "last served" order for round-robin fairness — higher = more recently served. */
535
- keyServed = /* @__PURE__ */ new Map();
536
- servedTick = 0;
537
- constructor(config, clock) {
538
- this.config = config;
539
- this.clock = clock;
540
- }
541
- /** Whether this controller does any priority/fairness ordering (else it's a plain FIFO gate). */
542
- get ordered() {
543
- return this.config.fairness === "key";
544
- }
545
- /**
546
- * Try to admit one unit of work. On `ok`, the caller has taken a concurrency slot and must call
547
- * {@link release} when the step settles. On block, `retryAt` is when admission may next succeed.
548
- *
549
- * `item` carries optional priority/fairness hints; omitting it is the legacy FIFO path. When
550
- * ordering is configured (or a priority is given), a blocked call REGISTERS as a waiter and is only
551
- * admitted on a later retry once it is the rightful next under (priority desc, fairness, FIFO).
552
- */
553
- tryAdmit(item) {
554
- const now = this.clock();
555
- const rl = this.config.rateLimit;
556
- if (rl) {
557
- if (now - this.windowStart >= rl.periodMs) {
558
- this.windowStart = now;
559
- this.windowCount = 0;
560
- }
561
- if (this.windowCount >= rl.limit) return {
562
- ok: false,
563
- retryAt: this.windowStart + rl.periodMs
564
- };
565
- }
566
- const usingOrder = this.ordered || item?.priority != null || item?.waiterId != null;
567
- const waiterId = item?.waiterId;
568
- const full = this.config.concurrency != null && this.inFlight >= this.config.concurrency;
569
- if (full) {
570
- if (usingOrder && waiterId != null) this.register(waiterId, item);
571
- return {
572
- ok: false,
573
- retryAt: now + (this.config.retryMs ?? 1e3)
574
- };
575
- }
576
- if (usingOrder && this.waiters.size > 0) {
577
- if (waiterId != null) this.register(waiterId, item);
578
- if (!this.isNext(waiterId, item)) {
579
- return {
580
- ok: false,
581
- retryAt: now + (this.config.retryMs ?? 1e3)
582
- };
583
- }
584
- }
585
- if (rl) this.windowCount += 1;
586
- this.inFlight += 1;
587
- if (waiterId != null) this.waiters.delete(waiterId);
588
- if (item?.key != null) this.keyServed.set(item.key, ++this.servedTick);
589
- return {
590
- ok: true
591
- };
592
- }
593
- /** Release a concurrency slot taken by a successful {@link tryAdmit}. */
594
- release() {
595
- if (this.inFlight > 0) this.inFlight -= 1;
596
- }
597
- /** Track (or refresh) a blocked call as a waiter; its FIFO seq is stamped only on first sight. */
598
- register(waiterId, item) {
599
- const existing = this.waiters.get(waiterId);
600
- if (existing) {
601
- existing.priority = item?.priority ?? 0;
602
- existing.key = item?.key;
603
- return;
604
- }
605
- this.waiters.set(waiterId, {
606
- priority: item?.priority ?? 0,
607
- key: item?.key,
608
- seq: this.waiterSeq++
609
- });
610
- }
611
- /**
612
- * Is the calling item the best candidate among all registered waiters? Ordering, best first:
613
- * 1. higher `priority`;
614
- * 2. fairness: with `fairness: 'key'`, the least-recently-served key wins (round-robin);
615
- * 3. earliest registration (FIFO) — stable tiebreak.
616
- * A bare item (no waiterId) is compared on its hints directly.
617
- */
618
- isNext(waiterId, item) {
619
- const self = (waiterId != null ? this.waiters.get(waiterId) : void 0) ?? {
620
- priority: item?.priority ?? 0,
621
- key: item?.key,
622
- seq: Number.POSITIVE_INFINITY
623
- };
624
- let best = self;
625
- for (const w of this.waiters.values()) {
626
- if (this.better(w, best)) best = w;
627
- }
628
- return best === self;
629
- }
630
- /** Strict "a should be admitted before b" comparison under the active policy. */
631
- better(a, b) {
632
- if (a.priority !== b.priority) return a.priority > b.priority;
633
- if (this.config.fairness === "key") {
634
- const sa = this.servedOf(a.key);
635
- const sb = this.servedOf(b.key);
636
- if (sa !== sb) return sa < sb;
637
- }
638
- return a.seq < b.seq;
639
- }
640
- /** Last-served tick for a key (0 = never served, so an unseen key is admitted before a busy one). */
641
- servedOf(key) {
642
- if (key == null) return 0;
643
- return this.keyServed.get(key) ?? 0;
644
- }
645
- };
646
-
647
675
  // src/singleton-gate.ts
648
676
  var SINGLETON_RETRY_MS = 1e3;
649
677
  var SINGLETON_RETRY_JITTER_MS = 250;
@@ -837,6 +865,12 @@ function workflowName(ref) {
837
865
  __name(workflowName, "workflowName");
838
866
 
839
867
  // src/workflow-ctx.ts
868
+ function normalizeChildOptions(options) {
869
+ return typeof options === "string" ? {
870
+ childId: options
871
+ } : options ?? {};
872
+ }
873
+ __name(normalizeChildOptions, "normalizeChildOptions");
840
874
  var Position = class Position2 {
841
875
  static {
842
876
  __name(this, "Position");
@@ -1086,7 +1120,8 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
1086
1120
  await step(`task:dispatch:${name}`, dispatch, options);
1087
1121
  return unwrapCompletion(await waitForSignal(`task:${runId}:${name}`), `task "${name}"`);
1088
1122
  }, "task");
1089
- const child = /* @__PURE__ */ __name(async (workflow2, input, childId) => {
1123
+ const child = /* @__PURE__ */ __name(async (workflow2, input, options) => {
1124
+ const { childId, priority } = normalizeChildOptions(options);
1090
1125
  const current = pos.next();
1091
1126
  const id = childId ?? `${runId}.child.${current}`;
1092
1127
  const existing = await readCheckpoint(current);
@@ -1098,7 +1133,7 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
1098
1133
  runId,
1099
1134
  seq: current
1100
1135
  });
1101
- if (!await store.getRun(id)) host.startChild(workflowName(workflow2), input, id);
1136
+ if (!await store.getRun(id)) host.startChild(workflowName(workflow2), input, id, priority);
1102
1137
  if (!existing) {
1103
1138
  await writeCheckpoint(instantCheckpoint({
1104
1139
  runId,
@@ -1110,12 +1145,13 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
1110
1145
  }
1111
1146
  throw new WorkflowSuspended();
1112
1147
  }, "child");
1113
- const startChild = /* @__PURE__ */ __name(async (workflow2, input, childId) => {
1148
+ const startChild = /* @__PURE__ */ __name(async (workflow2, input, options) => {
1149
+ const { childId, priority } = normalizeChildOptions(options);
1114
1150
  const current = pos.next();
1115
1151
  const id = childId ?? `${runId}.child.${current}`;
1116
1152
  const existing = await readCheckpoint(current);
1117
1153
  if (existing && existing.status === "completed") return existing.output;
1118
- if (!await store.getRun(id)) host.startChild(workflowName(workflow2), input, id);
1154
+ if (!await store.getRun(id)) host.startChild(workflowName(workflow2), input, id, priority);
1119
1155
  await writeCheckpoint(instantCheckpoint({
1120
1156
  runId,
1121
1157
  seq: current,
@@ -1339,8 +1375,10 @@ var WorkflowEngine = class {
1339
1375
  updateValidators = /* @__PURE__ */ new Map();
1340
1376
  /** Runs being cancelled WITH saga compensation — see `cancel({ compensate: true })`. */
1341
1377
  cancelRequested = /* @__PURE__ */ new Set();
1342
- /** Flow-control queues for remote steps, keyed by name (see {@link registerQueue}). */
1343
- queues = /* @__PURE__ */ new Map();
1378
+ /** Flow-control admission backend for remote steps (see {@link registerQueue}). */
1379
+ admission;
1380
+ /** Runs on THIS instance blocked on admission, by queue — woken early on a freed-slot signal. */
1381
+ queueWaiters = /* @__PURE__ */ new Map();
1344
1382
  /** Which queue a dispatched step took a slot from, by stepId — so the result can release it. */
1345
1383
  stepQueue = /* @__PURE__ */ new Map();
1346
1384
  /** Executions currently in flight, so a graceful shutdown can wait for them to settle. */
@@ -1356,6 +1394,8 @@ var WorkflowEngine = class {
1356
1394
  ] : []));
1357
1395
  this.controlPlane = deps.controlPlane;
1358
1396
  this.clock = deps.clock ?? Date.now;
1397
+ this.admission = deps.admission ?? new InMemoryAdmissionBackend(this.clock);
1398
+ this.admission.onFreed?.((queue) => this.wakeQueueWaiters(queue));
1359
1399
  this.instanceId = deps.instanceId ?? globalThis.crypto.randomUUID();
1360
1400
  this.leaseMs = deps.leaseMs ?? 3e4;
1361
1401
  this.maxRecoveryAttempts = deps.maxRecoveryAttempts;
@@ -1519,7 +1559,7 @@ var WorkflowEngine = class {
1519
1559
  * durable. Per engine instance (see {@link QueueConfig}). Registering the same name replaces it.
1520
1560
  */
1521
1561
  registerQueue(config) {
1522
- this.queues.set(config.name, new QueueController(config, this.clock));
1562
+ this.admission.register(config);
1523
1563
  }
1524
1564
  /** Subscribe to lifecycle events. Returns an unsubscribe function. */
1525
1565
  subscribe(listener) {
@@ -1640,6 +1680,7 @@ var WorkflowEngine = class {
1640
1680
  input,
1641
1681
  tags,
1642
1682
  searchAttributes: opts?.searchAttributes,
1683
+ priority: opts?.priority,
1643
1684
  createdAt: now,
1644
1685
  updatedAt: now
1645
1686
  };
@@ -2115,6 +2156,26 @@ var WorkflowEngine = class {
2115
2156
  return results;
2116
2157
  }
2117
2158
  /**
2159
+ * Hard-delete a run and its entire subtree. Unlike {@link cancel} (which marks a run `cancelled`
2160
+ * but keeps it as history), delete REMOVES the run and all its rows (checkpoints, signal waiters,
2161
+ * search-attribute rows) — it vanishes from {@link getRun} and {@link listRuns}. Use it to purge a
2162
+ * finished run whose data is being deleted (e.g. a pipeline run whose result rows are cleared).
2163
+ *
2164
+ * Cascades depth-first via {@link getRunChildren} so children are gone before the parent (no
2165
+ * orphaned child runs). Prefer {@link cancel} first for a live run — deleting one mid-flight orphans
2166
+ * its worker (its next checkpoint write fails). Returns the number of runs deleted (0 if absent).
2167
+ */
2168
+ async deleteRun(runId) {
2169
+ const run = await this.store.getRun(runId);
2170
+ if (!run) return 0;
2171
+ let deleted = 0;
2172
+ for (const childId of await this.getRunChildren(runId)) {
2173
+ deleted += await this.deleteRun(childId);
2174
+ }
2175
+ await this.store.deleteRun(runId);
2176
+ return deleted + 1;
2177
+ }
2178
+ /**
2118
2179
  * Cascade cancellation to a run's children — both awaited (`ctx.child`, found via its live
2119
2180
  * `child:<id>` waiter) and fire-and-forget (`ctx.startChild`, found via its `spawn:<id>`
2120
2181
  * checkpoint). Recursive, so a whole subtree is cancelled; the terminal guard in `cancel` stops it
@@ -2846,8 +2907,10 @@ var WorkflowEngine = class {
2846
2907
  failStep: /* @__PURE__ */ __name((s) => this.failStep(s), "failStep"),
2847
2908
  callRemote: /* @__PURE__ */ __name((runId, seq, step, input, queue, transport, admission) => this.callRemote(runId, seq, step, input, queue, transport, replay, admission), "callRemote"),
2848
2909
  // Defer so a fast child can't reentrantly resume a still-running parent.
2849
- startChild: /* @__PURE__ */ __name((workflow, input, id) => {
2850
- queueMicrotask(() => void this.start(workflow, input, id).catch(() => void 0));
2910
+ startChild: /* @__PURE__ */ __name((workflow, input, id, priority) => {
2911
+ queueMicrotask(() => void this.start(workflow, input, id, {
2912
+ priority
2913
+ }).catch(() => void 0));
2851
2914
  }, "startChild"),
2852
2915
  signalEntity: /* @__PURE__ */ __name((name, key, op, arg, reply) => {
2853
2916
  queueMicrotask(() => void this.entities.dispatch(name, key, op, arg, reply).catch(() => void 0));
@@ -2948,14 +3011,19 @@ var WorkflowEngine = class {
2948
3011
  attempt = existing.attempts + 1;
2949
3012
  }
2950
3013
  const id = stepId(runId, seq);
2951
- const controller = queue ? this.queues.get(queue) : void 0;
2952
- if (controller) {
2953
- const decision = controller.tryAdmit({
3014
+ if (queue && this.admission.handles(queue)) {
3015
+ const decision = await this.admission.tryAdmit(queue, {
2954
3016
  priority: admission?.priority,
2955
3017
  key: admission?.fairnessKey ?? runId,
2956
3018
  waiterId: id
2957
3019
  });
2958
- if (!decision.ok) throw new WorkflowSuspended(decision.retryAt);
3020
+ if (!decision.ok) {
3021
+ const waiters = this.queueWaiters.get(queue) ?? /* @__PURE__ */ new Set();
3022
+ waiters.add(runId);
3023
+ this.queueWaiters.set(queue, waiters);
3024
+ throw new WorkflowSuspended(decision.retryAt);
3025
+ }
3026
+ this.queueWaiters.get(queue)?.delete(runId);
2959
3027
  this.stepQueue.set(id, queue);
2960
3028
  }
2961
3029
  const validInput = step.input.parse(input);
@@ -2983,6 +3051,7 @@ var WorkflowEngine = class {
2983
3051
  input: validInput,
2984
3052
  traceparent: this.traceparent?.(),
2985
3053
  context: this.context?.(),
3054
+ priority: admission?.priority,
2986
3055
  attempt
2987
3056
  }, transport);
2988
3057
  this.emit({
@@ -3002,7 +3071,7 @@ var WorkflowEngine = class {
3002
3071
  async completeRemoteResult(result) {
3003
3072
  const cp = await this.store.getCheckpoint(result.runId, result.seq);
3004
3073
  if (!cp || cp.status !== "pending") return;
3005
- this.releaseQueueSlot(cp.stepId);
3074
+ void this.releaseQueueSlot(cp.stepId).catch(() => void 0);
3006
3075
  const run = await this.store.getRun(result.runId);
3007
3076
  if (run && (run.status === "cancelled" || run.status === "completed")) return;
3008
3077
  const finishedAt = /* @__PURE__ */ new Date();
@@ -3029,12 +3098,26 @@ var WorkflowEngine = class {
3029
3098
  });
3030
3099
  await this.resume(result.runId);
3031
3100
  }
3101
+ /**
3102
+ * A slot freed on `queue` (a fleet-wide signal): resume this instance's runs blocked on it so they
3103
+ * re-contend now instead of at their retry tick. Snapshot-and-clear — a run still blocked after the
3104
+ * retry re-registers itself, and one that's gone (cancelled/admitted) is dropped. Best-effort.
3105
+ */
3106
+ wakeQueueWaiters(queue) {
3107
+ const waiters = this.queueWaiters.get(queue);
3108
+ if (!waiters || waiters.size === 0) return;
3109
+ const runIds = [
3110
+ ...waiters
3111
+ ];
3112
+ waiters.clear();
3113
+ for (const runId of runIds) void this.resume(runId).catch(() => void 0);
3114
+ }
3032
3115
  /** Release the flow-control slot a dispatched step held (if any), by its stepId. */
3033
- releaseQueueSlot(id) {
3116
+ async releaseQueueSlot(id) {
3034
3117
  const queue = this.stepQueue.get(id);
3035
3118
  if (queue === void 0) return;
3036
3119
  this.stepQueue.delete(id);
3037
- this.queues.get(queue)?.release();
3120
+ await this.admission.release(queue, id);
3038
3121
  }
3039
3122
  /** In-memory await path for a remote step with a liveness `timeoutMs` (re-dispatch on timeout). */
3040
3123
  async callRemoteInMemory(runId, seq, step, input, transport) {
@@ -3195,6 +3278,7 @@ var RemoteWorkflowExecutor = class {
3195
3278
  input: run.input,
3196
3279
  history,
3197
3280
  group: this.group,
3281
+ priority: run.priority,
3198
3282
  attempt: 1
3199
3283
  };
3200
3284
  const decision = new Promise((resolve, reject) => {
@@ -3274,6 +3358,9 @@ var CodecStateStore = class {
3274
3358
  async getRun(runId) {
3275
3359
  return this.decRun(await this.inner.getRun(runId));
3276
3360
  }
3361
+ deleteRun(runId) {
3362
+ return this.inner.deleteRun(runId);
3363
+ }
3277
3364
  async getCheckpoint(runId, seq) {
3278
3365
  return this.decCp(await this.inner.getCheckpoint(runId, seq));
3279
3366
  }
@@ -3668,6 +3755,16 @@ var InMemoryStateStore = class {
3668
3755
  ...run
3669
3756
  } : null;
3670
3757
  }
3758
+ async deleteRun(runId) {
3759
+ this.runs.delete(runId);
3760
+ for (const [key, cp] of this.checkpoints) {
3761
+ if (cp.runId === runId) this.checkpoints.delete(key);
3762
+ }
3763
+ for (const [token, waiter] of this.signalWaiters) {
3764
+ if (waiter.runId === runId) this.signalWaiters.delete(token);
3765
+ }
3766
+ this.reindexAttributes(runId, void 0);
3767
+ }
3671
3768
  async getCheckpoint(runId, seq) {
3672
3769
  const cp = this.checkpoints.get(this.key(runId, seq));
3673
3770
  return cp ? {
@@ -3913,6 +4010,7 @@ export {
3913
4010
  Entities,
3914
4011
  EventAccumulators,
3915
4012
  FatalError,
4013
+ InMemoryAdmissionBackend,
3916
4014
  InMemoryStateStore,
3917
4015
  InMemoryTransport,
3918
4016
  NonDeterminismError,