@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,674 +1,367 @@
1
- import { NOT_PENDING, REACTIVE_DISPOSED, REACTIVE_DIRTY, REACTIVE_CHECK, unwrapOverride, REACTIVE_RECOMPUTING_DEPS, STATUS_PENDING, STATUS_UNINITIALIZED, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_MANUAL_WRITE, CONFIG_OVERRIDE_SUPERSEDED, CONFIG_HAS_COMPANIONS, CONFIG_ADOPTED_UNFLUSHED, CONFIG_CHILD_COMPANIONS, STATUS_ERROR } from "./constants.js";
1
+ import { CONFIG_VERDICT, REACTIVE_PROBED, CONFIG_INPUTS_PUBLISHED, CONFIG_HELD, REACTIVE_JOINED, REACTIVE_STAGED_READ, REACTIVE_LANE_READ, CONFIG_CHILDREN_FORBIDDEN, CONFIG_OVERRIDE, STATUS_PENDING, CONFIG_GUESS, NOT_PENDING, STATUS_UNINITIALIZED, EFFECT_RENDER, STATUS_ERROR, REACTIVE_PROBE_UNANSWERED } from "./constants.js";
2
2
 
3
- import { setSignal, visibleOverride, unflushedValue, context, markLateLinker, read, stale, hasActiveOverride, prepareComputed, tracking, ext, setLatestReadActive, setContextInternal, optimisticComputed, unflushed, unflushedCompanions, setPendingCheckActive, latestReadActive, optimisticSignal, pendingCheckActive } from "./core.js";
3
+ import { context, tracking, setVerdict, bornInto, verdict, pullComputed, joinPass, markLateLinker, spectating, unflushedValue } from "./core.js";
4
+
5
+ import "./dev.js";
4
6
 
5
7
  import { NotReadyError } from "./error.js";
6
8
 
7
9
  import { link } from "./graph.js";
8
10
 
9
- import { dispose } from "./owner.js";
10
-
11
- import { insertIntoHeap, queueFor, enqueueSub } from "./heap.js";
12
-
13
- import "./invariants.js";
14
-
15
- import { readsHeldCommitted, assignOrMergeLane } from "./lanes.js";
16
-
17
- import { installOptimisticEngine } from "./optimistic.js";
18
-
19
- import { GlobalQueue, insertSubs, schedule, activeTransition, currentTransition, runAsTransitionBatch, activeAffectsMarks } from "./scheduler.js";
20
-
21
- /**
22
- * The isPending()/latest() verdict layer, moved out of core.ts. Importing this
23
- * module installs the companion-maintenance hooks on GlobalQueue; apps that
24
- * never import isPending/latest never pay for any of it.
25
- */
26
- // Companions (pending signals / latest shadows) are optimistic nodes: their
27
- // writes go through the optimistic write path and their reversion rides the
28
- // same lanes, so the verdict layer brings the engine with it.
29
- installOptimisticEngine();
30
-
31
- let pendingProbe = null;
32
-
33
- /**
34
- * Probes whose verdict was suppressed by the fresh-read pairing rule while
35
- * the held write's fate was still undecided (see recordFreshRead /
36
- * wakeSuppressedProbes): held node → the wrapper computeds that probed it.
37
- * Entries die with the hold — the commit/revert snap clears them.
38
- */ const suppressedProbes = new Map;
39
-
40
- /**
41
- * Get or create the pending signal for a node (lazy).
42
- * Used by isPending() to track pending state reactively.
43
- */
44
- /** #3038: register a companion-carrying firewall child on its firewall's
45
- * companion set and arm the post-recompute snap (CONFIG_CHILD_COMPANIONS is
46
- * the one-load gate at the call sites). The snap then iterates exactly the
47
- * children someone asked verdicts of — O(companions) — never the full
48
- * `_child` chain, which carries one node per materialized leaf (the
49
- * O(all-leaves-ever-read)-per-update pathology). Entries live as long as the
50
- * store addresses the leaf — the unobserved sweep's `unlinkFirewallChild`
51
- * drops them (#3503); a store with no leaf-level isPending()/latest() reads
52
- * never allocates the set or pays the walk. */ function markFirewallChildCompanions(e) {
53
- const t = e.Ne;
54
- if (!t) return;
55
- t.C |= CONFIG_CHILD_COMPANIONS;
56
- (ext(t).xt ??= new Set).add(e);
57
- }
58
-
59
- function getPendingSignal(e) {
60
- let t = e.o?.xe;
61
- if (!t) {
62
- // Start false, write true if pending - ensures reversion returns to false
63
- t = optimisticSignal(false, {
64
- ownedWrite: true
65
- });
66
- ext(e).xe = t;
67
- e.C |= CONFIG_HAS_COMPANIONS;
68
- markFirewallChildCompanions(e);
69
- ext(t).$n = e;
70
- if (computePendingState(e)) backfillCompanion(e, t, true);
71
- joinUnflushedResync(e);
72
- }
73
- return t;
74
- }
75
-
76
- /**
77
- * A lazily created companion's first write mirrors state the owner already
78
- * carries — a held write, a pending verdict. The companion is created wherever
79
- * the first latest()/isPending() read happens to run, but the write belongs
80
- * to whatever HOLDS that state: written in the ambient window, the override
81
- * would register in the ambient batch and revert when that flush's round ends
82
- * — the shadow re-derived from the committed view, the verdict flipped false —
83
- * while the owner's hold was still on (#3336: A and B differing only in
84
- * whether a companion existed before the hold). A companion created lazily
85
- * answers as if it had always existed: its backfill is registered with the
86
- * owner's transaction and lives and reverts with it. A hold with no
87
- * transaction (a pending async, a same-flush staged write) is ambient and the
88
- * write stays ambient. (A28 (3), lifted from #3337.)
89
- */ function backfillCompanion(e, t, n) {
90
- const i = e.me;
91
- if (i) runAsTransitionBatch(i, () => setSignal(t, n)); else setSignal(t, n);
92
- }
93
-
94
- /** A28: a companion created while its source carries an UNFLUSHED write joins
95
- * the flush-start re-sync like a companion that existed at the write —
96
- * syncCompanions only reaches companions that exist at write time. Without
97
- * this the flush brings it current by a plain recompute instead of the
98
- * optimistic write: its readers are then staged under whatever transaction
99
- * the round entered (a memo over latest() of a held source was held with the
100
- * source, and its untracked reads answered the previous value until the hold
101
- * committed) rather than direct-committed as the optimistic view they are. */ function joinUnflushedResync(e) {
102
- if (unflushed(e)) unflushedCompanions.push(e);
11
+ import { verdictLane, laneValueOf, display } from "./lanes.js";
12
+
13
+ import { GlobalQueue, txOf, globalQueue, joinFuture, queuePendingNode, staleReader, passLane, setPassLane, flushTransaction, stagedReaders } from "./scheduler.js";
14
+
15
+ // Verdicts (plan sec. 28 replay, 2026-10-02; maintainer, 2026-10-01: verdicts are
16
+ // optimistic state the system supplies). `isPending(fn)` asks whether what
17
+ // `fn` reads is final; `latest(fn)` reads the proposal — the staged write or
18
+ // landing, the lane's value — instead of the screen. A verdict is a guess
19
+ // ("pending, until this lands"; "the proposal is the value") and its readers
20
+ // break out of the hold the way a lane's do: work of the holder's verdict
21
+ // lane (lanes.ts), shown now, re-derived at the landing, ending with it. A
22
+ // verdict reader is a frame reader: its plain reads of held nodes see the
23
+ // screen (core.ts `frameRead`), so `[isPending(x), x()]` never pairs the
24
+ // fresh value with pending (A10).
25
+ /** A verdict reader whose answer this flush could not give yet: the node it
26
+ * asked about is staged and not held — whether it will be is the seam's
27
+ * verdict. It read the staging as the screen: if the flush parks it runs
28
+ * again next round, as lane work that did the same does (`stagedReaders`,
29
+ * lanes.ts — entering the verdict lane then voids what it staged); if it
30
+ * commits, the answer stood. */
31
+ function watchVerdict(e, t = false) {
32
+ if (t) e.R |= REACTIVE_PROBE_UNANSWERED;
33
+ stagedReaders.push(e);
103
34
  }
104
35
 
105
- /** The staged value the verdict channels answer for (A28): while a node
106
- * carries an unflushed write its `_pendingValue` is not yet part of any
107
- * flushed world, so the channels answer for the value the last flush left
108
- * staged (a held node's stash) or for nothing (NOT_PENDING). */ function flushedStaged(e) {
109
- if (!unflushed(e)) return e._e;
110
- // Ambient, or adopted before any flush (CONFIG_ADOPTED_UNFLUSHED): nothing
111
- // a flush carried is staged for it. A held rewrite: the stash.
112
- return e.me === null || e.C & CONFIG_ADOPTED_UNFLUSHED ? NOT_PENDING : e.o.Ut;
36
+ /** The reading pass is the holder's verdict lane's — unless already lane
37
+ * work, or already the transaction's (it derives from the future — a held
38
+ * node's, or the staging of a write this flush may hold — and the verdict
39
+ * does not rescue it: its result shows at the landing, re-derived). A lane
40
+ * read, for `recompute`'s tail (REACTIVE_LANE_READ). */ function route(e, t) {
41
+ if (passLane === null && !(e.R & (REACTIVE_JOINED | REACTIVE_STAGED_READ))) setPassLane(verdictLane(t));
42
+ e.R |= REACTIVE_LANE_READ;
113
43
  }
114
44
 
115
- function collectPendingSources(e) {
116
- if (!pendingProbe) return;
117
- pendingProbe.sources.add(e);
118
- const t = e.Ne || e;
119
- if (t !== e) pendingProbe.sources.add(t);
120
- }
121
-
122
- /**
123
- * Adds a node to the active isPending() probe without reading it. The store's
124
- * untracked-probe fallback (`witnessAffectsMark`) reaches this through
125
- * `GlobalQueue._witnessAffects` — its callers guard on `pendingCheckActive`,
126
- * which only flips inside `isPending()`, so the hook is always installed by
127
- * the time it can fire.
128
- */ function witnessAffects(e) {
129
- pendingProbe?.sources.add(e);
130
- }
131
-
132
- /**
133
- * The affects() coverage walk — the read half of the dedicated mark channel.
134
- * A node is covered by a live mark iff it carries one (`_affectsCount`) or
135
- * derives, through its CURRENT deps (hopping store firewalls), from a node
136
- * that does. Pull-based coverage means graph rewires, mid-window recomputes,
137
- * and probe-triggered recomputes can never strand or strip a mark — there is
138
- * nothing stored downstream to corrupt. Probe-created links
139
- * (`_pendingObserver`) are skipped so an `isPending` wrapper memo never
140
- * inherits the coverage it reports on.
141
- */ function markWalk(e, t) {
142
- if (e.o?.t) return true;
143
- // A real error outranks an inherited mark (A16/A24c): an errored node
144
- // answers probes with its error, not a coverage verdict, and coverage does
145
- // not flow through it — matching the rails' behavior, where propagation
146
- // stopped at errored nodes. A DIRECT mark on an errored node still reads
147
- // pending (the count check above), also matching.
148
- if (e.S & STATUS_ERROR) return false;
149
- if (t.has(e)) return false;
150
- t.add(e);
151
- const n = e.Ne;
152
- if (n && markWalk(n, t)) return true;
153
- // Mid-recompute (the clearStatus companion poke runs before
154
- // trimStaleDeps), only the validated prefix [_deps.._depsTail] is this
155
- // pass's dependency set — walking past it would read dropped deps and
156
- // latch a stale verdict on the companion.
157
- const i = e;
158
- const r = i.oe & REACTIVE_RECOMPUTING_DEPS ? i.Je : undefined;
159
- if (r !== null) {
160
- for (let e = i.Ee ?? null; e !== null; e = e.Ie) {
161
- if (!e.Be && markWalk(e.De, t)) return true;
162
- if (e === r) break;
163
- }
45
+ /** A verdict reader served a flight's committed value (the A31 frame-reader
46
+ * case) still observed the flight — A15's stale reader "derives from the
47
+ * flight all the same": the frame holds (the flush's transaction opens if
48
+ * none has, as the throw would have had it), the reader is that
49
+ * transaction's verdict lane's (shown now, with the flight's verdict), and
50
+ * re-derives at the landing (REACTIVE_FRAME_READ — which is also what makes
51
+ * it a blocker of the hold, `blockedBy`; a probe alone is neither). The
52
+ * hold is on the flight: a reader served committed never goes pending, so
53
+ * the transaction waits on it only through the flight's node — one that
54
+ * went pending this flush is queued for the seam to hold; one in the air
55
+ * from before, held by nobody (a lane's derivation whose lane dissolved at
56
+ * its parent's landing with the flight still up; a flight nothing displayed,
57
+ * #3305), is registered by this, its first observer (A15, #3458): queued for
58
+ * the seam as if it had gone pending here, so the transaction has an entry
59
+ * for what it now shows a reader of. Held by nothing, it landed at this very
60
+ * seam and its `_reruns` re-derived the reader into the same observation,
61
+ * every flush (fuzzer F12). True when the reader is served the committed
62
+ * value — not over a flight committed beneath inputs already on screen (A15
63
+ * reveal corollary, #3305): that value would tear against them, and the
64
+ * reveal observes the flight. Nor for a reader born into the flight's
65
+ * transaction (`bornInto`, #4010): it has no screen — it waits on the flight
66
+ * with the rest of its frame. */ function observeFlight(e, t) {
67
+ if (t.i & CONFIG_INPUTS_PUBLISHED || t.i & CONFIG_HELD && bornInto(e, txOf(t))) return false;
68
+ if (globalQueue.X) {
69
+ joinFuture(null);
70
+ if (!(t.i & CONFIG_HELD)) queuePendingNode(t);
71
+ verdictRead(e, flushTransaction, true);
164
72
  }
165
- return false;
73
+ return true;
166
74
  }
167
75
 
168
- function quietPending(e) {
169
- if (e.o?.ue) {
170
- for (const t of e.o.ue) if (!t.o?.ve) return false;
171
- return true;
172
- }
173
- return e.o?.ve ?? false;
174
- }
175
-
176
- // NOTE: a loadingValue node's open loading window (_loading) is verdict-quiet
177
- // on purpose: commit #0 answers the question by declaration, so the window
178
- // reads NOT pending — first-load affordances live in the value channel
179
- // (null / skeleton provenance the author encoded), and isPending stays what
180
- // it always was: refetch truth for an answered question. This keeps the
181
- // verdict fully correlated with transition-class machinery and keeps server
182
- // (always false) and client hydration trivially consistent.
183
- function newQuestionInFlight(e) {
184
- return !!(e.S & STATUS_PENDING) && !(e.S & STATUS_UNINITIALIZED) && !quietPending(e);
76
+ /** Verdict windows. Inside `latest(fn)` a read of a held node serves the
77
+ * proposal (above the async, the held write; below it, the committed value
78
+ * — nothing newer exists) instead of joining or throwing; inside
79
+ * `isPending(fn)` a read records whether what it saw is final. Either way
80
+ * the reader breaks out: work of the holder's verdict lane, shown now and
81
+ * re-derived at the landing. */ let latestActive = false;
82
+
83
+ let probing = false;
84
+
85
+ let probeFound = false;
86
+
87
+ /** A verdict read of a node a transaction (or lane) holds: the reading pass
88
+ * is the holder's verdict lane's and re-derives at the holder's landing
89
+ * (`_reruns`, as a stale reader). `isPending` flips at the landing with no
90
+ * value notifying anyone (a held write's commit; a held landing's): its
91
+ * reader re-derives there. `latest` shows the proposal that lands — a
92
+ * rewrite or a landing reaches its reader through the link it holds — so a
93
+ * re-run would only repeat the frame. */ function verdictRead(e, t, n) {
94
+ route(e, t);
95
+ if (n) staleReader(e, t); else if (probing && t.an[t.an.length - 1] !== e) t.an.push(e);
185
96
  }
186
97
 
187
- function computePendingState(e) {
188
- const t = e;
189
- if (t.oe & REACTIVE_DISPOSED) return false;
190
- // Mark coverage is transitive by dep-graph reachability: a latest() shadow
191
- // reaches its owner (and a store leaf its firewall) through its own deps,
192
- // so the one walk covers direct marks, derivation, and companion chains.
193
- // Gated: apps with no live mark pay one integer compare.
194
- if (activeAffectsMarks !== 0 && markWalk(e, new Set)) return true;
195
- const n = e.Ne;
196
- if (e.o?.$n) {
197
- const t = e.o?.$n;
198
- const n = t.Ne || t;
199
- return newQuestionInFlight(n);
200
- }
201
- const i = flushedStaged(e);
202
- if (n && i !== NOT_PENDING && !hasActiveOverride(e)) {
203
- return !!(n.oe & REACTIVE_MANUAL_WRITE) || !n.o?.pe && !(n.S & STATUS_PENDING) || !!(n.S & STATUS_PENDING) && quietPending(n);
98
+ /** A verdict read of a node staged or in flight but not held yet: whether it
99
+ * will be is the seam's.
100
+ *
101
+ * `isPending` asks exactly that — final unless the flush parks — so its
102
+ * reader cannot be answered before the seam: a flush that has joined a
103
+ * transaction parks, and the node with it — the reader is that
104
+ * transaction's verdict lane's now; before anything joined, the reader
105
+ * watches the seam and runs again if the flush parks (a plain sync write
106
+ * never glitches it true).
107
+ *
108
+ * `latest` has its answer either way — the proposal — and only the reader's
109
+ * membership was open, so it is settled now: the frame gets its transaction
110
+ * (`joinFuture(null)`; nothing holds it at the seam and it lands there, the
111
+ * lane dissolving into it — a plain commit) and the reader is its verdict
112
+ * lane's. One pass: a provisional pass re-derived at the seam ran everything
113
+ * downstream of it twice (#3540 web). A pass that also read the frame's own
114
+ * stagings plainly (REACTIVE_STAGED_READ) stays the frame's (`route` does
115
+ * not move it); a verdict reader's plain read AFTER the window sees the
116
+ * screen (`read`: `stagedScreen`). */ function provisionalVerdict(e, t) {
117
+ if (!probing) {
118
+ joinFuture(null);
119
+ verdictRead(e, flushTransaction);
120
+ return;
204
121
  }
205
- // `!comp._loading`: a hold created while the loading window is still open is
206
- // the window's own landing in flight to its commit — verdict-quiet like the
207
- // rest of the window (the UNINITIALIZED check suppresses exactly this frame
208
- // for windowless first loads; born-committed nodes need their own gate, #2990).
209
- // A18 (d) for a body-end supersession (#3427): the truth at hand is the
210
- // COMMITTED value — nothing staged — yet the display still shows the
211
- // override; pending iff they differ, as for a staged arrival below.
212
- if (e.C & CONFIG_OVERRIDE_SUPERSEDED && e._e === NOT_PENDING && visibleOverride(e)) return !e.Fe || !e.Fe(e.ce, unwrapOverride(e.o?.we));
213
- // A28 (2): an unflushed write is not yet observable — the verdict answers
214
- // for the flushed staged value.
215
- if (i !== NOT_PENDING && !t.ye) {
216
- // A18 (d): under a displayed override the observable value is the
217
- // override, so the verdict is "the arrived truth differs from it" —
218
- // even before the node's first commit. The UNINITIALIZED suppression
219
- // below is A19 exception (1), "no observable value exists to be
220
- // non-final"; an override is one (a node whose first landing was held
221
- // by a reveal it never got to commit, then superseded under its
222
- // override, read false here).
223
- if (visibleOverride(e)) return !e.Fe || !e.Fe(i, unwrapOverride(e.o?.we));
224
- // A quiet re-ask's held landing still answers the same question: the
225
- // classification survives the landing (asyncWrite) and dies with the
226
- // commit (commitPendingNode) — verdict-quiet through the reveal, like
227
- // the loading window above (#3178).
228
- // A staged value equal to the committed one is no proposal (A34, #3494): the
229
- // observable value IS final (A19). The coalesced `setShow(false);
230
- // setShow(true)` read pending through the flush that carried it — and,
231
- // stamped into a hold that flush opened, until the hold settled.
232
- if (!(t.S & STATUS_UNINITIALIZED) && !t.o?.ve && (!e.Fe || !e.Fe(e.ce, i))) return true;
122
+ if (flushTransaction !== null) {
123
+ if (t) probeFound = true;
124
+ verdictRead(e, flushTransaction);
125
+ if (e.R & REACTIVE_STAGED_READ) watchVerdict(e);
126
+ return;
233
127
  }
234
- return newQuestionInFlight(t);
128
+ // Before anything joined, a staging that would not be final held has no
129
+ // answer yet: whatever the seam decides, the pass runs again — a later
130
+ // plain read of the same pass may route it into the verdict lane
131
+ // (`observeFlight`), which the seam must not take as this probe's answer.
132
+ watchVerdict(e, t);
235
133
  }
236
134
 
237
- function syncCompanions(e, t) {
238
- if (e.o?.xe) updatePendingSignal(e);
239
- if (e.o?.Me) setSignal(e.o?.Me, t);
240
- }
241
-
242
- function updatePendingSignal(e) {
243
- if (e.o?.xe) {
244
- setSignal(e.o?.xe, computePendingState(e));
135
+ /** A19 exc. 2 / A24: a flight that re-asks the question already answered
136
+ * (a `refresh()`, a poll) is quiet; a node pending through others is quiet
137
+ * when every flight it waits on is. */ function quietPending(e) {
138
+ const t = e.m;
139
+ if (t === null) return false;
140
+ if (t.Se !== undefined) {
141
+ for (const e of t.Se) if (!e.m?.It) return false;
142
+ return true;
245
143
  }
246
- if (e.o?.Me) updatePendingSignal(e.o?.Me);
144
+ return t.It;
247
145
  }
248
146
 
249
- function updateChildCompanions(e) {
250
- const t = e.o?.xt;
251
- if (t === undefined) return;
252
- for (const e of t) updatePendingSignal(e);
147
+ /** `isPending`'s answer for a held node (A19), relative to what the reader
148
+ * sees — a verdict reader is a frame reader and sees the committed value: a
149
+ * flight still up (cause ii) is not final unless quiet; a held write or
150
+ * landing (staged — cause i/iii) is not final unless it answers the question
151
+ * already shown (a quiet re-ask, through its reveal), is the node's first
152
+ * landing (commit #0: loading, not pending), or the probe sits inside a
153
+ * `latest` window, whose view of the node IS the proposal (A10: a reader
154
+ * that observed the fresh value is not told it is pending; inside `latest`,
155
+ * `isPending` asks whether the latest view is final). A member of a held
156
+ * frame with neither is final. A24: pending iff a value change is in
157
+ * flight — a staging equal to what is displayed changes nothing when it
158
+ * lands: final now (maintainer, 2026-10-02). */ function heldNotFinal(e) {
159
+ if ((e.S & STATUS_PENDING) !== 0 && !quietPending(e)) return true;
160
+ return e.Oe !== NOT_PENDING && !latestActive && !e.m.It && !(e.S & STATUS_UNINITIALIZED) && (!e.en || !e.en(e.Ae, e.Oe));
253
161
  }
254
162
 
255
- /**
256
- * Re-derive every verdict companion downstream of `el` (subs + firewall
257
- * children, dedup'd). The affects() channel's poke walk: registration and
258
- * re-ask flips use the live write path (companion setSignal — its own lane
259
- * lets the wake escape an incomplete transition's effect stash, #2887);
260
- * mark release passes `snap` because it runs inside queue finalization,
261
- * where companion writes must land committed (a setSignal there would open
262
- * a fresh override window that nothing settles).
263
- */ function repollDownstreamVerdicts(e, t = false) {
264
- const n = t ? snapCompanionsToState : updatePendingSignal;
265
- const i = new Set;
266
- const visit = e => {
267
- if (i.has(e)) return;
268
- i.add(e);
269
- if (e.o?.xe || e.o?.Me) n(e);
270
- for (let t = e.u; t !== null; t = t.Pe) visit(t.Ae);
271
- for (let t = e.o?.i ?? null; t !== null; t = t.ge) {
272
- visit(t);
163
+ /** `isPending`'s answer for an unheld node whose flight is up (cause ii). */ function pendingVerdict(e, t) {
164
+ if (t.S & STATUS_UNINITIALIZED) return;
165
+ if (probing && !quietPending(t)) probeFound = true;
166
+ if (e !== null && tracking) {
167
+ if (t.i & CONFIG_HELD) verdictRead(e, txOf(t)); else if (globalQueue.X) provisionalVerdict(e, false);
168
+ // decided above
273
169
  }
274
- };
275
- visit(e);
276
170
  }
277
171
 
278
- /**
279
- * The correction half of the provisional fresh-read suppression (see
280
- * collectPending): fired from the sanctioned async-registration site
281
- * (GlobalQueue.notify) when a transaction gains an in-flight async blocker.
282
- * Every probe that returned "not pending" purely because it read a held
283
- * value belonging to that transaction re-runs — its re-probe now sees the
284
- * live blocker through heldAwaitingAsync and lands the true verdict. The
285
- * wake mirrors a companion write's own notification (optimistic-dirty on the
286
- * companion's lane) so the corrected verdict commits and flushes immediately
287
- * instead of being held with the transaction it reports on.
288
- */ function wakeSuppressedProbes(e) {
289
- if (suppressedProbes.size === 0) return;
290
- let t = false;
291
- for (const [n, i] of suppressedProbes) {
292
- const r = n.me;
293
- const s = r ? currentTransition(r) : null;
294
- if (!s) {
295
- suppressedProbes.delete(n);
296
- continue;
297
- }
298
- if (s !== e) continue;
299
- suppressedProbes.delete(n);
300
- const o = n.o?.xe?.o?.Ce;
301
- for (const e of i) {
302
- if (e.oe & REACTIVE_DISPOSED) continue;
303
- e.oe |= REACTIVE_OPTIMISTIC_DIRTY;
304
- if (o) assignOrMergeLane(e, o); else if (e.o !== null) e.o.Ce = undefined;
305
- enqueueSub(e);
306
- t = true;
307
- }
172
+ /** The pass entering a verdict window is a verdict reader (CONFIG_VERDICT):
173
+ * a dependency going pending re-derives it, the verdict having changed. Not
174
+ * an untracked window: that is a snapshot, like any untracked read (#3946 —
175
+ * a router's link claim probing inside the computation that creates the
176
+ * anchor). A throw still reaches the pass through `untrack`. */ function markVerdictReader(e) {
177
+ let t = context;
178
+ if (t?.Ht) t = t.gt;
179
+ if (t !== null && tracking) {
180
+ t.i |= CONFIG_VERDICT;
181
+ t.R |= REACTIVE_PROBED;
182
+ // Observe tier: which windows this pass entered (1 `isPending`, 2
183
+ // `latest`) — the hold census names the affordance (attribution.ts).
184
+ t.Ze = (t.Ze ?? 0) | e;
308
185
  }
309
- if (t) schedule();
310
186
  }
311
187
 
312
- function snapCompanionsToState(e) {
313
- suppressedProbes.size !== 0 && suppressedProbes.delete(e);
314
- const t = e.o?.xe;
315
- if (t && (t.o?.we === undefined || t.o?.we === NOT_PENDING)) {
316
- const n = computePendingState(e);
317
- if (t.ce !== n || t._e !== NOT_PENDING) {
318
- t.ce = n;
319
- t._e = NOT_PENDING;
320
- insertSubs(t);
321
- schedule();
322
- }
323
- }
324
- const n = e.o?.Me;
325
- if (n && !(n.oe & REACTIVE_DISPOSED)) {
326
- // A leaf whose firewall is disposed (the projection's teardown snaps its
327
- // companion-bearing leaves): the shadow's compute reads through a
328
- // disposed, possibly still-pending projection and would sit
329
- // NotReady/uninitialized forever — never derived, its backfilled override
330
- // dropped at the settle — against a leaf whose committed value differs
331
- // (INV-4 at the next quiescence; spec O5). It dies with its source;
332
- // getLatestValueComputed treats a disposed shadow as absent, so a later
333
- // read recreates it from the committed view.
334
- if (e.Ne?.oe & REACTIVE_DISPOSED) {
335
- dispose(n);
336
- return;
337
- }
338
- if ((n.o?.we === undefined || n.o?.we === NOT_PENDING) && n._e === NOT_PENDING && !Object.is(n.ce, e.ce) && !(n.oe & (REACTIVE_DIRTY | REACTIVE_CHECK))) {
339
- n.oe |= REACTIVE_DIRTY;
340
- insertIntoHeap(n, queueFor(n));
341
- insertSubs(n);
342
- schedule();
343
- }
344
- snapCompanionsToState(n);
345
- }
346
- }
347
-
348
- function getLatestValueComputed(e) {
349
- let t = e.o?.Me;
350
- // A shadow disposed while unobserved (its gated reader unmounted at a
351
- // landing) is a corpse: sync writes into it equality-swallow against its
352
- // frozen _value, and a later read revives it via recompute — clearing
353
- // DISPOSED and re-deriving from the committed view, so the banner showed
354
- // the previous transition's target (#3041 follow-up). Treat it as absent;
355
- // recreation backfills from the in-flight write below.
356
- if (t && t.oe & REACTIVE_DISPOSED) t = undefined;
357
- if (!t) {
358
- const n = latestReadActive;
359
- setLatestReadActive(false);
360
- const i = pendingCheckActive;
361
- setPendingCheckActive(false);
362
- const r = context;
363
- setContextInternal(null);
364
- // Detach from owner so it isn't disposed with effects
365
- GlobalQueue.Tn = true;
366
- try {
367
- t = optimisticComputed(() => read(e), {
368
- ownedWrite: true
369
- });
370
- } finally {
371
- GlobalQueue.Tn = false;
372
- }
373
- ext(e).Me = t;
374
- e.C |= CONFIG_HAS_COMPANIONS;
375
- markFirewallChildCompanions(e);
376
- ext(t).$n = e;
377
- // Parent-child lane relationship
378
- // Backfill an in-flight write (mirrors getPendingSignal): the companion is
379
- // created lazily, possibly after the write was processed — syncCompanions
380
- // only pushes into companions that already exist, so the first latest()
381
- // read inside a held transition showed the committed value (#3041).
382
- const s = flushedStaged(e);
383
- if (s !== NOT_PENDING && !hasActiveOverride(e)) backfillCompanion(e, t, s);
384
- joinUnflushedResync(e);
385
- setContextInternal(r);
386
- setPendingCheckActive(i);
387
- setLatestReadActive(n);
188
+ /** The windows. `read` dispatches to `verdictValue` while one is open; each
189
+ * restores the dispatch it found — none inside a memo's pass a window
190
+ * pulled (`verdictValue`). */ function latest(e) {
191
+ markVerdictReader(2);
192
+ const t = latestActive, n = verdict;
193
+ latestActive = true;
194
+ setVerdict(verdictValue);
195
+ try {
196
+ return e();
197
+ } finally {
198
+ latestActive = t;
199
+ setVerdict(n);
388
200
  }
389
- return t;
390
201
  }
391
202
 
392
- /** The latest()-mode read path, installed as GlobalQueue._latestRead. */
393
- /** A7: the source has no visible value yet — judged on the OWNER, as read()
394
- * does: a store leaf behind a projection's firewall is a plain signal whose
395
- * `_value` is the seed (A25: a draft, never a value), and read() routes a
396
- * latest() read here before its own firewall/status logic. An override
397
- * displays a value even before the first commit (A17). */ function uninitializedSource(e) {
398
- const t = e.Ne || e;
399
- return !!(t.S & STATUS_UNINITIALIZED) && !hasActiveOverride(e);
400
- }
401
-
402
- function latestRead(e) {
403
- // A leaf of a DISPOSED projection has no flushed world left to mirror: a
404
- // shadow created for it now would read through the dead firewall, sit
405
- // NotReady/uninitialized forever, and no teardown would ever retire it (the
406
- // firewall's already ran — spec O5). Serve the committed value; create
407
- // nothing. (A read of a disposed node freezes at its last commit, #3024.)
408
- if (e.Ne?.oe & REACTIVE_DISPOSED) return e.ce;
409
- const t = getLatestValueComputed(e);
410
- const n = latestReadActive;
411
- setLatestReadActive(false);
412
- const i = visibleOverride(e) ? unwrapOverride(e.o?.we) : e.ce;
413
- // A28: an unflushed write is not the staged value latest() serves. The
414
- // shadow was written at the source's write to mirror it (A8) — consult it
415
- // only once a flush has carried the write.
416
- const r = unflushedValue(e);
417
- if (r !== NOT_PENDING) {
418
- // The reader derived from the flushed world because of an unflushed
419
- // write: it runs again in the flush that carries it (late linker) —
420
- // a pull may have cleared the mark the write's walk set.
421
- if (context !== null && context.oe & REACTIVE_RECOMPUTING_DEPS) markLateLinker(context);
422
- // Link the reader to the shadow so the flush that carries the write
423
- // updates it (the shadow itself already mirrors the write, A8).
424
- try {
425
- read(t);
426
- } catch {
427
- /* the flushed value answers */} finally {
428
- setLatestReadActive(n);
429
- }
430
- // An ambient write: the visible value (override or committed). A rewrite
431
- // of a held node: the staged value the last flush left.
432
- return e.me === null ? i : r;
433
- }
434
- let s;
203
+ function isPending(e) {
204
+ markVerdictReader(1);
205
+ const t = probing;
206
+ const n = probeFound;
207
+ const r = verdict;
208
+ probing = true;
209
+ probeFound = false;
210
+ setVerdict(verdictValue);
435
211
  try {
436
- // No mid-tick pull: writes become visible at flush (A28), so before the
437
- // flush the shadow is exactly as current as the flushed world — the read
438
- // below serves it as is. (#2922's pull, which brought the shadow current
439
- // against the unflushed write, is superseded: `flush()` first to read
440
- // your own write.)
441
- s = read(t);
442
- } catch (t) {
443
- // A NotReady from the shadow of an INITIALIZED source means the shadow
444
- // is mid-flight: serve the visible (committed / override) value. An
445
- // uninitialized source has no visible value — latest() throws in every
446
- // scope rather than fabricate `undefined` for a `T` that excludes it
447
- // (A7; the unowned scope used to return undefined here).
448
- if (t instanceof NotReadyError && !uninitializedSource(e)) return i;
449
- throw t;
212
+ e();
213
+ return probeFound;
214
+ } catch (e) {
215
+ // A pending node is served inside the probe; what still throws is an
216
+ // uninitialized one — loading, not pending (A19 exc. 1) — whose NotReady
217
+ // propagates to boundaries from inside an owner (B5a; a component body
218
+ // suspends on it like any read, #2928) and is swallowed outside one. A
219
+ // verdict never throws anything else: a thunk that errors is simply not
220
+ // pending.
221
+ if (e instanceof NotReadyError && !probeFound && context !== null) throw e;
222
+ return probeFound;
450
223
  } finally {
451
- setLatestReadActive(n);
452
- }
453
- if (t.S & STATUS_PENDING) {
454
- if (uninitializedSource(e)) throw new NotReadyError(e);
455
- return i;
224
+ probing = t;
225
+ probeFound = n;
226
+ setVerdict(r);
456
227
  }
457
- // A render effect off the shadow's HELD lane sees the committed value and
458
- // re-runs at the release (#3460; lanes mirror transitions — see
459
- // readsHeldCommitted). Was: only a reader under ANOTHER lane; a mainline
460
- // reader, mounted or re-run by a sync write mid-hold, showed the
461
- // speculative value beside the lane's deferred readers.
462
- if (stale && context !== null && readsHeldCommitted(t, context)) return e.ce;
463
- // A shadow recomputed by the pull above (not at creation) holds its fresh
464
- // speculative value in _pendingValue; a contextless read() only surfaces
465
- // _value. Overrides stay authoritative (A17), and stale readers keep the
466
- // other transition's committed view, matching read()'s own selection.
467
- if (t._e !== NOT_PENDING && !hasActiveOverride(t) && !(stale && t.me && activeTransition !== t.me)) return t._e;
468
- return s;
469
228
  }
470
229
 
471
- /**
472
- * A latest() shadow that is uninitialized only because it was CREATED during
473
- * an active flight — its parent source already has a committed value, so
474
- * latest() serves that as the visible value and a tracked reader has
475
- * something to pair a verdict with (#3166). Same parent resolution as
476
- * computePendingState. The pending signal companion also carries
477
- * `_parentSource` but is a plain signal (no `_fn`) that never goes pending,
478
- * so the `_fn` check is belt-and-braces for this call site.
479
- */ function latestShadowWithInitializedParent(e) {
480
- if (typeof e.Se !== "function") return false;
481
- const t = e.o?.$n;
482
- if (t === undefined) return false;
483
- const n = t.Ne || t;
484
- return !(n.S & STATUS_UNINITIALIZED);
230
+ /** `latest()` of a held node: the flushed staging — the proposal — and, for
231
+ * a tracked reader of a rewrite not yet flushed, the late-linker re-run in
232
+ * the flush that carries it. */ function heldLatest(e, t) {
233
+ const n = unflushedValue(e);
234
+ if (n === NOT_PENDING) return e.Oe;
235
+ if (t !== null) markLateLinker(t);
236
+ return n;
485
237
  }
486
238
 
487
- /** The isPending()-probe read path, installed as GlobalQueue._pendingCheck. */ function pendingCheckRead(e, t, n, i) {
488
- setPendingCheckActive(false);
489
- if (typeof e.Se === "function") {
490
- GlobalQueue.Tn = true;
491
- try {
492
- prepareComputed(e, true);
493
- } finally {
494
- GlobalQueue.Tn = false;
239
+ /** The verdict windows' read — `latest(fn)` and `isPending(fn)` — in one
240
+ * cold path off `read`'s. It links and pulls like a plain read, then answers
241
+ * for what the reader sees (a verdict reader is a frame reader):
242
+ * - a lane's node: the reader is the holder's verdict lane's (not the
243
+ * lane's — a lane blocked on its own flight would hold the reader's run,
244
+ * the opposite of a verdict's display-ahead). A guess is the value (A17),
245
+ * not final while its own question flies or a truth held beneath it
246
+ * differs (A24, A18 provenance). A derivation: `latest` sees the lane's
247
+ * latest (a landing a blocked lane holds has answered its question — the
248
+ * checkout's shipping text updates independently of the tax in flight),
249
+ * `isPending` the screen; not final while its own flight is up;
250
+ * - a held node: the reader breaks out as the holder's verdict lane's and
251
+ * re-derives at the landing; `latest` serves the proposal (`heldLatest`),
252
+ * `isPending` answers `heldNotFinal`;
253
+ * - a node staged this flush and not yet held: the seam's verdict
254
+ * (`provisionalVerdict`) — or, with a newer question already in flight
255
+ * (#3376, the stale first hop of a chain), its flight's;
256
+ * - a flight (cause ii): `isPending` records it (`pendingVerdict`), `latest`
257
+ * serves the committed value — below the async nothing newer exists; an
258
+ * uninitialized one throws NotReady: loading, not pending (A19 exc. 1);
259
+ * - an errored derivation throws for every reader (#2897). */ function verdictValue(e, t) {
260
+ const n = e;
261
+ const r = t !== null && !(t.i & CONFIG_CHILDREN_FORBIDDEN);
262
+ const i = r && tracking;
263
+ if (t !== null && tracking) {
264
+ link(e, t);
265
+ if (n.ee !== undefined) {
266
+ // A31: the pulled memo's pass is its own, not the probe's — it reads
267
+ // outside the window, and the probe's read below answers for what
268
+ // the pass produced.
269
+ setVerdict(null);
270
+ pullComputed(n, t);
271
+ setVerdict(verdictValue);
495
272
  }
496
273
  }
497
- const r = n.S;
498
- if (t && r & STATUS_PENDING && r & STATUS_UNINITIALIZED &&
499
- // The suspend-throw is for a genuinely-first-load source: the tracked
500
- // reader has nothing to pair a verdict with, so it parks on the source.
501
- // A latest() SHADOW created lazily mid-flight is born uninitialized even
502
- // though its parent has a committed value latest() will serve — throwing
503
- // here (swallowed by latestRead's fallback) dropped the shadow from the
504
- // probe, so a tracked latest(isPending()) probe created during a
505
- // new-question flight cached `false` for that whole flight (#3166).
506
- // Defer to the PARENT's initialization state and fall through to normal
507
- // collection; the plain pending throw downstream still links the reader.
508
- !latestShadowWithInitializedParent(n)) {
509
- if (tracking && e !== t) link(e, t);
510
- setPendingCheckActive(true);
511
- throw n.o?._;
512
- }
513
- collectPendingSources(e);
514
- if (i) collectPendingSources(i);
515
- setPendingCheckActive(true);
516
- }
517
-
518
- /**
519
- * A held node whose transaction still has an async question in flight. The
520
- * probe's fresh-read pairing rule (#2831 — "a reader that sees the fresh
521
- * value must not also be told it is pending") only applies to LANDED answers
522
- * awaiting reveal; while the answer is still computing, the fresh value the
523
- * reader saw is an input, and pending remains the truth for every reader
524
- * (#3028).
525
- */ function heldAwaitingAsync(e) {
526
- const t = e.me;
527
- const n = t ? currentTransition(t) : activeTransition;
528
- if (!n || n.Dt) return false;
529
- // A plain staged write (a signal/store leaf — no _fn) held while an action
530
- // is still running is an INPUT to a computation still in flight (#3078):
531
- // the pairing rule must not suppress the verdict, or a memo recomputing
532
- // mid-action reads the staged value, gets told "not pending", and
533
- // disagrees with a direct isPending() probe for the whole action window.
534
- // A computed's staged value is the opposite case — a LANDED answer
535
- // awaiting reveal — where the pairing rule stands even inside an open
536
- // action (#2831: a reader that saw the new value must not also see
537
- // pending); still-computing answers are covered by the reporter scan.
538
- if (n.tt.length && !e.Se) return true;
539
- // The reporter scan runs for an unstamped node too (#3457): a node staged
540
- // AFTER the transaction opened is pushed straight into the transaction's
541
- // batch (queuePendingNode, once initTransition adopted it) and only gets
542
- // its `_transition` stamp when the flush stashes the hold, but its staged
543
- // value is already the transaction's, and `t` resolved to that very
544
- // transaction above. Gating on the stamp let a memo whose recompute read
545
- // a sync memo's fresh staged value mid-flush pair "not pending" with it
546
- // (A10) while the transaction's async source was still computing, so a
547
- // memo-wrapped isPending() read false where a direct probe read true.
548
- for (const [e, t] of n.ae) {
549
- if (t.size && e.S & STATUS_PENDING && e.o?._?.source === e) return true;
550
- }
551
- return false;
552
- }
553
-
554
- function recordFreshRead(e, t) {
555
- if (pendingProbe !== null && e._e !== NOT_PENDING && t === e._e) {
556
- if (heldAwaitingAsync(e)) return;
557
- pendingProbe.freshReads.add(e);
558
- }
559
- }
560
-
561
- function applyReask(e, t) {
562
- const n = !!(e.S & STATUS_PENDING);
563
- const i = t && !(n && !e.o?.ve);
564
- const r = n && (e.o?.ve ?? false) !== i;
565
- // Allocation-free for the quiet case: false is the extension default.
566
- if (i) ext(e).ve = true; else if (e.o !== null) e.o.ve = false;
567
- return r;
568
- }
569
-
570
- function latest(e) {
571
- const t = latestReadActive;
572
- setLatestReadActive(true);
573
- try {
574
- return e();
575
- } finally {
576
- setLatestReadActive(t);
274
+ // A live `affects()` mark on the node or a dependency (affects.ts): not
275
+ // final by declaration — the value itself is read as below. The verdict is
276
+ // display-ahead like any: in a transaction's flush (the declaring action's
277
+ // body) the reader is its verdict lane's, not a staging the frame parks.
278
+ if (probing && GlobalQueue.N !== undefined && GlobalQueue.N(n)) {
279
+ probeFound = true;
280
+ if (i && flushTransaction !== null) verdictRead(t, flushTransaction);
577
281
  }
578
- }
579
-
580
- function isPending(e) {
581
- const t = pendingCheckActive;
582
- const n = pendingProbe;
583
- setPendingCheckActive(true);
584
- const i = pendingProbe = {
585
- found: false,
586
- sources: new Set,
587
- freshReads: new Set,
588
- suppressed: []
589
- };
590
- const collectPending = () => {
591
- setPendingCheckActive(false);
592
- // Companion reads are mode-neutral plumbing: under an outer latest()
593
- // (isPending inside a latest window — #3104's memo shape) leaving latest
594
- // mode active dispatched these reads through latestRead, which built a
595
- // SHADOW OF THE PENDING SIGNAL itself. The next updatePendingSignal then
596
- // wrote that companion-on-companion from inside a recompute
597
- // (syncCompanions → setSignal on a shadow created without ownedWrite)
598
- // and halted dev with the owned-scope write guard. The creation paths
599
- // (getLatestValueComputed / getPendingSignal) already suspend both
600
- // modes; this read site must too.
601
- const e = latestReadActive;
602
- setLatestReadActive(false);
603
- try {
604
- i.sources.forEach(e => {
605
- if (read(getPendingSignal(e))) {
606
- if (!i.freshReads.has(e)) i.found = true; else i.suppressed.push(e);
607
- }
608
- });
609
- } finally {
610
- setLatestReadActive(e);
611
- setPendingCheckActive(true);
282
+ if (e.i & CONFIG_OVERRIDE) {
283
+ const r = txOf(e);
284
+ if (i) verdictRead(t, r._parent ?? r);
285
+ const u = (n.S & STATUS_PENDING) !== 0 && !quietPending(n);
286
+ if (e.i & CONFIG_GUESS) {
287
+ if (probing && (u || e.Oe !== NOT_PENDING && (!e.en || !e.en(e.m.B, e.Oe)))) probeFound = true;
288
+ return e.m.B;
612
289
  }
613
- // A "not pending" verdict that exists only because this reader saw the
614
- // fresh held value is provisional: if the write turns out NOT to commit
615
- // this flush (a downstream async pends and holds it), the suppression was
616
- // wrong and the wrapper must re-ask (#3028). Remember who to wake — the
617
- // async registration (GlobalQueue.notify) triggers wakeSuppressedProbes.
618
- // A TRACKED probe only (#3648): an `untrack(() => isPending(x))` inside a
619
- // memo asked for a one-shot answer and declined the re-run its tracked
620
- // form would get — enrolling its host anyway marked the memo OPT-dirty on
621
- // the companion's lane and re-ran it (a router `query()` refetched, its
622
- // first flight abandoned mid-air) for a verdict it never depended on.
623
- if (tracking && !i.found && i.suppressed.length && context && typeof context.Se === "function") {
624
- for (const e of i.suppressed) {
625
- let t = suppressedProbes.get(e);
626
- if (!t) suppressedProbes.set(e, t = new Set);
627
- t.add(context);
290
+ if (probing && u) probeFound = true;
291
+ return latestActive && !u ? laneValueOf(e) : display(e);
292
+ }
293
+ const u = (n.S & STATUS_UNINITIALIZED) !== 0;
294
+ if (e.i & CONFIG_HELD) {
295
+ if (!u) {
296
+ const r = txOf(e);
297
+ // A render effect re-run (or mounted) outside the verdict lane's flush
298
+ // while the lane is blocked — its display held on its own derivation
299
+ // in flight — is a stale reader of the lane (#3460, as `laneRead`): the
300
+ // committed value now, re-derived at the reveal. Not inside a probe:
301
+ // `isPending` is answered below either way.
302
+ if (i && !probing && t.u === EFFECT_RENDER && passLane === null && r.rt !== null && r.rt.fn && flushTransaction !== r) {
303
+ staleReader(t, r.rt);
304
+ return e.Ae;
305
+ }
306
+ if (i) verdictRead(t, r);
307
+ if (probing && heldNotFinal(n)) probeFound = true;
308
+ if (latestActive && e.Oe !== NOT_PENDING) {
309
+ // An action body reading another transaction's proposal derives
310
+ // from it: the two settle as one (A15; posture C, 2026-09-15). An
311
+ // untracked read elsewhere has no transaction to entangle.
312
+ if (!i && flushTransaction !== null && !globalQueue.X) joinFuture(txOf(e));
313
+ return heldLatest(e, i ? t : null);
628
314
  }
315
+ return e.Ae;
629
316
  }
630
- };
631
- try {
632
- e();
633
- collectPending();
634
- return i.found;
635
- } catch (e) {
636
- collectPending();
637
- if (e instanceof NotReadyError) {
638
- const t = !!(e.source?.S & STATUS_UNINITIALIZED);
639
- if (i.found && !t) return true;
640
- if (context && t) throw e;
317
+ // Born into the future: its staging is its only value. It has no
318
+ // committed value, so no verdict: `isPending` reads it as its read does —
319
+ // the owner joins the hold (held with it, or caught by a boundary that
320
+ // has not shown content) and answers for the commit, where it is final.
321
+ if (e.Oe !== NOT_PENDING && !(n.S & STATUS_PENDING)) {
322
+ if (probing && r) joinPass(t, e);
323
+ return e.Oe;
641
324
  }
642
- return i.found;
643
- } finally {
644
- setPendingCheckActive(t);
645
- pendingProbe = n;
325
+ } else if (e.Oe !== NOT_PENDING) {
326
+ if (n.S & STATUS_PENDING && !u) pendingVerdict(i ? t : null, n); else if (i && globalQueue.X) provisionalVerdict(t, !latestActive && !u);
327
+ // Outside a flush, the staging is an unflushed write (A28): the probe
328
+ // answers for the flushed world, and watches the seam of the flush that
329
+ // carries the write — a memo of `isPending(x)` created after the write
330
+ // flips if that flush parks (#3078). (The write's own fan-out ran before
331
+ // the reader existed; nothing else would re-derive it.)
332
+ else if (i && probing) watchVerdict(t);
333
+ if (latestActive) {
334
+ // A28: an unflushed write is not what `latest` serves; a tracked
335
+ // reader derived from the flushed world runs again in the carrying
336
+ // flush.
337
+ if (globalQueue.X) return e.Oe;
338
+ if (t !== null) markLateLinker(t);
339
+ }
340
+ return e.Ae;
646
341
  }
342
+ if (n.S & STATUS_PENDING) {
343
+ if (!u) {
344
+ pendingVerdict(i ? t : null, n);
345
+ return e.Ae;
346
+ }
347
+ // As `read()`: an untracked read of a pending node still re-runs its
348
+ // reader when the node settles (a projection's pull reads its firewall
349
+ // untracked, store/store.ts).
350
+ if (t !== null && !tracking && !spectating && e !== t) link(e, t);
351
+ throw n.m?.F;
352
+ }
353
+ if (n.ee !== undefined && n.S & STATUS_ERROR) throw n.m.F;
354
+ return e.Ae;
647
355
  }
648
356
 
649
- // Hook installation (same late-binding pattern as GlobalQueue._update /
650
- // _propagateAffects): core call sites fire these behind the same guards the
651
- // direct calls used, so behavior is identical once this module loads.
652
- GlobalQueue.Le = syncCompanions;
653
-
654
- GlobalQueue.Ue = updatePendingSignal;
655
-
656
- GlobalQueue.qe = updateChildCompanions;
657
-
658
- GlobalQueue.Qn = snapCompanionsToState;
659
-
660
- GlobalQueue.Cn = latestRead;
661
-
662
- GlobalQueue.Rn = pendingCheckRead;
663
-
664
- GlobalQueue.Hn = recordFreshRead;
665
-
666
- GlobalQueue.At = applyReask;
667
-
668
- GlobalQueue.k = repollDownstreamVerdicts;
669
-
670
- GlobalQueue.ri = witnessAffects;
357
+ // Installed at module evaluation — present exactly when something imports
358
+ // `latest` or `isPending`. (`read` dispatches to `verdictValue` through
359
+ // `verdict`, set while a window is open.)
360
+ GlobalQueue.vt = observeFlight;
671
361
 
672
- GlobalQueue.si = wakeSuppressedProbes;
362
+ /** A store read inside an untracked `isPending` probe found a live mark on
363
+ * a record with no node to carry it (store/affects.ts): not final. */ GlobalQueue.Bt = () => {
364
+ if (probing) probeFound = true;
365
+ };
673
366
 
674
367
  export { isPending, latest };