@solidjs/signals 2.0.0-rc.4 → 2.0.0-rc.5
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.js +1169 -186
- package/dist/node.cjs +2041 -1167
- package/dist/prod/boundaries.js +4 -1
- package/dist/prod/core/async.js +124 -95
- package/dist/prod/core/constants.js +55 -1
- package/dist/prod/core/core.js +297 -222
- package/dist/prod/core/effect.js +28 -28
- package/dist/prod/core/error.js +13 -1
- package/dist/prod/core/external.js +2 -2
- package/dist/prod/core/graph.js +27 -27
- package/dist/prod/core/heap.js +30 -30
- package/dist/prod/core/lanes.js +32 -32
- package/dist/prod/core/optimistic.js +54 -54
- package/dist/prod/core/owner.js +34 -34
- package/dist/prod/core/scheduler.js +318 -137
- package/dist/prod/core/verdict.js +112 -59
- package/dist/prod/index.js +3 -3
- package/dist/prod/map.js +106 -106
- package/dist/prod/signals.js +253 -25
- package/dist/prod/store/next/optimistic.js +262 -123
- package/dist/prod/store/next/patch.js +6 -6
- package/dist/prod/store/next/projection.js +23 -19
- package/dist/prod/store/next/store.js +129 -35
- package/dist/prod/store/store.js +2 -2
- package/dist/types/core/async.d.ts +2 -0
- package/dist/types/core/attribution.d.ts +9 -4
- package/dist/types/core/constants.d.ts +54 -0
- package/dist/types/core/core.d.ts +19 -20
- package/dist/types/core/error.d.ts +9 -0
- package/dist/types/core/index.d.ts +2 -2
- package/dist/types/core/scheduler.d.ts +34 -0
- package/dist/types/core/types.d.ts +12 -0
- package/dist/types/index.d.ts +3 -3
- package/dist/types/signals.d.ts +108 -0
- package/dist/types/store/next/optimistic.d.ts +13 -10
- package/dist/types/store/next/projection.d.ts +1 -1
- package/dist/types/store/next/store.d.ts +22 -0
- package/dist/types/store/next/target.d.ts +15 -0
- package/dist/types-cjs/core/async.d.cts +2 -0
- package/dist/types-cjs/core/attribution.d.cts +9 -4
- package/dist/types-cjs/core/constants.d.cts +54 -0
- package/dist/types-cjs/core/core.d.cts +19 -20
- package/dist/types-cjs/core/error.d.cts +9 -0
- package/dist/types-cjs/core/index.d.cts +2 -2
- package/dist/types-cjs/core/scheduler.d.cts +34 -0
- package/dist/types-cjs/core/types.d.cts +12 -0
- package/dist/types-cjs/index.d.cts +3 -3
- package/dist/types-cjs/signals.d.cts +108 -0
- package/dist/types-cjs/store/next/optimistic.d.cts +13 -10
- package/dist/types-cjs/store/next/projection.d.cts +1 -1
- package/dist/types-cjs/store/next/store.d.cts +22 -0
- package/dist/types-cjs/store/next/target.d.cts +15 -0
- package/package.json +1 -1
package/dist/prod/core/core.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { handleAsync, clearStatus, parkLoadingWindow, notifyStatus, settleErroredDependents } from "./async.js";
|
|
1
|
+
import { releaseFlightTeardown, handleAsync, clearStatus, parkLoadingWindow, notifyStatus, settleErroredDependents, settlePendingSource } from "./async.js";
|
|
2
2
|
|
|
3
|
-
import { EFFECT_TRACKED, 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, 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_AUTHORITATIVE_OBSERVED, CONFIG_HAS_COMPANIONS, REACTIVE_LAZY, REACTIVE_DISPOSED, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_FRESH_READ, CONFIG_AUTHORITATIVE_READ, 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, CONFIG_FW_CHILDREN, NO_SNAPSHOT, CONFIG_HAS_SNAPSHOT, REACTIVE_MANUAL_WRITE, STORE_SNAPSHOT_PROPS, EFFECT_USER } from "./constants.js";
|
|
4
4
|
|
|
5
5
|
import { NotReadyError } from "./error.js";
|
|
6
6
|
|
|
@@ -8,15 +8,15 @@ 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, activeTransition, globalQueue, clock, insertSubs, queuePendingNode, runInTransition, schedule, notifyEpoch, dirtyQueue,
|
|
11
|
+
import { GlobalQueue, bumpNotifyEpoch, activeTransition, globalQueue, clock, insertSubs, queuePendingNode, runInTransition, schedule, notifyEpoch, dirtyQueue, projectionWriteActive, reaskArmed, armReaskClear } from "./scheduler.js";
|
|
12
12
|
|
|
13
13
|
import "./invariants.js";
|
|
14
14
|
|
|
15
15
|
import { disposeChildren, markDisposal, inheritId } from "./owner.js";
|
|
16
16
|
|
|
17
|
-
GlobalQueue.
|
|
17
|
+
GlobalQueue.Fe = recompute;
|
|
18
18
|
|
|
19
|
-
GlobalQueue.
|
|
19
|
+
GlobalQueue.He = disposeChildren;
|
|
20
20
|
|
|
21
21
|
let tracking = false;
|
|
22
22
|
|
|
@@ -48,7 +48,7 @@ let snapshotSources = null;
|
|
|
48
48
|
|
|
49
49
|
function ownerInSnapshotScope(e) {
|
|
50
50
|
while (e) {
|
|
51
|
-
if (e.
|
|
51
|
+
if (e.Ve) return true;
|
|
52
52
|
e = e.ke;
|
|
53
53
|
}
|
|
54
54
|
return false;
|
|
@@ -60,20 +60,20 @@ function setSnapshotCapture(e) {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function markSnapshotScope(e) {
|
|
63
|
-
e.
|
|
63
|
+
e.Ve = true;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
function releaseSnapshotScope(e) {
|
|
67
|
-
e.
|
|
67
|
+
e.Ve = false;
|
|
68
68
|
releaseSubtree(e);
|
|
69
69
|
schedule();
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
function releaseSubtree(e) {
|
|
73
|
-
let t = e.
|
|
73
|
+
let t = e.xe;
|
|
74
74
|
while (t) {
|
|
75
|
-
if (t.
|
|
76
|
-
t = t.
|
|
75
|
+
if (t.Ve) {
|
|
76
|
+
t = t.Le;
|
|
77
77
|
continue;
|
|
78
78
|
}
|
|
79
79
|
if (t.oe) {
|
|
@@ -82,19 +82,19 @@ function releaseSubtree(e) {
|
|
|
82
82
|
if (e.ie & REACTIVE_SNAPSHOT_STALE) {
|
|
83
83
|
e.ie &= ~REACTIVE_SNAPSHOT_STALE;
|
|
84
84
|
e.ie |= REACTIVE_DIRTY;
|
|
85
|
-
if (dirtyQueue.
|
|
85
|
+
if (dirtyQueue.Qe > e.Me) dirtyQueue.Qe = e.Me;
|
|
86
86
|
insertIntoHeap(e, dirtyQueue);
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
releaseSubtree(t);
|
|
90
|
-
t = t.
|
|
90
|
+
t = t.Le;
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
function clearSnapshots() {
|
|
95
95
|
if (snapshotSources) {
|
|
96
96
|
for (const e of snapshotSources) {
|
|
97
|
-
delete e.o?.
|
|
97
|
+
delete e.o?.We;
|
|
98
98
|
// StoreNode targets share one pre-initialized hidden class (see
|
|
99
99
|
// createStoreProxy) — assign undefined instead of deleting, and only
|
|
100
100
|
// when present so signal-node sources don't grow the field.
|
|
@@ -108,48 +108,63 @@ function clearSnapshots() {
|
|
|
108
108
|
function recompute(e, t = false) {
|
|
109
109
|
// §12d: any recompute can clean a marked subscriber — invalidate skips.
|
|
110
110
|
bumpNotifyEpoch();
|
|
111
|
-
const n = e.
|
|
111
|
+
const n = e.he;
|
|
112
112
|
if (!t) {
|
|
113
|
-
if (e.
|
|
113
|
+
if (e.Ae && (!n || activeTransition) && activeTransition !== e.Ae) globalQueue.initTransition(e.Ae);
|
|
114
114
|
deleteFromHeap(e, queueFor(e));
|
|
115
|
-
if (e.o !== null)
|
|
115
|
+
if (e.o !== null) {
|
|
116
|
+
e.o.Ie = null;
|
|
117
|
+
// Supersede is where an iterator flight dies (#3122): close it now.
|
|
118
|
+
// Its cleanup(close) registration may sit in a zombie-deferred
|
|
119
|
+
// disposal list that a held transition only drains when the
|
|
120
|
+
// SUPERSEDING flight settles — cancellation must not wait for the
|
|
121
|
+
// work that replaced it. Idempotent with the cleanup-channel close.
|
|
122
|
+
releaseFlightTeardown(e);
|
|
123
|
+
}
|
|
116
124
|
// Tracked effects run after finalizePureQueue, so dispose immediately instead of deferring
|
|
117
|
-
if (e.
|
|
125
|
+
if (e.Ae || n === EFFECT_TRACKED) disposeChildren(e); else if (e.xe !== null || e.pe !== null) {
|
|
118
126
|
markDisposal(e);
|
|
119
127
|
const t = ext(e);
|
|
120
|
-
t.
|
|
121
|
-
t.
|
|
122
|
-
e.
|
|
123
|
-
e.
|
|
124
|
-
e.
|
|
128
|
+
t.qe = e.pe;
|
|
129
|
+
t.Ye = e.xe;
|
|
130
|
+
e.pe = null;
|
|
131
|
+
e.xe = null;
|
|
132
|
+
e.Ze = 0;
|
|
125
133
|
} else ;
|
|
126
134
|
}
|
|
127
135
|
let i = !!(e.ie & REACTIVE_OPTIMISTIC_DIRTY);
|
|
128
|
-
const u = (e.T & CONFIG_OPTIMISTIC) !== 0 && e.o?.
|
|
136
|
+
const u = (e.T & CONFIG_OPTIMISTIC) !== 0 && e.o?.Oe !== NOT_PENDING && e.o?.Oe !== undefined;
|
|
129
137
|
const l = !!(e.S & STATUS_UNINITIALIZED);
|
|
130
138
|
// Outgoing error, captured before the compute clears status: if this run
|
|
131
139
|
// recovers to an unchanged value, dependents still holding this object must
|
|
132
140
|
// be swept (settleErroredDependents, #2949).
|
|
133
141
|
const o = e.S & STATUS_ERROR ? e.o?._ : undefined;
|
|
142
|
+
// Pending SOURCE-hood, captured before the compute clears status: a node
|
|
143
|
+
// whose own flight parked dependents self-registers in _pendingSources
|
|
144
|
+
// (notifyStatus, isSource). If this recompute supersedes that flight and
|
|
145
|
+
// settles synchronously, those dependents settle HERE — asyncWrite's
|
|
146
|
+
// settlePendingSource walk never runs for a landing that was preempted
|
|
147
|
+
// (#3181).
|
|
148
|
+
const s = (e.S & STATUS_PENDING) !== 0 && e.o?.le?.has(e) === true;
|
|
134
149
|
// Re-ask classification lives in the verdict module; capture the flag before
|
|
135
150
|
// the recompute wipes _flags below.
|
|
136
|
-
const
|
|
151
|
+
const a = (e.ie & REACTIVE_REASK) !== 0;
|
|
137
152
|
// Captured before the compute clears it on a sync landing: if that landing
|
|
138
153
|
// is transition-held below, the window must stay open until the hold
|
|
139
154
|
// commits (commitPendingNode) — a closed window plus a held value reads as
|
|
140
155
|
// a pending frame to live observers of the verdict (#2990).
|
|
141
|
-
const
|
|
142
|
-
const
|
|
156
|
+
const r = e.Ne;
|
|
157
|
+
const c = context;
|
|
143
158
|
context = e;
|
|
144
|
-
e.
|
|
145
|
-
e.
|
|
159
|
+
e.je = null;
|
|
160
|
+
e.Ke++;
|
|
146
161
|
e.ie = REACTIVE_RECOMPUTING_DEPS;
|
|
147
162
|
e.Te = clock;
|
|
148
|
-
let
|
|
149
|
-
let
|
|
150
|
-
let
|
|
151
|
-
let
|
|
152
|
-
let
|
|
163
|
+
let _ = e.Pe === NOT_PENDING ? e.be : e.Pe;
|
|
164
|
+
let f = e.Me;
|
|
165
|
+
let I = false;
|
|
166
|
+
let E = tracking;
|
|
167
|
+
let N = currentOptimisticLane;
|
|
153
168
|
tracking = true;
|
|
154
169
|
// A computed's fn establishes its OWN dependencies, so it must never run
|
|
155
170
|
// inside a latest() read window: read() short-circuits through the
|
|
@@ -157,54 +172,54 @@ function recompute(e, t = false) {
|
|
|
157
172
|
// computed) inside latest(fn) came out permanently dependency-less (#2926).
|
|
158
173
|
// latestRead() already suspends the flag for its pull-recomputes; this
|
|
159
174
|
// covers creation-time computes and flushes that run inside the window.
|
|
160
|
-
const
|
|
175
|
+
const T = latestReadActive;
|
|
161
176
|
latestReadActive = false;
|
|
162
177
|
// Lane posture lives with the engine: OPTIMISTIC_DIRTY is only ever set by
|
|
163
178
|
// engine-driven paths, and _optimisticNodes is only pushed by
|
|
164
179
|
// _optimisticWrite, so the hook is installed whenever either gate holds.
|
|
165
180
|
if (i) {
|
|
166
|
-
const t = GlobalQueue.
|
|
181
|
+
const t = GlobalQueue.Be(e, true);
|
|
167
182
|
if (t) currentOptimisticLane = t;
|
|
168
183
|
// `false` = wake-only lane demotion: recompute plain so a mid-tick
|
|
169
184
|
// latest()/isPending() pull stages instead of direct-committing (#3009).
|
|
170
185
|
// The predicate lives with the engine (recomputeLane).
|
|
171
186
|
else if (t === false) i = false;
|
|
172
|
-
} else if (activeTransition && !t && activeTransition.
|
|
187
|
+
} else if (activeTransition && !t && activeTransition.ze.length) {
|
|
173
188
|
// Lane adoption: parent-deeper-than-owned-child can run before its OPT-dirty
|
|
174
189
|
// child propagates. Walk deps once and inherit the OPT lane so this node
|
|
175
190
|
// recomputes under the right posture and propagates correctly.
|
|
176
|
-
const t = GlobalQueue.
|
|
191
|
+
const t = GlobalQueue.Be(e, false);
|
|
177
192
|
if (t) {
|
|
178
193
|
i = true;
|
|
179
194
|
currentOptimisticLane = t;
|
|
180
195
|
}
|
|
181
196
|
}
|
|
182
|
-
const
|
|
183
|
-
const
|
|
184
|
-
if (
|
|
197
|
+
const S = n && n !== EFFECT_USER;
|
|
198
|
+
const d = stale;
|
|
199
|
+
if (S) stale = true;
|
|
185
200
|
try {
|
|
186
201
|
if (!false && e.T & CONFIG_SYNC) {
|
|
187
|
-
|
|
188
|
-
if (e.o !== null) e.o.
|
|
189
|
-
e.
|
|
202
|
+
_ = e.oe(_);
|
|
203
|
+
if (e.o !== null) e.o.Ie = null;
|
|
204
|
+
e.Ne = false;
|
|
190
205
|
} else {
|
|
191
206
|
// Snapshot `_inFlight` so we can detect whether `_fn` self-registered an async
|
|
192
207
|
// subscription (e.g. `createProjection` calls `handleAsync` from inside its body
|
|
193
208
|
// with a setter callback). In that case, the outer `handleAsync` call below would
|
|
194
209
|
// clobber the fresh subscription, so we skip it and let the internally-registered
|
|
195
210
|
// iteration drive updates.
|
|
196
|
-
const t = e.o?.
|
|
197
|
-
const n = e.oe(
|
|
211
|
+
const t = e.o?.Ie;
|
|
212
|
+
const n = e.oe(_);
|
|
198
213
|
const i = typeof n === "object" && n !== null;
|
|
199
|
-
const u = e.o?.
|
|
200
|
-
|
|
214
|
+
const u = e.o?.Ie !== t;
|
|
215
|
+
_ = u || !i ? n : handleAsync(e, n);
|
|
201
216
|
if (!u && !i) {
|
|
202
|
-
if (e.o !== null) e.o.
|
|
217
|
+
if (e.o !== null) e.o.Ie = null;
|
|
203
218
|
// A sync (non-object) return is the first real answer; async-shaped
|
|
204
219
|
// results clear inside handleAsync at their own landing points, and a
|
|
205
220
|
// self-registered flight (inFlightChanged — projections) clears when
|
|
206
221
|
// its internal handleAsync lands.
|
|
207
|
-
e.
|
|
222
|
+
e.Ne = false;
|
|
208
223
|
}
|
|
209
224
|
}
|
|
210
225
|
// On a status-free node clearStatus is a guaranteed no-op: every field
|
|
@@ -214,10 +229,10 @@ function recompute(e, t = false) {
|
|
|
214
229
|
if (e.S !== 0 || e.o !== null) clearStatus(e, t);
|
|
215
230
|
// _optimisticLane is only ever assigned by engine paths (CONFIG_HAS_LANE
|
|
216
231
|
// is their sticky presence mark).
|
|
217
|
-
if (e.T & CONFIG_HAS_LANE && e.o?.
|
|
232
|
+
if (e.T & CONFIG_HAS_LANE && e.o?.Je) GlobalQueue.Xe(e);
|
|
218
233
|
} catch (t) {
|
|
219
234
|
const n = t instanceof NotReadyError;
|
|
220
|
-
if (n && e.
|
|
235
|
+
if (n && e.Ne) {
|
|
221
236
|
// Loading window with an unready sync dependency: register for the
|
|
222
237
|
// source's settle (the settlePendingSource walk runs off
|
|
223
238
|
// _pendingSources + _blocked alone) but take NO read-visible pending
|
|
@@ -233,30 +248,30 @@ function recompute(e, t = false) {
|
|
|
233
248
|
let i = false;
|
|
234
249
|
if (n) {
|
|
235
250
|
ext(e).fe = true;
|
|
236
|
-
if (GlobalQueue.
|
|
251
|
+
if (GlobalQueue.et !== null) i = GlobalQueue.et(e, a);
|
|
237
252
|
}
|
|
238
|
-
notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t, undefined, n ? e.o?.
|
|
253
|
+
notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t, undefined, n ? e.o?.Je : undefined);
|
|
239
254
|
if (i) GlobalQueue.k(e);
|
|
240
255
|
}
|
|
241
256
|
} finally {
|
|
242
|
-
tracking =
|
|
243
|
-
latestReadActive =
|
|
244
|
-
if (
|
|
257
|
+
tracking = E;
|
|
258
|
+
latestReadActive = T;
|
|
259
|
+
if (S) stale = d;
|
|
245
260
|
// Consume the missed-wake latch (#3037, set by insertSubs): a dep write
|
|
246
261
|
// landed beneath this pass on a link it had already validated. The wipe
|
|
247
262
|
// below must not key off DIRTY/CHECK — the read-time pull protocol
|
|
248
263
|
// (markNode(c) in read()) marks the running node as part of ordinary
|
|
249
264
|
// bookkeeping, and those marks are correctly discarded here.
|
|
250
|
-
|
|
265
|
+
I = (e.ie & REACTIVE_MISSED_WAKE) !== 0;
|
|
251
266
|
e.ie = REACTIVE_NONE | (t ? e.ie & REACTIVE_SNAPSHOT_STALE : 0);
|
|
252
|
-
context =
|
|
267
|
+
context = c;
|
|
253
268
|
}
|
|
254
269
|
if (!e.o?._) {
|
|
255
270
|
trimStaleDeps(e);
|
|
256
|
-
const
|
|
257
|
-
let
|
|
271
|
+
const a = u ? unwrapOverride(e.o?.Oe) : e.Pe === NOT_PENDING ? e.be : e.Pe;
|
|
272
|
+
let c = false;
|
|
258
273
|
try {
|
|
259
|
-
|
|
274
|
+
c = !n && l || !e.ge || !e.ge(a, _);
|
|
260
275
|
} catch (t) {
|
|
261
276
|
// A throwing user comparator is an error of this node's computation.
|
|
262
277
|
// Route it through the same status path as a compute-phase throw so
|
|
@@ -269,53 +284,63 @@ function recompute(e, t = false) {
|
|
|
269
284
|
// its runner — happen here instead. `!create` matches the previous `initialized`
|
|
270
285
|
// gate: the explicit recompute(node, true) inside effect() does not enqueue, so
|
|
271
286
|
// effect() can call its runner synchronously for the first run.
|
|
272
|
-
if (n &&
|
|
273
|
-
e.
|
|
287
|
+
if (n && c) {
|
|
288
|
+
e.tt = !e.o?._;
|
|
274
289
|
// Reuse one bound runner per effect — runEffect no-ops on a stale
|
|
275
290
|
// `_modified`, so re-enqueueing the same function is harmless.
|
|
276
|
-
if (!t) e.C.enqueue(n, e.
|
|
291
|
+
if (!t) e.C.enqueue(n, e.nt ??= GlobalQueue.it.bind(null, e));
|
|
277
292
|
}
|
|
278
|
-
if (e.o?._) ; else if (
|
|
279
|
-
const l = u ? e.o?.
|
|
293
|
+
if (e.o?._) ; else if (c) {
|
|
294
|
+
const l = u ? e.o?.Oe : undefined;
|
|
280
295
|
if (t ||
|
|
281
296
|
// Plain sync flush (no transition on either side) commits effect
|
|
282
297
|
// values directly — the pending round-trip (queuePendingNode +
|
|
283
298
|
// commitPendingNodes) exists to sequence transition reveals, and
|
|
284
299
|
// paying it per effect on the plain path is pure overhead.
|
|
285
|
-
|
|
286
|
-
|
|
300
|
+
// DIRECT_COMMIT effects (resolve/until) commit directly even under
|
|
301
|
+
// their own held transition: their applies deliver on a microtask,
|
|
302
|
+
// not the stashed queues, so a staged value would hand the immediate
|
|
303
|
+
// apply stale state — see CONFIG_DIRECT_COMMIT.
|
|
304
|
+
n && (activeTransition !== e.Ae || activeTransition === null || e.T & CONFIG_DIRECT_COMMIT) || i) {
|
|
305
|
+
e.be = _;
|
|
287
306
|
// Lane-propagated correction: upstream data is fresh, correct the
|
|
288
307
|
// override unconditionally. The direct _value commit is the lane's
|
|
289
308
|
// own reveal schedule; drop any superseded older hold so its queued
|
|
290
309
|
// commit can't clobber the fresh value.
|
|
291
310
|
if (u && i) {
|
|
292
|
-
ext(e).
|
|
311
|
+
ext(e).Oe = _ === undefined ? OVERRIDE_UNDEFINED : _;
|
|
293
312
|
e.Pe = NOT_PENDING;
|
|
294
313
|
}
|
|
295
314
|
} else {
|
|
296
|
-
e.Pe =
|
|
315
|
+
e.Pe = _;
|
|
297
316
|
// A window landing that gets held re-opens the window until the hold
|
|
298
317
|
// commits — the verdict's held-value branch is window-gated (#2990).
|
|
299
|
-
if (
|
|
318
|
+
if (r) e.Ne = true;
|
|
300
319
|
// Transition-held sync recompute is a write path like setSignal/asyncWrite,
|
|
301
320
|
// so sync derivations of held sources stay visible to isPending()/latest()
|
|
302
321
|
// (#2831). Both companion writes are transition-scoped (optimistic) and
|
|
303
322
|
// auto-revert/re-derive at commit. Skipped for plain flushes where the
|
|
304
323
|
// pending value commits before effects run.
|
|
305
|
-
if ((activeTransition || e.
|
|
324
|
+
if ((activeTransition || e.Ae) && GlobalQueue.Re !== null) GlobalQueue.Re(e, _);
|
|
306
325
|
}
|
|
307
326
|
// insertSubs only walks _subs (no scheduling of its own), so a
|
|
308
327
|
// subscriber-less node has nothing to notify.
|
|
309
|
-
if (e.u !== null && (!u || i || e.o?.
|
|
328
|
+
if (e.u !== null && (!u || i || e.o?.Oe !== l)) insertSubs(e, i || u);
|
|
310
329
|
} else if (u) {
|
|
311
330
|
// Unchanged value (equals the override) recomputed while the override
|
|
312
331
|
// is active: _value may still be stale, so hold the authoritative value
|
|
313
332
|
// for commit on its own transition's schedule — invisibly (A17/A18).
|
|
314
333
|
if (e.Pe === NOT_PENDING) queuePendingNode(e);
|
|
315
|
-
e.Pe =
|
|
316
|
-
if (
|
|
334
|
+
e.Pe = _;
|
|
335
|
+
if (r) e.Ne = true;
|
|
317
336
|
// see the held branch above (#2990)
|
|
318
|
-
|
|
337
|
+
// A authoritative-view reader (until()) observed this node past its
|
|
338
|
+
// override — and "authoritative arrival equal to the override" is
|
|
339
|
+
// exactly the acknowledgment it waits for. Wake those readers only;
|
|
340
|
+
// A17 silence holds for every ordinary subscriber. (Hook installed by
|
|
341
|
+
// until(), the only setter of the gating bit.)
|
|
342
|
+
if (e.T & CONFIG_AUTHORITATIVE_OBSERVED) GlobalQueue.Ue(e);
|
|
343
|
+
} else if (e.Me != f) {
|
|
319
344
|
for (let t = e.u; t !== null; t = t.ae) {
|
|
320
345
|
insertIntoHeapHeight(t.ce, queueFor(t.ce));
|
|
321
346
|
}
|
|
@@ -325,23 +350,34 @@ function recompute(e, t = false) {
|
|
|
325
350
|
// in an errored run and may sit on stale commits (#2949). Changed-value
|
|
326
351
|
// recoveries ride insertSubs above; a comparator throw re-errored the node
|
|
327
352
|
// (el._x?._error re-set), so this only runs on a genuinely clean recovery.
|
|
328
|
-
if (o !== undefined && !
|
|
353
|
+
if (o !== undefined && !c && !e.o?._) settleErroredDependents(e, o);
|
|
354
|
+
// Pending twin of the sweep above (#3181): a flight superseded by this
|
|
355
|
+
// synchronous settle leaves every registered dependent still flagged
|
|
356
|
+
// STATUS_PENDING with a pending source that will never land — asyncWrite
|
|
357
|
+
// owns the walk only for flights that actually land. An unchanged value
|
|
358
|
+
// is the dangerous shape (a projection reconciling in place: a memo over
|
|
359
|
+
// it re-throws its cached NotReadyError forever, and every reader that
|
|
360
|
+
// suspended through that memo re-parks on the dead source), but the walk
|
|
361
|
+
// runs on the changed shape too, exactly as the landing path does —
|
|
362
|
+
// insertSubs notifies value SUBSCRIBERS, not pending REGISTRANTS, and
|
|
363
|
+
// the two sets only partially overlap.
|
|
364
|
+
if (s && !(e.S & STATUS_PENDING)) settlePendingSource(e);
|
|
329
365
|
}
|
|
330
|
-
currentOptimisticLane =
|
|
331
|
-
const
|
|
366
|
+
currentOptimisticLane = N;
|
|
367
|
+
const A = e.Pe !== NOT_PENDING || e.o !== null && (e.o.Ye !== null || e.o.qe !== null) || (e.S & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0;
|
|
332
368
|
// Override-covered holds (hasOverride) always queue: their commit belongs
|
|
333
369
|
// to their own transition's schedule (A18 re-rule) and is unobservable
|
|
334
370
|
// under the override (A17). Revert no longer commits anything, so an
|
|
335
371
|
// unqueued covered hold would leak (INV-7) once the revert clears
|
|
336
372
|
// _transition.
|
|
337
|
-
|
|
338
|
-
e.
|
|
373
|
+
A && (!t || e.S & STATUS_PENDING) && (!e.Ae || u) && queuePendingNode(e);
|
|
374
|
+
e.Ae && n && activeTransition !== e.Ae && runInTransition(e.Ae, () => recompute(e));
|
|
339
375
|
// Missed-wake reschedule (see the finally above): values this pass read
|
|
340
376
|
// before the nested commit are stale, so run again now that the heap will
|
|
341
377
|
// accept the node. Equality gates stop same-value landings from cascading,
|
|
342
378
|
// and a re-run only latches again if another nested commit changes a dep
|
|
343
379
|
// beneath it — convergent unless deps genuinely keep changing.
|
|
344
|
-
if (
|
|
380
|
+
if (I) {
|
|
345
381
|
enqueueSub(e);
|
|
346
382
|
schedule();
|
|
347
383
|
}
|
|
@@ -352,11 +388,13 @@ function updateIfNecessary(e) {
|
|
|
352
388
|
// (_depsTail/_depGen) is live, and a nested recompute would corrupt it.
|
|
353
389
|
// A mid-pass mark stays latched for recompute's own tail to reschedule
|
|
354
390
|
// (#3037); readers meanwhile serve the values the pass has so far.
|
|
355
|
-
|
|
391
|
+
// Never recompute a DISPOSED node either: recompute rewrites _flags and
|
|
392
|
+
// would resurrect it (#2983) — readers serve its last value.
|
|
393
|
+
if (e.ie & (REACTIVE_RECOMPUTING_DEPS | REACTIVE_DISPOSED)) return;
|
|
356
394
|
if (e.ie & REACTIVE_CHECK) {
|
|
357
|
-
for (let t = e.
|
|
358
|
-
const n = t.
|
|
359
|
-
const i = n.
|
|
395
|
+
for (let t = e.ut; t; t = t.lt) {
|
|
396
|
+
const n = t.ot;
|
|
397
|
+
const i = n.st || n;
|
|
360
398
|
if (i.oe) {
|
|
361
399
|
updateIfNecessary(i);
|
|
362
400
|
}
|
|
@@ -365,7 +403,7 @@ function updateIfNecessary(e) {
|
|
|
365
403
|
}
|
|
366
404
|
}
|
|
367
405
|
}
|
|
368
|
-
if (e.ie & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY) || e.o?._ && e.Te < clock && !e.o?.
|
|
406
|
+
if (e.ie & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY) || e.o?._ && e.Te < clock && !e.o?.Ie) {
|
|
369
407
|
recompute(e);
|
|
370
408
|
}
|
|
371
409
|
e.ie = e.ie & (REACTIVE_SNAPSHOT_STALE | REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT);
|
|
@@ -380,40 +418,40 @@ function computed(e, t) {
|
|
|
380
418
|
const u = {
|
|
381
419
|
id: inheritId(t, n, context),
|
|
382
420
|
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),
|
|
383
|
-
|
|
384
|
-
|
|
421
|
+
ge: t?.equals ?? isEqual,
|
|
422
|
+
pe: null,
|
|
385
423
|
C: context?.C ?? globalQueue,
|
|
386
424
|
we: context?.we ?? defaultContext,
|
|
387
|
-
|
|
425
|
+
Ze: 0,
|
|
388
426
|
oe: e,
|
|
389
427
|
be: i ? t.loadingValue : undefined,
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
428
|
+
Me: 0,
|
|
429
|
+
rt: undefined,
|
|
430
|
+
ct: null,
|
|
431
|
+
ut: null,
|
|
432
|
+
je: null,
|
|
433
|
+
Ke: 0,
|
|
396
434
|
u: null,
|
|
397
|
-
|
|
435
|
+
_t: null,
|
|
398
436
|
ke: context,
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
437
|
+
Le: null,
|
|
438
|
+
ft: null,
|
|
439
|
+
xe: null,
|
|
402
440
|
ie: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
|
|
403
441
|
// A loadingValue node is born committed: commit #0 is already in _value.
|
|
404
442
|
S: i ? 0 : STATUS_UNINITIALIZED,
|
|
405
443
|
Te: clock,
|
|
406
444
|
Pe: NOT_PENDING,
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
445
|
+
Ae: null,
|
|
446
|
+
It: -1,
|
|
447
|
+
Ne: i,
|
|
410
448
|
// Cold machinery (async/transition/optimistic/verdict slots) lives one
|
|
411
449
|
// hop away in the lazily-allocated extension — the core literal MUST
|
|
412
450
|
// stay under V8's in-object boundary (§12: past ~39 fields every
|
|
413
451
|
// allocation spills to a backing store and creation cost ~4x's).
|
|
414
452
|
o: null
|
|
415
453
|
};
|
|
416
|
-
if (t?.unobserved) ext(u).
|
|
454
|
+
if (t?.unobserved) ext(u).Et = t.unobserved;
|
|
417
455
|
setupComputedNode(u, t);
|
|
418
456
|
return u;
|
|
419
457
|
}
|
|
@@ -423,25 +461,26 @@ function computed(e, t) {
|
|
|
423
461
|
* this; hot paths read `el._x?._field` gated by the _config presence bits.
|
|
424
462
|
* Never call ext() just to store a field's default. */ function ext(e) {
|
|
425
463
|
return e.o ??= {
|
|
426
|
-
|
|
464
|
+
Oe: undefined,
|
|
427
465
|
Nt: undefined,
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
466
|
+
Je: undefined,
|
|
467
|
+
ye: undefined,
|
|
468
|
+
Ce: undefined,
|
|
469
|
+
Tt: undefined,
|
|
432
470
|
t: 0,
|
|
471
|
+
Ie: null,
|
|
433
472
|
Ee: null,
|
|
434
473
|
_: undefined,
|
|
435
474
|
fe: undefined,
|
|
436
475
|
le: undefined,
|
|
437
476
|
h: undefined,
|
|
438
|
-
|
|
477
|
+
Ge: false,
|
|
439
478
|
i: null,
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
We: null,
|
|
479
|
+
Et: undefined,
|
|
480
|
+
We: undefined,
|
|
443
481
|
qe: null,
|
|
444
|
-
|
|
482
|
+
Ye: null,
|
|
483
|
+
St: undefined
|
|
445
484
|
};
|
|
446
485
|
}
|
|
447
486
|
|
|
@@ -454,39 +493,39 @@ function computed(e, t) {
|
|
|
454
493
|
const l = u?.transparent ?? false;
|
|
455
494
|
const o = {
|
|
456
495
|
id: inheritId(u, l, context),
|
|
457
|
-
T: (l ? CONFIG_TRANSPARENT : 0) | (u?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (u?.sync ? CONFIG_SYNC : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
458
|
-
|
|
459
|
-
|
|
496
|
+
T: (l ? CONFIG_TRANSPARENT : 0) | (u?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (u?.sync ? CONFIG_SYNC : 0) | (u?.dt ?? 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
497
|
+
ge: false,
|
|
498
|
+
pe: null,
|
|
460
499
|
C: context?.C ?? globalQueue,
|
|
461
500
|
we: context?.we ?? defaultContext,
|
|
462
|
-
|
|
501
|
+
Ze: 0,
|
|
463
502
|
oe: e,
|
|
464
503
|
be: undefined,
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
504
|
+
Me: 0,
|
|
505
|
+
rt: undefined,
|
|
506
|
+
ct: null,
|
|
507
|
+
ut: null,
|
|
508
|
+
je: null,
|
|
509
|
+
Ke: 0,
|
|
471
510
|
u: null,
|
|
472
|
-
|
|
511
|
+
_t: null,
|
|
473
512
|
ke: context,
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
513
|
+
Le: null,
|
|
514
|
+
ft: null,
|
|
515
|
+
xe: null,
|
|
477
516
|
ie: REACTIVE_LAZY,
|
|
478
517
|
S: STATUS_UNINITIALIZED,
|
|
479
518
|
Te: clock,
|
|
480
519
|
Pe: NOT_PENDING,
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
dt: undefined,
|
|
486
|
-
Tt: t,
|
|
487
|
-
St: n,
|
|
520
|
+
Ae: null,
|
|
521
|
+
It: -1,
|
|
522
|
+
Ne: false,
|
|
523
|
+
tt: false,
|
|
488
524
|
At: undefined,
|
|
489
|
-
|
|
525
|
+
Ot: t,
|
|
526
|
+
Ct: n,
|
|
527
|
+
Rt: undefined,
|
|
528
|
+
he: i,
|
|
490
529
|
o: null
|
|
491
530
|
};
|
|
492
531
|
// Effects dispatch status through the SHARED notifier (statusNotifierOf,
|
|
@@ -494,7 +533,7 @@ function computed(e, t) {
|
|
|
494
533
|
// allocation on EVERY effect at creation (an alloc + 19 field stores,
|
|
495
534
|
// +23% effect creation, caught by the creation benches). Only genuinely
|
|
496
535
|
// per-node channels (boundaries) live on _x.
|
|
497
|
-
if (u?.unobserved) ext(o).
|
|
536
|
+
if (u?.unobserved) ext(o).Et = u.unobserved;
|
|
498
537
|
setupComputedNode(o, lazyOptions);
|
|
499
538
|
return o;
|
|
500
539
|
}
|
|
@@ -515,10 +554,9 @@ function setEffectStatusNotify(e) {
|
|
|
515
554
|
* the field) fall back to the shared notifier. Presence doubles as the
|
|
516
555
|
* "display consumer" membership test in the status walks, exactly as the
|
|
517
556
|
* per-node field did when every effect carried one. */ function statusNotifierOf(e) {
|
|
518
|
-
const t = e.o;
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
return e.Re ? effectStatusNotify ?? undefined : undefined;
|
|
557
|
+
const t = e.o?.h;
|
|
558
|
+
if (t !== undefined) return t;
|
|
559
|
+
return e.he ? effectStatusNotify ?? undefined : undefined;
|
|
522
560
|
}
|
|
523
561
|
|
|
524
562
|
const lazyOptions = {
|
|
@@ -526,24 +564,24 @@ const lazyOptions = {
|
|
|
526
564
|
};
|
|
527
565
|
|
|
528
566
|
function setupComputedNode(e, t) {
|
|
529
|
-
e.
|
|
530
|
-
const n = context?.
|
|
567
|
+
e.ct = e;
|
|
568
|
+
const n = context?.Gt ? context.Dt : context;
|
|
531
569
|
if (context) {
|
|
532
|
-
const t = context.
|
|
570
|
+
const t = context.xe;
|
|
533
571
|
if (t === null) {
|
|
534
|
-
context.
|
|
572
|
+
context.xe = e;
|
|
535
573
|
} else {
|
|
536
|
-
e.
|
|
537
|
-
t.
|
|
538
|
-
context.
|
|
574
|
+
e.Le = t;
|
|
575
|
+
t.ft = e;
|
|
576
|
+
context.xe = e;
|
|
539
577
|
}
|
|
540
578
|
}
|
|
541
|
-
if (n) e.
|
|
542
|
-
if (GlobalQueue.
|
|
579
|
+
if (n) e.Me = n.Me + 1;
|
|
580
|
+
if (GlobalQueue.Pt !== null) GlobalQueue.Pt(e);
|
|
543
581
|
!t?.lazy && recompute(e, true);
|
|
544
582
|
if (snapshotCaptureActive && !t?.lazy) {
|
|
545
583
|
if (!(e.S & STATUS_PENDING) && !(e.T & CONFIG_NO_SNAPSHOT)) {
|
|
546
|
-
ext(e).
|
|
584
|
+
ext(e).We = e.be === undefined ? NO_SNAPSHOT : e.be;
|
|
547
585
|
e.T |= CONFIG_HAS_SNAPSHOT;
|
|
548
586
|
snapshotSources.add(e);
|
|
549
587
|
}
|
|
@@ -552,13 +590,13 @@ function setupComputedNode(e, t) {
|
|
|
552
590
|
|
|
553
591
|
function signal(e, t, n = null) {
|
|
554
592
|
const i = {
|
|
555
|
-
|
|
593
|
+
ge: t?.equals ?? isEqual,
|
|
556
594
|
T: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.H ? CONFIG_NO_SNAPSHOT : 0),
|
|
557
595
|
be: e,
|
|
558
596
|
u: null,
|
|
559
|
-
|
|
597
|
+
_t: null,
|
|
560
598
|
Te: clock,
|
|
561
|
-
|
|
599
|
+
st: n,
|
|
562
600
|
Se: n?.o?.i || null,
|
|
563
601
|
Pe: NOT_PENDING,
|
|
564
602
|
// Signal-literal diet (§12e): NO _time/_fn/_statusFlags slots. Stores
|
|
@@ -566,17 +604,17 @@ function signal(e, t, n = null) {
|
|
|
566
604
|
// bytes. _time is write-only on signals (every read site is computed-
|
|
567
605
|
// typed error-retry gating); _fn/_statusFlags read falsy-identically as
|
|
568
606
|
// missing properties on the shared paths (undefined masks to 0).
|
|
569
|
-
|
|
570
|
-
|
|
607
|
+
Ae: null,
|
|
608
|
+
It: -1,
|
|
571
609
|
o: null
|
|
572
610
|
};
|
|
573
|
-
if (t?.unobserved) ext(i).
|
|
611
|
+
if (t?.unobserved) ext(i).Et = t.unobserved;
|
|
574
612
|
if (n) {
|
|
575
613
|
ext(n).i = i;
|
|
576
614
|
n.T |= CONFIG_FW_CHILDREN;
|
|
577
615
|
}
|
|
578
616
|
if (snapshotCaptureActive && !(i.T & CONFIG_NO_SNAPSHOT) && !((n?.S ?? 0) & STATUS_PENDING)) {
|
|
579
|
-
ext(i).
|
|
617
|
+
ext(i).We = e === undefined ? NO_SNAPSHOT : e;
|
|
580
618
|
i.T |= CONFIG_HAS_SNAPSHOT;
|
|
581
619
|
snapshotSources.add(i);
|
|
582
620
|
}
|
|
@@ -585,14 +623,14 @@ function signal(e, t, n = null) {
|
|
|
585
623
|
|
|
586
624
|
function optimisticSignal(e, t) {
|
|
587
625
|
const n = signal(e, t);
|
|
588
|
-
ext(n).
|
|
626
|
+
ext(n).Oe = NOT_PENDING;
|
|
589
627
|
n.T |= CONFIG_OPTIMISTIC;
|
|
590
628
|
return n;
|
|
591
629
|
}
|
|
592
630
|
|
|
593
631
|
function optimisticComputed(e, t) {
|
|
594
632
|
const n = computed(e, t);
|
|
595
|
-
ext(n).
|
|
633
|
+
ext(n).Oe = NOT_PENDING;
|
|
596
634
|
n.T |= CONFIG_OPTIMISTIC;
|
|
597
635
|
return n;
|
|
598
636
|
}
|
|
@@ -623,11 +661,11 @@ function isEqual(e, t) {
|
|
|
623
661
|
* );
|
|
624
662
|
* ```
|
|
625
663
|
*/ function untrack(e, t) {
|
|
626
|
-
if (GlobalQueue.
|
|
664
|
+
if (GlobalQueue.ht === null && !tracking && true) return e();
|
|
627
665
|
const n = tracking;
|
|
628
666
|
tracking = false;
|
|
629
667
|
try {
|
|
630
|
-
if (GlobalQueue.
|
|
668
|
+
if (GlobalQueue.ht !== null) return GlobalQueue.ht(e);
|
|
631
669
|
return e();
|
|
632
670
|
} finally {
|
|
633
671
|
tracking = n;
|
|
@@ -668,10 +706,38 @@ function isEqual(e, t) {
|
|
|
668
706
|
* fast path, `_fn` nodes need prepareComputed, and firewall / override /
|
|
669
707
|
* snapshot / transition / lane / dev-strictRead state all take the full
|
|
670
708
|
* resolution. Anything slow returns READ_SLOW; the caller then calls read().
|
|
671
|
-
*/
|
|
672
|
-
|
|
709
|
+
*/
|
|
710
|
+
/**
|
|
711
|
+
* Wake only authoritative-view readers (until() predicates) subscribed to `el`.
|
|
712
|
+
* The A17-silent ack paths — an authoritative arrival equal to the active
|
|
713
|
+
* override — use this so the predicate re-evaluates without re-firing
|
|
714
|
+
* ordinary subscribers whose visible (override) value did not change.
|
|
715
|
+
* Pay-for-use: reached through GlobalQueue._notifyAuthoritativeObservers,
|
|
716
|
+
* installed at first until() call — apps that never use until() shake it.
|
|
717
|
+
*/ function notifyAuthoritativeObservers(e) {
|
|
718
|
+
for (let t = e.u; t !== null; t = t.ae) {
|
|
719
|
+
const e = t.ce;
|
|
720
|
+
if (!(e.T & CONFIG_AUTHORITATIVE_READ)) continue;
|
|
721
|
+
// Missed-wake latch (#3037), same contract as insertSubs: the reader may
|
|
722
|
+
// itself have pulled this recompute (updateIfNecessary from its own
|
|
723
|
+
// read), and the heap refuses RECOMPUTING nodes — latch so recompute's
|
|
724
|
+
// tail reschedules it with the staged value visible.
|
|
725
|
+
if (e.ie & REACTIVE_RECOMPUTING_DEPS && t.Ft === e.Ke && t !== e.je) e.ie |= REACTIVE_MISSED_WAKE;
|
|
726
|
+
enqueueSub(e);
|
|
727
|
+
}
|
|
728
|
+
schedule();
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/** Installs the until() machinery hook. Idempotent; called by until() before
|
|
732
|
+
* any authoritative-view read happens (same late-binding contract as the
|
|
733
|
+
* optimistic engine). */ function installAuthoritativeRead() {
|
|
734
|
+
if (GlobalQueue.Ue === null) GlobalQueue.Ue = notifyAuthoritativeObservers;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
function readNodeFast(e) {
|
|
738
|
+
if (latestReadActive || pendingCheckActive || e.oe || e.st || e.o?.Oe !== undefined || e.o?.We !== undefined || activeTransition !== null || currentOptimisticLane !== null || snapshotCaptureActive || false) return READ_SLOW;
|
|
673
739
|
let t = context;
|
|
674
|
-
if (t?.
|
|
740
|
+
if (t?.Gt) t = t.Dt;
|
|
675
741
|
if (t && tracking) link(e, t);
|
|
676
742
|
// Children-forbidden readers (createTrackedEffect / onSettled callbacks) get
|
|
677
743
|
// committed visibility: like the effect half of createEffect and event
|
|
@@ -685,21 +751,21 @@ function read(e) {
|
|
|
685
751
|
// Checked before isPending so that isPending(() => latest(x)) checks
|
|
686
752
|
// the _pendingSignal of _latestValueComputed (async in flight) rather
|
|
687
753
|
// than the original node (which stays "pending" while held in a transition).
|
|
688
|
-
if (latestReadActive) return GlobalQueue.
|
|
754
|
+
if (latestReadActive) return GlobalQueue.gt(e);
|
|
689
755
|
let t = context;
|
|
690
|
-
if (t?.
|
|
756
|
+
if (t?.Gt) t = t.Dt;
|
|
691
757
|
const n = e;
|
|
692
|
-
const i = e.
|
|
758
|
+
const i = e.st;
|
|
693
759
|
const u = i || e;
|
|
694
760
|
// Handle isPending() mode: collect pending state while preserving normal read semantics.
|
|
695
761
|
// Probe mode is suspended while preparing the node so nested reads during a
|
|
696
762
|
// recompute don't collect into the probe.
|
|
697
763
|
if (pendingCheckActive) {
|
|
698
|
-
GlobalQueue.
|
|
764
|
+
GlobalQueue.Ht(e, t, u, i);
|
|
699
765
|
} else if (typeof n.oe === "function") {
|
|
700
766
|
prepareComputed(e, false);
|
|
701
767
|
}
|
|
702
|
-
if (!n.oe && u === e && e.o?.
|
|
768
|
+
if (!n.oe && u === e && e.o?.Oe === undefined && e.o?.We === undefined && activeTransition === null && currentOptimisticLane === null && !snapshotCaptureActive && true) {
|
|
703
769
|
if (t && tracking) link(e, t);
|
|
704
770
|
// Committed visibility for children-forbidden readers — see readNodeFast.
|
|
705
771
|
return !t || e.Pe === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN ? e.be : e.Pe;
|
|
@@ -708,24 +774,31 @@ function read(e) {
|
|
|
708
774
|
link(e, t, pendingCheckActive);
|
|
709
775
|
if (u.oe) {
|
|
710
776
|
const n = queueFor(e);
|
|
711
|
-
if (u.
|
|
777
|
+
if (u.Me >= n.Qe) {
|
|
712
778
|
markNode(t);
|
|
713
779
|
markHeap(n);
|
|
714
780
|
updateIfNecessary(u);
|
|
715
781
|
}
|
|
716
|
-
|
|
782
|
+
// Fresh-pull readers (awaitable refresh's waiter) recompute a dirty
|
|
783
|
+
// source inline even when the height gate defers to the flush: the
|
|
784
|
+
// waiter must park on the re-ask's window (or serve its sync answer),
|
|
785
|
+
// never read the PRE-re-ask value as settled. Self-guarded: a clean
|
|
786
|
+
// node no-ops and updateIfNecessary refuses disposed nodes (#2983) —
|
|
787
|
+
// a dead target serves its last value, which is already quiescent.
|
|
788
|
+
else if (t.T & CONFIG_FRESH_READ) updateIfNecessary(u);
|
|
789
|
+
const i = u.Me;
|
|
717
790
|
// parent check is shallow, might need to be recursive
|
|
718
|
-
if (i >= t.
|
|
719
|
-
t.
|
|
791
|
+
if (i >= t.Me && e.ke !== t) {
|
|
792
|
+
t.Me = i + 1;
|
|
720
793
|
}
|
|
721
794
|
}
|
|
722
795
|
}
|
|
723
796
|
if (u.S & STATUS_PENDING) {
|
|
724
|
-
if (t && !(stale && u.
|
|
797
|
+
if (t && !(stale && u.Ae && activeTransition !== u.Ae)) {
|
|
725
798
|
// Per-lane suspension lives with the engine (a non-null lane implies it
|
|
726
799
|
// is installed): under a lane, only same-lane pending async without an
|
|
727
800
|
// active override throws.
|
|
728
|
-
if (currentOptimisticLane === null || GlobalQueue.
|
|
801
|
+
if (currentOptimisticLane === null || GlobalQueue.Vt(u)) {
|
|
729
802
|
if (!tracking && e !== t) link(e, t);
|
|
730
803
|
throw u.o?._;
|
|
731
804
|
}
|
|
@@ -761,7 +834,7 @@ function read(e) {
|
|
|
761
834
|
} else throw u.o?._;
|
|
762
835
|
}
|
|
763
836
|
if (snapshotCaptureActive && t && t.T & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
764
|
-
const n = e.o?.
|
|
837
|
+
const n = e.o?.We;
|
|
765
838
|
if (n !== undefined) {
|
|
766
839
|
const i = n === NO_SNAPSHOT ? undefined : n;
|
|
767
840
|
const u = e.Pe !== NOT_PENDING ? e.Pe : e.be;
|
|
@@ -769,9 +842,19 @@ function read(e) {
|
|
|
769
842
|
return i;
|
|
770
843
|
}
|
|
771
844
|
}
|
|
772
|
-
if (e.o?.
|
|
773
|
-
// A17: the override IS the value for every reader
|
|
774
|
-
|
|
845
|
+
if (e.o?.Oe !== undefined && e.o?.Oe !== NOT_PENDING) {
|
|
846
|
+
// A17: the override IS the value for every reader — except an authoritative
|
|
847
|
+
// reader (until()'s predicate carries CONFIG_AUTHORITATIVE_READ): it must
|
|
848
|
+
// observe independently-arriving truth, and serving it the caller's own
|
|
849
|
+
// tentative write would trivially satisfy the predicate. The bit is checked
|
|
850
|
+
// on the reading computation itself, so a shared computed the predicate
|
|
851
|
+
// pulls recomputes as ITSELF (context = the memo, no bit) under the normal
|
|
852
|
+
// view. Fall through to normal value selection (staged `_pendingValue` is
|
|
853
|
+
// authoritative — optimism never lives there); the sticky mark makes the
|
|
854
|
+
// A17-silent "landing equals override" paths notify this node's subs so
|
|
855
|
+
// the reader re-runs when truth arrives.
|
|
856
|
+
if (!(t && t.T & CONFIG_AUTHORITATIVE_READ)) return unwrapOverride(e.o?.Oe);
|
|
857
|
+
e.T |= CONFIG_AUTHORITATIVE_OBSERVED;
|
|
775
858
|
}
|
|
776
859
|
// Entanglement gate: a reader recomputing under an optimistic lane that reads
|
|
777
860
|
// a pending mid-transition write sees the committed value. Projection-store
|
|
@@ -780,7 +863,7 @@ function read(e) {
|
|
|
780
863
|
// _pendingValue for correct fetching. The sub is recorded for replay at commit
|
|
781
864
|
// so it re-runs with the new committed view. (Gate details live with the
|
|
782
865
|
// engine — a non-null lane implies it is installed.)
|
|
783
|
-
if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.
|
|
866
|
+
if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.vt(e, u, t)) {
|
|
784
867
|
return e.be;
|
|
785
868
|
}
|
|
786
869
|
// In optimistic lane context, return _value for optimistic/lane-assigned signals
|
|
@@ -789,10 +872,19 @@ function read(e) {
|
|
|
789
872
|
// (The lane-context clause lives with the engine.) Children-forbidden readers
|
|
790
873
|
// (createTrackedEffect / onSettled callbacks) get committed visibility — see
|
|
791
874
|
// readNodeFast (#3006).
|
|
792
|
-
const l = !t || currentOptimisticLane !== null && GlobalQueue.
|
|
875
|
+
const l = !t || currentOptimisticLane !== null && GlobalQueue.bt(e, u, t) || e.Pe === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN || stale && e.Ae && activeTransition !== e.Ae ||
|
|
876
|
+
// A17 for HELD truth (#3164, see CONFIG_HELD_TRUTH): staged confirming
|
|
877
|
+
// truth — fold-staged onto an armed family, or entangle-stolen by an
|
|
878
|
+
// awaited until() — is masked from ordinary readers until its
|
|
879
|
+
// transaction's reveal; the retaining transaction's own speculative
|
|
880
|
+
// recomputes included (partial override coverage would otherwise
|
|
881
|
+
// compose override + staged truth into a state no timeline contains).
|
|
882
|
+
// Authoritative readers (until()'s predicate) and latest() see the
|
|
883
|
+
// staged truth — the tunnel that keeps the hold deadlock-free.
|
|
884
|
+
e.T & CONFIG_HELD_TRUTH && !latestReadActive && !(t.T & CONFIG_AUTHORITATIVE_READ) ? e.be : e.Pe;
|
|
793
885
|
// Record that this isPending() probe observed the fresh pending value, so
|
|
794
886
|
// the probe doesn't pair "pending" with the new value (#2831).
|
|
795
|
-
if (pendingCheckActive) GlobalQueue.
|
|
887
|
+
if (pendingCheckActive) GlobalQueue.kt(e, l);
|
|
796
888
|
if (!t && u === e && typeof n.oe === "function" && e.T & CONFIG_AUTO_DISPOSE && !(u.S & STATUS_PENDING) && !e.u) {
|
|
797
889
|
// Deferred, not inline (#3078): an inline unobserved() here made untracked
|
|
798
890
|
// reads destructive — dispose on this read, full revival recompute on the
|
|
@@ -807,18 +899,18 @@ function read(e) {
|
|
|
807
899
|
}
|
|
808
900
|
|
|
809
901
|
function setSignal(e, t) {
|
|
810
|
-
if (e.
|
|
902
|
+
if (e.Ae && activeTransition !== e.Ae) globalQueue.initTransition(e.Ae);
|
|
811
903
|
// The optimistic write path lives with the engine: only optimisticSignal /
|
|
812
904
|
// optimisticComputed callers and optimistic store nodes carry an
|
|
813
905
|
// _overrideValue slot (flagged by CONFIG_OPTIMISTIC — a masked read of the
|
|
814
906
|
// always-present config instead of a missing-property probe), and every
|
|
815
907
|
// module that installs one installs the engine first.
|
|
816
|
-
if (e.T & CONFIG_OPTIMISTIC && !projectionWriteActive) return GlobalQueue.
|
|
908
|
+
if (e.T & CONFIG_OPTIMISTIC && !projectionWriteActive) return GlobalQueue.xt(e, t);
|
|
817
909
|
const n = e.Pe === NOT_PENDING ? e.be : e.Pe;
|
|
818
910
|
if (typeof t === "function") t = t(n);
|
|
819
911
|
// Uninitialized check first: the first commit has no previous value, so the
|
|
820
912
|
// user comparator must not run against `undefined` (matches recompute).
|
|
821
|
-
const i = !!(e.S & STATUS_UNINITIALIZED) || !e.
|
|
913
|
+
const i = !!(e.S & STATUS_UNINITIALIZED) || !e.ge || !e.ge(n, t);
|
|
822
914
|
if (!i) return t;
|
|
823
915
|
const u = e.Pe !== NOT_PENDING;
|
|
824
916
|
if (!u) queuePendingNode(e);
|
|
@@ -828,7 +920,7 @@ function setSignal(e, t) {
|
|
|
828
920
|
// only ever created, never removed, and creating one installs the hook and
|
|
829
921
|
// sets CONFIG_HAS_COMPANIONS — one masked read replaces two optional-field
|
|
830
922
|
// probes on every write).
|
|
831
|
-
e.T & CONFIG_HAS_COMPANIONS && GlobalQueue.
|
|
923
|
+
e.T & CONFIG_HAS_COMPANIONS && GlobalQueue.Re !== null && GlobalQueue.Re(e, t);
|
|
832
924
|
// _time is a computed-only slot (§12e): writing it on a signal would fork
|
|
833
925
|
// the lean shape. Every read site is computed-typed.
|
|
834
926
|
if (e.oe !== undefined) e.Te = clock;
|
|
@@ -837,7 +929,7 @@ function setSignal(e, t) {
|
|
|
837
929
|
// (epoch) — re-stages the value and stops. The walk is idempotent (subs
|
|
838
930
|
// marked, heap entries flag-guarded, effects queued once); lane and reask
|
|
839
931
|
// contexts change what a walk MEANS, so they always walk.
|
|
840
|
-
if (u && e.
|
|
932
|
+
if (u && e.It === notifyEpoch && currentOptimisticLane === null && !reaskArmed) return t;
|
|
841
933
|
insertSubs(e);
|
|
842
934
|
schedule();
|
|
843
935
|
return t;
|
|
@@ -856,7 +948,7 @@ function setSignal(e, t) {
|
|
|
856
948
|
schedule();
|
|
857
949
|
}
|
|
858
950
|
e.ie = e.ie & -4 | REACTIVE_MANUAL_WRITE;
|
|
859
|
-
e.
|
|
951
|
+
e.Ut = clock;
|
|
860
952
|
}
|
|
861
953
|
|
|
862
954
|
/**
|
|
@@ -912,39 +1004,22 @@ function staleValues(e, t = true) {
|
|
|
912
1004
|
}
|
|
913
1005
|
|
|
914
1006
|
/**
|
|
915
|
-
*
|
|
916
|
-
*
|
|
917
|
-
*
|
|
918
|
-
*
|
|
919
|
-
*
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
* Use it to invalidate cached async values (e.g. force a re-fetch) without
|
|
924
|
-
* tearing the consumer down.
|
|
925
|
-
*
|
|
926
|
-
* @example
|
|
927
|
-
* ```ts
|
|
928
|
-
* const user = createMemo(async () => fetch(`/users/${id()}`).then(r => r.json()));
|
|
929
|
-
*
|
|
930
|
-
* // Re-fetch on demand
|
|
931
|
-
* <button onClick={() => refresh(user)}>Reload</button>
|
|
932
|
-
* ```
|
|
933
|
-
*/ function refresh(e) {
|
|
934
|
-
const t = e?.[$REFRESH];
|
|
935
|
-
if (!t) {
|
|
936
|
-
return;
|
|
937
|
-
}
|
|
938
|
-
if (typeof t.oe === "function" && !(t.ie & REACTIVE_DISPOSED)) {
|
|
939
|
-
if (t.ie & REACTIVE_MANUAL_WRITE) {
|
|
1007
|
+
* Core marking half of `refresh()` (the public wrapper lives in signals.ts —
|
|
1008
|
+
* it validates the target, marks through here, then builds the quiescence
|
|
1009
|
+
* promise on the resolve()/until() effect machinery). Flags the node's next
|
|
1010
|
+
* recompute as a quiet re-ask and schedules it; no-ops for non-derived or
|
|
1011
|
+
* disposed targets and for same-tick manual writes.
|
|
1012
|
+
*/ function markRefresh(e) {
|
|
1013
|
+
if (typeof e.oe === "function" && !(e.ie & REACTIVE_DISPOSED)) {
|
|
1014
|
+
if (e.ie & REACTIVE_MANUAL_WRITE) {
|
|
940
1015
|
// A manual write in the CURRENT tick wins over the refresh (#2692).
|
|
941
1016
|
// A mask stamped in an earlier tick only survives because a
|
|
942
1017
|
// transaction (action) is holding the pending drain open; there the
|
|
943
1018
|
// refresh is a later, explicit re-ask and lifts the mask — otherwise
|
|
944
1019
|
// any setStore early in an action silently swallows every refresh()
|
|
945
1020
|
// for the rest of the transaction (#3026).
|
|
946
|
-
if (
|
|
947
|
-
|
|
1021
|
+
if (e.Ut === clock) return;
|
|
1022
|
+
e.ie &= ~REACTIVE_MANUAL_WRITE;
|
|
948
1023
|
// No REASK below: the batch carries a manual value change, so the
|
|
949
1024
|
// recompute is not a quiet re-ask of an unchanged question.
|
|
950
1025
|
}
|
|
@@ -955,14 +1030,14 @@ function staleValues(e, t = true) {
|
|
|
955
1030
|
// REACTIVE_IN_HEAP counts as dirt: insertSubs schedules subscribers by
|
|
956
1031
|
// heap insertion alone (no DIRTY/CHECK flag), so a same-batch value
|
|
957
1032
|
// change followed by refresh() must not be laundered into a quiet re-ask.
|
|
958
|
-
else if (!(
|
|
959
|
-
|
|
1033
|
+
else if (!(e.ie & (REACTIVE_DIRTY | REACTIVE_CHECK | REACTIVE_IN_HEAP))) {
|
|
1034
|
+
e.ie |= REACTIVE_REASK;
|
|
960
1035
|
armReaskClear();
|
|
961
1036
|
}
|
|
962
|
-
|
|
963
|
-
insertIntoHeap(
|
|
1037
|
+
e.ie = e.ie & ~REACTIVE_CHECK | REACTIVE_DIRTY;
|
|
1038
|
+
insertIntoHeap(e, queueFor(e));
|
|
964
1039
|
schedule();
|
|
965
1040
|
}
|
|
966
1041
|
}
|
|
967
1042
|
|
|
968
|
-
export { READ_SLOW, clearSnapshots, computed, context, createEffectNode, currentOptimisticLane, effectStatusNotify, ext, isEqual, latestReadActive, markSnapshotScope, optimisticComputed, optimisticSignal, pendingCheckActive, prepareComputed, read, readNodeFast, recompute,
|
|
1043
|
+
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, setSnapshotCapture, signal, snapshotCaptureActive, snapshotSources, stale, staleValues, statusNotifierOf, suppressComputedRecompute, tracking, untrack };
|