@solidjs/signals 2.0.0-rc.13 → 2.0.0-rc.15

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 (121) hide show
  1. package/dist/dev-shared.js +2059 -3768
  2. package/dist/dev.attribution.js +237 -101
  3. package/dist/dev.js +7466 -8497
  4. package/dist/observe/affects.js +128 -104
  5. package/dist/observe/boundaries.js +492 -665
  6. package/dist/observe/core/action.js +63 -66
  7. package/dist/observe/core/async.js +243 -379
  8. package/dist/observe/core/attribution-queries.js +1 -1
  9. package/dist/observe/core/attribution.js +394 -253
  10. package/dist/observe/core/constants.js +120 -187
  11. package/dist/observe/core/context.js +3 -3
  12. package/dist/observe/core/core.js +874 -1440
  13. package/dist/observe/core/dev.js +3 -3
  14. package/dist/observe/core/effect.js +73 -84
  15. package/dist/observe/core/external.js +5 -5
  16. package/dist/observe/core/graph.js +62 -71
  17. package/dist/observe/core/heap.js +75 -98
  18. package/dist/observe/core/invariants.js +1 -34
  19. package/dist/observe/core/lanes.js +750 -181
  20. package/dist/observe/core/owner.js +80 -101
  21. package/dist/observe/core/scheduler.js +719 -1356
  22. package/dist/observe/core/verdict.js +314 -621
  23. package/dist/observe/flatten.js +79 -0
  24. package/dist/observe/index.js +15 -13
  25. package/dist/observe/map.js +232 -235
  26. package/dist/observe/reveal.js +212 -0
  27. package/dist/observe/signals.js +144 -216
  28. package/dist/observe/store/affects.js +142 -0
  29. package/dist/observe/store/hooks.js +13 -0
  30. package/dist/observe/store/index.js +27 -24
  31. package/dist/observe/store/optimistic.js +571 -0
  32. package/dist/observe/store/projection.js +308 -0
  33. package/dist/observe/store/reconcile.js +259 -0
  34. package/dist/observe/store/store.js +1988 -277
  35. package/dist/observe/store/storePath.js +1 -1
  36. package/dist/{prod/store/next → observe/store}/target.js +17 -18
  37. package/dist/observe/store/types.js +73 -0
  38. package/dist/observe/store/utils.js +1 -13
  39. package/dist/prod/affects.js +130 -106
  40. package/dist/prod/boundaries.js +476 -654
  41. package/dist/prod/core/action.js +71 -74
  42. package/dist/prod/core/async.js +306 -444
  43. package/dist/prod/core/constants.js +117 -186
  44. package/dist/prod/core/context.js +3 -3
  45. package/dist/prod/core/core.js +888 -1462
  46. package/dist/prod/core/effect.js +88 -99
  47. package/dist/prod/core/external.js +5 -5
  48. package/dist/prod/core/graph.js +62 -71
  49. package/dist/prod/core/heap.js +75 -98
  50. package/dist/prod/core/invariants.js +1 -32
  51. package/dist/prod/core/lanes.js +712 -181
  52. package/dist/prod/core/owner.js +75 -96
  53. package/dist/prod/core/scheduler.js +686 -1335
  54. package/dist/prod/core/verdict.js +309 -621
  55. package/dist/prod/flatten.js +77 -0
  56. package/dist/prod/index.js +15 -13
  57. package/dist/prod/map.js +142 -145
  58. package/dist/prod/reveal.js +212 -0
  59. package/dist/prod/signals.js +155 -227
  60. package/dist/prod/store/affects.js +142 -0
  61. package/dist/prod/store/hooks.js +13 -0
  62. package/dist/prod/store/index.js +26 -19
  63. package/dist/prod/store/optimistic.js +566 -0
  64. package/dist/prod/store/projection.js +301 -0
  65. package/dist/prod/store/reconcile.js +259 -0
  66. package/dist/prod/store/store.js +1883 -272
  67. package/dist/prod/store/storePath.js +1 -1
  68. package/dist/{observe/store/next → prod/store}/target.js +13 -17
  69. package/dist/prod/store/types.js +73 -0
  70. package/dist/prod/store/utils.js +1 -11
  71. package/dist/types/affects.d.ts +11 -30
  72. package/dist/types/boundaries.d.ts +66 -176
  73. package/dist/types/carved.d.ts +1 -0
  74. package/dist/types/core/action.d.ts +5 -6
  75. package/dist/types/core/async.d.ts +10 -5
  76. package/dist/types/core/attribution-hooks.d.ts +20 -1
  77. package/dist/types/core/attribution.d.ts +26 -11
  78. package/dist/types/core/constants.d.ts +130 -180
  79. package/dist/types/core/core.d.ts +134 -264
  80. package/dist/types/core/dev.d.ts +2 -2
  81. package/dist/types/core/effect.d.ts +0 -7
  82. package/dist/types/core/graph.d.ts +7 -1
  83. package/dist/types/core/heap.d.ts +5 -7
  84. package/dist/types/core/index.d.ts +5 -5
  85. package/dist/types/core/invariants.d.ts +0 -29
  86. package/dist/types/core/lanes.d.ts +74 -86
  87. package/dist/types/core/owner.d.ts +3 -3
  88. package/dist/types/core/scheduler.d.ts +255 -331
  89. package/dist/types/core/types.d.ts +45 -85
  90. package/dist/types/core/verdict.d.ts +3 -0
  91. package/dist/types/flatten.d.ts +8 -0
  92. package/dist/types/index.d.ts +6 -4
  93. package/dist/types/reveal.d.ts +19 -0
  94. package/dist/types/signals.d.ts +35 -91
  95. package/dist/types/store/affects.d.ts +12 -0
  96. package/dist/types/store/hooks.d.ts +48 -0
  97. package/dist/types/store/index.d.ts +34 -15
  98. package/dist/types/store/optimistic.d.ts +23 -0
  99. package/dist/types/store/projection.d.ts +18 -0
  100. package/dist/types/store/reconcile.d.ts +3 -0
  101. package/dist/types/store/store.d.ts +132 -145
  102. package/dist/types/store/storePath.d.ts +1 -1
  103. package/dist/types/store/target.d.ts +151 -0
  104. package/dist/types/store/types.d.ts +84 -0
  105. package/package.json +1 -1
  106. package/dist/observe/core/optimistic.js +0 -669
  107. package/dist/observe/store/next/optimistic.js +0 -717
  108. package/dist/observe/store/next/projection.js +0 -266
  109. package/dist/observe/store/next/reconcile.js +0 -375
  110. package/dist/observe/store/next/store.js +0 -2608
  111. package/dist/prod/core/optimistic.js +0 -658
  112. package/dist/prod/store/next/optimistic.js +0 -710
  113. package/dist/prod/store/next/projection.js +0 -257
  114. package/dist/prod/store/next/reconcile.js +0 -373
  115. package/dist/prod/store/next/store.js +0 -2484
  116. package/dist/types/core/optimistic.d.ts +0 -6
  117. package/dist/types/store/next/optimistic.d.ts +0 -25
  118. package/dist/types/store/next/projection.d.ts +0 -27
  119. package/dist/types/store/next/reconcile.d.ts +0 -10
  120. package/dist/types/store/next/store.d.ts +0 -145
  121. package/dist/types/store/next/target.d.ts +0 -196
@@ -1,67 +1,301 @@
1
- import { EFFECT_RENDER, EFFECT_USER, STATUS_PENDING, CONFIG_ADOPTED_UNFLUSHED, NOT_PENDING, REACTIVE_MANUAL_WRITE, STATUS_UNINITIALIZED, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_DISPOSED, CONFIG_HELD_TRUTH, CONFIG_HAS_COMPANIONS, EFFECT_TRACKED, CONFIG_INPUTS_PUBLISHED, REACTIVE_ZOMBIE, CONFIG_LANE_FRAME, CONFIG_HAS_LANE, CONFIG_HAS_SNAPSHOT, REACTIVE_RECOMPUTING_DEPS, REACTIVE_MISSED_WAKE, CONFIG_IN_SNAPSHOT_SCOPE, REACTIVE_SNAPSHOT_STALE, CONFIG_SLOT_NODE, CONFIG_HELD_CHILDREN, REACTIVE_REASK, CONFIG_AUTHORITATIVE_READ } from "./constants.js";
1
+ import { EFFECT_RENDER, EFFECT_USER, CONFIG_HELD, CONFIG_OVERRIDE, NOT_PENDING, STATUS_UNINITIALIZED, STATUS_PENDING, CONFIG_STAGED, REACTIVE_DISPOSED, REACTIVE_ZOMBIE, CONFIG_VERDICT, CONFIG_HAS_SNAPSHOT, REACTIVE_RECOMPUTING_DEPS, REACTIVE_MISSED_WAKE, CONFIG_IN_SNAPSHOT_SCOPE, REACTIVE_SNAPSHOT_STALE, CONFIG_HELD_FRAME, REACTIVE_LANE_DIRTY, CONFIG_INPUTS_PUBLISHED, EFFECT_TRACKED, REACTIVE_MANUAL_WRITE, CONFIG_GUESS, REACTIVE_FRAME_READ, CONFIG_REDERIVE, STATUS_ERROR } from "./constants.js";
2
2
 
3
- import { resyncUnflushedCompanions, currentOptimisticLane, slotUnobservedHook, markUnflushedStaged, hasActiveOverride } from "./core.js";
3
+ import { attrHooks } from "./attribution-hooks.js";
4
4
 
5
- import { DEV } from "./dev.js";
5
+ import { resyncUnflushedCompanions, recompute, markUnflushedStaged, ext } from "./core.js";
6
6
 
7
- import { NotReadyError } from "./error.js";
7
+ import { DEV } from "./dev.js";
8
8
 
9
9
  import { sweepDormant, trimStaleDeps } from "./graph.js";
10
10
 
11
- import { runHeap, deleteFromHeap, enqueueSub } from "./heap.js";
11
+ import { runHeap, enqueueSub, deleteFromHeap } from "./heap.js";
12
+
13
+ import { devCheckQuiescent } from "./invariants.js";
14
+
15
+ /** Live transactions — opened, unmerged, not landed. Scanned at every seam
16
+ * while non-empty; nothing on the plain path. */ const transactions = [];
17
+
18
+ /** Observe tier: this flush parked its transaction (`holdStart` fired). */ let holding = false;
19
+
20
+ /** A transaction (listed here) or a lane (lanes.ts lists its own). */ function newTransaction(e, n = null) {
21
+ const t = {
22
+ ln: [],
23
+ rn: [ [], [] ],
24
+ fn: [],
25
+ dn: null,
26
+ _parent: n,
27
+ B: e,
28
+ xe: null,
29
+ tt: 0,
30
+ nt: false,
31
+ nn: null,
32
+ Ze: false,
33
+ sn: false,
34
+ T: null
35
+ };
36
+ if (!e) transactions.push(t);
37
+ return t;
38
+ }
12
39
 
13
- import { activeLanes, assignOrMergeLane, findLane } from "./lanes.js";
40
+ // Lanes (plan sec. 28, 2026-10-02; lanes.ts). A lane is "a new base of a transition":
41
+ // the sub-frame an optimistic write opens in the transition of the frame it
42
+ // is made in. It sees the screen plus its own guesses (plan sec. 19), breaks out of
43
+ // the parent's hold — its effects run now — and holds itself if its own
44
+ // derivations hit async. It ends when the parent lands: a guess reverts to
45
+ // the truth it covered, or lands as the truth that superseded it. A frame
46
+ // that does not park has nothing to be optimistic over: the write is void.
47
+
48
+ // The seat of a pass is its node's: a node carrying a lane's value runs as
49
+ // the lane's whoever dirtied it; a derivation's tracked read of a lane's
50
+ // value moves its pass into the lane; a leaf's never does (it reads the
51
+ // screen). Lane work's values go to the lane slot, its runs to the lane's
52
+ // queues; the rest of the frame is the flush's.
53
+ /** The lane the running pass is work of; set at `recompute`'s head from the
54
+ * node, moved by a derivation's lane read, saved and restored around the
55
+ * pass. */ let passLane = null;
56
+
57
+ function setPassLane(e) {
58
+ passLane = e;
59
+ }
60
+
61
+ /** The transaction this flush parks into: set by the first join, merged
62
+ * with every further one. Consumed at the seam. A join outside a flush (a
63
+ * mount's pending observer, a write to a held node, an action opening)
64
+ * schedules the seam that consumes it: membership is the tick's — the same
65
+ * synchronous frame — and the next tick's writes are not this one's. */ let flushTransaction = null;
66
+
67
+ /** The transaction the passes running OUTSIDE a flush this tick derive from
68
+ * (a mainline mount, a mainline re-pass): the passes' own (A29, creation-
69
+ * time form — "the entry is the pass's alone"): their results are staged
70
+ * into it, and nothing else in the tick is — a write made after the mount
71
+ * is a mainline write, a render effect mounted beside it a stale reader.
72
+ * Inside a flush, or in a tick that already has its transaction (an
73
+ * action's body), the frame joins instead (`flushTransaction`) — except a
74
+ * first pass a loading boundary that has not shown content catches (`own`,
75
+ * A29's boundary exemption, #3540): it is the boundary's, not the tick's,
76
+ * and a flush that has joined nothing keeps it pass-scoped as outside one.
77
+ * Cleared by the flush the join schedules, and at the end of a flush that
78
+ * set it. */ let passTx = null;
79
+
80
+ function joinPassTx(e, n) {
81
+ if (globalQueue.X && !n || flushTransaction !== null) return joinFuture(e);
82
+ if (passTx === null) passTx = resolveTx(e); else merge(resolveTx(e), passTx);
83
+ schedule();
84
+ }
14
85
 
15
- export { getOrCreateLane, mergeLanes, resolveLane } from "./lanes.js";
86
+ /** The transaction holding a node (CONFIG_HELD set), resolved through
87
+ * merges; the path is compressed. */ function txOf(e) {
88
+ return e.m.He = resolveTx(e.m.He);
89
+ }
16
90
 
17
- import { devCheckFlushStart, devCheckActiveOverrides, devCensusCompanions, devCheckQuiescent } from "./invariants.js";
91
+ /** `t` resolved through merges while it is still parked or open; null once
92
+ * it has landed. */ function liveTx(e) {
93
+ e = resolveTx(e);
94
+ return transactions.indexOf(e) !== -1 ? e : null;
95
+ }
18
96
 
19
- const transitions = new Set;
97
+ function resolveTx(e) {
98
+ while (e.dn !== null) e = e.dn;
99
+ return e;
100
+ }
20
101
 
21
- const dirtyQueue = {
22
- eE: new Array(2e3).fill(undefined),
23
- tE: false,
24
- ln: 0,
25
- EE: 0
26
- };
102
+ /** The flush joins transaction `t` — or needs one of its own (`null`: a pass
103
+ * went pending with nothing held). Joining a second one merges it into the
104
+ * flush's: the work that touched both entangles them (A15). */ function joinFuture(e) {
105
+ const n = flushTransaction;
106
+ if (n === null) {
107
+ flushTransaction = e === null ? newTransaction(false) : resolveTx(e);
108
+ } else if (e !== null) merge(resolveTx(e), n);
109
+ if (!globalQueue.X) schedule();
110
+ }
111
+
112
+ /** `t` merges into `f`: the work that touched both entangles them (A15).
113
+ * A lane never merges: it is a sub-frame, not a peer — its work is routed
114
+ * per pass (`passLane`). */ function merge(e, n) {
115
+ // Both ends resolved: a stale pointer to a transaction already merged
116
+ // into the other would close a cycle `resolveTx` never leaves.
117
+ e = resolveTx(e);
118
+ n = resolveTx(n);
119
+ if (e === n || e.B || n.B) return;
120
+ e.dn = n;
121
+ n.tt += e.tt;
122
+ n.nt ||= e.nt;
123
+ append(n.ln, e.ln);
124
+ append(n.rn[0], e.rn[0]);
125
+ append(n.rn[1], e.rn[1]);
126
+ append(n.fn, e.fn);
127
+ if (e.T !== null) append(n.T ??= [], e.T);
128
+ transactions.splice(transactions.indexOf(e), 1);
129
+ }
130
+
131
+ /** A15: a hold is a property of the async node — observed pending by a
132
+ * render reader. A transaction is blocked while a node it holds, in flight,
133
+ * has one: a pending render effect (the frame itself), or a pending held
134
+ * node a render effect reads. A stale reader served the committed value
135
+ * never went pending, but derives from the flight all the same (#3494) and
136
+ * is still linked to it; a memo between them is pending, and held, too — so
137
+ * one hop of `_subs` is the chain. Reads of the reader's LAST pass only (the
138
+ * link's generation is the pass's): a tail kept for A30 — the committed
139
+ * frame's dependency, awaiting the run that retires it — is not a read of
140
+ * the flight, and a reader that stopped reading releases it (O3, #3494).
141
+ * An errored pass (NotReady included) did not stop: it never got there, and
142
+ * its full list stands (A30) — the reads it did not reach still observe. A
143
+ * flight nobody renders holds nothing (A29). Only the nodes `t` still owns
144
+ * count: one a lane took over since is the lane's to wait on. A guess whose
145
+ * own truth is in flight blocks the lane's parent, not the lane — it stands
146
+ * in for the flight (A17: "visible until its own fetch settles"). */ function blocked(e) {
147
+ // The transaction being judged (a lane consulted on its behalf judges for
148
+ // it): a zombie whose removal it stages is moot for it alone (`onScreen`).
149
+ const n = judge;
150
+ judge ??= e;
151
+ // An action still running in it holds it open (action.ts). Its lanes
152
+ // (lanes.ts): a lane under `t` blocks it while blocked itself.
153
+ const t = e.tt !== 0 || blockedBy(e.ln, e) || !!GlobalQueue.On && GlobalQueue.On(e);
154
+ judge = n;
155
+ return t;
156
+ }
27
157
 
28
- const zombieQueue = {
29
- eE: new Array(2e3).fill(undefined),
30
- tE: false,
31
- ln: 0,
32
- EE: 0
33
- };
158
+ let judge = null;
159
+
160
+ /** `t`'s own flights only — not its lanes': a flight the frame asked for
161
+ * (a refetch, a plain load) is authoritative; a lane's derivation flight is
162
+ * not (lanes.ts, the body-end corollary). A pending reader counts only if
163
+ * one of the sources it waits on is `t`'s (a render effect marked pending
164
+ * by a lane flight, never re-run, is the lane's wait, not the frame's). */ function ownFlights(e) {
165
+ return blockedBy(e.ln, e, true);
166
+ }
167
+
168
+ /** A source the pending node waits on that is `t`'s (held by it, or by no
169
+ * one) — not another transaction's or lane's work. Itself, when it is the
170
+ * source. */ function ownSource(e, n) {
171
+ const t = e.m?.Se;
172
+ if (t === undefined) return true;
173
+ for (const e of t) {
174
+ const t = e.m?.He;
175
+ if (t == null || resolveTx(t) === n) return true;
176
+ }
177
+ return false;
178
+ }
34
179
 
35
- /** runHeap callback that discards a queued zombie recompute instead of running
36
- * it: unlink pure recompute entries; strip just the recompute bit from dirtied
37
- * height-adjust entries so their height work still happens. A zombie dirtied
38
- * through the lane channel (OPTIMISTIC_DIRTY — an override or a `latest()`
39
- * companion) runs instead (#3444): a zombie renders mainline until the commit
40
- * disposes it, and the lane's values ARE the mainline frame — the still-visible
41
- * branch a held `Show` is removing showed the old `latest(count)` beside the
42
- * new one outside. Its pass runs under the lane and its run lands on the
43
- * lane's effect queue, so a held lane defers it exactly as it defers every
44
- * other reader's. */ function cancelZombieRecompute(e) {
45
- if (e.oe & REACTIVE_OPTIMISTIC_DIRTY && !laneZombie(e)) return GlobalQueue.Ke(e);
46
- if (e.oe & REACTIVE_IN_HEAP_HEIGHT) e.oe &= -140; else {
47
- deleteFromHeap(e, zombieQueue);
48
- e.oe &= -132;
180
+ function blockedBy(e, n, t = false) {
181
+ for (let l = 0; l < e.length; l++) {
182
+ const o = e[l];
183
+ if (!(o.S & STATUS_PENDING) || o.R & REACTIVE_DISPOSED || o.i & CONFIG_GUESS || o.m.He !== null && txOf(o) !== n || t && !ownSource(o, n)) continue;
184
+ // The frame's own pending observer (off screen: this one holds nothing;
185
+ // the next listed node may).
186
+ if (o.u === EFFECT_RENDER) {
187
+ if (onScreen(o, judge ?? n)) return true;
188
+ continue;
189
+ }
190
+ for (let e = o.t; e !== null; e = e.o) {
191
+ const t = e.l;
192
+ // A frame reader: a render effect, or a stale reader of the flight
193
+ // (REACTIVE_FRAME_READ — a verdict reader, `observeFlight`; lane work,
194
+ // `frameRead`): served committed instead of going pending, it derives
195
+ // from the flight all the same and is re-derived at the landing — the
196
+ // frame's observation survives its reader becoming a lane's (a guess
197
+ // over a held window, V5/A17). A probe alone does not hold. A
198
+ // boundary showing content forwards the pending (its output pending,
199
+ // CONFIG_REDERIVE): transparent, its readers hold through it (A33; the
200
+ // lane-membership ruling, 2026-10-05). An output that never committed
201
+ // shows nothing — a mount the frame has not revealed — and holds
202
+ // nothing.
203
+ if ((t.u === EFFECT_RENDER && t.S & STATUS_PENDING || t.R & REACTIVE_FRAME_READ || t.i & CONFIG_REDERIVE && t.S === STATUS_PENDING) && (e.Pe === t.pe || t.m?.F != null) && !(t.R & REACTIVE_DISPOSED) && onScreen(t, judge ?? n)) return true;
204
+ }
49
205
  }
206
+ return false;
207
+ }
208
+
209
+ /** A frame reader whose say counts for `t`'s hold — `t` the transaction
210
+ * being judged: not a zombie whose removal `t` stages (A15 #3463: a re-ask
211
+ * in the unmount frame lands its pending on the zombie it unmounts — the
212
+ * compiled <Show> of #3372; a zombie blocks every other judgment while it
213
+ * is visible, a lane's reveal included: only the commit that disposes it
214
+ * makes its say moot), and not behind a fallback (A33: a boundary showing its fallback is the display of
215
+ * everything under it, so a reader there is not on screen and holds
216
+ * nothing — boundaries.ts). */ function onScreen(e, n) {
217
+ return !(e.R & REACTIVE_ZOMBIE && removalStagedBy(e, n)) && !(GlobalQueue.ce && GlobalQueue.ce(e));
50
218
  }
51
219
 
52
- /** A member of a parked LANE frame (CONFIG_LANE_FRAME on the owner whose pass
53
- * parked it, #3662). The #3444 exception is for transaction zombies; a lane
54
- * frame's member is retired by the run that carries the lane's values, so its
55
- * lane-channel recompute is cancelled with the rest — run, it republished the
56
- * retired frame under those values. Mainline writes still reach it (#3463). */ function laneZombie(e) {
220
+ /** A15 (#3463): a reader whose removal is staged — a zombie, parked by a
221
+ * pass awaiting its commit — is still on screen and live for every hold,
222
+ * except the transaction staging its removal: for that one its say is moot
223
+ * (done, and the commit disposes it; not done, and it stays parked). The
224
+ * staging transaction is the one holding the pass that parked the frame:
225
+ * the nearest non-zombie ancestor. */ function removalStagedBy(e, n) {
57
226
  let t = e;
58
- while (t !== null && t.oe & REACTIVE_ZOMBIE) t = t._parent;
59
- return t !== null && (t.C & CONFIG_LANE_FRAME) !== 0;
227
+ while (t !== null && t.R & REACTIVE_ZOMBIE) t = t._parent;
228
+ return t !== null && (t.i & CONFIG_HELD) !== 0 && t.m?.He != null && txOf(t) === resolveTx(n);
229
+ }
230
+
231
+ /** Unchanged passes this flush whose stale dependency tail awaits the
232
+ * flush's verdict (A30, #3469): trimmed when the flush commits, kept when
233
+ * it parks — the committed frame still derives from the previous pass's
234
+ * dependencies, and a write to one of them must reach the node until a
235
+ * committing pass replaces it. */ const heldTrims = [];
236
+
237
+ function heldTrim(e) {
238
+ heldTrims.push(e);
239
+ }
240
+
241
+ /** Zombies popped from the dirty heap this flush (`deferZombie`). The seam
242
+ * decides their fate: a park cancels them (the writes that dirtied them are
243
+ * held with it); a commit runs the survivors — the ones whose owner is still
244
+ * awaiting its commit — in height order, after the commits that would have
245
+ * disposed them (#3546). No second heap: the list is the deferral. */ const deferredZombies = [];
246
+
247
+ function deferZombie(e) {
248
+ deleteFromHeap(e, dirtyQueue);
249
+ e.R &= -4;
250
+ deferredZombies.push(e);
251
+ }
252
+
253
+ /** Put a staged node in a transaction: flagged, stamped, on its landing
254
+ * list. */ function holdNode(e, n) {
255
+ e.i |= CONFIG_HELD;
256
+ list(e, n);
257
+ }
258
+
259
+ /** Put a node on a transaction's list, once: a node's `_transaction` is the
260
+ * transaction whose `_nodes` it is on (every site that sets one does the
261
+ * other), so a re-pass, a re-landing or a re-staging adds nothing. */ function list(e, n) {
262
+ const t = ext(e);
263
+ if (t.He === n) return;
264
+ t.He = n;
265
+ n.ln.push(e);
266
+ }
267
+
268
+ /** Hold the (uncommitted) frame under a held pass: every computed in the
269
+ * live child chain, recursively. Plain owners are walked through, not held
270
+ * — nothing reads or re-passes them. Once per park, over the parked frames
271
+ * only; recomputes pay nothing for it. A lane's node in the frame (listed on
272
+ * a lane: a verdict reader broken out of this very hold — display-ahead — or
273
+ * a derivation of a guess, CONFIG_OVERRIDE; a lane pass's effect) is the
274
+ * lane's, as the seam's own loop has it: "lane work never makes its node
275
+ * transaction work" (A15, #3698). Re-listed here it would carry the lane's
276
+ * value on the transaction's list, where the landing nulls its transaction
277
+ * and `dissolveLane` no longer finds it — a lane node with no lane, which the
278
+ * next read of it trips over (`laneRead` → `txOf`). Its frame is the lane's
279
+ * too (ruling A: a lane pass's children are the lane's), revealed and
280
+ * retired at the lane's seam. */ function holdFrame(e, n) {
281
+ for (let t = e.Ve; t !== null; t = t.ge) {
282
+ if (t.m?.He?.B) continue;
283
+ if (t.ee !== undefined) {
284
+ if (!(t.i & CONFIG_HELD)) holdNode(t, n);
285
+ t.i |= CONFIG_HELD_FRAME;
286
+ }
287
+ holdFrame(t, n);
288
+ }
60
289
  }
61
290
 
62
- let clock = 0;
291
+ const dirtyQueue = {
292
+ Ht: new Array(2e3).fill(undefined),
293
+ N: false,
294
+ Ue: 0,
295
+ ft: 0
296
+ };
63
297
 
64
- let activeTransition = null;
298
+ let clock = 0;
65
299
 
66
300
  let scheduled = false;
67
301
 
@@ -71,56 +305,6 @@ let haltNotified = false;
71
305
 
72
306
  let syncDepth = 0;
73
307
 
74
- let projectionWriteActive = false;
75
-
76
- /** > 0 while an action's generator body is on the stack (the synchronous
77
- * slice between yields). Maintained by action.ts around `it.next()`. */ let actionStepDepth = 0;
78
-
79
- function enterActionStep() {
80
- actionStepDepth++;
81
- }
82
-
83
- function exitActionStep() {
84
- actionStepDepth--;
85
- }
86
-
87
- // Store property nodes whose last subscriber left while they carried state
88
- // the backing cannot reconstruct — an optimistic override (overrides live on
89
- // nodes, over a clone the setter discards) or a staged write. Releasing the
90
- // slot then would drop the override: an optimistic store key read `0` the
91
- // moment its only reader gated away while the action was live (S7). Swept
92
- // after each flush — a node still without subs whose override and staging
93
- // have resolved is released through the slot hook; one that regained a
94
- // subscriber leaves the set.
95
- const transientStoreNodes = new Set;
96
-
97
- /** Slot hook's deferral: release this node when its carried state resolves. */ function deferSlotRelease(e) {
98
- transientStoreNodes.add(e);
99
- }
100
-
101
- function canUseSimpleSyncFlush(e) {
102
- const t = e.m;
103
- return transitions.size === 0 && activeLanes.size === 0 && e.ft.length === 0 && t.cn.length === 0 && t.A.length === 0 && t.ti.size === 0 && transientStoreNodes.size === 0 && pendingRearms.size === 0;
104
- }
105
-
106
- function sweepTransientStoreNodes() {
107
- if (transientStoreNodes.size === 0) return;
108
- for (const e of transientStoreNodes) {
109
- if (e.u !== null) {
110
- transientStoreNodes.delete(e);
111
- continue;
112
- }
113
- if (e._e !== NOT_PENDING) continue;
114
- if (e.o?.Fe !== undefined && e.o?.Fe !== NOT_PENDING) continue;
115
- // A live affects() mark keeps the node addressable: sweeping it would
116
- // detach the refcount from the slot (a fresh probe would upsert a new,
117
- // unmarked node for the same property).
118
- if (e.o?.t) continue;
119
- transientStoreNodes.delete(e);
120
- if (e.C & CONFIG_SLOT_NODE) slotUnobservedHook(e); else e.o?.Hn?.();
121
- }
122
- }
123
-
124
308
  /**
125
309
  * Toggles the dev-mode "must be inside a `<Loading>` boundary" enforcement
126
310
  * window. Only `render()` calls this — wrapping the initial mount so that a
@@ -130,169 +314,39 @@ function sweepTransientStoreNodes() {
130
314
  * @internal
131
315
  */ function enforceLoadingBoundary(e) {}
132
316
 
133
- function setProjectionWriteActive(e) {
134
- projectionWriteActive = e;
135
- }
317
+ // Dev-only marker for the effect half of createEffect/createRenderEffect, so
318
+ // flush() can report the no-op instead of failing silently (React parity).
319
+ /** Action bodies on the stack (action.ts): `flush()` is refused inside one —
320
+ * the body's writes are its transaction's and commit when it settles; a
321
+ * drain mid-step cannot reveal them, and dev says so. */ let actionDepth = 0;
136
322
 
137
- /**
138
- * Ambient work IS a transaction: the global queue always carries one
139
- * current-transaction-shaped batch (`globalQueue._batch`). With no transition
140
- * active, registrations (pending commits, optimistic nodes, affects marks,
141
- * optimistic stores) land in a plain ambient batch that the plain flush
142
- * finalizes; when a transition initializes it adopts the ambient batch's
143
- * contents and `_batch` becomes the transition itself, so later registrations
144
- * land there directly — no per-field aliasing.
145
- */ function createBatch() {
146
- return {
147
- be: clock,
148
- Gn: [],
149
- le: new Map,
150
- cn: [],
151
- A: [],
152
- ti: new Set,
153
- pe: [],
154
- dt: {
155
- Tt: [ [], [] ],
156
- ft: []
157
- },
158
- Rn: false,
159
- In: new Set,
160
- Dn: null
161
- };
323
+ function enterAction() {
324
+ actionDepth++;
162
325
  }
163
326
 
164
- function mergeTransitionState(e, t) {
165
- t.Rn = e;
166
- e.pe.push(...t.pe);
167
- e.Te ||= t.Te;
168
- for (const n of activeLanes) if (n.we === t) n.we = e;
169
- if (t.cn.length) {
170
- // Move (don't copy): the global queue's batch may still be the outgoing
171
- // transition, and the adoption pass in initTransition would re-push its
172
- // contents into the target — duplicating every entry.
173
- e.cn.push(...t.cn);
174
- t.cn.length = 0;
175
- }
176
- if (t.A.length) {
177
- // Move (don't copy): the global queue's batch may still be the outgoing
178
- // transition, and the adoption pass in initTransition would re-push its
179
- // contents into the target — double-releasing every mark.
180
- e.A.push(...t.A);
181
- t.A.length = 0;
182
- }
183
- for (const n of t.ti) e.ti.add(n);
184
- for (const [n, i] of t.le) {
185
- let t = e.le.get(n);
186
- if (!t) e.le.set(n, t = new Set);
187
- for (const e of i) t.add(e);
188
- }
189
- for (const n of t.In) e.In.add(n);
190
- if (t.Dn) (e.Dn ??= []).push(...t.Dn);
327
+ function exitAction() {
328
+ actionDepth--;
191
329
  }
192
330
 
193
- /**
194
- * Flip-entanglement (#3164 follow-up): `until()` is a declaration of
195
- * relatedness — the predicate names the condition that confirms the awaiting
196
- * transaction. When the predicate settles truthy, every live foreign
197
- * transition whose staged write it read IS the confirming event by the
198
- * user's own definition, so it merges into the awaiting transaction and
199
- * reveals at the joint settle — the cross-primitive twin of the family fold
200
- * (a landing on an optimism-carrying family joins the retaining
201
- * transaction). Non-flipping updates never pass through here: falsy
202
- * evaluations don't entangle, so unrelated traffic on the watched sources
203
- * reveals freely on its own schedule.
204
- *
205
- * Runs inside the predicate's compute (pure phase) — the confirming
206
- * transition's stamps are still live and its commit decision hasn't run, so
207
- * the merge lands before any reveal. Only the tree-shaken graphs that call
208
- * `until()` retain this.
209
- */ function entangleConfirmingTransitions(e, t) {
210
- t = currentTransition(t);
211
- if (t.Rn === true) return;
212
- // The confirming evidence is a dep whose value is STAGED (pending,
213
- // uncommitted) at flip evaluation — committed deps are public already and
214
- // carry nothing to entangle. A staged dep lives in one of two carriers: a
215
- // stamped transition, or the queue's current batch (ambient registrations
216
- // don't stamp; "ambient work IS a transaction" — the batch is the
217
- // carrier). The entangle STEALS the carrier's staged cargo — its pending
218
- // nodes move (re-stamped) into the awaiting transaction and reveal at its
219
- // settle — but never the carrier itself: its async reporters, actions,
220
- // and stashes are its own future (a live stream's flight must not chain
221
- // the awaiting transaction to landings that haven't happened; a merged
222
- // reporter deadlocked exactly that way).
223
- let n = false;
224
- for (let i = e.Ie; i !== null; i = i.Ne) {
225
- const r = i.Oe;
226
- if (r._e !== NOT_PENDING) {
227
- const e = r.we;
228
- const i = e != null ? currentTransition(e) : null;
229
- // Skip the awaiting transaction's own cargo (t === target: a
230
- // fold-staged landing or a write the action itself issued — hold and
231
- // reveal already correct) and dead carriers. Ambient-batch staging
232
- // (t === null) must leave the batch NOW — it commits at this flush's
233
- // end, which would reveal the confirmation under the live optimism
234
- // it just confirmed.
235
- const s = i === null ? currentBatch.Gn : i !== t && i.Rn !== true ? i.Gn : null;
236
- if (s !== null) n = stealEntangledCargo(s, t) || n;
237
- }
238
- if (i === e.Nn) break;
239
- }
240
- // The steal never activates the awaiting transaction: the predicate can
241
- // flip inside another transaction's finalize heap, and adopting the queue
242
- // batch there hands the stolen cargo to that finalize's commit sweep — a
243
- // premature reveal at a foreign settle. Subscribers that computed against
244
- // the pre-steal world were re-dirtied by the steal itself, so this
245
- // flush's applies paint the mid-hold view (committed for lane and stale
246
- // readers; a deriving reader is held with the cargo); the cargo commits
247
- // at the awaiting transaction's own settle.
331
+ /** Provenance (A18, #3331): the question being asked — an action's
332
+ * sequence while a slice of its body and the flush that carries it run, 0
333
+ * otherwise. A guess is stamped with it (lanes.ts; a mainline guess takes a
334
+ * fresh sequence: the newest intent), a flight with the question that
335
+ * started it (async.ts; mainline is always the current question). An
336
+ * answer from an older question than the guess it lands on is stale: held
337
+ * silently, it does not move the graph. */ let question = 0;
338
+
339
+ let questions = 0;
340
+
341
+ function setQuestion(e) {
342
+ question = e;
248
343
  }
249
344
 
250
- /** Move a confirming carrier's staged nodes into the awaiting transaction:
251
- * re-stamp and arm the held-truth mask (override-covered nodes skip it —
252
- * the override already hides their staged value per A17, and is usually
253
- * the very optimism this confirmation settles); the mask's commit
254
- * registers the settle-side post-revert wake. The carrier's array is
255
- * emptied so its own commit point commits none of the stolen cargo.
256
- *
257
- * EFFECT subs of stolen nodes re-run: any that recomputed against the
258
- * staging BEFORE the steal (the carrier's landing notified them as a plain
259
- * write) hold a private result derived from the carrier's world — one
260
- * that the next paint gate (stash-point lane run, a foreign flush's
261
- * completion drain) would apply as the carrier's. Re-running them now that
262
- * the cargo is the awaiting transaction's re-routes each by its posture in
263
- * this same heap pass: a lane pass keeps committed (the mask), a stale
264
- * render effect keeps committed and registers for the reveal's replay
265
- * (stale-of-foreign), and a deriving user effect is served the truth and
266
- * held with the transaction (A29). PURE computeds are deliberately NOT
267
- * re-run: a staged value of theirs is itself stolen cargo — held with the
268
- * transaction, so ordinary readers already serve their committed value —
269
- * while re-running them would re-derive the OLD world and re-stage it over
270
- * the held truth. The reveal re-notifies (commitPendingNodes), which is
271
- * when they re-derive for real. */ function stealEntangledCargo(e, t) {
272
- if (e === t.Gn || e.length === 0) return false;
273
- for (let n = 0; n < e.length; n++) {
274
- const i = e[n];
275
- i.we = t;
276
- t.Gn.push(i);
277
- // Override-covered nodes stay silent AND unmasked: the override is the
278
- // display (A17 — its staging never notified, its revert will), so their
279
- // subs saw nothing and re-running one would break the silence with a
280
- // duplicate fire of an unchanged view.
281
- if (!hasActiveOverride(i)) {
282
- i.C |= CONFIG_HELD_TRUTH;
283
- for (let e = i.u; e !== null; e = e.Ae) {
284
- const t = e.ge;
285
- if (t.je && !(t.C & CONFIG_AUTHORITATIVE_READ)) enqueueSub(t);
286
- }
287
- }
288
- }
289
- e.length = 0;
290
- transitions.add(t);
291
- return true;
345
+ function nextQuestion() {
346
+ return ++questions;
292
347
  }
293
348
 
294
- /** `schedule()` armed `scheduled` but withheld the microtask because a
295
- * projection draft was writing (see below). Consumed by `scheduleWithheld`. */ let withheld = false;
349
+ const MAINLINE_QUESTION = 2147483647;
296
350
 
297
351
  function schedule() {
298
352
  if (halted) {
@@ -301,83 +355,11 @@ function schedule() {
301
355
  }
302
356
  if (scheduled) return;
303
357
  scheduled = true;
304
- if (!syncDepth && !globalQueue.Jn) {
305
- // A projection draft's writes withhold the microtask: a flight's landing
306
- // drains them (asyncWrite's flush), and a drain BEFORE the landing tears
307
- // the pre- and post-await halves of the run apart (createProjection.async
308
- // "notifies only changed paths"). A draft write that no landing will
309
- // follow re-arms through scheduleWithheld (proj R37).
310
- withheld = projectionWriteActive;
311
- if (!withheld) queueMicrotask(flush);
312
- }
313
- }
314
-
315
- /** Arm the microtask `schedule()` withheld under projectionWriteActive. The
316
- * projection draft calls this after a write made outside its run with no
317
- * flight up (proj R37): nothing else will drain, and leaving `scheduled`
318
- * armed with no microtask strands the whole scheduler — every later
319
- * `schedule()` early-returns — until something calls `flush()` by hand.
320
- * (`withheld` was set in the same synchronous slice as this call, so the
321
- * syncDepth/_running gates it passed still hold; a stale mark left by a
322
- * landing's own flush arms at worst one no-op drain.) */ function scheduleWithheld() {
323
- if (withheld) {
324
- withheld = false;
358
+ if (!syncDepth && !globalQueue.X) {
325
359
  queueMicrotask(flush);
326
360
  }
327
361
  }
328
362
 
329
- /**
330
- * Parked transactions whose reporter set changed without a write. A
331
- * transaction completes when nothing live reports a flight it waits on, but
332
- * the flush only judges the ACTIVE transaction: a parked one is re-entered by
333
- * a stamped node's landing or an action's resume. A reporter that stops
334
- * counting for another reason — its loading boundary flipped to the fallback
335
- * (#3375), or it was disposed by ambient work (#3372) — is neither: the
336
- * pruning in `reporterBlocksSource` would drop it at the next check, but no
337
- * check comes, and the writes held with it stay staged. Such sites record the
338
- * transaction here (deduped: one idle pass per transaction, however many
339
- * reporters changed); the flush re-enters it on an otherwise idle pass, so
340
- * the re-evaluation adopts no unrelated ambient work.
341
- */ const wokenTransitions = [];
342
-
343
- /** Wake every parked transaction — for a site that knows a reporter stopped
344
- * counting but not whose (a boundary reset). */ function wakeParked() {
345
- for (const e of transitions) wokenTransitions.includes(e) || wokenTransitions.push(e);
346
- schedule();
347
- }
348
-
349
- /**
350
- * Boundaries whose `on` dependencies notified this flush (#3540; a Set:
351
- * many notifications, one re-arm). The notification arrives inside a pass,
352
- * at the height of the `on` reads — before the readers the write put in
353
- * flight are registered — so the re-arm waits for the heap and runs before
354
- * the verdict (GlobalQueue.run → drainRearms), still under the write's
355
- * transaction: the release is seen by the verdict that follows, and the
356
- * staged fallback swap lands with the write's frame.
357
- */ const pendingRearms = new Set;
358
-
359
- function queueRearm(e) {
360
- pendingRearms.add(e);
361
- schedule();
362
- }
363
-
364
- /** The drain (see pendingRearms): each boundary decides for itself, from
365
- * what is pending under it now, whether re-arming means its fallback or
366
- * nothing. Snapshot first: a re-arm can queue another — a write it makes
367
- * notifying an `on` that reads it — which is the next pass's. */ function drainRearms() {
368
- const e = Array.from(pendingRearms);
369
- pendingRearms.clear();
370
- for (let t = 0; t < e.length; t++) e[t].se();
371
- }
372
-
373
- /** Transactions a mainline tick has PROPOSED against (A34, #3494): a write to a
374
- * node one of them holds — the same value or another — is a second proposal
375
- * on a contested node, and the tick reveals with the hold ("both are
376
- * suggesting a value; if one finished before the other that would be odd").
377
- * Entered at the next flush's start, where the ambient batch is adopted;
378
- * never from the write itself, which left `activeTransition` set across the
379
- * caller's block and made creation after the write the transaction's (A29). */ const batchJoins = [];
380
-
381
363
  /**
382
364
  * Permanently halts the reactive system. Called when a user error escapes
383
365
  * every boundary — app state is undefined at that point, so scheduling stops
@@ -394,7 +376,7 @@ function queueRearm(e) {
394
376
  function haltReactivity(e) {
395
377
  if (halted) return;
396
378
  halted = true;
397
- let t = "[REACTIVITY_HALTED]";
379
+ let n = "[REACTIVITY_HALTED]";
398
380
  // Surface the cause here too: callers rethrow it, but a creation-time throw
399
381
  // unwinds through ancestor recomputes that convert it to status instead of
400
382
  // surfacing it (#2884), so the rethrow alone cannot guarantee visibility.
@@ -404,9 +386,9 @@ function haltReactivity(e) {
404
386
  // nothing an app or its telemetry can act on (#3338 — an uncaught throw
405
387
  // during the hydration render). The rethrow may reach the top as well in
406
388
  // the non-swallowed cases; a duplicate report beats a silent one.
407
- const n = e !== undefined && globalThis.reportError;
408
- n || e === undefined ? console.error(t) : console.error(t, e);
409
- n && n(e);
389
+ const t = e !== undefined && globalThis.reportError;
390
+ t || e === undefined ? console.error(n) : console.error(n, e);
391
+ t && t(e);
410
392
  }
411
393
 
412
394
  // Logs on the first write after a halt so a frozen interaction is traceable.
@@ -421,558 +403,325 @@ function notifyHalted() {
421
403
  haltNotified = false;
422
404
  }
423
405
 
424
- // Identifies one child-traversal pass in `Queue.run` so a rescan after the
425
- // child list shifts can tell "already run this pass" from "still pending".
426
- let queueRunToken = 0;
427
-
428
- class Queue {
429
- _parent=null;
430
- Tt=[ [], [] ];
431
- ft=[];
432
- ht=0;
433
- created=clock;
434
- addChild(e) {
435
- this.ft.push(e);
436
- e._parent = this;
437
- }
438
- removeChild(e) {
439
- const t = this.ft.indexOf(e);
440
- if (t >= 0) {
441
- this.ft.splice(t, 1);
442
- e._parent = null;
443
- }
444
- }
445
- notify(e, t, n, i) {
446
- if (this._parent) return this._parent.notify(e, t, n, i);
447
- return false;
448
- }
449
- run(e) {
450
- if (this.Tt[e - 1].length) {
451
- const t = this.Tt[e - 1];
452
- this.Tt[e - 1] = [];
453
- runQueue(t, e);
454
- }
455
- // Effects run here can dispose owners, and disposal removes queues from
456
- // this list — the running child itself, an earlier sibling, or several at
457
- // once. A plain index walk then skips whatever shifted into the cursor.
458
- // Stamping each child before it runs makes the pass idempotent, so a shift
459
- // can be recovered by rescanning from the front and every child still runs
460
- // exactly once. Children appended mid-pass carry a stale stamp and run,
461
- // matching the previous live-array behaviour.
462
- const t = this.ft;
463
- const n = ++queueRunToken;
464
- for (let i = 0; i < t.length; ) {
465
- const r = t[i];
466
- if (r.ht !== n) {
467
- r.ht = n;
468
- r.run?.(e);
469
- if (t[i] !== r) {
470
- i = 0;
471
- continue;
472
- }
473
- }
474
- i++;
475
- }
476
- }
477
- enqueue(e, t) {
478
- if (e) {
479
- // Route to lane's effect queue if we're in an optimistic recomputation
480
- if (currentOptimisticLane) {
481
- const n = findLane(currentOptimisticLane);
482
- n.dn[e - 1].push(t);
483
- } else {
484
- this.Tt[e - 1].push(t);
485
- }
486
- }
487
- schedule();
488
- }
489
- stashQueues(e) {
490
- e.Tt[0].push(...this.Tt[0]);
491
- e.Tt[1].push(...this.Tt[1]);
492
- this.Tt = [ [], [] ];
493
- for (let t = 0; t < this.ft.length; t++) {
494
- let n = this.ft[t];
495
- let i = e.ft[t];
496
- if (!i) {
497
- i = {
498
- Tt: [ [], [] ],
499
- ft: []
500
- };
501
- e.ft[t] = i;
502
- }
503
- n.stashQueues(i);
504
- }
505
- }
506
- restoreQueues(e) {
507
- this.Tt[0].push(...e.Tt[0]);
508
- this.Tt[1].push(...e.Tt[1]);
509
- for (let t = 0; t < e.ft.length; t++) {
510
- const n = e.ft[t];
511
- let i = this.ft[t];
512
- if (i) i.restoreQueues(n);
513
- }
514
- }
515
- }
516
-
517
- class GlobalQueue extends Queue {
518
- Jn=false;
519
- // The current transaction-shaped batch: a plain ambient batch while no
520
- // transition is active, the active transition itself after initTransition.
521
- m=createBatch();
522
- static Ke;
523
- static en;
524
- static Cn;
525
- static _t=null;
526
- // Store-side hook: drops a keyless affects() mark's identity scope when the
527
- // carrier node's last registration releases (wired by store.ts, mirroring
528
- // _clearOptimisticStore).
529
- static p=null;
530
- // affects()-side hooks (wired by affects.ts, mirroring _update): the mark
531
- // engine — count/register/release — lives with the feature. Every call site
532
- // is gated by state only that module creates, so `!` invocations are safe
533
- // once the gate holds.
534
- static G=null;
535
- static M=null;
536
- static N=null;
537
- // External-source bridge (wired by enableExternalSource(); null while no
538
- // config is active — including after _resetExternalSourceConfig()).
539
- static Zn=null;
540
- static jn=null;
541
- // Verdict-layer hooks (wired by verdict.ts when isPending()/latest() are
542
- // imported; null in apps that never use them). Call sites either guard for
543
- // null or sit behind state only the verdict layer can create (`!` is safe
544
- // there: `_pendingSignal`/`_latestValueComputed` are only ever assigned by
545
- // verdict.ts, and `pendingCheckActive`/`latestReadActive` only flip inside
546
- // isPending()/latest()).
547
- static Ve=null;
548
- static Ge=null;
549
- static We=null;
550
- static ri=null;
551
- static et=null;
552
- static nt=null;
553
- static rt=null;
554
- static On=null;
555
- static k=null;
556
- static Et=null;
557
- // Re-asks probes whose verdict was provisionally suppressed by a fresh read
558
- // of a held value, once the transaction gains an async blocker (#3028).
559
- static Nt=null;
560
- // Optimistic-engine hooks (wired by core/optimistic.ts via
561
- // installOptimisticEngine(), called from verdict.ts / createOptimistic /
562
- // createOptimisticStore — every module that can create optimistic state).
563
- // Call sites are gated by state only the engine can create: an
564
- // `_overrideValue` slot, a lane in `activeLanes`, an `_optimisticNodes`
565
- // entry, or a non-null `currentOptimisticLane`, so `!` invocations are safe
566
- // once the gate holds.
567
- static ot=null;
568
- static oi=null;
569
- static ai=null;
570
- static li=null;
571
- static si=null;
572
- /** Patch-channel optimistic drain (next/patch.ts): optimistic emissions
573
- * apply at lane-effect timing — visible in flight, unlike the regular
574
- * effect queues an action stashes. Injected; null when unused. */
575
- static ui=null;
576
- static lt=null;
577
- static it=null;
578
- /** Is the node routed through a LIVE lane (`resolveLane`)? read()'s reveal
579
- * carve-out asks before showing a foreign-held pending node's committed
580
- * value: a lane-derived flight's inputs are already revealed through the
581
- * lane (#3334). Gated on CONFIG_HAS_LANE, which only the engine sets. */
582
- static tt=null;
583
- static $n=null;
584
- static an=null;
585
- static Sn=null;
586
- /** Authoritative-view reader wakeup: installed by until() and refresh() before
587
- * their first read. Call sites are gated by CONFIG_AUTHORITATIVE_OBSERVED, which
588
- * only such a reader's carve-out read can set, so `!` invocations are safe once
589
- * the gate holds (#3303). */
590
- static zn=null;
591
- static Tn=null;
592
- /** A18 supersession (#3331): own-source truth `value` landed under an active
593
- * override. The engine decides whether the graph re-derives — the value
594
- * differs from the override and is not a stale (older-action) answer (mark
595
- * the node, demote its lane cascade, notify), or returns to it after an
596
- * earlier differing arrival (clear the mark, notify) — and owns the
597
- * authoritative-observer wake for a silent confirm. Installed with the
598
- * optimistic engine; only reachable on a node that has an override. */
599
- static ke=null;
600
- /** The flush's pre-verdict step (#3427): once the transaction's action
601
- * bodies have all ended and nothing authoritative is left in flight, the
602
- * engine supersedes every override still in force with the truth it
603
- * reverts to, so the graph re-derives from it now, as the transaction's
604
- * held work, instead of after the flights the overrides fed have landed.
605
- * True when it superseded something: the caller re-runs the heap ahead of
606
- * the verdict. The engine owns every gate (acted, actions drained, has
607
- * overrides, no store edits, no authoritative flight); null without it. */
608
- static Ei=null;
609
- /** read()'s value for a TRACKED reader of a superseded node (#3331): the
610
- * staged truth, unless the reader is a stale (render) reader of another
611
- * transaction — then the displayed override, as it keeps a foreign
612
- * transaction's committed value over its staged write. */
613
- /** A tracked read of an active override: the lane outside-view rule
614
- * (#3460) and the A18 supersession selection (#3331) — see optimistic.ts. */
615
- static ut=null;
616
- /** A lane pass's publish for a memo (#3479, lanes stage): the speculative
617
- * result becomes a DERIVED override, `_value` stays committed — see
618
- * optimistic.ts laneOverride. Set with the engine, which a lane implies. */
619
- static Me=null;
620
- /** Verdict-layer recompute in progress (companion creation, latest()/
621
- * isPending() pulls): never born held — see core.ts enterStagedRead. */
622
- static Xn=false;
623
- /** setSignal's authoritative (projection-write) landing on an override-
624
- * covered node (#3331 store twin): stage the truth for its transaction's
625
- * commit whatever its relation to the committed value — a landing equal to
626
- * committed still differs from the override — then _supersedeOverride
627
- * decides. Installed with the optimistic engine; only reachable on a node
628
- * that has an override — or, from `mapArray` once a lane pass has run over
629
- * the map, on a per-slot signal (never CONFIG_OPTIMISTIC): the slot arm
630
- * publishes a lane pass's write as the slot's derived override, lands a
631
- * plain pass's write over one, and is the plain `setSignal` otherwise (F1,
632
- * see optimistic.ts landOnOverride). */
633
- static st=null;
634
- static fi=null;
406
+ class GlobalQueue {
407
+ rn=[ [], [] ];
408
+ X=false;
635
409
  flush() {
636
- if (this.Jn) return;
637
- // Fast drain: nothing in flight but plain pending commits — no dirty
638
- // computeds, no queued effects, no child queues, no transitions/lanes/
639
- // optimistic state. Commit and go; anything a commit hook schedules
640
- // (companion snaps, store folds notifying subs) re-arms `scheduled`
641
- // below and the outer drain loop takes the full spine next round.
642
- if (activeTransition === null && dirtyQueue.EE < dirtyQueue.ln && this.Tt[0].length === 0 && this.Tt[1].length === 0 && this.ft.length === 0 && !wokenTransitions.length && !batchJoins.length && // a join must drain in its own tick (#3519 review)
643
- canUseSimpleSyncFlush(this)) {
644
- this.Jn = true;
645
- try {
646
- // A28: companions of nodes written since the last flush mirror the
647
- // flushed world — re-synced inside the running window (the write is
648
- // "flushed" from here on).
410
+ if (this.X) return;
411
+ this.X = true;
412
+ // A28: companions of nodes written since the last flush mirror the
413
+ // flushed world — re-synced inside the running window (the write is
414
+ // "flushed" from here on).
649
415
  resyncUnflushedCompanions();
650
- // Sweep first: unobserved() pulls swept nodes out of the dirty heap,
651
- // so a dormant memo dirtied in the same tick is reclaimed instead of
652
- // recomputed (matching the old inline dispose-on-read counts).
653
- sweepDormant();
654
- commitPendingNodes();
655
- } finally {
656
- this.Jn = false;
657
- }
658
- clock++;
659
- scheduled = dirtyQueue.EE >= dirtyQueue.ln || this.Tt[0].length !== 0 || this.Tt[1].length !== 0 || this.m.Gn.length !== 0;
660
- return;
661
- }
662
- this.Jn = true;
663
- resyncUnflushedCompanions();
664
- // A28, see above
665
- try {
666
- // The tick proposed against a hold (#3494): adopt its batch into it.
667
- // Inside the try: the adoption runs user comparators (the no-proposal
668
- // drop), and a throw there must not leave `_running` set.
669
- while (batchJoins.length) this.initTransition(batchJoins.pop());
670
- if (false) ;
671
- // Before runHeap for the same reason as the fast drain above; late
672
- // subscribers (an effect reading a swept memo this flush) revive it,
673
- // which is the pay-for-use contract.
674
- sweepDormant();
675
- runHeap(dirtyQueue, GlobalQueue.Ke);
676
- // The action bodies are over: the overrides they leave in force
677
- // revert at this settle, and the correction is this transaction's
678
- // held work — re-derived here, under it, ahead of the verdict — not a
679
- // waterfall after the flights the overrides fed (#3427). After the
680
- // heap, not before: a synchronous body's own writes (a refresh that
681
- // puts an override node's source in flight) are judged applied.
682
- if (activeTransition && GlobalQueue.Ei?.(activeTransition)) runHeap(dirtyQueue, GlobalQueue.Ke);
683
- // Re-arm the boundaries whose `on` notified this pass (pendingRearms):
684
- // after the heap — what the notification put in flight is registered —
685
- // and before the verdict, under the transaction that carried it. The
686
- // boundary releases its hold and stages its fallback swap with the
687
- // frame; the heap re-runs so its output pass is staged too, ahead of
688
- // the verdict that commits or parks it (#3540).
689
- if (pendingRearms.size) {
690
- drainRearms();
691
- runHeap(dirtyQueue, GlobalQueue.Ke);
692
- }
693
- if (activeTransition) {
694
- // A boundary whose fallback read something not ready is judged before
695
- // the verdict, under the transaction (boundaries.ts `_judgeHeld`,
696
- // #3540): its output is pending on that read and holds the frame,
697
- // and only a sweep re-runs it — the commit sweep, after the verdict
698
- // its own read keeps parking. Ready, it stages `_disabled` false with
699
- // the frame; the heap re-runs so the output drops the read ahead of
700
- // the verdict.
701
- if (this.ft.length) {
702
- checkBoundaryChildren(this, true);
703
- if (dirtyQueue.EE >= dirtyQueue.ln) runHeap(dirtyQueue, GlobalQueue.Ke);
704
- }
705
- const e = transitionComplete(activeTransition);
706
- if (!e) {
707
- const e = activeTransition;
708
- // Parked: the unchanged passes' inputs are held; their tails stay (A30).
709
- heldTrims.length = 0;
710
- // When the parking batch IS the transition, all of its writes commit
711
- // only with it — every zombie recompute they queued would run against
712
- // a world the zombie never displays (zombies render mainline until
713
- // commit), so cancel them instead of running them. Only an ambient
714
- // batch's mainline writes (the #2916 shape below) legitimately reach
715
- // zombies here. Height-adjust entries still process normally: a
716
- // dirtied one keeps its height flag and falls through to runHeap's
717
- // adjustHeight path on the next pass of the bucket.
718
- runHeap(zombieQueue, this.m === e ? cancelZombieRecompute : GlobalQueue.Ke);
719
- // Detach: the stashed transition keeps its batch; ambient work that
720
- // follows lands in a fresh one. If the batch is already a separate
721
- // ambient one — action done() restored activeTransition without
722
- // adopting the batch, and an ordinary write landed there before
723
- // the scheduled flush (#2916) — keep it: replacing it would strand
724
- // its queued pending nodes with held _pendingValues forever.
725
- if (this.m === e) currentBatch = this.m = createBatch();
726
- // Run lane effects immediately (before stashing) - lanes with no pending async
727
- if (activeLanes.size) {
728
- GlobalQueue.si(EFFECT_RENDER);
729
- GlobalQueue.si(EFFECT_USER);
730
- }
731
- this.stashQueues(e.dt);
732
- clock++;
733
- // A kept ambient batch may hold pending nodes (#2916): stay
734
- // scheduled so the outer drain loop commits them via the plain
735
- // flush path instead of leaving them until the next natural flush.
736
- scheduled = dirtyQueue.EE >= dirtyQueue.ln || this.m.Gn.length > 0;
737
- reassignPendingTransition(e.Gn);
738
- activeTransition = null;
739
- finalizePureQueue(null, true);
740
- return;
741
- }
742
- const t = activeTransition;
743
- const n = this.m;
744
- n !== t && n.Gn.push(...t.Gn);
745
- this.restoreQueues(t.dt);
746
- transitions.delete(t);
747
- activeTransition = null;
748
- reassignPendingTransition(n.Gn);
749
- finalizePureQueue(t);
750
- if (n === t) {
751
- // Drop the dead Transition wrapper but keep its (drained) containers
752
- // as the ambient batch — late registrations during finalization live
753
- // there and must survive to the next flush.
754
- const e = createBatch();
755
- e.Gn = n.Gn;
756
- e.cn = n.cn;
757
- e.A = n.A;
758
- e.ti = n.ti;
759
- currentBatch = this.m = e;
760
- }
761
- } else {
762
- if (canUseSimpleSyncFlush(this)) {
763
- commitPendingNodes();
764
- if (dirtyQueue.EE >= dirtyQueue.ln) {
765
- runHeap(dirtyQueue, GlobalQueue.Ke);
766
- commitPendingNodes();
767
- }
768
- } else {
769
- // Parked transactions elsewhere: their owners' zombies render
770
- // mainline until the commit that disposes them, so a mainline write
771
- // reaches them here (#2916, #3463). Commit THIS flush's pending nodes
772
- // first (#3546): a zombie whose owner commits now is disposed by that
773
- // commit and never reruns — the same fate it has when no transaction
774
- // is parked, where this queue is not run at all. Run before the
775
- // commit, it reran and notified its owner through the previous
776
- // pass's dependency tail (kept by A30 until the commit trims it),
777
- // and the owner recomputed a second time with identical inputs,
778
- // creating and disposing one more child per write. Only the zombies
779
- // of actually parked owners survive the commit and rerun; the
780
- // finalize's own commit picks up whatever those reruns stage.
781
- if (transitions.size) {
782
- commitPendingNodes();
783
- runHeap(zombieQueue, GlobalQueue.Ke);
784
- }
785
- finalizePureQueue();
786
- }
787
- }
416
+ passLane = null;
417
+ passTx = null;
418
+ try {
419
+ // Sweep before the heap: unobserved() pulls swept nodes out of it, so
420
+ // a dormant memo dirtied in the same tick is reclaimed instead of
421
+ // recomputed; late subscribers (an effect reading a swept memo this
422
+ // flush) revive it, which is the pay-for-use contract.
423
+ sweepDormant();
424
+ runHeap(dirtyQueue, GlobalQueue.Ce);
425
+ this.settle();
788
426
  clock++;
789
- // Check if finalization added items to the heap (from optimistic reversion).
790
- // Finalization may also have ENTERED a transaction (a commit hook, boundary
791
- // sweep or recompute wrote a node it owns): effects computed under it
792
- // since are its to apply, not this flush's — runEffect leaves them queued
793
- // and the next pass parks them with it (#3319). Everything computed
794
- // mainline applies now. A write the finalize staged in the ambient
795
- // batch with no subscriber to dirty (an optimistic store settle's
796
- // keyset bump under a reader that never tracks the key set) is work
797
- // too — the fast drain and the park exit already count it — so the
798
- // next round commits it and the woken re-entry below does not adopt
799
- // it into a parked transaction it never belonged to (matrix F6).
800
- scheduled = dirtyQueue.EE >= dirtyQueue.ln || activeTransition !== null || this.m.Gn.length !== 0;
801
- // Run lane effects first (for ready lanes), then regular effects
802
- activeLanes.size && GlobalQueue.si(EFFECT_RENDER);
427
+ // A write the commit sweep staged with no subscriber to dirty is work
428
+ // too — the next round commits it.
429
+ scheduled = dirtyQueue.ft >= dirtyQueue.Ue || pendingNodes.length !== 0;
803
430
  this.run(EFFECT_RENDER);
804
- activeLanes.size && GlobalQueue.si(EFFECT_USER);
805
431
  this.run(EFFECT_USER);
806
- if (false) ;
807
- if (false && !scheduled && !activeTransition && transitions.size === 0 && activeLanes.size === 0) ;
432
+ if (false && holding) ;
433
+ if (false && !scheduled) ;
808
434
  if (false) ;
809
435
  } finally {
810
- // Re-enter a woken transaction (see wokenTransitions) only from an
811
- // idle pass: entering adopts the ambient batch, and staged or dirty
812
- // ambient work would be held behind flights it never read. `scheduled`
813
- // is that test here — after the park exit as well as the normal one:
814
- // it was recomputed from the heap and the ambient batch's staged nodes
815
- // this pass, every write since re-armed it, and optimistic ambient
816
- // nodes reverted with the finalize — so a wake in a pass with work
817
- // simply falls to the next. (A staged node with no subscriber — the
818
- // finalize's keyset bump under a length-only reader — used to be
819
- // missed here: the wake adopted it, stamped it, and a later ambient
820
- // write to the same node joined the parked transaction and never
821
- // reverted; matrix F6.) Entering re-arms
822
- // it itself; a dead (completed) wake is a bare return in
823
- // initTransition, and the loop moves on to the next.
824
- while (!scheduled && !activeTransition && wokenTransitions.length) this.initTransition(wokenTransitions.pop());
825
- this.Jn = false;
436
+ this.X = false;
437
+ passTx = null;
826
438
  }
827
439
  }
828
- notify(e, t, n, i) {
829
- // Only track async if the boundary is propagating STATUS_PENDING (not caught by boundary)
830
- if (t & STATUS_PENDING) {
831
- if (n & STATUS_PENDING) {
832
- // Callers pass either nothing or this node's own `_x._error`, so `??`
833
- // is exact (a null error falls back to the same null).
834
- const t = i ?? e.o?._;
835
- // A visibility-only mark notification (the affects() boundary
836
- // channel) updates display state on its way up but must be invisible
837
- // to completion accounting BY CONSTRUCTION: it never registers a
838
- // reporter and never counts toward the loading-boundary diagnostic.
839
- if (t?.l) return true;
840
- if (t) {
841
- // A reveal can discover a flight started in an earlier flush. Hold
842
- // the staged writes with that reader (A15), even if the reader is
843
- // new. Fresh/reset loading boundaries consume pending before it
844
- // reaches here. A reader already parked in a transition must not
845
- // open a second one.
846
- if (!activeTransition && !e.we && currentBatch.Gn.length) this.initTransition();
847
- if (activeTransition) {
848
- const n = t.source;
849
- let i = activeTransition.le.get(n);
850
- if (!i) activeTransition.le.set(n, i = new Set);
851
- const r = i.size;
852
- i.add(e);
853
- if (i.size !== r) {
854
- schedule();
855
- GlobalQueue.Nt?.(activeTransition);
856
- }
440
+ /** L2 — the seam: end of the pure phase. Commit this flush's staged nodes,
441
+ * or park them with the transaction the flush joined; then land every
442
+ * transaction no frame is waiting on. The landing flush is an ordinary
443
+ * flush: the settle walk (async.ts) re-ran the parked readers in its pure
444
+ * phase, and here no flight the transaction holds has a frame deriving
445
+ * from it (`blocked`). */ settle() {
446
+ // Lanes: an ended action body judges its guesses now that their passes
447
+ // have run (lanes.ts); a correction's re-derivations run in one more
448
+ // pure round — the parent's flights before the parent is judged below.
449
+ if (GlobalQueue.cn?.()) runHeap(dirtyQueue, GlobalQueue.Ce);
450
+ const e = flushTransaction;
451
+ flushTransaction = null;
452
+ // The frame's verdict is in: the guesses written since the last seam
453
+ // open their lane under the frame's transaction (a lane's own flush
454
+ // parents a nested lane to it), or are dropped — the frame commits, and
455
+ // there is nothing to be optimistic over. Their passes run next round;
456
+ // a parent landing at this very seam ends the lane before they do.
457
+ GlobalQueue.In?.(e);
458
+ // A lane's own flush (its flight landing, its reveal) parks nothing: the
459
+ // lane's work reached it per pass; the frame's plain writes commit.
460
+ // (Resolved: a guess over another transaction's guess merged them.)
461
+ const n = e !== null && !e.B ? resolveTx(e) : null;
462
+ if (n !== null) {
463
+ // `t._nodes` grows under the walk (holdFrame pushes); the bound is
464
+ // this flush's list, which does not.
465
+ const e = pendingNodes.length;
466
+ for (let t = 0; t < e; t++) {
467
+ const e = pendingNodes[t];
468
+ // Already held: the pass that re-staged it joined its transaction,
469
+ // which is `t` (or merged into it). Lane work is the lane's (a
470
+ // pending mark propagated onto it queued it here too).
471
+ if (!(e.i & (CONFIG_HELD | CONFIG_OVERRIDE)) && (e.m === null || e.m.He === null || !e.m.He.B)) {
472
+ // A loading source — pending with nothing staged and no committed
473
+ // value (A16, A19 exc. 1: uninitialized is loading, not pending)
474
+ // that is not an observer — is nobody's frame: not held. Its
475
+ // first landing is commit #0, not a resumption of this frame, and
476
+ // a write that reaches it later overlaps nothing (#2937: the
477
+ // stale stamp was the bridge that entangled unrelated updates
478
+ // with a never-resolving flight). Its observers are held and
479
+ // wait on its flight (`blocked`).
480
+ if (e.Oe === NOT_PENDING && e.S & STATUS_UNINITIALIZED && e.u !== EFFECT_RENDER) continue;
481
+ // A34 (2): a tick whose writes net to the committed value made no
482
+ // proposal (`setShow(false); setShow(true)` on a committed true) —
483
+ // not staged, not held, pends nothing; a later write to it is
484
+ // plain mainline. (A node already held stays held when written
485
+ // back: A34 (1).)
486
+ if (e.Oe !== NOT_PENDING && !(e.S & (STATUS_PENDING | STATUS_UNINITIALIZED)) && e.ye && e.ye(e.Ae, e.Oe) && e.m?.Qe == null && e.m?.ve == null) {
487
+ e.Oe = NOT_PENDING;
488
+ e.i &= ~CONFIG_STAGED;
489
+ continue;
857
490
  }
491
+ holdNode(e, n);
858
492
  }
493
+ // A held pass's frame is held with it (ruling A, O2): the children it
494
+ // created are the transaction's — they compute, and a pass over them
495
+ // or a write that reaches them joins; they effect at the landing.
496
+ if (e.i & CONFIG_STAGED) holdFrame(e, n);
497
+ }
498
+ pendingNodes.length = 0;
499
+ GlobalQueue.Tn?.(n);
500
+ // (This flush's runs are stashed with `t` below — after the landings,
501
+ // so a `t` that lands at this very seam runs them first, ahead of
502
+ // the runs it held from earlier flushes.)
503
+ // The batch joins: the writes that dirtied these are held with it — a
504
+ // zombie never displays that world. A verdict reader among them does
505
+ // (#3444: `latest()` inside a branch a held Show is removing peers
506
+ // through like the same read outside): it re-runs now, as the
507
+ // holder's verdict lane's work — before the lane seams, which reveal
508
+ // it (nothing commits this seam that could dispose it).
509
+ const t = deferredZombies.splice(0).sort((e, n) => e.me - n.me);
510
+ for (let e = 0; e < t.length; e++) if ((t[e].R & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE)) === REACTIVE_ZOMBIE && t[e].i & CONFIG_VERDICT) recompute(t[e]);
511
+ // The unchanged passes' tails stay linked (A30, #3469): their inputs
512
+ // are held, and the committed frame still derives from them — no trim.
513
+ } else {
514
+ commitPendingNodes();
515
+ // The flush committed: an unchanged pass's stale tail goes now. Not
516
+ // after a later pass this flush that threw (NotReady included — it
517
+ // keeps its full list, `_depsTail` marking where it stopped) or that
518
+ // left an effect owing a run (`runEffect` trims when the run applies):
519
+ // `recompute`'s own gates.
520
+ for (let e = 0; e < heldTrims.length; e++) {
521
+ const n = heldTrims[e];
522
+ if (n.m?.F == null && !n.Ge) trimStaleDeps(n);
859
523
  }
860
- return true;
861
524
  }
862
- return false;
863
- }
864
- initTransition(e) {
865
- if (e) {
866
- e = currentTransition(e);
867
- // A finished transaction cannot be re-entered: its state is committed
868
- // or reverted, so "rejoining" it (A26) is meaningless and re-activating
869
- // it spins the drain loop (#3140). The refusal must be a bare return —
870
- // redirecting the caller to a fresh batch would re-arm the loop with a
871
- // new transaction identity each pass. Stamps are cleared at commit, so
872
- // this is a belt for paths that hand over a chased-dead reference
873
- // (merged chains, async settles racing completion).
874
- if (e.Rn === true || e === activeTransition) return;
525
+ // The list is the flush's either way. Reset only when there is something
526
+ // to reset (`stagedReaders` below likewise): `length = 0` is a runtime
527
+ // call even on an empty array, and the plain flush — no unchanged pass,
528
+ // no lane work — has nothing in either.
529
+ if (heldTrims.length) heldTrims.length = 0;
530
+ // Lanes: a blocked one parks its frame (its own flight is up); an
531
+ // unblocked one reveals this round's work. Then land every transaction
532
+ // no frame is waiting on — its lanes end with it. Backwards: a landing
533
+ // removes its entry.
534
+ // This flush's own runs, the lanes' reveals, and the landings' held runs
535
+ // are collected apart and ordered below.
536
+ const t = this.rn;
537
+ const l = this.rn = [ [], [] ];
538
+ // The passes that read this frame's stagings as the screen read held
539
+ // writes if it parked: the lane seam re-derives them on the committed
540
+ // world (a lane's runs wait this round).
541
+ GlobalQueue._n?.(n !== null ? stagedReaders : null);
542
+ if (stagedReaders.length) stagedReaders.length = 0;
543
+ this.rn = [ [], [] ];
544
+ for (let e = transactions.length - 1; e >= 0; e--) {
545
+ const n = transactions[e];
546
+ if (blocked(n)) continue;
547
+ transactions.splice(e, 1);
548
+ GlobalQueue.Gn?.(n);
549
+ land(n);
550
+ // A landing's commits can dispose a zombie a transaction judged above
551
+ // was blocked on (#3463: live "until the commit that disposes it") —
552
+ // judge them again.
553
+ e = transactions.length;
875
554
  }
876
- if (!e && activeTransition && activeTransition.be === clock) return;
877
- if (!activeTransition) {
878
- activeTransition = e ?? createBatch();
879
- } else if (e) {
880
- const t = activeTransition;
881
- mergeTransitionState(e, t);
882
- // Effects the outgoing transaction parked belong to the surviving one
883
- // now: back onto the live queue, where this flush parks them under
884
- // `transition` or runs them at its completion. The outgoing stash is
885
- // never read again — the transaction is dead (#3310).
886
- this.restoreQueues(t.dt);
887
- transitions.delete(t);
888
- activeTransition = e;
555
+ // The store folds the pending backings whose container nodes committed
556
+ // — this flush's, or a landing's (store/store.ts installs it).
557
+ GlobalQueue.Sn?.();
558
+ // Marks declared outside a transaction: the flush that carried them
559
+ // parked (something async below) — the window is the frame's, the marks
560
+ // are its transaction's; else they release at the seam, verdict-only,
561
+ // nothing to show (affects.ts).
562
+ const o = n && liveTx(n);
563
+ GlobalQueue.A?.(o);
564
+ // The effect phase: lane work first (displayed ahead of the frame), then
565
+ // this flush's runs, then the runs the landings held from earlier
566
+ // flushes — as one pass would have queued them (#3540: a shell
567
+ // re-derived at the landing before a boundary swap held since the
568
+ // write; #3528: a display-ahead swap before the count it is shown
569
+ // beside). A parked frame's runs wait with its transaction instead: the
570
+ // effect phase applies a committed frame.
571
+ const u = o !== null;
572
+ for (let e = 0; e < 2; e++) {
573
+ if (u) append(o.rn[e], t[e]);
574
+ // The plain flush — no reveal, no landing, not parked — has nothing to
575
+ // order around its own runs: they are the queue as they stand (the
576
+ // same array, not a copy; `run` takes it whole). Every other seam
577
+ // builds the ordered queue above.
578
+ this.rn[e] = u || l[e].length || this.rn[e].length ? l[e].concat(u ? [] : t[e], this.rn[e]) : t[e];
889
579
  }
890
- transitions.add(activeTransition);
891
- activeTransition.be = clock;
892
- const t = this.m;
893
- if (t !== activeTransition) {
894
- // Adopt the ambient batch into the transaction, then make the
895
- // transaction the batch so later registrations land there directly.
896
- // Pending and optimistic nodes are re-stamped as the transaction's;
897
- // marks don't hijack the node's _transition — a mark on a plain signal
898
- // must not entangle unrelated writes to it; the same rule holds one hop
899
- // downstream: propagation never queues pended subscribers as pending
900
- // nodes, see propagateAffectsMark, #2893.
901
- // Adopted outside a flush: the staging is still unflushed — the stamp
902
- // must not make it read as held-and-carried (CONFIG_ADOPTED_UNFLUSHED;
903
- // the carrying flush clears it in reassignPendingTransition).
904
- const e = this.Jn ? 0 : CONFIG_ADOPTED_UNFLUSHED;
905
- for (let n = 0; n < t.Gn.length; n++) {
906
- const i = t.Gn[n];
907
- // A tick that nets to the committed value proposed nothing (A34, #3494):
908
- // `setShow(false); setShow(true)` beside a write that opens a hold
909
- // left `show` staged at its own value, stamped, pending to the
910
- // verdict, and its next mainline write held by a flight it never
911
- // derived from. Unstage it here — its subscribers were walked at the
912
- // write and re-derive the same value. Writes only: a signal's staging
913
- // is always one, a computed's only under REACTIVE_MANUAL_WRITE
914
- // (`createSignal(fn)`'s setter, #3519 review) — otherwise it is its
915
- // pass's result, which may equal an uninitialized `undefined` (a
916
- // born-held first pass). `_equals: false` opts out. The unstaging is
917
- // the commit's own path (commitPendingNode with nothing staged): the
918
- // manual-write flag, companions and the rest are cleaned up as a
919
- // commit would, and the node is stamped nowhere. Unstamped
920
- // only: a node already a transaction's — arriving here as a parked
921
- // batch folds into a merge — carries a FLUSHED proposal a later
922
- // rewrite brought back to the committed value; it is held, not
923
- // proposal-free. Dropped, it kept the dead stamp, and the next write
924
- // to it queued under the merged transaction a value the commit then
925
- // skipped as another's (fuzzer latest-2 #1470, S3).
926
- if (i.we === null && i._e !== NOT_PENDING && (!i.he || i.oe & REACTIVE_MANUAL_WRITE && !(i.h & STATUS_UNINITIALIZED)) && i.xe && i.xe(i.ce, i._e)) {
927
- i._e = NOT_PENDING;
928
- commitPendingNode(i);
929
- continue;
930
- }
931
- i.we = activeTransition;
932
- i.C |= e;
933
- activeTransition.Gn.push(i);
934
- }
935
- for (let e = 0; e < t.cn.length; e++) {
936
- const n = t.cn[e];
937
- n.we = activeTransition;
938
- activeTransition.cn.push(n);
580
+ if (deferredZombies.length !== 0) {
581
+ // Survivors are the displayed frame of a held pass: they rerun for the
582
+ // live write, parents before children.
583
+ const e = deferredZombies.splice(0).sort((e, n) => e.me - n.me);
584
+ for (let n = 0; n < e.length; n++) {
585
+ if (!(e[n].R & REACTIVE_DISPOSED)) recompute(e[n]);
939
586
  }
940
- if (t.A.length) activeTransition.A.push(...t.A);
941
- for (const e of t.ti) activeTransition.ti.add(e);
942
- // Gated readers recorded against the ambient batch move with it: their
943
- // replay-at-commit now happens at the transaction's completion.
944
- if (t.In.size) {
945
- for (const e of t.In) activeTransition.In.add(e);
946
- t.In.clear();
587
+ }
588
+ // Boundaries: readers that settled, landed or died this flush are
589
+ // dropped; a fallback with none left reveals next round.
590
+ GlobalQueue.se?.();
591
+ }
592
+ run(e) {
593
+ const n = this.rn[e - 1];
594
+ if (n.length) {
595
+ this.rn[e - 1] = [];
596
+ runQueue(n, e);
597
+ }
598
+ }
599
+ enqueue(e, n, t = passLane) {
600
+ // Lane work's runs are the lane's: they run at its reveal, not with
601
+ // the frame (which may park). A lane pass that read the frame's own
602
+ // staging is the frame's (`recompute` passes null): its run waits with
603
+ // the frame.
604
+ if (e) (t !== null ? t : this).rn[e - 1].push(n);
605
+ schedule();
606
+ }
607
+ /** Status reaching the root: pending is absorbed (the root mount defers —
608
+ * dev records it for the ASYNC_OUTSIDE_LOADING_BOUNDARY FYI); an error is
609
+ * unhandled (`false` → the caller halts).
610
+ *
611
+ * L2: this is where a transaction is registered. Pending that reaches an
612
+ * observer (a render effect: the frame) is a frame that cannot show — the
613
+ * flush parks, and the transaction waits on the flight while a frame
614
+ * derives from it (`blocked`). Pending nobody observes holds nothing: a
615
+ * memo in flight that no frame reads is not a frame waiting. (Base: the
616
+ * queue's `_asyncReporters`, INV-3's one registration site.)
617
+ *
618
+ * A boundary between the observer and the root (boundaries.ts) is asked
619
+ * first: a fallback catches what is not ready under it, and the root
620
+ * never hears of it. */ notify(e, n, t, l) {
621
+ if (GlobalQueue.ie && GlobalQueue.ie(e, t & n, l)) return true;
622
+ if (n & STATUS_PENDING) {
623
+ if (t & STATUS_PENDING) {
624
+ // A pass that went pending holds its frame: the async's own
625
+ // synchronous frame is not ready (A15 reveal corollary: a reveal
626
+ // that discovers a flight whose inputs are visible holds and joins
627
+ // it; one whose inputs are held is a stale reader and never gets
628
+ // here — `read` serves it the committed value). The frame's, not the
629
+ // observer's: a render effect belongs to whoever dirtied its pass
630
+ // (A15 shared-hole, #3407) — one held by T1 and re-run by T2's write
631
+ // pends for T2's flight, and the two stay parallel; a frame that is
632
+ // T1's already joined T1 through the write or the memo that made it
633
+ // so. Outside a flush — root setup, a mount — the frame is the
634
+ // mount's: its pending observers are one transaction, and a flight
635
+ // started in it resumes it when it lands (maintainer, #3461: "part
636
+ // of the same one"); what the mount published synchronously stays
637
+ // published. Lane work going pending holds the lane, not the frame:
638
+ // `blocked(lane)` finds it in the lane's staging ("if further async
639
+ // downstream is hit it holds like its own transition").
640
+ if (passLane === null) joinFuture(null);
947
641
  }
948
- currentBatch = this.m = activeTransition;
642
+ return true;
949
643
  }
950
- for (const e of activeLanes) {
951
- if (!e.we) e.we = activeTransition;
644
+ return false;
645
+ }
646
+ }
647
+
648
+ /** A15's stale readers: a frame whose LAST pass read a held node as
649
+ * committed re-derives on the landed world — next round, after the commits.
650
+ * One re-derived since (any later pass cleared the bit: in the transaction,
651
+ * or without the held read) owes nothing. A run this transaction stashed
652
+ * for it is void (#3322: an effect has one value slot, and the stale pass
653
+ * overwrote the value that run was for) — the re-derivation's run replaces
654
+ * it. */ function reruns(e) {
655
+ for (let n = 0; n < e.fn.length; n++) {
656
+ const t = e.fn[n];
657
+ // An errored reader's display is its error (D-39): the landing gives it
658
+ // nothing to re-derive, and a re-run would be a read that retries the
659
+ // errored source (#3945).
660
+ if (t.R & REACTIVE_DISPOSED || t.S & STATUS_ERROR) continue;
661
+ if (t.R & REACTIVE_FRAME_READ || t.i & CONFIG_VERDICT) {
662
+ t.R &= ~REACTIVE_FRAME_READ;
663
+ t.Ge = false;
664
+ // Its frame is replaced by the re-derivation: a run one of its
665
+ // children queued this flush (re-run by the same landing) is for the
666
+ // frame being replaced — void too (#3404).
667
+ for (let e = t.Ve; e !== null; e = e.ge) if (e.u) e.Ge = false;
668
+ enqueueSub(t);
952
669
  }
953
- // A transaction's ambient window is one flush. Entering must therefore
954
- // guarantee a flush: a transaction opened with no writes (an action whose
955
- // first statements only await) otherwise leaves activeTransition and the
956
- // adopted batch armed across the async gap, and the next unrelated work
957
- // to arrive — an optimistic store's authoritative landing, a plain async
958
- // settle — is adopted into a transaction it has nothing to do with
959
- // (#3141). The scheduled flush parks the incomplete transaction through
960
- // the normal machinery and detaches the ambient slots first.
961
- schedule();
670
+ }
671
+ e.fn.length = 0;
672
+ }
673
+
674
+ /** The landing: the held frame becomes the frame. */ function land(e) {
675
+ reruns(e);
676
+ if (e.T !== null) GlobalQueue.k(e.T);
677
+ // Old children die in the commits (cleanups first), then the stashed
678
+ // effects run ahead of this flush's own. A node a lane took over since
679
+ // (a guess written over the staged truth) is the lane's to land.
680
+ for (let n = 0; n < e.ln.length; n++) {
681
+ const t = e.ln[n];
682
+ if (t.m.He === null || txOf(t) !== e) continue;
683
+ t.m.He = null;
684
+ commitPendingNode(t);
685
+ }
686
+ releaseQueues(e);
687
+ }
688
+
689
+ /** The held runs join this flush's effect phase after its own runs (the
690
+ * landing's re-passes, in height order): the frame's effects run as one
691
+ * pass would have queued them — a shell re-derived at the landing before a
692
+ * boundary swap held since the write (#3540's log order). */ function releaseQueues(e) {
693
+ for (let n = 0; n < 2; n++) {
694
+ append(globalQueue.rn[n], e.rn[n]);
695
+ e.rn[n] = [];
696
+ }
697
+ }
698
+
699
+ /** `a.push(...b)` without the argument-count limit. */ function append(e, n) {
700
+ for (let t = 0; t < n.length; t++) e.push(n[t]);
701
+ }
702
+
703
+ /** Nodes staged this flush (`_pendingValue` set), committed at its end. */ const pendingNodes = [];
704
+
705
+ /** Passes that read a staging of this flush as the screen — lane work
706
+ * (core.ts `stagedRead`), a verdict reader before the frame's verdict
707
+ * (verdict.ts): if the frame parks they read a held write — re-derived next
708
+ * round (lanes.ts; a lane's runs wait that round). */ const stagedReaders = [];
709
+
710
+ /** A15's stale reader: `c` was served the committed value of a node `t`
711
+ * holds (a render effect, a verdict reader, lane work) and is re-derived
712
+ * after `t`'s landing (`_reruns`). Once per pass. */ function staleReader(e, n) {
713
+ if (!(e.R & REACTIVE_FRAME_READ)) {
714
+ e.R |= REACTIVE_FRAME_READ;
715
+ n.fn.push(e);
962
716
  }
963
717
  }
964
718
 
965
719
  function queuePendingNode(e) {
966
- currentBatch.Gn.push(e);
967
- if (!globalQueue.Jn) markUnflushedStaged();
720
+ pendingNodes.push(e);
721
+ if (!globalQueue.X) markUnflushedStaged();
968
722
  // A28
969
723
  }
970
724
 
971
- // Sticky: flips true on the first refresh() ever (the only setter of
972
- // REACTIVE_REASK) so the hot notification loop skips the per-subscriber flag
973
- // clear entirely in apps that never refresh.
974
- let reaskArmed = false;
975
-
976
725
  /** §12d: bumped by every recompute and every new subscriber edge. A node's
977
726
  * staged-rewrite skip is sound only while NOTHING recomputed or linked since
978
727
  * its last notify — a mid-batch pull can clean a marked subscriber, and a
@@ -982,49 +731,17 @@ function bumpNotifyEpoch() {
982
731
  notifyEpoch++;
983
732
  }
984
733
 
985
- function armReaskClear() {
986
- reaskArmed = true;
987
- }
988
-
989
- /** Provenance of the work currently running (A18 supersession, #3331): the
990
- * invocation sequence of the action whose ambient window this is — set by
991
- * action() for each slice; the flush that ends the window clears it — or,
992
- * inside an async landing, the sequence captured when that flight was
993
- * registered (asyncWrite sets it for the landing's synchronous propagation,
994
- * so a sync recompute downstream of the landing — an optimistic wrapper over
995
- * the async source — derives under the flight's provenance, and flights it
996
- * registers inherit it). 0 is mainline: no action, always the current
997
- * question. An override stamps this at its write (`_overrideStamp`); an
998
- * answer whose flight an OLDER action issued is a stale question the user
999
- * has since changed — it holds silently to commit instead of superseding. A
1000
- * slow source must not leak back in over a newer intent. Transactions merge,
1001
- * so the transition object cannot say WHICH action asked; this can. */ let origin = 0;
1002
-
1003
- function setOrigin(e) {
1004
- const t = origin;
1005
- origin = e;
1006
- return t;
1007
- }
1008
-
1009
- function insertSubs(e, t = false) {
734
+ function insertSubs(e) {
1010
735
  // §12d: stamp before walking — setSignal's staged-rewrite fast path skips
1011
736
  // the next walk for this node while the epoch holds (marking is idempotent).
1012
- e.vn = notifyEpoch;
1013
- // Get source lane: prefer node's own lane over current context
1014
- // This is important for isPending signals which need their own lane to flush immediately
737
+ e.qe = notifyEpoch;
1015
738
  // Presence bits gate the optional-slot probes (see constants.ts): one
1016
739
  // masked read of the always-present _config instead of missing-property
1017
- // lookups in the hottest notify loop. Bits are sticky — the field read
1018
- // stays authoritative when a bit is set.
1019
- const n = e.C;
1020
- const i = (n & CONFIG_HAS_LANE ? e.o?.me : undefined) || currentOptimisticLane;
1021
- const r = (n & CONFIG_HAS_SNAPSHOT) !== 0 && e.o?.sn !== undefined;
1022
- const s = reaskArmed;
1023
- for (let n = e.u; n !== null; n = n.Ae) {
1024
- const e = n.ge;
1025
- // A value-change notification is a new question for the subscriber: any
1026
- // pending re-ask mark (refresh) it carried is superseded.
1027
- if (s) e.oe &= ~REACTIVE_REASK;
740
+ // lookups in the hottest notify loop.
741
+ const n = e.i;
742
+ const t = (n & CONFIG_HAS_SNAPSHOT) !== 0 && e.m?.Le !== undefined;
743
+ for (let n = e.t; n !== null; n = n.o) {
744
+ const e = n.l;
1028
745
  // Missed-wake latch (#3037): this write is landing while the subscriber
1029
746
  // is mid-recompute (a nested pull committing beneath its reads), and the
1030
747
  // heap refuses RECOMPUTING nodes. A gen-current link means the pass
@@ -1033,274 +750,103 @@ function insertSubs(e, t = false) {
1033
750
  // pass either re-reads them fresh or trims them), and neither does the
1034
751
  // tail link: it is the read IN FLIGHT — read() links before it pulls, so
1035
752
  // this very commit is what that read returns.
1036
- if (e.oe & REACTIVE_RECOMPUTING_DEPS && n.Be === e.Ye && n !== e.Nn) e.oe |= REACTIVE_MISSED_WAKE;
1037
- if (r && e.C & CONFIG_IN_SNAPSHOT_SCOPE) {
1038
- e.oe |= REACTIVE_SNAPSHOT_STALE;
753
+ if (e.R & REACTIVE_RECOMPUTING_DEPS && n.Pe === e.pe && n !== e.G) e.R |= REACTIVE_MISSED_WAKE;
754
+ if (t && e.i & CONFIG_IN_SNAPSHOT_SCOPE) {
755
+ e.R |= REACTIVE_SNAPSHOT_STALE;
1039
756
  continue;
1040
757
  }
1041
- if (t && i) {
1042
- e.oe |= REACTIVE_OPTIMISTIC_DIRTY;
1043
- assignOrMergeLane(e, i);
1044
- } else if (t) {
1045
- e.oe |= REACTIVE_OPTIMISTIC_DIRTY;
1046
- // No source lane means reversion - clear subscriber's lane so effects go to regular queue
1047
- if (e.o) e.o.me = undefined;
1048
- }
1049
758
  enqueueSub(e);
1050
759
  }
1051
760
  }
1052
761
 
1053
- function commitPendingNode(e) {
1054
- const t = e;
1055
- if (!t.he) {
1056
- if (e._e !== NOT_PENDING) {
1057
- e.ce = e._e;
1058
- e._e = NOT_PENDING;
1059
- }
1060
- if (e.C & CONFIG_HAS_COMPANIONS) GlobalQueue.ri(e);
1061
- return;
762
+ /** Lane work re-staged (lanes.ts; `recompute`): the lane's members among
763
+ * its subscribers re-derive as the lane's work (REACTIVE_LANE_DIRTY), their
764
+ * runs held with it — not as stale readers republishing the committed view
765
+ * (#3460). After `insertSubs`. A render effect on no lane is marked too:
766
+ * inert for its pass (`recompute` seats a lane only from its own `_x`), it
767
+ * is a live write if the effect becomes a zombie later this flush — an
768
+ * on-screen reader whose removal an adopted write stages follows the lane
769
+ * (the lane-membership ruling, 2026-10-05; `_update`). */ function laneDirty(e, n) {
770
+ for (let t = e.t; t !== null; t = t.o) {
771
+ const e = t.l.m?.He;
772
+ if (e?.B ? sameLane(e, n) : t.l.u) t.l.R |= REACTIVE_LANE_DIRTY;
1062
773
  }
1063
- if (e._e !== NOT_PENDING) {
1064
- e.ce = e._e;
1065
- e._e = NOT_PENDING;
1066
- // A node born held (recompute) initializes at this commit.
1067
- t.h &= ~STATUS_UNINITIALIZED;
1068
- // Set _modified for effects, but not for tracked effects (they handle their own scheduling)
1069
- if (e.je && e.je !== EFFECT_TRACKED) e.Xe = true;
1070
- // A quiet re-ask classification preserved through a held landing dies
1071
- // with the value commit — the commit IS the reveal (#3178). Gated on the
1072
- // staged value: status propagation queues pending nodes whose windows
1073
- // are still OPEN (no staged value), and their live classification must
1074
- // survive this sweep.
1075
- if (e.o) e.o.Le = false;
1076
- }
1077
- // The committed hold is the first observable answer for a loading-window
1078
- // node — the window closes here, not at compute time (#2990). Unconditional
1079
- // store to an always-present computed slot.
1080
- t.ve = false;
1081
- t.oe &= ~REACTIVE_MANUAL_WRITE;
1082
- // The children this commit publishes are the frame's now (#3404) — and so
1083
- // are the dependencies of the pass that produced the value: the previous
1084
- // frame's tail goes (A30, #3410; `recompute` left it for a staged pass). Only
1085
- // after a clean pass: `_error` is cleared by a clean pass or by the node's
1086
- // own landing (whose pass was clean), so a set `_error` means the last pass
1087
- // threw, kept its full list, and `_depsTail` marks where it stopped.
1088
- if (t.o?._ == null) trimStaleDeps(t);
1089
- // A LANE frame still parked here (#3662) rode a hold that stashed the run
1090
- // that would have retired it: this commit applies that run, so it goes too.
1091
- t.C &= -68157441;
1092
- if (!(t.h & STATUS_PENDING)) t.h &= ~STATUS_UNINITIALIZED;
1093
- // A flight this commit leaves in the air (unobserved, or observed only by
1094
- // a boundary) now has PUBLISHED inputs: its committed value is stale
1095
- // against the frame. read()'s reveal carve-out keys on the mark (#3305).
1096
- else e.C |= CONFIG_INPUTS_PUBLISHED;
1097
- if (t.o != null && (t.o._n !== null || t.o.fn !== null)) GlobalQueue.en(t, false, true);
1098
- if (e.C & CONFIG_HAS_COMPANIONS) GlobalQueue.ri(e);
1099
774
  }
1100
775
 
1101
- // Store commit hook (INTERNALS-STORE-STATE.md §3): installed by the store
1102
- // module at init (same treeshakeable pattern as _resolveOptimistic /
1103
- // _clearOptimisticStores). Folds committed store-node values into their
1104
- // backing objects at the same moment pending values commit — the single
1105
- // mutation point of the owned-raw model.
1106
- let storeCommitHook = null;
1107
-
1108
- function setStoreCommitHook(e) {
1109
- storeCommitHook = e;
776
+ /** One lane, or two of one link group (#3335): one reveal unit. */ function sameLane(e, n) {
777
+ return e === n || e.nn !== null && e.nn === n.nn;
1110
778
  }
1111
779
 
1112
- /** Held truth committed this finalize, awaiting its post-revert wake (see
1113
- * finalizePureQueue): the commit IS the reveal, but subscribers must not
1114
- * re-derive until the settling transaction's optimistic overrides have
1115
- * reverted — a commit-time wake recomputes them in the window where
1116
- * confirming truth is committed and the override still displays, a torn
1117
- * frame no timeline contains. */ const heldRevealed = [];
1118
-
1119
- /** Unchanged passes with a stale dependency tail, waiting on this flush's
1120
- * verdict (A30, #3469). A pass that changed nothing replaced nothing either —
1121
- * and cannot know at its own tail whether the flush that ran it will park:
1122
- * parked, its inputs are held and the committed frame still derives from the
1123
- * tail (`b() ? b() : a()` computed `1` from the held `b`, equal to the `1` it
1124
- * had from `a` — with `a` trimmed, the mainline `a = 2` never reached it).
1125
- * Trimmed when the flush commits; dropped with a park, the tail stays linked
1126
- * until a committing pass trims it (one spurious recompute at most). */ const heldTrims = [];
1127
-
1128
- function commitPendingNodes() {
1129
- while (heldTrims.length) trimStaleDeps(heldTrims.pop());
1130
- const e = currentBatch.Gn;
1131
- for (let t = 0; t < e.length; t++) {
1132
- const n = e[t];
1133
- commitPendingNode(n);
1134
- // The stamp dies with the commit (#3143) — symmetric with
1135
- // resolveOptimisticNodes clearing optimistic stamps. A stamp outliving
1136
- // its transaction let any later write (even a value-equal no-op, which
1137
- // re-opens before the equality bail) resurrect the finished transaction;
1138
- // a boundary flag rewritten every finalize pass then spun the drain loop
1139
- // forever (#3140). The held-truth mark dies the same death — the commit
1140
- // IS the reveal — but its wake defers to the post-revert pass: ordinary
1141
- // subscribers were masked to committed all hold (some re-derived against
1142
- // that old view and cached it), and commits are otherwise silent
1143
- // (staging already notified), so without a wake they'd hold the old
1144
- // world forever.
1145
- n.we = null;
1146
- if (n.C & CONFIG_HELD_TRUTH) {
1147
- n.C &= ~CONFIG_HELD_TRUTH;
1148
- heldRevealed.push(n);
780
+ function commitPendingNode(e) {
781
+ const n = e;
782
+ // L2: the commit is where a pass, held or not, stops being uncommitted —
783
+ // unless it has no answer yet. A pass still in flight (pending, nothing
784
+ // staged) stays uncommitted: a re-pass disposes its children on the spot,
785
+ // and the frame it parked stays the frame until a pass of this node
786
+ // commits a value.
787
+ if (!n.ee) {
788
+ e.i &= -385;
789
+ if (e.Oe !== NOT_PENDING) {
790
+ e.Ae = e.Oe;
791
+ e.Oe = NOT_PENDING;
1149
792
  }
793
+ return;
1150
794
  }
1151
- e.length = 0;
1152
- storeCommitHook?.();
1153
- }
1154
-
1155
- function finalizePureQueue(e = null, t = false) {
1156
- // For incomplete transitions, skip pending resolution and optimistic reversion
1157
- // For completing transitions or no-transition, resolve pending and revert optimistic
1158
- const n = currentBatch;
1159
- const i = !t;
1160
- if (i) commitPendingNodes();
1161
- // A parked finalize sweeps nothing: the boundaries' staged swaps are the
1162
- // transaction's, and a boundary whose own output parks the verdict was
1163
- // judged under it in run(), ahead of the verdict (#3540).
1164
- if (!t && globalQueue.ft.length) checkBoundaryChildren(globalQueue);
1165
- // Contested effects (#3322) re-derive from the world this commit just
1166
- // produced. Ahead of the heap run — not the post-heap gated replay — so
1167
- // the recompute and the effect phase land in this same pass and the value
1168
- // the other transaction wrote into the slot is never published.
1169
- // (No clear: a completed transition is never finalized again.)
1170
- // A transaction whose settle reverts optimism re-derives them post-revert
1171
- // instead (below, with the gated replay): between commitPendingNodes and
1172
- // _resolveOptimistic the truth is committed but the overrides still
1173
- // display, and a re-derive here would compose the two — the #3164 tear,
1174
- // one window later. The slot meanwhile holds the frame that is on screen.
1175
- const r = e?.Dn;
1176
- const s = i && (e ?? n).cn.length !== 0;
1177
- if (r && !s) for (const e of r) if (!(e.oe & REACTIVE_DISPOSED)) enqueueSub(e);
1178
- const o = dirtyQueue.EE >= dirtyQueue.ln;
1179
- if (o) runHeap(dirtyQueue, GlobalQueue.Ke);
1180
- if (i) {
1181
- // Boundary checks, commit hooks and recomputes can enter a transaction,
1182
- // which adopts the batch this finalize was settling: nothing batch-derived
1183
- // may be committed or reverted here — the entered transaction owns it now
1184
- // (#3319). A completing transaction's OWN containers are a different
1185
- // matter: when the ambient batch was separate from it (the #2916 shape),
1186
- // adoption never touched them and it must still settle them; when the
1187
- // batch WAS the completing transaction, adoption re-stamped its contents
1188
- // into the entered one and there is nothing left to settle.
1189
- if (currentBatch !== n) {
1190
- if (e === null || e === n) return;
1191
- } else if (o) commitPendingNodes();
1192
- // The settling batch: the completing transaction's, or the ambient one.
1193
- const t = e ?? n;
1194
- // Optimistic reversion: a non-empty batch means _optimisticWrite ran,
1195
- // which installed the engine's hooks.
1196
- if (t.cn.length) GlobalQueue.oi(t.cn);
1197
- if (r && s) {
1198
- for (const e of r) if (!(e.oe & REACTIVE_DISPOSED)) enqueueSub(e);
1199
- schedule();
1200
- }
1201
- // Replay entanglement: subs recorded by the read-time gate get rescheduled
1202
- // so they re-run with the now-committed values visible. The ambient batch
1203
- // replays too — laneReadsCommitted records readers whose committed-view
1204
- // read hid a same-tick plain write that just committed above (#2963).
1205
- if (t.In.size) {
1206
- for (const e of t.In) {
1207
- if (e.oe & REACTIVE_DISPOSED) continue;
1208
- enqueueSub(e);
1209
- }
1210
- t.In.clear();
1211
- // A completing transition keeps the outer flush loop alive by itself;
1212
- // the ambient batch needs the re-arm or the replay sits in the heap
1213
- // until the next unrelated write.
1214
- schedule();
1215
- }
1216
- // Declared motion ends with the transaction: settle (or plain flush end
1217
- // for ambient marks) releases each registration's refcount. A non-empty
1218
- // batch means registerAffectsMark ran, which installed the hook. Marks
1219
- // held boundary display state through the visual channel, and their
1220
- // release is the display-state update point — re-run the boundary sweep
1221
- // (the earlier sweep above ran while the marks were still live).
1222
- if (t.A.length) {
1223
- GlobalQueue.G(t.A);
1224
- if (globalQueue.ft.length) checkBoundaryChildren(globalQueue);
1225
- }
1226
- // A non-empty set means trackOptimisticStore ran, which installed the
1227
- // hook; the hook iterates, clears, and schedules (keeping the loop out of
1228
- // core lets esbuild shake it — rollup already folds the null guard). The
1229
- // completing transition scopes the clear to its own layer keys (#2899).
1230
- if (t.ti.size) GlobalQueue._t(t.ti, e);
1231
- // Held-truth reveal wake (#3164), post-revert by construction: this
1232
- // finalize committed confirming truth whose subscribers were masked all
1233
- // hold — some re-derived against the committed view (the staging, or a
1234
- // confirming carrier's landing, notified them as a plain write) and
1235
- // cached it, and stash-restored applies may carry those torn values.
1236
- // Waking and recomputing HERE — after _resolveOptimistic and the store
1237
- // clears above — means every apply paints the settled view; a wake at
1238
- // commit time would recompute them in the window where truth is
1239
- // committed but the settling transaction's overrides still display.
1240
- if (heldRevealed.length !== 0) {
1241
- while (heldRevealed.length) insertSubs(heldRevealed.pop());
1242
- if (dirtyQueue.EE >= dirtyQueue.ln) {
1243
- runHeap(dirtyQueue, GlobalQueue.Ke);
1244
- commitPendingNodes();
1245
- }
795
+ // Computeds only from here (`_statusFlags` is not a signal field). A
796
+ // commit beneath a flight publishes its inputs (A15 reveal corollary,
797
+ // #3305): a reveal of it must observe, not show the pre-flight value.
798
+ const t = e.Oe === NOT_PENDING && (n.S & STATUS_PENDING) !== 0;
799
+ e.i = t ? e.i & -1048833 | CONFIG_INPUTS_PUBLISHED : e.i & -1049985;
800
+ if (e.Oe !== NOT_PENDING) {
801
+ e.Ae = e.Oe;
802
+ e.Oe = NOT_PENDING;
803
+ // A node born staged (recompute) initializes at this commit.
804
+ n.S &= ~STATUS_UNINITIALIZED;
805
+ // A quiet re-ask's classification survives its landing and dies with
806
+ // the landed value's commit (A19 exc. 2, #3178): verdict-quiet through
807
+ // the reveal. (The sweep of a node still in flight is not a landing.)
808
+ // The A28 stash goes with it.
809
+ if (n.m != null) n.m.ke = false, n.m.ze = NOT_PENDING;
810
+ // An effect with a staged value was born held (L2): its first run is
811
+ // this commit's, not its creation's (A29) — queue it now.
812
+ if (e.u && e.u !== EFFECT_TRACKED) {
813
+ e.Ge = true;
814
+ globalQueue.enqueue(e.u, e.Y ??= GlobalQueue.q.bind(null, n));
1246
815
  }
1247
- sweepTransientStoreNodes();
1248
- // Lanes only enter activeLanes through the engine's getOrCreateLane.
1249
- if (activeLanes.size) GlobalQueue.li(e);
1250
816
  }
817
+ // The committed value is the first observable answer for a loading-window
818
+ // node — the window closes here, not at compute time (#2990). Unconditional
819
+ // store to an always-present computed slot.
820
+ n.De = false;
821
+ n.R &= ~REACTIVE_MANUAL_WRITE;
822
+ // The dependencies of the pass that produced the value are the frame's now:
823
+ // the previous frame's tail goes (A30, #3410; `recompute` left it for a
824
+ // staged pass). Only after a clean pass: `_error` is cleared by a clean
825
+ // pass or by the node's own landing (whose pass was clean), so a set
826
+ // `_error` means the last pass threw, kept its full list, and `_depsTail`
827
+ // marks where it stopped.
828
+ if (n.m?.F == null) trimStaleDeps(n);
829
+ if (!(n.S & STATUS_PENDING)) n.S &= ~STATUS_UNINITIALIZED;
830
+ // L2: the children this commit publishes are the frame's now — the frame
831
+ // they replace, parked by the pass (`recompute`), goes.
832
+ if (!t && n.m != null && (n.m.Qe !== null || n.m.ve !== null)) GlobalQueue.Fe(n, false, true);
1251
833
  }
1252
834
 
1253
- /** The boundary sweep: the commit's (`_checkSources`), or — `held`, before
1254
- * the verdict (#3540) — the one for a collecting boundary whose output is
1255
- * pending on its fallback's read (`_judgeHeld`). */ function checkBoundaryChildren(e, t) {
1256
- for (const n of e.ft) {
1257
- t ? n.Re?.() : n.Ee?.();
1258
- checkBoundaryChildren(n, t);
835
+ function commitPendingNodes() {
836
+ for (let e = 0; e < pendingNodes.length; e++) {
837
+ const n = pendingNodes[e];
838
+ // A node a lane pass re-staged this round is the lane's to reveal; one
839
+ // a transaction holds (born held by a mainline pass, `passTx`) is that
840
+ // transaction's to commit, at its landing.
841
+ if (!(n.i & (CONFIG_OVERRIDE | CONFIG_HELD))) commitPendingNode(n);
1259
842
  }
1260
- }
1261
-
1262
- /**
1263
- * Count of live `affects()` registrations across the system (including
1264
- * store-scope inherited marks). Gates the read-path mark check in `read()` so
1265
- * graphs that never use the feature pay one integer compare.
1266
- */ let activeAffectsMarks = 0;
1267
-
1268
- /**
1269
- * Counter mutation seam for the mark engine in affects.ts: an imported `let`
1270
- * binding is read-only, and the read-path gate above must stay a plain module
1271
- * variable so `read()` pays one integer compare, not a function call.
1272
- *
1273
- * @internal
1274
- */ function shiftAffectsMarks(e) {
1275
- activeAffectsMarks += e;
1276
- }
1277
-
1278
- function reassignPendingTransition(e) {
1279
- for (let t = 0; t < e.length; t++) {
1280
- e[t].we = activeTransition;
1281
- e[t].C &= ~CONFIG_ADOPTED_UNFLUSHED;
1282
- // this flush carried it
1283
- }
843
+ pendingNodes.length = 0;
1284
844
  }
1285
845
 
1286
846
  const globalQueue = new GlobalQueue;
1287
847
 
1288
- // Hot-path mirror of `globalQueue._batch`: `queuePendingNode` runs once per
1289
- // staged write and `commitPendingNodes` once per flush, and the extra
1290
- // property hop through `_batch` was a measured instruction-count regression
1291
- // (CodSpeed update1to1, PR #2905). The field stays authoritative for
1292
- // cross-module readers; every `_batch` assignment updates both.
1293
- let currentBatch = globalQueue.m;
1294
-
1295
848
  function flush(e) {
1296
- // Inside an action body the drain is incoherent (#3333): the action's
1297
- // writes are held by its transaction until it settles, so a drain can't
1298
- // reveal them — and the loop below only exits once `activeTransition` is
1299
- // null, so it would PARK the transaction mid-slice and every write after it
1300
- // in the body would land as a plain, committed write. The reporter's
1301
- // "leading flush()" workaround was exactly that leak. Prod: run `fn` if
1302
- // given (its writes stay in the transaction) and skip the drain.
1303
- if (actionStepDepth > 0) {
849
+ if (actionDepth > 0) {
1304
850
  return e ? e() : undefined;
1305
851
  }
1306
852
  if (e) {
@@ -1317,212 +863,17 @@ function flush(e) {
1317
863
  }
1318
864
  }
1319
865
  }
1320
- if (globalQueue.Jn) {
866
+ if (globalQueue.X) {
1321
867
  return;
1322
868
  }
1323
869
  if (halted) return;
1324
- // `flush()` is an explicit drain point, so it must also process an active
1325
- // transition even if no microtask was scheduled for it yet.
1326
- while (scheduled || activeTransition) {
870
+ while (scheduled) {
1327
871
  globalQueue.flush();
1328
872
  }
1329
- // Provenance ends with the drain: every ambient window (an action's first
1330
- // slice, a landing's propagation) runs to this flush.
1331
- origin = 0;
1332
- }
1333
-
1334
- function runQueue(e, t) {
1335
- for (let n = 0; n < e.length; n++) e[n](t);
1336
873
  }
1337
874
 
1338
- /** Does `reporter` still hold the transaction waiting on `source` — live,
1339
- * routed to no collecting loading boundary, and deriving from the source in
1340
- * its current pass? The verdict's per-reporter test (sourceObserved), also
1341
- * a boundary re-arm's (boundaries.ts `_rearm` runs before the verdict prunes
1342
- * registrations that stopped counting). */ function reporterBlocksSource(e, t, n) {
1343
- const i = e.oe;
1344
- if (i & REACTIVE_DISPOSED) return false;
1345
- // A zombie renders until the commit that disposes it (#3463): while its
1346
- // removal is staged in a live transaction it is still on screen, and what
1347
- // it displays must stay consistent with the frame — a held `Show`'s
1348
- // `Details: 0` beside the lane's `Value: 1` otherwise. Its say is moot for
1349
- // the verdict of the transaction that stages the removal (`verdict`): done,
1350
- // and the commit disposes it; not done, and it stays parked regardless. A
1351
- // zombie whose removal commits this flush (owner's pass not held) is dead.
1352
- // The owner is stamped when the flush parks; held in this flush, its
1353
- // staging transaction is the active one.
1354
- if (i & REACTIVE_ZOMBIE) {
1355
- let t = e;
1356
- while (t && t.oe & REACTIVE_ZOMBIE) t = t._parent;
1357
- let i = t && (t.we || (t.C & CONFIG_HELD_CHILDREN ? activeTransition : null));
1358
- // A LANE frame's member (#3662) is displayed until its owner's run
1359
- // applies, and the lane's transaction is what applies it (its completion
1360
- // runs the lane's queue): moot for that verdict, live for every other.
1361
- if (!i && t && t.C & CONFIG_LANE_FRAME && t.o?.me) i = findLane(t.o.me).we;
1362
- if (!i || (i = currentTransition(i)).Rn === true || i === n) return false;
1363
- }
1364
- // Fallback-caught async holds nothing. A collecting loading boundary
1365
- // consumes the notification, so a reader under a fallback never registers —
1366
- // but a reader registered while its boundary showed content stays
1367
- // registered when the boundary's `on` later changes and it flips to the
1368
- // fallback. The reader is behind the fallback now; if nothing outside the
1369
- // boundary consumes the flight, the hold is over (A33, ruled 2026-09-12, #3375).
1370
- for (let t = e.T; t; t = t._parent) if (t.te & STATUS_PENDING && !t.q) return false;
1371
- // "Still derives from the source" is a question about THIS pass's reads:
1372
- // the deps up to `_depsTail`. Past it lie the committed frame's — kept
1373
- // linked by A30 until the commit trims them (a staged pass, an errored
1374
- // one). Reading them here made a reporter whose pass had stopped reading
1375
- // the source (a gate closed in the same flush as the write) look live, and
1376
- // the hold it kept was the commit that would have trimmed the dep that
1377
- // kept it (spec O3, same-flush form; fuzzer #3446 P1 cases 21/79). A
1378
- // trimmed list ends at `_depsTail`, so the bound is free there; a pass
1379
- // that read nothing has a null tail and derives from nothing.
1380
- // The registration is trusted: a pending mark rides only this pass's links
1381
- // (notifyStatus skips the kept tail), so a registered reporter read the
1382
- // source, or threw on it.
1383
- if (e.o?.ue?.has(t)) return true;
1384
- const r = e.Nn;
1385
- for (let n = r === null ? null : e.Ie; n; n = n === r ? null : n.Ne) {
1386
- let e = n.Oe;
1387
- while (e) {
1388
- // Or through a memo pending on the flight (#3494): a stale reader
1389
- // served a held memo's committed value never turned pending itself, so
1390
- // its only trace of the flight is the memo between them — `copy` of
1391
- // `details`. Judged dead, its transaction released `count=1` beside
1392
- // the `Copy: 0` it displays. `_pendingSources` is transitive, so one
1393
- // hop covers any depth.
1394
- if (e === t || e.De === t || e.o?.ue?.has(t)) return true;
1395
- e = e.o?.kn;
1396
- }
1397
- }
1398
- return !!(e.h & STATUS_PENDING && e.o?._ instanceof NotReadyError && e.o?._.source === t);
1399
- }
1400
-
1401
- /**
1402
- * Does a live reporter of `transition` still observe `source` pending? Dead
1403
- * reporters (disposed, behind a fallback, no longer reading the source) are
1404
- * pruned as they are found, and the source's entry with them. Shared by the
1405
- * settle verdict and the lane's hold check (`waitingTransition`): a live
1406
- * action parks its transaction without a verdict, so this prune is the only
1407
- * one an optimistic lane whose last async reader unmounted mid-action ever
1408
- * gets — without it the lane held on the dead reporter's registration until
1409
- * the flight it no longer observed landed (#3426).
1410
- */ function sourceObserved(e, t, n) {
1411
- const i = e.le.get(t);
1412
- let r = false;
1413
- for (const e of i ?? []) {
1414
- if (reporterBlocksSource(e, t, n)) return true;
1415
- // A zombie the verdict passes over is kept, not pruned (#3463): moot for
1416
- // this verdict, it still holds a lane's reveal while the transaction
1417
- // stays parked on something else.
1418
- if (n && e.oe & REACTIVE_ZOMBIE) r = true; else i.delete(e);
1419
- }
1420
- if (!r) e.le.delete(t);
1421
- return false;
1422
- }
1423
-
1424
- function transitionComplete(e) {
1425
- if (e.Rn) return true;
1426
- if (e.pe.length) {
1427
- return false;
1428
- }
1429
- let t = true;
1430
- for (const n of e.le.keys()) {
1431
- // The source blocks while its OWN flight is up — the self entry in its
1432
- // pending sources (added by notifyStatus's source path, with status;
1433
- // retired by the landing and the supersede sweep, so it implies
1434
- // STATUS_PENDING). `_error.source` is not that test: propagation from an
1435
- // input that went pending later overwrites it with the input (#3375 — a
1436
- // boundary-consumed load re-asked under a held derivation), and the
1437
- // still-flying source read as settled, committing the writes it was
1438
- // asked with ahead of its answer.
1439
- // Not the self entry alone: a source whose own flight an upstream re-ask
1440
- // superseded is still pending — on that re-ask (#3462). Its reader cannot
1441
- // render until the chain lands, and the landing folds this transaction in
1442
- // (enterWaiting). Judged complete instead, a re-entry between the two (a
1443
- // repeated write to a held signal) committed the held writes beside the
1444
- // reader's stale frame.
1445
- if (sourceObserved(e, n, e) && n.o?.ue?.size) {
1446
- t = false;
1447
- break;
1448
- }
1449
- }
1450
- // Override blockage lives with the engine (absent hook = "no optimistic
1451
- // blockage"); the hook's loops over _optimisticNodes/_optimisticStores are
1452
- // no-ops when the transition holds neither, so no pre-check is needed.
1453
- if (t && GlobalQueue.ai?.(e)) t = false;
1454
- t && (e.Rn = true);
1455
- return t;
1456
- }
1457
-
1458
- /** A fresh, unentered transaction (#3146): the optimistic store's truth
1459
- * flight DECLARES an owned transaction instead of relying on whatever the
1460
- * ambient adoption machinery stamped on its firewall. Activate it with
1461
- * initTransition; it is a plain batch until then. */ function createTransition() {
1462
- return createBatch();
1463
- }
1464
-
1465
- function currentTransition(e) {
1466
- while (e.Rn && typeof e.Rn === "object") e = e.Rn;
1467
- return e;
1468
- }
1469
-
1470
- /**
1471
- * The live transition blocked on `source` — the one whose render reader
1472
- * observed it pending (INV-3 records the observation in whichever transaction
1473
- * was active when the reader was notified). The observation is a fact about
1474
- * the node, so a hold check must not assume it was recorded in the transaction
1475
- * it happens to hold — lanes merge across transactions (#2912), and a merged
1476
- * root's transaction knows nothing of the async its members' transactions
1477
- * observed (#3335). Null when nobody is waiting — a registration whose every
1478
- * reporter has since died is nobody (#3426).
1479
- */ function waitingTransition(e) {
1480
- for (const t of transitions) if (sourceObserved(t, e)) return t;
1481
- return null;
1482
- }
1483
-
1484
- /** A landing enters EVERY parked transaction still waiting on `source`, folding
1485
- * them into the active one (A15: each reveal that discovered the flight
1486
- * completes at its landing). The fold used to happen as the waiters' stamped
1487
- * readers recomputed under the landing — recompute re-entering an effect's
1488
- * stamp — which also folded in writes those readers merely shared a hole
1489
- * with (#3407); effects no longer re-enter, so the landing folds explicitly.
1490
- * Live iteration is safe: a merge deletes the outgoing (active) entry and
1491
- * re-adds the visited one. */ function enterWaiting(e) {
1492
- for (const t of transitions) if (sourceObserved(t, e)) globalQueue.initTransition(t);
1493
- }
1494
-
1495
- function runInTransition(e, t) {
1496
- const n = activeTransition;
1497
- try {
1498
- activeTransition = currentTransition(e);
1499
- return t();
1500
- } finally {
1501
- activeTransition = n;
1502
- }
1503
- }
1504
-
1505
- /** Run `fn` with `transition` as BOTH the ambient transaction and the
1506
- * registration batch, restoring both after. runInTransition alone is not
1507
- * enough for code that WRITES on behalf of a transaction from inside someone
1508
- * else's window (optimistic replay re-arming a still-open action's edits
1509
- * during a landing commit, #3123): registrations route through the queue's
1510
- * batch pointer, and a bare activeTransition swap leaves them in the ambient
1511
- * batch — a plain batch "completes" at the next flush and reverts optimistic
1512
- * registrations that were supposed to live with the transaction.
1513
- * initTransition is the wrong tool here: it MERGES the currently ambient
1514
- * transaction into the target, entangling whatever the interrupted window
1515
- * belonged to. */ function runAsTransitionBatch(e, t) {
1516
- const n = activeTransition;
1517
- const i = globalQueue.m;
1518
- try {
1519
- activeTransition = currentTransition(e);
1520
- currentBatch = globalQueue.m = activeTransition;
1521
- return t();
1522
- } finally {
1523
- activeTransition = n;
1524
- currentBatch = globalQueue.m = i;
1525
- }
875
+ function runQueue(e, n) {
876
+ for (let t = 0; t < e.length; t++) e[t](n);
1526
877
  }
1527
878
 
1528
- export { GlobalQueue, Queue, ROOT_ERROR_HOOK, actionStepDepth, activeAffectsMarks, activeLanes, activeTransition, armReaskClear, assignOrMergeLane, batchJoins, bumpNotifyEpoch, clock, createTransition, currentTransition, deferSlotRelease, dirtyQueue, enforceLoadingBoundary, entangleConfirmingTransitions, enterActionStep, enterWaiting, exitActionStep, finalizePureQueue, findLane, flush, globalQueue, haltReactivity, hasActiveOverride, heldTrims, insertSubs, notifyEpoch, origin, pendingRearms, projectionWriteActive, queuePendingNode, queueRearm, reaskArmed, reporterBlocksSource, resetErrorHalt, runAsTransitionBatch, runInTransition, schedule, scheduleWithheld, setOrigin, setProjectionWriteActive, setStoreCommitHook, shiftAffectsMarks, sourceObserved, storeCommitHook, transitions, waitingTransition, wakeParked, wokenTransitions, zombieQueue };
879
+ export { GlobalQueue, MAINLINE_QUESTION, ROOT_ERROR_HOOK, actionDepth, blocked, bumpNotifyEpoch, clock, commitPendingNode, deferZombie, dirtyQueue, enforceLoadingBoundary, enterAction, exitAction, flush, flushTransaction, globalQueue, haltReactivity, heldTrim, holdNode, insertSubs, joinFuture, joinPassTx, laneDirty, list, liveTx, merge, newTransaction, nextQuestion, notifyEpoch, ownFlights, passLane, passTx, question, queuePendingNode, releaseQueues, reruns, resetErrorHalt, resolveTx, sameLane, schedule, setPassLane, setQuestion, stagedReaders, staleReader, txOf };