@solidjs/signals 2.0.0-rc.1 → 2.0.0-rc.2
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 +1661 -361
- package/dist/node.cjs +1939 -1331
- package/dist/prod/affects.js +16 -16
- package/dist/prod/boundaries.js +90 -90
- package/dist/prod/core/action.js +3 -3
- package/dist/prod/core/async.js +74 -72
- package/dist/prod/core/constants.js +39 -1
- package/dist/prod/core/core.js +332 -224
- package/dist/prod/core/effect.js +56 -56
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +65 -54
- package/dist/prod/core/heap.js +52 -44
- package/dist/prod/core/invariants.js +3 -2
- package/dist/prod/core/lanes.js +41 -34
- package/dist/prod/core/optimistic.js +63 -60
- package/dist/prod/core/owner.js +97 -96
- package/dist/prod/core/scheduler.js +243 -194
- package/dist/prod/core/verdict.js +184 -77
- package/dist/prod/map.js +104 -104
- package/dist/prod/signals.js +1 -1
- package/dist/prod/store/next/optimistic.js +31 -23
- package/dist/prod/store/next/projection.js +3 -3
- package/dist/prod/store/next/reconcile.js +78 -74
- package/dist/prod/store/next/store.js +357 -90
- package/dist/prod/store/store.js +7 -7
- package/dist/types/core/attribution-hooks.d.ts +52 -0
- package/dist/types/core/attribution.d.ts +186 -0
- package/dist/types/core/constants.d.ts +26 -0
- package/dist/types/core/core.d.ts +8 -1
- package/dist/types/core/dev.d.ts +20 -3
- package/dist/types/core/graph.d.ts +1 -0
- package/dist/types/core/lanes.d.ts +4 -16
- package/dist/types/core/scheduler.d.ts +8 -0
- package/dist/types/core/types.d.ts +85 -41
- package/dist/types/store/next/projection.d.ts +0 -16
- package/dist/types/store/next/store.d.ts +6 -0
- package/dist/types/store/next/target.d.ts +18 -0
- package/dist/types-cjs/core/attribution-hooks.d.cts +52 -0
- package/dist/types-cjs/core/attribution.d.cts +186 -0
- package/dist/types-cjs/core/constants.d.cts +26 -0
- package/dist/types-cjs/core/core.d.cts +8 -1
- package/dist/types-cjs/core/dev.d.cts +20 -3
- package/dist/types-cjs/core/graph.d.cts +1 -0
- package/dist/types-cjs/core/lanes.d.cts +4 -16
- package/dist/types-cjs/core/scheduler.d.cts +8 -0
- package/dist/types-cjs/core/types.d.cts +85 -41
- package/dist/types-cjs/store/next/projection.d.cts +0 -16
- package/dist/types-cjs/store/next/store.d.cts +6 -0
- package/dist/types-cjs/store/next/target.d.cts +18 -0
- package/package.json +1 -1
package/dist/prod/core/async.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { STATUS_UNINITIALIZED, STATUS_ERROR, STATUS_PENDING, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_DISPOSED } from "./constants.js";
|
|
1
|
+
import { STATUS_UNINITIALIZED, CONFIG_CHILD_COMPANIONS, STATUS_ERROR, STATUS_PENDING, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_DISPOSED } from "./constants.js";
|
|
2
2
|
|
|
3
|
-
import { untrack, setSignal, context } from "./core.js";
|
|
3
|
+
import { untrack, ext, setSignal, context } from "./core.js";
|
|
4
4
|
|
|
5
5
|
import "./invariants.js";
|
|
6
6
|
|
|
@@ -22,20 +22,20 @@ import { GlobalQueue, schedule, flush, queuePendingNode, insertSubs, clock, curr
|
|
|
22
22
|
// overlapping source landed beside the Set and removePendingSource refused
|
|
23
23
|
// to clear it, stranding the Set members' pending forever (#2893).
|
|
24
24
|
function addPendingSource(e, n) {
|
|
25
|
-
if (e.
|
|
26
|
-
(e.
|
|
25
|
+
if (e.o?.le?.has(n)) return false;
|
|
26
|
+
(ext(e).le ??= new Set).add(n);
|
|
27
27
|
return true;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function removePendingSource(e, n) {
|
|
31
|
-
if (!e.
|
|
32
|
-
if (e.
|
|
31
|
+
if (!e.o?.le?.delete(n)) return false;
|
|
32
|
+
if (e.o?.le.size === 0) if (e.o !== null) e.o.le = undefined;
|
|
33
33
|
return true;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
function clearPendingSources(e) {
|
|
37
|
-
e.
|
|
38
|
-
e.
|
|
37
|
+
e.o?.le?.clear();
|
|
38
|
+
if (e.o !== null) e.o.le = undefined;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
@@ -45,7 +45,7 @@ function clearPendingSources(e) {
|
|
|
45
45
|
* with NO read-visible pending status, no downstream propagation, no
|
|
46
46
|
* transition, no lane registration. Commit #0 keeps serving.
|
|
47
47
|
*/ function parkLoadingWindow(e, n) {
|
|
48
|
-
e.
|
|
48
|
+
ext(e).ue = true;
|
|
49
49
|
if (n.source) addPendingSource(e, n.source);
|
|
50
50
|
// A settled error is the node's answer ("the error stays the answer until
|
|
51
51
|
// this retry can actually run") — the park must not replace it: reads
|
|
@@ -56,24 +56,24 @@ function clearPendingSources(e) {
|
|
|
56
56
|
|
|
57
57
|
function setPendingError(e, n, t) {
|
|
58
58
|
if (!n) {
|
|
59
|
-
e._ = null;
|
|
59
|
+
if (e.o !== null) e.o._ = null;
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
62
|
if (t instanceof NotReadyError && t.source === n) {
|
|
63
|
-
e._ = t;
|
|
63
|
+
ext(e)._ = t;
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
|
-
const r = e._;
|
|
66
|
+
const r = e.o?._;
|
|
67
67
|
if (!(r instanceof NotReadyError) || r.source !== n) {
|
|
68
|
-
e._ = new NotReadyError(n);
|
|
68
|
+
ext(e)._ = new NotReadyError(n);
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
function forEachDependent(e, n) {
|
|
73
|
-
for (let t = e.
|
|
73
|
+
for (let t = e.u; t !== null; t = t.fe) n(t.ae, t);
|
|
74
74
|
// `?? null`: affects() marks route plain signals (no `_child` slot) through here.
|
|
75
|
-
for (let t = e.
|
|
76
|
-
for (let e = t.
|
|
75
|
+
for (let t = e.o?.l ?? null; t !== null; t = t.ce) {
|
|
76
|
+
for (let e = t.u; e !== null; e = e.fe) n(e.ae, e);
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
@@ -88,7 +88,7 @@ function forEachDependent(e, n) {
|
|
|
88
88
|
// callbacks handle their own release (settleAutodispose in handleAsync); this
|
|
89
89
|
// covers derivatively-pending dependents, which have no callbacks of their own.
|
|
90
90
|
function releaseIfSettledUnobserved(e) {
|
|
91
|
-
e.
|
|
91
|
+
e.Se && e.T & CONFIG_AUTO_DISPOSE && !e.u && !(e.ie & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
// Error-path sweep: notifyStatus(STATUS_ERROR) clears dependents' pending
|
|
@@ -101,7 +101,7 @@ function releaseSettledDependents(e) {
|
|
|
101
101
|
const visit = e => {
|
|
102
102
|
if (t.has(e)) return;
|
|
103
103
|
t.add(e);
|
|
104
|
-
if (!e.
|
|
104
|
+
if (!e.u && e.T & CONFIG_AUTO_DISPOSE) (n ??= []).push(e);
|
|
105
105
|
forEachDependent(e, visit);
|
|
106
106
|
};
|
|
107
107
|
forEachDependent(e, visit);
|
|
@@ -129,7 +129,7 @@ function settleErroredDependents(e, n) {
|
|
|
129
129
|
const visit = e => {
|
|
130
130
|
if (r.has(e)) return;
|
|
131
131
|
r.add(e);
|
|
132
|
-
if (e._ === n) {
|
|
132
|
+
if (e.o?._ === n) {
|
|
133
133
|
enqueueSub(e);
|
|
134
134
|
t = true;
|
|
135
135
|
}
|
|
@@ -144,12 +144,12 @@ function settlePendingSource(e) {
|
|
|
144
144
|
let t;
|
|
145
145
|
const r = new Set;
|
|
146
146
|
// Companion updates no-op without the verdict layer (null hook).
|
|
147
|
-
const o = GlobalQueue.
|
|
147
|
+
const o = GlobalQueue.de;
|
|
148
148
|
const settle = l => {
|
|
149
149
|
if (r.has(l) || !removePendingSource(l, e)) return;
|
|
150
150
|
r.add(l);
|
|
151
|
-
l.
|
|
152
|
-
const u = l.
|
|
151
|
+
l.Te = clock;
|
|
152
|
+
const u = l.o?.le?.values().next().value;
|
|
153
153
|
// STATUS_ERROR + pending sources only coexist via an errored loading
|
|
154
154
|
// window's park (notifyStatus(STATUS_ERROR) clears pending sources
|
|
155
155
|
// otherwise): the settled error stays the answer through the settle —
|
|
@@ -163,15 +163,15 @@ function settlePendingSource(e) {
|
|
|
163
163
|
l.S &= ~STATUS_PENDING;
|
|
164
164
|
if (!i) setPendingError(l);
|
|
165
165
|
o !== null && o(l);
|
|
166
|
-
if (l.
|
|
166
|
+
if (l.o?.ue) {
|
|
167
167
|
enqueueSub(l);
|
|
168
168
|
n = true;
|
|
169
169
|
}
|
|
170
|
-
l.
|
|
170
|
+
if (l.o !== null) l.o.ue = false;
|
|
171
171
|
// Fully settled with nobody watching: release candidate (#2934). Checked
|
|
172
172
|
// again at release time — deferred so unobserved() can't unlink subs
|
|
173
173
|
// lists this walk is still iterating.
|
|
174
|
-
if (!l.
|
|
174
|
+
if (!l.u && l.T & CONFIG_AUTO_DISPOSE) (t ??= []).push(l);
|
|
175
175
|
}
|
|
176
176
|
forEachDependent(l, settle);
|
|
177
177
|
};
|
|
@@ -197,12 +197,12 @@ function handleAsync(e, n, t) {
|
|
|
197
197
|
});
|
|
198
198
|
}
|
|
199
199
|
if (!o && !r) {
|
|
200
|
-
e.
|
|
200
|
+
if (e.o !== null) e.o.Ee = null;
|
|
201
201
|
// A sync landing is the first real answer for a loadingValue node.
|
|
202
|
-
e.
|
|
202
|
+
e.Ie = false;
|
|
203
203
|
return n;
|
|
204
204
|
}
|
|
205
|
-
e.
|
|
205
|
+
ext(e).Ee = n;
|
|
206
206
|
let l;
|
|
207
207
|
// Settle-time transition re-entry. The loading rail is invisible to
|
|
208
208
|
// transactions (#2933): a boundary-caught first load never registers as an
|
|
@@ -216,42 +216,42 @@ function handleAsync(e, n, t) {
|
|
|
216
216
|
// are the transaction's reveal machinery and always re-enter.
|
|
217
217
|
const settleTransition = () => {
|
|
218
218
|
const n = resolveTransition(e);
|
|
219
|
-
if (n && e.S & STATUS_UNINITIALIZED && !currentTransition(n).
|
|
219
|
+
if (n && e.S & STATUS_UNINITIALIZED && !currentTransition(n).Ne.has(e)) {
|
|
220
220
|
// Drop the stale stamp too: the plain settle write (setSignal) and the
|
|
221
221
|
// stash-path restamp both re-enter the transaction through it.
|
|
222
|
-
e.
|
|
222
|
+
e._e = null;
|
|
223
223
|
return;
|
|
224
224
|
}
|
|
225
225
|
globalQueue.initTransition(n);
|
|
226
226
|
};
|
|
227
227
|
const handleError = t => {
|
|
228
|
-
if (e.
|
|
228
|
+
if (e.o?.Ee !== n) return;
|
|
229
229
|
// NotReadyError from rejected promises should be treated as pending, not error
|
|
230
230
|
let r = t instanceof NotReadyError;
|
|
231
|
-
if (r && e.
|
|
231
|
+
if (r && e.Ie) {
|
|
232
232
|
// Loading window: the flight died waiting on an unready source. Keep
|
|
233
233
|
// serving commit #0 — same parking as recompute's catch for sync
|
|
234
234
|
// dependency throws. The dead flight is released so the clock-gated
|
|
235
235
|
// error-retry pull (updateIfNecessary) can also re-ask.
|
|
236
|
-
e.
|
|
236
|
+
if (e.o !== null) e.o.Ee = null;
|
|
237
237
|
parkLoadingWindow(e, t);
|
|
238
|
-
e.
|
|
238
|
+
e.Te = clock;
|
|
239
239
|
return;
|
|
240
240
|
}
|
|
241
241
|
settleTransition();
|
|
242
242
|
notifyStatus(e, r ? STATUS_PENDING : STATUS_ERROR, t);
|
|
243
|
-
e.
|
|
243
|
+
e.Te = clock;
|
|
244
244
|
// A real error settles derivatively-pending dependents (notifyStatus
|
|
245
245
|
// cleared their pending sources), so stranded lazy ones release here —
|
|
246
246
|
// the error twin of settlePendingSource's release (#2934).
|
|
247
247
|
if (!r) releaseSettledDependents(e);
|
|
248
248
|
};
|
|
249
249
|
const asyncWrite = (r, o) => {
|
|
250
|
-
if (e.
|
|
250
|
+
if (e.o?.Ee !== n) return;
|
|
251
251
|
// If the node was dirtied by a newer write (optimistic override or regular),
|
|
252
252
|
// skip this stale async result — the upcoming flush will recompute the node
|
|
253
253
|
// with the new value, creating a fresh Promise that supersedes this one.
|
|
254
|
-
if (e.
|
|
254
|
+
if (e.ie & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
|
|
255
255
|
settleTransition();
|
|
256
256
|
const l = !!(e.S & STATUS_UNINITIALIZED);
|
|
257
257
|
trimStaleDeps(e);
|
|
@@ -261,7 +261,7 @@ function handleAsync(e, n, t) {
|
|
|
261
261
|
if (t) {
|
|
262
262
|
t(r);
|
|
263
263
|
if (l) clearStatus(e, true);
|
|
264
|
-
} else if (e.De !== undefined) {
|
|
264
|
+
} else if (e.o?.De !== undefined) {
|
|
265
265
|
// Optimistic node — resting OR covered by an active override — holds
|
|
266
266
|
// through the shared pending-node path, exactly like a plain async memo,
|
|
267
267
|
// so the commit clears STATUS_UNINITIALIZED (#2806) and elevation to
|
|
@@ -271,8 +271,8 @@ function handleAsync(e, n, t) {
|
|
|
271
271
|
// (A17 — every reader sees the override); the revert reveals whatever
|
|
272
272
|
// has committed by then, so corrections reveal atomically with their
|
|
273
273
|
// transition rather than escaping it.
|
|
274
|
-
if (e.
|
|
275
|
-
e.
|
|
274
|
+
if (e.Pe === NOT_PENDING) queuePendingNode(e);
|
|
275
|
+
e.Pe = r;
|
|
276
276
|
// The hold is a companion-visible write like any other (A13/A19): the
|
|
277
277
|
// clearStatus() above computed its verdict before the hold existed, so
|
|
278
278
|
// isPending must re-derive (the value is not final until commit — V1)
|
|
@@ -280,9 +280,11 @@ function handleAsync(e, n, t) {
|
|
|
280
280
|
// only notified when the hold is visible to them: under an active
|
|
281
281
|
// override every reader sees the override (A17), so waking subs would
|
|
282
282
|
// re-show an unchanged view — the revert is the notification point.
|
|
283
|
-
GlobalQueue.
|
|
284
|
-
if (!hasActiveOverride(e))
|
|
285
|
-
|
|
283
|
+
GlobalQueue.Oe !== null && GlobalQueue.Oe(e, r);
|
|
284
|
+
if (!hasActiveOverride(e)) {
|
|
285
|
+
insertSubs(e);
|
|
286
|
+
}
|
|
287
|
+
e.Te = clock;
|
|
286
288
|
} else if (u) {
|
|
287
289
|
// Route through lane's effect queue for independent flushing
|
|
288
290
|
const n = e.Re;
|
|
@@ -291,11 +293,11 @@ function handleAsync(e, n, t) {
|
|
|
291
293
|
try {
|
|
292
294
|
if (!n && l || !o || !o(r, t)) {
|
|
293
295
|
e.be = r;
|
|
294
|
-
e.
|
|
296
|
+
e.Te = clock;
|
|
295
297
|
// The latest() shadow write gives latest() effects independent lanes; the
|
|
296
298
|
// _pendingSignal update is a no-op repeat of the clearStatus() call above
|
|
297
299
|
// (computePendingState doesn't read _value).
|
|
298
|
-
GlobalQueue.
|
|
300
|
+
GlobalQueue.Oe !== null && GlobalQueue.Oe(e, r);
|
|
299
301
|
insertSubs(e, true);
|
|
300
302
|
}
|
|
301
303
|
} catch (n) {
|
|
@@ -320,7 +322,7 @@ function handleAsync(e, n, t) {
|
|
|
320
322
|
// held-value branch is window-gated, and commitPendingNode closes the
|
|
321
323
|
// window when the hold commits, so no one-frame isPending pulse can leak
|
|
322
324
|
// to live observers between the landing and its commit (#2990).
|
|
323
|
-
if (e.
|
|
325
|
+
if (e.Pe === NOT_PENDING) e.Ie = false;
|
|
324
326
|
settlePendingSource(e);
|
|
325
327
|
schedule();
|
|
326
328
|
flush();
|
|
@@ -334,7 +336,7 @@ function handleAsync(e, n, t) {
|
|
|
334
336
|
// Returns whether the node released, so the iterator branch can stop
|
|
335
337
|
// pulling values instead of pumping an unobserved stream forever (#2935).
|
|
336
338
|
const settleAutodispose = () => {
|
|
337
|
-
if (e.T & CONFIG_AUTO_DISPOSE && !e.
|
|
339
|
+
if (e.T & CONFIG_AUTO_DISPOSE && !e.u && !(e.S & STATUS_PENDING)) {
|
|
338
340
|
unobserved(e);
|
|
339
341
|
return true;
|
|
340
342
|
}
|
|
@@ -394,7 +396,7 @@ function handleAsync(e, n, t) {
|
|
|
394
396
|
t = r;
|
|
395
397
|
f = true;
|
|
396
398
|
if (r.done) i = true;
|
|
397
|
-
} else if (e.
|
|
399
|
+
} else if (e.o?.Ee !== n) {
|
|
398
400
|
return;
|
|
399
401
|
} else if (!r.done) {
|
|
400
402
|
u = true;
|
|
@@ -414,7 +416,7 @@ function handleAsync(e, n, t) {
|
|
|
414
416
|
if (c && s) {
|
|
415
417
|
r = t;
|
|
416
418
|
a = true;
|
|
417
|
-
} else if (e.
|
|
419
|
+
} else if (e.o?.Ee === n) {
|
|
418
420
|
i = true;
|
|
419
421
|
handleError(t);
|
|
420
422
|
settleAutodispose();
|
|
@@ -480,7 +482,7 @@ function handleAsync(e, n, t) {
|
|
|
480
482
|
if (u) {
|
|
481
483
|
l = r;
|
|
482
484
|
t = true;
|
|
483
|
-
} else if (e.
|
|
485
|
+
} else if (e.o?.Ee === n && !(e.ie & REACTIVE_DISPOSED) && flattenIfIterable(r, registerDeferredClose)) ; else {
|
|
484
486
|
asyncWrite(r);
|
|
485
487
|
settleAutodispose();
|
|
486
488
|
}
|
|
@@ -505,12 +507,12 @@ function handleAsync(e, n, t) {
|
|
|
505
507
|
// is opened — first-flight work on a loadingValue node is loading-class
|
|
506
508
|
// (invisible to boundaries and transitions); the flight itself is
|
|
507
509
|
// already registered in _inFlight and lands through asyncWrite.
|
|
508
|
-
if (e.
|
|
510
|
+
if (e.Ie) return e.be;
|
|
509
511
|
globalQueue.initTransition(resolveTransition(e));
|
|
510
512
|
throw new NotReadyError(context);
|
|
511
513
|
} else if (!flattenIfIterable(l)) {
|
|
512
514
|
// Synchronously-resolved promise: the first real answer landed.
|
|
513
|
-
e.
|
|
515
|
+
e.Ie = false;
|
|
514
516
|
}
|
|
515
517
|
// A sync-resolved promise holding an AsyncIterable flattened LIVE (we
|
|
516
518
|
// are still inside the synchronous read): full initial-drain semantics
|
|
@@ -520,31 +522,31 @@ function handleAsync(e, n, t) {
|
|
|
520
522
|
if (u !== null) {
|
|
521
523
|
if (!u) {
|
|
522
524
|
// Loading window: serve commit #0 (see the promise branch above).
|
|
523
|
-
if (e.
|
|
525
|
+
if (e.Ie) return e.be;
|
|
524
526
|
globalQueue.initTransition(resolveTransition(e));
|
|
525
527
|
throw new NotReadyError(context);
|
|
526
528
|
}
|
|
527
529
|
// A sync first yield (or immediate empty completion) is the first real
|
|
528
530
|
// answer; async yields clear inside asyncWrite.
|
|
529
|
-
e.
|
|
531
|
+
e.Ie = false;
|
|
530
532
|
}
|
|
531
533
|
return l;
|
|
532
534
|
}
|
|
533
535
|
|
|
534
536
|
function clearStatus(e, n = false) {
|
|
535
|
-
if (e.
|
|
536
|
-
if (e.
|
|
537
|
+
if (e.o?.le) clearPendingSources(e);
|
|
538
|
+
if (e.o?.ue) if (e.o !== null) e.o.ue = false;
|
|
537
539
|
// The pending window is over; its quiet classification dies with it.
|
|
538
540
|
// (Unconditional: _reask is baked into the node literals, so this is a
|
|
539
541
|
// plain store to an existing slot — no shape change.)
|
|
540
|
-
e.pe = false;
|
|
542
|
+
if (e.o !== null) e.o.pe = false;
|
|
541
543
|
e.S = n ? 0 : e.S & STATUS_UNINITIALIZED;
|
|
542
|
-
if (e._) setPendingError(e);
|
|
544
|
+
if (e.o?._) setPendingError(e);
|
|
543
545
|
// Update pending signal for isPending() reactivity (companions only exist
|
|
544
546
|
// once the verdict layer created them, which installs the hooks).
|
|
545
|
-
if (e.
|
|
546
|
-
if (e.
|
|
547
|
-
if (e.
|
|
547
|
+
if (e.o?.Ge || e.o?.ge) GlobalQueue.de(e);
|
|
548
|
+
if (e.o?.l && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.ye !== null) GlobalQueue.ye(e);
|
|
549
|
+
if (e.o?.A) e.o.A.call(e);
|
|
548
550
|
}
|
|
549
551
|
|
|
550
552
|
function notifyStatus(e, n, t, r, o) {
|
|
@@ -552,7 +554,7 @@ function notifyStatus(e, n, t, r, o) {
|
|
|
552
554
|
if (n === STATUS_ERROR && !(t instanceof StatusError) && !(t instanceof NotReadyError)) t = new StatusError(e, t);
|
|
553
555
|
const l = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
|
|
554
556
|
const u = l === e;
|
|
555
|
-
const i = n === STATUS_PENDING && e.De !== undefined && !u;
|
|
557
|
+
const i = n === STATUS_PENDING && e.o?.De !== undefined && !u;
|
|
556
558
|
const s = i && hasActiveOverride(e);
|
|
557
559
|
if (!r) {
|
|
558
560
|
if (n === STATUS_PENDING && l) {
|
|
@@ -564,41 +566,41 @@ function notifyStatus(e, n, t, r, o) {
|
|
|
564
566
|
} else {
|
|
565
567
|
clearPendingSources(e);
|
|
566
568
|
e.S = n | (n !== STATUS_ERROR ? e.S & STATUS_UNINITIALIZED : 0);
|
|
567
|
-
e._ = t;
|
|
569
|
+
ext(e)._ = t;
|
|
568
570
|
}
|
|
569
|
-
GlobalQueue.
|
|
570
|
-
if (e.
|
|
571
|
+
GlobalQueue.de !== null && GlobalQueue.de(e);
|
|
572
|
+
if (e.o?.l && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.ye !== null) GlobalQueue.ye(e);
|
|
571
573
|
}
|
|
572
574
|
if (o && !r) {
|
|
573
575
|
assignOrMergeLane(e, o);
|
|
574
576
|
}
|
|
575
577
|
const f = r || s;
|
|
576
578
|
const a = r || i ? undefined : o;
|
|
577
|
-
if (e.
|
|
579
|
+
if (e.o?.A) {
|
|
578
580
|
if (r && n === STATUS_PENDING) {
|
|
579
581
|
return;
|
|
580
582
|
}
|
|
581
583
|
if (f) {
|
|
582
|
-
e.
|
|
584
|
+
e.o.A.call(e, n, t);
|
|
583
585
|
} else {
|
|
584
|
-
e.
|
|
586
|
+
e.o.A.call(e);
|
|
585
587
|
}
|
|
586
588
|
return;
|
|
587
589
|
}
|
|
588
590
|
forEachDependent(e, (e, r) => {
|
|
589
|
-
e.
|
|
590
|
-
if (n === STATUS_PENDING && l && !e.
|
|
591
|
+
e.Te = clock;
|
|
592
|
+
if (n === STATUS_PENDING && l && !e.o?.le?.has(l) || n !== STATUS_PENDING && (e.o?._ !== t || e.o?.le)) {
|
|
591
593
|
// A pending-observer link is the subscription an `isPending` read created.
|
|
592
594
|
// It exists so the observer re-runs when the source settles, but it must
|
|
593
595
|
// not carry a real (non-NotReadyError) error — the synchronous `isPending`
|
|
594
596
|
// read swallows those, and the async path must match. Re-run the observer
|
|
595
597
|
// so `isPending` re-evaluates (to not-pending) instead of forwarding.
|
|
596
|
-
if (r.
|
|
598
|
+
if (r.me && n !== STATUS_PENDING && !(t instanceof NotReadyError)) {
|
|
597
599
|
enqueueSub(e);
|
|
598
600
|
schedule();
|
|
599
601
|
return;
|
|
600
602
|
}
|
|
601
|
-
if (!f && !e.
|
|
603
|
+
if (!f && !e._e) queuePendingNode(e);
|
|
602
604
|
notifyStatus(e, n, t, f, a);
|
|
603
605
|
}
|
|
604
606
|
});
|
|
@@ -30,6 +30,15 @@ const REACTIVE_MANUAL_WRITE = 1 << 10;
|
|
|
30
30
|
* pending window does not read as pending (question-scoped pending model).
|
|
31
31
|
*/ const REACTIVE_REASK = 1 << 11;
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* A dependency write landed while this subscriber was mid-recompute — a
|
|
35
|
+
* nested pull committed beneath one of its reads (#3037). The heap refuses
|
|
36
|
+
* RECOMPUTING nodes, so recompute's tail consumes this latch and reschedules:
|
|
37
|
+
* values the pass read before the nested commit are stale. Only set for
|
|
38
|
+
* links validated this pass (gen-current): a write to an untouched link is
|
|
39
|
+
* either re-read later in the pass (fresh) or trimmed with it (not a dep).
|
|
40
|
+
*/ const REACTIVE_MISSED_WAKE = 1 << 12;
|
|
41
|
+
|
|
33
42
|
// Static configuration bits packed into Owner/Computed/Signal _config.
|
|
34
43
|
const CONFIG_OWNED_WRITE = 1 << 0;
|
|
35
44
|
|
|
@@ -45,6 +54,35 @@ const CONFIG_AUTO_DISPOSE = 1 << 5;
|
|
|
45
54
|
|
|
46
55
|
const CONFIG_SYNC = 1 << 6;
|
|
47
56
|
|
|
57
|
+
// Presence bits (stage-3 hot-path monomorphism, DESIGN-PATCH-CHANNEL §11b):
|
|
58
|
+
// optional per-node slots (_overrideValue, _pendingSignal/_latestValueComputed,
|
|
59
|
+
// _snapshotValue, _optimisticLane) are NOT part of every node's hidden class —
|
|
60
|
+
// reading a missing property defeats V8's inline caches on the hottest write/
|
|
61
|
+
// notify loops. These bits live on the always-present `_config` so hot paths
|
|
62
|
+
// pay one monomorphic masked read and only touch the optional field when its
|
|
63
|
+
// installer flagged it. Bits are STICKY ("may be set") — the guarded field
|
|
64
|
+
// read remains authoritative.
|
|
65
|
+
const CONFIG_OPTIMISTIC = 1 << 7;
|
|
66
|
+
|
|
67
|
+
const CONFIG_HAS_COMPANIONS = 1 << 8;
|
|
68
|
+
|
|
69
|
+
const CONFIG_HAS_SNAPSHOT = 1 << 9;
|
|
70
|
+
|
|
71
|
+
const CONFIG_HAS_LANE = 1 << 10;
|
|
72
|
+
|
|
73
|
+
/** Set on a FIREWALL computed when any of its child signals creates an
|
|
74
|
+
* isPending()/latest() companion. Gates the post-recompute child-companion
|
|
75
|
+
* walk (#3038): a store computed's `_child` chain holds one node per
|
|
76
|
+
* materialized leaf, so walking it unconditionally makes every update cost
|
|
77
|
+
* O(all leaves ever read). Sticky — set at companion creation, never
|
|
78
|
+
* cleared; sync-only apps never set it and never pay the walk. */ const CONFIG_CHILD_COMPANIONS = 1 << 11;
|
|
79
|
+
|
|
80
|
+
/** Set on a computed when its first firewall child signal is installed
|
|
81
|
+
* (projection machinery). Gates markNode's firewall-children walk with one
|
|
82
|
+
* masked read of the always-present _config — the walk's old `_child` read
|
|
83
|
+
* moved into the cold extension (§12), and an unconditional `_x` deref per
|
|
84
|
+
* marked node measurably taxed the propagation hot path (diamond -22%). */ const CONFIG_FW_CHILDREN = 1 << 12;
|
|
85
|
+
|
|
48
86
|
const STATUS_PENDING = 1 << 0;
|
|
49
87
|
|
|
50
88
|
const STATUS_ERROR = 1 << 1;
|
|
@@ -89,4 +127,4 @@ const defaultContext = {};
|
|
|
89
127
|
* @internal
|
|
90
128
|
*/ const $REFRESH = Symbol("refresh");
|
|
91
129
|
|
|
92
|
-
export { $REFRESH, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_NO_SNAPSHOT, CONFIG_OWNED_WRITE, CONFIG_SYNC, CONFIG_TRANSPARENT, EFFECT_RENDER, EFFECT_TRACKED, EFFECT_USER, NOT_PENDING, NO_SNAPSHOT, OVERRIDE_UNDEFINED, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_DISPOSED, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_LAZY, REACTIVE_MANUAL_WRITE, REACTIVE_NONE, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, REACTIVE_SNAPSHOT_STALE, REACTIVE_ZOMBIE, STATUS_ERROR, STATUS_PENDING, STATUS_UNINITIALIZED, STORE_SNAPSHOT_PROPS, SUPPORTS_PROXY, defaultContext, unwrapOverride };
|
|
130
|
+
export { $REFRESH, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_CHILD_COMPANIONS, CONFIG_FW_CHILDREN, CONFIG_HAS_COMPANIONS, CONFIG_HAS_LANE, CONFIG_HAS_SNAPSHOT, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_NO_SNAPSHOT, CONFIG_OPTIMISTIC, CONFIG_OWNED_WRITE, CONFIG_SYNC, CONFIG_TRANSPARENT, EFFECT_RENDER, EFFECT_TRACKED, EFFECT_USER, NOT_PENDING, NO_SNAPSHOT, OVERRIDE_UNDEFINED, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_DISPOSED, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_LAZY, REACTIVE_MANUAL_WRITE, REACTIVE_MISSED_WAKE, REACTIVE_NONE, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, REACTIVE_SNAPSHOT_STALE, REACTIVE_ZOMBIE, STATUS_ERROR, STATUS_PENDING, STATUS_UNINITIALIZED, STORE_SNAPSHOT_PROPS, SUPPORTS_PROXY, defaultContext, unwrapOverride };
|