@solidjs/signals 2.0.0-rc.5 → 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 +475 -72
- package/dist/node.cjs +851 -662
- package/dist/prod/core/async.js +41 -19
- package/dist/prod/core/core.js +58 -62
- package/dist/prod/core/effect.js +6 -6
- package/dist/prod/core/heap.js +1 -1
- package/dist/prod/core/lanes.js +4 -4
- package/dist/prod/core/optimistic.js +16 -16
- package/dist/prod/core/owner.js +4 -4
- package/dist/prod/core/scheduler.js +45 -32
- package/dist/prod/core/verdict.js +23 -19
- package/dist/prod/signals.js +68 -1
- package/dist/prod/store/next/optimistic.js +137 -64
- package/dist/prod/store/next/projection.js +2 -2
- package/dist/prod/store/next/store.js +122 -106
- package/dist/types/core/attribution-hooks.d.ts +11 -0
- package/dist/types/core/attribution.d.ts +48 -0
- package/dist/types/core/dev.d.ts +8 -2
- package/dist/types/core/scheduler.d.ts +5 -0
- package/dist/types/signals.d.ts +0 -11
- package/dist/types/store/next/target.d.ts +10 -0
- package/dist/types-cjs/core/attribution-hooks.d.cts +11 -0
- package/dist/types-cjs/core/attribution.d.cts +48 -0
- package/dist/types-cjs/core/dev.d.cts +8 -2
- package/dist/types-cjs/core/scheduler.d.cts +5 -0
- package/dist/types-cjs/signals.d.cts +0 -11
- package/dist/types-cjs/store/next/target.d.cts +10 -0
- package/package.json +1 -1
package/dist/prod/core/async.js
CHANGED
|
@@ -28,13 +28,15 @@ function addPendingSource(e, n) {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function removePendingSource(e, n) {
|
|
31
|
-
|
|
32
|
-
if (
|
|
31
|
+
const t = e.o?.le;
|
|
32
|
+
if (!t?.delete(n)) return false;
|
|
33
|
+
if (!t.size) e.o.le = undefined;
|
|
33
34
|
return true;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
function clearPendingSources(e) {
|
|
37
|
-
|
|
38
|
+
// This set is node-owned and never shared; dropping the sole reference
|
|
39
|
+
// releases the set and every entry without a redundant clear() walk.
|
|
38
40
|
if (e.o !== null) e.o.le = undefined;
|
|
39
41
|
}
|
|
40
42
|
|
|
@@ -140,6 +142,10 @@ function settleErroredDependents(e, n) {
|
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
function settlePendingSource(e) {
|
|
145
|
+
// The normal landing path already cleared the source's own set. Superseded
|
|
146
|
+
// re-parks arrive here with an abandoned self entry, which must retire in
|
|
147
|
+
// the same walk as its propagated copies.
|
|
148
|
+
removePendingSource(e, e);
|
|
143
149
|
let n = false;
|
|
144
150
|
let t;
|
|
145
151
|
const r = new Set;
|
|
@@ -253,6 +259,10 @@ function handleAsync(e, n, t) {
|
|
|
253
259
|
}
|
|
254
260
|
settleTransition();
|
|
255
261
|
notifyStatus(e, r ? STATUS_PENDING : STATUS_ERROR, t);
|
|
262
|
+
// A NotReady rejection is a landing into another pending source. The
|
|
263
|
+
// rejected flight will never settle its self entry, so transfer ownership
|
|
264
|
+
// after notifyStatus has propagated the replacement source.
|
|
265
|
+
if (r) settlePendingSource(e);
|
|
256
266
|
e.Te = clock;
|
|
257
267
|
// A real error settles derivatively-pending dependents (notifyStatus
|
|
258
268
|
// cleared their pending sources), so stranded lazy ones release here —
|
|
@@ -267,14 +277,22 @@ function handleAsync(e, n, t) {
|
|
|
267
277
|
if (e.ie & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
|
|
268
278
|
settleTransition();
|
|
269
279
|
const i = !!(e.S & STATUS_UNINITIALIZED);
|
|
280
|
+
// Captured before clearStatus wipes it: a quiet re-ask's landing may be
|
|
281
|
+
// transition-held below, and the displayed value keeps answering the same
|
|
282
|
+
// question until the hold commits — the classification must survive to
|
|
283
|
+
// that reveal or companion synchronization briefly classifies the held
|
|
284
|
+
// old value as pending, a one-frame pulse to direct observers (#3178).
|
|
285
|
+
// A truthy capture implies `_x` exists, so the restore writes it directly.
|
|
286
|
+
const l = e.o?.De;
|
|
270
287
|
trimStaleDeps(e);
|
|
271
288
|
clearStatus(e);
|
|
272
|
-
|
|
273
|
-
|
|
289
|
+
if (l) e.o.De = true;
|
|
290
|
+
const s = resolveLane(e);
|
|
291
|
+
if (s) s.Oe.delete(e);
|
|
274
292
|
if (t) {
|
|
275
293
|
t(r);
|
|
276
294
|
if (i) clearStatus(e, true);
|
|
277
|
-
} else if (e.o?.
|
|
295
|
+
} else if (e.o?.Pe !== undefined) {
|
|
278
296
|
// Optimistic node — resting OR covered by an active override — holds
|
|
279
297
|
// through the shared pending-node path, exactly like a plain async memo,
|
|
280
298
|
// so the commit clears STATUS_UNINITIALIZED (#2806) and elevation to
|
|
@@ -284,8 +302,8 @@ function handleAsync(e, n, t) {
|
|
|
284
302
|
// (A17 — every reader sees the override); the revert reveals whatever
|
|
285
303
|
// has committed by then, so corrections reveal atomically with their
|
|
286
304
|
// transition rather than escaping it.
|
|
287
|
-
if (e.
|
|
288
|
-
e.
|
|
305
|
+
if (e.Re === NOT_PENDING) queuePendingNode(e);
|
|
306
|
+
e.Re = r;
|
|
289
307
|
// The hold is a companion-visible write like any other (A13/A19): the
|
|
290
308
|
// clearStatus() above computed its verdict before the hold existed, so
|
|
291
309
|
// isPending must re-derive (the value is not final until commit — V1)
|
|
@@ -293,7 +311,7 @@ function handleAsync(e, n, t) {
|
|
|
293
311
|
// only notified when the hold is visible to them: under an active
|
|
294
312
|
// override every reader sees the override (A17), so waking subs would
|
|
295
313
|
// re-show an unchanged view — the revert is the notification point.
|
|
296
|
-
GlobalQueue.
|
|
314
|
+
GlobalQueue.Ue?.(e, r);
|
|
297
315
|
if (!hasActiveOverride(e)) {
|
|
298
316
|
insertSubs(e);
|
|
299
317
|
} else if (e.T & CONFIG_AUTHORITATIVE_OBSERVED) {
|
|
@@ -306,14 +324,14 @@ function handleAsync(e, n, t) {
|
|
|
306
324
|
// branch in recompute(). Optional call: the bit implies the
|
|
307
325
|
// optimistic engine WAS consulted, but the hook only installs with
|
|
308
326
|
// it — a bare-core build must not crash here.
|
|
309
|
-
GlobalQueue.
|
|
327
|
+
GlobalQueue.he?.(e);
|
|
310
328
|
}
|
|
311
329
|
e.Te = clock;
|
|
312
|
-
} else if (
|
|
330
|
+
} else if (s) {
|
|
313
331
|
// Route through lane's effect queue for independent flushing
|
|
314
|
-
const n = e.
|
|
332
|
+
const n = e.ge;
|
|
315
333
|
const t = e.be;
|
|
316
|
-
const o = e.
|
|
334
|
+
const o = e.pe;
|
|
317
335
|
try {
|
|
318
336
|
if (!n && i || !o || !o(r, t)) {
|
|
319
337
|
e.be = r;
|
|
@@ -321,7 +339,7 @@ function handleAsync(e, n, t) {
|
|
|
321
339
|
// The latest() shadow write gives latest() effects independent lanes; the
|
|
322
340
|
// _pendingSignal update is a no-op repeat of the clearStatus() call above
|
|
323
341
|
// (computePendingState doesn't read _value).
|
|
324
|
-
GlobalQueue.
|
|
342
|
+
GlobalQueue.Ue?.(e, r);
|
|
325
343
|
insertSubs(e, true);
|
|
326
344
|
}
|
|
327
345
|
} catch (n) {
|
|
@@ -345,8 +363,12 @@ function handleAsync(e, n, t) {
|
|
|
345
363
|
// (`_pendingValue` set above or inside setSignal) is not — the verdict's
|
|
346
364
|
// held-value branch is window-gated, and commitPendingNode closes the
|
|
347
365
|
// window when the hold commits, so no one-frame isPending pulse can leak
|
|
348
|
-
// to live observers between the landing and its commit (#2990).
|
|
349
|
-
|
|
366
|
+
// to live observers between the landing and its commit (#2990). The
|
|
367
|
+
// quiet re-ask classification follows the same schedule (#3178).
|
|
368
|
+
if (e.Re === NOT_PENDING) {
|
|
369
|
+
e.Ne = false;
|
|
370
|
+
if (l) e.o.De = false;
|
|
371
|
+
}
|
|
350
372
|
settlePendingSource(e);
|
|
351
373
|
schedule();
|
|
352
374
|
flush();
|
|
@@ -505,7 +527,7 @@ function handleAsync(e, n, t) {
|
|
|
505
527
|
// becomes disposal-bearing, which is exactly the class a directly
|
|
506
528
|
// returned iterable already occupies.
|
|
507
529
|
const registerDeferredClose = n => {
|
|
508
|
-
if (!e.
|
|
530
|
+
if (!e.Ge) e.Ge = n; else if (Array.isArray(e.Ge)) e.Ge.push(n); else e.Ge = [ e.Ge, n ];
|
|
509
531
|
};
|
|
510
532
|
n.then(r => {
|
|
511
533
|
if (l) {
|
|
@@ -568,7 +590,7 @@ function clearStatus(e, n = false) {
|
|
|
568
590
|
// The pending window is over; its quiet classification dies with it.
|
|
569
591
|
// (Unconditional: _reask is baked into the node literals, so this is a
|
|
570
592
|
// plain store to an existing slot — no shape change.)
|
|
571
|
-
if (e.o !== null) e.o.
|
|
593
|
+
if (e.o !== null) e.o.De = false;
|
|
572
594
|
e.S = n ? 0 : e.S & STATUS_UNINITIALIZED;
|
|
573
595
|
if (e.o?._) setPendingError(e);
|
|
574
596
|
// Update pending signal for isPending() reactivity (companions only exist
|
|
@@ -584,7 +606,7 @@ function notifyStatus(e, n, t, r, o) {
|
|
|
584
606
|
if (n === STATUS_ERROR && !(t instanceof StatusError) && !(t instanceof NotReadyError)) t = new StatusError(e, t);
|
|
585
607
|
const i = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
|
|
586
608
|
const l = i === e;
|
|
587
|
-
const s = n === STATUS_PENDING && e.o?.
|
|
609
|
+
const s = n === STATUS_PENDING && e.o?.Pe !== undefined && !l;
|
|
588
610
|
const u = s && hasActiveOverride(e);
|
|
589
611
|
if (!r) {
|
|
590
612
|
if (n === STATUS_PENDING && i) {
|
package/dist/prod/core/core.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { releaseFlightTeardown, handleAsync, clearStatus, parkLoadingWindow, notifyStatus,
|
|
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,
|
|
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
|
|
|
@@ -108,7 +108,7 @@ 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
113
|
if (e.Ae && (!n || activeTransition) && activeTransition !== e.Ae) globalQueue.initTransition(e.Ae);
|
|
114
114
|
deleteFromHeap(e, queueFor(e));
|
|
@@ -122,18 +122,18 @@ function recompute(e, t = false) {
|
|
|
122
122
|
releaseFlightTeardown(e);
|
|
123
123
|
}
|
|
124
124
|
// Tracked effects run after finalizePureQueue, so dispose immediately instead of deferring
|
|
125
|
-
if (e.Ae || n === EFFECT_TRACKED) disposeChildren(e); else if (e.xe !== null || e.
|
|
125
|
+
if (e.Ae || n === EFFECT_TRACKED) disposeChildren(e); else if (e.xe !== null || e.Ge !== null) {
|
|
126
126
|
markDisposal(e);
|
|
127
127
|
const t = ext(e);
|
|
128
|
-
t.qe = e.
|
|
128
|
+
t.qe = e.Ge;
|
|
129
129
|
t.Ye = e.xe;
|
|
130
|
-
e.
|
|
130
|
+
e.Ge = null;
|
|
131
131
|
e.xe = null;
|
|
132
132
|
e.Ze = 0;
|
|
133
133
|
} else ;
|
|
134
134
|
}
|
|
135
135
|
let i = !!(e.ie & REACTIVE_OPTIMISTIC_DIRTY);
|
|
136
|
-
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;
|
|
137
137
|
const l = !!(e.S & STATUS_UNINITIALIZED);
|
|
138
138
|
// Outgoing error, captured before the compute clears status: if this run
|
|
139
139
|
// recovers to an unchanged value, dependents still holding this object must
|
|
@@ -145,7 +145,7 @@ function recompute(e, t = false) {
|
|
|
145
145
|
// settles synchronously, those dependents settle HERE — asyncWrite's
|
|
146
146
|
// settlePendingSource walk never runs for a landing that was preempted
|
|
147
147
|
// (#3181).
|
|
148
|
-
const s =
|
|
148
|
+
const s = e.o?.le?.has(e);
|
|
149
149
|
// Re-ask classification lives in the verdict module; capture the flag before
|
|
150
150
|
// the recompute wipes _flags below.
|
|
151
151
|
const a = (e.ie & REACTIVE_REASK) !== 0;
|
|
@@ -160,7 +160,7 @@ function recompute(e, t = false) {
|
|
|
160
160
|
e.Ke++;
|
|
161
161
|
e.ie = REACTIVE_RECOMPUTING_DEPS;
|
|
162
162
|
e.Te = clock;
|
|
163
|
-
let _ = e.
|
|
163
|
+
let _ = e.Re === NOT_PENDING ? e.be : e.Re;
|
|
164
164
|
let f = e.Me;
|
|
165
165
|
let I = false;
|
|
166
166
|
let E = tracking;
|
|
@@ -251,6 +251,9 @@ function recompute(e, t = false) {
|
|
|
251
251
|
if (GlobalQueue.et !== null) i = GlobalQueue.et(e, a);
|
|
252
252
|
}
|
|
253
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);
|
|
254
257
|
if (i) GlobalQueue.k(e);
|
|
255
258
|
}
|
|
256
259
|
} finally {
|
|
@@ -268,10 +271,10 @@ function recompute(e, t = false) {
|
|
|
268
271
|
}
|
|
269
272
|
if (!e.o?._) {
|
|
270
273
|
trimStaleDeps(e);
|
|
271
|
-
const a = u ? unwrapOverride(e.o?.
|
|
274
|
+
const a = u ? unwrapOverride(e.o?.Pe) : e.Re === NOT_PENDING ? e.be : e.Re;
|
|
272
275
|
let c = false;
|
|
273
276
|
try {
|
|
274
|
-
c = !n && l || !e.
|
|
277
|
+
c = !n && l || !e.pe || !e.pe(a, _);
|
|
275
278
|
} catch (t) {
|
|
276
279
|
// A throwing user comparator is an error of this node's computation.
|
|
277
280
|
// Route it through the same status path as a compute-phase throw so
|
|
@@ -291,7 +294,7 @@ function recompute(e, t = false) {
|
|
|
291
294
|
if (!t) e.C.enqueue(n, e.nt ??= GlobalQueue.it.bind(null, e));
|
|
292
295
|
}
|
|
293
296
|
if (e.o?._) ; else if (c) {
|
|
294
|
-
const l = u ? e.o?.
|
|
297
|
+
const l = u ? e.o?.Pe : undefined;
|
|
295
298
|
if (t ||
|
|
296
299
|
// Plain sync flush (no transition on either side) commits effect
|
|
297
300
|
// values directly — the pending round-trip (queuePendingNode +
|
|
@@ -308,11 +311,11 @@ function recompute(e, t = false) {
|
|
|
308
311
|
// own reveal schedule; drop any superseded older hold so its queued
|
|
309
312
|
// commit can't clobber the fresh value.
|
|
310
313
|
if (u && i) {
|
|
311
|
-
ext(e).
|
|
312
|
-
e.
|
|
314
|
+
ext(e).Pe = _ === undefined ? OVERRIDE_UNDEFINED : _;
|
|
315
|
+
e.Re = NOT_PENDING;
|
|
313
316
|
}
|
|
314
317
|
} else {
|
|
315
|
-
e.
|
|
318
|
+
e.Re = _;
|
|
316
319
|
// A window landing that gets held re-opens the window until the hold
|
|
317
320
|
// commits — the verdict's held-value branch is window-gated (#2990).
|
|
318
321
|
if (r) e.Ne = true;
|
|
@@ -321,17 +324,17 @@ function recompute(e, t = false) {
|
|
|
321
324
|
// (#2831). Both companion writes are transition-scoped (optimistic) and
|
|
322
325
|
// auto-revert/re-derive at commit. Skipped for plain flushes where the
|
|
323
326
|
// pending value commits before effects run.
|
|
324
|
-
if ((activeTransition || e.Ae) && GlobalQueue.
|
|
327
|
+
if ((activeTransition || e.Ae) && GlobalQueue.Ue !== null) GlobalQueue.Ue(e, _);
|
|
325
328
|
}
|
|
326
329
|
// insertSubs only walks _subs (no scheduling of its own), so a
|
|
327
330
|
// subscriber-less node has nothing to notify.
|
|
328
|
-
if (e.u !== null && (!u || i || e.o?.
|
|
331
|
+
if (e.u !== null && (!u || i || e.o?.Pe !== l)) insertSubs(e, i || u);
|
|
329
332
|
} else if (u) {
|
|
330
333
|
// Unchanged value (equals the override) recomputed while the override
|
|
331
334
|
// is active: _value may still be stale, so hold the authoritative value
|
|
332
335
|
// for commit on its own transition's schedule — invisibly (A17/A18).
|
|
333
|
-
if (e.
|
|
334
|
-
e.
|
|
336
|
+
if (e.Re === NOT_PENDING) queuePendingNode(e);
|
|
337
|
+
e.Re = _;
|
|
335
338
|
if (r) e.Ne = true;
|
|
336
339
|
// see the held branch above (#2990)
|
|
337
340
|
// A authoritative-view reader (until()) observed this node past its
|
|
@@ -339,7 +342,7 @@ function recompute(e, t = false) {
|
|
|
339
342
|
// exactly the acknowledgment it waits for. Wake those readers only;
|
|
340
343
|
// A17 silence holds for every ordinary subscriber. (Hook installed by
|
|
341
344
|
// until(), the only setter of the gating bit.)
|
|
342
|
-
if (e.T & CONFIG_AUTHORITATIVE_OBSERVED) GlobalQueue.
|
|
345
|
+
if (e.T & CONFIG_AUTHORITATIVE_OBSERVED) GlobalQueue.he(e);
|
|
343
346
|
} else if (e.Me != f) {
|
|
344
347
|
for (let t = e.u; t !== null; t = t.ae) {
|
|
345
348
|
insertIntoHeapHeight(t.ce, queueFor(t.ce));
|
|
@@ -351,20 +354,13 @@ function recompute(e, t = false) {
|
|
|
351
354
|
// recoveries ride insertSubs above; a comparator throw re-errored the node
|
|
352
355
|
// (el._x?._error re-set), so this only runs on a genuinely clean recovery.
|
|
353
356
|
if (o !== undefined && !c && !e.o?._) settleErroredDependents(e, o);
|
|
354
|
-
//
|
|
355
|
-
//
|
|
356
|
-
//
|
|
357
|
-
|
|
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);
|
|
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);
|
|
365
361
|
}
|
|
366
362
|
currentOptimisticLane = N;
|
|
367
|
-
const A = e.
|
|
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;
|
|
368
364
|
// Override-covered holds (hasOverride) always queue: their commit belongs
|
|
369
365
|
// to their own transition's schedule (A18 re-rule) and is unobservable
|
|
370
366
|
// under the override (A17). Revert no longer commits anything, so an
|
|
@@ -418,8 +414,8 @@ function computed(e, t) {
|
|
|
418
414
|
const u = {
|
|
419
415
|
id: inheritId(t, n, context),
|
|
420
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),
|
|
421
|
-
|
|
422
|
-
|
|
417
|
+
pe: t?.equals ?? isEqual,
|
|
418
|
+
Ge: null,
|
|
423
419
|
C: context?.C ?? globalQueue,
|
|
424
420
|
we: context?.we ?? defaultContext,
|
|
425
421
|
Ze: 0,
|
|
@@ -441,7 +437,7 @@ function computed(e, t) {
|
|
|
441
437
|
// A loadingValue node is born committed: commit #0 is already in _value.
|
|
442
438
|
S: i ? 0 : STATUS_UNINITIALIZED,
|
|
443
439
|
Te: clock,
|
|
444
|
-
|
|
440
|
+
Re: NOT_PENDING,
|
|
445
441
|
Ae: null,
|
|
446
442
|
It: -1,
|
|
447
443
|
Ne: i,
|
|
@@ -461,7 +457,7 @@ function computed(e, t) {
|
|
|
461
457
|
* this; hot paths read `el._x?._field` gated by the _config presence bits.
|
|
462
458
|
* Never call ext() just to store a field's default. */ function ext(e) {
|
|
463
459
|
return e.o ??= {
|
|
464
|
-
|
|
460
|
+
Pe: undefined,
|
|
465
461
|
Nt: undefined,
|
|
466
462
|
Je: undefined,
|
|
467
463
|
ye: undefined,
|
|
@@ -474,7 +470,7 @@ function computed(e, t) {
|
|
|
474
470
|
fe: undefined,
|
|
475
471
|
le: undefined,
|
|
476
472
|
h: undefined,
|
|
477
|
-
|
|
473
|
+
De: false,
|
|
478
474
|
i: null,
|
|
479
475
|
Et: undefined,
|
|
480
476
|
We: undefined,
|
|
@@ -494,8 +490,8 @@ function computed(e, t) {
|
|
|
494
490
|
const o = {
|
|
495
491
|
id: inheritId(u, l, context),
|
|
496
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),
|
|
497
|
-
|
|
498
|
-
|
|
493
|
+
pe: false,
|
|
494
|
+
Ge: null,
|
|
499
495
|
C: context?.C ?? globalQueue,
|
|
500
496
|
we: context?.we ?? defaultContext,
|
|
501
497
|
Ze: 0,
|
|
@@ -516,7 +512,7 @@ function computed(e, t) {
|
|
|
516
512
|
ie: REACTIVE_LAZY,
|
|
517
513
|
S: STATUS_UNINITIALIZED,
|
|
518
514
|
Te: clock,
|
|
519
|
-
|
|
515
|
+
Re: NOT_PENDING,
|
|
520
516
|
Ae: null,
|
|
521
517
|
It: -1,
|
|
522
518
|
Ne: false,
|
|
@@ -525,7 +521,7 @@ function computed(e, t) {
|
|
|
525
521
|
Ot: t,
|
|
526
522
|
Ct: n,
|
|
527
523
|
Rt: undefined,
|
|
528
|
-
|
|
524
|
+
ge: i,
|
|
529
525
|
o: null
|
|
530
526
|
};
|
|
531
527
|
// Effects dispatch status through the SHARED notifier (statusNotifierOf,
|
|
@@ -556,7 +552,7 @@ function setEffectStatusNotify(e) {
|
|
|
556
552
|
* per-node field did when every effect carried one. */ function statusNotifierOf(e) {
|
|
557
553
|
const t = e.o?.h;
|
|
558
554
|
if (t !== undefined) return t;
|
|
559
|
-
return e.
|
|
555
|
+
return e.ge ? effectStatusNotify ?? undefined : undefined;
|
|
560
556
|
}
|
|
561
557
|
|
|
562
558
|
const lazyOptions = {
|
|
@@ -590,7 +586,7 @@ function setupComputedNode(e, t) {
|
|
|
590
586
|
|
|
591
587
|
function signal(e, t, n = null) {
|
|
592
588
|
const i = {
|
|
593
|
-
|
|
589
|
+
pe: t?.equals ?? isEqual,
|
|
594
590
|
T: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.H ? CONFIG_NO_SNAPSHOT : 0),
|
|
595
591
|
be: e,
|
|
596
592
|
u: null,
|
|
@@ -598,7 +594,7 @@ function signal(e, t, n = null) {
|
|
|
598
594
|
Te: clock,
|
|
599
595
|
st: n,
|
|
600
596
|
Se: n?.o?.i || null,
|
|
601
|
-
|
|
597
|
+
Re: NOT_PENDING,
|
|
602
598
|
// Signal-literal diet (§12e): NO _time/_fn/_statusFlags slots. Stores
|
|
603
599
|
// materialize one signal per touched leaf, so signal bytes are store
|
|
604
600
|
// bytes. _time is write-only on signals (every read site is computed-
|
|
@@ -623,14 +619,14 @@ function signal(e, t, n = null) {
|
|
|
623
619
|
|
|
624
620
|
function optimisticSignal(e, t) {
|
|
625
621
|
const n = signal(e, t);
|
|
626
|
-
ext(n).
|
|
622
|
+
ext(n).Pe = NOT_PENDING;
|
|
627
623
|
n.T |= CONFIG_OPTIMISTIC;
|
|
628
624
|
return n;
|
|
629
625
|
}
|
|
630
626
|
|
|
631
627
|
function optimisticComputed(e, t) {
|
|
632
628
|
const n = computed(e, t);
|
|
633
|
-
ext(n).
|
|
629
|
+
ext(n).Pe = NOT_PENDING;
|
|
634
630
|
n.T |= CONFIG_OPTIMISTIC;
|
|
635
631
|
return n;
|
|
636
632
|
}
|
|
@@ -731,11 +727,11 @@ function isEqual(e, t) {
|
|
|
731
727
|
/** Installs the until() machinery hook. Idempotent; called by until() before
|
|
732
728
|
* any authoritative-view read happens (same late-binding contract as the
|
|
733
729
|
* optimistic engine). */ function installAuthoritativeRead() {
|
|
734
|
-
if (GlobalQueue.
|
|
730
|
+
if (GlobalQueue.he === null) GlobalQueue.he = notifyAuthoritativeObservers;
|
|
735
731
|
}
|
|
736
732
|
|
|
737
733
|
function readNodeFast(e) {
|
|
738
|
-
if (latestReadActive || pendingCheckActive || e.oe || e.st || e.o?.
|
|
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;
|
|
739
735
|
let t = context;
|
|
740
736
|
if (t?.Gt) t = t.Dt;
|
|
741
737
|
if (t && tracking) link(e, t);
|
|
@@ -743,7 +739,7 @@ function readNodeFast(e) {
|
|
|
743
739
|
// committed visibility: like the effect half of createEffect and event
|
|
744
740
|
// handlers, effect-phase code never observes its own unsettled write — the
|
|
745
741
|
// write lands in the same flush's continuation (#3006).
|
|
746
|
-
return !t || e.
|
|
742
|
+
return !t || e.Re === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN ? e.be : e.Re;
|
|
747
743
|
}
|
|
748
744
|
|
|
749
745
|
function read(e) {
|
|
@@ -765,10 +761,10 @@ function read(e) {
|
|
|
765
761
|
} else if (typeof n.oe === "function") {
|
|
766
762
|
prepareComputed(e, false);
|
|
767
763
|
}
|
|
768
|
-
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) {
|
|
769
765
|
if (t && tracking) link(e, t);
|
|
770
766
|
// Committed visibility for children-forbidden readers — see readNodeFast.
|
|
771
|
-
return !t || e.
|
|
767
|
+
return !t || e.Re === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN ? e.be : e.Re;
|
|
772
768
|
}
|
|
773
769
|
if (t && tracking) {
|
|
774
770
|
link(e, t, pendingCheckActive);
|
|
@@ -837,12 +833,12 @@ function read(e) {
|
|
|
837
833
|
const n = e.o?.We;
|
|
838
834
|
if (n !== undefined) {
|
|
839
835
|
const i = n === NO_SNAPSHOT ? undefined : n;
|
|
840
|
-
const u = e.
|
|
836
|
+
const u = e.Re !== NOT_PENDING ? e.Re : e.be;
|
|
841
837
|
if (u !== i) t.ie |= REACTIVE_SNAPSHOT_STALE;
|
|
842
838
|
return i;
|
|
843
839
|
}
|
|
844
840
|
}
|
|
845
|
-
if (e.o?.
|
|
841
|
+
if (e.o?.Pe !== undefined && e.o?.Pe !== NOT_PENDING) {
|
|
846
842
|
// A17: the override IS the value for every reader — except an authoritative
|
|
847
843
|
// reader (until()'s predicate carries CONFIG_AUTHORITATIVE_READ): it must
|
|
848
844
|
// observe independently-arriving truth, and serving it the caller's own
|
|
@@ -853,7 +849,7 @@ function read(e) {
|
|
|
853
849
|
// authoritative — optimism never lives there); the sticky mark makes the
|
|
854
850
|
// A17-silent "landing equals override" paths notify this node's subs so
|
|
855
851
|
// the reader re-runs when truth arrives.
|
|
856
|
-
if (!(t && t.T & CONFIG_AUTHORITATIVE_READ)) return unwrapOverride(e.o?.
|
|
852
|
+
if (!(t && t.T & CONFIG_AUTHORITATIVE_READ)) return unwrapOverride(e.o?.Pe);
|
|
857
853
|
e.T |= CONFIG_AUTHORITATIVE_OBSERVED;
|
|
858
854
|
}
|
|
859
855
|
// Entanglement gate: a reader recomputing under an optimistic lane that reads
|
|
@@ -872,7 +868,7 @@ function read(e) {
|
|
|
872
868
|
// (The lane-context clause lives with the engine.) Children-forbidden readers
|
|
873
869
|
// (createTrackedEffect / onSettled callbacks) get committed visibility — see
|
|
874
870
|
// readNodeFast (#3006).
|
|
875
|
-
const l = !t || currentOptimisticLane !== null && GlobalQueue.bt(e, u, t) || e.
|
|
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 ||
|
|
876
872
|
// A17 for HELD truth (#3164, see CONFIG_HELD_TRUTH): staged confirming
|
|
877
873
|
// truth — fold-staged onto an armed family, or entangle-stolen by an
|
|
878
874
|
// awaited until() — is masked from ordinary readers until its
|
|
@@ -881,7 +877,7 @@ function read(e) {
|
|
|
881
877
|
// compose override + staged truth into a state no timeline contains).
|
|
882
878
|
// Authoritative readers (until()'s predicate) and latest() see the
|
|
883
879
|
// 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.
|
|
880
|
+
e.T & CONFIG_HELD_TRUTH && !latestReadActive && !(t.T & CONFIG_AUTHORITATIVE_READ) ? e.be : e.Re;
|
|
885
881
|
// Record that this isPending() probe observed the fresh pending value, so
|
|
886
882
|
// the probe doesn't pair "pending" with the new value (#2831).
|
|
887
883
|
if (pendingCheckActive) GlobalQueue.kt(e, l);
|
|
@@ -906,21 +902,21 @@ function setSignal(e, t) {
|
|
|
906
902
|
// always-present config instead of a missing-property probe), and every
|
|
907
903
|
// module that installs one installs the engine first.
|
|
908
904
|
if (e.T & CONFIG_OPTIMISTIC && !projectionWriteActive) return GlobalQueue.xt(e, t);
|
|
909
|
-
const n = e.
|
|
905
|
+
const n = e.Re === NOT_PENDING ? e.be : e.Re;
|
|
910
906
|
if (typeof t === "function") t = t(n);
|
|
911
907
|
// Uninitialized check first: the first commit has no previous value, so the
|
|
912
908
|
// user comparator must not run against `undefined` (matches recompute).
|
|
913
|
-
const i = !!(e.S & STATUS_UNINITIALIZED) || !e.
|
|
909
|
+
const i = !!(e.S & STATUS_UNINITIALIZED) || !e.pe || !e.pe(n, t);
|
|
914
910
|
if (!i) return t;
|
|
915
|
-
const u = e.
|
|
911
|
+
const u = e.Re !== NOT_PENDING;
|
|
916
912
|
if (!u) queuePendingNode(e);
|
|
917
|
-
e.
|
|
913
|
+
e.Re = t;
|
|
918
914
|
// syncCompanions only pokes _pendingSignal/_latestValueComputed — with
|
|
919
915
|
// neither companion present the call is a guaranteed no-op (companions are
|
|
920
916
|
// only ever created, never removed, and creating one installs the hook and
|
|
921
917
|
// sets CONFIG_HAS_COMPANIONS — one masked read replaces two optional-field
|
|
922
918
|
// probes on every write).
|
|
923
|
-
e.T & CONFIG_HAS_COMPANIONS && GlobalQueue.
|
|
919
|
+
e.T & CONFIG_HAS_COMPANIONS && GlobalQueue.Ue !== null && GlobalQueue.Ue(e, t);
|
|
924
920
|
// _time is a computed-only slot (§12e): writing it on a signal would fork
|
|
925
921
|
// the lean shape. Every read site is computed-typed.
|
|
926
922
|
if (e.oe !== undefined) e.Te = clock;
|
|
@@ -943,7 +939,7 @@ function setSignal(e, t) {
|
|
|
943
939
|
* cleanup point.
|
|
944
940
|
*/ function suppressComputedRecompute(e) {
|
|
945
941
|
deleteFromHeap(e, queueFor(e));
|
|
946
|
-
if (!(e.ie & REACTIVE_MANUAL_WRITE) && e.
|
|
942
|
+
if (!(e.ie & REACTIVE_MANUAL_WRITE) && e.Re === NOT_PENDING) {
|
|
947
943
|
queuePendingNode(e);
|
|
948
944
|
schedule();
|
|
949
945
|
}
|
package/dist/prod/core/effect.js
CHANGED
|
@@ -14,7 +14,7 @@ import { GlobalQueue, haltReactivity } from "./scheduler.js";
|
|
|
14
14
|
const r = !!f?.user;
|
|
15
15
|
const R = createEffectNode(t, e, E, r ? EFFECT_USER : EFFECT_RENDER, f);
|
|
16
16
|
recompute(R, true);
|
|
17
|
-
!f?.defer && (R.
|
|
17
|
+
!f?.defer && (R.ge === EFFECT_USER || f?.schedule ? R.C.enqueue(R.ge, runEffect.bind(null, R)) : runEffect(R));
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
function notifyEffectStatus(t, e) {
|
|
@@ -23,7 +23,7 @@ function notifyEffectStatus(t, e) {
|
|
|
23
23
|
const f = e !== undefined ? e : this.o?._;
|
|
24
24
|
if (E & STATUS_ERROR) {
|
|
25
25
|
this.C.notify(this, STATUS_PENDING, 0);
|
|
26
|
-
if (this.
|
|
26
|
+
if (this.ge === EFFECT_USER) {
|
|
27
27
|
// The error handler is the error arm of the effect phase (#2840 ruling):
|
|
28
28
|
// queue it like the effect function. It runs in the same imperative,
|
|
29
29
|
// writable scope, throws escalate the same way, and a held transition
|
|
@@ -35,7 +35,7 @@ function notifyEffectStatus(t, e) {
|
|
|
35
35
|
// re-propagates unblocked at commit.
|
|
36
36
|
if (this.S & STATUS_ERROR) {
|
|
37
37
|
this.tt = true;
|
|
38
|
-
this.C.enqueue(this.
|
|
38
|
+
this.C.enqueue(this.ge, this.nt ??= runEffect.bind(null, this));
|
|
39
39
|
}
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
@@ -43,7 +43,7 @@ function notifyEffectStatus(t, e) {
|
|
|
43
43
|
haltReactivity(unwrapStatusError(f));
|
|
44
44
|
throw f;
|
|
45
45
|
}
|
|
46
|
-
} else if (this.
|
|
46
|
+
} else if (this.ge === EFFECT_RENDER) {
|
|
47
47
|
this.C.notify(this, STATUS_PENDING | STATUS_ERROR, E, f);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
@@ -60,7 +60,7 @@ function runEffect(t) {
|
|
|
60
60
|
// Render effects bypass: their errors route to boundaries synchronously in
|
|
61
61
|
// notifyEffectStatus, and a runner queued by an earlier valueChanged in the
|
|
62
62
|
// same flush must not be hijacked by a later-arriving error status.
|
|
63
|
-
if (t.S & STATUS_ERROR && t.
|
|
63
|
+
if (t.S & STATUS_ERROR && t.ge === EFFECT_USER) {
|
|
64
64
|
const e = unwrapStatusError(t.o?._);
|
|
65
65
|
t.At = t.be;
|
|
66
66
|
t.tt = false;
|
|
@@ -126,7 +126,7 @@ GlobalQueue.it = runEffect;
|
|
|
126
126
|
E.Rt = undefined;
|
|
127
127
|
E.T = E.T & ~CONFIG_AUTO_DISPOSE | CONFIG_CHILDREN_FORBIDDEN;
|
|
128
128
|
E.tt = true;
|
|
129
|
-
E.
|
|
129
|
+
E.ge = EFFECT_TRACKED;
|
|
130
130
|
// Status dispatch rides the SHARED notifier (statusNotifierOf keys off
|
|
131
131
|
// _type): its error arm is behavior-identical to the closure that used to
|
|
132
132
|
// live here, without the per-node NodeExtension allocation.
|
package/dist/prod/core/heap.js
CHANGED
|
@@ -13,7 +13,7 @@ import { zombieQueue, dirtyQueue } from "./scheduler.js";
|
|
|
13
13
|
* the heap and go directly to their effect queue; everything else is inserted
|
|
14
14
|
* into its own (zombie-flag-routed) heap with the `_min` cursor pulled down.
|
|
15
15
|
*/ function enqueueSub(e) {
|
|
16
|
-
if (e.
|
|
16
|
+
if (e.ge === EFFECT_TRACKED) {
|
|
17
17
|
const E = e;
|
|
18
18
|
if (!E.tt) {
|
|
19
19
|
E.tt = true;
|
package/dist/prod/core/lanes.js
CHANGED
|
@@ -25,7 +25,7 @@ const activeLanes = new Set;
|
|
|
25
25
|
const r = t ? findLane(t) : null;
|
|
26
26
|
e = {
|
|
27
27
|
nn: n,
|
|
28
|
-
|
|
28
|
+
Oe: new Set,
|
|
29
29
|
tn: [ [], [] ],
|
|
30
30
|
rn: null,
|
|
31
31
|
Ae: activeTransition,
|
|
@@ -71,8 +71,8 @@ function adoptCompanionLane(n, e) {
|
|
|
71
71
|
// Move (not copy) the merged lane's work: after the merge all routing goes
|
|
72
72
|
// through findLane() to the root, so anything left behind here is dead —
|
|
73
73
|
// and anything *added* here later is a routing bug (INV-5).
|
|
74
|
-
for (const i of e.
|
|
75
|
-
e.
|
|
74
|
+
for (const i of e.Oe) n.Oe.add(i);
|
|
75
|
+
e.Oe.clear();
|
|
76
76
|
n.tn[0].push(...e.tn[0]);
|
|
77
77
|
n.tn[1].push(...e.tn[1]);
|
|
78
78
|
e.tn[0].length = 0;
|
|
@@ -109,7 +109,7 @@ function resolveTransition(n) {
|
|
|
109
109
|
* Check if a node has an active optimistic override.
|
|
110
110
|
*/ function hasActiveOverride(n) {
|
|
111
111
|
const e = n.o;
|
|
112
|
-
return e !== null && e.
|
|
112
|
+
return e !== null && e.Pe !== undefined && e.Pe !== NOT_PENDING;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
/**
|