@solidjs/signals 2.0.0-rc.7 → 2.0.0-rc.9
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 +6357 -0
- package/dist/dev.attribution.js +2485 -0
- package/dist/dev.js +2851 -7027
- package/dist/observe/affects.js +129 -0
- package/dist/observe/attribution.js +7 -0
- package/dist/observe/boundaries.js +641 -0
- package/dist/observe/core/action.js +177 -0
- package/dist/observe/core/async.js +846 -0
- package/dist/observe/core/attribution-costs.js +66 -0
- package/dist/observe/core/attribution-feedback.js +282 -0
- package/dist/observe/core/attribution-hooks.js +82 -0
- package/dist/observe/core/attribution-queries.js +28 -0
- package/dist/observe/core/attribution.js +2095 -0
- package/dist/observe/core/constants.js +248 -0
- package/dist/{types-cjs/core/context.d.cts → observe/core/context.js} +37 -12
- package/dist/observe/core/core.js +1789 -0
- package/dist/observe/core/dev.js +293 -0
- package/dist/observe/core/effect.js +195 -0
- package/dist/observe/core/error-hooks.js +71 -0
- package/dist/observe/core/error.js +80 -0
- package/dist/observe/core/external.js +98 -0
- package/dist/observe/core/graph.js +155 -0
- package/dist/observe/core/heap.js +147 -0
- package/dist/observe/core/invariants.js +45 -0
- package/dist/observe/core/lanes.js +205 -0
- package/dist/observe/core/optimistic.js +585 -0
- package/dist/observe/core/owner.js +322 -0
- package/dist/observe/core/scheduler.js +1428 -0
- package/dist/observe/core/verdict.js +669 -0
- package/dist/observe/index.js +59 -0
- package/dist/observe/map.js +484 -0
- package/dist/{types-cjs/signals.d.cts → observe/signals.js} +311 -317
- package/dist/observe/store/index.js +40 -0
- package/dist/observe/store/next/optimistic.js +660 -0
- package/dist/observe/store/next/projection.js +255 -0
- package/dist/observe/store/next/reconcile.js +375 -0
- package/dist/observe/store/next/store.js +2327 -0
- package/dist/observe/store/next/target.js +51 -0
- package/dist/observe/store/store.js +325 -0
- package/dist/observe/store/storePath.js +103 -0
- package/dist/observe/store/utils.js +1023 -0
- package/dist/prod/attribution.js +41 -0
- package/dist/prod/boundaries.js +128 -76
- package/dist/prod/core/action.js +65 -37
- package/dist/prod/core/async.js +324 -172
- package/dist/prod/core/constants.js +57 -1
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +1003 -364
- package/dist/prod/core/dev.js +17 -1
- package/dist/prod/core/effect.js +76 -42
- package/dist/prod/core/error-hooks.js +71 -0
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +37 -37
- package/dist/prod/core/heap.js +61 -56
- package/dist/prod/core/lanes.js +107 -63
- package/dist/prod/core/optimistic.js +374 -93
- package/dist/prod/core/owner.js +62 -46
- package/dist/prod/core/scheduler.js +653 -286
- package/dist/prod/core/verdict.js +245 -127
- package/dist/prod/index.js +15 -5
- package/dist/prod/map.js +292 -141
- package/dist/prod/signals.js +29 -16
- package/dist/prod/store/next/optimistic.js +77 -55
- package/dist/prod/store/next/projection.js +31 -20
- package/dist/prod/store/next/reconcile.js +87 -84
- package/dist/prod/store/next/store.js +718 -401
- package/dist/prod/store/next/target.js +32 -10
- package/dist/prod/store/store.js +12 -14
- package/dist/prod/store/utils.js +950 -130
- package/dist/types/attribution.d.ts +20 -0
- package/dist/types/attribution.prod.d.ts +22 -0
- package/dist/types/boundaries.d.ts +10 -1
- package/dist/types/core/action.d.ts +12 -5
- package/dist/types/core/async.d.ts +1 -1
- package/dist/types/core/attribution-costs.d.ts +35 -0
- package/dist/types/core/attribution-feedback.d.ts +133 -0
- package/dist/types/core/attribution-hooks.d.ts +147 -9
- package/dist/types/core/attribution-queries.d.ts +10 -0
- package/dist/types/core/attribution.d.ts +216 -182
- package/dist/types/core/constants.d.ts +56 -0
- package/dist/types/core/core.d.ts +190 -2
- package/dist/types/core/dev.d.ts +246 -28
- package/dist/types/core/effect.d.ts +8 -1
- package/dist/types/core/error-hooks.d.ts +71 -0
- package/dist/types/core/index.d.ts +4 -1
- package/dist/types/core/invariants.d.ts +4 -0
- package/dist/types/core/lanes.d.ts +39 -5
- package/dist/types/core/scheduler.d.ts +153 -0
- package/dist/types/core/types.d.ts +32 -9
- package/dist/types/index.d.ts +10 -3
- package/dist/types/map.d.ts +2 -0
- package/dist/types/signals.d.ts +8 -0
- package/dist/types/store/index.d.ts +2 -0
- package/dist/types/store/next/optimistic.d.ts +1 -1
- package/dist/types/store/next/store.d.ts +11 -5
- package/dist/types/store/next/target.d.ts +37 -10
- package/dist/types/store/utils.d.ts +178 -0
- package/package.json +21 -17
- package/dist/node.cjs +0 -10541
- package/dist/node.dev.cjs +0 -13724
- package/dist/types-cjs/affects.d.cts +0 -47
- package/dist/types-cjs/boundaries.d.cts +0 -175
- package/dist/types-cjs/core/action.d.cts +0 -64
- package/dist/types-cjs/core/async.d.cts +0 -23
- package/dist/types-cjs/core/attribution-hooks.d.cts +0 -127
- package/dist/types-cjs/core/attribution.d.cts +0 -495
- package/dist/types-cjs/core/constants.d.cts +0 -159
- package/dist/types-cjs/core/core.d.cts +0 -185
- package/dist/types-cjs/core/dev.d.cts +0 -136
- package/dist/types-cjs/core/effect.d.cts +0 -30
- package/dist/types-cjs/core/error.d.cts +0 -56
- package/dist/types-cjs/core/external.d.cts +0 -15
- package/dist/types-cjs/core/graph.d.cts +0 -28
- package/dist/types-cjs/core/heap.d.cts +0 -24
- package/dist/types-cjs/core/index.d.cts +0 -13
- package/dist/types-cjs/core/invariants.d.cts +0 -59
- package/dist/types-cjs/core/lanes.d.cts +0 -54
- package/dist/types-cjs/core/optimistic.d.cts +0 -6
- package/dist/types-cjs/core/owner.d.cts +0 -124
- package/dist/types-cjs/core/scheduler.d.cts +0 -236
- package/dist/types-cjs/core/types.d.cts +0 -204
- package/dist/types-cjs/core/verdict.d.cts +0 -2
- package/dist/types-cjs/index.d.cts +0 -10
- package/dist/types-cjs/map.d.cts +0 -69
- package/dist/types-cjs/package.json +0 -3
- package/dist/types-cjs/store/index.d.cts +0 -18
- package/dist/types-cjs/store/next/optimistic.d.cts +0 -25
- package/dist/types-cjs/store/next/projection.d.cts +0 -8
- package/dist/types-cjs/store/next/reconcile.d.cts +0 -10
- package/dist/types-cjs/store/next/store.d.cts +0 -121
- package/dist/types-cjs/store/next/target.d.cts +0 -166
- package/dist/types-cjs/store/store.d.cts +0 -143
- package/dist/types-cjs/store/storePath.d.cts +0 -58
- package/dist/types-cjs/store/utils.d.cts +0 -74
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
import { NOT_PENDING, REACTIVE_DISPOSED, REACTIVE_DIRTY, REACTIVE_CHECK, unwrapOverride,
|
|
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";
|
|
2
2
|
|
|
3
|
-
import { setSignal,
|
|
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";
|
|
4
4
|
|
|
5
5
|
import { NotReadyError } from "./error.js";
|
|
6
6
|
|
|
7
7
|
import { link } from "./graph.js";
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { dispose } from "./owner.js";
|
|
10
|
+
|
|
11
|
+
import { insertIntoHeap, queueFor, enqueueSub } from "./heap.js";
|
|
10
12
|
|
|
11
13
|
import "./invariants.js";
|
|
12
14
|
|
|
13
|
-
import {
|
|
15
|
+
import { readsHeldCommitted, assignOrMergeLane } from "./lanes.js";
|
|
14
16
|
|
|
15
17
|
import { installOptimisticEngine } from "./optimistic.js";
|
|
16
18
|
|
|
17
|
-
import { GlobalQueue, insertSubs, schedule, activeTransition, currentTransition, activeAffectsMarks } from "./scheduler.js";
|
|
19
|
+
import { GlobalQueue, insertSubs, schedule, activeTransition, currentTransition, runAsTransitionBatch, activeAffectsMarks } from "./scheduler.js";
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* The isPending()/latest() verdict layer, moved out of core.ts. Importing this
|
|
@@ -44,35 +46,76 @@ let pendingProbe = null;
|
|
|
44
46
|
* the one-load gate at the call sites). The snap then iterates exactly the
|
|
45
47
|
* children someone asked verdicts of — O(companions) — never the full
|
|
46
48
|
* `_child` chain, which carries one node per materialized leaf (the
|
|
47
|
-
* O(all-leaves-ever-read)-per-update pathology). Entries
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
|
|
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.Te;
|
|
51
54
|
if (!t) return;
|
|
52
55
|
t.T |= CONFIG_CHILD_COMPANIONS;
|
|
53
|
-
(ext(t).
|
|
56
|
+
(ext(t).bt ??= new Set).add(e);
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
function getPendingSignal(e) {
|
|
57
|
-
let t = e.o?.
|
|
60
|
+
let t = e.o?.je;
|
|
58
61
|
if (!t) {
|
|
59
62
|
// Start false, write true if pending - ensures reversion returns to false
|
|
60
63
|
t = optimisticSignal(false, {
|
|
61
64
|
ownedWrite: true
|
|
62
65
|
});
|
|
63
|
-
ext(e).
|
|
66
|
+
ext(e).je = t;
|
|
64
67
|
e.T |= CONFIG_HAS_COMPANIONS;
|
|
65
68
|
markFirewallChildCompanions(e);
|
|
66
|
-
ext(t).
|
|
67
|
-
if (computePendingState(e))
|
|
69
|
+
ext(t).Ht = e;
|
|
70
|
+
if (computePendingState(e)) backfillCompanion(e, t, true);
|
|
71
|
+
joinUnflushedResync(e);
|
|
68
72
|
}
|
|
69
73
|
return t;
|
|
70
74
|
}
|
|
71
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.Ge;
|
|
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);
|
|
103
|
+
}
|
|
104
|
+
|
|
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.ve;
|
|
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.Ge === null || e.T & CONFIG_ADOPTED_UNFLUSHED ? NOT_PENDING : e.o.gt;
|
|
113
|
+
}
|
|
114
|
+
|
|
72
115
|
function collectPendingSources(e) {
|
|
73
116
|
if (!pendingProbe) return;
|
|
74
117
|
pendingProbe.sources.add(e);
|
|
75
|
-
const t = e.
|
|
118
|
+
const t = e.Te || e;
|
|
76
119
|
if (t !== e) pendingProbe.sources.add(t);
|
|
77
120
|
}
|
|
78
121
|
|
|
@@ -105,17 +148,17 @@ function collectPendingSources(e) {
|
|
|
105
148
|
if (e.S & STATUS_ERROR) return false;
|
|
106
149
|
if (t.has(e)) return false;
|
|
107
150
|
t.add(e);
|
|
108
|
-
const n = e.
|
|
151
|
+
const n = e.Te;
|
|
109
152
|
if (n && markWalk(n, t)) return true;
|
|
110
153
|
// Mid-recompute (the clearStatus companion poke runs before
|
|
111
154
|
// trimStaleDeps), only the validated prefix [_deps.._depsTail] is this
|
|
112
155
|
// pass's dependency set — walking past it would read dropped deps and
|
|
113
156
|
// latch a stale verdict on the companion.
|
|
114
157
|
const i = e;
|
|
115
|
-
const r = i.
|
|
158
|
+
const r = i.ue & REACTIVE_RECOMPUTING_DEPS ? i.ot : undefined;
|
|
116
159
|
if (r !== null) {
|
|
117
|
-
for (let e = i.
|
|
118
|
-
if (!e.
|
|
160
|
+
for (let e = i.Se ?? null; e !== null; e = e.de) {
|
|
161
|
+
if (!e.He && markWalk(e.Ee, t)) return true;
|
|
119
162
|
if (e === r) break;
|
|
120
163
|
}
|
|
121
164
|
}
|
|
@@ -123,11 +166,11 @@ function collectPendingSources(e) {
|
|
|
123
166
|
}
|
|
124
167
|
|
|
125
168
|
function quietPending(e) {
|
|
126
|
-
if (e.o?.
|
|
127
|
-
for (const t of e.o.
|
|
169
|
+
if (e.o?.ae) {
|
|
170
|
+
for (const t of e.o.ae) if (!t.o?.be) return false;
|
|
128
171
|
return true;
|
|
129
172
|
}
|
|
130
|
-
return e.o?.
|
|
173
|
+
return e.o?.be ?? false;
|
|
131
174
|
}
|
|
132
175
|
|
|
133
176
|
// NOTE: a loadingValue node's open loading window (_loading) is verdict-quiet
|
|
@@ -143,50 +186,68 @@ function newQuestionInFlight(e) {
|
|
|
143
186
|
|
|
144
187
|
function computePendingState(e) {
|
|
145
188
|
const t = e;
|
|
146
|
-
if (t.
|
|
189
|
+
if (t.ue & REACTIVE_DISPOSED) return false;
|
|
147
190
|
// Mark coverage is transitive by dep-graph reachability: a latest() shadow
|
|
148
191
|
// reaches its owner (and a store leaf its firewall) through its own deps,
|
|
149
192
|
// so the one walk covers direct marks, derivation, and companion chains.
|
|
150
193
|
// Gated: apps with no live mark pay one integer compare.
|
|
151
194
|
if (activeAffectsMarks !== 0 && markWalk(e, new Set)) return true;
|
|
152
|
-
const n = e.
|
|
153
|
-
if (e.o?.
|
|
154
|
-
const t = e.o?.
|
|
155
|
-
const n = t.
|
|
195
|
+
const n = e.Te;
|
|
196
|
+
if (e.o?.Ht) {
|
|
197
|
+
const t = e.o?.Ht;
|
|
198
|
+
const n = t.Te || t;
|
|
156
199
|
return newQuestionInFlight(n);
|
|
157
200
|
}
|
|
158
|
-
|
|
159
|
-
|
|
201
|
+
const i = flushedStaged(e);
|
|
202
|
+
if (n && i !== NOT_PENDING && !hasActiveOverride(e)) {
|
|
203
|
+
return !!(n.ue & REACTIVE_MANUAL_WRITE) || !n.o?.Re && !(n.S & STATUS_PENDING) || !!(n.S & STATUS_PENDING) && quietPending(n);
|
|
160
204
|
}
|
|
161
205
|
// `!comp._loading`: a hold created while the loading window is still open is
|
|
162
206
|
// the window's own landing in flight to its commit — verdict-quiet like the
|
|
163
207
|
// rest of the window (the UNINITIALIZED check suppresses exactly this frame
|
|
164
208
|
// for windowless first loads; born-committed nodes need their own gate, #2990).
|
|
165
|
-
|
|
166
|
-
|
|
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.T & CONFIG_OVERRIDE_SUPERSEDED && e.ve === NOT_PENDING && visibleOverride(e)) return !e.Fe || !e.Fe(e.Qe, unwrapOverride(e.o?.Ce));
|
|
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.ge) {
|
|
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?.Ce));
|
|
167
224
|
// A quiet re-ask's held landing still answers the same question: the
|
|
168
225
|
// classification survives the landing (asyncWrite) and dies with the
|
|
169
226
|
// commit (commitPendingNode) — verdict-quiet through the reveal, like
|
|
170
227
|
// the loading window above (#3178).
|
|
171
|
-
|
|
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?.be && (!e.Fe || !e.Fe(e.Qe, i))) return true;
|
|
172
233
|
}
|
|
173
234
|
return newQuestionInFlight(t);
|
|
174
235
|
}
|
|
175
236
|
|
|
176
237
|
function syncCompanions(e, t) {
|
|
177
|
-
if (e.o?.
|
|
178
|
-
if (e.o?.
|
|
238
|
+
if (e.o?.je) updatePendingSignal(e);
|
|
239
|
+
if (e.o?.xe) setSignal(e.o?.xe, t);
|
|
179
240
|
}
|
|
180
241
|
|
|
181
242
|
function updatePendingSignal(e) {
|
|
182
|
-
if (e.o?.
|
|
183
|
-
setSignal(e.o?.
|
|
243
|
+
if (e.o?.je) {
|
|
244
|
+
setSignal(e.o?.je, computePendingState(e));
|
|
184
245
|
}
|
|
185
|
-
if (e.o?.
|
|
246
|
+
if (e.o?.xe) updatePendingSignal(e.o?.xe);
|
|
186
247
|
}
|
|
187
248
|
|
|
188
249
|
function updateChildCompanions(e) {
|
|
189
|
-
const t = e.o?.
|
|
250
|
+
const t = e.o?.bt;
|
|
190
251
|
if (t === undefined) return;
|
|
191
252
|
for (const e of t) updatePendingSignal(e);
|
|
192
253
|
}
|
|
@@ -205,9 +266,9 @@ function updateChildCompanions(e) {
|
|
|
205
266
|
const visit = e => {
|
|
206
267
|
if (i.has(e)) return;
|
|
207
268
|
i.add(e);
|
|
208
|
-
if (e.o?.
|
|
209
|
-
for (let t = e.u; t !== null; t = t.
|
|
210
|
-
for (let t = e.o?.i ?? null; t !== null; t = t.
|
|
269
|
+
if (e.o?.je || e.o?.xe) n(e);
|
|
270
|
+
for (let t = e.u; t !== null; t = t.Ne) visit(t._e);
|
|
271
|
+
for (let t = e.o?.i ?? null; t !== null; t = t.De) {
|
|
211
272
|
visit(t);
|
|
212
273
|
}
|
|
213
274
|
};
|
|
@@ -228,7 +289,7 @@ function updateChildCompanions(e) {
|
|
|
228
289
|
if (suppressedProbes.size === 0) return;
|
|
229
290
|
let t = false;
|
|
230
291
|
for (const [n, i] of suppressedProbes) {
|
|
231
|
-
const r = n.
|
|
292
|
+
const r = n.Ge;
|
|
232
293
|
const s = r ? currentTransition(r) : null;
|
|
233
294
|
if (!s) {
|
|
234
295
|
suppressedProbes.delete(n);
|
|
@@ -236,11 +297,11 @@ function updateChildCompanions(e) {
|
|
|
236
297
|
}
|
|
237
298
|
if (s !== e) continue;
|
|
238
299
|
suppressedProbes.delete(n);
|
|
239
|
-
const o = n.o?.
|
|
300
|
+
const o = n.o?.je?.o?.Ue;
|
|
240
301
|
for (const e of i) {
|
|
241
|
-
if (e.
|
|
242
|
-
e.
|
|
243
|
-
if (o) assignOrMergeLane(e, o); else if (e.o !== null) e.o
|
|
302
|
+
if (e.ue & REACTIVE_DISPOSED) continue;
|
|
303
|
+
e.ue |= REACTIVE_OPTIMISTIC_DIRTY;
|
|
304
|
+
if (o) assignOrMergeLane(e, o); else if (e.o !== null) e.o.Ue = undefined;
|
|
244
305
|
enqueueSub(e);
|
|
245
306
|
t = true;
|
|
246
307
|
}
|
|
@@ -250,20 +311,32 @@ function updateChildCompanions(e) {
|
|
|
250
311
|
|
|
251
312
|
function snapCompanionsToState(e) {
|
|
252
313
|
suppressedProbes.size !== 0 && suppressedProbes.delete(e);
|
|
253
|
-
const t = e.o?.
|
|
254
|
-
if (t && (t.o?.
|
|
314
|
+
const t = e.o?.je;
|
|
315
|
+
if (t && (t.o?.Ce === undefined || t.o?.Ce === NOT_PENDING)) {
|
|
255
316
|
const n = computePendingState(e);
|
|
256
|
-
if (t.
|
|
257
|
-
t.
|
|
258
|
-
t.
|
|
317
|
+
if (t.Qe !== n || t.ve !== NOT_PENDING) {
|
|
318
|
+
t.Qe = n;
|
|
319
|
+
t.ve = NOT_PENDING;
|
|
259
320
|
insertSubs(t);
|
|
260
321
|
schedule();
|
|
261
322
|
}
|
|
262
323
|
}
|
|
263
|
-
const n = e.o?.
|
|
264
|
-
if (n && !(n.
|
|
265
|
-
|
|
266
|
-
|
|
324
|
+
const n = e.o?.xe;
|
|
325
|
+
if (n && !(n.ue & 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.Te?.ue & REACTIVE_DISPOSED) {
|
|
335
|
+
dispose(n);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if ((n.o?.Ce === undefined || n.o?.Ce === NOT_PENDING) && n.ve === NOT_PENDING && !Object.is(n.Qe, e.Qe) && !(n.ue & (REACTIVE_DIRTY | REACTIVE_CHECK))) {
|
|
339
|
+
n.ue |= REACTIVE_DIRTY;
|
|
267
340
|
insertIntoHeap(n, queueFor(n));
|
|
268
341
|
insertSubs(n);
|
|
269
342
|
schedule();
|
|
@@ -273,14 +346,14 @@ function snapCompanionsToState(e) {
|
|
|
273
346
|
}
|
|
274
347
|
|
|
275
348
|
function getLatestValueComputed(e) {
|
|
276
|
-
let t = e.o?.
|
|
349
|
+
let t = e.o?.xe;
|
|
277
350
|
// A shadow disposed while unobserved (its gated reader unmounted at a
|
|
278
351
|
// landing) is a corpse: sync writes into it equality-swallow against its
|
|
279
352
|
// frozen _value, and a later read revives it via recompute — clearing
|
|
280
353
|
// DISPOSED and re-deriving from the committed view, so the banner showed
|
|
281
354
|
// the previous transition's target (#3041 follow-up). Treat it as absent;
|
|
282
355
|
// recreation backfills from the in-flight write below.
|
|
283
|
-
if (t && t.
|
|
356
|
+
if (t && t.ue & REACTIVE_DISPOSED) t = undefined;
|
|
284
357
|
if (!t) {
|
|
285
358
|
const n = latestReadActive;
|
|
286
359
|
setLatestReadActive(false);
|
|
@@ -289,17 +362,26 @@ function getLatestValueComputed(e) {
|
|
|
289
362
|
const r = context;
|
|
290
363
|
setContextInternal(null);
|
|
291
364
|
// Detach from owner so it isn't disposed with effects
|
|
292
|
-
|
|
293
|
-
|
|
365
|
+
GlobalQueue.jt = true;
|
|
366
|
+
try {
|
|
367
|
+
t = optimisticComputed(() => read(e), {
|
|
368
|
+
ownedWrite: true
|
|
369
|
+
});
|
|
370
|
+
} finally {
|
|
371
|
+
GlobalQueue.jt = false;
|
|
372
|
+
}
|
|
373
|
+
ext(e).xe = t;
|
|
294
374
|
e.T |= CONFIG_HAS_COMPANIONS;
|
|
295
375
|
markFirewallChildCompanions(e);
|
|
296
|
-
ext(t).
|
|
376
|
+
ext(t).Ht = e;
|
|
297
377
|
// Parent-child lane relationship
|
|
298
378
|
// Backfill an in-flight write (mirrors getPendingSignal): the companion is
|
|
299
379
|
// created lazily, possibly after the write was processed — syncCompanions
|
|
300
380
|
// only pushes into companions that already exist, so the first latest()
|
|
301
381
|
// read inside a held transition showed the committed value (#3041).
|
|
302
|
-
|
|
382
|
+
const s = flushedStaged(e);
|
|
383
|
+
if (s !== NOT_PENDING && !hasActiveOverride(e)) backfillCompanion(e, t, s);
|
|
384
|
+
joinUnflushedResync(e);
|
|
303
385
|
setContextInternal(r);
|
|
304
386
|
setPendingCheckActive(i);
|
|
305
387
|
setLatestReadActive(n);
|
|
@@ -307,59 +389,83 @@ function getLatestValueComputed(e) {
|
|
|
307
389
|
return t;
|
|
308
390
|
}
|
|
309
391
|
|
|
310
|
-
/** The latest()-mode read path, installed as GlobalQueue._latestRead. */
|
|
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.Te || 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.Te?.ue & REACTIVE_DISPOSED) return e.Qe;
|
|
311
409
|
const t = getLatestValueComputed(e);
|
|
312
410
|
const n = latestReadActive;
|
|
313
411
|
setLatestReadActive(false);
|
|
314
|
-
const i = e
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
//
|
|
321
|
-
//
|
|
322
|
-
|
|
323
|
-
if (
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
// depended on whether anything had pulled the shadow current earlier
|
|
332
|
-
// in the tick (#3104: reading latest(m) flipped a later
|
|
333
|
-
// latest(() => isPending(x)) from true to false).
|
|
334
|
-
const n = pendingCheckActive;
|
|
335
|
-
setPendingCheckActive(false);
|
|
336
|
-
try {
|
|
337
|
-
prepareComputed(t, true);
|
|
338
|
-
} finally {
|
|
339
|
-
setPendingCheckActive(n);
|
|
340
|
-
}
|
|
412
|
+
const i = visibleOverride(e) ? unwrapOverride(e.o?.Ce) : e.Qe;
|
|
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.ue & 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);
|
|
341
429
|
}
|
|
342
|
-
|
|
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.Ge === null ? i : r;
|
|
433
|
+
}
|
|
434
|
+
let s;
|
|
435
|
+
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);
|
|
343
442
|
} catch (t) {
|
|
344
|
-
|
|
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;
|
|
345
449
|
throw t;
|
|
346
450
|
} finally {
|
|
347
451
|
setLatestReadActive(n);
|
|
348
452
|
}
|
|
349
|
-
if (t.S & STATUS_PENDING)
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
const n = findLane(currentOptimisticLane);
|
|
353
|
-
if (e !== n && laneHeld(e)) {
|
|
354
|
-
return i;
|
|
355
|
-
}
|
|
453
|
+
if (t.S & STATUS_PENDING) {
|
|
454
|
+
if (uninitializedSource(e)) throw new NotReadyError(e);
|
|
455
|
+
return i;
|
|
356
456
|
}
|
|
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.Qe;
|
|
357
463
|
// A shadow recomputed by the pull above (not at creation) holds its fresh
|
|
358
464
|
// speculative value in _pendingValue; a contextless read() only surfaces
|
|
359
465
|
// _value. Overrides stay authoritative (A17), and stale readers keep the
|
|
360
466
|
// other transition's committed view, matching read()'s own selection.
|
|
361
|
-
if (t.
|
|
362
|
-
return
|
|
467
|
+
if (t.ve !== NOT_PENDING && !hasActiveOverride(t) && !(stale && t.Ge && activeTransition !== t.Ge)) return t.ve;
|
|
468
|
+
return s;
|
|
363
469
|
}
|
|
364
470
|
|
|
365
471
|
/**
|
|
@@ -371,16 +477,23 @@ function getLatestValueComputed(e) {
|
|
|
371
477
|
* `_parentSource` but is a plain signal (no `_fn`) that never goes pending,
|
|
372
478
|
* so the `_fn` check is belt-and-braces for this call site.
|
|
373
479
|
*/ function latestShadowWithInitializedParent(e) {
|
|
374
|
-
if (typeof e.
|
|
375
|
-
const t = e.o?.
|
|
480
|
+
if (typeof e.ce !== "function") return false;
|
|
481
|
+
const t = e.o?.Ht;
|
|
376
482
|
if (t === undefined) return false;
|
|
377
|
-
const n = t.
|
|
483
|
+
const n = t.Te || t;
|
|
378
484
|
return !(n.S & STATUS_UNINITIALIZED);
|
|
379
485
|
}
|
|
380
486
|
|
|
381
487
|
/** The isPending()-probe read path, installed as GlobalQueue._pendingCheck. */ function pendingCheckRead(e, t, n, i) {
|
|
382
488
|
setPendingCheckActive(false);
|
|
383
|
-
if (typeof e.
|
|
489
|
+
if (typeof e.ce === "function") {
|
|
490
|
+
GlobalQueue.jt = true;
|
|
491
|
+
try {
|
|
492
|
+
prepareComputed(e, true);
|
|
493
|
+
} finally {
|
|
494
|
+
GlobalQueue.jt = false;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
384
497
|
const r = n.S;
|
|
385
498
|
if (t && r & STATUS_PENDING && r & STATUS_UNINITIALIZED &&
|
|
386
499
|
// The suspend-throw is for a genuinely-first-load source: the tracked
|
|
@@ -410,9 +523,9 @@ function getLatestValueComputed(e) {
|
|
|
410
523
|
* reader saw is an input, and pending remains the truth for every reader
|
|
411
524
|
* (#3028).
|
|
412
525
|
*/ function heldAwaitingAsync(e) {
|
|
413
|
-
const t = e.
|
|
526
|
+
const t = e.Ge;
|
|
414
527
|
const n = t ? currentTransition(t) : activeTransition;
|
|
415
|
-
if (!n || n.
|
|
528
|
+
if (!n || n.Tt) return false;
|
|
416
529
|
// A plain staged write (a signal/store leaf — no _fn) held while an action
|
|
417
530
|
// is still running is an INPUT to a computation still in flight (#3078):
|
|
418
531
|
// the pairing rule must not suppress the verdict, or a memo recomputing
|
|
@@ -422,19 +535,24 @@ function getLatestValueComputed(e) {
|
|
|
422
535
|
// awaiting reveal — where the pairing rule stands even inside an open
|
|
423
536
|
// action (#2831: a reader that saw the new value must not also see
|
|
424
537
|
// pending); still-computing answers are covered by the reporter scan.
|
|
425
|
-
if (n.
|
|
426
|
-
//
|
|
427
|
-
//
|
|
428
|
-
//
|
|
429
|
-
|
|
430
|
-
|
|
538
|
+
if (n.pe.length && !e.ce) 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.oe) {
|
|
431
549
|
if (t.size && e.S & STATUS_PENDING && e.o?._?.source === e) return true;
|
|
432
550
|
}
|
|
433
551
|
return false;
|
|
434
552
|
}
|
|
435
553
|
|
|
436
554
|
function recordFreshRead(e, t) {
|
|
437
|
-
if (pendingProbe !== null && e.
|
|
555
|
+
if (pendingProbe !== null && e.ve !== NOT_PENDING && t === e.ve) {
|
|
438
556
|
if (heldAwaitingAsync(e)) return;
|
|
439
557
|
pendingProbe.freshReads.add(e);
|
|
440
558
|
}
|
|
@@ -442,10 +560,10 @@ function recordFreshRead(e, t) {
|
|
|
442
560
|
|
|
443
561
|
function applyReask(e, t) {
|
|
444
562
|
const n = !!(e.S & STATUS_PENDING);
|
|
445
|
-
const i = t && !(n && !e.o?.
|
|
446
|
-
const r = n && (e.o?.
|
|
563
|
+
const i = t && !(n && !e.o?.be);
|
|
564
|
+
const r = n && (e.o?.be ?? false) !== i;
|
|
447
565
|
// Allocation-free for the quiet case: false is the extension default.
|
|
448
|
-
if (i) ext(e).
|
|
566
|
+
if (i) ext(e).be = true; else if (e.o !== null) e.o.be = false;
|
|
449
567
|
return r;
|
|
450
568
|
}
|
|
451
569
|
|
|
@@ -497,7 +615,7 @@ function isPending(e) {
|
|
|
497
615
|
// this flush (a downstream async pends and holds it), the suppression was
|
|
498
616
|
// wrong and the wrapper must re-ask (#3028). Remember who to wake — the
|
|
499
617
|
// async registration (GlobalQueue.notify) triggers wakeSuppressedProbes.
|
|
500
|
-
if (!i.found && i.suppressed.length && context && typeof context.
|
|
618
|
+
if (!i.found && i.suppressed.length && context && typeof context.ce === "function") {
|
|
501
619
|
for (const e of i.suppressed) {
|
|
502
620
|
let t = suppressedProbes.get(e);
|
|
503
621
|
if (!t) suppressedProbes.set(e, t = new Set);
|
|
@@ -526,26 +644,26 @@ function isPending(e) {
|
|
|
526
644
|
// Hook installation (same late-binding pattern as GlobalQueue._update /
|
|
527
645
|
// _propagateAffects): core call sites fire these behind the same guards the
|
|
528
646
|
// direct calls used, so behavior is identical once this module loads.
|
|
529
|
-
GlobalQueue.
|
|
647
|
+
GlobalQueue.me = syncCompanions;
|
|
530
648
|
|
|
531
|
-
GlobalQueue.
|
|
649
|
+
GlobalQueue.Oe = updatePendingSignal;
|
|
532
650
|
|
|
533
|
-
GlobalQueue.
|
|
651
|
+
GlobalQueue.Me = updateChildCompanions;
|
|
534
652
|
|
|
535
|
-
GlobalQueue.
|
|
653
|
+
GlobalQueue.En = snapCompanionsToState;
|
|
536
654
|
|
|
537
|
-
GlobalQueue.
|
|
655
|
+
GlobalQueue.zt = latestRead;
|
|
538
656
|
|
|
539
|
-
GlobalQueue.
|
|
657
|
+
GlobalQueue.Jt = pendingCheckRead;
|
|
540
658
|
|
|
541
|
-
GlobalQueue.
|
|
659
|
+
GlobalQueue.nn = recordFreshRead;
|
|
542
660
|
|
|
543
|
-
GlobalQueue.
|
|
661
|
+
GlobalQueue.Nt = applyReask;
|
|
544
662
|
|
|
545
663
|
GlobalQueue.k = repollDownstreamVerdicts;
|
|
546
664
|
|
|
547
|
-
GlobalQueue.
|
|
665
|
+
GlobalQueue.pn = witnessAffects;
|
|
548
666
|
|
|
549
|
-
GlobalQueue.
|
|
667
|
+
GlobalQueue.vn = wakeSuppressedProbes;
|
|
550
668
|
|
|
551
669
|
export { isPending, latest };
|