@solidjs/signals 2.0.0-rc.7 → 2.0.0-rc.8
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 +5243 -0
- package/dist/dev.attribution.js +2333 -0
- package/dist/dev.js +1229 -6834
- package/dist/observe/affects.js +127 -0
- package/dist/observe/attribution.js +1 -0
- package/dist/observe/boundaries.js +586 -0
- package/dist/observe/core/action.js +167 -0
- package/dist/observe/core/async.js +736 -0
- package/dist/observe/core/attribution-hooks.js +60 -0
- package/dist/observe/core/attribution.js +2318 -0
- package/dist/{types-cjs/core/constants.d.cts → observe/core/constants.js} +129 -73
- package/dist/{types-cjs/core/context.d.cts → observe/core/context.js} +37 -12
- package/dist/observe/core/core.js +1289 -0
- package/dist/observe/core/dev.js +232 -0
- package/dist/observe/core/effect.js +179 -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 +43 -0
- package/dist/observe/core/lanes.js +168 -0
- package/dist/observe/core/optimistic.js +438 -0
- package/dist/observe/core/owner.js +308 -0
- package/dist/observe/core/scheduler.js +1190 -0
- package/dist/observe/core/verdict.js +551 -0
- package/dist/observe/index.js +55 -0
- package/dist/observe/map.js +482 -0
- package/dist/{types-cjs/signals.d.cts → observe/signals.js} +295 -317
- package/dist/observe/store/index.js +38 -0
- package/dist/observe/store/next/optimistic.js +651 -0
- package/dist/observe/store/next/projection.js +242 -0
- package/dist/observe/store/next/reconcile.js +373 -0
- package/dist/observe/store/next/store.js +2213 -0
- package/dist/observe/store/next/target.js +51 -0
- package/dist/observe/store/store.js +323 -0
- package/dist/observe/store/storePath.js +103 -0
- package/dist/observe/store/utils.js +210 -0
- package/dist/prod/attribution.js +32 -0
- package/dist/prod/core/action.js +52 -32
- package/dist/prod/core/async.js +171 -127
- package/dist/prod/core/constants.js +24 -1
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +434 -291
- package/dist/prod/core/effect.js +54 -34
- package/dist/prod/core/external.js +2 -2
- package/dist/prod/core/graph.js +32 -32
- package/dist/prod/core/heap.js +49 -44
- package/dist/prod/core/lanes.js +45 -38
- package/dist/prod/core/optimistic.js +207 -73
- package/dist/prod/core/owner.js +43 -39
- package/dist/prod/core/scheduler.js +319 -190
- package/dist/prod/core/verdict.js +67 -67
- package/dist/prod/index.js +9 -3
- package/dist/prod/map.js +292 -141
- package/dist/prod/signals.js +7 -10
- package/dist/prod/store/next/optimistic.js +140 -125
- package/dist/prod/store/next/projection.js +3 -3
- package/dist/prod/store/next/reconcile.js +87 -84
- package/dist/prod/store/next/store.js +524 -280
- package/dist/prod/store/next/target.js +32 -10
- package/dist/prod/store/store.js +12 -14
- package/dist/prod/store/utils.js +36 -27
- package/dist/types/attribution.d.ts +15 -0
- package/dist/types/attribution.prod.d.ts +13 -0
- package/dist/types/core/async.d.ts +1 -1
- package/dist/types/core/attribution-hooks.d.ts +119 -6
- package/dist/types/core/attribution.d.ts +208 -53
- package/dist/types/core/constants.d.ts +23 -0
- package/dist/types/core/core.d.ts +7 -0
- package/dist/types/core/dev.d.ts +124 -26
- package/dist/types/core/effect.d.ts +8 -1
- package/dist/types/core/index.d.ts +2 -1
- package/dist/types/core/lanes.d.ts +8 -1
- package/dist/types/core/scheduler.d.ts +60 -0
- package/dist/types/core/types.d.ts +29 -9
- package/dist/types/index.d.ts +9 -2
- package/dist/types/map.d.ts +2 -0
- package/dist/types/store/index.d.ts +1 -0
- package/dist/types/store/next/store.d.ts +5 -0
- package/dist/types/store/next/target.d.ts +36 -9
- package/dist/types/store/utils.d.ts +7 -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/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
package/dist/prod/core/core.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { releaseFlightTeardown, handleAsync, clearStatus, parkLoadingWindow, notifyStatus, settlePendingSource, settleErroredDependents } from "./async.js";
|
|
2
2
|
|
|
3
|
-
import { EFFECT_TRACKED, EFFECT_USER, REACTIVE_OPTIMISTIC_DIRTY, CONFIG_OPTIMISTIC, NOT_PENDING, STATUS_UNINITIALIZED, STATUS_ERROR, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, CONFIG_SYNC, CONFIG_HAS_LANE,
|
|
3
|
+
import { EFFECT_TRACKED, EFFECT_USER, REACTIVE_OPTIMISTIC_DIRTY, CONFIG_OPTIMISTIC, NOT_PENDING, STATUS_UNINITIALIZED, STATUS_ERROR, STATUS_PENDING, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, CONFIG_SYNC, CONFIG_HAS_LANE, REACTIVE_MISSED_WAKE, REACTIVE_NONE, REACTIVE_SNAPSHOT_STALE, unwrapOverride, CONFIG_DIRECT_COMMIT, OVERRIDE_UNDEFINED, CONFIG_HAS_COMPANIONS, REACTIVE_LAZY, REACTIVE_DISPOSED, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_FRESH_READ, CONFIG_INPUTS_PUBLISHED, CONFIG_AUTHORITATIVE_READ, CONFIG_OVERRIDE_SUPERSEDED, CONFIG_AUTHORITATIVE_OBSERVED, CONFIG_HELD_TRUTH, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, defaultContext, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_NO_SNAPSHOT, NO_SNAPSHOT, CONFIG_HAS_SNAPSHOT, REACTIVE_MANUAL_WRITE, CONFIG_FW_CHILDREN, CONFIG_SLOT_NODE, STORE_SNAPSHOT_PROPS } from "./constants.js";
|
|
4
4
|
|
|
5
5
|
import { NotReadyError } from "./error.js";
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ import { trimStaleDeps, link, dormantNodes } from "./graph.js";
|
|
|
8
8
|
|
|
9
9
|
import { deleteFromHeap, queueFor, insertIntoHeapHeight, enqueueSub, markNode, markHeap, insertIntoHeap } from "./heap.js";
|
|
10
10
|
|
|
11
|
-
import { GlobalQueue, bumpNotifyEpoch, activeTransition, globalQueue, clock, insertSubs, queuePendingNode, runInTransition, schedule, notifyEpoch, dirtyQueue, projectionWriteActive, reaskArmed, armReaskClear } from "./scheduler.js";
|
|
11
|
+
import { GlobalQueue, bumpNotifyEpoch, activeTransition, globalQueue, clock, currentTransition, insertSubs, queuePendingNode, runInTransition, schedule, notifyEpoch, dirtyQueue, projectionWriteActive, reaskArmed, armReaskClear } from "./scheduler.js";
|
|
12
12
|
|
|
13
13
|
import "./invariants.js";
|
|
14
14
|
|
|
@@ -23,14 +23,14 @@ import { disposeChildren, markDisposal, inheritId } from "./owner.js";
|
|
|
23
23
|
// directly into the user queue ran in the SAME pass, read the old value, and
|
|
24
24
|
// nothing re-notified it when the value landed (#3291).
|
|
25
25
|
GlobalQueue.Fe = e => {
|
|
26
|
-
if (e.
|
|
26
|
+
if (e.Ce === EFFECT_TRACKED) {
|
|
27
27
|
deleteFromHeap(e, queueFor(e));
|
|
28
28
|
e.He = true;
|
|
29
29
|
e.C.enqueue(EFFECT_USER, e.Ve);
|
|
30
30
|
} else recompute(e);
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
GlobalQueue.
|
|
33
|
+
GlobalQueue.We = disposeChildren;
|
|
34
34
|
|
|
35
35
|
let tracking = false;
|
|
36
36
|
|
|
@@ -62,8 +62,8 @@ let snapshotSources = null;
|
|
|
62
62
|
|
|
63
63
|
function ownerInSnapshotScope(e) {
|
|
64
64
|
while (e) {
|
|
65
|
-
if (e.
|
|
66
|
-
e = e.
|
|
65
|
+
if (e.Me) return true;
|
|
66
|
+
e = e.qe;
|
|
67
67
|
}
|
|
68
68
|
return false;
|
|
69
69
|
}
|
|
@@ -74,20 +74,20 @@ function setSnapshotCapture(e) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
function markSnapshotScope(e) {
|
|
77
|
-
e.
|
|
77
|
+
e.Me = true;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
function releaseSnapshotScope(e) {
|
|
81
|
-
e.
|
|
81
|
+
e.Me = false;
|
|
82
82
|
releaseSubtree(e);
|
|
83
83
|
schedule();
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
function releaseSubtree(e) {
|
|
87
|
-
let t = e.
|
|
87
|
+
let t = e.Ye;
|
|
88
88
|
while (t) {
|
|
89
|
-
if (t.
|
|
90
|
-
t = t.
|
|
89
|
+
if (t.Me) {
|
|
90
|
+
t = t.Ze;
|
|
91
91
|
continue;
|
|
92
92
|
}
|
|
93
93
|
if (t.oe) {
|
|
@@ -96,19 +96,19 @@ function releaseSubtree(e) {
|
|
|
96
96
|
if (e.ie & REACTIVE_SNAPSHOT_STALE) {
|
|
97
97
|
e.ie &= ~REACTIVE_SNAPSHOT_STALE;
|
|
98
98
|
e.ie |= REACTIVE_DIRTY;
|
|
99
|
-
if (dirtyQueue.
|
|
99
|
+
if (dirtyQueue.Ke > e.Be) dirtyQueue.Ke = e.Be;
|
|
100
100
|
insertIntoHeap(e, dirtyQueue);
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
releaseSubtree(t);
|
|
104
|
-
t = t.
|
|
104
|
+
t = t.Ze;
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
function clearSnapshots() {
|
|
109
109
|
if (snapshotSources) {
|
|
110
110
|
for (const e of snapshotSources) {
|
|
111
|
-
delete e.o?.
|
|
111
|
+
delete e.o?.ze;
|
|
112
112
|
// StoreNode targets share one pre-initialized hidden class (see
|
|
113
113
|
// createStoreProxy) — assign undefined instead of deleting, and only
|
|
114
114
|
// when present so signal-node sources don't grow the field.
|
|
@@ -122,12 +122,12 @@ function clearSnapshots() {
|
|
|
122
122
|
function recompute(e, t = false) {
|
|
123
123
|
// §12d: any recompute can clean a marked subscriber — invalidate skips.
|
|
124
124
|
bumpNotifyEpoch();
|
|
125
|
-
const n = e.
|
|
125
|
+
const n = e.Ce;
|
|
126
126
|
if (!t) {
|
|
127
|
-
if (e.
|
|
127
|
+
if (e.ge && (!n || activeTransition) && activeTransition !== e.ge) globalQueue.initTransition(e.ge);
|
|
128
128
|
deleteFromHeap(e, queueFor(e));
|
|
129
129
|
if (e.o !== null) {
|
|
130
|
-
e.o.
|
|
130
|
+
e.o.Pe = null;
|
|
131
131
|
// Supersede is where an iterator flight dies (#3122): close it now.
|
|
132
132
|
// Its cleanup(close) registration may sit in a zombie-deferred
|
|
133
133
|
// disposal list that a held transition only drains when the
|
|
@@ -136,49 +136,50 @@ function recompute(e, t = false) {
|
|
|
136
136
|
releaseFlightTeardown(e);
|
|
137
137
|
}
|
|
138
138
|
// Tracked effects run after finalizePureQueue, so dispose immediately instead of deferring
|
|
139
|
-
if (e.
|
|
139
|
+
if (e.ge || n === EFFECT_TRACKED) disposeChildren(e); else if (e.Ye !== null || e.we !== null) {
|
|
140
140
|
markDisposal(e);
|
|
141
141
|
const t = ext(e);
|
|
142
|
-
t.
|
|
143
|
-
t.
|
|
144
|
-
e.
|
|
145
|
-
e.
|
|
146
|
-
e
|
|
142
|
+
t.Je = e.we;
|
|
143
|
+
t.Xe = e.Ye;
|
|
144
|
+
e.we = null;
|
|
145
|
+
e.Ye = null;
|
|
146
|
+
e.$e = 0;
|
|
147
147
|
} else ;
|
|
148
148
|
}
|
|
149
149
|
let i = !!(e.ie & REACTIVE_OPTIMISTIC_DIRTY);
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
150
|
+
const l = (e.T & CONFIG_OPTIMISTIC) !== 0 && e.o?.be !== NOT_PENDING && e.o?.be !== undefined;
|
|
151
|
+
const u = !!(e.S & STATUS_UNINITIALIZED);
|
|
152
|
+
// Capture both error and pending status before the compute clears them.
|
|
153
|
+
// A conditional can drop its pending source and recover to an unchanged
|
|
154
|
+
// value, leaving blocked dependents outside that source’s settle walk.
|
|
155
155
|
const o = e.S & STATUS_ERROR ? e.o?._ : undefined;
|
|
156
|
+
const s = e.S & STATUS_PENDING ? e.o?.le : undefined;
|
|
156
157
|
// Pending SOURCE-hood, captured before the compute clears status: a node
|
|
157
158
|
// whose own flight parked dependents self-registers in _pendingSources
|
|
158
159
|
// (notifyStatus, isSource). If this recompute supersedes that flight and
|
|
159
160
|
// settles synchronously, those dependents settle HERE — asyncWrite's
|
|
160
161
|
// settlePendingSource walk never runs for a landing that was preempted
|
|
161
162
|
// (#3181).
|
|
162
|
-
const
|
|
163
|
+
const a = e.o?.le?.has(e);
|
|
163
164
|
// Re-ask classification lives in the verdict module; capture the flag before
|
|
164
165
|
// the recompute wipes _flags below.
|
|
165
|
-
const
|
|
166
|
+
const r = (e.ie & REACTIVE_REASK) !== 0;
|
|
166
167
|
// Captured before the compute clears it on a sync landing: if that landing
|
|
167
168
|
// is transition-held below, the window must stay open until the hold
|
|
168
169
|
// commits (commitPendingNode) — a closed window plus a held value reads as
|
|
169
170
|
// a pending frame to live observers of the verdict (#2990).
|
|
170
|
-
const
|
|
171
|
+
const c = e.Ae;
|
|
171
172
|
const _ = context;
|
|
172
173
|
context = e;
|
|
173
|
-
e.
|
|
174
|
-
e.
|
|
174
|
+
e.et = null;
|
|
175
|
+
e.tt++;
|
|
175
176
|
e.ie = REACTIVE_RECOMPUTING_DEPS;
|
|
176
|
-
e.
|
|
177
|
-
let
|
|
178
|
-
let
|
|
177
|
+
e._e = clock;
|
|
178
|
+
let f = e.Ge === NOT_PENDING ? e.me : e.Ge;
|
|
179
|
+
let E = e.Be;
|
|
179
180
|
let I = false;
|
|
180
|
-
let
|
|
181
|
-
let
|
|
181
|
+
let N = tracking;
|
|
182
|
+
let T = currentOptimisticLane;
|
|
182
183
|
tracking = true;
|
|
183
184
|
// A computed's fn establishes its OWN dependencies, so it must never run
|
|
184
185
|
// inside a latest() read window: read() short-circuits through the
|
|
@@ -186,54 +187,61 @@ function recompute(e, t = false) {
|
|
|
186
187
|
// computed) inside latest(fn) came out permanently dependency-less (#2926).
|
|
187
188
|
// latestRead() already suspends the flag for its pull-recomputes; this
|
|
188
189
|
// covers creation-time computes and flushes that run inside the window.
|
|
189
|
-
const
|
|
190
|
+
const d = latestReadActive;
|
|
190
191
|
latestReadActive = false;
|
|
191
192
|
// Lane posture lives with the engine: OPTIMISTIC_DIRTY is only ever set by
|
|
192
193
|
// engine-driven paths, and _optimisticNodes is only pushed by
|
|
193
194
|
// _optimisticWrite, so the hook is installed whenever either gate holds.
|
|
194
195
|
if (i) {
|
|
195
|
-
const t = GlobalQueue.
|
|
196
|
+
const t = GlobalQueue.nt(e, true);
|
|
196
197
|
if (t) currentOptimisticLane = t;
|
|
197
198
|
// `false` = wake-only lane demotion: recompute plain so a mid-tick
|
|
198
199
|
// latest()/isPending() pull stages instead of direct-committing (#3009).
|
|
199
200
|
// The predicate lives with the engine (recomputeLane).
|
|
200
201
|
else if (t === false) i = false;
|
|
201
|
-
} else if (activeTransition && !t && activeTransition.
|
|
202
|
+
} else if (activeTransition && !t && activeTransition.it.length) {
|
|
202
203
|
// Lane adoption: parent-deeper-than-owned-child can run before its OPT-dirty
|
|
203
204
|
// child propagates. Walk deps once and inherit the OPT lane so this node
|
|
204
205
|
// recomputes under the right posture and propagates correctly.
|
|
205
|
-
const t = GlobalQueue.
|
|
206
|
+
const t = GlobalQueue.nt(e, false);
|
|
206
207
|
if (t) {
|
|
207
208
|
i = true;
|
|
208
209
|
currentOptimisticLane = t;
|
|
209
210
|
}
|
|
210
211
|
}
|
|
211
212
|
const S = n && n !== EFFECT_USER;
|
|
212
|
-
const
|
|
213
|
+
const A = stale;
|
|
213
214
|
if (S) stale = true;
|
|
215
|
+
// An effect recorded for this transaction's commit replay (it once read a
|
|
216
|
+
// node the transaction held and showed the committed value) and now
|
|
217
|
+
// recomputing UNDER the transaction sees its staged view: the value this
|
|
218
|
+
// run produces is applied by the commit itself, and the stale recording
|
|
219
|
+
// would publish the frame a second time. Drop it; the reads below re-record
|
|
220
|
+
// if they are served the committed view again (a lane's committed read).
|
|
221
|
+
if (n && activeTransition !== null && activeTransition.lt.size) activeTransition.lt.delete(e);
|
|
214
222
|
try {
|
|
215
223
|
if (!false && e.T & CONFIG_SYNC) {
|
|
216
|
-
|
|
217
|
-
if (e.o !== null) e.o.
|
|
218
|
-
e.
|
|
224
|
+
f = e.oe(f);
|
|
225
|
+
if (e.o !== null) e.o.Pe = null;
|
|
226
|
+
e.Ae = false;
|
|
219
227
|
} else {
|
|
220
228
|
// Snapshot `_inFlight` so we can detect whether `_fn` self-registered an async
|
|
221
229
|
// subscription (e.g. `createProjection` calls `handleAsync` from inside its body
|
|
222
230
|
// with a setter callback). In that case, the outer `handleAsync` call below would
|
|
223
231
|
// clobber the fresh subscription, so we skip it and let the internally-registered
|
|
224
232
|
// iteration drive updates.
|
|
225
|
-
const t = e.o?.
|
|
226
|
-
const n = e.oe(
|
|
233
|
+
const t = e.o?.Pe;
|
|
234
|
+
const n = e.oe(f);
|
|
227
235
|
const i = typeof n === "object" && n !== null;
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
if (!
|
|
231
|
-
if (e.o !== null) e.o.
|
|
236
|
+
const l = e.o?.Pe !== t;
|
|
237
|
+
f = l || !i ? n : handleAsync(e, n);
|
|
238
|
+
if (!l && !i) {
|
|
239
|
+
if (e.o !== null) e.o.Pe = null;
|
|
232
240
|
// A sync (non-object) return is the first real answer; async-shaped
|
|
233
241
|
// results clear inside handleAsync at their own landing points, and a
|
|
234
242
|
// self-registered flight (inFlightChanged — projections) clears when
|
|
235
243
|
// its internal handleAsync lands.
|
|
236
|
-
e.
|
|
244
|
+
e.Ae = false;
|
|
237
245
|
}
|
|
238
246
|
}
|
|
239
247
|
// On a status-free node clearStatus is a guaranteed no-op: every field
|
|
@@ -243,10 +251,10 @@ function recompute(e, t = false) {
|
|
|
243
251
|
if (e.S !== 0 || e.o !== null) clearStatus(e, t);
|
|
244
252
|
// _optimisticLane is only ever assigned by engine paths (CONFIG_HAS_LANE
|
|
245
253
|
// is their sticky presence mark).
|
|
246
|
-
if (e.T & CONFIG_HAS_LANE && e.o
|
|
254
|
+
if (e.T & CONFIG_HAS_LANE && e.o?.Oe) GlobalQueue.ut(e);
|
|
247
255
|
} catch (t) {
|
|
248
256
|
const n = t instanceof NotReadyError;
|
|
249
|
-
if (n && e.
|
|
257
|
+
if (n && e.Ae) {
|
|
250
258
|
// Loading window with an unready sync dependency: register for the
|
|
251
259
|
// source's settle (the settlePendingSource walk runs off
|
|
252
260
|
// _pendingSources + _blocked alone) but take NO read-visible pending
|
|
@@ -258,22 +266,22 @@ function recompute(e, t = false) {
|
|
|
258
266
|
} else {
|
|
259
267
|
// Track pending async in the lane (not the lane's source — it creates the lane
|
|
260
268
|
// but doesn't belong to it). Set lane BEFORE notifyStatus for downstream propagation.
|
|
261
|
-
if (n && currentOptimisticLane) GlobalQueue.
|
|
269
|
+
if (n && currentOptimisticLane) GlobalQueue.ot(e);
|
|
262
270
|
let i = false;
|
|
263
271
|
if (n) {
|
|
264
|
-
ext(e).
|
|
265
|
-
if (GlobalQueue.
|
|
272
|
+
ext(e).de = true;
|
|
273
|
+
if (GlobalQueue.st !== null) i = GlobalQueue.st(e, r);
|
|
266
274
|
}
|
|
267
|
-
notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t, undefined, n ? e.o
|
|
275
|
+
notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t, undefined, n ? e.o?.Oe : undefined);
|
|
268
276
|
// The replacement source is fully propagated now. If no new flight
|
|
269
277
|
// re-owned self, retire the superseded flight and its dependent copies.
|
|
270
|
-
if (n &&
|
|
278
|
+
if (n && a && !e.o?.Pe) settlePendingSource(e);
|
|
271
279
|
if (i) GlobalQueue.k(e);
|
|
272
280
|
}
|
|
273
281
|
} finally {
|
|
274
|
-
tracking =
|
|
275
|
-
latestReadActive =
|
|
276
|
-
if (S) stale =
|
|
282
|
+
tracking = N;
|
|
283
|
+
latestReadActive = d;
|
|
284
|
+
if (S) stale = A;
|
|
277
285
|
// Consume the missed-wake latch (#3037, set by insertSubs): a dep write
|
|
278
286
|
// landed beneath this pass on a link it had already validated. The wipe
|
|
279
287
|
// below must not key off DIRTY/CHECK — the read-time pull protocol
|
|
@@ -285,10 +293,16 @@ function recompute(e, t = false) {
|
|
|
285
293
|
}
|
|
286
294
|
if (!e.o?._) {
|
|
287
295
|
trimStaleDeps(e);
|
|
288
|
-
|
|
296
|
+
// INV-11 (#3330): the equality gate compares against the slot this run
|
|
297
|
+
// publishes to. An override-covered node publishes the override; a lane
|
|
298
|
+
// recompute (OPT-dirty) direct-commits `_value` — the lane's own reveal
|
|
299
|
+
// schedule — so a transaction-held `_pendingValue` that already equals
|
|
300
|
+
// the new result is not "unchanged": the screen still shows `_value`.
|
|
301
|
+
// Only a transaction-staged run compares against `_pendingValue`.
|
|
302
|
+
const r = l ? unwrapOverride(e.o?.be) : i || e.Ge === NOT_PENDING ? e.me : e.Ge;
|
|
289
303
|
let _ = false;
|
|
290
304
|
try {
|
|
291
|
-
_ = !n &&
|
|
305
|
+
_ = !n && u || !e.ve || !e.ve(r, f);
|
|
292
306
|
} catch (t) {
|
|
293
307
|
// A throwing user comparator is an error of this node's computation.
|
|
294
308
|
// Route it through the same status path as a compute-phase throw so
|
|
@@ -305,10 +319,33 @@ function recompute(e, t = false) {
|
|
|
305
319
|
e.He = !e.o?._;
|
|
306
320
|
// Reuse one bound runner per effect — runEffect no-ops on a stale
|
|
307
321
|
// `_modified`, so re-enqueueing the same function is harmless.
|
|
308
|
-
if (!t)
|
|
322
|
+
if (!t) {
|
|
323
|
+
e.C.enqueue(n, e.rt ??= GlobalQueue.ct.bind(null, e));
|
|
324
|
+
// Contested effect (#3322). Effects don't entangle transactions (a
|
|
325
|
+
// shared effect is not shared state), yet they have one value slot:
|
|
326
|
+
// when this write commits under a different transaction
|
|
327
|
+
// (`activeTransition`, null = mainline) than the one that produced the
|
|
328
|
+
// previous value (`_valueTransition`), that view is gone. Rather than
|
|
329
|
+
// merge the two, each commit re-derives the effect against its own
|
|
330
|
+
// committed world (Transition._contested, re-dirtied by
|
|
331
|
+
// finalizePureQueue ahead of the heap run). The previous owner always
|
|
332
|
+
// needs it if still live: its commit is silent (staging already
|
|
333
|
+
// notified) and the value it computed is gone. The new owner needs it
|
|
334
|
+
// too, for the same reason, unless it is mainline — mainline publishes
|
|
335
|
+
// what it computes. A previous owner that was mainline, or already
|
|
336
|
+
// committed, left nothing to protect.
|
|
337
|
+
let t = e._t;
|
|
338
|
+
if (t !== activeTransition) {
|
|
339
|
+
e._t = activeTransition;
|
|
340
|
+
if (t !== null && (t = currentTransition(t)) !== activeTransition && !t.ft) {
|
|
341
|
+
(t.Et ??= []).push(e);
|
|
342
|
+
if (activeTransition !== null) (activeTransition.Et ??= []).push(e);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
309
346
|
}
|
|
310
347
|
if (e.o?._) ; else if (_) {
|
|
311
|
-
const
|
|
348
|
+
const u = l ? e.o?.be : undefined;
|
|
312
349
|
if (t ||
|
|
313
350
|
// Plain sync flush (no transition on either side) commits effect
|
|
314
351
|
// values directly — the pending round-trip (queuePendingNode +
|
|
@@ -318,48 +355,62 @@ function recompute(e, t = false) {
|
|
|
318
355
|
// their own held transition: their applies deliver on a microtask,
|
|
319
356
|
// not the stashed queues, so a staged value would hand the immediate
|
|
320
357
|
// apply stale state — see CONFIG_DIRECT_COMMIT.
|
|
321
|
-
n && (activeTransition !== e.
|
|
322
|
-
e.
|
|
358
|
+
n && (activeTransition !== e.ge || activeTransition === null || e.T & CONFIG_DIRECT_COMMIT) || i) {
|
|
359
|
+
e.me = f;
|
|
323
360
|
// Lane-propagated correction: upstream data is fresh, correct the
|
|
324
361
|
// override unconditionally. The direct _value commit is the lane's
|
|
325
362
|
// own reveal schedule; drop any superseded older hold so its queued
|
|
326
363
|
// commit can't clobber the fresh value.
|
|
327
|
-
if (
|
|
328
|
-
ext(e).
|
|
329
|
-
e.
|
|
364
|
+
if (l && i) {
|
|
365
|
+
ext(e).be = f === undefined ? OVERRIDE_UNDEFINED : f;
|
|
366
|
+
e.Ge = NOT_PENDING;
|
|
330
367
|
}
|
|
331
368
|
} else {
|
|
332
|
-
e.
|
|
369
|
+
e.Ge = f;
|
|
333
370
|
// A window landing that gets held re-opens the window until the hold
|
|
334
371
|
// commits — the verdict's held-value branch is window-gated (#2990).
|
|
335
|
-
if (
|
|
372
|
+
if (c) e.Ae = true;
|
|
336
373
|
// Transition-held sync recompute is a write path like setSignal/asyncWrite,
|
|
337
374
|
// so sync derivations of held sources stay visible to isPending()/latest()
|
|
338
375
|
// (#2831). Both companion writes are transition-scoped (optimistic) and
|
|
339
376
|
// auto-revert/re-derive at commit. Skipped for plain flushes where the
|
|
340
377
|
// pending value commits before effects run.
|
|
341
|
-
if ((activeTransition || e.
|
|
378
|
+
if ((activeTransition || e.ge) && GlobalQueue.pe !== null) GlobalQueue.pe(e, f);
|
|
342
379
|
}
|
|
343
380
|
// insertSubs only walks _subs (no scheduling of its own), so a
|
|
344
381
|
// subscriber-less node has nothing to notify.
|
|
345
|
-
if (e.u !== null && (!
|
|
346
|
-
|
|
382
|
+
if (e.u !== null && (!l || i || e.o?.be !== u)) insertSubs(e, i || l);
|
|
383
|
+
// A18 supersession, sync twin of asyncWrite's override branch (#3331):
|
|
384
|
+
// this pass published truth that differs from the override (the gate
|
|
385
|
+
// compared against it) into the transaction-held slot. Whether the
|
|
386
|
+
// source is this node's own async or an upstream node it derives from
|
|
387
|
+
// synchronously makes no difference — "the source recomputed". The
|
|
388
|
+
// override stays displayed until the commit; the graph moves to the
|
|
389
|
+
// staged truth now (plain channel, lane demoted). Ordering: "a new
|
|
390
|
+
// value from the source" postdates the override — an override written
|
|
391
|
+
// in this same tick (optimisticWrite stamps `_overrideTime`) is the
|
|
392
|
+
// newer intent over whatever this pass derives from the batch's staged
|
|
393
|
+
// inputs, and is not superseded by it.
|
|
394
|
+
else if (l && !i && e.o.It !== clock) GlobalQueue.ye(e, f);
|
|
395
|
+
} else if (l) {
|
|
347
396
|
// Unchanged value (equals the override) recomputed while the override
|
|
348
397
|
// is active: _value may still be stale, so hold the authoritative value
|
|
349
398
|
// for commit on its own transition's schedule — invisibly (A17/A18).
|
|
350
|
-
if (e.
|
|
351
|
-
e.
|
|
352
|
-
if (
|
|
399
|
+
if (e.Ge === NOT_PENDING) queuePendingNode(e);
|
|
400
|
+
e.Ge = f;
|
|
401
|
+
if (c) e.Ae = true;
|
|
353
402
|
// see the held branch above (#2990)
|
|
354
|
-
//
|
|
355
|
-
//
|
|
356
|
-
//
|
|
357
|
-
//
|
|
358
|
-
//
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
403
|
+
// A confirmation after a supersession restores the override as the
|
|
404
|
+
// graph's value and notifies; a plain confirmation wakes only an
|
|
405
|
+
// authoritative-view reader (until()'s predicate, refresh()'s waiter)
|
|
406
|
+
// that observed this node past its override — "authoritative arrival
|
|
407
|
+
// equal to the override" is exactly the acknowledgment it waits for;
|
|
408
|
+
// A17 silence holds for every ordinary subscriber. Both live in the
|
|
409
|
+
// engine's supersedeOverride.
|
|
410
|
+
GlobalQueue.ye(e, f);
|
|
411
|
+
} else if (e.Be != E) {
|
|
412
|
+
for (let t = e.u; t !== null; t = t.Te) {
|
|
413
|
+
insertIntoHeapHeight(t.Ie, queueFor(t.Ie));
|
|
363
414
|
}
|
|
364
415
|
}
|
|
365
416
|
// Silent recovery: errored → unchanged value fires no notification, but
|
|
@@ -367,21 +418,26 @@ function recompute(e, t = false) {
|
|
|
367
418
|
// in an errored run and may sit on stale commits (#2949). Changed-value
|
|
368
419
|
// recoveries ride insertSubs above; a comparator throw re-errored the node
|
|
369
420
|
// (el._x?._error re-set), so this only runs on a genuinely clean recovery.
|
|
370
|
-
if (
|
|
421
|
+
if (!_ && !e.o?._) {
|
|
422
|
+
if (o !== undefined) settleErroredDependents(e, o);
|
|
423
|
+
// Self-registration (this node's own superseded flight) is the #3181
|
|
424
|
+
// sweep's business below — retiring it here too would walk twice.
|
|
425
|
+
if (s) for (const t of s) if (t !== e) settlePendingSource(e, t);
|
|
426
|
+
}
|
|
371
427
|
// #3181: a synchronous settle supersedes the old landing callback, so
|
|
372
428
|
// recompute owns its pending-source sweep. An uninitialized node without
|
|
373
429
|
// a replacement source still has no truth to reveal and must stay parked.
|
|
374
|
-
if (
|
|
430
|
+
if (a && !(e.S & (STATUS_PENDING | STATUS_UNINITIALIZED))) settlePendingSource(e);
|
|
375
431
|
}
|
|
376
|
-
currentOptimisticLane =
|
|
377
|
-
const
|
|
432
|
+
currentOptimisticLane = T;
|
|
433
|
+
const C = e.Ge !== NOT_PENDING || e.o !== null && (e.o.Xe !== null || e.o.Je !== null) || (e.S & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0;
|
|
378
434
|
// Override-covered holds (hasOverride) always queue: their commit belongs
|
|
379
435
|
// to their own transition's schedule (A18 re-rule) and is unobservable
|
|
380
436
|
// under the override (A17). Revert no longer commits anything, so an
|
|
381
437
|
// unqueued covered hold would leak (INV-7) once the revert clears
|
|
382
438
|
// _transition.
|
|
383
|
-
|
|
384
|
-
e.
|
|
439
|
+
C && (!t || e.S & STATUS_PENDING) && (!e.ge || l) && queuePendingNode(e);
|
|
440
|
+
e.ge && n && activeTransition !== e.ge && runInTransition(e.ge, () => recompute(e));
|
|
385
441
|
// Missed-wake reschedule (see the finally above): values this pass read
|
|
386
442
|
// before the nested commit are stale, so run again now that the heap will
|
|
387
443
|
// accept the node. Equality gates stop same-value landings from cascading,
|
|
@@ -402,9 +458,9 @@ function updateIfNecessary(e) {
|
|
|
402
458
|
// would resurrect it (#2983) — readers serve its last value.
|
|
403
459
|
if (e.ie & (REACTIVE_RECOMPUTING_DEPS | REACTIVE_DISPOSED)) return;
|
|
404
460
|
if (e.ie & REACTIVE_CHECK) {
|
|
405
|
-
for (let t = e.
|
|
406
|
-
const n = t.
|
|
407
|
-
const i = n.
|
|
461
|
+
for (let t = e.fe; t; t = t.ae) {
|
|
462
|
+
const n = t.Se;
|
|
463
|
+
const i = n.ce || n;
|
|
408
464
|
if (i.oe) {
|
|
409
465
|
updateIfNecessary(i);
|
|
410
466
|
}
|
|
@@ -413,7 +469,7 @@ function updateIfNecessary(e) {
|
|
|
413
469
|
}
|
|
414
470
|
}
|
|
415
471
|
}
|
|
416
|
-
if (e.ie & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY) || e.o?._ && e.
|
|
472
|
+
if (e.ie & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY) || e.o?._ && e._e < clock && !e.o?.Pe) {
|
|
417
473
|
recompute(e);
|
|
418
474
|
}
|
|
419
475
|
e.ie = e.ie & (REACTIVE_SNAPSHOT_STALE | REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT);
|
|
@@ -425,45 +481,53 @@ function computed(e, t) {
|
|
|
425
481
|
// `T | undefined` node is a real commit #0. The typeof guard tolerates
|
|
426
482
|
// non-object option values that older call shapes force through `as any`.
|
|
427
483
|
const i = t !== null && typeof t === "object" && "loadingValue" in t;
|
|
428
|
-
|
|
484
|
+
// Two literals, one per tier, selected at build time (the observe flag is
|
|
485
|
+
// a literal after replacement; the untaken branch is dead code). The observe
|
|
486
|
+
// literal is the prod literal plus its `_name` slot — a slot in the
|
|
487
|
+
// boilerplate, because a post-construction `self._name = …` forces a
|
|
488
|
+
// hidden-class transition and an out-of-object property store on EVERY node
|
|
489
|
+
// (measured: the whole of the observe tier's creation overhead). Keep the
|
|
490
|
+
// two in sync — the dist artifact test pins observe's key set to prod's
|
|
491
|
+
// plus `_name`.
|
|
492
|
+
const l = {
|
|
429
493
|
id: inheritId(t, n, context),
|
|
430
494
|
T: (n ? CONFIG_TRANSPARENT : 0) | (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (!context || t?.lazy ? CONFIG_AUTO_DISPOSE : 0) | (t?.sync ? CONFIG_SYNC : 0) | (t?.H ? CONFIG_NO_SNAPSHOT : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
431
|
-
|
|
432
|
-
|
|
495
|
+
ve: t?.equals ?? isEqual,
|
|
496
|
+
we: null,
|
|
433
497
|
C: context?.C ?? globalQueue,
|
|
434
|
-
|
|
435
|
-
|
|
498
|
+
xe: context?.xe ?? defaultContext,
|
|
499
|
+
$e: 0,
|
|
436
500
|
oe: e,
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
501
|
+
me: i ? t.loadingValue : undefined,
|
|
502
|
+
Be: 0,
|
|
503
|
+
Nt: undefined,
|
|
504
|
+
Tt: null,
|
|
505
|
+
fe: null,
|
|
506
|
+
et: null,
|
|
507
|
+
tt: 0,
|
|
444
508
|
u: null,
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
509
|
+
dt: null,
|
|
510
|
+
qe: context,
|
|
511
|
+
Ze: null,
|
|
512
|
+
St: null,
|
|
513
|
+
Ye: null,
|
|
450
514
|
ie: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
|
|
451
515
|
// A loadingValue node is born committed: commit #0 is already in _value.
|
|
452
516
|
S: i ? 0 : STATUS_UNINITIALIZED,
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
517
|
+
_e: clock,
|
|
518
|
+
Ge: NOT_PENDING,
|
|
519
|
+
ge: null,
|
|
520
|
+
At: -1,
|
|
521
|
+
Ae: i,
|
|
458
522
|
// Cold machinery (async/transition/optimistic/verdict slots) lives one
|
|
459
523
|
// hop away in the lazily-allocated extension — the core literal MUST
|
|
460
524
|
// stay under V8's in-object boundary (§12: past ~39 fields every
|
|
461
525
|
// allocation spills to a backing store and creation cost ~4x's).
|
|
462
526
|
o: null
|
|
463
527
|
};
|
|
464
|
-
if (t?.unobserved) ext(
|
|
465
|
-
setupComputedNode(
|
|
466
|
-
return
|
|
528
|
+
if (t?.unobserved) ext(l).Ct = t.unobserved;
|
|
529
|
+
setupComputedNode(l, t);
|
|
530
|
+
return l;
|
|
467
531
|
}
|
|
468
532
|
|
|
469
533
|
/** Lazily allocate a node's cold extension (ONE shape for signals and
|
|
@@ -471,26 +535,28 @@ function computed(e, t) {
|
|
|
471
535
|
* this; hot paths read `el._x?._field` gated by the _config presence bits.
|
|
472
536
|
* Never call ext() just to store a field's default. */ function ext(e) {
|
|
473
537
|
return e.o ??= {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
538
|
+
be: undefined,
|
|
539
|
+
Ot: undefined,
|
|
540
|
+
It: 0,
|
|
541
|
+
Rt: 0,
|
|
542
|
+
Oe: undefined,
|
|
543
|
+
Le: undefined,
|
|
544
|
+
Qe: undefined,
|
|
545
|
+
Gt: undefined,
|
|
480
546
|
t: 0,
|
|
481
|
-
|
|
482
|
-
|
|
547
|
+
Pe: null,
|
|
548
|
+
De: null,
|
|
483
549
|
_: undefined,
|
|
484
|
-
|
|
550
|
+
de: undefined,
|
|
485
551
|
le: undefined,
|
|
486
552
|
h: undefined,
|
|
487
|
-
|
|
553
|
+
Ue: false,
|
|
488
554
|
i: null,
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
555
|
+
Ct: undefined,
|
|
556
|
+
ze: undefined,
|
|
557
|
+
Je: null,
|
|
558
|
+
Xe: null,
|
|
559
|
+
Dt: undefined
|
|
494
560
|
};
|
|
495
561
|
}
|
|
496
562
|
|
|
@@ -499,43 +565,49 @@ function computed(e, t) {
|
|
|
499
565
|
* so V8 sees the full hidden class shape at construction time. Effects always run in lazy
|
|
500
566
|
* mode (recompute is called explicitly by `effect()`), so we hardcode the lazy bits and skip
|
|
501
567
|
* the auto-dispose CONFIG bit (effect() previously cleared it post-construction).
|
|
502
|
-
*/ function createEffectNode(e, t, n, i,
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
568
|
+
*/ function createEffectNode(e, t, n, i, l) {
|
|
569
|
+
const u = l?.transparent ?? false;
|
|
570
|
+
// Prod and observe boilerplates — see computed() for why the observe tier
|
|
571
|
+
// gets its `_name` as a literal slot rather than a write after the fact.
|
|
572
|
+
// The default label is the node kind (tracked effects relabel their computed
|
|
573
|
+
// in trackedEffect); the wrappers in signals.ts no longer spread a name into
|
|
574
|
+
// the options to get it.
|
|
575
|
+
const o = {
|
|
576
|
+
id: inheritId(l, u, context),
|
|
577
|
+
T: (u ? CONFIG_TRANSPARENT : 0) | (l?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (l?.sync ? CONFIG_SYNC : 0) | (l?.Pt ?? 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
578
|
+
ve: false,
|
|
579
|
+
we: null,
|
|
509
580
|
C: context?.C ?? globalQueue,
|
|
510
|
-
|
|
511
|
-
|
|
581
|
+
xe: context?.xe ?? defaultContext,
|
|
582
|
+
$e: 0,
|
|
512
583
|
oe: e,
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
584
|
+
me: undefined,
|
|
585
|
+
Be: 0,
|
|
586
|
+
Nt: undefined,
|
|
587
|
+
Tt: null,
|
|
588
|
+
fe: null,
|
|
589
|
+
et: null,
|
|
590
|
+
tt: 0,
|
|
520
591
|
u: null,
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
592
|
+
dt: null,
|
|
593
|
+
qe: context,
|
|
594
|
+
Ze: null,
|
|
595
|
+
St: null,
|
|
596
|
+
Ye: null,
|
|
526
597
|
ie: REACTIVE_LAZY,
|
|
527
598
|
S: STATUS_UNINITIALIZED,
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
599
|
+
_e: clock,
|
|
600
|
+
Ge: NOT_PENDING,
|
|
601
|
+
ge: null,
|
|
602
|
+
At: -1,
|
|
603
|
+
Ae: false,
|
|
533
604
|
He: false,
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
605
|
+
Ft: undefined,
|
|
606
|
+
ht: t,
|
|
607
|
+
vt: n,
|
|
608
|
+
Ht: undefined,
|
|
609
|
+
Ce: i,
|
|
610
|
+
_t: null,
|
|
539
611
|
o: null
|
|
540
612
|
};
|
|
541
613
|
// Effects dispatch status through the SHARED notifier (statusNotifierOf,
|
|
@@ -543,7 +615,7 @@ function computed(e, t) {
|
|
|
543
615
|
// allocation on EVERY effect at creation (an alloc + 19 field stores,
|
|
544
616
|
// +23% effect creation, caught by the creation benches). Only genuinely
|
|
545
617
|
// per-node channels (boundaries) live on _x.
|
|
546
|
-
if (
|
|
618
|
+
if (l?.unobserved) ext(o).Ct = l.unobserved;
|
|
547
619
|
setupComputedNode(o, lazyOptions);
|
|
548
620
|
return o;
|
|
549
621
|
}
|
|
@@ -566,7 +638,7 @@ function setEffectStatusNotify(e) {
|
|
|
566
638
|
* per-node field did when every effect carried one. */ function statusNotifierOf(e) {
|
|
567
639
|
const t = e.o?.h;
|
|
568
640
|
if (t !== undefined) return t;
|
|
569
|
-
return e.
|
|
641
|
+
return e.Ce ? effectStatusNotify ?? undefined : undefined;
|
|
570
642
|
}
|
|
571
643
|
|
|
572
644
|
const lazyOptions = {
|
|
@@ -574,24 +646,24 @@ const lazyOptions = {
|
|
|
574
646
|
};
|
|
575
647
|
|
|
576
648
|
function setupComputedNode(e, t) {
|
|
577
|
-
e.
|
|
578
|
-
const n = context?.
|
|
649
|
+
e.Tt = e;
|
|
650
|
+
const n = context?.gt ? context.bt : context;
|
|
579
651
|
if (context) {
|
|
580
|
-
const t = context.
|
|
652
|
+
const t = context.Ye;
|
|
581
653
|
if (t === null) {
|
|
582
|
-
context.
|
|
654
|
+
context.Ye = e;
|
|
583
655
|
} else {
|
|
584
|
-
e.
|
|
585
|
-
t.
|
|
586
|
-
context.
|
|
656
|
+
e.Ze = t;
|
|
657
|
+
t.St = e;
|
|
658
|
+
context.Ye = e;
|
|
587
659
|
}
|
|
588
660
|
}
|
|
589
|
-
if (n) e.
|
|
590
|
-
if (GlobalQueue.
|
|
661
|
+
if (n) e.Be = n.Be + 1;
|
|
662
|
+
if (GlobalQueue.kt !== null) GlobalQueue.kt(e);
|
|
591
663
|
!t?.lazy && recompute(e, true);
|
|
592
664
|
if (snapshotCaptureActive && !t?.lazy) {
|
|
593
665
|
if (!(e.S & STATUS_PENDING) && !(e.T & CONFIG_NO_SNAPSHOT)) {
|
|
594
|
-
ext(e).
|
|
666
|
+
ext(e).ze = e.me === undefined ? NO_SNAPSHOT : e.me;
|
|
595
667
|
e.T |= CONFIG_HAS_SNAPSHOT;
|
|
596
668
|
snapshotSources.add(e);
|
|
597
669
|
}
|
|
@@ -599,32 +671,34 @@ function setupComputedNode(e, t) {
|
|
|
599
671
|
}
|
|
600
672
|
|
|
601
673
|
function signal(e, t, n = null) {
|
|
674
|
+
// Prod and observe boilerplates — see computed(). The observe literal adds
|
|
675
|
+
// `_name` and `_owner` (the creating owner, stamped by registerGraph for
|
|
676
|
+
// createSignal nodes so ownerPath can locate signal subjects; null here,
|
|
677
|
+
// and staying null on internal signals — one shape either way).
|
|
602
678
|
const i = {
|
|
603
|
-
|
|
679
|
+
ve: t?.equals ?? isEqual,
|
|
604
680
|
T: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.H ? CONFIG_NO_SNAPSHOT : 0),
|
|
605
|
-
|
|
681
|
+
me: e,
|
|
606
682
|
u: null,
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
683
|
+
dt: null,
|
|
684
|
+
_e: clock,
|
|
685
|
+
ce: n,
|
|
686
|
+
Ee: n?.o?.i || null,
|
|
687
|
+
Vt: null,
|
|
688
|
+
Ge: NOT_PENDING,
|
|
612
689
|
// Signal-literal diet (§12e): NO _time/_fn/_statusFlags slots. Stores
|
|
613
690
|
// materialize one signal per touched leaf, so signal bytes are store
|
|
614
691
|
// bytes. _time is write-only on signals (every read site is computed-
|
|
615
692
|
// typed error-retry gating); _fn/_statusFlags read falsy-identically as
|
|
616
693
|
// missing properties on the shared paths (undefined masks to 0).
|
|
617
|
-
|
|
618
|
-
|
|
694
|
+
ge: null,
|
|
695
|
+
At: -1,
|
|
619
696
|
o: null
|
|
620
697
|
};
|
|
621
|
-
if (t?.unobserved) ext(i).
|
|
622
|
-
if (n)
|
|
623
|
-
ext(n).i = i;
|
|
624
|
-
n.T |= CONFIG_FW_CHILDREN;
|
|
625
|
-
}
|
|
698
|
+
if (t?.unobserved) ext(i).Ct = t.unobserved;
|
|
699
|
+
if (n) linkFirewallChild(n, i);
|
|
626
700
|
if (snapshotCaptureActive && !(i.T & CONFIG_NO_SNAPSHOT) && !((n?.S ?? 0) & STATUS_PENDING)) {
|
|
627
|
-
ext(i).
|
|
701
|
+
ext(i).ze = e === undefined ? NO_SNAPSHOT : e;
|
|
628
702
|
i.T |= CONFIG_HAS_SNAPSHOT;
|
|
629
703
|
snapshotSources.add(i);
|
|
630
704
|
}
|
|
@@ -649,34 +723,60 @@ function signal(e, t, n = null) {
|
|
|
649
723
|
slotUnobservedHook = e;
|
|
650
724
|
}
|
|
651
725
|
|
|
652
|
-
|
|
726
|
+
/** Push a new node onto its firewall's child chain (the literal already
|
|
727
|
+
* points `_nextChild` at the old head). Doubly linked so a released leaf
|
|
728
|
+
* unlinks in O(1) — the chain is walked per mark of the projection and
|
|
729
|
+
* would otherwise grow by one node per leaf ever read (#3351). */ function linkFirewallChild(e, t) {
|
|
730
|
+
const n = t.Ee;
|
|
731
|
+
if (n !== null) n.Vt = t;
|
|
732
|
+
ext(e).i = t;
|
|
733
|
+
e.T |= CONFIG_FW_CHILDREN;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/** Release a firewall child the store no longer addresses (unobserved sweep
|
|
737
|
+
* dropped it from its target's cache): unlink it from the chain so the
|
|
738
|
+
* projection stops retaining it and its last value. The node keeps its own
|
|
739
|
+
* `_nextChild` so a walk that is mid-chain on it still terminates. Nodes in
|
|
740
|
+
* `_companionChildren` stay there — companions are permanent by contract
|
|
741
|
+
* and snap through that set, not the chain. */ function unlinkFirewallChild(e) {
|
|
742
|
+
const t = e;
|
|
743
|
+
const n = t.ce;
|
|
744
|
+
if (!n) return;
|
|
745
|
+
const i = t.Vt;
|
|
746
|
+
const l = t.Ee;
|
|
747
|
+
if (i !== null) i.Ee = l; else if (n.o.i === t) n.o.i = l;
|
|
748
|
+
if (l !== null) l.Vt = i;
|
|
749
|
+
t.Vt = null;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
function slotSignal(e, t, n, i, l, u = null) {
|
|
753
|
+
// Prod and observe boilerplates — see computed(). The store relabels the
|
|
754
|
+
// observe slot (`store.<key>`) when the attribution engine is installed.
|
|
653
755
|
const o = {
|
|
654
|
-
|
|
756
|
+
ve: t,
|
|
655
757
|
T: CONFIG_OWNED_WRITE | CONFIG_SLOT_NODE,
|
|
656
|
-
|
|
758
|
+
me: e,
|
|
657
759
|
u: null,
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
760
|
+
dt: null,
|
|
761
|
+
_e: clock,
|
|
762
|
+
ce: u,
|
|
763
|
+
Ee: u?.o?.i || null,
|
|
764
|
+
Vt: null,
|
|
765
|
+
Ge: NOT_PENDING,
|
|
766
|
+
ge: null,
|
|
767
|
+
At: -1,
|
|
665
768
|
o: null,
|
|
666
769
|
// Slot backrefs: what the equals/unobserved closures used to capture.
|
|
667
|
-
|
|
668
|
-
|
|
770
|
+
Ut: n,
|
|
771
|
+
xt: i,
|
|
669
772
|
// Store read-path caches, pre-shaped (were post-construction expandos).
|
|
670
|
-
acc:
|
|
773
|
+
acc: l,
|
|
671
774
|
px: undefined,
|
|
672
775
|
pxv: undefined
|
|
673
776
|
};
|
|
674
|
-
if (
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
}
|
|
678
|
-
if (snapshotCaptureActive && !((l?.S ?? 0) & STATUS_PENDING)) {
|
|
679
|
-
ext(o).Ye = e === undefined ? NO_SNAPSHOT : e;
|
|
777
|
+
if (u) linkFirewallChild(u, o);
|
|
778
|
+
if (snapshotCaptureActive && !((u?.S ?? 0) & STATUS_PENDING)) {
|
|
779
|
+
ext(o).ze = e === undefined ? NO_SNAPSHOT : e;
|
|
680
780
|
o.T |= CONFIG_HAS_SNAPSHOT;
|
|
681
781
|
snapshotSources.add(o);
|
|
682
782
|
}
|
|
@@ -685,14 +785,14 @@ function slotSignal(e, t, n, i, u, l = null) {
|
|
|
685
785
|
|
|
686
786
|
function optimisticSignal(e, t) {
|
|
687
787
|
const n = signal(e, t);
|
|
688
|
-
ext(n).
|
|
788
|
+
ext(n).be = NOT_PENDING;
|
|
689
789
|
n.T |= CONFIG_OPTIMISTIC;
|
|
690
790
|
return n;
|
|
691
791
|
}
|
|
692
792
|
|
|
693
793
|
function optimisticComputed(e, t) {
|
|
694
794
|
const n = computed(e, t);
|
|
695
|
-
ext(n).
|
|
795
|
+
ext(n).be = NOT_PENDING;
|
|
696
796
|
n.T |= CONFIG_OPTIMISTIC;
|
|
697
797
|
return n;
|
|
698
798
|
}
|
|
@@ -723,11 +823,11 @@ function isEqual(e, t) {
|
|
|
723
823
|
* );
|
|
724
824
|
* ```
|
|
725
825
|
*/ function untrack(e, t) {
|
|
726
|
-
if (GlobalQueue.
|
|
826
|
+
if (GlobalQueue.Lt === null && !tracking && true) return e();
|
|
727
827
|
const n = tracking;
|
|
728
828
|
tracking = false;
|
|
729
829
|
try {
|
|
730
|
-
if (GlobalQueue.
|
|
830
|
+
if (GlobalQueue.Lt !== null) return GlobalQueue.Lt(e);
|
|
731
831
|
return e();
|
|
732
832
|
} finally {
|
|
733
833
|
tracking = n;
|
|
@@ -777,14 +877,14 @@ function isEqual(e, t) {
|
|
|
777
877
|
* Pay-for-use: reached through GlobalQueue._notifyAuthoritativeObservers,
|
|
778
878
|
* installed at first until() call — apps that never use until() shake it.
|
|
779
879
|
*/ function notifyAuthoritativeObservers(e) {
|
|
780
|
-
for (let t = e.u; t !== null; t = t.
|
|
781
|
-
const e = t.
|
|
880
|
+
for (let t = e.u; t !== null; t = t.Te) {
|
|
881
|
+
const e = t.Ie;
|
|
782
882
|
if (!(e.T & CONFIG_AUTHORITATIVE_READ)) continue;
|
|
783
883
|
// Missed-wake latch (#3037), same contract as insertSubs: the reader may
|
|
784
884
|
// itself have pulled this recompute (updateIfNecessary from its own
|
|
785
885
|
// read), and the heap refuses RECOMPUTING nodes — latch so recompute's
|
|
786
886
|
// tail reschedules it with the staged value visible.
|
|
787
|
-
if (e.ie & REACTIVE_RECOMPUTING_DEPS && t.
|
|
887
|
+
if (e.ie & REACTIVE_RECOMPUTING_DEPS && t.yt === e.tt && t !== e.et) e.ie |= REACTIVE_MISSED_WAKE;
|
|
788
888
|
enqueueSub(e);
|
|
789
889
|
}
|
|
790
890
|
schedule();
|
|
@@ -795,19 +895,44 @@ function isEqual(e, t) {
|
|
|
795
895
|
* before its first read (same late-binding contract as the optimistic engine;
|
|
796
896
|
* the gating bit is only ever set by such a read, so the `!` call sites are
|
|
797
897
|
* safe once every setter installs, #3303). */ function installAuthoritativeRead() {
|
|
798
|
-
if (GlobalQueue.
|
|
898
|
+
if (GlobalQueue.Qt === null) GlobalQueue.Qt = notifyAuthoritativeObservers;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* Stale-reader term of the value selections below: a render effect reading a
|
|
903
|
+
* node some OTHER live transaction has staged sees the committed value. The
|
|
904
|
+
* commit is silent — the staging walk was the notification — so a reader
|
|
905
|
+
* that linked AFTER that walk (an effect created during the hold, a store
|
|
906
|
+
* key first read under it) would show the old value past the reveal: record
|
|
907
|
+
* it for the transaction's commit replay (the `_gatedSubs` contract lanes
|
|
908
|
+
* already use). An effect the transaction itself computed re-derives at its
|
|
909
|
+
* commit on its own (parked run, or the contested re-derive, #3322) and is
|
|
910
|
+
* not recorded — replaying it too would publish the frame twice.
|
|
911
|
+
*/ function heldFromStale(e, t) {
|
|
912
|
+
const n = e.ge;
|
|
913
|
+
if (n === null || n === activeTransition) return false;
|
|
914
|
+
const i = currentTransition(n);
|
|
915
|
+
const l = t._t;
|
|
916
|
+
if (l == null || currentTransition(l) !== i) i.lt.add(t);
|
|
917
|
+
return true;
|
|
799
918
|
}
|
|
800
919
|
|
|
801
920
|
function readNodeFast(e) {
|
|
802
|
-
if (latestReadActive || pendingCheckActive || e.oe || e.
|
|
921
|
+
if (latestReadActive || pendingCheckActive || e.oe || e.ce || e.o?.be !== undefined || e.o?.ze !== undefined || activeTransition !== null || currentOptimisticLane !== null || snapshotCaptureActive || false) return READ_SLOW;
|
|
803
922
|
let t = context;
|
|
804
|
-
if (t?.
|
|
923
|
+
if (t?.gt) t = t.bt;
|
|
805
924
|
if (t && tracking) link(e, t);
|
|
806
925
|
// Children-forbidden readers (createTrackedEffect / onSettled callbacks) get
|
|
807
926
|
// committed visibility: like the effect half of createEffect and event
|
|
808
927
|
// handlers, effect-phase code never observes its own unsettled write — the
|
|
809
928
|
// write lands in the same flush's continuation (#3006).
|
|
810
|
-
|
|
929
|
+
// A stale reader (render effect) recomputing with no transaction active is
|
|
930
|
+
// mainline: a write staged by a live transaction (`_transition` stamped —
|
|
931
|
+
// ambient staging never is) stays masked until that transaction's reveal,
|
|
932
|
+
// the same rule the slow path applies (#3322). Without it a zombie
|
|
933
|
+
// recompute, or a commit-time re-derive of a contested effect, published
|
|
934
|
+
// another transaction's uncommitted value.
|
|
935
|
+
return !t || e.Ge === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN || stale && heldFromStale(e, t) ? e.me : e.Ge;
|
|
811
936
|
}
|
|
812
937
|
|
|
813
938
|
function read(e) {
|
|
@@ -815,33 +940,34 @@ function read(e) {
|
|
|
815
940
|
// Checked before isPending so that isPending(() => latest(x)) checks
|
|
816
941
|
// the _pendingSignal of _latestValueComputed (async in flight) rather
|
|
817
942
|
// than the original node (which stays "pending" while held in a transition).
|
|
818
|
-
if (latestReadActive) return GlobalQueue.
|
|
943
|
+
if (latestReadActive) return GlobalQueue.wt(e);
|
|
819
944
|
let t = context;
|
|
820
|
-
if (t?.
|
|
945
|
+
if (t?.gt) t = t.bt;
|
|
821
946
|
const n = e;
|
|
822
|
-
const i = e.
|
|
823
|
-
const
|
|
947
|
+
const i = e.ce;
|
|
948
|
+
const l = i || e;
|
|
824
949
|
// Handle isPending() mode: collect pending state while preserving normal read semantics.
|
|
825
950
|
// Probe mode is suspended while preparing the node so nested reads during a
|
|
826
951
|
// recompute don't collect into the probe.
|
|
827
952
|
if (pendingCheckActive) {
|
|
828
|
-
GlobalQueue.
|
|
953
|
+
GlobalQueue.Wt(e, t, l, i);
|
|
829
954
|
} else if (typeof n.oe === "function") {
|
|
830
955
|
prepareComputed(e, false);
|
|
831
956
|
}
|
|
832
|
-
if (!n.oe &&
|
|
957
|
+
if (!n.oe && l === e && e.o?.be === undefined && e.o?.ze === undefined && activeTransition === null && currentOptimisticLane === null && !snapshotCaptureActive && true) {
|
|
833
958
|
if (t && tracking) link(e, t);
|
|
834
|
-
// Committed visibility for children-forbidden readers
|
|
835
|
-
|
|
959
|
+
// Committed visibility for children-forbidden readers and for stale
|
|
960
|
+
// readers of a foreign transaction's staged write — see readNodeFast.
|
|
961
|
+
return !t || e.Ge === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN || stale && heldFromStale(e, t) ? e.me : e.Ge;
|
|
836
962
|
}
|
|
837
963
|
if (t && tracking) {
|
|
838
964
|
link(e, t, pendingCheckActive);
|
|
839
|
-
if (
|
|
965
|
+
if (l.oe) {
|
|
840
966
|
const n = queueFor(e);
|
|
841
|
-
if (
|
|
967
|
+
if (l.Be >= n.Ke) {
|
|
842
968
|
markNode(t);
|
|
843
969
|
markHeap(n);
|
|
844
|
-
updateIfNecessary(
|
|
970
|
+
updateIfNecessary(l);
|
|
845
971
|
}
|
|
846
972
|
// Fresh-pull readers (awaitable refresh's waiter) recompute a dirty
|
|
847
973
|
// source inline even when the height gate defers to the flush: the
|
|
@@ -849,66 +975,69 @@ function read(e) {
|
|
|
849
975
|
// never read the PRE-re-ask value as settled. Self-guarded: a clean
|
|
850
976
|
// node no-ops and updateIfNecessary refuses disposed nodes (#2983) —
|
|
851
977
|
// a dead target serves its last value, which is already quiescent.
|
|
852
|
-
else if (t.T & CONFIG_FRESH_READ) updateIfNecessary(
|
|
853
|
-
const i =
|
|
978
|
+
else if (t.T & CONFIG_FRESH_READ) updateIfNecessary(l);
|
|
979
|
+
const i = l.Be;
|
|
854
980
|
// parent check is shallow, might need to be recursive
|
|
855
|
-
if (i >= t.
|
|
856
|
-
t.
|
|
981
|
+
if (i >= t.Be && e.qe !== t) {
|
|
982
|
+
t.Be = i + 1;
|
|
857
983
|
}
|
|
858
984
|
}
|
|
859
985
|
}
|
|
860
|
-
if (
|
|
861
|
-
|
|
986
|
+
if (l.S & STATUS_PENDING) {
|
|
987
|
+
// A reader landing on a pending node throws — the reveal that discovered
|
|
988
|
+
// the flight holds on it (A15: observed async settles as one unit) — with
|
|
989
|
+
// one carve-out: a stale (render) reader of a node pending in some OTHER
|
|
990
|
+
// transaction keeps showing the node's committed value, no entanglement
|
|
991
|
+
// (parallel transactions; the reader is recorded for that transaction's
|
|
992
|
+
// commit replay, `heldFromStale`). The carve-out is sound only while the
|
|
993
|
+
// committed value is coherent with the visible frame, i.e. while the
|
|
994
|
+
// flight's inputs are themselves unpublished: the stamp alone does not
|
|
995
|
+
// say so (it is pending-node bookkeeping), so it is refused when the
|
|
996
|
+
// inputs are on screen — committed by a batch that left the flight in
|
|
997
|
+
// the air (CONFIG_INPUTS_PUBLISHED, #3305) or revealed through a lane
|
|
998
|
+
// (optimistic / latest, #3334) — and the reader holds instead. An
|
|
999
|
+
// UNINITIALIZED node has no committed value to show and holds too
|
|
1000
|
+
// (firewall-backed store reads always did; plain memos since the #3043
|
|
1001
|
+
// port): falling through served `undefined` as if settled and stranded
|
|
1002
|
+
// the reader outside both transactions, so it never re-ran.
|
|
1003
|
+
if (t && !(stale && !(l.S & STATUS_UNINITIALIZED) && !(l.T & CONFIG_INPUTS_PUBLISHED) && !(l.T & CONFIG_HAS_LANE && GlobalQueue.Mt(l)) && heldFromStale(l, t))) {
|
|
862
1004
|
// Per-lane suspension lives with the engine (a non-null lane implies it
|
|
863
1005
|
// is installed): under a lane, only same-lane pending async without an
|
|
864
1006
|
// active override throws — plus uninitialized sources regardless of
|
|
865
1007
|
// lane (#3276); that check rides laneSuspends so floor bundles don't
|
|
866
1008
|
// pay for it.
|
|
867
|
-
if (currentOptimisticLane === null || GlobalQueue.
|
|
1009
|
+
if (currentOptimisticLane === null || GlobalQueue.qt(l)) {
|
|
868
1010
|
if (!tracking && e !== t) link(e, t);
|
|
869
|
-
throw
|
|
1011
|
+
throw l.o?._;
|
|
870
1012
|
}
|
|
871
|
-
} else if (t &&
|
|
872
|
-
|
|
873
|
-
// normally keeps showing the node's committed value instead of
|
|
874
|
-
// entangling the two transactions — but an uninitialized node has no
|
|
875
|
-
// committed value to show. Suspend on it (firewall-backed store reads
|
|
876
|
-
// always took this branch; plain memos now do too): the reader
|
|
877
|
-
// registers as a reporter of that source, and its pending-node stamp
|
|
878
|
-
// ties it to the active transaction, so the two transactions merge
|
|
879
|
-
// when the source settles. Falling through served `undefined` as if
|
|
880
|
-
// settled and stranded the reader outside both transactions, so it
|
|
881
|
-
// never re-ran when either landed (#3043 port).
|
|
882
|
-
if (!tracking && e !== t) link(e, t);
|
|
883
|
-
throw u.o?._;
|
|
884
|
-
} else if (!t && u.S & STATUS_UNINITIALIZED) {
|
|
885
|
-
throw u.o?._;
|
|
1013
|
+
} else if (!t && l.S & STATUS_UNINITIALIZED) {
|
|
1014
|
+
throw l.o?._;
|
|
886
1015
|
}
|
|
887
1016
|
}
|
|
888
1017
|
// `owner` is the computed itself, or the firewall behind a store node —
|
|
889
1018
|
// firewall-backed reads follow the same rules (memo parity, #2897 ruling):
|
|
890
1019
|
// an errored derive throws for every late reader instead of silently
|
|
891
1020
|
// serving node values (the seed, or last-good data after a failed refetch).
|
|
892
|
-
if (
|
|
1021
|
+
if (l.oe && l.S & STATUS_ERROR) {
|
|
893
1022
|
// Only a genuine reactive re-read may retry an errored async source:
|
|
894
1023
|
// - tracking: owned/tracked scope only (never events / `untrack` / effect side-effect phase)
|
|
895
1024
|
// - !pendingCheckActive: an `isPending` probe observes the error, never refetches
|
|
896
1025
|
// - owner._time < clock: only on a later cycle than the one the error was found
|
|
897
|
-
if (tracking && !pendingCheckActive &&
|
|
898
|
-
recompute(
|
|
1026
|
+
if (tracking && !pendingCheckActive && l._e < clock) {
|
|
1027
|
+
recompute(l);
|
|
899
1028
|
return read(e);
|
|
900
|
-
} else throw
|
|
1029
|
+
} else throw l.o?._;
|
|
901
1030
|
}
|
|
902
1031
|
if (snapshotCaptureActive && t && t.T & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
903
|
-
const n = e.o?.
|
|
1032
|
+
const n = e.o?.ze;
|
|
904
1033
|
if (n !== undefined) {
|
|
905
1034
|
const i = n === NO_SNAPSHOT ? undefined : n;
|
|
906
|
-
const
|
|
907
|
-
if (
|
|
1035
|
+
const l = e.Ge !== NOT_PENDING ? e.Ge : e.me;
|
|
1036
|
+
if (l !== i) t.ie |= REACTIVE_SNAPSHOT_STALE;
|
|
908
1037
|
return i;
|
|
909
1038
|
}
|
|
910
1039
|
}
|
|
911
|
-
if (e.o?.
|
|
1040
|
+
if (e.o?.be !== undefined && e.o?.be !== NOT_PENDING) {
|
|
912
1041
|
// A17: the override IS the value for every reader — except an authoritative
|
|
913
1042
|
// reader (until()'s predicate carries CONFIG_AUTHORITATIVE_READ): it must
|
|
914
1043
|
// observe independently-arriving truth, and serving it the caller's own
|
|
@@ -919,7 +1048,15 @@ function read(e) {
|
|
|
919
1048
|
// authoritative — optimism never lives there); the sticky mark makes the
|
|
920
1049
|
// A17-silent "landing equals override" paths notify this node's subs so
|
|
921
1050
|
// the reader re-runs when truth arrives.
|
|
922
|
-
if (!(t && t.T & CONFIG_AUTHORITATIVE_READ))
|
|
1051
|
+
if (!(t && t.T & CONFIG_AUTHORITATIVE_READ)) {
|
|
1052
|
+
// A18 supersession (#3331): the node's own source answered with a
|
|
1053
|
+
// DIFFERENT value. The optimism is over for the graph — a tracked
|
|
1054
|
+
// reader sees the staged truth — while the override remains the
|
|
1055
|
+
// DISPLAYED value for untracked reads (and for a stale reader of some
|
|
1056
|
+
// other transaction). The selection lives with the engine.
|
|
1057
|
+
if (t && e.T & CONFIG_OVERRIDE_SUPERSEDED) return GlobalQueue.Yt(e);
|
|
1058
|
+
return unwrapOverride(e.o?.be);
|
|
1059
|
+
}
|
|
923
1060
|
e.T |= CONFIG_AUTHORITATIVE_OBSERVED;
|
|
924
1061
|
}
|
|
925
1062
|
// Entanglement gate: a reader recomputing under an optimistic lane that reads
|
|
@@ -929,8 +1066,8 @@ function read(e) {
|
|
|
929
1066
|
// _pendingValue for correct fetching. The sub is recorded for replay at commit
|
|
930
1067
|
// so it re-runs with the new committed view. (Gate details live with the
|
|
931
1068
|
// engine — a non-null lane implies it is installed.)
|
|
932
|
-
if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.
|
|
933
|
-
return e.
|
|
1069
|
+
if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.Zt(e, l, t)) {
|
|
1070
|
+
return e.me;
|
|
934
1071
|
}
|
|
935
1072
|
// In optimistic lane context, return _value for optimistic/lane-assigned signals
|
|
936
1073
|
// and for regular signals in stale mode (render effects). Non-stale readers (user
|
|
@@ -938,7 +1075,7 @@ function read(e) {
|
|
|
938
1075
|
// (The lane-context clause lives with the engine.) Children-forbidden readers
|
|
939
1076
|
// (createTrackedEffect / onSettled callbacks) get committed visibility — see
|
|
940
1077
|
// readNodeFast (#3006).
|
|
941
|
-
const
|
|
1078
|
+
const u = !t || currentOptimisticLane !== null && GlobalQueue.Kt(e, l, t) || e.Ge === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN || stale && heldFromStale(e, t) ||
|
|
942
1079
|
// A17 for HELD truth (#3164, see CONFIG_HELD_TRUTH): staged confirming
|
|
943
1080
|
// truth — fold-staged onto an armed family, or entangle-stolen by an
|
|
944
1081
|
// awaited until() — is masked from ordinary readers until its
|
|
@@ -947,11 +1084,11 @@ function read(e) {
|
|
|
947
1084
|
// compose override + staged truth into a state no timeline contains).
|
|
948
1085
|
// Authoritative readers (until()'s predicate) and latest() see the
|
|
949
1086
|
// staged truth — the tunnel that keeps the hold deadlock-free.
|
|
950
|
-
e.T & CONFIG_HELD_TRUTH && !latestReadActive && !(t.T & CONFIG_AUTHORITATIVE_READ) ? e.
|
|
1087
|
+
e.T & CONFIG_HELD_TRUTH && !latestReadActive && !(t.T & CONFIG_AUTHORITATIVE_READ) ? e.me : e.Ge;
|
|
951
1088
|
// Record that this isPending() probe observed the fresh pending value, so
|
|
952
1089
|
// the probe doesn't pair "pending" with the new value (#2831).
|
|
953
|
-
if (pendingCheckActive) GlobalQueue.
|
|
954
|
-
if (!t &&
|
|
1090
|
+
if (pendingCheckActive) GlobalQueue.jt(e, u);
|
|
1091
|
+
if (!t && l === e && typeof n.oe === "function" && e.T & CONFIG_AUTO_DISPOSE && !(l.S & STATUS_PENDING) && !e.u) {
|
|
955
1092
|
// Deferred, not inline (#3078): an inline unobserved() here made untracked
|
|
956
1093
|
// reads destructive — dispose on this read, full revival recompute on the
|
|
957
1094
|
// next — so consecutive reads could answer differently with no write in
|
|
@@ -961,41 +1098,47 @@ function read(e) {
|
|
|
961
1098
|
dormantNodes.add(e);
|
|
962
1099
|
schedule();
|
|
963
1100
|
}
|
|
964
|
-
return
|
|
1101
|
+
return u;
|
|
965
1102
|
}
|
|
966
1103
|
|
|
967
1104
|
function setSignal(e, t) {
|
|
968
|
-
if (e.
|
|
1105
|
+
if (e.ge && activeTransition !== e.ge) globalQueue.initTransition(e.ge);
|
|
969
1106
|
// The optimistic write path lives with the engine: only optimisticSignal /
|
|
970
1107
|
// optimisticComputed callers and optimistic store nodes carry an
|
|
971
1108
|
// _overrideValue slot (flagged by CONFIG_OPTIMISTIC — a masked read of the
|
|
972
1109
|
// always-present config instead of a missing-property probe), and every
|
|
973
1110
|
// module that installs one installs the engine first.
|
|
974
|
-
if (e.T & CONFIG_OPTIMISTIC
|
|
975
|
-
|
|
1111
|
+
if (e.T & CONFIG_OPTIMISTIC) {
|
|
1112
|
+
if (!projectionWriteActive) return GlobalQueue.Bt(e, t);
|
|
1113
|
+
// An authoritative store landing on an override-covered node: the store
|
|
1114
|
+
// twin of asyncWrite's override branch, decided by the engine (#3331).
|
|
1115
|
+
const n = e.o?.be;
|
|
1116
|
+
if (n !== undefined && n !== NOT_PENDING) return GlobalQueue.zt(e, t);
|
|
1117
|
+
}
|
|
1118
|
+
const n = e.Ge === NOT_PENDING ? e.me : e.Ge;
|
|
976
1119
|
if (typeof t === "function") t = t(n);
|
|
977
1120
|
// Uninitialized check first: the first commit has no previous value, so the
|
|
978
1121
|
// user comparator must not run against `undefined` (matches recompute).
|
|
979
|
-
const i = !!(e.S & STATUS_UNINITIALIZED) || !e.
|
|
1122
|
+
const i = !!(e.S & STATUS_UNINITIALIZED) || !e.ve || !e.ve(n, t);
|
|
980
1123
|
if (!i) return t;
|
|
981
|
-
const
|
|
982
|
-
if (!
|
|
983
|
-
e.
|
|
1124
|
+
const l = e.Ge !== NOT_PENDING;
|
|
1125
|
+
if (!l) queuePendingNode(e);
|
|
1126
|
+
e.Ge = t;
|
|
984
1127
|
// syncCompanions only pokes _pendingSignal/_latestValueComputed — with
|
|
985
1128
|
// neither companion present the call is a guaranteed no-op (companions are
|
|
986
1129
|
// only ever created, never removed, and creating one installs the hook and
|
|
987
1130
|
// sets CONFIG_HAS_COMPANIONS — one masked read replaces two optional-field
|
|
988
1131
|
// probes on every write).
|
|
989
|
-
e.T & CONFIG_HAS_COMPANIONS && GlobalQueue.
|
|
1132
|
+
e.T & CONFIG_HAS_COMPANIONS && GlobalQueue.pe !== null && GlobalQueue.pe(e, t);
|
|
990
1133
|
// _time is a computed-only slot (§12e): writing it on a signal would fork
|
|
991
1134
|
// the lean shape. Every read site is computed-typed.
|
|
992
|
-
if (e.oe !== undefined) e.
|
|
1135
|
+
if (e.oe !== undefined) e._e = clock;
|
|
993
1136
|
// Staged-rewrite fast path (§12d): a re-write to a node whose subscribers
|
|
994
1137
|
// were already walked — and where nothing has recomputed or linked since
|
|
995
1138
|
// (epoch) — re-stages the value and stops. The walk is idempotent (subs
|
|
996
1139
|
// marked, heap entries flag-guarded, effects queued once); lane and reask
|
|
997
1140
|
// contexts change what a walk MEANS, so they always walk.
|
|
998
|
-
if (
|
|
1141
|
+
if (l && e.At === notifyEpoch && currentOptimisticLane === null && !reaskArmed) return t;
|
|
999
1142
|
insertSubs(e);
|
|
1000
1143
|
schedule();
|
|
1001
1144
|
return t;
|
|
@@ -1009,12 +1152,12 @@ function setSignal(e, t) {
|
|
|
1009
1152
|
* cleanup point.
|
|
1010
1153
|
*/ function suppressComputedRecompute(e) {
|
|
1011
1154
|
deleteFromHeap(e, queueFor(e));
|
|
1012
|
-
if (!(e.ie & REACTIVE_MANUAL_WRITE) && e.
|
|
1155
|
+
if (!(e.ie & REACTIVE_MANUAL_WRITE) && e.Ge === NOT_PENDING) {
|
|
1013
1156
|
queuePendingNode(e);
|
|
1014
1157
|
schedule();
|
|
1015
1158
|
}
|
|
1016
1159
|
e.ie = e.ie & -4 | REACTIVE_MANUAL_WRITE;
|
|
1017
|
-
e.
|
|
1160
|
+
e.Jt = clock;
|
|
1018
1161
|
}
|
|
1019
1162
|
|
|
1020
1163
|
/**
|
|
@@ -1084,7 +1227,7 @@ function staleValues(e, t = true) {
|
|
|
1084
1227
|
// refresh is a later, explicit re-ask and lifts the mask — otherwise
|
|
1085
1228
|
// any setStore early in an action silently swallows every refresh()
|
|
1086
1229
|
// for the rest of the transaction (#3026).
|
|
1087
|
-
if (e.
|
|
1230
|
+
if (e.Jt === clock) return;
|
|
1088
1231
|
e.ie &= ~REACTIVE_MANUAL_WRITE;
|
|
1089
1232
|
// The lift falls through to the re-ask classification below. The held
|
|
1090
1233
|
// write's value change already rides the transaction; the refetch it
|
|
@@ -1112,4 +1255,4 @@ function staleValues(e, t = true) {
|
|
|
1112
1255
|
}
|
|
1113
1256
|
}
|
|
1114
1257
|
|
|
1115
|
-
export { READ_SLOW, clearSnapshots, computed, context, createEffectNode, currentOptimisticLane, effectStatusNotify, ext, installAuthoritativeRead, isEqual, latestReadActive, markRefresh, markSnapshotScope, notifyAuthoritativeObservers, optimisticComputed, optimisticSignal, pendingCheckActive, prepareComputed, read, readNodeFast, recompute, releaseSnapshotScope, runWithOwner, setContextInternal, setEffectStatusNotify, setLatestReadActive, setMemo, setPendingCheckActive, setSignal, setSlotUnobserved, setSnapshotCapture, signal, slotSignal, slotUnobservedHook, snapshotCaptureActive, snapshotSources, stale, staleValues, statusNotifierOf, suppressComputedRecompute, tracking, untrack };
|
|
1258
|
+
export { READ_SLOW, clearSnapshots, computed, context, createEffectNode, currentOptimisticLane, effectStatusNotify, ext, installAuthoritativeRead, isEqual, latestReadActive, markRefresh, markSnapshotScope, notifyAuthoritativeObservers, optimisticComputed, optimisticSignal, pendingCheckActive, prepareComputed, read, readNodeFast, recompute, releaseSnapshotScope, runWithOwner, setContextInternal, setEffectStatusNotify, setLatestReadActive, setMemo, setPendingCheckActive, setSignal, setSlotUnobserved, setSnapshotCapture, signal, slotSignal, slotUnobservedHook, snapshotCaptureActive, snapshotSources, stale, staleValues, statusNotifierOf, suppressComputedRecompute, tracking, unlinkFirewallChild, untrack };
|