@bastani/atomic 0.9.60-alpha.1 → 0.9.61-alpha.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.
@@ -17001,6 +17001,25 @@ function admissionProcessStopped(pid) {
17001
17001
  return error.code === "ESRCH";
17002
17002
  }
17003
17003
  }
17004
+ function workerProcessExited(pid, final = false) {
17005
+ if (admissionProcessStopped(pid))
17006
+ return true;
17007
+ if (process.platform === "linux") {
17008
+ try {
17009
+ const stat = readFileSync3(`/proc/${pid}/stat`, "utf8");
17010
+ return stat.slice(stat.lastIndexOf(")") + 2).startsWith("Z");
17011
+ } catch {
17012
+ return admissionProcessStopped(pid);
17013
+ }
17014
+ }
17015
+ if (!final)
17016
+ return false;
17017
+ try {
17018
+ return execFileSync("ps", ["-o", "stat=", "-p", String(pid)], { encoding: "utf8", timeout: 5000 }).trim().startsWith("Z");
17019
+ } catch {
17020
+ return admissionProcessStopped(pid);
17021
+ }
17022
+ }
17004
17023
  function scanProcessGroup(group) {
17005
17024
  const members = new Map;
17006
17025
  if (process.platform === "linux") {
@@ -59494,17 +59513,26 @@ function isSameCheckpointValue(stored, next) {
59494
59513
  const hash = (value2) => durableHash(value2);
59495
59514
  return hash(stored) === hash(next);
59496
59515
  }
59516
+ function withTimesOf(later, recorded) {
59517
+ const both = (key) => typeof recorded[key] === "number" && typeof later[key] === "number";
59518
+ return {
59519
+ ...later,
59520
+ ...both("startedAt") ? { startedAt: recorded.startedAt } : {},
59521
+ ...both("endedAt") ? { endedAt: recorded.endedAt } : {},
59522
+ ...both("completedAt") ? { completedAt: recorded.completedAt } : {}
59523
+ };
59524
+ }
59497
59525
  function isRecordedCheckpointValue(stored, next) {
59498
59526
  const recorded = stored;
59499
59527
  if (isSameCheckpointValue(recorded, next))
59500
59528
  return true;
59501
- const { completedAt, startedAt } = recorded;
59502
- if (typeof completedAt !== "number")
59529
+ if (typeof recorded.completedAt !== "number")
59503
59530
  return false;
59504
59531
  const later = next;
59505
- if (startedAt === undefined !== (later.startedAt === undefined))
59532
+ if (recorded.startedAt === undefined !== (later.startedAt === undefined))
59506
59533
  return false;
59507
- return isSameCheckpointValue({ ...later, completedAt, ...startedAt === undefined ? {} : { startedAt } }, recorded);
59534
+ const retimed = withTimesOf(later, recorded);
59535
+ return isSameCheckpointValue(recorded.topology !== undefined && later.topology !== undefined ? { ...retimed, topology: withTimesOf(later.topology, recorded.topology) } : retimed, recorded);
59508
59536
  }
59509
59537
  var MIRROR_FRESH_MS = 2000;
59510
59538
  var init_pool_remote_journal = __esm(() => {
@@ -60540,15 +60568,7 @@ class SqlPoolQueue {
60540
60568
  }
60541
60569
  }
60542
60570
  function isRecordedCheckpoint(stored, payload) {
60543
- if (stored === payload)
60544
- return true;
60545
- const { completedAt, startedAt } = JSON.parse(stored);
60546
- if (typeof completedAt !== "number")
60547
- return false;
60548
- const next = JSON.parse(payload);
60549
- if (startedAt === undefined !== (next.startedAt === undefined))
60550
- return false;
60551
- return serializeJson({ ...next, completedAt, ...startedAt === undefined ? {} : { startedAt } }) === stored;
60571
+ return stored === payload || isRecordedCheckpointValue(JSON.parse(stored), JSON.parse(payload));
60552
60572
  }
60553
60573
  function openSqlPoolQueue(journal, remote) {
60554
60574
  const storage = sqlPoolStorage(journal);
@@ -65299,7 +65319,7 @@ var init_startup_timeout = () => {};
65299
65319
  import { fork } from "node:child_process";
65300
65320
  import { randomUUID as randomUUID17 } from "node:crypto";
65301
65321
  import { createWriteStream } from "node:fs";
65302
- import { mkdir as mkdir6, rm as rm3, rmdir } from "node:fs/promises";
65322
+ import { appendFile, mkdir as mkdir6, rm as rm3, rmdir } from "node:fs/promises";
65303
65323
  import { dirname as dirname14, join as join30, resolve as resolve13 } from "node:path";
65304
65324
  import { setTimeout as delay2 } from "node:timers/promises";
65305
65325
  function isRootLeaseLost(error) {
@@ -65491,6 +65511,9 @@ class PoolCoordinator {
65491
65511
  incarnations = new Map;
65492
65512
  readyWorkers = new Set;
65493
65513
  logs = new Map;
65514
+ keptLogs = new Set;
65515
+ unconfirmedExits = new WeakSet;
65516
+ failed = false;
65494
65517
  cleanups = new Set;
65495
65518
  controlAcks = new Map;
65496
65519
  lastHeard = new Map;
@@ -65538,7 +65561,6 @@ class PoolCoordinator {
65538
65561
  nextRecoveryAt = 0;
65539
65562
  cleanupFailure;
65540
65563
  held = new Set;
65541
- quarantined = new Set;
65542
65564
  unreaped = [];
65543
65565
  retained = new Map;
65544
65566
  retainedTransitions = Promise.resolve();
@@ -65731,18 +65753,15 @@ class PoolCoordinator {
65731
65753
  try {
65732
65754
  const { survivors, unprobeable } = await reapProcessGroups(this.journal, this.root, { attempt: work });
65733
65755
  if (survivors.length > 0) {
65734
- this.quarantined.add(work.id);
65735
65756
  this.unreaped.push({ attempt: work });
65736
- this.port.send({ kind: "work-failure", workId: work.id, error: unconfirmedCleanup(survivors).message });
65737
- return;
65757
+ this.reportHealth(undefined, `${unconfirmedCleanup(survivors).message}; child ${work.childRunId} of an earlier incarnation replays`);
65738
65758
  }
65739
65759
  if (unprobeable.length === 0)
65740
65760
  await this.releaseRetained(work);
65741
65761
  } catch (error) {
65742
65762
  this.cleanupFailure ??= error instanceof Error ? error : new Error(String(error));
65743
65763
  } finally {
65744
- if (!this.quarantined.has(work.id))
65745
- this.held.delete(work.id);
65764
+ this.held.delete(work.id);
65746
65765
  }
65747
65766
  }
65748
65767
  releaseRetained(work) {
@@ -65873,6 +65892,7 @@ class PoolCoordinator {
65873
65892
  });
65874
65893
  }
65875
65894
  fail(error) {
65895
+ this.failed = true;
65876
65896
  this.port.send({ kind: "failure", error: error.message });
65877
65897
  }
65878
65898
  failWork(work, error) {
@@ -66281,7 +66301,11 @@ class PoolCoordinator {
66281
66301
  terminate();
66282
66302
  });
66283
66303
  const closed = new Promise((resolve) => child.once("close", () => resolve()));
66284
- child.once("exit", () => {
66304
+ let retired = false;
66305
+ const retire = () => {
66306
+ if (retired)
66307
+ return;
66308
+ retired = true;
66285
66309
  Promise.race([closed, delay2(STDERR_DRAIN_GRACE_MS)]).then(() => reject(diagnosed(timedOut ? startupTimeoutMessage(startupTimeoutMs) : "Local worker exited before readiness")));
66286
66310
  this.children.delete(child);
66287
66311
  this.logs.delete(child);
@@ -66305,7 +66329,7 @@ class PoolCoordinator {
66305
66329
  const held = slot.work;
66306
66330
  if (held)
66307
66331
  this.held.add(held.id);
66308
- let childFailed = false;
66332
+ let unconfirmed;
66309
66333
  const cleanup = disconnected.then(() => serial).then(async () => {
66310
66334
  try {
66311
66335
  const admission = this.admission;
@@ -66318,18 +66342,13 @@ class PoolCoordinator {
66318
66342
  throw unconfirmedCleanup(alive);
66319
66343
  if (alive.length === 0)
66320
66344
  return;
66321
- const error = unconfirmedCleanup(alive);
66322
- if (held && await this.queue.result(this.root, held.id) === undefined) {
66323
- this.quarantined.add(held.id);
66324
- slot.work = undefined;
66325
- childFailed = true;
66326
- this.port.send({ kind: "work-failure", workId: held.id, error: error.message });
66327
- }
66345
+ unconfirmed = unconfirmedCleanup(alive);
66346
+ this.reportHealth(logPath, `${unconfirmed.message}; the worker was retired and replaced`);
66328
66347
  } catch (error) {
66329
66348
  this.cleanupFailure = error instanceof Error ? error : new Error(String(error));
66330
66349
  throw this.cleanupFailure;
66331
66350
  } finally {
66332
- if (held && !this.quarantined.has(held.id))
66351
+ if (held)
66333
66352
  this.held.delete(held.id);
66334
66353
  }
66335
66354
  }).then(async () => {
@@ -66350,8 +66369,8 @@ class PoolCoordinator {
66350
66369
  const settled = attempt !== undefined && await this.whileRootLate(queue, () => exhausted ? queue.failExited(lease, attempt) : queue.releaseExited(lease, attempt));
66351
66370
  const superseded = work !== undefined && !settled && await this.superseded(work);
66352
66371
  if (work !== undefined && exhausted && !superseded)
66353
- this.failWork(work, diagnosed(childRetryLimit(work, "lost its worker")));
66354
- if (work === undefined && !childFailed && ++this.replacements > 3)
66372
+ this.failWork(work, diagnosed(`${childRetryLimit(work, "lost its worker")}${unconfirmed === undefined ? "" : `; ${unconfirmed.message}`}`));
66373
+ if (work === undefined && ++this.replacements > 3)
66355
66374
  this.fail(diagnosed("Local worker retry limit"));
66356
66375
  else
66357
66376
  await this.spawn();
@@ -66360,7 +66379,9 @@ class PoolCoordinator {
66360
66379
  });
66361
66380
  this.cleanups.add(cleanup);
66362
66381
  cleanup.finally(() => this.cleanups.delete(cleanup));
66363
- });
66382
+ };
66383
+ slot.retire = retire;
66384
+ child.once("exit", retire);
66364
66385
  child.on("message", (message) => {
66365
66386
  this.observeResponsiveness();
66366
66387
  this.lastHeard.set(worker, { at: performance.now(), stalled: this.stalledMs });
@@ -66434,10 +66455,11 @@ class PoolCoordinator {
66434
66455
  const fenced = slot.work;
66435
66456
  slot.work = undefined;
66436
66457
  if (fenced && !await this.superseded(fenced)) {
66458
+ this.fence(fenced);
66437
66459
  if (this.attempts(fenced) < MAX_CHILD_ATTEMPTS)
66438
66460
  await leased(() => queue.releaseExited(lease, fenced));
66439
66461
  else if (await leased(() => queue.failExited(lease, fenced)))
66440
- this.failWork(fenced, new Error(childRetryLimit(fenced, "was refused by the root as stale on every attempt")));
66462
+ this.failWork(fenced, new Error(childRetryLimit(fenced, message.fault === undefined ? "was refused by the root as stale on every attempt" : `failed on a fault of the pool on every attempt (last: ${message.fault})`)));
66441
66463
  }
66442
66464
  return;
66443
66465
  }
@@ -66598,8 +66620,6 @@ class PoolCoordinator {
66598
66620
  return;
66599
66621
  }
66600
66622
  for (const work of expired) {
66601
- if (this.quarantined.has(work.id))
66602
- continue;
66603
66623
  if (this.held.has(work.id)) {
66604
66624
  await queue.extend(lease, work, leaseMs).catch((error) => {
66605
66625
  if (error.message !== STALE_WORK_LEASE)
@@ -66615,9 +66635,9 @@ class PoolCoordinator {
66615
66635
  });
66616
66636
  continue;
66617
66637
  }
66618
- if (child && !await this.stop(child).then(() => true, () => false))
66619
- continue;
66620
- if (this.held.has(work.id) || this.quarantined.has(work.id))
66638
+ if (child && !await this.stop(child))
66639
+ this.retireUnconfirmed(child);
66640
+ if (this.held.has(work.id))
66621
66641
  continue;
66622
66642
  if ((await unconfirmedProcessGroups(this.journal, root, { attempt: work })).length > 0)
66623
66643
  continue;
@@ -66644,17 +66664,49 @@ class PoolCoordinator {
66644
66664
  const heard = this.lastHeard.get(worker);
66645
66665
  return child.exitCode === null && child.signalCode === null && child.connected && heard !== undefined && performance.now() - heard.at - (this.stalledMs - heard.stalled) < this.options.leaseMs;
66646
66666
  }
66647
- stop(child) {
66648
- if (child.pid === undefined || child.exitCode !== null || child.signalCode !== null)
66649
- return Promise.resolve();
66650
- return new Promise((resolve, reject) => {
66651
- const timeout = setTimeout(() => reject(new Error("Local worker cleanup was not confirmed")), 5000);
66652
- child.once("exit", () => {
66653
- clearTimeout(timeout);
66654
- resolve();
66655
- });
66656
- child.kill("SIGKILL");
66657
- });
66667
+ async stop(child) {
66668
+ const pid = child.pid;
66669
+ const exited = () => child.exitCode !== null || child.signalCode !== null;
66670
+ if (pid === undefined || exited())
66671
+ return true;
66672
+ const exit = new Promise((resolve) => child.once("exit", () => resolve()));
66673
+ const confirmed = async () => {
66674
+ await Promise.race([exit, delay2(WORKER_EXIT_CONFIRMATION_MS)]);
66675
+ return true;
66676
+ };
66677
+ this.observeResponsiveness();
66678
+ child.kill("SIGKILL");
66679
+ const since = performance.now();
66680
+ const stalledAt = this.stalledMs;
66681
+ for (;; ) {
66682
+ await Promise.race([exit, delay2(WORKER_EXIT_POLL_MS)]);
66683
+ if (exited())
66684
+ return true;
66685
+ if (workerProcessExited(pid))
66686
+ return confirmed();
66687
+ this.observeResponsiveness();
66688
+ if (performance.now() - since - (this.stalledMs - stalledAt) >= WORKER_EXIT_CONFIRMATION_MS)
66689
+ return exited() || workerProcessExited(pid, true) && confirmed();
66690
+ }
66691
+ }
66692
+ retireUnconfirmed(child) {
66693
+ if (this.unconfirmedExits.has(child))
66694
+ return;
66695
+ this.unconfirmedExits.add(child);
66696
+ this.reportHealth(this.logs.get(child), `Local worker cleanup was not confirmed: worker process ${child.pid} was still running ${WORKER_EXIT_CONFIRMATION_MS} ms after SIGKILL; it was retired and replaced`);
66697
+ if (child.connected)
66698
+ child.disconnect();
66699
+ for (const slot of this.slots)
66700
+ if (slot.child === child)
66701
+ slot.retire?.();
66702
+ }
66703
+ reportHealth(log, message) {
66704
+ if (log !== undefined) {
66705
+ this.keptLogs.add(log);
66706
+ appendFile(log, `[atomic ${new Date().toISOString()}] ${message}
66707
+ `).catch(() => {});
66708
+ }
66709
+ this.port.send({ kind: "health", message: log === undefined ? message : `${message} (worker log: ${log})` });
66658
66710
  }
66659
66711
  async close() {
66660
66712
  this.closed = true;
@@ -66664,10 +66716,15 @@ class PoolCoordinator {
66664
66716
  this.rateLimitOutbox.close();
66665
66717
  let failure;
66666
66718
  let stopping = [];
66719
+ let confirmed = [];
66667
66720
  try {
66668
66721
  await this.recovering?.catch(() => {});
66669
66722
  stopping = [...this.children].map((child) => ({ child, log: this.logs.get(child) }));
66670
- await Promise.all(stopping.map(({ child }) => this.stop(child)));
66723
+ confirmed = await Promise.all(stopping.map(({ child }) => this.stop(child)));
66724
+ stopping.forEach(({ child }, index) => {
66725
+ if (!confirmed[index])
66726
+ this.retireUnconfirmed(child);
66727
+ });
66671
66728
  await Promise.all([...this.cleanups]);
66672
66729
  if (this.cleanupFailure)
66673
66730
  throw this.cleanupFailure;
@@ -66679,15 +66736,15 @@ class PoolCoordinator {
66679
66736
  if (alive.length > 0)
66680
66737
  throw unconfirmedCleanup(alive);
66681
66738
  if (this.desiredControl !== "quit" && this.queue !== undefined && this.lease !== undefined)
66682
- await this.queue.releaseClosed(this.lease, [...this.quarantined]).catch((error) => {
66739
+ await this.queue.releaseClosed(this.lease).catch((error) => {
66683
66740
  if (!(error instanceof StaleLeaseError))
66684
66741
  throw error;
66685
66742
  });
66686
66743
  } catch (error) {
66687
66744
  failure = error;
66688
66745
  }
66689
- if (failure === undefined) {
66690
- const stopped = stopping.flatMap(({ child, log }) => log !== undefined && (child.exitCode !== null || child.signalCode !== null) ? [log] : []);
66746
+ if (failure === undefined && !this.failed) {
66747
+ const stopped = stopping.flatMap(({ log }, index) => log !== undefined && !this.keptLogs.has(log) && confirmed[index] === true ? [log] : []);
66691
66748
  await Promise.all(stopped.map((log) => rm3(log, { force: true }).catch(() => {})));
66692
66749
  if (this.logDirectory !== undefined)
66693
66750
  await rmdir(this.logDirectory).catch(() => {});
@@ -66713,7 +66770,7 @@ function childRetryLimit(work, reason) {
66713
66770
  const exportName = work.definition.slice(work.definition.lastIndexOf("#") + 1);
66714
66771
  return `Local worker retry limit: child ${work.childRunId} (${exportName}) ${reason} ${MAX_CHILD_ATTEMPTS} times`;
66715
66772
  }
66716
- var CONTROL_ACKNOWLEDGEMENT_STALL_MS = 5000, RECOVERY_INTERVAL_MS = 500, TICK_INTERVAL_MS = 100, STALL_THRESHOLD_MS = 500, COORDINATOR_LEASE_MS, LATE_ROOT_POLL_MS, MIRROR_INTERVAL_MS = 1e4, STDERR_TAIL_CHARACTERS = 8192, STDERR_DRAIN_GRACE_MS = 1000, MAX_CHILD_ATTEMPTS = 3, ROUTE_REGISTRATION_WAIT_MS = 120000, STAGE_WATCH_BACKLOG_EVENTS = 2000, STAGE_WATCH_BACKLOG_BYTES, PENDING_STAGE_MESSAGE_EVENT2 = "atomic:workflow-pending-stage-message", STICKY_LIVE_DELIVERED_EVENT2 = "atomic:workflow-sticky-live-delivered", PENDING_STAGE_UNDELIVERABLE_EVENT2 = "atomic:workflow-pending-stage-undeliverable", ENDED_WATCHES_REMEMBERED = 1024;
66773
+ var CONTROL_ACKNOWLEDGEMENT_STALL_MS = 5000, RECOVERY_INTERVAL_MS = 500, TICK_INTERVAL_MS = 100, STALL_THRESHOLD_MS = 500, COORDINATOR_LEASE_MS, LATE_ROOT_POLL_MS, MIRROR_INTERVAL_MS = 1e4, STDERR_TAIL_CHARACTERS = 8192, STDERR_DRAIN_GRACE_MS = 1000, WORKER_EXIT_CONFIRMATION_MS = 5000, WORKER_EXIT_POLL_MS = 50, MAX_CHILD_ATTEMPTS = 3, ROUTE_REGISTRATION_WAIT_MS = 120000, STAGE_WATCH_BACKLOG_EVENTS = 2000, STAGE_WATCH_BACKLOG_BYTES, PENDING_STAGE_MESSAGE_EVENT2 = "atomic:workflow-pending-stage-message", STICKY_LIVE_DELIVERED_EVENT2 = "atomic:workflow-sticky-live-delivered", PENDING_STAGE_UNDELIVERABLE_EVENT2 = "atomic:workflow-pending-stage-undeliverable", ENDED_WATCHES_REMEMBERED = 1024;
66717
66774
  var init_pool_coordinator = __esm(() => {
66718
66775
  init_internal();
66719
66776
  init_pool_remote_journal();
@@ -66728,6 +66785,7 @@ var init_pool_coordinator = __esm(() => {
66728
66785
  init_pending_stage_route_readiness();
66729
66786
  init_store();
66730
66787
  init_workflow_infrastructure_error();
66788
+ init_admission_host();
66731
66789
  init_pool_journal();
66732
66790
  init_pooled_watch_trace();
66733
66791
  init_resource_admission();
@@ -68658,6 +68716,7 @@ class LocalWorkerPool {
68658
68716
  works = new Map;
68659
68717
  attempts = new Map;
68660
68718
  results = new Map;
68719
+ healthEvents = [];
68661
68720
  workFailures = new Map;
68662
68721
  changed = Promise.withResolvers();
68663
68722
  waiting = 0;
@@ -68715,6 +68774,9 @@ class LocalWorkerPool {
68715
68774
  if (options.admission && options.rootId === undefined)
68716
68775
  workflowResourceAdmission.set(backend, options.admission);
68717
68776
  }
68777
+ get health() {
68778
+ return this.healthEvents;
68779
+ }
68718
68780
  get workerPids() {
68719
68781
  return [...this.pids];
68720
68782
  }
@@ -69308,14 +69370,8 @@ class LocalWorkerPool {
69308
69370
  try {
69309
69371
  return await this.dispatch(definition, inputs, options);
69310
69372
  } catch (error) {
69311
- if (error instanceof WorkflowInfrastructureError && [...this.workFailures.values()].includes(error))
69312
- throw error;
69313
- try {
69314
- if (this.desiredControl !== "quit" || !findWorkflowGracefulQuit(error))
69315
- await this.shutdown();
69316
- } catch (closeError) {
69317
- throw asWorkflowInfrastructureError(closeError) ?? closeError;
69318
- }
69373
+ if (this.failure !== undefined || this.coordinatorLost !== undefined)
69374
+ await this.shutdown().catch(() => {});
69319
69375
  throw asWorkflowInfrastructureError(error) ?? error;
69320
69376
  }
69321
69377
  }
@@ -69383,8 +69439,13 @@ class LocalWorkerPool {
69383
69439
  try {
69384
69440
  for (;; ) {
69385
69441
  options.signal?.throwIfAborted();
69386
- if (this.closed)
69387
- throw this.closedError(root);
69442
+ if (!this.results.has(work.id) && (this.closed || this.failure !== undefined)) {
69443
+ const recorded = await this.queue?.result(root, work.id).catch(() => {
69444
+ return;
69445
+ });
69446
+ if (recorded !== undefined)
69447
+ this.results.set(work.id, recorded);
69448
+ }
69388
69449
  const result = this.results.get(work.id);
69389
69450
  if (result) {
69390
69451
  this.completing++;
@@ -69408,6 +69469,8 @@ class LocalWorkerPool {
69408
69469
  this.endFailedChild(work, failed);
69409
69470
  throw failed;
69410
69471
  }
69472
+ if (this.closed)
69473
+ throw this.closedError(root);
69411
69474
  if (this.failure)
69412
69475
  throw this.failure;
69413
69476
  if (this.budgetBlocked)
@@ -69442,6 +69505,19 @@ class LocalWorkerPool {
69442
69505
  if (this.reconnectingViews.size > 0 || this.outboxes.size > 0)
69443
69506
  this.reconnectStageViews();
69444
69507
  }
69508
+ recordHealth(message) {
69509
+ this.healthEvents.push(message);
69510
+ const root = this.root;
69511
+ if (root === undefined || this.store === undefined)
69512
+ return;
69513
+ this.store.recordNotice({
69514
+ id: `local-worker-health:${root}:${this.healthEvents.length}`,
69515
+ runId: root,
69516
+ level: "warning",
69517
+ message: `Local worker pool: ${message}`,
69518
+ createdAt: Date.now()
69519
+ });
69520
+ }
69445
69521
  evaluateBudget() {
69446
69522
  const decision = this.rootBudget?.checkpoint();
69447
69523
  const blocked = decision?.kind === "exhausted" || decision?.kind === "wrap_up";
@@ -69560,6 +69636,9 @@ class LocalWorkerPool {
69560
69636
  this.failure ??= new Error(event.error);
69561
69637
  this.signalChange();
69562
69638
  return;
69639
+ case "health":
69640
+ this.recordHealth(event.message);
69641
+ return;
69563
69642
  case "stage-events":
69564
69643
  this.stageWatches.get(event.watch)?.({ kind: "events", events: event.events });
69565
69644
  this.notice({ kind: "stage-events-applied", watch: event.watch });