@solidjs/signals 2.0.0-rc.4 → 2.0.0-rc.6
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 +1618 -232
- package/dist/node.cjs +2262 -1199
- package/dist/prod/boundaries.js +4 -1
- package/dist/prod/core/async.js +152 -101
- package/dist/prod/core/constants.js +55 -1
- package/dist/prod/core/core.js +305 -234
- 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 +57 -57
- package/dist/prod/core/owner.js +34 -34
- package/dist/prod/core/scheduler.js +346 -152
- package/dist/prod/core/verdict.js +122 -65
- package/dist/prod/index.js +3 -3
- package/dist/prod/map.js +106 -106
- package/dist/prod/signals.js +321 -26
- package/dist/prod/store/next/optimistic.js +363 -151
- package/dist/prod/store/next/patch.js +6 -6
- package/dist/prod/store/next/projection.js +25 -21
- package/dist/prod/store/next/store.js +223 -113
- package/dist/prod/store/store.js +2 -2
- package/dist/types/core/async.d.ts +2 -0
- package/dist/types/core/attribution-hooks.d.ts +11 -0
- package/dist/types/core/attribution.d.ts +57 -4
- package/dist/types/core/constants.d.ts +54 -0
- package/dist/types/core/core.d.ts +19 -20
- package/dist/types/core/dev.d.ts +8 -2
- 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 +39 -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 -11
- 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 +25 -0
- package/dist/types-cjs/core/async.d.cts +2 -0
- package/dist/types-cjs/core/attribution-hooks.d.cts +11 -0
- package/dist/types-cjs/core/attribution.d.cts +57 -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/dev.d.cts +8 -2
- 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 +39 -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 -11
- 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 +25 -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, settlePendingSource, settleErroredDependents } 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, STATUS_PENDING, REACTIVE_MISSED_WAKE, REACTIVE_NONE, REACTIVE_SNAPSHOT_STALE, unwrapOverride, OVERRIDE_UNDEFINED, CONFIG_HAS_COMPANIONS, REACTIVE_LAZY, REACTIVE_DISPOSED, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, defaultContext, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_NO_SNAPSHOT,
|
|
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, STATUS_PENDING, 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.ge;
|
|
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.Ge !== 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.Ge;
|
|
129
|
+
t.Ye = e.xe;
|
|
130
|
+
e.Ge = 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?.Pe !== NOT_PENDING && e.o?.Pe !== 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.o?.le?.has(e);
|
|
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.Re === NOT_PENDING ? e.be : e.Re;
|
|
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,33 @@ 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);
|
|
254
|
+
// The replacement source is fully propagated now. If no new flight
|
|
255
|
+
// re-owned self, retire the superseded flight and its dependent copies.
|
|
256
|
+
if (n && s && !e.o?.Ie) settlePendingSource(e);
|
|
239
257
|
if (i) GlobalQueue.k(e);
|
|
240
258
|
}
|
|
241
259
|
} finally {
|
|
242
|
-
tracking =
|
|
243
|
-
latestReadActive =
|
|
244
|
-
if (
|
|
260
|
+
tracking = E;
|
|
261
|
+
latestReadActive = T;
|
|
262
|
+
if (S) stale = d;
|
|
245
263
|
// Consume the missed-wake latch (#3037, set by insertSubs): a dep write
|
|
246
264
|
// landed beneath this pass on a link it had already validated. The wipe
|
|
247
265
|
// below must not key off DIRTY/CHECK — the read-time pull protocol
|
|
248
266
|
// (markNode(c) in read()) marks the running node as part of ordinary
|
|
249
267
|
// bookkeeping, and those marks are correctly discarded here.
|
|
250
|
-
|
|
268
|
+
I = (e.ie & REACTIVE_MISSED_WAKE) !== 0;
|
|
251
269
|
e.ie = REACTIVE_NONE | (t ? e.ie & REACTIVE_SNAPSHOT_STALE : 0);
|
|
252
|
-
context =
|
|
270
|
+
context = c;
|
|
253
271
|
}
|
|
254
272
|
if (!e.o?._) {
|
|
255
273
|
trimStaleDeps(e);
|
|
256
|
-
const
|
|
257
|
-
let
|
|
274
|
+
const a = u ? unwrapOverride(e.o?.Pe) : e.Re === NOT_PENDING ? e.be : e.Re;
|
|
275
|
+
let c = false;
|
|
258
276
|
try {
|
|
259
|
-
|
|
277
|
+
c = !n && l || !e.pe || !e.pe(a, _);
|
|
260
278
|
} catch (t) {
|
|
261
279
|
// A throwing user comparator is an error of this node's computation.
|
|
262
280
|
// Route it through the same status path as a compute-phase throw so
|
|
@@ -269,53 +287,63 @@ function recompute(e, t = false) {
|
|
|
269
287
|
// its runner — happen here instead. `!create` matches the previous `initialized`
|
|
270
288
|
// gate: the explicit recompute(node, true) inside effect() does not enqueue, so
|
|
271
289
|
// effect() can call its runner synchronously for the first run.
|
|
272
|
-
if (n &&
|
|
273
|
-
e.
|
|
290
|
+
if (n && c) {
|
|
291
|
+
e.tt = !e.o?._;
|
|
274
292
|
// Reuse one bound runner per effect — runEffect no-ops on a stale
|
|
275
293
|
// `_modified`, so re-enqueueing the same function is harmless.
|
|
276
|
-
if (!t) e.C.enqueue(n, e.
|
|
294
|
+
if (!t) e.C.enqueue(n, e.nt ??= GlobalQueue.it.bind(null, e));
|
|
277
295
|
}
|
|
278
|
-
if (e.o?._) ; else if (
|
|
279
|
-
const l = u ? e.o?.
|
|
296
|
+
if (e.o?._) ; else if (c) {
|
|
297
|
+
const l = u ? e.o?.Pe : undefined;
|
|
280
298
|
if (t ||
|
|
281
299
|
// Plain sync flush (no transition on either side) commits effect
|
|
282
300
|
// values directly — the pending round-trip (queuePendingNode +
|
|
283
301
|
// commitPendingNodes) exists to sequence transition reveals, and
|
|
284
302
|
// paying it per effect on the plain path is pure overhead.
|
|
285
|
-
|
|
286
|
-
|
|
303
|
+
// DIRECT_COMMIT effects (resolve/until) commit directly even under
|
|
304
|
+
// their own held transition: their applies deliver on a microtask,
|
|
305
|
+
// not the stashed queues, so a staged value would hand the immediate
|
|
306
|
+
// apply stale state — see CONFIG_DIRECT_COMMIT.
|
|
307
|
+
n && (activeTransition !== e.Ae || activeTransition === null || e.T & CONFIG_DIRECT_COMMIT) || i) {
|
|
308
|
+
e.be = _;
|
|
287
309
|
// Lane-propagated correction: upstream data is fresh, correct the
|
|
288
310
|
// override unconditionally. The direct _value commit is the lane's
|
|
289
311
|
// own reveal schedule; drop any superseded older hold so its queued
|
|
290
312
|
// commit can't clobber the fresh value.
|
|
291
313
|
if (u && i) {
|
|
292
|
-
ext(e).
|
|
293
|
-
e.
|
|
314
|
+
ext(e).Pe = _ === undefined ? OVERRIDE_UNDEFINED : _;
|
|
315
|
+
e.Re = NOT_PENDING;
|
|
294
316
|
}
|
|
295
317
|
} else {
|
|
296
|
-
e.
|
|
318
|
+
e.Re = _;
|
|
297
319
|
// A window landing that gets held re-opens the window until the hold
|
|
298
320
|
// commits — the verdict's held-value branch is window-gated (#2990).
|
|
299
|
-
if (
|
|
321
|
+
if (r) e.Ne = true;
|
|
300
322
|
// Transition-held sync recompute is a write path like setSignal/asyncWrite,
|
|
301
323
|
// so sync derivations of held sources stay visible to isPending()/latest()
|
|
302
324
|
// (#2831). Both companion writes are transition-scoped (optimistic) and
|
|
303
325
|
// auto-revert/re-derive at commit. Skipped for plain flushes where the
|
|
304
326
|
// pending value commits before effects run.
|
|
305
|
-
if ((activeTransition || e.
|
|
327
|
+
if ((activeTransition || e.Ae) && GlobalQueue.Ue !== null) GlobalQueue.Ue(e, _);
|
|
306
328
|
}
|
|
307
329
|
// insertSubs only walks _subs (no scheduling of its own), so a
|
|
308
330
|
// subscriber-less node has nothing to notify.
|
|
309
|
-
if (e.u !== null && (!u || i || e.o?.
|
|
331
|
+
if (e.u !== null && (!u || i || e.o?.Pe !== l)) insertSubs(e, i || u);
|
|
310
332
|
} else if (u) {
|
|
311
333
|
// Unchanged value (equals the override) recomputed while the override
|
|
312
334
|
// is active: _value may still be stale, so hold the authoritative value
|
|
313
335
|
// for commit on its own transition's schedule — invisibly (A17/A18).
|
|
314
|
-
if (e.
|
|
315
|
-
e.
|
|
316
|
-
if (
|
|
336
|
+
if (e.Re === NOT_PENDING) queuePendingNode(e);
|
|
337
|
+
e.Re = _;
|
|
338
|
+
if (r) e.Ne = true;
|
|
317
339
|
// see the held branch above (#2990)
|
|
318
|
-
|
|
340
|
+
// A authoritative-view reader (until()) observed this node past its
|
|
341
|
+
// override — and "authoritative arrival equal to the override" is
|
|
342
|
+
// exactly the acknowledgment it waits for. Wake those readers only;
|
|
343
|
+
// A17 silence holds for every ordinary subscriber. (Hook installed by
|
|
344
|
+
// until(), the only setter of the gating bit.)
|
|
345
|
+
if (e.T & CONFIG_AUTHORITATIVE_OBSERVED) GlobalQueue.he(e);
|
|
346
|
+
} else if (e.Me != f) {
|
|
319
347
|
for (let t = e.u; t !== null; t = t.ae) {
|
|
320
348
|
insertIntoHeapHeight(t.ce, queueFor(t.ce));
|
|
321
349
|
}
|
|
@@ -325,23 +353,27 @@ function recompute(e, t = false) {
|
|
|
325
353
|
// in an errored run and may sit on stale commits (#2949). Changed-value
|
|
326
354
|
// recoveries ride insertSubs above; a comparator throw re-errored the node
|
|
327
355
|
// (el._x?._error re-set), so this only runs on a genuinely clean recovery.
|
|
328
|
-
if (o !== undefined && !
|
|
356
|
+
if (o !== undefined && !c && !e.o?._) settleErroredDependents(e, o);
|
|
357
|
+
// #3181: a synchronous settle supersedes the old landing callback, so
|
|
358
|
+
// recompute owns its pending-source sweep. An uninitialized node without
|
|
359
|
+
// a replacement source still has no truth to reveal and must stay parked.
|
|
360
|
+
if (s && !(e.S & (STATUS_PENDING | STATUS_UNINITIALIZED))) settlePendingSource(e);
|
|
329
361
|
}
|
|
330
|
-
currentOptimisticLane =
|
|
331
|
-
const
|
|
362
|
+
currentOptimisticLane = N;
|
|
363
|
+
const A = e.Re !== NOT_PENDING || e.o !== null && (e.o.Ye !== null || e.o.qe !== null) || (e.S & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0;
|
|
332
364
|
// Override-covered holds (hasOverride) always queue: their commit belongs
|
|
333
365
|
// to their own transition's schedule (A18 re-rule) and is unobservable
|
|
334
366
|
// under the override (A17). Revert no longer commits anything, so an
|
|
335
367
|
// unqueued covered hold would leak (INV-7) once the revert clears
|
|
336
368
|
// _transition.
|
|
337
|
-
|
|
338
|
-
e.
|
|
369
|
+
A && (!t || e.S & STATUS_PENDING) && (!e.Ae || u) && queuePendingNode(e);
|
|
370
|
+
e.Ae && n && activeTransition !== e.Ae && runInTransition(e.Ae, () => recompute(e));
|
|
339
371
|
// Missed-wake reschedule (see the finally above): values this pass read
|
|
340
372
|
// before the nested commit are stale, so run again now that the heap will
|
|
341
373
|
// accept the node. Equality gates stop same-value landings from cascading,
|
|
342
374
|
// and a re-run only latches again if another nested commit changes a dep
|
|
343
375
|
// beneath it — convergent unless deps genuinely keep changing.
|
|
344
|
-
if (
|
|
376
|
+
if (I) {
|
|
345
377
|
enqueueSub(e);
|
|
346
378
|
schedule();
|
|
347
379
|
}
|
|
@@ -352,11 +384,13 @@ function updateIfNecessary(e) {
|
|
|
352
384
|
// (_depsTail/_depGen) is live, and a nested recompute would corrupt it.
|
|
353
385
|
// A mid-pass mark stays latched for recompute's own tail to reschedule
|
|
354
386
|
// (#3037); readers meanwhile serve the values the pass has so far.
|
|
355
|
-
|
|
387
|
+
// Never recompute a DISPOSED node either: recompute rewrites _flags and
|
|
388
|
+
// would resurrect it (#2983) — readers serve its last value.
|
|
389
|
+
if (e.ie & (REACTIVE_RECOMPUTING_DEPS | REACTIVE_DISPOSED)) return;
|
|
356
390
|
if (e.ie & REACTIVE_CHECK) {
|
|
357
|
-
for (let t = e.
|
|
358
|
-
const n = t.
|
|
359
|
-
const i = n.
|
|
391
|
+
for (let t = e.ut; t; t = t.lt) {
|
|
392
|
+
const n = t.ot;
|
|
393
|
+
const i = n.st || n;
|
|
360
394
|
if (i.oe) {
|
|
361
395
|
updateIfNecessary(i);
|
|
362
396
|
}
|
|
@@ -365,7 +399,7 @@ function updateIfNecessary(e) {
|
|
|
365
399
|
}
|
|
366
400
|
}
|
|
367
401
|
}
|
|
368
|
-
if (e.ie & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY) || e.o?._ && e.Te < clock && !e.o?.
|
|
402
|
+
if (e.ie & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY) || e.o?._ && e.Te < clock && !e.o?.Ie) {
|
|
369
403
|
recompute(e);
|
|
370
404
|
}
|
|
371
405
|
e.ie = e.ie & (REACTIVE_SNAPSHOT_STALE | REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT);
|
|
@@ -380,40 +414,40 @@ function computed(e, t) {
|
|
|
380
414
|
const u = {
|
|
381
415
|
id: inheritId(t, n, context),
|
|
382
416
|
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
|
-
|
|
417
|
+
pe: t?.equals ?? isEqual,
|
|
418
|
+
Ge: null,
|
|
385
419
|
C: context?.C ?? globalQueue,
|
|
386
420
|
we: context?.we ?? defaultContext,
|
|
387
|
-
|
|
421
|
+
Ze: 0,
|
|
388
422
|
oe: e,
|
|
389
423
|
be: i ? t.loadingValue : undefined,
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
424
|
+
Me: 0,
|
|
425
|
+
rt: undefined,
|
|
426
|
+
ct: null,
|
|
427
|
+
ut: null,
|
|
428
|
+
je: null,
|
|
429
|
+
Ke: 0,
|
|
396
430
|
u: null,
|
|
397
|
-
|
|
431
|
+
_t: null,
|
|
398
432
|
ke: context,
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
433
|
+
Le: null,
|
|
434
|
+
ft: null,
|
|
435
|
+
xe: null,
|
|
402
436
|
ie: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
|
|
403
437
|
// A loadingValue node is born committed: commit #0 is already in _value.
|
|
404
438
|
S: i ? 0 : STATUS_UNINITIALIZED,
|
|
405
439
|
Te: clock,
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
440
|
+
Re: NOT_PENDING,
|
|
441
|
+
Ae: null,
|
|
442
|
+
It: -1,
|
|
443
|
+
Ne: i,
|
|
410
444
|
// Cold machinery (async/transition/optimistic/verdict slots) lives one
|
|
411
445
|
// hop away in the lazily-allocated extension — the core literal MUST
|
|
412
446
|
// stay under V8's in-object boundary (§12: past ~39 fields every
|
|
413
447
|
// allocation spills to a backing store and creation cost ~4x's).
|
|
414
448
|
o: null
|
|
415
449
|
};
|
|
416
|
-
if (t?.unobserved) ext(u).
|
|
450
|
+
if (t?.unobserved) ext(u).Et = t.unobserved;
|
|
417
451
|
setupComputedNode(u, t);
|
|
418
452
|
return u;
|
|
419
453
|
}
|
|
@@ -423,25 +457,26 @@ function computed(e, t) {
|
|
|
423
457
|
* this; hot paths read `el._x?._field` gated by the _config presence bits.
|
|
424
458
|
* Never call ext() just to store a field's default. */ function ext(e) {
|
|
425
459
|
return e.o ??= {
|
|
426
|
-
|
|
460
|
+
Pe: undefined,
|
|
427
461
|
Nt: undefined,
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
462
|
+
Je: undefined,
|
|
463
|
+
ye: undefined,
|
|
464
|
+
Ce: undefined,
|
|
465
|
+
Tt: undefined,
|
|
432
466
|
t: 0,
|
|
467
|
+
Ie: null,
|
|
433
468
|
Ee: null,
|
|
434
469
|
_: undefined,
|
|
435
470
|
fe: undefined,
|
|
436
471
|
le: undefined,
|
|
437
472
|
h: undefined,
|
|
438
|
-
|
|
473
|
+
De: false,
|
|
439
474
|
i: null,
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
We: null,
|
|
475
|
+
Et: undefined,
|
|
476
|
+
We: undefined,
|
|
443
477
|
qe: null,
|
|
444
|
-
|
|
478
|
+
Ye: null,
|
|
479
|
+
St: undefined
|
|
445
480
|
};
|
|
446
481
|
}
|
|
447
482
|
|
|
@@ -454,39 +489,39 @@ function computed(e, t) {
|
|
|
454
489
|
const l = u?.transparent ?? false;
|
|
455
490
|
const o = {
|
|
456
491
|
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
|
-
|
|
492
|
+
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),
|
|
493
|
+
pe: false,
|
|
494
|
+
Ge: null,
|
|
460
495
|
C: context?.C ?? globalQueue,
|
|
461
496
|
we: context?.we ?? defaultContext,
|
|
462
|
-
|
|
497
|
+
Ze: 0,
|
|
463
498
|
oe: e,
|
|
464
499
|
be: undefined,
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
500
|
+
Me: 0,
|
|
501
|
+
rt: undefined,
|
|
502
|
+
ct: null,
|
|
503
|
+
ut: null,
|
|
504
|
+
je: null,
|
|
505
|
+
Ke: 0,
|
|
471
506
|
u: null,
|
|
472
|
-
|
|
507
|
+
_t: null,
|
|
473
508
|
ke: context,
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
509
|
+
Le: null,
|
|
510
|
+
ft: null,
|
|
511
|
+
xe: null,
|
|
477
512
|
ie: REACTIVE_LAZY,
|
|
478
513
|
S: STATUS_UNINITIALIZED,
|
|
479
514
|
Te: clock,
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
dt: undefined,
|
|
486
|
-
Tt: t,
|
|
487
|
-
St: n,
|
|
515
|
+
Re: NOT_PENDING,
|
|
516
|
+
Ae: null,
|
|
517
|
+
It: -1,
|
|
518
|
+
Ne: false,
|
|
519
|
+
tt: false,
|
|
488
520
|
At: undefined,
|
|
489
|
-
|
|
521
|
+
Ot: t,
|
|
522
|
+
Ct: n,
|
|
523
|
+
Rt: undefined,
|
|
524
|
+
ge: i,
|
|
490
525
|
o: null
|
|
491
526
|
};
|
|
492
527
|
// Effects dispatch status through the SHARED notifier (statusNotifierOf,
|
|
@@ -494,7 +529,7 @@ function computed(e, t) {
|
|
|
494
529
|
// allocation on EVERY effect at creation (an alloc + 19 field stores,
|
|
495
530
|
// +23% effect creation, caught by the creation benches). Only genuinely
|
|
496
531
|
// per-node channels (boundaries) live on _x.
|
|
497
|
-
if (u?.unobserved) ext(o).
|
|
532
|
+
if (u?.unobserved) ext(o).Et = u.unobserved;
|
|
498
533
|
setupComputedNode(o, lazyOptions);
|
|
499
534
|
return o;
|
|
500
535
|
}
|
|
@@ -515,10 +550,9 @@ function setEffectStatusNotify(e) {
|
|
|
515
550
|
* the field) fall back to the shared notifier. Presence doubles as the
|
|
516
551
|
* "display consumer" membership test in the status walks, exactly as the
|
|
517
552
|
* 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;
|
|
553
|
+
const t = e.o?.h;
|
|
554
|
+
if (t !== undefined) return t;
|
|
555
|
+
return e.ge ? effectStatusNotify ?? undefined : undefined;
|
|
522
556
|
}
|
|
523
557
|
|
|
524
558
|
const lazyOptions = {
|
|
@@ -526,24 +560,24 @@ const lazyOptions = {
|
|
|
526
560
|
};
|
|
527
561
|
|
|
528
562
|
function setupComputedNode(e, t) {
|
|
529
|
-
e.
|
|
530
|
-
const n = context?.
|
|
563
|
+
e.ct = e;
|
|
564
|
+
const n = context?.Gt ? context.Dt : context;
|
|
531
565
|
if (context) {
|
|
532
|
-
const t = context.
|
|
566
|
+
const t = context.xe;
|
|
533
567
|
if (t === null) {
|
|
534
|
-
context.
|
|
568
|
+
context.xe = e;
|
|
535
569
|
} else {
|
|
536
|
-
e.
|
|
537
|
-
t.
|
|
538
|
-
context.
|
|
570
|
+
e.Le = t;
|
|
571
|
+
t.ft = e;
|
|
572
|
+
context.xe = e;
|
|
539
573
|
}
|
|
540
574
|
}
|
|
541
|
-
if (n) e.
|
|
542
|
-
if (GlobalQueue.
|
|
575
|
+
if (n) e.Me = n.Me + 1;
|
|
576
|
+
if (GlobalQueue.Pt !== null) GlobalQueue.Pt(e);
|
|
543
577
|
!t?.lazy && recompute(e, true);
|
|
544
578
|
if (snapshotCaptureActive && !t?.lazy) {
|
|
545
579
|
if (!(e.S & STATUS_PENDING) && !(e.T & CONFIG_NO_SNAPSHOT)) {
|
|
546
|
-
ext(e).
|
|
580
|
+
ext(e).We = e.be === undefined ? NO_SNAPSHOT : e.be;
|
|
547
581
|
e.T |= CONFIG_HAS_SNAPSHOT;
|
|
548
582
|
snapshotSources.add(e);
|
|
549
583
|
}
|
|
@@ -552,31 +586,31 @@ function setupComputedNode(e, t) {
|
|
|
552
586
|
|
|
553
587
|
function signal(e, t, n = null) {
|
|
554
588
|
const i = {
|
|
555
|
-
|
|
589
|
+
pe: t?.equals ?? isEqual,
|
|
556
590
|
T: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.H ? CONFIG_NO_SNAPSHOT : 0),
|
|
557
591
|
be: e,
|
|
558
592
|
u: null,
|
|
559
|
-
|
|
593
|
+
_t: null,
|
|
560
594
|
Te: clock,
|
|
561
|
-
|
|
595
|
+
st: n,
|
|
562
596
|
Se: n?.o?.i || null,
|
|
563
|
-
|
|
597
|
+
Re: NOT_PENDING,
|
|
564
598
|
// Signal-literal diet (§12e): NO _time/_fn/_statusFlags slots. Stores
|
|
565
599
|
// materialize one signal per touched leaf, so signal bytes are store
|
|
566
600
|
// bytes. _time is write-only on signals (every read site is computed-
|
|
567
601
|
// typed error-retry gating); _fn/_statusFlags read falsy-identically as
|
|
568
602
|
// missing properties on the shared paths (undefined masks to 0).
|
|
569
|
-
|
|
570
|
-
|
|
603
|
+
Ae: null,
|
|
604
|
+
It: -1,
|
|
571
605
|
o: null
|
|
572
606
|
};
|
|
573
|
-
if (t?.unobserved) ext(i).
|
|
607
|
+
if (t?.unobserved) ext(i).Et = t.unobserved;
|
|
574
608
|
if (n) {
|
|
575
609
|
ext(n).i = i;
|
|
576
610
|
n.T |= CONFIG_FW_CHILDREN;
|
|
577
611
|
}
|
|
578
612
|
if (snapshotCaptureActive && !(i.T & CONFIG_NO_SNAPSHOT) && !((n?.S ?? 0) & STATUS_PENDING)) {
|
|
579
|
-
ext(i).
|
|
613
|
+
ext(i).We = e === undefined ? NO_SNAPSHOT : e;
|
|
580
614
|
i.T |= CONFIG_HAS_SNAPSHOT;
|
|
581
615
|
snapshotSources.add(i);
|
|
582
616
|
}
|
|
@@ -585,14 +619,14 @@ function signal(e, t, n = null) {
|
|
|
585
619
|
|
|
586
620
|
function optimisticSignal(e, t) {
|
|
587
621
|
const n = signal(e, t);
|
|
588
|
-
ext(n).
|
|
622
|
+
ext(n).Pe = NOT_PENDING;
|
|
589
623
|
n.T |= CONFIG_OPTIMISTIC;
|
|
590
624
|
return n;
|
|
591
625
|
}
|
|
592
626
|
|
|
593
627
|
function optimisticComputed(e, t) {
|
|
594
628
|
const n = computed(e, t);
|
|
595
|
-
ext(n).
|
|
629
|
+
ext(n).Pe = NOT_PENDING;
|
|
596
630
|
n.T |= CONFIG_OPTIMISTIC;
|
|
597
631
|
return n;
|
|
598
632
|
}
|
|
@@ -623,11 +657,11 @@ function isEqual(e, t) {
|
|
|
623
657
|
* );
|
|
624
658
|
* ```
|
|
625
659
|
*/ function untrack(e, t) {
|
|
626
|
-
if (GlobalQueue.
|
|
660
|
+
if (GlobalQueue.ht === null && !tracking && true) return e();
|
|
627
661
|
const n = tracking;
|
|
628
662
|
tracking = false;
|
|
629
663
|
try {
|
|
630
|
-
if (GlobalQueue.
|
|
664
|
+
if (GlobalQueue.ht !== null) return GlobalQueue.ht(e);
|
|
631
665
|
return e();
|
|
632
666
|
} finally {
|
|
633
667
|
tracking = n;
|
|
@@ -668,16 +702,44 @@ function isEqual(e, t) {
|
|
|
668
702
|
* fast path, `_fn` nodes need prepareComputed, and firewall / override /
|
|
669
703
|
* snapshot / transition / lane / dev-strictRead state all take the full
|
|
670
704
|
* resolution. Anything slow returns READ_SLOW; the caller then calls read().
|
|
671
|
-
*/
|
|
672
|
-
|
|
705
|
+
*/
|
|
706
|
+
/**
|
|
707
|
+
* Wake only authoritative-view readers (until() predicates) subscribed to `el`.
|
|
708
|
+
* The A17-silent ack paths — an authoritative arrival equal to the active
|
|
709
|
+
* override — use this so the predicate re-evaluates without re-firing
|
|
710
|
+
* ordinary subscribers whose visible (override) value did not change.
|
|
711
|
+
* Pay-for-use: reached through GlobalQueue._notifyAuthoritativeObservers,
|
|
712
|
+
* installed at first until() call — apps that never use until() shake it.
|
|
713
|
+
*/ function notifyAuthoritativeObservers(e) {
|
|
714
|
+
for (let t = e.u; t !== null; t = t.ae) {
|
|
715
|
+
const e = t.ce;
|
|
716
|
+
if (!(e.T & CONFIG_AUTHORITATIVE_READ)) continue;
|
|
717
|
+
// Missed-wake latch (#3037), same contract as insertSubs: the reader may
|
|
718
|
+
// itself have pulled this recompute (updateIfNecessary from its own
|
|
719
|
+
// read), and the heap refuses RECOMPUTING nodes — latch so recompute's
|
|
720
|
+
// tail reschedules it with the staged value visible.
|
|
721
|
+
if (e.ie & REACTIVE_RECOMPUTING_DEPS && t.Ft === e.Ke && t !== e.je) e.ie |= REACTIVE_MISSED_WAKE;
|
|
722
|
+
enqueueSub(e);
|
|
723
|
+
}
|
|
724
|
+
schedule();
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/** Installs the until() machinery hook. Idempotent; called by until() before
|
|
728
|
+
* any authoritative-view read happens (same late-binding contract as the
|
|
729
|
+
* optimistic engine). */ function installAuthoritativeRead() {
|
|
730
|
+
if (GlobalQueue.he === null) GlobalQueue.he = notifyAuthoritativeObservers;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function readNodeFast(e) {
|
|
734
|
+
if (latestReadActive || pendingCheckActive || e.oe || e.st || e.o?.Pe !== undefined || e.o?.We !== undefined || activeTransition !== null || currentOptimisticLane !== null || snapshotCaptureActive || false) return READ_SLOW;
|
|
673
735
|
let t = context;
|
|
674
|
-
if (t?.
|
|
736
|
+
if (t?.Gt) t = t.Dt;
|
|
675
737
|
if (t && tracking) link(e, t);
|
|
676
738
|
// Children-forbidden readers (createTrackedEffect / onSettled callbacks) get
|
|
677
739
|
// committed visibility: like the effect half of createEffect and event
|
|
678
740
|
// handlers, effect-phase code never observes its own unsettled write — the
|
|
679
741
|
// write lands in the same flush's continuation (#3006).
|
|
680
|
-
return !t || e.
|
|
742
|
+
return !t || e.Re === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN ? e.be : e.Re;
|
|
681
743
|
}
|
|
682
744
|
|
|
683
745
|
function read(e) {
|
|
@@ -685,47 +747,54 @@ function read(e) {
|
|
|
685
747
|
// Checked before isPending so that isPending(() => latest(x)) checks
|
|
686
748
|
// the _pendingSignal of _latestValueComputed (async in flight) rather
|
|
687
749
|
// than the original node (which stays "pending" while held in a transition).
|
|
688
|
-
if (latestReadActive) return GlobalQueue.
|
|
750
|
+
if (latestReadActive) return GlobalQueue.gt(e);
|
|
689
751
|
let t = context;
|
|
690
|
-
if (t?.
|
|
752
|
+
if (t?.Gt) t = t.Dt;
|
|
691
753
|
const n = e;
|
|
692
|
-
const i = e.
|
|
754
|
+
const i = e.st;
|
|
693
755
|
const u = i || e;
|
|
694
756
|
// Handle isPending() mode: collect pending state while preserving normal read semantics.
|
|
695
757
|
// Probe mode is suspended while preparing the node so nested reads during a
|
|
696
758
|
// recompute don't collect into the probe.
|
|
697
759
|
if (pendingCheckActive) {
|
|
698
|
-
GlobalQueue.
|
|
760
|
+
GlobalQueue.Ht(e, t, u, i);
|
|
699
761
|
} else if (typeof n.oe === "function") {
|
|
700
762
|
prepareComputed(e, false);
|
|
701
763
|
}
|
|
702
|
-
if (!n.oe && u === e && e.o?.
|
|
764
|
+
if (!n.oe && u === e && e.o?.Pe === undefined && e.o?.We === undefined && activeTransition === null && currentOptimisticLane === null && !snapshotCaptureActive && true) {
|
|
703
765
|
if (t && tracking) link(e, t);
|
|
704
766
|
// Committed visibility for children-forbidden readers — see readNodeFast.
|
|
705
|
-
return !t || e.
|
|
767
|
+
return !t || e.Re === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN ? e.be : e.Re;
|
|
706
768
|
}
|
|
707
769
|
if (t && tracking) {
|
|
708
770
|
link(e, t, pendingCheckActive);
|
|
709
771
|
if (u.oe) {
|
|
710
772
|
const n = queueFor(e);
|
|
711
|
-
if (u.
|
|
773
|
+
if (u.Me >= n.Qe) {
|
|
712
774
|
markNode(t);
|
|
713
775
|
markHeap(n);
|
|
714
776
|
updateIfNecessary(u);
|
|
715
777
|
}
|
|
716
|
-
|
|
778
|
+
// Fresh-pull readers (awaitable refresh's waiter) recompute a dirty
|
|
779
|
+
// source inline even when the height gate defers to the flush: the
|
|
780
|
+
// waiter must park on the re-ask's window (or serve its sync answer),
|
|
781
|
+
// never read the PRE-re-ask value as settled. Self-guarded: a clean
|
|
782
|
+
// node no-ops and updateIfNecessary refuses disposed nodes (#2983) —
|
|
783
|
+
// a dead target serves its last value, which is already quiescent.
|
|
784
|
+
else if (t.T & CONFIG_FRESH_READ) updateIfNecessary(u);
|
|
785
|
+
const i = u.Me;
|
|
717
786
|
// parent check is shallow, might need to be recursive
|
|
718
|
-
if (i >= t.
|
|
719
|
-
t.
|
|
787
|
+
if (i >= t.Me && e.ke !== t) {
|
|
788
|
+
t.Me = i + 1;
|
|
720
789
|
}
|
|
721
790
|
}
|
|
722
791
|
}
|
|
723
792
|
if (u.S & STATUS_PENDING) {
|
|
724
|
-
if (t && !(stale && u.
|
|
793
|
+
if (t && !(stale && u.Ae && activeTransition !== u.Ae)) {
|
|
725
794
|
// Per-lane suspension lives with the engine (a non-null lane implies it
|
|
726
795
|
// is installed): under a lane, only same-lane pending async without an
|
|
727
796
|
// active override throws.
|
|
728
|
-
if (currentOptimisticLane === null || GlobalQueue.
|
|
797
|
+
if (currentOptimisticLane === null || GlobalQueue.Vt(u)) {
|
|
729
798
|
if (!tracking && e !== t) link(e, t);
|
|
730
799
|
throw u.o?._;
|
|
731
800
|
}
|
|
@@ -761,17 +830,27 @@ function read(e) {
|
|
|
761
830
|
} else throw u.o?._;
|
|
762
831
|
}
|
|
763
832
|
if (snapshotCaptureActive && t && t.T & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
764
|
-
const n = e.o?.
|
|
833
|
+
const n = e.o?.We;
|
|
765
834
|
if (n !== undefined) {
|
|
766
835
|
const i = n === NO_SNAPSHOT ? undefined : n;
|
|
767
|
-
const u = e.
|
|
836
|
+
const u = e.Re !== NOT_PENDING ? e.Re : e.be;
|
|
768
837
|
if (u !== i) t.ie |= REACTIVE_SNAPSHOT_STALE;
|
|
769
838
|
return i;
|
|
770
839
|
}
|
|
771
840
|
}
|
|
772
|
-
if (e.o?.
|
|
773
|
-
// A17: the override IS the value for every reader
|
|
774
|
-
|
|
841
|
+
if (e.o?.Pe !== undefined && e.o?.Pe !== NOT_PENDING) {
|
|
842
|
+
// A17: the override IS the value for every reader — except an authoritative
|
|
843
|
+
// reader (until()'s predicate carries CONFIG_AUTHORITATIVE_READ): it must
|
|
844
|
+
// observe independently-arriving truth, and serving it the caller's own
|
|
845
|
+
// tentative write would trivially satisfy the predicate. The bit is checked
|
|
846
|
+
// on the reading computation itself, so a shared computed the predicate
|
|
847
|
+
// pulls recomputes as ITSELF (context = the memo, no bit) under the normal
|
|
848
|
+
// view. Fall through to normal value selection (staged `_pendingValue` is
|
|
849
|
+
// authoritative — optimism never lives there); the sticky mark makes the
|
|
850
|
+
// A17-silent "landing equals override" paths notify this node's subs so
|
|
851
|
+
// the reader re-runs when truth arrives.
|
|
852
|
+
if (!(t && t.T & CONFIG_AUTHORITATIVE_READ)) return unwrapOverride(e.o?.Pe);
|
|
853
|
+
e.T |= CONFIG_AUTHORITATIVE_OBSERVED;
|
|
775
854
|
}
|
|
776
855
|
// Entanglement gate: a reader recomputing under an optimistic lane that reads
|
|
777
856
|
// a pending mid-transition write sees the committed value. Projection-store
|
|
@@ -780,7 +859,7 @@ function read(e) {
|
|
|
780
859
|
// _pendingValue for correct fetching. The sub is recorded for replay at commit
|
|
781
860
|
// so it re-runs with the new committed view. (Gate details live with the
|
|
782
861
|
// engine — a non-null lane implies it is installed.)
|
|
783
|
-
if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.
|
|
862
|
+
if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.vt(e, u, t)) {
|
|
784
863
|
return e.be;
|
|
785
864
|
}
|
|
786
865
|
// In optimistic lane context, return _value for optimistic/lane-assigned signals
|
|
@@ -789,10 +868,19 @@ function read(e) {
|
|
|
789
868
|
// (The lane-context clause lives with the engine.) Children-forbidden readers
|
|
790
869
|
// (createTrackedEffect / onSettled callbacks) get committed visibility — see
|
|
791
870
|
// readNodeFast (#3006).
|
|
792
|
-
const l = !t || currentOptimisticLane !== null && GlobalQueue.
|
|
871
|
+
const l = !t || currentOptimisticLane !== null && GlobalQueue.bt(e, u, t) || e.Re === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN || stale && e.Ae && activeTransition !== e.Ae ||
|
|
872
|
+
// A17 for HELD truth (#3164, see CONFIG_HELD_TRUTH): staged confirming
|
|
873
|
+
// truth — fold-staged onto an armed family, or entangle-stolen by an
|
|
874
|
+
// awaited until() — is masked from ordinary readers until its
|
|
875
|
+
// transaction's reveal; the retaining transaction's own speculative
|
|
876
|
+
// recomputes included (partial override coverage would otherwise
|
|
877
|
+
// compose override + staged truth into a state no timeline contains).
|
|
878
|
+
// Authoritative readers (until()'s predicate) and latest() see the
|
|
879
|
+
// staged truth — the tunnel that keeps the hold deadlock-free.
|
|
880
|
+
e.T & CONFIG_HELD_TRUTH && !latestReadActive && !(t.T & CONFIG_AUTHORITATIVE_READ) ? e.be : e.Re;
|
|
793
881
|
// Record that this isPending() probe observed the fresh pending value, so
|
|
794
882
|
// the probe doesn't pair "pending" with the new value (#2831).
|
|
795
|
-
if (pendingCheckActive) GlobalQueue.
|
|
883
|
+
if (pendingCheckActive) GlobalQueue.kt(e, l);
|
|
796
884
|
if (!t && u === e && typeof n.oe === "function" && e.T & CONFIG_AUTO_DISPOSE && !(u.S & STATUS_PENDING) && !e.u) {
|
|
797
885
|
// Deferred, not inline (#3078): an inline unobserved() here made untracked
|
|
798
886
|
// reads destructive — dispose on this read, full revival recompute on the
|
|
@@ -807,28 +895,28 @@ function read(e) {
|
|
|
807
895
|
}
|
|
808
896
|
|
|
809
897
|
function setSignal(e, t) {
|
|
810
|
-
if (e.
|
|
898
|
+
if (e.Ae && activeTransition !== e.Ae) globalQueue.initTransition(e.Ae);
|
|
811
899
|
// The optimistic write path lives with the engine: only optimisticSignal /
|
|
812
900
|
// optimisticComputed callers and optimistic store nodes carry an
|
|
813
901
|
// _overrideValue slot (flagged by CONFIG_OPTIMISTIC — a masked read of the
|
|
814
902
|
// always-present config instead of a missing-property probe), and every
|
|
815
903
|
// module that installs one installs the engine first.
|
|
816
|
-
if (e.T & CONFIG_OPTIMISTIC && !projectionWriteActive) return GlobalQueue.
|
|
817
|
-
const n = e.
|
|
904
|
+
if (e.T & CONFIG_OPTIMISTIC && !projectionWriteActive) return GlobalQueue.xt(e, t);
|
|
905
|
+
const n = e.Re === NOT_PENDING ? e.be : e.Re;
|
|
818
906
|
if (typeof t === "function") t = t(n);
|
|
819
907
|
// Uninitialized check first: the first commit has no previous value, so the
|
|
820
908
|
// user comparator must not run against `undefined` (matches recompute).
|
|
821
|
-
const i = !!(e.S & STATUS_UNINITIALIZED) || !e.
|
|
909
|
+
const i = !!(e.S & STATUS_UNINITIALIZED) || !e.pe || !e.pe(n, t);
|
|
822
910
|
if (!i) return t;
|
|
823
|
-
const u = e.
|
|
911
|
+
const u = e.Re !== NOT_PENDING;
|
|
824
912
|
if (!u) queuePendingNode(e);
|
|
825
|
-
e.
|
|
913
|
+
e.Re = t;
|
|
826
914
|
// syncCompanions only pokes _pendingSignal/_latestValueComputed — with
|
|
827
915
|
// neither companion present the call is a guaranteed no-op (companions are
|
|
828
916
|
// only ever created, never removed, and creating one installs the hook and
|
|
829
917
|
// sets CONFIG_HAS_COMPANIONS — one masked read replaces two optional-field
|
|
830
918
|
// probes on every write).
|
|
831
|
-
e.T & CONFIG_HAS_COMPANIONS && GlobalQueue.
|
|
919
|
+
e.T & CONFIG_HAS_COMPANIONS && GlobalQueue.Ue !== null && GlobalQueue.Ue(e, t);
|
|
832
920
|
// _time is a computed-only slot (§12e): writing it on a signal would fork
|
|
833
921
|
// the lean shape. Every read site is computed-typed.
|
|
834
922
|
if (e.oe !== undefined) e.Te = clock;
|
|
@@ -837,7 +925,7 @@ function setSignal(e, t) {
|
|
|
837
925
|
// (epoch) — re-stages the value and stops. The walk is idempotent (subs
|
|
838
926
|
// marked, heap entries flag-guarded, effects queued once); lane and reask
|
|
839
927
|
// contexts change what a walk MEANS, so they always walk.
|
|
840
|
-
if (u && e.
|
|
928
|
+
if (u && e.It === notifyEpoch && currentOptimisticLane === null && !reaskArmed) return t;
|
|
841
929
|
insertSubs(e);
|
|
842
930
|
schedule();
|
|
843
931
|
return t;
|
|
@@ -851,12 +939,12 @@ function setSignal(e, t) {
|
|
|
851
939
|
* cleanup point.
|
|
852
940
|
*/ function suppressComputedRecompute(e) {
|
|
853
941
|
deleteFromHeap(e, queueFor(e));
|
|
854
|
-
if (!(e.ie & REACTIVE_MANUAL_WRITE) && e.
|
|
942
|
+
if (!(e.ie & REACTIVE_MANUAL_WRITE) && e.Re === NOT_PENDING) {
|
|
855
943
|
queuePendingNode(e);
|
|
856
944
|
schedule();
|
|
857
945
|
}
|
|
858
946
|
e.ie = e.ie & -4 | REACTIVE_MANUAL_WRITE;
|
|
859
|
-
e.
|
|
947
|
+
e.Ut = clock;
|
|
860
948
|
}
|
|
861
949
|
|
|
862
950
|
/**
|
|
@@ -912,39 +1000,22 @@ function staleValues(e, t = true) {
|
|
|
912
1000
|
}
|
|
913
1001
|
|
|
914
1002
|
/**
|
|
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) {
|
|
1003
|
+
* Core marking half of `refresh()` (the public wrapper lives in signals.ts —
|
|
1004
|
+
* it validates the target, marks through here, then builds the quiescence
|
|
1005
|
+
* promise on the resolve()/until() effect machinery). Flags the node's next
|
|
1006
|
+
* recompute as a quiet re-ask and schedules it; no-ops for non-derived or
|
|
1007
|
+
* disposed targets and for same-tick manual writes.
|
|
1008
|
+
*/ function markRefresh(e) {
|
|
1009
|
+
if (typeof e.oe === "function" && !(e.ie & REACTIVE_DISPOSED)) {
|
|
1010
|
+
if (e.ie & REACTIVE_MANUAL_WRITE) {
|
|
940
1011
|
// A manual write in the CURRENT tick wins over the refresh (#2692).
|
|
941
1012
|
// A mask stamped in an earlier tick only survives because a
|
|
942
1013
|
// transaction (action) is holding the pending drain open; there the
|
|
943
1014
|
// refresh is a later, explicit re-ask and lifts the mask — otherwise
|
|
944
1015
|
// any setStore early in an action silently swallows every refresh()
|
|
945
1016
|
// for the rest of the transaction (#3026).
|
|
946
|
-
if (
|
|
947
|
-
|
|
1017
|
+
if (e.Ut === clock) return;
|
|
1018
|
+
e.ie &= ~REACTIVE_MANUAL_WRITE;
|
|
948
1019
|
// No REASK below: the batch carries a manual value change, so the
|
|
949
1020
|
// recompute is not a quiet re-ask of an unchanged question.
|
|
950
1021
|
}
|
|
@@ -955,14 +1026,14 @@ function staleValues(e, t = true) {
|
|
|
955
1026
|
// REACTIVE_IN_HEAP counts as dirt: insertSubs schedules subscribers by
|
|
956
1027
|
// heap insertion alone (no DIRTY/CHECK flag), so a same-batch value
|
|
957
1028
|
// change followed by refresh() must not be laundered into a quiet re-ask.
|
|
958
|
-
else if (!(
|
|
959
|
-
|
|
1029
|
+
else if (!(e.ie & (REACTIVE_DIRTY | REACTIVE_CHECK | REACTIVE_IN_HEAP))) {
|
|
1030
|
+
e.ie |= REACTIVE_REASK;
|
|
960
1031
|
armReaskClear();
|
|
961
1032
|
}
|
|
962
|
-
|
|
963
|
-
insertIntoHeap(
|
|
1033
|
+
e.ie = e.ie & ~REACTIVE_CHECK | REACTIVE_DIRTY;
|
|
1034
|
+
insertIntoHeap(e, queueFor(e));
|
|
964
1035
|
schedule();
|
|
965
1036
|
}
|
|
966
1037
|
}
|
|
967
1038
|
|
|
968
|
-
export { READ_SLOW, clearSnapshots, computed, context, createEffectNode, currentOptimisticLane, effectStatusNotify, ext, isEqual, latestReadActive, markSnapshotScope, optimisticComputed, optimisticSignal, pendingCheckActive, prepareComputed, read, readNodeFast, recompute,
|
|
1039
|
+
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 };
|