@solidjs/signals 2.0.0-rc.11 → 2.0.0-rc.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dev-shared.js +2056 -3773
- package/dist/dev.attribution.js +159 -91
- package/dist/dev.js +7263 -8384
- package/dist/observe/affects.js +125 -104
- package/dist/observe/boundaries.js +458 -665
- package/dist/observe/core/action.js +63 -66
- package/dist/observe/core/async.js +240 -383
- package/dist/observe/core/attribution-queries.js +1 -1
- package/dist/observe/core/attribution.js +271 -199
- package/dist/observe/core/constants.js +115 -178
- package/dist/observe/core/context.js +3 -3
- package/dist/observe/core/core.js +840 -1428
- package/dist/observe/core/dev.js +41 -18
- package/dist/observe/core/effect.js +75 -86
- package/dist/observe/core/external.js +5 -5
- package/dist/observe/core/graph.js +61 -70
- package/dist/observe/core/heap.js +75 -98
- package/dist/observe/core/invariants.js +1 -34
- package/dist/observe/core/lanes.js +704 -181
- package/dist/observe/core/owner.js +80 -101
- package/dist/observe/core/scheduler.js +708 -1351
- package/dist/observe/core/verdict.js +302 -621
- package/dist/observe/flatten.js +79 -0
- package/dist/observe/index.js +15 -13
- package/dist/observe/map.js +232 -235
- package/dist/observe/reveal.js +212 -0
- package/dist/observe/signals.js +133 -215
- package/dist/observe/store/affects.js +135 -0
- package/dist/observe/store/index.js +29 -24
- package/dist/observe/store/optimistic.js +538 -0
- package/dist/observe/store/projection.js +302 -0
- package/dist/observe/store/reconcile.js +257 -0
- package/dist/observe/store/store.js +1945 -277
- package/dist/observe/store/storePath.js +1 -1
- package/dist/{prod/store/next → observe/store}/target.js +17 -18
- package/dist/observe/store/types.js +73 -0
- package/dist/observe/store/utils.js +1 -13
- package/dist/prod/affects.js +127 -106
- package/dist/prod/boundaries.js +442 -654
- package/dist/prod/core/action.js +71 -74
- package/dist/prod/core/async.js +307 -442
- package/dist/prod/core/constants.js +112 -177
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +852 -1448
- package/dist/prod/core/effect.js +88 -99
- package/dist/prod/core/external.js +5 -5
- package/dist/prod/core/graph.js +61 -70
- package/dist/prod/core/heap.js +75 -98
- package/dist/prod/core/invariants.js +1 -32
- package/dist/prod/core/lanes.js +666 -181
- package/dist/prod/core/owner.js +75 -96
- package/dist/prod/core/scheduler.js +677 -1332
- package/dist/prod/core/verdict.js +297 -621
- package/dist/prod/flatten.js +77 -0
- package/dist/prod/index.js +15 -13
- package/dist/prod/map.js +143 -146
- package/dist/prod/reveal.js +212 -0
- package/dist/prod/signals.js +144 -226
- package/dist/prod/store/affects.js +135 -0
- package/dist/prod/store/index.js +28 -19
- package/dist/prod/store/optimistic.js +533 -0
- package/dist/prod/store/projection.js +295 -0
- package/dist/prod/store/reconcile.js +257 -0
- package/dist/prod/store/store.js +1840 -272
- package/dist/prod/store/storePath.js +1 -1
- package/dist/{observe/store/next → prod/store}/target.js +13 -17
- package/dist/prod/store/types.js +73 -0
- package/dist/prod/store/utils.js +1 -11
- package/dist/types/affects.d.ts +11 -30
- package/dist/types/boundaries.d.ts +62 -176
- package/dist/types/carved.d.ts +1 -0
- package/dist/types/core/action.d.ts +5 -6
- package/dist/types/core/async.d.ts +10 -5
- package/dist/types/core/attribution-hooks.d.ts +48 -1
- package/dist/types/core/attribution.d.ts +29 -9
- package/dist/types/core/constants.d.ts +125 -171
- package/dist/types/core/core.d.ts +104 -264
- package/dist/types/core/dev.d.ts +60 -7
- package/dist/types/core/effect.d.ts +0 -7
- package/dist/types/core/graph.d.ts +7 -1
- package/dist/types/core/heap.d.ts +5 -7
- package/dist/types/core/index.d.ts +6 -6
- package/dist/types/core/invariants.d.ts +0 -29
- package/dist/types/core/lanes.d.ts +65 -87
- package/dist/types/core/owner.d.ts +3 -3
- package/dist/types/core/scheduler.d.ts +249 -331
- package/dist/types/core/types.d.ts +51 -85
- package/dist/types/core/verdict.d.ts +3 -0
- package/dist/types/flatten.d.ts +8 -0
- package/dist/types/index.d.ts +7 -5
- package/dist/types/reveal.d.ts +19 -0
- package/dist/types/signals.d.ts +53 -92
- package/dist/types/store/affects.d.ts +1 -0
- package/dist/types/store/index.d.ts +35 -15
- package/dist/types/store/optimistic.d.ts +23 -0
- package/dist/types/store/projection.d.ts +18 -0
- package/dist/types/store/reconcile.d.ts +3 -0
- package/dist/types/store/store.d.ts +165 -143
- package/dist/types/store/storePath.d.ts +1 -1
- package/dist/types/store/target.d.ts +151 -0
- package/dist/types/store/types.d.ts +84 -0
- package/package.json +1 -1
- package/dist/observe/core/optimistic.js +0 -669
- package/dist/observe/store/next/optimistic.js +0 -717
- package/dist/observe/store/next/projection.js +0 -266
- package/dist/observe/store/next/reconcile.js +0 -375
- package/dist/observe/store/next/store.js +0 -2480
- package/dist/prod/core/optimistic.js +0 -658
- package/dist/prod/store/next/optimistic.js +0 -710
- package/dist/prod/store/next/projection.js +0 -257
- package/dist/prod/store/next/reconcile.js +0 -373
- package/dist/prod/store/next/store.js +0 -2356
- package/dist/types/core/optimistic.d.ts +0 -6
- package/dist/types/store/next/optimistic.d.ts +0 -25
- package/dist/types/store/next/projection.d.ts +0 -27
- package/dist/types/store/next/reconcile.d.ts +0 -10
- package/dist/types/store/next/store.d.ts +0 -143
- package/dist/types/store/next/target.d.ts +0 -196
|
@@ -1,674 +1,355 @@
|
|
|
1
|
-
import {
|
|
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 {
|
|
3
|
+
import { context, setVerdict, verdict, tracking, pullComputed, 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 {
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
*
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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, globalQueue, joinFuture, queuePendingNode, staleReader, passLane, setPassLane, flushTransaction, txOf, 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
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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. */ function observeFlight(e, t) {
|
|
65
|
+
if (t.u & CONFIG_INPUTS_PUBLISHED) return false;
|
|
66
|
+
if (globalQueue.ee) {
|
|
67
|
+
joinFuture(null);
|
|
68
|
+
if (!(t.u & CONFIG_HELD)) queuePendingNode(t);
|
|
69
|
+
verdictRead(e, flushTransaction, true);
|
|
164
70
|
}
|
|
165
|
-
return
|
|
71
|
+
return true;
|
|
166
72
|
}
|
|
167
73
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
74
|
+
/** Verdict windows. Inside `latest(fn)` a read of a held node serves the
|
|
75
|
+
* proposal (above the async, the held write; below it, the committed value
|
|
76
|
+
* — nothing newer exists) instead of joining or throwing; inside
|
|
77
|
+
* `isPending(fn)` a read records whether what it saw is final. Either way
|
|
78
|
+
* the reader breaks out: work of the holder's verdict lane, shown now and
|
|
79
|
+
* re-derived at the landing. */ let latestActive = false;
|
|
80
|
+
|
|
81
|
+
let probing = false;
|
|
82
|
+
|
|
83
|
+
let probeFound = false;
|
|
84
|
+
|
|
85
|
+
/** A verdict read of a node a transaction (or lane) holds: the reading pass
|
|
86
|
+
* is the holder's verdict lane's and re-derives at the holder's landing
|
|
87
|
+
* (`_reruns`, as a stale reader). `isPending` flips at the landing with no
|
|
88
|
+
* value notifying anyone (a held write's commit; a held landing's): its
|
|
89
|
+
* reader re-derives there. `latest` shows the proposal that lands — a
|
|
90
|
+
* rewrite or a landing reaches its reader through the link it holds — so a
|
|
91
|
+
* re-run would only repeat the frame. */ function verdictRead(e, t, n = probing) {
|
|
92
|
+
route(e, t);
|
|
93
|
+
if (n) staleReader(e, t);
|
|
185
94
|
}
|
|
186
95
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
96
|
+
/** A verdict read of a node staged or in flight but not held yet: whether it
|
|
97
|
+
* will be is the seam's.
|
|
98
|
+
*
|
|
99
|
+
* `isPending` asks exactly that — final unless the flush parks — so its
|
|
100
|
+
* reader cannot be answered before the seam: a flush that has joined a
|
|
101
|
+
* transaction parks, and the node with it — the reader is that
|
|
102
|
+
* transaction's verdict lane's now; before anything joined, the reader
|
|
103
|
+
* watches the seam and runs again if the flush parks (a plain sync write
|
|
104
|
+
* never glitches it true).
|
|
105
|
+
*
|
|
106
|
+
* `latest` has its answer either way — the proposal — and only the reader's
|
|
107
|
+
* membership was open, so it is settled now: the frame gets its transaction
|
|
108
|
+
* (`joinFuture(null)`; nothing holds it at the seam and it lands there, the
|
|
109
|
+
* lane dissolving into it — a plain commit) and the reader is its verdict
|
|
110
|
+
* lane's. One pass: a provisional pass re-derived at the seam ran everything
|
|
111
|
+
* downstream of it twice (#3540 web). A pass that also read the frame's own
|
|
112
|
+
* stagings plainly (REACTIVE_STAGED_READ) stays the frame's (`route` does
|
|
113
|
+
* not move it); a verdict reader's plain read AFTER the window sees the
|
|
114
|
+
* screen (`read`: `stagedScreen`). */ function provisionalVerdict(e, t) {
|
|
115
|
+
if (!probing) {
|
|
116
|
+
joinFuture(null);
|
|
117
|
+
verdictRead(e, flushTransaction);
|
|
118
|
+
return;
|
|
204
119
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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;
|
|
120
|
+
if (flushTransaction !== null) {
|
|
121
|
+
if (t) probeFound = true;
|
|
122
|
+
verdictRead(e, flushTransaction);
|
|
123
|
+
if (e.R & REACTIVE_STAGED_READ) watchVerdict(e);
|
|
124
|
+
return;
|
|
233
125
|
}
|
|
234
|
-
|
|
126
|
+
// Before anything joined, a staging that would not be final held has no
|
|
127
|
+
// answer yet: whatever the seam decides, the pass runs again — a later
|
|
128
|
+
// plain read of the same pass may route it into the verdict lane
|
|
129
|
+
// (`observeFlight`), which the seam must not take as this probe's answer.
|
|
130
|
+
watchVerdict(e, t);
|
|
235
131
|
}
|
|
236
132
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
133
|
+
/** A19 exc. 2 / A24: a flight that re-asks the question already answered
|
|
134
|
+
* (a `refresh()`, a poll) is quiet; a node pending through others is quiet
|
|
135
|
+
* when every flight it waits on is. */ function quietPending(e) {
|
|
136
|
+
const t = e.m;
|
|
137
|
+
if (t === null) return false;
|
|
138
|
+
if (t.Se !== undefined) {
|
|
139
|
+
for (const e of t.Se) if (!e.m?.It) return false;
|
|
140
|
+
return true;
|
|
245
141
|
}
|
|
246
|
-
|
|
142
|
+
return t.It;
|
|
247
143
|
}
|
|
248
144
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
145
|
+
/** `isPending`'s answer for a held node (A19), relative to what the reader
|
|
146
|
+
* sees — a verdict reader is a frame reader and sees the committed value: a
|
|
147
|
+
* flight still up (cause ii) is not final unless quiet; a held write or
|
|
148
|
+
* landing (staged — cause i/iii) is not final unless it answers the question
|
|
149
|
+
* already shown (a quiet re-ask, through its reveal), is the node's first
|
|
150
|
+
* landing (commit #0: loading, not pending), or the probe sits inside a
|
|
151
|
+
* `latest` window, whose view of the node IS the proposal (A10: a reader
|
|
152
|
+
* that observed the fresh value is not told it is pending; inside `latest`,
|
|
153
|
+
* `isPending` asks whether the latest view is final). A member of a held
|
|
154
|
+
* frame with neither is final. A24: pending iff a value change is in
|
|
155
|
+
* flight — a staging equal to what is displayed changes nothing when it
|
|
156
|
+
* lands: final now (maintainer, 2026-10-02). */ function heldNotFinal(e) {
|
|
157
|
+
if ((e.G & STATUS_PENDING) !== 0 && !quietPending(e)) return true;
|
|
158
|
+
return e.Ae !== NOT_PENDING && !latestActive && !e.m.It && !(e.G & STATUS_UNINITIALIZED) && (!e.tn || !e.tn(e.pe, e.Ae));
|
|
253
159
|
}
|
|
254
160
|
|
|
255
|
-
/**
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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);
|
|
161
|
+
/** `isPending`'s answer for an unheld node whose flight is up (cause ii). */ function pendingVerdict(e, t) {
|
|
162
|
+
if (t.G & STATUS_UNINITIALIZED) return;
|
|
163
|
+
if (probing && !quietPending(t)) probeFound = true;
|
|
164
|
+
if (e !== null && tracking) {
|
|
165
|
+
if (t.u & CONFIG_HELD) verdictRead(e, txOf(t)); else if (globalQueue.ee) provisionalVerdict(e, false);
|
|
166
|
+
// decided above
|
|
273
167
|
}
|
|
274
|
-
};
|
|
275
|
-
visit(e);
|
|
276
168
|
}
|
|
277
169
|
|
|
278
|
-
/**
|
|
279
|
-
*
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
}
|
|
170
|
+
/** The pass entering a verdict window is a verdict reader (CONFIG_VERDICT):
|
|
171
|
+
* a dependency going pending re-derives it, the verdict having changed. */ function markVerdictReader(e) {
|
|
172
|
+
let t = context;
|
|
173
|
+
if (t?.Ht) t = t.gt;
|
|
174
|
+
if (t !== null) {
|
|
175
|
+
t.u |= CONFIG_VERDICT;
|
|
176
|
+
t.R |= REACTIVE_PROBED;
|
|
177
|
+
// Observe tier: which windows this pass entered (1 `isPending`, 2
|
|
178
|
+
// `latest`) — the hold census names the affordance (attribution.ts).
|
|
179
|
+
t.en = (t.en ?? 0) | e;
|
|
308
180
|
}
|
|
309
|
-
if (t) schedule();
|
|
310
181
|
}
|
|
311
182
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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);
|
|
183
|
+
/** The windows. `read` dispatches to `verdictValue` while one is open; each
|
|
184
|
+
* restores the dispatch it found — none inside a memo's pass a window
|
|
185
|
+
* pulled (`verdictValue`). */ function latest(e) {
|
|
186
|
+
markVerdictReader(2);
|
|
187
|
+
const t = latestActive, n = verdict;
|
|
188
|
+
latestActive = true;
|
|
189
|
+
setVerdict(verdictValue);
|
|
190
|
+
try {
|
|
191
|
+
return e();
|
|
192
|
+
} finally {
|
|
193
|
+
latestActive = t;
|
|
194
|
+
setVerdict(n);
|
|
388
195
|
}
|
|
389
|
-
return t;
|
|
390
196
|
}
|
|
391
197
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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;
|
|
198
|
+
function isPending(e) {
|
|
199
|
+
markVerdictReader(1);
|
|
200
|
+
const t = probing;
|
|
201
|
+
const n = probeFound;
|
|
202
|
+
const r = verdict;
|
|
203
|
+
probing = true;
|
|
204
|
+
probeFound = false;
|
|
205
|
+
setVerdict(verdictValue);
|
|
435
206
|
try {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
//
|
|
440
|
-
//
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
//
|
|
444
|
-
//
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
// (A7; the unowned scope used to return undefined here).
|
|
448
|
-
if (t instanceof NotReadyError && !uninitializedSource(e)) return i;
|
|
449
|
-
throw t;
|
|
207
|
+
e();
|
|
208
|
+
return probeFound;
|
|
209
|
+
} catch (e) {
|
|
210
|
+
// A pending node is served inside the probe; what still throws is an
|
|
211
|
+
// uninitialized one — loading, not pending (A19 exc. 1) — whose NotReady
|
|
212
|
+
// propagates to boundaries from inside an owner (B5a; a component body
|
|
213
|
+
// suspends on it like any read, #2928) and is swallowed outside one. A
|
|
214
|
+
// verdict never throws anything else: a thunk that errors is simply not
|
|
215
|
+
// pending.
|
|
216
|
+
if (e instanceof NotReadyError && !probeFound && context !== null) throw e;
|
|
217
|
+
return probeFound;
|
|
450
218
|
} finally {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
if (uninitializedSource(e)) throw new NotReadyError(e);
|
|
455
|
-
return i;
|
|
219
|
+
probing = t;
|
|
220
|
+
probeFound = n;
|
|
221
|
+
setVerdict(r);
|
|
456
222
|
}
|
|
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
223
|
}
|
|
470
224
|
|
|
471
|
-
/**
|
|
472
|
-
*
|
|
473
|
-
*
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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);
|
|
225
|
+
/** `latest()` of a held node: the flushed staging — the proposal — and, for
|
|
226
|
+
* a tracked reader of a rewrite not yet flushed, the late-linker re-run in
|
|
227
|
+
* the flush that carries it. */ function heldLatest(e, t) {
|
|
228
|
+
const n = unflushedValue(e);
|
|
229
|
+
if (n === NOT_PENDING) return e.Ae;
|
|
230
|
+
if (t !== null) markLateLinker(t);
|
|
231
|
+
return n;
|
|
485
232
|
}
|
|
486
233
|
|
|
487
|
-
/** The
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
234
|
+
/** The verdict windows' read — `latest(fn)` and `isPending(fn)` — in one
|
|
235
|
+
* cold path off `read`'s. It links and pulls like a plain read, then answers
|
|
236
|
+
* for what the reader sees (a verdict reader is a frame reader):
|
|
237
|
+
* - a lane's node: the reader is the holder's verdict lane's (not the
|
|
238
|
+
* lane's — a lane blocked on its own flight would hold the reader's run,
|
|
239
|
+
* the opposite of a verdict's display-ahead). A guess is the value (A17),
|
|
240
|
+
* not final while its own question flies or a truth held beneath it
|
|
241
|
+
* differs (A24, A18 provenance). A derivation: `latest` sees the lane's
|
|
242
|
+
* latest (a landing a blocked lane holds has answered its question — the
|
|
243
|
+
* checkout's shipping text updates independently of the tax in flight),
|
|
244
|
+
* `isPending` the screen; not final while its own flight is up;
|
|
245
|
+
* - a held node: the reader breaks out as the holder's verdict lane's and
|
|
246
|
+
* re-derives at the landing; `latest` serves the proposal (`heldLatest`),
|
|
247
|
+
* `isPending` answers `heldNotFinal`;
|
|
248
|
+
* - a node staged this flush and not yet held: the seam's verdict
|
|
249
|
+
* (`provisionalVerdict`) — or, with a newer question already in flight
|
|
250
|
+
* (#3376, the stale first hop of a chain), its flight's;
|
|
251
|
+
* - a flight (cause ii): `isPending` records it (`pendingVerdict`), `latest`
|
|
252
|
+
* serves the committed value — below the async nothing newer exists; an
|
|
253
|
+
* uninitialized one throws NotReady: loading, not pending (A19 exc. 1);
|
|
254
|
+
* - an errored derivation throws for every reader (#2897). */ function verdictValue(e, t) {
|
|
255
|
+
const n = e;
|
|
256
|
+
const r = t !== null && tracking && !(t.u & CONFIG_CHILDREN_FORBIDDEN);
|
|
257
|
+
if (t !== null && tracking) {
|
|
258
|
+
link(e, t);
|
|
259
|
+
if (n.re !== undefined) {
|
|
260
|
+
// A31: the pulled memo's pass is its own, not the probe's — it reads
|
|
261
|
+
// outside the window, and the probe's read below answers for what
|
|
262
|
+
// the pass produced.
|
|
263
|
+
setVerdict(null);
|
|
264
|
+
pullComputed(n, t);
|
|
265
|
+
setVerdict(verdictValue);
|
|
495
266
|
}
|
|
496
267
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
//
|
|
500
|
-
//
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
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);
|
|
268
|
+
// A live `affects()` mark on the node or a dependency (affects.ts): not
|
|
269
|
+
// final by declaration — the value itself is read as below. The verdict is
|
|
270
|
+
// display-ahead like any: in a transaction's flush (the declaring action's
|
|
271
|
+
// body) the reader is its verdict lane's, not a staging the frame parks.
|
|
272
|
+
if (probing && GlobalQueue.C !== undefined && GlobalQueue.C(n)) {
|
|
273
|
+
probeFound = true;
|
|
274
|
+
if (r && flushTransaction !== null) verdictRead(t, flushTransaction);
|
|
577
275
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
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);
|
|
276
|
+
if (e.u & CONFIG_OVERRIDE) {
|
|
277
|
+
const i = txOf(e);
|
|
278
|
+
if (r) verdictRead(t, i._parent ?? i);
|
|
279
|
+
const u = (n.G & STATUS_PENDING) !== 0 && !quietPending(n);
|
|
280
|
+
if (e.u & CONFIG_GUESS) {
|
|
281
|
+
if (probing && (u || e.Ae !== NOT_PENDING && (!e.tn || !e.tn(e.m.Z, e.Ae)))) probeFound = true;
|
|
282
|
+
return e.m.Z;
|
|
612
283
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
284
|
+
if (probing && u) probeFound = true;
|
|
285
|
+
return latestActive && !u ? laneValueOf(e) : display(e);
|
|
286
|
+
}
|
|
287
|
+
const i = (n.G & STATUS_UNINITIALIZED) !== 0;
|
|
288
|
+
if (e.u & CONFIG_HELD) {
|
|
289
|
+
if (!i) {
|
|
290
|
+
const i = txOf(e);
|
|
291
|
+
// A render effect re-run (or mounted) outside the verdict lane's flush
|
|
292
|
+
// while the lane is blocked — its display held on its own derivation
|
|
293
|
+
// in flight — is a stale reader of the lane (#3460, as `laneRead`): the
|
|
294
|
+
// committed value now, re-derived at the reveal. Not inside a probe:
|
|
295
|
+
// `isPending` is answered below either way.
|
|
296
|
+
if (r && !probing && t.i === EFFECT_RENDER && passLane === null && i.rt !== null && i.rt.fn && flushTransaction !== i) {
|
|
297
|
+
staleReader(t, i.rt);
|
|
298
|
+
return e.pe;
|
|
299
|
+
}
|
|
300
|
+
if (r) verdictRead(t, i);
|
|
301
|
+
if (probing && heldNotFinal(n)) probeFound = true;
|
|
302
|
+
if (latestActive && e.Ae !== NOT_PENDING) {
|
|
303
|
+
// An action body reading another transaction's proposal derives
|
|
304
|
+
// from it: the two settle as one (A15; posture C, 2026-09-15). An
|
|
305
|
+
// untracked read elsewhere has no transaction to entangle.
|
|
306
|
+
if (!r && flushTransaction !== null && !globalQueue.ee) joinFuture(txOf(e));
|
|
307
|
+
return heldLatest(e, r ? t : null);
|
|
628
308
|
}
|
|
309
|
+
return e.pe;
|
|
629
310
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
if
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
311
|
+
// Born into the future: its staging is its only value.
|
|
312
|
+
if (e.Ae !== NOT_PENDING && !(n.G & STATUS_PENDING)) return e.Ae;
|
|
313
|
+
} else if (e.Ae !== NOT_PENDING) {
|
|
314
|
+
if (n.G & STATUS_PENDING && !i) pendingVerdict(r ? t : null, n); else if (r && globalQueue.ee) provisionalVerdict(t, !latestActive && !i);
|
|
315
|
+
// Outside a flush, the staging is an unflushed write (A28): the probe
|
|
316
|
+
// answers for the flushed world, and watches the seam of the flush that
|
|
317
|
+
// carries the write — a memo of `isPending(x)` created after the write
|
|
318
|
+
// flips if that flush parks (#3078). (The write's own fan-out ran before
|
|
319
|
+
// the reader existed; nothing else would re-derive it.)
|
|
320
|
+
else if (r && probing) watchVerdict(t);
|
|
321
|
+
if (latestActive) {
|
|
322
|
+
// A28: an unflushed write is not what `latest` serves; a tracked
|
|
323
|
+
// reader derived from the flushed world runs again in the carrying
|
|
324
|
+
// flush.
|
|
325
|
+
if (globalQueue.ee) return e.Ae;
|
|
326
|
+
if (t !== null) markLateLinker(t);
|
|
641
327
|
}
|
|
642
|
-
return
|
|
643
|
-
} finally {
|
|
644
|
-
setPendingCheckActive(t);
|
|
645
|
-
pendingProbe = n;
|
|
328
|
+
return e.pe;
|
|
646
329
|
}
|
|
330
|
+
if (n.G & STATUS_PENDING) {
|
|
331
|
+
if (!i) {
|
|
332
|
+
pendingVerdict(r ? t : null, n);
|
|
333
|
+
return e.pe;
|
|
334
|
+
}
|
|
335
|
+
// As `read()`: an untracked read of a pending node still re-runs its
|
|
336
|
+
// reader when the node settles (a projection's pull reads its firewall
|
|
337
|
+
// untracked, store/store.ts).
|
|
338
|
+
if (t !== null && !tracking && !spectating && e !== t) link(e, t);
|
|
339
|
+
throw n.m?.v;
|
|
340
|
+
}
|
|
341
|
+
if (n.re !== undefined && n.G & STATUS_ERROR) throw n.m.v;
|
|
342
|
+
return e.pe;
|
|
647
343
|
}
|
|
648
344
|
|
|
649
|
-
//
|
|
650
|
-
//
|
|
651
|
-
//
|
|
652
|
-
GlobalQueue.
|
|
653
|
-
|
|
654
|
-
GlobalQueue.Ue = updatePendingSignal;
|
|
655
|
-
|
|
656
|
-
GlobalQueue.qe = updateChildCompanions;
|
|
657
|
-
|
|
658
|
-
GlobalQueue.Qn = snapCompanionsToState;
|
|
659
|
-
|
|
660
|
-
GlobalQueue.An = latestRead;
|
|
661
|
-
|
|
662
|
-
GlobalQueue.Cn = pendingCheckRead;
|
|
663
|
-
|
|
664
|
-
GlobalQueue.Hn = recordFreshRead;
|
|
665
|
-
|
|
666
|
-
GlobalQueue.At = applyReask;
|
|
667
|
-
|
|
668
|
-
GlobalQueue.k = repollDownstreamVerdicts;
|
|
669
|
-
|
|
670
|
-
GlobalQueue.ri = witnessAffects;
|
|
345
|
+
// Installed at module evaluation — present exactly when something imports
|
|
346
|
+
// `latest` or `isPending`. (`read` dispatches to `verdictValue` through
|
|
347
|
+
// `verdict`, set while a window is open.)
|
|
348
|
+
GlobalQueue.vt = observeFlight;
|
|
671
349
|
|
|
672
|
-
|
|
350
|
+
/** A store read inside an untracked `isPending` probe found a live mark on
|
|
351
|
+
* a record with no node to carry it (store/affects.ts): not final. */ GlobalQueue.Bt = () => {
|
|
352
|
+
if (probing) probeFound = true;
|
|
353
|
+
};
|
|
673
354
|
|
|
674
355
|
export { isPending, latest };
|