@solidjs/signals 2.0.0-rc.16 → 2.0.0-rc.17

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.
Files changed (39) hide show
  1. package/dist/dev-shared.js +1719 -1603
  2. package/dist/dev.attribution.js +13 -13
  3. package/dist/dev.js +182 -45
  4. package/dist/observe/boundaries.js +2 -2
  5. package/dist/observe/core/async.js +3 -1
  6. package/dist/observe/core/context.js +2 -0
  7. package/dist/observe/core/core.js +26 -10
  8. package/dist/observe/core/effect.js +2 -0
  9. package/dist/observe/core/graph.js +10 -8
  10. package/dist/observe/core/lanes.js +25 -14
  11. package/dist/observe/core/owner.js +73 -47
  12. package/dist/observe/core/scheduler.js +189 -173
  13. package/dist/observe/core/verdict.js +3 -0
  14. package/dist/observe/map.js +2 -2
  15. package/dist/observe/signals.js +6 -1
  16. package/dist/observe/store/affects.js +13 -13
  17. package/dist/observe/store/optimistic.js +112 -71
  18. package/dist/observe/store/store.js +25 -16
  19. package/dist/prod/boundaries.js +2 -2
  20. package/dist/prod/core/async.js +3 -1
  21. package/dist/prod/core/context.js +2 -0
  22. package/dist/prod/core/core.js +31 -15
  23. package/dist/prod/core/effect.js +2 -0
  24. package/dist/prod/core/graph.js +10 -8
  25. package/dist/prod/core/heap.js +1 -1
  26. package/dist/prod/core/lanes.js +25 -14
  27. package/dist/prod/core/owner.js +70 -44
  28. package/dist/prod/core/scheduler.js +43 -27
  29. package/dist/prod/core/verdict.js +4 -1
  30. package/dist/prod/map.js +2 -2
  31. package/dist/prod/signals.js +18 -13
  32. package/dist/prod/store/optimistic.js +112 -71
  33. package/dist/prod/store/store.js +18 -7
  34. package/dist/types/core/core.d.ts +10 -3
  35. package/dist/types/core/dev.d.ts +9 -1
  36. package/dist/types/core/error.d.ts +9 -1
  37. package/dist/types/core/owner.d.ts +4 -0
  38. package/dist/types/core/scheduler.d.ts +23 -4
  39. package/package.json +1 -1
@@ -14,6 +14,8 @@ import { runHeap, enqueueSub, deleteFromHeap } from "./heap.js";
14
14
 
15
15
  import { devCheckQuiescent } from "./invariants.js";
16
16
 
17
+ import { rethrowCleanupError } from "./owner.js";
18
+
17
19
  // Optional-module hooks: module bindings, not `GlobalQueue` statics, so a
18
20
  // bundle that never imports the installing module never assigns them — its
19
21
  // minifier folds every guarded call site (and the branch behind it) away.
@@ -38,16 +40,16 @@ let endLanesHook = null;
38
40
 
39
41
  let lanesBlockedHook = null;
40
42
 
41
- function installLaneHooks(e, n, o, t, l, s, u, i, r) {
43
+ function installLaneHooks(e, o, n, t, l, s, u, r, i) {
42
44
  laneReadHook = e;
43
- laneStageHook = n;
44
- laneOutcomeHook = o;
45
+ laneStageHook = o;
46
+ laneOutcomeHook = n;
45
47
  laneWriteHook = t;
46
48
  applyGuessesHook = l;
47
49
  laneSeamsHook = s;
48
50
  laneCorrectionsHook = u;
49
- endLanesHook = i;
50
- lanesBlockedHook = r;
51
+ endLanesHook = r;
52
+ lanesBlockedHook = i;
51
53
  }
52
54
 
53
55
  // Boundaries (boundaries.ts): the display consumers between an observer
@@ -73,10 +75,10 @@ let boundarySeamHook = null;
73
75
 
74
76
  let heldRunHook = null;
75
77
 
76
- function installBoundaryHooks(e, n, o, t, l) {
78
+ function installBoundaryHooks(e, o, n, t, l) {
77
79
  catchHook = e;
78
- freshHook = n;
79
- hiddenHook = o;
80
+ freshHook = o;
81
+ hiddenHook = n;
80
82
  boundarySeamHook = t;
81
83
  heldRunHook = l;
82
84
  }
@@ -90,9 +92,9 @@ function installBoundaryHooks(e, n, o, t, l) {
90
92
 
91
93
  let storeCommitHook = null;
92
94
 
93
- function installStoreHooks(e, n) {
95
+ function installStoreHooks(e, o) {
94
96
  storeParkHook = e;
95
- storeCommitHook = n;
97
+ storeCommitHook = o;
96
98
  }
97
99
 
98
100
  /** `affects()` marks (affects.ts): the releases at a landing and at the seam
@@ -100,9 +102,9 @@ function installStoreHooks(e, n) {
100
102
 
101
103
  let releaseAmbientMarksHook = null;
102
104
 
103
- function installMarkHooks(e, n) {
105
+ function installMarkHooks(e, o) {
104
106
  releaseMarksHook = e;
105
- releaseAmbientMarksHook = n;
107
+ releaseAmbientMarksHook = o;
106
108
  }
107
109
 
108
110
  /** Verdicts (verdict.ts). */ let observeFlightHook = null;
@@ -116,9 +118,9 @@ function installVerdictHooks(e) {
116
118
 
117
119
  let externalUntrackHook = undefined;
118
120
 
119
- function setExternalHooks(e, n) {
121
+ function setExternalHooks(e, o) {
120
122
  wireExternalSourceHook = e;
121
- externalUntrackHook = n;
123
+ externalUntrackHook = o;
122
124
  }
123
125
 
124
126
  /** Live transactions — opened, unmerged, not landed. Scanned at every seam
@@ -126,13 +128,13 @@ function setExternalHooks(e, n) {
126
128
 
127
129
  /** Observe tier: this flush parked its transaction (`holdStart` fired). */ let holding = false;
128
130
 
129
- /** A transaction (listed here) or a lane (lanes.ts lists its own). */ function newTransaction(e, n = null) {
130
- const o = {
131
+ /** A transaction (listed here) or a lane (lanes.ts lists its own). */ function newTransaction(e, o = null) {
132
+ const n = {
131
133
  je: [],
132
134
  un: [ [], [] ],
133
135
  En: [],
134
- cn: null,
135
- _parent: n,
136
+ eo: null,
137
+ _parent: o,
136
138
  V: e,
137
139
  it: null,
138
140
  tt: 0,
@@ -142,8 +144,8 @@ function setExternalHooks(e, n) {
142
144
  ln: false,
143
145
  I: null
144
146
  };
145
- if (!e) transactions.push(o);
146
- return o;
147
+ if (!e) transactions.push(n);
148
+ return n;
147
149
  }
148
150
 
149
151
  // Lanes (plan sec. 28, 2026-10-02; lanes.ts). A lane is "a new base of a transition":
@@ -186,8 +188,8 @@ function setPassLane(e) {
186
188
  * Cleared by the flush the join schedules, and at the end of a flush that
187
189
  * set it. */ let passTx = null;
188
190
 
189
- function joinPassTx(e, n) {
190
- if (globalQueue.q && !n || flushTransaction !== null) return joinFuture(e);
191
+ function joinPassTx(e, o) {
192
+ if (globalQueue.q && !o || flushTransaction !== null) return joinFuture(e);
191
193
  if (passTx === null) passTx = resolveTx(e); else merge(resolveTx(e), passTx);
192
194
  schedule();
193
195
  }
@@ -204,37 +206,37 @@ function joinPassTx(e, n) {
204
206
  }
205
207
 
206
208
  function resolveTx(e) {
207
- while (e.cn !== null) e = e.cn;
209
+ while (e.eo !== null) e = e.eo;
208
210
  return e;
209
211
  }
210
212
 
211
213
  /** The flush joins transaction `t` — or needs one of its own (`null`: a pass
212
214
  * went pending with nothing held). Joining a second one merges it into the
213
215
  * flush's: the work that touched both entangles them (A15). */ function joinFuture(e) {
214
- const n = flushTransaction;
215
- if (n === null) {
216
+ const o = flushTransaction;
217
+ if (o === null) {
216
218
  flushTransaction = e === null ? newTransaction(false) : resolveTx(e);
217
- } else if (e !== null) merge(resolveTx(e), n);
219
+ } else if (e !== null) merge(resolveTx(e), o);
218
220
  if (!globalQueue.q) schedule();
219
221
  }
220
222
 
221
223
  /** `t` merges into `f`: the work that touched both entangles them (A15).
222
224
  * A lane never merges: it is a sub-frame, not a peer — its work is routed
223
- * per pass (`passLane`). */ function merge(e, n) {
225
+ * per pass (`passLane`). */ function merge(e, o) {
224
226
  // Both ends resolved: a stale pointer to a transaction already merged
225
227
  // into the other would close a cycle `resolveTx` never leaves.
226
228
  e = resolveTx(e);
227
- n = resolveTx(n);
228
- if (e === n || e.V || n.V) return;
229
- if (attrHooks !== null) attrHooks.transitionMerged(n, e);
230
- e.cn = n;
231
- n.tt += e.tt;
232
- n.nt ||= e.nt;
233
- append(n.je, e.je);
234
- append(n.un[0], e.un[0]);
235
- append(n.un[1], e.un[1]);
236
- append(n.En, e.En);
237
- if (e.I !== null) append(n.I ??= [], e.I);
229
+ o = resolveTx(o);
230
+ if (e === o || e.V || o.V) return;
231
+ if (attrHooks !== null) attrHooks.transitionMerged(o, e);
232
+ e.eo = o;
233
+ o.tt += e.tt;
234
+ o.nt ||= e.nt;
235
+ append(o.je, e.je);
236
+ append(o.un[0], e.un[0]);
237
+ append(o.un[1], e.un[1]);
238
+ append(o.En, e.En);
239
+ if (e.I !== null) append(o.I ??= [], e.I);
238
240
  transactions.splice(transactions.indexOf(e), 1);
239
241
  }
240
242
 
@@ -256,13 +258,13 @@ function resolveTx(e) {
256
258
  * in for the flight (A17: "visible until its own fetch settles"). */ function blocked(e) {
257
259
  // The transaction being judged (a lane consulted on its behalf judges for
258
260
  // it): a zombie whose removal it stages is moot for it alone (`onScreen`).
259
- const n = judge;
261
+ const o = judge;
260
262
  judge ??= e;
261
263
  // An action still running in it holds it open (action.ts). Its lanes
262
264
  // (lanes.ts): a lane under `t` blocks it while blocked itself.
263
- const o = e.tt !== 0 || blockedBy(e.je, e) || !!lanesBlockedHook?.(e);
264
- judge = n;
265
- return o;
265
+ const n = e.tt !== 0 || blockedBy(e.je, e) || !!lanesBlockedHook?.(e);
266
+ judge = o;
267
+ return n;
266
268
  }
267
269
 
268
270
  let judge = null;
@@ -270,12 +272,12 @@ let judge = null;
270
272
  /** Observe tier: the flights `t` waits on — its pending held sources a
271
273
  * frame reader observes (`blockedBy`'s predicate, collected; the readers
272
274
  * themselves are not sources). */ function blockersOf(e) {
273
- const n = [];
274
- for (let o = 0; o < e.je.length; o++) {
275
- const t = e.je[o];
276
- if (!t.i && t.m & STATUS_PENDING && !(t.R & REACTIVE_DISPOSED) && (t.T.Je === null || txOf(t) === e) && blockedBy([ t ], e)) n.push(t);
275
+ const o = [];
276
+ for (let n = 0; n < e.je.length; n++) {
277
+ const t = e.je[n];
278
+ if (!t.i && t.m & STATUS_PENDING && !(t.R & REACTIVE_DISPOSED) && (t.T.Je === null || txOf(t) === e) && blockedBy([ t ], e)) o.push(t);
277
279
  }
278
- return n;
280
+ return o;
279
281
  }
280
282
 
281
283
  /** `t`'s own flights only — not its lanes': a flight the frame asked for
@@ -288,28 +290,28 @@ let judge = null;
288
290
 
289
291
  /** A source the pending node waits on that is `t`'s (held by it, or by no
290
292
  * one) — not another transaction's or lane's work. Itself, when it is the
291
- * source. */ function ownSource(e, n) {
292
- const o = e.T?.fe;
293
- if (o === undefined) return true;
294
- for (const e of o) {
295
- const o = e.T?.Je;
296
- if (o == null || resolveTx(o) === n) return true;
293
+ * source. */ function ownSource(e, o) {
294
+ const n = e.T?.fe;
295
+ if (n === undefined) return true;
296
+ for (const e of n) {
297
+ const n = e.T?.Je;
298
+ if (n == null || resolveTx(n) === o) return true;
297
299
  }
298
300
  return false;
299
301
  }
300
302
 
301
- function blockedBy(e, n, o = false) {
303
+ function blockedBy(e, o, n = false) {
302
304
  for (let t = 0; t < e.length; t++) {
303
305
  const l = e[t];
304
- if (!(l.m & STATUS_PENDING) || l.R & REACTIVE_DISPOSED || l.l & CONFIG_GUESS || l.T.Je !== null && txOf(l) !== n || o && !ownSource(l, n)) continue;
306
+ if (!(l.m & STATUS_PENDING) || l.R & REACTIVE_DISPOSED || l.l & CONFIG_GUESS || l.T.Je !== null && txOf(l) !== o || n && !ownSource(l, o)) continue;
305
307
  // The frame's own pending observer (off screen: this one holds nothing;
306
308
  // the next listed node may).
307
309
  if (l.i === EFFECT_RENDER) {
308
- if (onScreen(l, judge ?? n)) return true;
310
+ if (onScreen(l, judge ?? o)) return true;
309
311
  continue;
310
312
  }
311
313
  for (let e = l.o; e !== null; e = e.t) {
312
- const o = e.u;
314
+ const n = e.u;
313
315
  // A frame reader: a render effect, or a stale reader of the flight
314
316
  // (REACTIVE_FRAME_READ — a verdict reader, `observeFlight`; lane work,
315
317
  // `frameRead`): served committed instead of going pending, it derives
@@ -321,7 +323,7 @@ function blockedBy(e, n, o = false) {
321
323
  // lane-membership ruling, 2026-10-05). An output that never committed
322
324
  // shows nothing — a mount the frame has not revealed — and holds
323
325
  // nothing.
324
- if ((o.i === EFFECT_RENDER && o.m & STATUS_PENDING || o.R & REACTIVE_FRAME_READ || o.l & CONFIG_REDERIVE && o.m === STATUS_PENDING) && (e.Te === o.Ne || o.T?.F != null) && !(o.R & REACTIVE_DISPOSED) && onScreen(o, judge ?? n)) return true;
326
+ if ((n.i === EFFECT_RENDER && n.m & STATUS_PENDING || n.R & REACTIVE_FRAME_READ || n.l & CONFIG_REDERIVE && n.m === STATUS_PENDING) && (e.Te === n.Ne || n.T?.F != null) && !(n.R & REACTIVE_DISPOSED) && onScreen(n, judge ?? o)) return true;
325
327
  }
326
328
  }
327
329
  return false;
@@ -334,8 +336,8 @@ function blockedBy(e, n, o = false) {
334
336
  * is visible, a lane's reveal included: only the commit that disposes it
335
337
  * makes its say moot), and not behind a fallback (A33: a boundary showing its fallback is the display of
336
338
  * everything under it, so a reader there is not on screen and holds
337
- * nothing — boundaries.ts). */ function onScreen(e, n) {
338
- return !(e.R & REACTIVE_ZOMBIE && removalStagedBy(e, n)) && !hiddenHook?.(e);
339
+ * nothing — boundaries.ts). */ function onScreen(e, o) {
340
+ return !(e.R & REACTIVE_ZOMBIE && removalStagedBy(e, o)) && !hiddenHook?.(e);
339
341
  }
340
342
 
341
343
  /** A15 (#3463): a reader whose removal is staged — a zombie, parked by a
@@ -343,10 +345,10 @@ function blockedBy(e, n, o = false) {
343
345
  * except the transaction staging its removal: for that one its say is moot
344
346
  * (done, and the commit disposes it; not done, and it stays parked). The
345
347
  * staging transaction is the one holding the pass that parked the frame:
346
- * the nearest non-zombie ancestor. */ function removalStagedBy(e, n) {
347
- let o = e;
348
- while (o?.R & REACTIVE_ZOMBIE) o = o._parent;
349
- return o?.l & CONFIG_HELD && o.T?.Je != null && txOf(o) === resolveTx(n);
348
+ * the nearest non-zombie ancestor. */ function removalStagedBy(e, o) {
349
+ let n = e;
350
+ while (n?.R & REACTIVE_ZOMBIE) n = n._parent;
351
+ return n?.l & CONFIG_HELD && n.T?.Je != null && txOf(n) === resolveTx(o);
350
352
  }
351
353
 
352
354
  /** Unchanged passes this flush whose stale dependency tail awaits the
@@ -372,18 +374,18 @@ function deferZombie(e) {
372
374
  }
373
375
 
374
376
  /** Put a staged node in a transaction: flagged, stamped, on its landing
375
- * list. */ function holdNode(e, n) {
377
+ * list. */ function holdNode(e, o) {
376
378
  e.l |= CONFIG_HELD;
377
- list(e, n);
379
+ list(e, o);
378
380
  }
379
381
 
380
382
  /** Put a node on a transaction's list, once: a node's `_transaction` is the
381
383
  * transaction whose `_nodes` it is on (every site that sets one does the
382
- * other), so a re-pass, a re-landing or a re-staging adds nothing. */ function list(e, n) {
383
- const o = ext(e);
384
- if (o.Je === n) return;
385
- o.Je = n;
386
- n.je.push(e);
384
+ * other), so a re-pass, a re-landing or a re-staging adds nothing. */ function list(e, o) {
385
+ const n = ext(e);
386
+ if (n.Je === o) return;
387
+ n.Je = o;
388
+ o.je.push(e);
387
389
  }
388
390
 
389
391
  /** Hold the (uncommitted) frame under a held pass: every computed in the
@@ -398,22 +400,22 @@ function deferZombie(e) {
398
400
  * and `dissolveLane` no longer finds it — a lane node with no lane, which the
399
401
  * next read of it trips over (`laneRead` → `txOf`). Its frame is the lane's
400
402
  * too (ruling A: a lane pass's children are the lane's), revealed and
401
- * retired at the lane's seam. */ function holdFrame(e, n) {
402
- for (let o = e.ze; o !== null; o = o.Xe) {
403
- if (o.T?.Je?.V) continue;
404
- if (o.X !== undefined) {
405
- if (!(o.l & CONFIG_HELD)) holdNode(o, n);
406
- o.l |= CONFIG_HELD_FRAME;
403
+ * retired at the lane's seam. */ function holdFrame(e, o) {
404
+ for (let n = e.ze; n !== null; n = n.Xe) {
405
+ if (n.T?.Je?.V) continue;
406
+ if (n.X !== undefined) {
407
+ if (!(n.l & CONFIG_HELD)) holdNode(n, o);
408
+ n.l |= CONFIG_HELD_FRAME;
407
409
  }
408
- holdFrame(o, n);
410
+ holdFrame(n, o);
409
411
  }
410
412
  }
411
413
 
412
414
  /** The nodes under `owner` a transaction other than `t` holds (boundaries.ts:
413
- * what an earlier hold staged under a re-armed boundary). */ function heldUnder(e, n, o) {
415
+ * what an earlier hold staged under a re-armed boundary). */ function heldUnder(e, o, n) {
414
416
  for (let t = e.ze; t !== null; t = t.Xe) {
415
- if ((t.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && txOf(t) !== n) o(t);
416
- heldUnder(t, n, o);
417
+ if ((t.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && txOf(t) !== o) n(t);
418
+ heldUnder(t, o, n);
417
419
  }
418
420
  }
419
421
 
@@ -510,7 +512,9 @@ function schedule() {
510
512
  function haltReactivity(e) {
511
513
  if (halted) return;
512
514
  halted = true;
513
- let n = "[REACTIVITY_HALTED]";
515
+ // A cleanup error collected before the halt is dropped (owner.ts).
516
+ rethrowCleanupError(true);
517
+ let o = "[REACTIVITY_HALTED]";
514
518
  // Surface the cause here too: callers rethrow it, but a creation-time throw
515
519
  // unwinds through ancestor recomputes that convert it to status instead of
516
520
  // surfacing it (#2884), so the rethrow alone cannot guarantee visibility.
@@ -520,9 +524,18 @@ function haltReactivity(e) {
520
524
  // nothing an app or its telemetry can act on (#3338 — an uncaught throw
521
525
  // during the hydration render). The rethrow may reach the top as well in
522
526
  // the non-swallowed cases; a duplicate report beats a silent one.
523
- const o = e !== undefined && globalThis.reportError;
524
- o || e === undefined ? console.error(n) : console.error(n, e);
525
- o && o(e);
527
+ const n = e !== undefined && globalThis.reportError;
528
+ n || e === undefined ? console.error(o) : console.error(o, e);
529
+ n && n(e);
530
+ }
531
+
532
+ /**
533
+ * Dev: `haltReactivity`, located at `subject` — the computation the uncaught
534
+ * error escaped from — for the REACTIVITY_HALTED message's owner path. Call
535
+ * sites pick it in dev builds only (`haltReactivity(e)` in production), so
536
+ * production keeps the one-argument halt and its bytes unchanged.
537
+ */ function haltReactivityAt(e, o) {
538
+ haltReactivity(e);
526
539
  }
527
540
 
528
541
  // Logs on the first write after a halt so a frozen interaction is traceable.
@@ -595,13 +608,13 @@ class GlobalQueue {
595
608
  // A lane's own flush (its flight landing, its reveal) parks nothing: the
596
609
  // lane's work reached it per pass; the frame's plain writes commit.
597
610
  // (Resolved: a guess over another transaction's guess merged them.)
598
- const n = e !== null && !e.V ? resolveTx(e) : null;
599
- if (n !== null) {
611
+ const o = e !== null && !e.V ? resolveTx(e) : null;
612
+ if (o !== null) {
600
613
  // `t._nodes` grows under the walk (holdFrame pushes); the bound is
601
614
  // this flush's list, which does not.
602
615
  const e = pendingNodes.length;
603
- for (let o = 0; o < e; o++) {
604
- const e = pendingNodes[o];
616
+ for (let n = 0; n < e; n++) {
617
+ const e = pendingNodes[n];
605
618
  // Already held: the pass that re-staged it joined its transaction,
606
619
  // which is `t` (or merged into it). Lane work is the lane's (a
607
620
  // pending mark propagated onto it queued it here too).
@@ -629,15 +642,15 @@ class GlobalQueue {
629
642
  e.l &= ~CONFIG_STAGED;
630
643
  continue;
631
644
  }
632
- holdNode(e, n);
645
+ holdNode(e, o);
633
646
  }
634
647
  // A held pass's frame is held with it (ruling A, O2): the children it
635
648
  // created are the transaction's — they compute, and a pass over them
636
649
  // or a write that reaches them joins; they effect at the landing.
637
- if (e.l & CONFIG_STAGED) holdFrame(e, n);
650
+ if (e.l & CONFIG_STAGED) holdFrame(e, o);
638
651
  }
639
652
  pendingNodes.length = 0;
640
- storeParkHook?.(n);
653
+ storeParkHook?.(o);
641
654
  // (This flush's runs are stashed with `t` below — after the landings,
642
655
  // so a `t` that lands at this very seam runs them first, ahead of
643
656
  // the runs it held from earlier flushes.)
@@ -648,10 +661,10 @@ class GlobalQueue {
648
661
  // holder's verdict lane's work — before the lane seams, which reveal
649
662
  // it (nothing commits this seam that could dispose it).
650
663
  // (Verdict readers exist only with the verdict engine installed.)
651
- const o = deferredZombies.splice(0);
664
+ const n = deferredZombies.splice(0);
652
665
  if (observeFlightHook) {
653
- o.sort((e, n) => e.Et - n.Et);
654
- for (let e = 0; e < o.length; e++) if ((o[e].R & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE)) === REACTIVE_ZOMBIE && o[e].l & CONFIG_VERDICT) recompute(o[e]);
666
+ n.sort((e, o) => e.Et - o.Et);
667
+ for (let e = 0; e < n.length; e++) if ((n[e].R & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE)) === REACTIVE_ZOMBIE && n[e].l & CONFIG_VERDICT) recompute(n[e]);
655
668
  }
656
669
  // The unchanged passes' tails stay linked (A30, #3469): their inputs
657
670
  // are held, and the committed frame still derives from them — no trim.
@@ -663,8 +676,8 @@ class GlobalQueue {
663
676
  // left an effect owing a run (`runEffect` trims when the run applies):
664
677
  // `recompute`'s own gates.
665
678
  for (let e = 0; e < heldTrims.length; e++) {
666
- const n = heldTrims[e];
667
- if (n.T?.F == null && !n.Qe) trimStaleDeps(n);
679
+ const o = heldTrims[e];
680
+ if (o.T?.F == null && !o.Qe) trimStaleDeps(o);
668
681
  }
669
682
  }
670
683
  // The list is the flush's either way. Reset only when there is something
@@ -678,22 +691,22 @@ class GlobalQueue {
678
691
  // removes its entry.
679
692
  // This flush's own runs, the lanes' reveals, and the landings' held runs
680
693
  // are collected apart and ordered below.
681
- const o = this.un;
694
+ const n = this.un;
682
695
  const t = this.un = [ [], [] ];
683
696
  // The passes that read this frame's stagings as the screen read held
684
697
  // writes if it parked: the lane seam re-derives them on the committed
685
698
  // world (a lane's runs wait this round).
686
699
  if (laneSeamsHook) {
687
- laneSeamsHook(n !== null ? stagedReaders : null);
700
+ laneSeamsHook(o !== null ? stagedReaders : null);
688
701
  if (stagedReaders.length) stagedReaders.length = 0;
689
702
  }
690
703
  this.un = [ [], [] ];
691
704
  for (let e = transactions.length - 1; e >= 0; e--) {
692
- const n = transactions[e];
693
- if (blocked(n)) continue;
705
+ const o = transactions[e];
706
+ if (blocked(o)) continue;
694
707
  transactions.splice(e, 1);
695
- endLanesHook?.(n);
696
- land(n);
708
+ endLanesHook?.(o);
709
+ land(o);
697
710
  // A landing's commits can dispose a zombie a transaction judged above
698
711
  // was blocked on (#3463: live "until the commit that disposes it") —
699
712
  // judge them again.
@@ -706,7 +719,7 @@ class GlobalQueue {
706
719
  // parked (something async below) — the window is the frame's, the marks
707
720
  // are its transaction's; else they release at the seam, verdict-only,
708
721
  // nothing to show (affects.ts).
709
- const l = n && liveTx(n);
722
+ const l = o && liveTx(o);
710
723
  releaseAmbientMarksHook?.(l);
711
724
  // The effect phase: lane work first (displayed ahead of the frame), then
712
725
  // this flush's runs, then the runs the landings held from earlier
@@ -725,19 +738,19 @@ class GlobalQueue {
725
738
  holding = true;
726
739
  }
727
740
  for (let e = 0; e < 2; e++) {
728
- if (s) append(l.un[e], o[e]);
741
+ if (s) append(l.un[e], n[e]);
729
742
  // The plain flush — no reveal, no landing, not parked — has nothing to
730
743
  // order around its own runs: they are the queue as they stand (the
731
744
  // same array, not a copy; `run` takes it whole). Every other seam
732
745
  // builds the ordered queue above.
733
- this.un[e] = s || t[e].length || this.un[e].length ? t[e].concat(s ? [] : o[e], this.un[e]) : o[e];
746
+ this.un[e] = s || t[e].length || this.un[e].length ? t[e].concat(s ? [] : n[e], this.un[e]) : n[e];
734
747
  }
735
748
  if (deferredZombies.length !== 0) {
736
749
  // Survivors are the displayed frame of a held pass: they rerun for the
737
750
  // live write, parents before children.
738
- const e = deferredZombies.splice(0).sort((e, n) => e.Et - n.Et);
739
- for (let n = 0; n < e.length; n++) {
740
- if (!(e[n].R & REACTIVE_DISPOSED)) recompute(e[n]);
751
+ const e = deferredZombies.splice(0).sort((e, o) => e.Et - o.Et);
752
+ for (let o = 0; o < e.length; o++) {
753
+ if (!(e[o].R & REACTIVE_DISPOSED)) recompute(e[o]);
741
754
  }
742
755
  }
743
756
  // Boundaries: readers that settled, landed or died this flush are
@@ -745,18 +758,18 @@ class GlobalQueue {
745
758
  boundarySeamHook?.();
746
759
  }
747
760
  run(e) {
748
- const n = this.un[e - 1];
749
- if (n.length) {
761
+ const o = this.un[e - 1];
762
+ if (o.length) {
750
763
  this.un[e - 1] = [];
751
- runQueue(n, e);
764
+ runQueue(o, e);
752
765
  }
753
766
  }
754
- enqueue(e, n, o = passLane) {
767
+ enqueue(e, o, n = passLane) {
755
768
  // Lane work's runs are the lane's: they run at its reveal, not with
756
769
  // the frame (which may park). A lane pass that read the frame's own
757
770
  // staging is the frame's (`recompute` passes null): its run waits with
758
771
  // the frame.
759
- if (e) (o !== null ? o : this).un[e - 1].push(n);
772
+ if (e) (n !== null ? n : this).un[e - 1].push(o);
760
773
  schedule();
761
774
  }
762
775
  /** Status reaching the root: pending is absorbed (the root mount defers —
@@ -772,10 +785,10 @@ class GlobalQueue {
772
785
  *
773
786
  * A boundary between the observer and the root (boundaries.ts) is asked
774
787
  * first: a fallback catches what is not ready under it, and the root
775
- * never hears of it. */ notify(e, n, o, t) {
776
- if (catchHook?.(e, o & n, t)) return true;
777
- if (n & STATUS_PENDING) {
778
- if (o & STATUS_PENDING) {
788
+ * never hears of it. */ notify(e, o, n, t) {
789
+ if (catchHook?.(e, n & o, t)) return true;
790
+ if (o & STATUS_PENDING) {
791
+ if (n & STATUS_PENDING) {
779
792
  // A pass that went pending holds its frame: the async's own
780
793
  // synchronous frame is not ready (A15 reveal corollary: a reveal
781
794
  // that discovers a flight whose inputs are visible holds and joins
@@ -807,20 +820,20 @@ class GlobalQueue {
807
820
  * for it is void (#3322: an effect has one value slot, and the stale pass
808
821
  * overwrote the value that run was for) — the re-derivation's run replaces
809
822
  * it. */ function reruns(e) {
810
- for (let n = 0; n < e.En.length; n++) {
811
- const o = e.En[n];
823
+ for (let o = 0; o < e.En.length; o++) {
824
+ const n = e.En[o];
812
825
  // An errored reader's display is its error (D-39): the landing gives it
813
826
  // nothing to re-derive, and a re-run would be a read that retries the
814
827
  // errored source (#3945).
815
- if (o.R & REACTIVE_DISPOSED || o.m & STATUS_ERROR) continue;
816
- if (o.R & REACTIVE_FRAME_READ || o.l & CONFIG_VERDICT) {
817
- o.R &= ~REACTIVE_FRAME_READ;
818
- o.Qe = false;
828
+ if (n.R & REACTIVE_DISPOSED || n.m & STATUS_ERROR) continue;
829
+ if (n.R & REACTIVE_FRAME_READ || n.l & CONFIG_VERDICT) {
830
+ n.R &= ~REACTIVE_FRAME_READ;
831
+ n.Qe = false;
819
832
  // Its frame is replaced by the re-derivation: a run one of its
820
833
  // children queued this flush (re-run by the same landing) is for the
821
834
  // frame being replaced — void too (#3404).
822
- for (let e = o.ze; e !== null; e = e.Xe) if (e.i) e.Qe = false;
823
- enqueueSub(o);
835
+ for (let e = n.ze; e !== null; e = e.Xe) if (e.i) e.Qe = false;
836
+ enqueueSub(n);
824
837
  }
825
838
  }
826
839
  e.En.length = 0;
@@ -835,11 +848,11 @@ class GlobalQueue {
835
848
  // Old children die in the commits (cleanups first), then the stashed
836
849
  // effects run ahead of this flush's own. A node a lane took over since
837
850
  // (a guess written over the staged truth) is the lane's to land.
838
- for (let n = 0; n < e.je.length; n++) {
839
- const o = e.je[n];
840
- if (o.T.Je === null || txOf(o) !== e) continue;
841
- o.T.Je = null;
842
- commitPendingNode(o);
851
+ for (let o = 0; o < e.je.length; o++) {
852
+ const n = e.je[o];
853
+ if (n.T.Je === null || txOf(n) !== e) continue;
854
+ n.T.Je = null;
855
+ commitPendingNode(n);
843
856
  }
844
857
  // A cleanup those commits ran that wrote a node `u` still held joined
845
858
  // `u`, which has left `transactions`: the write is this landing's, and
@@ -852,14 +865,14 @@ class GlobalQueue {
852
865
  * landing's re-passes, in height order): the frame's effects run as one
853
866
  * pass would have queued them — a shell re-derived at the landing before a
854
867
  * boundary swap held since the write (#3540's log order). */ function releaseQueues(e) {
855
- for (let n = 0; n < 2; n++) {
856
- append(globalQueue.un[n], e.un[n]);
857
- e.un[n] = [];
868
+ for (let o = 0; o < 2; o++) {
869
+ append(globalQueue.un[o], e.un[o]);
870
+ e.un[o] = [];
858
871
  }
859
872
  }
860
873
 
861
- /** `a.push(...b)` without the argument-count limit. */ function append(e, n) {
862
- for (let o = 0; o < n.length; o++) e.push(n[o]);
874
+ /** `a.push(...b)` without the argument-count limit. */ function append(e, o) {
875
+ for (let n = 0; n < o.length; n++) e.push(o[n]);
863
876
  }
864
877
 
865
878
  /** Nodes staged this flush (`_pendingValue` set), committed at its end. */ const pendingNodes = [];
@@ -871,10 +884,10 @@ class GlobalQueue {
871
884
 
872
885
  /** A15's stale reader: `c` was served the committed value of a node `t`
873
886
  * holds (a render effect, a verdict reader, lane work) and is re-derived
874
- * after `t`'s landing (`_reruns`). Once per pass. */ function staleReader(e, n) {
887
+ * after `t`'s landing (`_reruns`). Once per pass. */ function staleReader(e, o) {
875
888
  if (!(e.R & REACTIVE_FRAME_READ)) {
876
889
  e.R |= REACTIVE_FRAME_READ;
877
- n.En.push(e);
890
+ o.En.push(e);
878
891
  }
879
892
  }
880
893
 
@@ -900,13 +913,13 @@ function insertSubs(e) {
900
913
  // Presence bits gate the optional-slot probes (see constants.ts): one
901
914
  // masked read of the always-present _config instead of missing-property
902
915
  // lookups in the hottest notify loop.
903
- const n = e.l;
904
- const o = (n & CONFIG_HAS_SNAPSHOT) !== 0 && e.T?.lt !== undefined;
916
+ const o = e.l;
917
+ const n = (o & CONFIG_HAS_SNAPSHOT) !== 0 && e.T?.lt !== undefined;
905
918
  // Observe-tier fan-out: this walk visits every subscriber edge anyway, so
906
919
  // the graph-size count is one local increment here and no field anywhere.
907
920
  let t = 0;
908
- for (let n = e.o; n !== null; n = n.t) {
909
- const e = n.u;
921
+ for (let o = e.o; o !== null; o = o.t) {
922
+ const e = o.u;
910
923
  t++;
911
924
  // Missed-wake latch (#3037): this write is landing while the subscriber
912
925
  // is mid-recompute (a nested pull committing beneath its reads), and the
@@ -916,8 +929,8 @@ function insertSubs(e) {
916
929
  // pass either re-reads them fresh or trims them), and neither does the
917
930
  // tail link: it is the read IN FLIGHT — read() links before it pulls, so
918
931
  // this very commit is what that read returns.
919
- if (e.R & REACTIVE_RECOMPUTING_DEPS && n.Te === e.Ne && n !== e._) e.R |= REACTIVE_MISSED_WAKE;
920
- if (o && e.l & CONFIG_IN_SNAPSHOT_SCOPE) {
932
+ if (e.R & REACTIVE_RECOMPUTING_DEPS && o.Te === e.Ne && o !== e._) e.R |= REACTIVE_MISSED_WAKE;
933
+ if (n && e.l & CONFIG_IN_SNAPSHOT_SCOPE) {
921
934
  e.R |= REACTIVE_SNAPSHOT_STALE;
922
935
  continue;
923
936
  }
@@ -933,25 +946,25 @@ function insertSubs(e) {
933
946
  * inert for its pass (`recompute` seats a lane only from its own `_x`), it
934
947
  * is a live write if the effect becomes a zombie later this flush — an
935
948
  * on-screen reader whose removal an adopted write stages follows the lane
936
- * (the lane-membership ruling, 2026-10-05; `_update`). */ function laneDirty(e, n) {
937
- for (let o = e.o; o !== null; o = o.t) {
938
- const e = o.u.T?.Je;
939
- if (e?.V ? sameLane(e, n) : o.u.i) o.u.R |= REACTIVE_LANE_DIRTY;
949
+ * (the lane-membership ruling, 2026-10-05; `_update`). */ function laneDirty(e, o) {
950
+ for (let n = e.o; n !== null; n = n.t) {
951
+ const e = n.u.T?.Je;
952
+ if (e?.V ? sameLane(e, o) : n.u.i) n.u.R |= REACTIVE_LANE_DIRTY;
940
953
  }
941
954
  }
942
955
 
943
- /** One lane, or two of one link group (#3335): one reveal unit. */ function sameLane(e, n) {
944
- return e === n || e.nn !== null && e.nn === n.nn;
956
+ /** One lane, or two of one link group (#3335): one reveal unit. */ function sameLane(e, o) {
957
+ return e === o || e.nn !== null && e.nn === o.nn;
945
958
  }
946
959
 
947
960
  function commitPendingNode(e) {
948
- const n = e;
961
+ const o = e;
949
962
  // L2: the commit is where a pass, held or not, stops being uncommitted —
950
963
  // unless it has no answer yet. A pass still in flight (pending, nothing
951
964
  // staged) stays uncommitted: a re-pass disposes its children on the spot,
952
965
  // and the frame it parked stays the frame until a pass of this node
953
966
  // commits a value.
954
- if (!n.X) {
967
+ if (!o.X) {
955
968
  e.l &= -385;
956
969
  if (e.ue !== NOT_PENDING) {
957
970
  e.Ie = e.ue;
@@ -962,62 +975,62 @@ function commitPendingNode(e) {
962
975
  // Computeds only from here (`_statusFlags` is not a signal field). A
963
976
  // commit beneath a flight publishes its inputs (A15 reveal corollary,
964
977
  // #3305): a reveal of it must observe, not show the pre-flight value.
965
- const o = e.ue === NOT_PENDING && (n.m & STATUS_PENDING) !== 0;
978
+ const n = e.ue === NOT_PENDING && (o.m & STATUS_PENDING) !== 0;
966
979
  const t = e.l & CONFIG_STAGED_ERROR;
967
980
  // An answer commits: the error it replaces leaves the display (D-41).
968
- if (!o && n.T) n.T.de = undefined;
969
- e.l = o ? e.l & -1048833 | CONFIG_INPUTS_PUBLISHED : e.l & -3147137;
981
+ if (!n && o.T) o.T.de = undefined;
982
+ e.l = n ? e.l & -1048833 | CONFIG_INPUTS_PUBLISHED : e.l & -3147137;
970
983
  // D-41: the staged answer is an error — published now, when a value would
971
984
  // show, through the ordinary error path (`stageError`). Its readers first
972
985
  // see it in the cycle after this seam: a tracked re-read there is not a
973
986
  // retry (`read`), or a boundary re-derived by it would retry it forever.
974
987
  if (t) {
975
- const o = e.ue;
988
+ const n = e.ue;
976
989
  e.ue = NOT_PENDING;
977
990
  e.ie = clock + 1;
978
- notifyStatus(n, STATUS_ERROR, o);
991
+ notifyStatus(o, STATUS_ERROR, n);
979
992
  } else if (e.ue !== NOT_PENDING) {
980
993
  e.Ie = e.ue;
981
994
  e.ue = NOT_PENDING;
982
995
  // A node born staged (recompute) initializes at this commit.
983
- n.m &= ~STATUS_UNINITIALIZED;
996
+ o.m &= ~STATUS_UNINITIALIZED;
984
997
  // A quiet re-ask's classification survives its landing and dies with
985
998
  // the landed value's commit (A19 exc. 2, #3178): verdict-quiet through
986
999
  // the reveal. (The sweep of a node still in flight is not a landing.)
987
1000
  // The A28 stash goes with it.
988
- if (n.T != null) n.T.ut = false, n.T.Gt = NOT_PENDING;
1001
+ if (o.T != null) o.T.ut = false, o.T.Gt = NOT_PENDING;
989
1002
  // An effect with a staged value was born held (L2): its first run is
990
1003
  // this commit's, not its creation's (A29) — queue it now.
991
1004
  if (e.i && e.i !== EFFECT_TRACKED) {
992
1005
  e.Qe = true;
993
- globalQueue.enqueue(e.i, e.W ??= GlobalQueue.K.bind(null, n));
1006
+ globalQueue.enqueue(e.i, e.W ??= GlobalQueue.K.bind(null, o));
994
1007
  }
995
1008
  }
996
1009
  // The committed value is the first observable answer for a loading-window
997
1010
  // node — the window closes here, not at compute time (#2990). Unconditional
998
1011
  // store to an always-present computed slot.
999
- n.ce = false;
1000
- n.R &= ~REACTIVE_MANUAL_WRITE;
1012
+ o.ce = false;
1013
+ o.R &= ~REACTIVE_MANUAL_WRITE;
1001
1014
  // The dependencies of the pass that produced the value are the frame's now:
1002
1015
  // the previous frame's tail goes (A30, #3410; `recompute` left it for a
1003
1016
  // staged pass). Only after a clean pass: `_error` is cleared by a clean
1004
1017
  // pass or by the node's own landing (whose pass was clean), so a set
1005
1018
  // `_error` means the last pass threw, kept its full list, and `_depsTail`
1006
1019
  // marks where it stopped.
1007
- if (n.T?.F == null) trimStaleDeps(n);
1008
- if (!(n.m & STATUS_PENDING)) n.m &= ~STATUS_UNINITIALIZED;
1020
+ if (o.T?.F == null) trimStaleDeps(o);
1021
+ if (!(o.m & STATUS_PENDING)) o.m &= ~STATUS_UNINITIALIZED;
1009
1022
  // L2: the children this commit publishes are the frame's now — the frame
1010
1023
  // they replace, parked by the pass (`recompute`), goes.
1011
- if (!o && n.T != null && (n.T.Nt !== null || n.T.ft !== null)) GlobalQueue.et(n, false, true);
1024
+ if (!n && o.T != null && (o.T.Nt !== null || o.T.ft !== null)) GlobalQueue.et(o, false, true);
1012
1025
  }
1013
1026
 
1014
1027
  function commitPendingNodes() {
1015
1028
  for (let e = 0; e < pendingNodes.length; e++) {
1016
- const n = pendingNodes[e];
1029
+ const o = pendingNodes[e];
1017
1030
  // A node a lane pass re-staged this round is the lane's to reveal; one
1018
1031
  // a transaction holds (born held by a mainline pass, `passTx`) is that
1019
1032
  // transaction's to commit, at its landing.
1020
- if (!(n.l & (CONFIG_OVERRIDE | CONFIG_HELD))) commitPendingNode(n);
1033
+ if (!(o.l & (CONFIG_OVERRIDE | CONFIG_HELD))) commitPendingNode(o);
1021
1034
  }
1022
1035
  pendingNodes.length = 0;
1023
1036
  }
@@ -1049,24 +1062,27 @@ function flush(e) {
1049
1062
  // Attribution: whether this call drained anything, so `flushEnd` fires once
1050
1063
  // per real drain and never for a no-op call. The declaration is dead in
1051
1064
  // prod (its only write is behind true) and rollup drops it.
1052
- let n = false;
1065
+ let o = false;
1053
1066
  // The drain's opening instant, under the loop's own condition so it fires
1054
1067
  // exactly when `flushEnd` below will. Outside every try (see the rule in
1055
1068
  // attribution-hooks.ts).
1056
1069
  if (attrHooks !== null && scheduled) attrHooks.flushStart();
1057
1070
  while (scheduled) {
1058
1071
  globalQueue.flush();
1059
- n = true;
1072
+ o = true;
1060
1073
  }
1061
1074
  // Outside every try in this function (see the rule in attribution-hooks.ts):
1062
1075
  // the drain loop above is the one place all scheduled work funnels through,
1063
1076
  // so this is the "committed and effects ran" instant for everything the
1064
1077
  // loop processed.
1065
- if (n && attrHooks !== null) attrHooks.flushEnd();
1078
+ if (o && attrHooks !== null) attrHooks.flushEnd();
1079
+ // A cleanup that threw during the drain, now that the drain is complete
1080
+ // (owner.ts).
1081
+ rethrowCleanupError();
1066
1082
  }
1067
1083
 
1068
- function runQueue(e, n) {
1069
- for (let o = 0; o < e.length; o++) e[o](n);
1084
+ function runQueue(e, o) {
1085
+ for (let n = 0; n < e.length; n++) e[n](o);
1070
1086
  }
1071
1087
 
1072
- export { GlobalQueue, MAINLINE_QUESTION, ROOT_ERROR_HOOK, actionDepth, applyGuessesHook, blocked, blockersOf, boundarySeamHook, bumpNotifyEpoch, catchHook, clock, commitPendingNode, deferZombie, dirtyQueue, endLanesHook, enforceLoadingBoundary, enterAction, exitAction, externalUntrackHook, flush, flushTransaction, freshHook, globalQueue, haltReactivity, heldRunHook, heldTrim, heldUnder, hiddenHook, holdNode, insertSubs, installBoundaryHooks, installLaneHooks, installMarkHooks, installStoreHooks, installVerdictHooks, joinFuture, joinPassTx, laneCorrectionsHook, laneDirty, laneOutcomeHook, laneReadHook, laneSeamsHook, laneStageHook, laneWriteHook, lanesBlockedHook, list, liveTx, merge, newTransaction, nextQuestion, notifyEpoch, observeFlightHook, ownFlights, passLane, passTx, question, queuePendingNode, releaseAmbientMarksHook, releaseMarksHook, releaseQueues, reruns, resetErrorHalt, resolveTx, sameLane, schedule, setExternalHooks, setPassLane, setQuestion, stagedReaders, staleReader, storeCommitHook, storeParkHook, txOf, unhold, wireExternalSourceHook };
1088
+ export { GlobalQueue, MAINLINE_QUESTION, ROOT_ERROR_HOOK, actionDepth, applyGuessesHook, blocked, blockersOf, boundarySeamHook, bumpNotifyEpoch, catchHook, clock, commitPendingNode, deferZombie, dirtyQueue, endLanesHook, enforceLoadingBoundary, enterAction, exitAction, externalUntrackHook, flush, flushTransaction, freshHook, globalQueue, haltReactivity, haltReactivityAt, heldRunHook, heldTrim, heldUnder, hiddenHook, holdNode, insertSubs, installBoundaryHooks, installLaneHooks, installMarkHooks, installStoreHooks, installVerdictHooks, joinFuture, joinPassTx, laneCorrectionsHook, laneDirty, laneOutcomeHook, laneReadHook, laneSeamsHook, laneStageHook, laneWriteHook, lanesBlockedHook, list, liveTx, merge, newTransaction, nextQuestion, notifyEpoch, observeFlightHook, ownFlights, passLane, passTx, question, queuePendingNode, releaseAmbientMarksHook, releaseMarksHook, releaseQueues, reruns, resetErrorHalt, resolveTx, sameLane, schedule, setExternalHooks, setPassLane, setQuestion, stagedReaders, staleReader, storeCommitHook, storeParkHook, txOf, unhold, wireExternalSourceHook };