@solidjs/signals 2.0.0-beta.20 → 2.0.0-beta.22
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/README.md +5 -1
- package/dist/dev.js +267 -307
- package/dist/node.cjs +1125 -1168
- package/dist/prod/affects.js +71 -163
- package/dist/prod/boundaries.js +141 -137
- package/dist/prod/core/action.js +50 -12
- package/dist/prod/core/async.js +83 -99
- package/dist/prod/core/core.js +255 -298
- package/dist/prod/core/effect.js +46 -46
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +54 -46
- package/dist/prod/core/heap.js +58 -50
- package/dist/prod/core/lanes.js +34 -34
- package/dist/prod/core/optimistic.js +64 -106
- package/dist/prod/core/owner.js +52 -52
- package/dist/prod/core/scheduler.js +217 -211
- package/dist/prod/core/verdict.js +144 -78
- package/dist/prod/map.js +101 -101
- package/dist/prod/signals.js +23 -5
- package/dist/prod/store/optimistic.js +11 -11
- package/dist/prod/store/projection.js +2 -2
- package/dist/prod/store/store.js +14 -14
- package/dist/types/affects.d.ts +9 -9
- package/dist/types/core/action.d.ts +16 -0
- package/dist/types/core/async.d.ts +1 -1
- package/dist/types/core/error.d.ts +7 -0
- package/dist/types/core/scheduler.d.ts +1 -6
- package/dist/types/core/types.d.ts +6 -11
- package/dist/types-cjs/affects.d.cts +9 -9
- package/dist/types-cjs/core/action.d.cts +16 -0
- package/dist/types-cjs/core/async.d.cts +1 -1
- package/dist/types-cjs/core/error.d.cts +7 -0
- package/dist/types-cjs/core/scheduler.d.cts +1 -6
- package/dist/types-cjs/core/types.d.cts +6 -11
- package/package.json +1 -1
package/dist/node.cjs
CHANGED
|
@@ -330,15 +330,15 @@ const transitions = new Set;
|
|
|
330
330
|
const dirtyQueue = {
|
|
331
331
|
h: new Array(2e3).fill(undefined),
|
|
332
332
|
N: false,
|
|
333
|
-
|
|
334
|
-
|
|
333
|
+
C: 0,
|
|
334
|
+
P: 0
|
|
335
335
|
};
|
|
336
336
|
|
|
337
337
|
const zombieQueue = {
|
|
338
338
|
h: new Array(2e3).fill(undefined),
|
|
339
339
|
N: false,
|
|
340
|
-
|
|
341
|
-
|
|
340
|
+
C: 0,
|
|
341
|
+
P: 0
|
|
342
342
|
};
|
|
343
343
|
|
|
344
344
|
let clock = 0;
|
|
@@ -572,35 +572,32 @@ class GlobalQueue extends Queue {
|
|
|
572
572
|
// _clearOptimisticStore).
|
|
573
573
|
static ne=null;
|
|
574
574
|
// affects()-side hooks (wired by affects.ts, mirroring _update): the mark
|
|
575
|
-
// engine — count/register/release
|
|
576
|
-
//
|
|
577
|
-
//
|
|
575
|
+
// engine — count/register/release — lives with the feature. Every call site
|
|
576
|
+
// is gated by state only that module creates, so `!` invocations are safe
|
|
577
|
+
// once the gate holds.
|
|
578
578
|
static re=null;
|
|
579
579
|
static ie=null;
|
|
580
580
|
static oe=null;
|
|
581
|
-
static se=null;
|
|
582
|
-
static ue=null;
|
|
583
|
-
static le=null;
|
|
584
581
|
// External-source bridge (wired by enableExternalSource(); null while no
|
|
585
582
|
// config is active — including after _resetExternalSourceConfig()).
|
|
586
|
-
static
|
|
587
|
-
static
|
|
583
|
+
static se=null;
|
|
584
|
+
static ue=null;
|
|
588
585
|
// Verdict-layer hooks (wired by verdict.ts when isPending()/latest() are
|
|
589
586
|
// imported; null in apps that never use them). Call sites either guard for
|
|
590
587
|
// null or sit behind state only the verdict layer can create (`!` is safe
|
|
591
588
|
// there: `_pendingSignal`/`_latestValueComputed` are only ever assigned by
|
|
592
589
|
// verdict.ts, and `pendingCheckActive`/`latestReadActive` only flip inside
|
|
593
590
|
// isPending()/latest()).
|
|
591
|
+
static le=null;
|
|
592
|
+
static ce=null;
|
|
593
|
+
static ae=null;
|
|
594
594
|
static fe=null;
|
|
595
595
|
static Ee=null;
|
|
596
|
-
static de=null;
|
|
597
596
|
static Se=null;
|
|
597
|
+
static de=null;
|
|
598
598
|
static Te=null;
|
|
599
599
|
static pe=null;
|
|
600
600
|
static _e=null;
|
|
601
|
-
static Oe=null;
|
|
602
|
-
static Re=null;
|
|
603
|
-
static Ie=null;
|
|
604
601
|
// Optimistic-engine hooks (wired by core/optimistic.ts via
|
|
605
602
|
// installOptimisticEngine(), called from verdict.ts / createOptimistic /
|
|
606
603
|
// createOptimisticStore — every module that can create optimistic state).
|
|
@@ -608,20 +605,18 @@ class GlobalQueue extends Queue {
|
|
|
608
605
|
// `_overrideValue` slot, a lane in `activeLanes`, an `_optimisticNodes`
|
|
609
606
|
// entry, or a non-null `currentOptimisticLane`, so `!` invocations are safe
|
|
610
607
|
// once the gate holds.
|
|
608
|
+
static Oe=null;
|
|
609
|
+
static Re=null;
|
|
610
|
+
static Ie=null;
|
|
611
611
|
static Ae=null;
|
|
612
612
|
static he=null;
|
|
613
613
|
static Ne=null;
|
|
614
614
|
static ye=null;
|
|
615
|
-
static Pe=null;
|
|
616
615
|
static Ce=null;
|
|
616
|
+
static Pe=null;
|
|
617
617
|
static ge=null;
|
|
618
618
|
static be=null;
|
|
619
619
|
static De=null;
|
|
620
|
-
static me=null;
|
|
621
|
-
static ve=null;
|
|
622
|
-
static we=null;
|
|
623
|
-
static Ge=null;
|
|
624
|
-
static Le=null;
|
|
625
620
|
flush() {
|
|
626
621
|
if (this.B) return;
|
|
627
622
|
this.B = true;
|
|
@@ -634,26 +629,26 @@ class GlobalQueue extends Queue {
|
|
|
634
629
|
const e = activeTransition;
|
|
635
630
|
runHeap(zombieQueue, GlobalQueue.X);
|
|
636
631
|
// Detach: the stashed transition keeps its batch; ambient work that
|
|
637
|
-
// follows lands in a fresh one.
|
|
638
|
-
|
|
632
|
+
// follows lands in a fresh one. If the batch is already a separate
|
|
633
|
+
// ambient one — action done() restored activeTransition without
|
|
634
|
+
// adopting the batch, and an ordinary write landed there before
|
|
635
|
+
// the scheduled flush (#2916) — keep it: replacing it would strand
|
|
636
|
+
// its queued pending nodes with held _pendingValues forever.
|
|
637
|
+
if (this.D === e) currentBatch = this.D = createBatch();
|
|
639
638
|
// Run lane effects immediately (before stashing) - lanes with no pending async
|
|
640
639
|
if (activeLanes.size) {
|
|
641
|
-
GlobalQueue.
|
|
642
|
-
GlobalQueue.
|
|
640
|
+
GlobalQueue.he(EFFECT_RENDER);
|
|
641
|
+
GlobalQueue.he(EFFECT_USER);
|
|
643
642
|
}
|
|
644
643
|
this.stashQueues(e.K);
|
|
645
644
|
clock++;
|
|
646
|
-
|
|
645
|
+
// A kept ambient batch may hold pending nodes (#2916): stay
|
|
646
|
+
// scheduled so the outer drain loop commits them via the plain
|
|
647
|
+
// flush path instead of leaving them until the next natural flush.
|
|
648
|
+
scheduled = dirtyQueue.P >= dirtyQueue.C || this.D.H.length > 0;
|
|
647
649
|
reassignPendingTransition(e.H);
|
|
648
650
|
activeTransition = null;
|
|
649
|
-
|
|
650
|
-
// wraps finalizePureQueue in the engine; a non-empty _optimisticNodes
|
|
651
|
-
// means _optimisticWrite ran, which installed the hook.
|
|
652
|
-
if (!e.$.length && !e.j.size && e.G.length) {
|
|
653
|
-
GlobalQueue.Ne(e);
|
|
654
|
-
} else {
|
|
655
|
-
finalizePureQueue(null, true);
|
|
656
|
-
}
|
|
651
|
+
finalizePureQueue(null, true);
|
|
657
652
|
return;
|
|
658
653
|
}
|
|
659
654
|
const t = activeTransition;
|
|
@@ -678,7 +673,7 @@ class GlobalQueue extends Queue {
|
|
|
678
673
|
} else {
|
|
679
674
|
if (canUseSimpleSyncFlush(this)) {
|
|
680
675
|
commitPendingNodes();
|
|
681
|
-
if (dirtyQueue.
|
|
676
|
+
if (dirtyQueue.P >= dirtyQueue.C) {
|
|
682
677
|
runHeap(dirtyQueue, GlobalQueue.X);
|
|
683
678
|
commitPendingNodes();
|
|
684
679
|
}
|
|
@@ -689,11 +684,11 @@ class GlobalQueue extends Queue {
|
|
|
689
684
|
}
|
|
690
685
|
clock++;
|
|
691
686
|
// Check if finalization added items to the heap (from optimistic reversion)
|
|
692
|
-
scheduled = dirtyQueue.
|
|
687
|
+
scheduled = dirtyQueue.P >= dirtyQueue.C;
|
|
693
688
|
// Run lane effects first (for ready lanes), then regular effects
|
|
694
|
-
activeLanes.size && GlobalQueue.
|
|
689
|
+
activeLanes.size && GlobalQueue.he(EFFECT_RENDER);
|
|
695
690
|
this.run(EFFECT_RENDER);
|
|
696
|
-
activeLanes.size && GlobalQueue.
|
|
691
|
+
activeLanes.size && GlobalQueue.he(EFFECT_USER);
|
|
697
692
|
this.run(EFFECT_USER);
|
|
698
693
|
if (false) ;
|
|
699
694
|
if (false && !scheduled && !activeTransition && transitions.size === 0 && activeLanes.size === 0) ;
|
|
@@ -706,7 +701,12 @@ class GlobalQueue extends Queue {
|
|
|
706
701
|
// Only track async if the boundary is propagating STATUS_PENDING (not caught by boundary)
|
|
707
702
|
if (t & STATUS_PENDING) {
|
|
708
703
|
if (n & STATUS_PENDING) {
|
|
709
|
-
const t = r !== undefined ? r : e.
|
|
704
|
+
const t = r !== undefined ? r : e.me;
|
|
705
|
+
// A visibility-only mark notification (the affects() boundary
|
|
706
|
+
// channel) updates display state on its way up but must be invisible
|
|
707
|
+
// to completion accounting BY CONSTRUCTION: it never registers a
|
|
708
|
+
// reporter and never counts toward the loading-boundary diagnostic.
|
|
709
|
+
if (t?.ve) return true;
|
|
710
710
|
if (activeTransition && t) {
|
|
711
711
|
const n = t.source;
|
|
712
712
|
let r = activeTransition.j.get(n);
|
|
@@ -780,48 +780,48 @@ function insertSubs(e, t = false) {
|
|
|
780
780
|
// Get source lane: prefer node's own lane over current context
|
|
781
781
|
// This is important for isPending signals which need their own lane to flush immediately
|
|
782
782
|
const n = e.i || currentOptimisticLane;
|
|
783
|
-
const r = e.
|
|
783
|
+
const r = e.we !== undefined;
|
|
784
784
|
const i = reaskArmed;
|
|
785
|
-
for (let o = e.k; o !== null; o = o.
|
|
785
|
+
for (let o = e.k; o !== null; o = o.Ge) {
|
|
786
786
|
// A value-change notification is a new question for the subscriber: any
|
|
787
787
|
// pending re-ask mark (refresh) it carried is superseded.
|
|
788
|
-
if (i) o.
|
|
789
|
-
if (r && o.
|
|
790
|
-
o.
|
|
788
|
+
if (i) o.Ue.Le &= ~REACTIVE_REASK;
|
|
789
|
+
if (r && o.Ue.ke & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
790
|
+
o.Ue.Le |= REACTIVE_SNAPSHOT_STALE;
|
|
791
791
|
continue;
|
|
792
792
|
}
|
|
793
793
|
if (t && n) {
|
|
794
|
-
o.
|
|
795
|
-
assignOrMergeLane(o.
|
|
794
|
+
o.Ue.Le |= REACTIVE_OPTIMISTIC_DIRTY;
|
|
795
|
+
assignOrMergeLane(o.Ue, n);
|
|
796
796
|
} else if (t) {
|
|
797
|
-
o.
|
|
797
|
+
o.Ue.Le |= REACTIVE_OPTIMISTIC_DIRTY;
|
|
798
798
|
// No source lane means reversion - clear subscriber's lane so effects go to regular queue
|
|
799
|
-
o.
|
|
799
|
+
o.Ue.i = undefined;
|
|
800
800
|
}
|
|
801
|
-
enqueueSub(o.
|
|
801
|
+
enqueueSub(o.Ue);
|
|
802
802
|
}
|
|
803
803
|
}
|
|
804
804
|
|
|
805
805
|
function commitPendingNode(e) {
|
|
806
806
|
const t = e;
|
|
807
|
-
if (!t.
|
|
807
|
+
if (!t.Ve) {
|
|
808
808
|
if (e.V !== NOT_PENDING) {
|
|
809
|
-
e.
|
|
809
|
+
e.xe = e.V;
|
|
810
810
|
e.V = NOT_PENDING;
|
|
811
811
|
}
|
|
812
|
-
if (e._ || e.O) GlobalQueue.
|
|
812
|
+
if (e._ || e.O) GlobalQueue.fe(e);
|
|
813
813
|
return;
|
|
814
814
|
}
|
|
815
815
|
if (e.V !== NOT_PENDING) {
|
|
816
|
-
e.
|
|
816
|
+
e.xe = e.V;
|
|
817
817
|
e.V = NOT_PENDING;
|
|
818
818
|
// Set _modified for effects, but not for tracked effects (they handle their own scheduling)
|
|
819
|
-
if (e.
|
|
819
|
+
if (e.Qe && e.Qe !== EFFECT_TRACKED) e.Fe = true;
|
|
820
820
|
}
|
|
821
|
-
t.
|
|
822
|
-
if (!(t
|
|
823
|
-
if (t.
|
|
824
|
-
if (e._ || e.O) GlobalQueue.
|
|
821
|
+
t.Le &= ~REACTIVE_MANUAL_WRITE;
|
|
822
|
+
if (!(t.We & STATUS_PENDING)) t.We &= ~STATUS_UNINITIALIZED;
|
|
823
|
+
if (t.Me !== null || t.He !== null) GlobalQueue.J(t, false, true);
|
|
824
|
+
if (e._ || e.O) GlobalQueue.fe(e);
|
|
825
825
|
}
|
|
826
826
|
|
|
827
827
|
function commitPendingNodes() {
|
|
@@ -838,7 +838,7 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
838
838
|
const n = !t;
|
|
839
839
|
if (n) commitPendingNodes();
|
|
840
840
|
if (!t && globalQueue.m.length) checkBoundaryChildren(globalQueue);
|
|
841
|
-
const r = dirtyQueue.
|
|
841
|
+
const r = dirtyQueue.P >= dirtyQueue.C;
|
|
842
842
|
if (r) runHeap(dirtyQueue, GlobalQueue.X);
|
|
843
843
|
if (n) {
|
|
844
844
|
if (r) commitPendingNodes();
|
|
@@ -846,20 +846,26 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
846
846
|
const t = e ?? globalQueue.D;
|
|
847
847
|
// Optimistic reversion: a non-empty batch means _optimisticWrite ran,
|
|
848
848
|
// which installed the engine's hooks.
|
|
849
|
-
if (t.G.length) GlobalQueue.
|
|
849
|
+
if (t.G.length) GlobalQueue.Re(t.G);
|
|
850
850
|
// Replay entanglement: subs recorded by the read-time gate get rescheduled
|
|
851
851
|
// so they re-run with the now-committed values visible.
|
|
852
852
|
if (e && e.q.size) {
|
|
853
853
|
for (const t of e.q) {
|
|
854
|
-
if (t.
|
|
854
|
+
if (t.Le & REACTIVE_DISPOSED) continue;
|
|
855
855
|
enqueueSub(t);
|
|
856
856
|
}
|
|
857
857
|
e.q.clear();
|
|
858
858
|
}
|
|
859
859
|
// Declared motion ends with the transaction: settle (or plain flush end
|
|
860
860
|
// for ambient marks) releases each registration's refcount. A non-empty
|
|
861
|
-
// batch means registerAffectsMark ran, which installed the hook.
|
|
862
|
-
|
|
861
|
+
// batch means registerAffectsMark ran, which installed the hook. Marks
|
|
862
|
+
// held boundary display state through the visual channel, and their
|
|
863
|
+
// release is the display-state update point — re-run the boundary sweep
|
|
864
|
+
// (the earlier sweep above ran while the marks were still live).
|
|
865
|
+
if (t.L.length) {
|
|
866
|
+
GlobalQueue.re(t.L);
|
|
867
|
+
if (globalQueue.m.length) checkBoundaryChildren(globalQueue);
|
|
868
|
+
}
|
|
863
869
|
// A non-empty set means trackOptimisticStore ran, which installed the
|
|
864
870
|
// hook; the hook iterates, clears, and schedules (keeping the loop out of
|
|
865
871
|
// core lets esbuild shake it — rollup already folds the null guard). The
|
|
@@ -867,13 +873,13 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
867
873
|
if (t.U.size) GlobalQueue.te(t.U, e);
|
|
868
874
|
sweepTransientStoreNodes();
|
|
869
875
|
// Lanes only enter activeLanes through the engine's getOrCreateLane.
|
|
870
|
-
if (activeLanes.size) GlobalQueue.
|
|
876
|
+
if (activeLanes.size) GlobalQueue.Ae(e);
|
|
871
877
|
}
|
|
872
878
|
}
|
|
873
879
|
|
|
874
880
|
function checkBoundaryChildren(e) {
|
|
875
881
|
for (const t of e.m) {
|
|
876
|
-
t.
|
|
882
|
+
t.je?.();
|
|
877
883
|
checkBoundaryChildren(t);
|
|
878
884
|
}
|
|
879
885
|
}
|
|
@@ -940,16 +946,16 @@ function runQueue$1(e, t) {
|
|
|
940
946
|
}
|
|
941
947
|
|
|
942
948
|
function reporterBlocksSource(e, t) {
|
|
943
|
-
if (e.
|
|
944
|
-
if (e
|
|
945
|
-
for (let n = e.
|
|
946
|
-
let e = n.
|
|
949
|
+
if (e.Le & (REACTIVE_ZOMBIE | REACTIVE_DISPOSED)) return false;
|
|
950
|
+
if (e.$e?.has(t)) return true;
|
|
951
|
+
for (let n = e.Ke; n; n = n.Ye) {
|
|
952
|
+
let e = n.qe;
|
|
947
953
|
while (e) {
|
|
948
|
-
if (e === t || e.
|
|
954
|
+
if (e === t || e.Be === t) return true;
|
|
949
955
|
e = e.t;
|
|
950
956
|
}
|
|
951
957
|
}
|
|
952
|
-
return !!(e
|
|
958
|
+
return !!(e.We & STATUS_PENDING && e.me instanceof NotReadyError && e.me.source === t);
|
|
953
959
|
}
|
|
954
960
|
|
|
955
961
|
function transitionComplete(e) {
|
|
@@ -965,7 +971,7 @@ function transitionComplete(e) {
|
|
|
965
971
|
}
|
|
966
972
|
r.delete(e);
|
|
967
973
|
}
|
|
968
|
-
if (!i) e.j.delete(n); else if (n
|
|
974
|
+
if (!i) e.j.delete(n); else if (n.We & STATUS_PENDING && n.me?.source === n) {
|
|
969
975
|
t = false;
|
|
970
976
|
break;
|
|
971
977
|
}
|
|
@@ -973,7 +979,7 @@ function transitionComplete(e) {
|
|
|
973
979
|
// Override blockage lives with the engine. Absent hook = "no optimistic
|
|
974
980
|
// blockage", which is exact: only _optimisticWrite (engine) pushes to
|
|
975
981
|
// _optimisticNodes, so without the engine the loop was vacuous anyway.
|
|
976
|
-
if (t && e.G.length && GlobalQueue.
|
|
982
|
+
if (t && e.G.length && GlobalQueue.Ie(e)) t = false;
|
|
977
983
|
t && (e.I = true);
|
|
978
984
|
return t;
|
|
979
985
|
}
|
|
@@ -998,7 +1004,7 @@ function runInTransition(e, t) {
|
|
|
998
1004
|
}
|
|
999
1005
|
|
|
1000
1006
|
/** The queue a node belongs to, picked from its own zombie flag. */ function queueFor(e) {
|
|
1001
|
-
return e.
|
|
1007
|
+
return e.Le & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue;
|
|
1002
1008
|
}
|
|
1003
1009
|
|
|
1004
1010
|
/**
|
|
@@ -1006,86 +1012,94 @@ function runInTransition(e, t) {
|
|
|
1006
1012
|
* the heap and go directly to their effect queue; everything else is inserted
|
|
1007
1013
|
* into its own (zombie-flag-routed) heap with the `_min` cursor pulled down.
|
|
1008
1014
|
*/ function enqueueSub(e) {
|
|
1009
|
-
if (e.
|
|
1015
|
+
if (e.Qe === EFFECT_TRACKED) {
|
|
1010
1016
|
const t = e;
|
|
1011
|
-
if (!t.
|
|
1012
|
-
t.
|
|
1013
|
-
t.
|
|
1017
|
+
if (!t.Fe) {
|
|
1018
|
+
t.Fe = true;
|
|
1019
|
+
t.Ze.enqueue(EFFECT_USER, t.Xe);
|
|
1014
1020
|
}
|
|
1015
1021
|
return;
|
|
1016
1022
|
}
|
|
1017
1023
|
const t = queueFor(e);
|
|
1018
|
-
if (t.
|
|
1024
|
+
if (t.C > e.ze) t.C = e.ze;
|
|
1019
1025
|
insertIntoHeap(e, t);
|
|
1020
1026
|
}
|
|
1021
1027
|
|
|
1022
1028
|
function actualInsertIntoHeap(e, t) {
|
|
1023
|
-
const n = (e.Z?.
|
|
1024
|
-
if (n >= e.
|
|
1025
|
-
const r = e.
|
|
1029
|
+
const n = (e.Z?.Je ? e.Z.et?.ze : e.Z?.ze) ?? -1;
|
|
1030
|
+
if (n >= e.ze) e.ze = n + 1;
|
|
1031
|
+
const r = e.ze;
|
|
1026
1032
|
const i = t.h[r];
|
|
1027
1033
|
if (i === undefined) t.h[r] = e; else {
|
|
1028
|
-
const t = i.
|
|
1029
|
-
t.
|
|
1030
|
-
e.
|
|
1031
|
-
i.
|
|
1034
|
+
const t = i.tt;
|
|
1035
|
+
t.nt = e;
|
|
1036
|
+
e.tt = t;
|
|
1037
|
+
i.tt = e;
|
|
1032
1038
|
}
|
|
1033
|
-
if (r > t.
|
|
1039
|
+
if (r > t.P) t.P = r;
|
|
1034
1040
|
}
|
|
1035
1041
|
|
|
1036
1042
|
function insertIntoHeap(e, t) {
|
|
1037
|
-
let n = e.
|
|
1043
|
+
let n = e.Le;
|
|
1038
1044
|
if (n & (REACTIVE_IN_HEAP | REACTIVE_RECOMPUTING_DEPS | REACTIVE_MANUAL_WRITE)) return;
|
|
1039
1045
|
if (n & REACTIVE_CHECK) {
|
|
1040
|
-
e.
|
|
1041
|
-
} else
|
|
1046
|
+
e.Le = n & -4 | REACTIVE_DIRTY | REACTIVE_IN_HEAP;
|
|
1047
|
+
} else {
|
|
1048
|
+
e.Le = n | REACTIVE_IN_HEAP;
|
|
1049
|
+
// An unmarked node entering a marked heap invalidates the markHeap memo:
|
|
1050
|
+
// `_marked` is only reset by runHeap, so a write between two mid-tick
|
|
1051
|
+
// pulls (read-time markHeap + updateIfNecessary) would otherwise leave
|
|
1052
|
+
// this node unmarked and every downstream pull stale until the next
|
|
1053
|
+
// flush (#2922: the second `latest()` returned the first write's value).
|
|
1054
|
+
if (t.N && !(n & REACTIVE_DIRTY)) t.N = false;
|
|
1055
|
+
}
|
|
1042
1056
|
if (!(n & REACTIVE_IN_HEAP_HEIGHT)) actualInsertIntoHeap(e, t);
|
|
1043
1057
|
}
|
|
1044
1058
|
|
|
1045
1059
|
function insertIntoHeapHeight(e, t) {
|
|
1046
|
-
let n = e.
|
|
1060
|
+
let n = e.Le;
|
|
1047
1061
|
if (n & (REACTIVE_IN_HEAP | REACTIVE_RECOMPUTING_DEPS | REACTIVE_IN_HEAP_HEIGHT | REACTIVE_MANUAL_WRITE)) return;
|
|
1048
|
-
e.
|
|
1062
|
+
e.Le = n | REACTIVE_IN_HEAP_HEIGHT;
|
|
1049
1063
|
actualInsertIntoHeap(e, t);
|
|
1050
1064
|
}
|
|
1051
1065
|
|
|
1052
1066
|
function deleteFromHeap(e, t) {
|
|
1053
|
-
const n = e.
|
|
1067
|
+
const n = e.Le;
|
|
1054
1068
|
if (!(n & (REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT))) return;
|
|
1055
|
-
e.
|
|
1056
|
-
const r = e.
|
|
1057
|
-
if (e.
|
|
1058
|
-
const n = e.
|
|
1069
|
+
e.Le = n & -25;
|
|
1070
|
+
const r = e.ze;
|
|
1071
|
+
if (e.tt === e) t.h[r] = undefined; else {
|
|
1072
|
+
const n = e.nt;
|
|
1059
1073
|
const i = t.h[r];
|
|
1060
1074
|
const o = n ?? i;
|
|
1061
|
-
if (e === i) t.h[r] = n; else e.
|
|
1062
|
-
o.
|
|
1075
|
+
if (e === i) t.h[r] = n; else e.tt.nt = n;
|
|
1076
|
+
o.tt = e.tt;
|
|
1063
1077
|
}
|
|
1064
|
-
e.
|
|
1065
|
-
e.
|
|
1078
|
+
e.tt = e;
|
|
1079
|
+
e.nt = undefined;
|
|
1066
1080
|
}
|
|
1067
1081
|
|
|
1068
1082
|
function markHeap(e) {
|
|
1069
1083
|
if (e.N) return;
|
|
1070
1084
|
e.N = true;
|
|
1071
|
-
for (let t = 0; t <= e.
|
|
1072
|
-
for (let n = e.h[t]; n !== undefined; n = n.
|
|
1073
|
-
if (n.
|
|
1085
|
+
for (let t = 0; t <= e.P; t++) {
|
|
1086
|
+
for (let n = e.h[t]; n !== undefined; n = n.nt) {
|
|
1087
|
+
if (n.Le & REACTIVE_IN_HEAP) markNode(n);
|
|
1074
1088
|
}
|
|
1075
1089
|
}
|
|
1076
1090
|
}
|
|
1077
1091
|
|
|
1078
1092
|
function markNode(e, t = REACTIVE_DIRTY) {
|
|
1079
|
-
const n = e.
|
|
1093
|
+
const n = e.Le;
|
|
1080
1094
|
if ((n & (REACTIVE_CHECK | REACTIVE_DIRTY)) >= t) return;
|
|
1081
|
-
e.
|
|
1082
|
-
for (let t = e.k; t !== null; t = t.
|
|
1083
|
-
markNode(t.
|
|
1084
|
-
}
|
|
1085
|
-
if (e.
|
|
1086
|
-
for (let t = e.
|
|
1087
|
-
for (let e = t.k; e !== null; e = e.
|
|
1088
|
-
markNode(e.
|
|
1095
|
+
e.Le = n & -4 | t;
|
|
1096
|
+
for (let t = e.k; t !== null; t = t.Ge) {
|
|
1097
|
+
markNode(t.Ue, REACTIVE_CHECK);
|
|
1098
|
+
}
|
|
1099
|
+
if (e.rt !== null) {
|
|
1100
|
+
for (let t = e.rt; t !== null; t = t.it) {
|
|
1101
|
+
for (let e = t.k; e !== null; e = e.Ge) {
|
|
1102
|
+
markNode(e.Ue, REACTIVE_CHECK);
|
|
1089
1103
|
}
|
|
1090
1104
|
}
|
|
1091
1105
|
}
|
|
@@ -1093,33 +1107,33 @@ function markNode(e, t = REACTIVE_DIRTY) {
|
|
|
1093
1107
|
|
|
1094
1108
|
function runHeap(e, t) {
|
|
1095
1109
|
e.N = false;
|
|
1096
|
-
for (e.
|
|
1097
|
-
let n = e.h[e.
|
|
1110
|
+
for (e.C = 0; e.C <= e.P; e.C++) {
|
|
1111
|
+
let n = e.h[e.C];
|
|
1098
1112
|
while (n !== undefined) {
|
|
1099
|
-
if (n.
|
|
1100
|
-
n = e.h[e.
|
|
1113
|
+
if (n.Le & REACTIVE_IN_HEAP) t(n); else adjustHeight(n, e);
|
|
1114
|
+
n = e.h[e.C];
|
|
1101
1115
|
}
|
|
1102
1116
|
}
|
|
1103
|
-
e.
|
|
1117
|
+
e.P = 0;
|
|
1104
1118
|
}
|
|
1105
1119
|
|
|
1106
1120
|
function adjustHeight(e, t) {
|
|
1107
1121
|
deleteFromHeap(e, t);
|
|
1108
|
-
let n = e.
|
|
1109
|
-
for (let t = e.
|
|
1110
|
-
const e = t.
|
|
1111
|
-
const r = e.
|
|
1112
|
-
if (r.
|
|
1113
|
-
}
|
|
1114
|
-
if (e.
|
|
1115
|
-
e.
|
|
1116
|
-
for (let t = e.k; t !== null; t = t.
|
|
1122
|
+
let n = e.ze;
|
|
1123
|
+
for (let t = e.Ke; t; t = t.Ye) {
|
|
1124
|
+
const e = t.qe;
|
|
1125
|
+
const r = e.Be || e;
|
|
1126
|
+
if (r.Ve && r.ze >= n) n = r.ze + 1;
|
|
1127
|
+
}
|
|
1128
|
+
if (e.ze !== n) {
|
|
1129
|
+
e.ze = n;
|
|
1130
|
+
for (let t = e.k; t !== null; t = t.Ge) {
|
|
1117
1131
|
// Route each subscriber by its own zombie flag, mirroring the
|
|
1118
1132
|
// post-recompute height-adjust path. Inserting into the running `heap`
|
|
1119
1133
|
// unconditionally can park a zombie in `dirtyQueue` (or a live node in
|
|
1120
1134
|
// `zombieQueue`), breaking the flag/queue invariant `deleteFromHeap`
|
|
1121
1135
|
// relies on — the same corruption class as #2759.
|
|
1122
|
-
insertIntoHeapHeight(t.
|
|
1136
|
+
insertIntoHeapHeight(t.Ue, queueFor(t.Ue));
|
|
1123
1137
|
}
|
|
1124
1138
|
}
|
|
1125
1139
|
}
|
|
@@ -1128,10 +1142,10 @@ const PENDING_OWNER = {};
|
|
|
1128
1142
|
|
|
1129
1143
|
// Dummy owner to trigger store's read() path
|
|
1130
1144
|
function markDisposal(e) {
|
|
1131
|
-
let t = e.
|
|
1145
|
+
let t = e.ot;
|
|
1132
1146
|
while (t) {
|
|
1133
|
-
const e = t.
|
|
1134
|
-
t.
|
|
1147
|
+
const e = t.Le;
|
|
1148
|
+
t.Le = e | REACTIVE_ZOMBIE;
|
|
1135
1149
|
// migrate height-adjust entries too, not just recompute entries: every
|
|
1136
1150
|
// `deleteFromHeap` call site picks the queue from the zombie flag, so a
|
|
1137
1151
|
// node left physically linked in `dirtyQueue` after being zombified gets
|
|
@@ -1142,79 +1156,79 @@ function markDisposal(e) {
|
|
|
1142
1156
|
if (e & REACTIVE_IN_HEAP) insertIntoHeap(t, zombieQueue); else insertIntoHeapHeight(t, zombieQueue);
|
|
1143
1157
|
}
|
|
1144
1158
|
markDisposal(t);
|
|
1145
|
-
t = t.
|
|
1159
|
+
t = t.st;
|
|
1146
1160
|
}
|
|
1147
1161
|
}
|
|
1148
1162
|
|
|
1149
1163
|
function dispose(e) {
|
|
1150
|
-
let t = e.
|
|
1164
|
+
let t = e.Ke;
|
|
1151
1165
|
while (t !== null) {
|
|
1152
1166
|
t = unlinkSubs(t);
|
|
1153
1167
|
}
|
|
1154
|
-
e.
|
|
1155
|
-
e.
|
|
1168
|
+
e.Ke = null;
|
|
1169
|
+
e.ut = null;
|
|
1156
1170
|
disposeChildren(e, true);
|
|
1157
1171
|
}
|
|
1158
1172
|
|
|
1159
1173
|
function disposeChildren(e, t = false, n) {
|
|
1160
|
-
const r = e.
|
|
1174
|
+
const r = e.Le;
|
|
1161
1175
|
if (r & REACTIVE_DISPOSED) return;
|
|
1162
1176
|
if (t) {
|
|
1163
|
-
e.
|
|
1177
|
+
e.Le = r | REACTIVE_DISPOSED;
|
|
1164
1178
|
// Companions are created detached and outlive their owner, but a verdict
|
|
1165
1179
|
// must not: a disposed source can never settle, so an isPending companion
|
|
1166
1180
|
// latched `true` here would hold a spinner forever (INV-9, the PR #2845
|
|
1167
1181
|
// edge). Snap runs after the DISPOSED flag is set so the oracle reads
|
|
1168
1182
|
// false, and notifies subscribers still watching the companion.
|
|
1169
1183
|
const t = e;
|
|
1170
|
-
if (t._ || t.O) GlobalQueue.
|
|
1184
|
+
if (t._ || t.O) GlobalQueue.fe(t);
|
|
1171
1185
|
}
|
|
1172
|
-
if (t && e.
|
|
1173
|
-
let i = n ? e.
|
|
1186
|
+
if (t && e.Ve) e.lt = null;
|
|
1187
|
+
let i = n ? e.Me : e.ot;
|
|
1174
1188
|
while (i) {
|
|
1175
|
-
const e = i.
|
|
1176
|
-
if (i.
|
|
1189
|
+
const e = i.st;
|
|
1190
|
+
if (i.Ke) {
|
|
1177
1191
|
const e = i;
|
|
1178
1192
|
deleteFromHeap(e, queueFor(e));
|
|
1179
|
-
let t = e.
|
|
1193
|
+
let t = e.Ke;
|
|
1180
1194
|
do {
|
|
1181
1195
|
t = unlinkSubs(t);
|
|
1182
1196
|
} while (t !== null);
|
|
1183
|
-
e.
|
|
1184
|
-
e.
|
|
1197
|
+
e.Ke = null;
|
|
1198
|
+
e.ut = null;
|
|
1185
1199
|
}
|
|
1186
1200
|
disposeChildren(i, true);
|
|
1187
1201
|
i = e;
|
|
1188
1202
|
}
|
|
1189
1203
|
if (n) {
|
|
1190
|
-
e.
|
|
1204
|
+
e.Me = null;
|
|
1191
1205
|
} else {
|
|
1192
|
-
e.
|
|
1193
|
-
e.
|
|
1206
|
+
e.ot = null;
|
|
1207
|
+
e.ct = 0;
|
|
1194
1208
|
}
|
|
1195
1209
|
// O(1) splice out of parent's chain on individual dispose. Skipped during
|
|
1196
1210
|
// batch dispose (parent already disposed) and zombie disposal (node sits on
|
|
1197
1211
|
// parent's _pendingFirstChild). We leave node._nextSibling intact so outer
|
|
1198
1212
|
// walks that already advanced past us still reach later siblings.
|
|
1199
|
-
if (t && !n && !(r & REACTIVE_ZOMBIE) && e.Z !== null && !(e.Z.
|
|
1200
|
-
const t = e.
|
|
1201
|
-
const n = e.
|
|
1202
|
-
if (t !== null) t.
|
|
1203
|
-
if (n !== null) n.
|
|
1204
|
-
e.
|
|
1213
|
+
if (t && !n && !(r & REACTIVE_ZOMBIE) && e.Z !== null && !(e.Z.Le & REACTIVE_DISPOSED)) {
|
|
1214
|
+
const t = e.ft;
|
|
1215
|
+
const n = e.st;
|
|
1216
|
+
if (t !== null) t.st = n; else e.Z.ot = n;
|
|
1217
|
+
if (n !== null) n.ft = t;
|
|
1218
|
+
e.ft = null;
|
|
1205
1219
|
}
|
|
1206
1220
|
runDisposal(e, n);
|
|
1207
1221
|
// Final effect-returned cleanup fires at true disposal, after `_disposal`
|
|
1208
1222
|
// to mirror rerun ordering (compute-phase teardown first, cleanup last).
|
|
1209
|
-
if (t && e.
|
|
1210
|
-
const t = e.
|
|
1211
|
-
e.
|
|
1223
|
+
if (t && e.Et) {
|
|
1224
|
+
const t = e.Et;
|
|
1225
|
+
e.Et = undefined;
|
|
1212
1226
|
t();
|
|
1213
1227
|
}
|
|
1214
1228
|
}
|
|
1215
1229
|
|
|
1216
1230
|
function runDisposal(e, t) {
|
|
1217
|
-
let n = t ? e.
|
|
1231
|
+
let n = t ? e.He : e.St;
|
|
1218
1232
|
if (!n) return;
|
|
1219
1233
|
if (Array.isArray(n)) {
|
|
1220
1234
|
for (let e = 0; e < n.length; e++) {
|
|
@@ -1224,13 +1238,13 @@ function runDisposal(e, t) {
|
|
|
1224
1238
|
} else {
|
|
1225
1239
|
n.call(n);
|
|
1226
1240
|
}
|
|
1227
|
-
t ? e.
|
|
1241
|
+
t ? e.He = null : e.St = null;
|
|
1228
1242
|
}
|
|
1229
1243
|
|
|
1230
1244
|
function childId(e, t) {
|
|
1231
1245
|
let n = e;
|
|
1232
|
-
while (n.
|
|
1233
|
-
if (n.id != null) return formatId(n.id, t ? n.
|
|
1246
|
+
while (n.ke & CONFIG_TRANSPARENT && n.Z) n = n.Z;
|
|
1247
|
+
if (n.id != null) return formatId(n.id, t ? n.ct++ : n.ct);
|
|
1234
1248
|
throw new Error("");
|
|
1235
1249
|
}
|
|
1236
1250
|
|
|
@@ -1311,7 +1325,7 @@ function formatId(e, t) {
|
|
|
1311
1325
|
* checks. `cleanup()` is the unchecked primitive used by internals.
|
|
1312
1326
|
*/ function cleanup(e) {
|
|
1313
1327
|
if (!context) return e;
|
|
1314
|
-
if (!context.
|
|
1328
|
+
if (!context.St) context.St = e; else if (Array.isArray(context.St)) context.St.push(e); else context.St = [ context.St, e ];
|
|
1315
1329
|
return e;
|
|
1316
1330
|
}
|
|
1317
1331
|
|
|
@@ -1331,7 +1345,7 @@ function formatId(e, t) {
|
|
|
1331
1345
|
* }
|
|
1332
1346
|
* ```
|
|
1333
1347
|
*/ function isDisposed(e) {
|
|
1334
|
-
return !!(e.
|
|
1348
|
+
return !!(e.Le & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE));
|
|
1335
1349
|
}
|
|
1336
1350
|
|
|
1337
1351
|
function disposeRootSelf(e = true) {
|
|
@@ -1350,29 +1364,29 @@ function disposeRootSelf(e = true) {
|
|
|
1350
1364
|
const n = e?.transparent ?? false;
|
|
1351
1365
|
const r = {
|
|
1352
1366
|
id: inheritId(e, n, t),
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1367
|
+
ke: n ? CONFIG_TRANSPARENT : 0,
|
|
1368
|
+
Je: true,
|
|
1369
|
+
et: t?.Je ? t.et : t,
|
|
1370
|
+
ot: null,
|
|
1371
|
+
st: null,
|
|
1357
1372
|
ft: null,
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
Ke: null,
|
|
1373
|
+
St: null,
|
|
1374
|
+
Ze: t?.Ze ?? globalQueue,
|
|
1375
|
+
dt: t?.dt || defaultContext,
|
|
1376
|
+
ct: 0,
|
|
1377
|
+
He: null,
|
|
1378
|
+
Me: null,
|
|
1365
1379
|
Z: t,
|
|
1366
1380
|
dispose: disposeRootSelf
|
|
1367
1381
|
};
|
|
1368
1382
|
if (t) {
|
|
1369
|
-
const e = t.
|
|
1383
|
+
const e = t.ot;
|
|
1370
1384
|
if (e === null) {
|
|
1371
|
-
t.
|
|
1385
|
+
t.ot = r;
|
|
1372
1386
|
} else {
|
|
1373
|
-
r.
|
|
1374
|
-
e.
|
|
1375
|
-
t.
|
|
1387
|
+
r.st = e;
|
|
1388
|
+
e.ft = r;
|
|
1389
|
+
t.ot = r;
|
|
1376
1390
|
}
|
|
1377
1391
|
}
|
|
1378
1392
|
return r;
|
|
@@ -1408,60 +1422,66 @@ function disposeRootSelf(e = true) {
|
|
|
1408
1422
|
|
|
1409
1423
|
// https://github.com/stackblitz/alien-signals/blob/v2.0.3/src/system.ts#L100
|
|
1410
1424
|
function unlinkSubs(e) {
|
|
1411
|
-
const t = e.
|
|
1412
|
-
const n = e.
|
|
1413
|
-
const r = e.
|
|
1414
|
-
const i = e.
|
|
1415
|
-
if (r !== null) r.
|
|
1416
|
-
if (i !== null) i.
|
|
1425
|
+
const t = e.qe;
|
|
1426
|
+
const n = e.Ye;
|
|
1427
|
+
const r = e.Ge;
|
|
1428
|
+
const i = e.Tt;
|
|
1429
|
+
if (r !== null) r.Tt = i; else t._t = i;
|
|
1430
|
+
if (i !== null) i.Ge = r; else {
|
|
1417
1431
|
t.k = r;
|
|
1418
1432
|
if (r === null) {
|
|
1419
1433
|
t.W?.();
|
|
1420
1434
|
// No more subscribers; only tear down if CONFIG_AUTO_DISPOSE is set.
|
|
1421
1435
|
const e = t;
|
|
1422
|
-
e.
|
|
1436
|
+
e.Ve && e.ke & CONFIG_AUTO_DISPOSE && !(e.Le & REACTIVE_ZOMBIE) && unobserved(e);
|
|
1423
1437
|
}
|
|
1424
1438
|
}
|
|
1425
1439
|
return n;
|
|
1426
1440
|
}
|
|
1427
1441
|
|
|
1428
1442
|
function trimStaleDeps(e) {
|
|
1429
|
-
const t = e.
|
|
1430
|
-
let n = t !== null ? t.
|
|
1443
|
+
const t = e.ut;
|
|
1444
|
+
let n = t !== null ? t.Ye : e.Ke;
|
|
1431
1445
|
if (n !== null) {
|
|
1432
1446
|
do {
|
|
1433
1447
|
n = unlinkSubs(n);
|
|
1434
1448
|
} while (n !== null);
|
|
1435
|
-
if (t !== null) t.
|
|
1449
|
+
if (t !== null) t.Ye = null; else e.Ke = null;
|
|
1436
1450
|
}
|
|
1437
1451
|
}
|
|
1438
1452
|
|
|
1439
1453
|
function unobserved(e) {
|
|
1440
1454
|
deleteFromHeap(e, queueFor(e));
|
|
1441
|
-
let t = e.
|
|
1455
|
+
let t = e.Ke;
|
|
1442
1456
|
while (t !== null) {
|
|
1443
1457
|
t = unlinkSubs(t);
|
|
1444
1458
|
}
|
|
1445
|
-
e.
|
|
1446
|
-
e.
|
|
1459
|
+
e.Ke = null;
|
|
1460
|
+
e.ut = null;
|
|
1447
1461
|
disposeChildren(e, true);
|
|
1448
1462
|
}
|
|
1449
1463
|
|
|
1450
1464
|
// https://github.com/stackblitz/alien-signals/blob/v2.0.3/src/system.ts#L52
|
|
1451
1465
|
function link(e, t, n = false) {
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1466
|
+
// Repeat touches within one pass AND-combine `_pendingObserver`: a probe
|
|
1467
|
+
// read (`isPending(() => x())`) beside a value read of the same dep must
|
|
1468
|
+
// not relabel the value dependency as probe-only — the value read is what
|
|
1469
|
+
// real-error propagation and affects() coverage key off, regardless of
|
|
1470
|
+
// read order within the computation.
|
|
1471
|
+
const r = t.ut;
|
|
1472
|
+
if (r !== null && r.qe === e) {
|
|
1473
|
+
r.Ot &&= n;
|
|
1455
1474
|
return;
|
|
1456
1475
|
}
|
|
1457
1476
|
let i = null;
|
|
1458
|
-
const o = t.
|
|
1477
|
+
const o = t.Le & REACTIVE_RECOMPUTING_DEPS;
|
|
1459
1478
|
if (o) {
|
|
1460
|
-
i = r !== null ? r.
|
|
1461
|
-
if (i !== null && i.
|
|
1462
|
-
i.
|
|
1463
|
-
t.
|
|
1464
|
-
|
|
1479
|
+
i = r !== null ? r.Ye : t.Ke;
|
|
1480
|
+
if (i !== null && i.qe === e) {
|
|
1481
|
+
i.Rt = t.It;
|
|
1482
|
+
t.ut = i;
|
|
1483
|
+
// First touch of this pass: the previous pass's label is stale.
|
|
1484
|
+
i.Ot = n;
|
|
1465
1485
|
return;
|
|
1466
1486
|
}
|
|
1467
1487
|
}
|
|
@@ -1470,22 +1490,24 @@ function link(e, t, n = false) {
|
|
|
1470
1490
|
// [deps.._depsTail] prefix — the O(1) equivalent of scanning the dep list
|
|
1471
1491
|
// (the old alien-signals `isValidLink` walk, O(n²) when a computation
|
|
1472
1492
|
// re-reads earlier deps non-consecutively, e.g. store leaf reads).
|
|
1473
|
-
const s = e.
|
|
1474
|
-
if (s !== null && s.
|
|
1475
|
-
|
|
1493
|
+
const s = e._t;
|
|
1494
|
+
if (s !== null && s.Ue === t && (!o || s.Rt === t.It)) {
|
|
1495
|
+
// Gen-matched during a recompute = repeat touch this pass (AND); outside
|
|
1496
|
+
// a recompute there is no pass boundary, so the latest read labels it.
|
|
1497
|
+
if (o) s.Ot &&= n; else s.Ot = n;
|
|
1476
1498
|
return;
|
|
1477
1499
|
}
|
|
1478
|
-
const u = t.
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1500
|
+
const u = t.ut = e._t = {
|
|
1501
|
+
qe: e,
|
|
1502
|
+
Ue: t,
|
|
1503
|
+
Ye: i,
|
|
1504
|
+
Tt: s,
|
|
1505
|
+
Ge: null,
|
|
1506
|
+
Rt: t.It,
|
|
1507
|
+
Ot: n
|
|
1486
1508
|
};
|
|
1487
|
-
if (r !== null) r.
|
|
1488
|
-
if (s !== null) s.
|
|
1509
|
+
if (r !== null) r.Ye = u; else t.Ke = u;
|
|
1510
|
+
if (s !== null) s.Ge = u; else e.k = u;
|
|
1489
1511
|
}
|
|
1490
1512
|
|
|
1491
1513
|
// The lazily-created Set is the ONE container for pending sources. Its
|
|
@@ -1494,79 +1516,75 @@ function link(e, t, n = false) {
|
|
|
1494
1516
|
// overlapping source landed beside the Set and removePendingSource refused
|
|
1495
1517
|
// to clear it, stranding the Set members' pending forever (#2893).
|
|
1496
1518
|
function addPendingSource(e, t) {
|
|
1497
|
-
if (e
|
|
1498
|
-
(e
|
|
1519
|
+
if (e.$e?.has(t)) return false;
|
|
1520
|
+
(e.$e ??= new Set).add(t);
|
|
1499
1521
|
return true;
|
|
1500
1522
|
}
|
|
1501
1523
|
|
|
1502
1524
|
function removePendingSource(e, t) {
|
|
1503
|
-
if (!e
|
|
1504
|
-
if (e.
|
|
1525
|
+
if (!e.$e?.delete(t)) return false;
|
|
1526
|
+
if (e.$e.size === 0) e.$e = undefined;
|
|
1505
1527
|
return true;
|
|
1506
1528
|
}
|
|
1507
1529
|
|
|
1508
1530
|
function clearPendingSources(e) {
|
|
1509
|
-
e
|
|
1510
|
-
e
|
|
1531
|
+
e.$e?.clear();
|
|
1532
|
+
e.$e = undefined;
|
|
1511
1533
|
}
|
|
1512
1534
|
|
|
1513
1535
|
function setPendingError(e, t, n) {
|
|
1514
1536
|
if (!t) {
|
|
1515
|
-
e.
|
|
1537
|
+
e.me = null;
|
|
1516
1538
|
return;
|
|
1517
1539
|
}
|
|
1518
1540
|
if (n instanceof NotReadyError && n.source === t) {
|
|
1519
|
-
e.
|
|
1541
|
+
e.me = n;
|
|
1520
1542
|
return;
|
|
1521
1543
|
}
|
|
1522
|
-
const r = e.
|
|
1544
|
+
const r = e.me;
|
|
1523
1545
|
if (!(r instanceof NotReadyError) || r.source !== t) {
|
|
1524
|
-
e.
|
|
1546
|
+
e.me = new NotReadyError(t);
|
|
1525
1547
|
}
|
|
1526
1548
|
}
|
|
1527
1549
|
|
|
1528
1550
|
function forEachDependent(e, t) {
|
|
1529
|
-
for (let n = e.k; n !== null; n = n.
|
|
1551
|
+
for (let n = e.k; n !== null; n = n.Ge) t(n.Ue, n);
|
|
1530
1552
|
// `?? null`: affects() marks route plain signals (no `_child` slot) through here.
|
|
1531
|
-
for (let n = e.
|
|
1532
|
-
for (let e = n.k; e !== null; e = e.
|
|
1553
|
+
for (let n = e.rt ?? null; n !== null; n = n.it) {
|
|
1554
|
+
for (let e = n.k; e !== null; e = e.Ge) t(e.Ue, e);
|
|
1533
1555
|
}
|
|
1534
1556
|
}
|
|
1535
1557
|
|
|
1536
1558
|
// Queue a node to re-run on the next flush (used both when a pending source
|
|
1537
1559
|
// settles and when an `isPending` observer must re-evaluate after a real error):
|
|
1538
1560
|
// shared scheduling helper in heap.ts (tracked effects bypass the heap).
|
|
1539
|
-
function settlePendingSource(e
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
//
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
if (
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
const n = e.Be?.values().next().value;
|
|
1553
|
-
if (n) {
|
|
1554
|
-
setPendingError(e, n);
|
|
1555
|
-
o !== null && o(e);
|
|
1561
|
+
function settlePendingSource(e) {
|
|
1562
|
+
let t = false;
|
|
1563
|
+
const n = new Set;
|
|
1564
|
+
// Companion updates no-op without the verdict layer (null hook).
|
|
1565
|
+
const r = GlobalQueue.ce;
|
|
1566
|
+
const settle = i => {
|
|
1567
|
+
if (n.has(i) || !removePendingSource(i, e)) return;
|
|
1568
|
+
n.add(i);
|
|
1569
|
+
i.M = clock;
|
|
1570
|
+
const o = i.$e?.values().next().value;
|
|
1571
|
+
if (o) {
|
|
1572
|
+
setPendingError(i, o);
|
|
1573
|
+
r !== null && r(i);
|
|
1556
1574
|
} else {
|
|
1557
|
-
|
|
1558
|
-
setPendingError(
|
|
1559
|
-
|
|
1560
|
-
if (
|
|
1561
|
-
enqueueSub(
|
|
1562
|
-
|
|
1575
|
+
i.We &= ~STATUS_PENDING;
|
|
1576
|
+
setPendingError(i);
|
|
1577
|
+
r !== null && r(i);
|
|
1578
|
+
if (i.At) {
|
|
1579
|
+
enqueueSub(i);
|
|
1580
|
+
t = true;
|
|
1563
1581
|
}
|
|
1564
|
-
|
|
1582
|
+
i.At = false;
|
|
1565
1583
|
}
|
|
1566
|
-
forEachDependent(
|
|
1584
|
+
forEachDependent(i, settle);
|
|
1567
1585
|
};
|
|
1568
1586
|
forEachDependent(e, settle);
|
|
1569
|
-
if (
|
|
1587
|
+
if (t) schedule();
|
|
1570
1588
|
}
|
|
1571
1589
|
|
|
1572
1590
|
// Object-thenable detection (Promises/A+ shape).
|
|
@@ -1584,26 +1602,26 @@ function handleAsync(e, t, n) {
|
|
|
1584
1602
|
});
|
|
1585
1603
|
}
|
|
1586
1604
|
if (!i && !r) {
|
|
1587
|
-
e.
|
|
1605
|
+
e.lt = null;
|
|
1588
1606
|
return t;
|
|
1589
1607
|
}
|
|
1590
|
-
e.
|
|
1608
|
+
e.lt = t;
|
|
1591
1609
|
let o;
|
|
1592
1610
|
const handleError = n => {
|
|
1593
|
-
if (e.
|
|
1611
|
+
if (e.lt !== t) return;
|
|
1594
1612
|
globalQueue.initTransition(resolveTransition(e));
|
|
1595
1613
|
// NotReadyError from rejected promises should be treated as pending, not error
|
|
1596
1614
|
notifyStatus(e, n instanceof NotReadyError ? STATUS_PENDING : STATUS_ERROR, n);
|
|
1597
1615
|
e.M = clock;
|
|
1598
1616
|
};
|
|
1599
1617
|
const asyncWrite = (r, i) => {
|
|
1600
|
-
if (e.
|
|
1618
|
+
if (e.lt !== t) return;
|
|
1601
1619
|
// If the node was dirtied by a newer write (optimistic override or regular),
|
|
1602
1620
|
// skip this stale async result — the upcoming flush will recompute the node
|
|
1603
1621
|
// with the new value, creating a fresh Promise that supersedes this one.
|
|
1604
|
-
if (e.
|
|
1622
|
+
if (e.Le & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
|
|
1605
1623
|
globalQueue.initTransition(resolveTransition(e));
|
|
1606
|
-
const o = !!(e
|
|
1624
|
+
const o = !!(e.We & STATUS_UNINITIALIZED);
|
|
1607
1625
|
trimStaleDeps(e);
|
|
1608
1626
|
clearStatus(e);
|
|
1609
1627
|
const s = resolveLane(e);
|
|
@@ -1630,22 +1648,22 @@ function handleAsync(e, t, n) {
|
|
|
1630
1648
|
// only notified when the hold is visible to them: under an active
|
|
1631
1649
|
// override every reader sees the override (A17), so waking subs would
|
|
1632
1650
|
// re-show an unchanged view — the revert is the notification point.
|
|
1633
|
-
GlobalQueue.
|
|
1651
|
+
GlobalQueue.le !== null && GlobalQueue.le(e, r);
|
|
1634
1652
|
if (!hasActiveOverride(e)) insertSubs(e);
|
|
1635
1653
|
e.M = clock;
|
|
1636
1654
|
} else if (s) {
|
|
1637
1655
|
// Route through lane's effect queue for independent flushing
|
|
1638
|
-
const t = e.
|
|
1639
|
-
const n = e.
|
|
1640
|
-
const i = e.
|
|
1656
|
+
const t = e.Qe;
|
|
1657
|
+
const n = e.xe;
|
|
1658
|
+
const i = e.ht;
|
|
1641
1659
|
try {
|
|
1642
1660
|
if (!t && o || !i || !i(r, n)) {
|
|
1643
|
-
e.
|
|
1661
|
+
e.xe = r;
|
|
1644
1662
|
e.M = clock;
|
|
1645
1663
|
// The latest() shadow write gives latest() effects independent lanes; the
|
|
1646
1664
|
// _pendingSignal update is a no-op repeat of the clearStatus() call above
|
|
1647
1665
|
// (computePendingState doesn't read _value).
|
|
1648
|
-
GlobalQueue.
|
|
1666
|
+
GlobalQueue.le !== null && GlobalQueue.le(e, r);
|
|
1649
1667
|
insertSubs(e, true);
|
|
1650
1668
|
}
|
|
1651
1669
|
} catch (t) {
|
|
@@ -1714,7 +1732,7 @@ function handleAsync(e, t, n) {
|
|
|
1714
1732
|
u = n;
|
|
1715
1733
|
c = true;
|
|
1716
1734
|
if (n.done) i = true;
|
|
1717
|
-
} else if (e.
|
|
1735
|
+
} else if (e.lt !== t) {
|
|
1718
1736
|
return;
|
|
1719
1737
|
} else if (!n.done) {
|
|
1720
1738
|
r = true;
|
|
@@ -1733,7 +1751,7 @@ function handleAsync(e, t, n) {
|
|
|
1733
1751
|
if (f) {
|
|
1734
1752
|
l = n;
|
|
1735
1753
|
a = true;
|
|
1736
|
-
} else if (e.
|
|
1754
|
+
} else if (e.lt === t) {
|
|
1737
1755
|
i = true;
|
|
1738
1756
|
handleError(n);
|
|
1739
1757
|
}
|
|
@@ -1765,86 +1783,74 @@ function handleAsync(e, t, n) {
|
|
|
1765
1783
|
}
|
|
1766
1784
|
|
|
1767
1785
|
function clearStatus(e, t = false) {
|
|
1768
|
-
if (e
|
|
1769
|
-
if (e.
|
|
1786
|
+
if (e.$e) clearPendingSources(e);
|
|
1787
|
+
if (e.At) e.At = false;
|
|
1770
1788
|
// The pending window is over; its quiet classification dies with it.
|
|
1771
1789
|
// (Unconditional: _reask is baked into the node literals, so this is a
|
|
1772
1790
|
// plain store to an existing slot — no shape change.)
|
|
1773
|
-
e.
|
|
1774
|
-
e
|
|
1775
|
-
if (e.
|
|
1791
|
+
e.Nt = false;
|
|
1792
|
+
e.We = t ? 0 : e.We & STATUS_UNINITIALIZED;
|
|
1793
|
+
if (e.me) setPendingError(e);
|
|
1776
1794
|
// Update pending signal for isPending() reactivity (companions only exist
|
|
1777
1795
|
// once the verdict layer created them, which installs the hooks).
|
|
1778
|
-
if (e._ || e.O) GlobalQueue.
|
|
1779
|
-
if (e.
|
|
1780
|
-
if (e.
|
|
1796
|
+
if (e._ || e.O) GlobalQueue.ce(e);
|
|
1797
|
+
if (e.rt && GlobalQueue.ae !== null) GlobalQueue.ae(e);
|
|
1798
|
+
if (e.yt) e.yt();
|
|
1781
1799
|
}
|
|
1782
1800
|
|
|
1783
1801
|
function notifyStatus(e, t, n, r, i) {
|
|
1784
1802
|
// Wrap regular errors to track source node
|
|
1785
1803
|
if (t === STATUS_ERROR && !(n instanceof StatusError) && !(n instanceof NotReadyError)) n = new StatusError(e, n);
|
|
1786
1804
|
const o = t === STATUS_PENDING && n instanceof NotReadyError ? n.source : undefined;
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
// freeze unrelated writes that share a downstream memo until the action
|
|
1791
|
-
// settles. Real async keeps queuing (its commits ride the transition).
|
|
1792
|
-
const s = o?.Dt !== undefined;
|
|
1793
|
-
// A real error is a settled verdict a mark must not erase (#2893): landing
|
|
1794
|
-
// STATUS_PENDING here would clobber `_error` with the sentinel's
|
|
1795
|
-
// NotReadyError, and unlike real async there is no arriving value whose
|
|
1796
|
-
// recompute would surface the error again. Descent stops too — everything
|
|
1797
|
-
// downstream holds the propagated error for the same reason.
|
|
1798
|
-
if (s && e.$e & STATUS_ERROR) return;
|
|
1799
|
-
const u = o === e;
|
|
1800
|
-
const l = t === STATUS_PENDING && e.A !== undefined && !u;
|
|
1801
|
-
const c = l && hasActiveOverride(e);
|
|
1805
|
+
const s = o === e;
|
|
1806
|
+
const u = t === STATUS_PENDING && e.A !== undefined && !s;
|
|
1807
|
+
const l = u && hasActiveOverride(e);
|
|
1802
1808
|
if (!r) {
|
|
1803
1809
|
if (t === STATUS_PENDING && o) {
|
|
1804
1810
|
addPendingSource(e, o);
|
|
1805
|
-
e
|
|
1811
|
+
e.We = STATUS_PENDING | e.We & STATUS_UNINITIALIZED;
|
|
1806
1812
|
// Preserve the current source on this propagation so render-effect notification
|
|
1807
1813
|
// can register every distinct pending source with the transition.
|
|
1808
1814
|
setPendingError(e, o, n);
|
|
1809
1815
|
} else {
|
|
1810
1816
|
clearPendingSources(e);
|
|
1811
|
-
e
|
|
1812
|
-
e.
|
|
1817
|
+
e.We = t | (t !== STATUS_ERROR ? e.We & STATUS_UNINITIALIZED : 0);
|
|
1818
|
+
e.me = n;
|
|
1813
1819
|
}
|
|
1814
|
-
GlobalQueue.
|
|
1815
|
-
if (e.
|
|
1820
|
+
GlobalQueue.ce !== null && GlobalQueue.ce(e);
|
|
1821
|
+
if (e.rt && GlobalQueue.ae !== null) GlobalQueue.ae(e);
|
|
1816
1822
|
}
|
|
1817
1823
|
if (i && !r) {
|
|
1818
1824
|
assignOrMergeLane(e, i);
|
|
1819
1825
|
}
|
|
1820
|
-
const
|
|
1821
|
-
const
|
|
1822
|
-
if (e.
|
|
1826
|
+
const c = r || l;
|
|
1827
|
+
const a = r || u ? undefined : i;
|
|
1828
|
+
if (e.yt) {
|
|
1823
1829
|
if (r && t === STATUS_PENDING) {
|
|
1824
1830
|
return;
|
|
1825
1831
|
}
|
|
1826
|
-
if (
|
|
1827
|
-
e.
|
|
1832
|
+
if (c) {
|
|
1833
|
+
e.yt(t, n);
|
|
1828
1834
|
} else {
|
|
1829
|
-
e.
|
|
1835
|
+
e.yt();
|
|
1830
1836
|
}
|
|
1831
1837
|
return;
|
|
1832
1838
|
}
|
|
1833
1839
|
forEachDependent(e, (e, r) => {
|
|
1834
1840
|
e.M = clock;
|
|
1835
|
-
if (t === STATUS_PENDING && o && !e
|
|
1841
|
+
if (t === STATUS_PENDING && o && !e.$e?.has(o) || t !== STATUS_PENDING && (e.me !== n || e.$e)) {
|
|
1836
1842
|
// A pending-observer link is the subscription an `isPending` read created.
|
|
1837
1843
|
// It exists so the observer re-runs when the source settles, but it must
|
|
1838
1844
|
// not carry a real (non-NotReadyError) error — the synchronous `isPending`
|
|
1839
1845
|
// read swallows those, and the async path must match. Re-run the observer
|
|
1840
1846
|
// so `isPending` re-evaluates (to not-pending) instead of forwarding.
|
|
1841
|
-
if (r.
|
|
1847
|
+
if (r.Ot && t !== STATUS_PENDING && !(n instanceof NotReadyError)) {
|
|
1842
1848
|
enqueueSub(e);
|
|
1843
1849
|
schedule();
|
|
1844
1850
|
return;
|
|
1845
1851
|
}
|
|
1846
|
-
if (!
|
|
1847
|
-
notifyStatus(e, t, n,
|
|
1852
|
+
if (!c && !e.T) queuePendingNode(e);
|
|
1853
|
+
notifyStatus(e, t, n, c, a);
|
|
1848
1854
|
}
|
|
1849
1855
|
});
|
|
1850
1856
|
}
|
|
@@ -1877,24 +1883,13 @@ let context = null;
|
|
|
1877
1883
|
|
|
1878
1884
|
let currentOptimisticLane = null;
|
|
1879
1885
|
|
|
1880
|
-
/**
|
|
1881
|
-
* Marked sources read by the recompute currently on the stack (saved/restored
|
|
1882
|
-
* per recompute, like `context`). A computed that reads a node with a live
|
|
1883
|
-
* `affects()` mark inherits the mark's pendingness — `recompute` applies the
|
|
1884
|
-
* collected sources through `applyAffectsReads` after its commit, because the
|
|
1885
|
-
* `clearStatus` at the top of the commit path wipes whatever sentinel entries
|
|
1886
|
-
* the node held from the registration-time push. This is the pull half of
|
|
1887
|
-
* mark propagation (real async re-establishes itself by re-throwing on read;
|
|
1888
|
-
* marks are value-transparent, so they re-establish here instead).
|
|
1889
|
-
*/ let affectsReads = null;
|
|
1890
|
-
|
|
1891
1886
|
let snapshotCaptureActive = false;
|
|
1892
1887
|
|
|
1893
1888
|
let snapshotSources = null;
|
|
1894
1889
|
|
|
1895
1890
|
function ownerInSnapshotScope(e) {
|
|
1896
1891
|
while (e) {
|
|
1897
|
-
if (e.
|
|
1892
|
+
if (e.Ct) return true;
|
|
1898
1893
|
e = e.Z;
|
|
1899
1894
|
}
|
|
1900
1895
|
return false;
|
|
@@ -1906,41 +1901,41 @@ function setSnapshotCapture(e) {
|
|
|
1906
1901
|
}
|
|
1907
1902
|
|
|
1908
1903
|
function markSnapshotScope(e) {
|
|
1909
|
-
e.
|
|
1904
|
+
e.Ct = true;
|
|
1910
1905
|
}
|
|
1911
1906
|
|
|
1912
1907
|
function releaseSnapshotScope(e) {
|
|
1913
|
-
e.
|
|
1908
|
+
e.Ct = false;
|
|
1914
1909
|
releaseSubtree(e);
|
|
1915
1910
|
schedule();
|
|
1916
1911
|
}
|
|
1917
1912
|
|
|
1918
1913
|
function releaseSubtree(e) {
|
|
1919
|
-
let t = e.
|
|
1914
|
+
let t = e.ot;
|
|
1920
1915
|
while (t) {
|
|
1921
|
-
if (t.
|
|
1922
|
-
t = t.
|
|
1916
|
+
if (t.Ct) {
|
|
1917
|
+
t = t.st;
|
|
1923
1918
|
continue;
|
|
1924
1919
|
}
|
|
1925
|
-
if (t.
|
|
1920
|
+
if (t.Ve) {
|
|
1926
1921
|
const e = t;
|
|
1927
|
-
e.
|
|
1928
|
-
if (e.
|
|
1929
|
-
e.
|
|
1930
|
-
e.
|
|
1931
|
-
if (dirtyQueue.
|
|
1922
|
+
e.ke &= ~CONFIG_IN_SNAPSHOT_SCOPE;
|
|
1923
|
+
if (e.Le & REACTIVE_SNAPSHOT_STALE) {
|
|
1924
|
+
e.Le &= ~REACTIVE_SNAPSHOT_STALE;
|
|
1925
|
+
e.Le |= REACTIVE_DIRTY;
|
|
1926
|
+
if (dirtyQueue.C > e.ze) dirtyQueue.C = e.ze;
|
|
1932
1927
|
insertIntoHeap(e, dirtyQueue);
|
|
1933
1928
|
}
|
|
1934
1929
|
}
|
|
1935
1930
|
releaseSubtree(t);
|
|
1936
|
-
t = t.
|
|
1931
|
+
t = t.st;
|
|
1937
1932
|
}
|
|
1938
1933
|
}
|
|
1939
1934
|
|
|
1940
1935
|
function clearSnapshots() {
|
|
1941
1936
|
if (snapshotSources) {
|
|
1942
1937
|
for (const e of snapshotSources) {
|
|
1943
|
-
delete e.
|
|
1938
|
+
delete e.we;
|
|
1944
1939
|
delete e[STORE_SNAPSHOT_PROPS];
|
|
1945
1940
|
}
|
|
1946
1941
|
snapshotSources = null;
|
|
@@ -1949,105 +1944,109 @@ function clearSnapshots() {
|
|
|
1949
1944
|
}
|
|
1950
1945
|
|
|
1951
1946
|
function recompute(e, t = false) {
|
|
1952
|
-
const n = e.
|
|
1947
|
+
const n = e.Qe;
|
|
1953
1948
|
if (!t) {
|
|
1954
1949
|
if (e.T && (!n || activeTransition) && activeTransition !== e.T) globalQueue.initTransition(e.T);
|
|
1955
1950
|
deleteFromHeap(e, queueFor(e));
|
|
1956
|
-
e.
|
|
1951
|
+
e.lt = null;
|
|
1957
1952
|
// Tracked effects run after finalizePureQueue, so dispose immediately instead of deferring
|
|
1958
|
-
if (e.T || n === EFFECT_TRACKED) disposeChildren(e); else if (e.
|
|
1953
|
+
if (e.T || n === EFFECT_TRACKED) disposeChildren(e); else if (e.ot !== null || e.St !== null) {
|
|
1959
1954
|
markDisposal(e);
|
|
1960
|
-
e.
|
|
1961
|
-
e.
|
|
1962
|
-
e.
|
|
1963
|
-
e.
|
|
1964
|
-
e.
|
|
1955
|
+
e.He = e.St;
|
|
1956
|
+
e.Me = e.ot;
|
|
1957
|
+
e.St = null;
|
|
1958
|
+
e.ot = null;
|
|
1959
|
+
e.ct = 0;
|
|
1965
1960
|
} else ;
|
|
1966
1961
|
}
|
|
1967
|
-
let r = !!(e.
|
|
1962
|
+
let r = !!(e.Le & REACTIVE_OPTIMISTIC_DIRTY);
|
|
1968
1963
|
const i = e.A !== undefined && e.A !== NOT_PENDING;
|
|
1969
|
-
const o = !!(e
|
|
1964
|
+
const o = !!(e.We & STATUS_UNINITIALIZED);
|
|
1970
1965
|
// Re-ask classification lives in the verdict module; capture the flag before
|
|
1971
1966
|
// the recompute wipes _flags below.
|
|
1972
|
-
const s = (e.
|
|
1967
|
+
const s = (e.Le & REACTIVE_REASK) !== 0;
|
|
1973
1968
|
const u = context;
|
|
1974
1969
|
context = e;
|
|
1975
|
-
e.
|
|
1976
|
-
e.
|
|
1977
|
-
e.
|
|
1970
|
+
e.ut = null;
|
|
1971
|
+
e.It++;
|
|
1972
|
+
e.Le = REACTIVE_RECOMPUTING_DEPS;
|
|
1978
1973
|
e.M = clock;
|
|
1979
|
-
let l = e.V === NOT_PENDING ? e.
|
|
1980
|
-
let c = e.
|
|
1974
|
+
let l = e.V === NOT_PENDING ? e.xe : e.V;
|
|
1975
|
+
let c = e.ze;
|
|
1981
1976
|
let a = tracking;
|
|
1982
|
-
|
|
1983
|
-
affectsReads = null;
|
|
1984
|
-
let E = null;
|
|
1985
|
-
let d = currentOptimisticLane;
|
|
1977
|
+
let f = currentOptimisticLane;
|
|
1986
1978
|
tracking = true;
|
|
1979
|
+
// A computed's fn establishes its OWN dependencies, so it must never run
|
|
1980
|
+
// inside a latest() read window: read() short-circuits through the
|
|
1981
|
+
// companion path before dependency linking, so a memo created (eagerly
|
|
1982
|
+
// computed) inside latest(fn) came out permanently dependency-less (#2926).
|
|
1983
|
+
// latestRead() already suspends the flag for its pull-recomputes; this
|
|
1984
|
+
// covers creation-time computes and flushes that run inside the window.
|
|
1985
|
+
const E = latestReadActive;
|
|
1986
|
+
latestReadActive = false;
|
|
1987
1987
|
// Lane posture lives with the engine: OPTIMISTIC_DIRTY is only ever set by
|
|
1988
1988
|
// engine-driven paths, and _optimisticNodes is only pushed by
|
|
1989
1989
|
// _optimisticWrite, so the hook is installed whenever either gate holds.
|
|
1990
1990
|
if (r) {
|
|
1991
|
-
const t = GlobalQueue.
|
|
1991
|
+
const t = GlobalQueue.Pe(e, true);
|
|
1992
1992
|
if (t) currentOptimisticLane = t;
|
|
1993
1993
|
} else if (activeTransition && !t && activeTransition.G.length) {
|
|
1994
1994
|
// Lane adoption: parent-deeper-than-owned-child can run before its OPT-dirty
|
|
1995
1995
|
// child propagates. Walk deps once and inherit the OPT lane so this node
|
|
1996
1996
|
// recomputes under the right posture and propagates correctly.
|
|
1997
|
-
const t = GlobalQueue.
|
|
1997
|
+
const t = GlobalQueue.Pe(e, false);
|
|
1998
1998
|
if (t) {
|
|
1999
1999
|
r = true;
|
|
2000
2000
|
currentOptimisticLane = t;
|
|
2001
2001
|
}
|
|
2002
2002
|
}
|
|
2003
2003
|
const S = n && n !== EFFECT_USER;
|
|
2004
|
-
const
|
|
2004
|
+
const d = stale;
|
|
2005
2005
|
if (S) stale = true;
|
|
2006
2006
|
try {
|
|
2007
|
-
if (!false && e.
|
|
2008
|
-
l = e.
|
|
2009
|
-
e.
|
|
2007
|
+
if (!false && e.ke & CONFIG_SYNC) {
|
|
2008
|
+
l = e.Ve(l);
|
|
2009
|
+
e.lt = null;
|
|
2010
2010
|
} else {
|
|
2011
2011
|
// Snapshot `_inFlight` so we can detect whether `_fn` self-registered an async
|
|
2012
2012
|
// subscription (e.g. `createProjection` calls `handleAsync` from inside its body
|
|
2013
2013
|
// with a setter callback). In that case, the outer `handleAsync` call below would
|
|
2014
2014
|
// clobber the fresh subscription, so we skip it and let the internally-registered
|
|
2015
2015
|
// iteration drive updates.
|
|
2016
|
-
const t = e.
|
|
2017
|
-
const n = e.
|
|
2016
|
+
const t = e.lt;
|
|
2017
|
+
const n = e.Ve(l);
|
|
2018
2018
|
const r = typeof n === "object" && n !== null;
|
|
2019
|
-
const i = e.
|
|
2019
|
+
const i = e.lt !== t;
|
|
2020
2020
|
l = i || !r ? n : handleAsync(e, n);
|
|
2021
|
-
if (!i && !r) e.
|
|
2021
|
+
if (!i && !r) e.lt = null;
|
|
2022
2022
|
}
|
|
2023
2023
|
clearStatus(e, t);
|
|
2024
2024
|
// _optimisticLane is only ever assigned by engine paths.
|
|
2025
|
-
if (e.i) GlobalQueue.
|
|
2025
|
+
if (e.i) GlobalQueue.be(e);
|
|
2026
2026
|
} catch (t) {
|
|
2027
2027
|
// Track pending async in the lane (not the lane's source — it creates the lane
|
|
2028
2028
|
// but doesn't belong to it). Set lane BEFORE notifyStatus for downstream propagation.
|
|
2029
|
-
if (t instanceof NotReadyError && currentOptimisticLane) GlobalQueue.
|
|
2029
|
+
if (t instanceof NotReadyError && currentOptimisticLane) GlobalQueue.ge(e);
|
|
2030
2030
|
let n = false;
|
|
2031
2031
|
if (t instanceof NotReadyError) {
|
|
2032
|
-
e.
|
|
2033
|
-
if (GlobalQueue.
|
|
2032
|
+
e.At = true;
|
|
2033
|
+
if (GlobalQueue.Te !== null) n = GlobalQueue.Te(e, s);
|
|
2034
2034
|
}
|
|
2035
2035
|
notifyStatus(e, t instanceof NotReadyError ? STATUS_PENDING : STATUS_ERROR, t, undefined, t instanceof NotReadyError ? e.i : undefined);
|
|
2036
|
-
if (n) GlobalQueue.
|
|
2036
|
+
if (n) GlobalQueue.pe(e);
|
|
2037
2037
|
} finally {
|
|
2038
2038
|
tracking = a;
|
|
2039
|
-
|
|
2040
|
-
|
|
2039
|
+
latestReadActive = E;
|
|
2040
|
+
if (S) stale = d;
|
|
2041
|
+
e.Le = REACTIVE_NONE | (t ? e.Le & REACTIVE_SNAPSHOT_STALE : 0);
|
|
2041
2042
|
context = u;
|
|
2042
|
-
E = affectsReads;
|
|
2043
|
-
affectsReads = f;
|
|
2044
2043
|
}
|
|
2045
|
-
if (!e.
|
|
2044
|
+
if (!e.me) {
|
|
2046
2045
|
trimStaleDeps(e);
|
|
2047
|
-
const s = i ? unwrapOverride(e.A) : e.V === NOT_PENDING ? e.
|
|
2046
|
+
const s = i ? unwrapOverride(e.A) : e.V === NOT_PENDING ? e.xe : e.V;
|
|
2048
2047
|
let u = false;
|
|
2049
2048
|
try {
|
|
2050
|
-
u = !n && o || !e.
|
|
2049
|
+
u = !n && o || !e.ht || !e.ht(s, l);
|
|
2051
2050
|
} catch (t) {
|
|
2052
2051
|
// A throwing user comparator is an error of this node's computation.
|
|
2053
2052
|
// Route it through the same status path as a compute-phase throw so
|
|
@@ -2061,15 +2060,15 @@ function recompute(e, t = false) {
|
|
|
2061
2060
|
// gate: the explicit recompute(node, true) inside effect() does not enqueue, so
|
|
2062
2061
|
// effect() can call its runner synchronously for the first run.
|
|
2063
2062
|
if (n && u) {
|
|
2064
|
-
e.
|
|
2063
|
+
e.Fe = !e.me;
|
|
2065
2064
|
// Reuse one bound runner per effect — runEffect no-ops on a stale
|
|
2066
2065
|
// `_modified`, so re-enqueueing the same function is harmless.
|
|
2067
|
-
if (!t) e.
|
|
2066
|
+
if (!t) e.Ze.enqueue(n, e.Pt ??= GlobalQueue.ee.bind(null, e));
|
|
2068
2067
|
}
|
|
2069
|
-
if (e.
|
|
2068
|
+
if (e.me) ; else if (u) {
|
|
2070
2069
|
const o = i ? e.A : undefined;
|
|
2071
2070
|
if (t || n && activeTransition !== e.T || r) {
|
|
2072
|
-
e.
|
|
2071
|
+
e.xe = l;
|
|
2073
2072
|
// Lane-propagated correction: upstream data is fresh, correct the
|
|
2074
2073
|
// override unconditionally. The direct _value commit is the lane's
|
|
2075
2074
|
// own reveal schedule; drop any superseded older hold so its queued
|
|
@@ -2085,7 +2084,7 @@ function recompute(e, t = false) {
|
|
|
2085
2084
|
// (#2831). Both companion writes are transition-scoped (optimistic) and
|
|
2086
2085
|
// auto-revert/re-derive at commit. Skipped for plain flushes where the
|
|
2087
2086
|
// pending value commits before effects run.
|
|
2088
|
-
if ((activeTransition || e.T) && GlobalQueue.
|
|
2087
|
+
if ((activeTransition || e.T) && GlobalQueue.le !== null) GlobalQueue.le(e, l);
|
|
2089
2088
|
}
|
|
2090
2089
|
if (!i || r || e.A !== o) insertSubs(e, r || i);
|
|
2091
2090
|
} else if (i) {
|
|
@@ -2094,90 +2093,77 @@ function recompute(e, t = false) {
|
|
|
2094
2093
|
// for commit on its own transition's schedule — invisibly (A17/A18).
|
|
2095
2094
|
if (e.V === NOT_PENDING) queuePendingNode(e);
|
|
2096
2095
|
e.V = l;
|
|
2097
|
-
} else if (e.
|
|
2098
|
-
for (let t = e.k; t !== null; t = t.
|
|
2099
|
-
insertIntoHeapHeight(t.
|
|
2096
|
+
} else if (e.ze != c) {
|
|
2097
|
+
for (let t = e.k; t !== null; t = t.Ge) {
|
|
2098
|
+
insertIntoHeapHeight(t.Ue, queueFor(t.Ue));
|
|
2100
2099
|
}
|
|
2101
2100
|
}
|
|
2102
2101
|
}
|
|
2103
|
-
currentOptimisticLane =
|
|
2104
|
-
|
|
2105
|
-
// earlier, the sentinel's NotReadyError in `_error` would have routed the
|
|
2106
|
-
// fresh value into the error-skip branch instead of committing it. A real
|
|
2107
|
-
// (non-NotReady) error wins over marks (#2893): applying the sentinel here
|
|
2108
|
-
// would clobber the user's error with a NotReadyError from a node whose
|
|
2109
|
-
// reads value-transparency promises can never throw NotReady.
|
|
2110
|
-
// `markedReads` only collects under a live mark, so the hook is installed.
|
|
2111
|
-
if (E && !(e.$e & STATUS_ERROR)) GlobalQueue.re(e, E);
|
|
2112
|
-
// Mark-only pending doesn't queue (#2893): the node holds no value needing
|
|
2113
|
-
// a transition-scheduled commit, and queueing would stamp the marking
|
|
2114
|
-
// action's transaction onto it — freezing unrelated writes that share it.
|
|
2115
|
-
// (Single mask test up front keeps the mark-free hot path at original cost.)
|
|
2116
|
-
const p = e.$e & (STATUS_PENDING | STATUS_UNINITIALIZED);
|
|
2117
|
-
const _ = e.V !== NOT_PENDING || e.Ke !== null || e.Ye !== null || p !== 0 && (p !== STATUS_PENDING || activeAffectsMarks === 0 || !GlobalQueue.ue(e));
|
|
2102
|
+
currentOptimisticLane = f;
|
|
2103
|
+
const T = e.V !== NOT_PENDING || e.Me !== null || e.He !== null || (e.We & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0;
|
|
2118
2104
|
// Override-covered holds (hasOverride) always queue: their commit belongs
|
|
2119
2105
|
// to their own transition's schedule (A18 re-rule) and is unobservable
|
|
2120
2106
|
// under the override (A17). Revert no longer commits anything, so an
|
|
2121
2107
|
// unqueued covered hold would leak (INV-7) once the revert clears
|
|
2122
2108
|
// _transition.
|
|
2123
|
-
|
|
2109
|
+
T && (!t || e.We & STATUS_PENDING) && (!e.T || i) && queuePendingNode(e);
|
|
2124
2110
|
e.T && n && activeTransition !== e.T && runInTransition(e.T, () => recompute(e));
|
|
2125
2111
|
}
|
|
2126
2112
|
|
|
2127
2113
|
function updateIfNecessary(e) {
|
|
2128
|
-
if (e.
|
|
2129
|
-
for (let t = e.
|
|
2130
|
-
const n = t.
|
|
2131
|
-
const r = n.
|
|
2132
|
-
if (r.
|
|
2114
|
+
if (e.Le & REACTIVE_CHECK) {
|
|
2115
|
+
for (let t = e.Ke; t; t = t.Ye) {
|
|
2116
|
+
const n = t.qe;
|
|
2117
|
+
const r = n.Be || n;
|
|
2118
|
+
if (r.Ve) {
|
|
2133
2119
|
updateIfNecessary(r);
|
|
2134
2120
|
}
|
|
2135
|
-
if (e.
|
|
2121
|
+
if (e.Le & REACTIVE_DIRTY) {
|
|
2136
2122
|
break;
|
|
2137
2123
|
}
|
|
2138
2124
|
}
|
|
2139
2125
|
}
|
|
2140
|
-
if (e.
|
|
2126
|
+
if (e.Le & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY) || e.me && e.M < clock && !e.lt) {
|
|
2141
2127
|
recompute(e);
|
|
2142
2128
|
}
|
|
2143
|
-
e.
|
|
2129
|
+
e.Le = e.Le & (REACTIVE_SNAPSHOT_STALE | REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT);
|
|
2144
2130
|
}
|
|
2145
2131
|
|
|
2146
2132
|
function computed(e, t) {
|
|
2147
2133
|
const n = t?.transparent ?? false;
|
|
2148
2134
|
const r = {
|
|
2149
2135
|
id: inheritId(t, n, context),
|
|
2150
|
-
|
|
2151
|
-
|
|
2136
|
+
ke: (n ? CONFIG_TRANSPARENT : 0) | (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (!context || t?.lazy ? CONFIG_AUTO_DISPOSE : 0) | (t?.sync ? CONFIG_SYNC : 0) | (t?.gt ? CONFIG_NO_SNAPSHOT : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
2137
|
+
ht: t?.equals != null ? t.equals : isEqual,
|
|
2152
2138
|
W: t?.unobserved,
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2139
|
+
St: null,
|
|
2140
|
+
Ze: context?.Ze ?? globalQueue,
|
|
2141
|
+
dt: context?.dt ?? defaultContext,
|
|
2142
|
+
ct: 0,
|
|
2143
|
+
Ve: e,
|
|
2144
|
+
xe: undefined,
|
|
2145
|
+
ze: 0,
|
|
2146
|
+
rt: null,
|
|
2147
|
+
nt: undefined,
|
|
2148
|
+
tt: null,
|
|
2149
|
+
Ke: null,
|
|
2160
2150
|
ut: null,
|
|
2161
|
-
|
|
2162
|
-
ot: null,
|
|
2163
|
-
Ze: null,
|
|
2164
|
-
Et: null,
|
|
2165
|
-
yt: 0,
|
|
2151
|
+
It: 0,
|
|
2166
2152
|
k: null,
|
|
2167
|
-
|
|
2153
|
+
_t: null,
|
|
2168
2154
|
Z: context,
|
|
2155
|
+
st: null,
|
|
2169
2156
|
ft: null,
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
$e: STATUS_UNINITIALIZED,
|
|
2157
|
+
ot: null,
|
|
2158
|
+
Le: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
|
|
2159
|
+
We: STATUS_UNINITIALIZED,
|
|
2174
2160
|
M: clock,
|
|
2175
2161
|
V: NOT_PENDING,
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2162
|
+
He: null,
|
|
2163
|
+
Me: null,
|
|
2164
|
+
lt: null,
|
|
2179
2165
|
T: null,
|
|
2180
|
-
|
|
2166
|
+
Nt: false
|
|
2181
2167
|
};
|
|
2182
2168
|
setupComputedNode(r, t);
|
|
2183
2169
|
return r;
|
|
@@ -2192,44 +2178,44 @@ function computed(e, t) {
|
|
|
2192
2178
|
const s = o?.transparent ?? false;
|
|
2193
2179
|
const u = {
|
|
2194
2180
|
id: inheritId(o, s, context),
|
|
2195
|
-
|
|
2196
|
-
|
|
2181
|
+
ke: (s ? CONFIG_TRANSPARENT : 0) | (o?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (o?.sync ? CONFIG_SYNC : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
2182
|
+
ht: false,
|
|
2197
2183
|
W: o?.unobserved,
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2184
|
+
St: null,
|
|
2185
|
+
Ze: context?.Ze ?? globalQueue,
|
|
2186
|
+
dt: context?.dt ?? defaultContext,
|
|
2187
|
+
ct: 0,
|
|
2188
|
+
Ve: e,
|
|
2189
|
+
xe: undefined,
|
|
2190
|
+
ze: 0,
|
|
2191
|
+
rt: null,
|
|
2192
|
+
nt: undefined,
|
|
2193
|
+
tt: null,
|
|
2194
|
+
Ke: null,
|
|
2205
2195
|
ut: null,
|
|
2206
|
-
|
|
2207
|
-
ot: null,
|
|
2208
|
-
Ze: null,
|
|
2209
|
-
Et: null,
|
|
2210
|
-
yt: 0,
|
|
2196
|
+
It: 0,
|
|
2211
2197
|
k: null,
|
|
2212
|
-
|
|
2198
|
+
_t: null,
|
|
2213
2199
|
Z: context,
|
|
2200
|
+
st: null,
|
|
2214
2201
|
ft: null,
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
$e: STATUS_UNINITIALIZED,
|
|
2202
|
+
ot: null,
|
|
2203
|
+
Le: REACTIVE_LAZY,
|
|
2204
|
+
We: STATUS_UNINITIALIZED,
|
|
2219
2205
|
M: clock,
|
|
2220
2206
|
V: NOT_PENDING,
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2207
|
+
He: null,
|
|
2208
|
+
Me: null,
|
|
2209
|
+
lt: null,
|
|
2224
2210
|
T: null,
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2211
|
+
Nt: false,
|
|
2212
|
+
Fe: false,
|
|
2213
|
+
bt: undefined,
|
|
2214
|
+
Dt: t,
|
|
2215
|
+
vt: n,
|
|
2216
|
+
Et: undefined,
|
|
2217
|
+
Qe: r,
|
|
2218
|
+
yt: i
|
|
2233
2219
|
};
|
|
2234
2220
|
setupComputedNode(u, lazyOptions);
|
|
2235
2221
|
return u;
|
|
@@ -2240,24 +2226,24 @@ const lazyOptions = {
|
|
|
2240
2226
|
};
|
|
2241
2227
|
|
|
2242
2228
|
function setupComputedNode(e, t) {
|
|
2243
|
-
e.
|
|
2244
|
-
const n = context?.
|
|
2229
|
+
e.tt = e;
|
|
2230
|
+
const n = context?.Je ? context.et : context;
|
|
2245
2231
|
if (context) {
|
|
2246
|
-
const t = context.
|
|
2232
|
+
const t = context.ot;
|
|
2247
2233
|
if (t === null) {
|
|
2248
|
-
context.
|
|
2234
|
+
context.ot = e;
|
|
2249
2235
|
} else {
|
|
2250
|
-
e.
|
|
2251
|
-
t.
|
|
2252
|
-
context.
|
|
2236
|
+
e.st = t;
|
|
2237
|
+
t.ft = e;
|
|
2238
|
+
context.ot = e;
|
|
2253
2239
|
}
|
|
2254
2240
|
}
|
|
2255
|
-
if (n) e.
|
|
2256
|
-
if (GlobalQueue.
|
|
2241
|
+
if (n) e.ze = n.ze + 1;
|
|
2242
|
+
if (GlobalQueue.se !== null) GlobalQueue.se(e);
|
|
2257
2243
|
!t?.lazy && recompute(e, true);
|
|
2258
2244
|
if (snapshotCaptureActive && !t?.lazy) {
|
|
2259
|
-
if (!(e
|
|
2260
|
-
e.
|
|
2245
|
+
if (!(e.We & STATUS_PENDING) && !(e.ke & CONFIG_NO_SNAPSHOT)) {
|
|
2246
|
+
e.we = e.xe === undefined ? NO_SNAPSHOT : e.xe;
|
|
2261
2247
|
snapshotSources.add(e);
|
|
2262
2248
|
}
|
|
2263
2249
|
}
|
|
@@ -2265,20 +2251,20 @@ function setupComputedNode(e, t) {
|
|
|
2265
2251
|
|
|
2266
2252
|
function signal(e, t, n = null) {
|
|
2267
2253
|
const r = {
|
|
2268
|
-
|
|
2269
|
-
|
|
2254
|
+
ht: t?.equals != null ? t.equals : isEqual,
|
|
2255
|
+
ke: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.gt ? CONFIG_NO_SNAPSHOT : 0),
|
|
2270
2256
|
W: t?.unobserved,
|
|
2271
|
-
|
|
2257
|
+
xe: e,
|
|
2272
2258
|
k: null,
|
|
2273
|
-
|
|
2259
|
+
_t: null,
|
|
2274
2260
|
M: clock,
|
|
2275
|
-
|
|
2276
|
-
|
|
2261
|
+
Be: n,
|
|
2262
|
+
it: n?.rt || null,
|
|
2277
2263
|
V: NOT_PENDING
|
|
2278
2264
|
};
|
|
2279
|
-
n && (n.
|
|
2280
|
-
if (snapshotCaptureActive && !(r.
|
|
2281
|
-
r.
|
|
2265
|
+
n && (n.rt = r);
|
|
2266
|
+
if (snapshotCaptureActive && !(r.ke & CONFIG_NO_SNAPSHOT) && !((n?.We ?? 0) & STATUS_PENDING)) {
|
|
2267
|
+
r.we = e === undefined ? NO_SNAPSHOT : e;
|
|
2282
2268
|
snapshotSources.add(r);
|
|
2283
2269
|
}
|
|
2284
2270
|
return r;
|
|
@@ -2322,11 +2308,11 @@ function isEqual(e, t) {
|
|
|
2322
2308
|
* );
|
|
2323
2309
|
* ```
|
|
2324
2310
|
*/ function untrack(e, t) {
|
|
2325
|
-
if (GlobalQueue.
|
|
2311
|
+
if (GlobalQueue.ue === null && !tracking && true) return e();
|
|
2326
2312
|
const n = tracking;
|
|
2327
2313
|
tracking = false;
|
|
2328
2314
|
try {
|
|
2329
|
-
if (GlobalQueue.
|
|
2315
|
+
if (GlobalQueue.ue !== null) return GlobalQueue.ue(e);
|
|
2330
2316
|
return e();
|
|
2331
2317
|
} finally {
|
|
2332
2318
|
tracking = n;
|
|
@@ -2338,10 +2324,10 @@ function isEqual(e, t) {
|
|
|
2338
2324
|
* an isPending() probe (`refresh`) additionally pulls the node fully up to
|
|
2339
2325
|
* date so its status flags reflect the current graph.
|
|
2340
2326
|
*/ function prepareComputed(e, t) {
|
|
2341
|
-
if (e.
|
|
2342
|
-
e.
|
|
2327
|
+
if (e.Le & REACTIVE_LAZY) {
|
|
2328
|
+
e.Le &= ~REACTIVE_LAZY;
|
|
2343
2329
|
recompute(e, true);
|
|
2344
|
-
} else if (e.
|
|
2330
|
+
} else if (e.Le & REACTIVE_DISPOSED) {
|
|
2345
2331
|
recompute(e, true);
|
|
2346
2332
|
} else if (t) {
|
|
2347
2333
|
updateIfNecessary(e);
|
|
@@ -2353,78 +2339,57 @@ function read(e) {
|
|
|
2353
2339
|
// Checked before isPending so that isPending(() => latest(x)) checks
|
|
2354
2340
|
// the _pendingSignal of _latestValueComputed (async in flight) rather
|
|
2355
2341
|
// than the original node (which stays "pending" while held in a transition).
|
|
2356
|
-
if (latestReadActive) return GlobalQueue.
|
|
2342
|
+
if (latestReadActive) return GlobalQueue.Ee(e);
|
|
2357
2343
|
let t = context;
|
|
2358
|
-
if (t?.
|
|
2344
|
+
if (t?.Je) t = t.et;
|
|
2359
2345
|
const n = e;
|
|
2360
|
-
const r = e.
|
|
2346
|
+
const r = e.Be;
|
|
2361
2347
|
const i = r || e;
|
|
2362
2348
|
// Handle isPending() mode: collect pending state while preserving normal read semantics.
|
|
2363
2349
|
// Probe mode is suspended while preparing the node so nested reads during a
|
|
2364
2350
|
// recompute don't collect into the probe.
|
|
2365
2351
|
if (pendingCheckActive) {
|
|
2366
|
-
GlobalQueue.
|
|
2367
|
-
} else if (typeof n.
|
|
2352
|
+
GlobalQueue.Se(e, t, i, r);
|
|
2353
|
+
} else if (typeof n.Ve === "function") {
|
|
2368
2354
|
prepareComputed(e, false);
|
|
2369
2355
|
}
|
|
2370
|
-
if (!n.
|
|
2371
|
-
if (t && tracking)
|
|
2372
|
-
|
|
2373
|
-
// A live mark on a read source flows to the reader (probe reads are
|
|
2374
|
-
// excluded: the probe's own collection owns that verdict, and marking
|
|
2375
|
-
// the enclosing memo would make an `isPending` wrapper itself pending).
|
|
2376
|
-
if (activeAffectsMarks !== 0 && e.F && !pendingCheckActive) (affectsReads ??= []).push(e);
|
|
2377
|
-
}
|
|
2378
|
-
return !t || e.V === NOT_PENDING ? e.Me : e.V;
|
|
2356
|
+
if (!n.Ve && i === e && e.A === undefined && e.we === undefined && activeTransition === null && currentOptimisticLane === null && !snapshotCaptureActive && true) {
|
|
2357
|
+
if (t && tracking) link(e, t);
|
|
2358
|
+
return !t || e.V === NOT_PENDING ? e.xe : e.V;
|
|
2379
2359
|
}
|
|
2380
2360
|
if (t && tracking) {
|
|
2381
2361
|
link(e, t, pendingCheckActive);
|
|
2382
|
-
|
|
2383
|
-
// transitive half (#2893): a mark-pended owner's sentinel sources flow to
|
|
2384
|
-
// the reader like a direct mark — value-transparent reads have no throw
|
|
2385
|
-
// to re-establish through, so without this a mid-window recompute sheds
|
|
2386
|
-
// pendingness permanently past one derivation level.
|
|
2387
|
-
if (activeAffectsMarks !== 0 && !pendingCheckActive) {
|
|
2388
|
-
if (e.F) (affectsReads ??= []).push(e);
|
|
2389
|
-
if (i.$e & STATUS_PENDING) GlobalQueue.le(i, affectsReads ??= []);
|
|
2390
|
-
}
|
|
2391
|
-
if (i.We) {
|
|
2362
|
+
if (i.Ve) {
|
|
2392
2363
|
const n = queueFor(e);
|
|
2393
|
-
if (i.
|
|
2364
|
+
if (i.ze >= n.C) {
|
|
2394
2365
|
markNode(t);
|
|
2395
2366
|
markHeap(n);
|
|
2396
2367
|
updateIfNecessary(i);
|
|
2397
2368
|
}
|
|
2398
|
-
const r = i.
|
|
2369
|
+
const r = i.ze;
|
|
2399
2370
|
// parent check is shallow, might need to be recursive
|
|
2400
|
-
if (r >= t.
|
|
2401
|
-
t.
|
|
2371
|
+
if (r >= t.ze && e.Z !== t) {
|
|
2372
|
+
t.ze = r + 1;
|
|
2402
2373
|
}
|
|
2403
2374
|
}
|
|
2404
2375
|
}
|
|
2405
|
-
|
|
2406
|
-
// a promise of change, not an absence of value, so a derived node whose only
|
|
2407
|
-
// pending sources are mark sentinels keeps its real value readable —
|
|
2408
|
-
// pendingness reaches readers through verdicts (isPending), not throws.
|
|
2409
|
-
// (`activeAffectsMarks` gates the source scan off the mark-free hot path;
|
|
2410
|
-
// sentinels can't survive in pending sources past their last release.)
|
|
2411
|
-
if (i.$e & STATUS_PENDING && !(activeAffectsMarks !== 0 && GlobalQueue.ue(i))) {
|
|
2376
|
+
if (i.We & STATUS_PENDING) {
|
|
2412
2377
|
if (t && !(stale && i.T && activeTransition !== i.T)) {
|
|
2413
2378
|
// Per-lane suspension lives with the engine (a non-null lane implies it
|
|
2414
2379
|
// is installed): under a lane, only same-lane pending async without an
|
|
2415
2380
|
// active override throws.
|
|
2416
|
-
if (currentOptimisticLane === null || GlobalQueue.
|
|
2381
|
+
if (currentOptimisticLane === null || GlobalQueue.ye(i)) {
|
|
2417
2382
|
if (!tracking && e !== t) link(e, t);
|
|
2418
|
-
throw i.
|
|
2383
|
+
throw i.me;
|
|
2419
2384
|
}
|
|
2420
|
-
} else if (t && i !== e && i
|
|
2385
|
+
} else if (t && i !== e && i.We & STATUS_UNINITIALIZED) {
|
|
2421
2386
|
if (!tracking && e !== t) link(e, t);
|
|
2422
|
-
throw i.
|
|
2423
|
-
} else if (!t && i
|
|
2424
|
-
throw i.
|
|
2387
|
+
throw i.me;
|
|
2388
|
+
} else if (!t && i.We & STATUS_UNINITIALIZED) {
|
|
2389
|
+
throw i.me;
|
|
2425
2390
|
}
|
|
2426
2391
|
}
|
|
2427
|
-
if (e.
|
|
2392
|
+
if (e.Ve && e.We & STATUS_ERROR) {
|
|
2428
2393
|
// Only a genuine reactive re-read may retry an errored async source:
|
|
2429
2394
|
// - tracking: owned/tracked scope only (never events / `untrack` / effect side-effect phase)
|
|
2430
2395
|
// - !pendingCheckActive: an `isPending` probe observes the error, never refetches
|
|
@@ -2432,21 +2397,19 @@ function read(e) {
|
|
|
2432
2397
|
if (tracking && !pendingCheckActive && e.M < clock) {
|
|
2433
2398
|
recompute(e);
|
|
2434
2399
|
return read(e);
|
|
2435
|
-
} else throw e.
|
|
2400
|
+
} else throw e.me;
|
|
2436
2401
|
}
|
|
2437
|
-
if (snapshotCaptureActive && t && t.
|
|
2438
|
-
const n = e.
|
|
2402
|
+
if (snapshotCaptureActive && t && t.ke & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
2403
|
+
const n = e.we;
|
|
2439
2404
|
if (n !== undefined) {
|
|
2440
2405
|
const r = n === NO_SNAPSHOT ? undefined : n;
|
|
2441
|
-
const i = e.V !== NOT_PENDING ? e.V : e.
|
|
2442
|
-
if (i !== r) t.
|
|
2406
|
+
const i = e.V !== NOT_PENDING ? e.V : e.xe;
|
|
2407
|
+
if (i !== r) t.Le |= REACTIVE_SNAPSHOT_STALE;
|
|
2443
2408
|
return r;
|
|
2444
2409
|
}
|
|
2445
2410
|
}
|
|
2446
2411
|
if (e.A !== undefined && e.A !== NOT_PENDING) {
|
|
2447
|
-
//
|
|
2448
|
-
// the value for every reader — that check itself stays right here).
|
|
2449
|
-
if (t && stale && GlobalQueue.ge(e)) return e.Me;
|
|
2412
|
+
// A17: the override IS the value for every reader.
|
|
2450
2413
|
return unwrapOverride(e.A);
|
|
2451
2414
|
}
|
|
2452
2415
|
// Entanglement gate: a reader recomputing under an optimistic lane that reads
|
|
@@ -2456,18 +2419,18 @@ function read(e) {
|
|
|
2456
2419
|
// _pendingValue for correct fetching. The sub is recorded for replay at commit
|
|
2457
2420
|
// so it re-runs with the new committed view. (Gate details live with the
|
|
2458
2421
|
// engine — a non-null lane implies it is installed.)
|
|
2459
|
-
if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.
|
|
2460
|
-
return e.
|
|
2422
|
+
if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.Ne(e, i, t)) {
|
|
2423
|
+
return e.xe;
|
|
2461
2424
|
}
|
|
2462
2425
|
// In optimistic lane context, return _value for optimistic/lane-assigned signals
|
|
2463
2426
|
// and for regular signals in stale mode (render effects). Non-stale readers (user
|
|
2464
2427
|
// effects) see _pendingValue so that latest() and direct reads stay consistent.
|
|
2465
2428
|
// (The lane-context clause lives with the engine.)
|
|
2466
|
-
const o = !t || currentOptimisticLane !== null && GlobalQueue.
|
|
2429
|
+
const o = !t || currentOptimisticLane !== null && GlobalQueue.Ce(e, i, t) || e.V === NOT_PENDING || stale && e.T && activeTransition !== e.T ? e.xe : e.V;
|
|
2467
2430
|
// Record that this isPending() probe observed the fresh pending value, so
|
|
2468
2431
|
// the probe doesn't pair "pending" with the new value (#2831).
|
|
2469
|
-
if (pendingCheckActive) GlobalQueue.
|
|
2470
|
-
if (!t && i === e && typeof n.
|
|
2432
|
+
if (pendingCheckActive) GlobalQueue.de(e, o);
|
|
2433
|
+
if (!t && i === e && typeof n.Ve === "function" && e.ke & CONFIG_AUTO_DISPOSE && !(i.We & STATUS_PENDING) && !e.k) {
|
|
2471
2434
|
unobserved(e);
|
|
2472
2435
|
}
|
|
2473
2436
|
return o;
|
|
@@ -2479,16 +2442,16 @@ function setSignal(e, t) {
|
|
|
2479
2442
|
// optimisticComputed callers and optimistic store nodes carry an
|
|
2480
2443
|
// _overrideValue slot, and every module that creates one installs the
|
|
2481
2444
|
// engine first.
|
|
2482
|
-
if (e.A !== undefined && !projectionWriteActive) return GlobalQueue.
|
|
2483
|
-
const n = e.V === NOT_PENDING ? e.
|
|
2445
|
+
if (e.A !== undefined && !projectionWriteActive) return GlobalQueue.Oe(e, t);
|
|
2446
|
+
const n = e.V === NOT_PENDING ? e.xe : e.V;
|
|
2484
2447
|
if (typeof t === "function") t = t(n);
|
|
2485
2448
|
// Uninitialized check first: the first commit has no previous value, so the
|
|
2486
2449
|
// user comparator must not run against `undefined` (matches recompute).
|
|
2487
|
-
const r = !!(e
|
|
2450
|
+
const r = !!(e.We & STATUS_UNINITIALIZED) || !e.ht || !e.ht(n, t);
|
|
2488
2451
|
if (!r) return t;
|
|
2489
2452
|
if (e.V === NOT_PENDING) queuePendingNode(e);
|
|
2490
2453
|
e.V = t;
|
|
2491
|
-
GlobalQueue.
|
|
2454
|
+
GlobalQueue.le !== null && GlobalQueue.le(e, t);
|
|
2492
2455
|
e.M = clock;
|
|
2493
2456
|
insertSubs(e);
|
|
2494
2457
|
schedule();
|
|
@@ -2503,11 +2466,11 @@ function setSignal(e, t) {
|
|
|
2503
2466
|
* cleanup point.
|
|
2504
2467
|
*/ function suppressComputedRecompute(e) {
|
|
2505
2468
|
deleteFromHeap(e, queueFor(e));
|
|
2506
|
-
if (!(e.
|
|
2469
|
+
if (!(e.Le & REACTIVE_MANUAL_WRITE) && e.V === NOT_PENDING) {
|
|
2507
2470
|
queuePendingNode(e);
|
|
2508
2471
|
schedule();
|
|
2509
2472
|
}
|
|
2510
|
-
e.
|
|
2473
|
+
e.Le = e.Le & -4 | REACTIVE_MANUAL_WRITE;
|
|
2511
2474
|
}
|
|
2512
2475
|
|
|
2513
2476
|
/**
|
|
@@ -2586,7 +2549,7 @@ function staleValues(e, t = true) {
|
|
|
2586
2549
|
if (!t) {
|
|
2587
2550
|
return;
|
|
2588
2551
|
}
|
|
2589
|
-
if (typeof t.
|
|
2552
|
+
if (typeof t.Ve === "function" && !(t.Le & (REACTIVE_DISPOSED | REACTIVE_MANUAL_WRITE))) {
|
|
2590
2553
|
// A refresh with no value-change dirt already queued is a re-ask of the
|
|
2591
2554
|
// same question: mark it so the recompute classifies any resulting
|
|
2592
2555
|
// pending window as quiet (not pending). If the node is already dirty
|
|
@@ -2594,11 +2557,11 @@ function staleValues(e, t = true) {
|
|
|
2594
2557
|
// REACTIVE_IN_HEAP counts as dirt: insertSubs schedules subscribers by
|
|
2595
2558
|
// heap insertion alone (no DIRTY/CHECK flag), so a same-batch value
|
|
2596
2559
|
// change followed by refresh() must not be laundered into a quiet re-ask.
|
|
2597
|
-
if (!(t.
|
|
2598
|
-
t.
|
|
2560
|
+
if (!(t.Le & (REACTIVE_DIRTY | REACTIVE_CHECK | REACTIVE_IN_HEAP))) {
|
|
2561
|
+
t.Le |= REACTIVE_REASK;
|
|
2599
2562
|
armReaskClear();
|
|
2600
2563
|
}
|
|
2601
|
-
t.
|
|
2564
|
+
t.Le = t.Le & ~REACTIVE_CHECK | REACTIVE_DIRTY;
|
|
2602
2565
|
insertIntoHeap(t, queueFor(t));
|
|
2603
2566
|
schedule();
|
|
2604
2567
|
}
|
|
@@ -2646,11 +2609,11 @@ function wireExternalSource(e) {
|
|
|
2646
2609
|
equals: false,
|
|
2647
2610
|
ownedWrite: true
|
|
2648
2611
|
});
|
|
2649
|
-
const n = externalSourceConfig.factory(e.
|
|
2612
|
+
const n = externalSourceConfig.factory(e.Ve, () => {
|
|
2650
2613
|
setSignal(t, undefined);
|
|
2651
2614
|
});
|
|
2652
2615
|
cleanup(() => n.dispose());
|
|
2653
|
-
e.
|
|
2616
|
+
e.Ve = e => {
|
|
2654
2617
|
read(t);
|
|
2655
2618
|
return n.track(e);
|
|
2656
2619
|
};
|
|
@@ -2664,8 +2627,8 @@ function externalUntrack(e) {
|
|
|
2664
2627
|
// removed on reset) so core's null checks stay equivalent to the old
|
|
2665
2628
|
// `externalSourceConfig` truthiness checks.
|
|
2666
2629
|
function syncExternalHooks() {
|
|
2667
|
-
GlobalQueue.
|
|
2668
|
-
GlobalQueue.
|
|
2630
|
+
GlobalQueue.se = externalSourceConfig ? wireExternalSource : null;
|
|
2631
|
+
GlobalQueue.ue = externalSourceConfig ? externalUntrack : null;
|
|
2669
2632
|
}
|
|
2670
2633
|
|
|
2671
2634
|
function enableExternalSource(e) {
|
|
@@ -2722,7 +2685,7 @@ function enableExternalSource(e) {
|
|
|
2722
2685
|
if (!t) {
|
|
2723
2686
|
throw new NoOwnerError;
|
|
2724
2687
|
}
|
|
2725
|
-
const n = hasContext(e, t) ? t.
|
|
2688
|
+
const n = hasContext(e, t) ? t.dt[e.id] : e.defaultValue;
|
|
2726
2689
|
if (isUndefined(n)) {
|
|
2727
2690
|
throw new ContextNotFoundError;
|
|
2728
2691
|
}
|
|
@@ -2743,14 +2706,14 @@ function enableExternalSource(e) {
|
|
|
2743
2706
|
}
|
|
2744
2707
|
// We're creating a new object to avoid child context values being exposed to parent owners. If
|
|
2745
2708
|
// we don't do this, everything will be a singleton and all hell will break lose.
|
|
2746
|
-
n.
|
|
2747
|
-
...n.
|
|
2709
|
+
n.dt = {
|
|
2710
|
+
...n.dt,
|
|
2748
2711
|
[e.id]: isUndefined(t) ? e.defaultValue : t
|
|
2749
2712
|
};
|
|
2750
2713
|
}
|
|
2751
2714
|
|
|
2752
2715
|
function hasContext(e, t) {
|
|
2753
|
-
return !isUndefined(t?.
|
|
2716
|
+
return !isUndefined(t?.dt[e.id]);
|
|
2754
2717
|
}
|
|
2755
2718
|
|
|
2756
2719
|
function isUndefined(e) {
|
|
@@ -2770,15 +2733,11 @@ function isUndefined(e) {
|
|
|
2770
2733
|
* `_optimisticNodes.length`, `activeLanes.size`), so `!` invocations are safe
|
|
2771
2734
|
* once the gate holds — the same late-binding contract as verdict.ts.
|
|
2772
2735
|
*/
|
|
2773
|
-
// When a background transition is stashed, plain optimistic signals need one
|
|
2774
|
-
// committed-view rerun. Keep that override local to the stash flush.
|
|
2775
|
-
let stashedOptimisticReads = null;
|
|
2776
|
-
|
|
2777
2736
|
/** The optimistic half of setSignal, fired when `_overrideValue !== undefined`. */ function optimisticWrite(e, t) {
|
|
2778
2737
|
const n = e.A !== NOT_PENDING;
|
|
2779
|
-
const r = n ? unwrapOverride(e.A) : e.
|
|
2738
|
+
const r = n ? unwrapOverride(e.A) : e.xe;
|
|
2780
2739
|
if (typeof t === "function") t = t(r);
|
|
2781
|
-
const i = !!(e
|
|
2740
|
+
const i = !!(e.We & STATUS_UNINITIALIZED) || !e.ht || !e.ht(r, t);
|
|
2782
2741
|
if (!i) {
|
|
2783
2742
|
// Same-value write with an active override still entangles the current
|
|
2784
2743
|
// action's transition — the hold must outlast all overlapping actions.
|
|
@@ -2803,43 +2762,13 @@ let stashedOptimisticReads = null;
|
|
|
2803
2762
|
// brand, and erasing it makes the write invisible and routes follow-up
|
|
2804
2763
|
// writes off the optimistic path into permanent commits (#2898).
|
|
2805
2764
|
e.A = t === undefined ? OVERRIDE_UNDEFINED : t;
|
|
2806
|
-
GlobalQueue.
|
|
2765
|
+
GlobalQueue.le !== null && GlobalQueue.le(e, t);
|
|
2807
2766
|
e.M = clock;
|
|
2808
2767
|
insertSubs(e, true);
|
|
2809
2768
|
schedule();
|
|
2810
2769
|
return t;
|
|
2811
2770
|
}
|
|
2812
2771
|
|
|
2813
|
-
function readStashed(e) {
|
|
2814
|
-
return !!stashedOptimisticReads?.has(e);
|
|
2815
|
-
}
|
|
2816
|
-
|
|
2817
|
-
function queueStashedOptimisticEffects(e) {
|
|
2818
|
-
for (let t = e.k; t !== null; t = t.Ve) {
|
|
2819
|
-
const e = t.xe;
|
|
2820
|
-
if (!e.He) continue;
|
|
2821
|
-
enqueueSub(e);
|
|
2822
|
-
}
|
|
2823
|
-
}
|
|
2824
|
-
|
|
2825
|
-
/**
|
|
2826
|
-
* Incomplete-transition finalization when the stashed transition holds
|
|
2827
|
-
* optimistic nodes: give plain optimistic signals one committed-view rerun.
|
|
2828
|
-
*/ function stashOptimistic(e) {
|
|
2829
|
-
stashedOptimisticReads = new Set;
|
|
2830
|
-
for (let t = 0; t < e.G.length; t++) {
|
|
2831
|
-
const n = e.G[t];
|
|
2832
|
-
if (n.We || n.Fe & CONFIG_OWNED_WRITE) continue;
|
|
2833
|
-
stashedOptimisticReads.add(n);
|
|
2834
|
-
queueStashedOptimisticEffects(n);
|
|
2835
|
-
}
|
|
2836
|
-
try {
|
|
2837
|
-
finalizePureQueue(null, true);
|
|
2838
|
-
} finally {
|
|
2839
|
-
stashedOptimisticReads = null;
|
|
2840
|
-
}
|
|
2841
|
-
}
|
|
2842
|
-
|
|
2843
2772
|
/**
|
|
2844
2773
|
* transitionComplete's override blockage: a settling transition stays open
|
|
2845
2774
|
* while one of its optimistic nodes holds an active override that is still
|
|
@@ -2847,11 +2776,7 @@ function queueStashedOptimisticEffects(e) {
|
|
|
2847
2776
|
*/ function transitionBlocked(e) {
|
|
2848
2777
|
for (let t = 0; t < e.G.length; t++) {
|
|
2849
2778
|
const n = e.G[t];
|
|
2850
|
-
if (hasActiveOverride(n) && "
|
|
2851
|
-
// Mark-sourced pending never blocks settlement: affects() releases AT
|
|
2852
|
-
// settle, so counting its sentinel here would deadlock the window it
|
|
2853
|
-
// is scoped to.
|
|
2854
|
-
!n.Ue.source?.Dt) {
|
|
2779
|
+
if (hasActiveOverride(n) && "We" in n && n.We & STATUS_PENDING && n.me instanceof NotReadyError) {
|
|
2855
2780
|
return true;
|
|
2856
2781
|
}
|
|
2857
2782
|
}
|
|
@@ -2869,10 +2794,10 @@ function resolveOptimisticNodes(e) {
|
|
|
2869
2794
|
// Revert is a pure drop: there is no revert target to commit —
|
|
2870
2795
|
// override-covered authoritative values hold in _pendingValue and
|
|
2871
2796
|
// elevate on their OWN transition's schedule (A18 as re-ruled 2026-07-07).
|
|
2872
|
-
if (!(t
|
|
2797
|
+
if (!(t.We & STATUS_PENDING)) t.We &= ~STATUS_UNINITIALIZED;
|
|
2873
2798
|
const r = t.A;
|
|
2874
2799
|
t.A = NOT_PENDING;
|
|
2875
|
-
if (r !== NOT_PENDING && t.
|
|
2800
|
+
if (r !== NOT_PENDING && t.xe !== unwrapOverride(r)) insertSubs(t, true);
|
|
2876
2801
|
t.T = null;
|
|
2877
2802
|
t.R = null;
|
|
2878
2803
|
}
|
|
@@ -2881,9 +2806,9 @@ function resolveOptimisticNodes(e) {
|
|
|
2881
2806
|
// transition that produced them (A19 — pending is a property of the data).
|
|
2882
2807
|
for (let n = 0; n < t; n++) {
|
|
2883
2808
|
const t = e[n];
|
|
2884
|
-
if (t._ || t.O) GlobalQueue.
|
|
2809
|
+
if (t._ || t.O) GlobalQueue.fe(t);
|
|
2885
2810
|
const r = t.t;
|
|
2886
|
-
if (r && (r._ === t || r.O === t)) GlobalQueue.
|
|
2811
|
+
if (r && (r._ === t || r.O === t)) GlobalQueue.fe(r);
|
|
2887
2812
|
}
|
|
2888
2813
|
e.splice(0, t);
|
|
2889
2814
|
}
|
|
@@ -2936,7 +2861,7 @@ function cleanupCompletedLanes(e) {
|
|
|
2936
2861
|
* that reads a pending mid-transition write sees the committed value; the sub
|
|
2937
2862
|
* is recorded for replay at commit.
|
|
2938
2863
|
*/ function gatedRead(e, t, n) {
|
|
2939
|
-
if (latestReadActive || e.V === NOT_PENDING || e.
|
|
2864
|
+
if (latestReadActive || e.V === NOT_PENDING || e.Ve || t !== e && !(t.Le & REACTIVE_MANUAL_WRITE)) {
|
|
2940
2865
|
return false;
|
|
2941
2866
|
}
|
|
2942
2867
|
activeTransition.q.add(n);
|
|
@@ -2947,7 +2872,7 @@ function cleanupCompletedLanes(e) {
|
|
|
2947
2872
|
* read()'s value selection under a lane: return the committed `_value` for
|
|
2948
2873
|
* optimistic/lane-assigned signals, stale-mode reads, and pending owners.
|
|
2949
2874
|
*/ function laneReadsCommitted(e, t, n) {
|
|
2950
|
-
return e.A !== undefined || !!e.i || t === e && stale && n.t !== e || !!(t
|
|
2875
|
+
return e.A !== undefined || !!e.i || t === e && stale && n.t !== e || !!(t.We & STATUS_PENDING);
|
|
2951
2876
|
}
|
|
2952
2877
|
|
|
2953
2878
|
/**
|
|
@@ -2956,12 +2881,12 @@ function cleanupCompletedLanes(e) {
|
|
|
2956
2881
|
* can run before its OPT-dirty child propagates).
|
|
2957
2882
|
*/ function recomputeLane(e, t) {
|
|
2958
2883
|
if (t) return resolveLane(e) ?? null;
|
|
2959
|
-
for (let t = e.
|
|
2960
|
-
const n = t.
|
|
2961
|
-
if (n.
|
|
2884
|
+
for (let t = e.Ke; t; t = t.Ye) {
|
|
2885
|
+
const n = t.qe;
|
|
2886
|
+
if (n.Le & REACTIVE_OPTIMISTIC_DIRTY) {
|
|
2962
2887
|
const t = resolveLane(n);
|
|
2963
2888
|
if (t) {
|
|
2964
|
-
e.
|
|
2889
|
+
e.Le |= REACTIVE_OPTIMISTIC_DIRTY;
|
|
2965
2890
|
assignOrMergeLane(e, t);
|
|
2966
2891
|
return t;
|
|
2967
2892
|
}
|
|
@@ -2975,7 +2900,7 @@ function cleanupCompletedLanes(e) {
|
|
|
2975
2900
|
if (t.o !== e) {
|
|
2976
2901
|
t.u.add(e);
|
|
2977
2902
|
e.i = t;
|
|
2978
|
-
GlobalQueue.
|
|
2903
|
+
GlobalQueue.ce !== null && GlobalQueue.ce(t.o);
|
|
2979
2904
|
}
|
|
2980
2905
|
}
|
|
2981
2906
|
|
|
@@ -2983,7 +2908,7 @@ function cleanupCompletedLanes(e) {
|
|
|
2983
2908
|
const t = resolveLane(e);
|
|
2984
2909
|
if (t) {
|
|
2985
2910
|
t.u.delete(e);
|
|
2986
|
-
GlobalQueue.
|
|
2911
|
+
GlobalQueue.ce !== null && GlobalQueue.ce(t.o);
|
|
2987
2912
|
}
|
|
2988
2913
|
}
|
|
2989
2914
|
|
|
@@ -2998,21 +2923,19 @@ function trackOptimisticStore(e) {
|
|
|
2998
2923
|
* create optimistic state (verdict.ts at module top level, createOptimistic
|
|
2999
2924
|
* and createOptimisticStore at first call) BEFORE any optimistic node exists.
|
|
3000
2925
|
*/ function installOptimisticEngine() {
|
|
3001
|
-
if (GlobalQueue.
|
|
3002
|
-
GlobalQueue.
|
|
3003
|
-
GlobalQueue.
|
|
3004
|
-
GlobalQueue.
|
|
3005
|
-
GlobalQueue.
|
|
3006
|
-
GlobalQueue.
|
|
3007
|
-
GlobalQueue.
|
|
3008
|
-
GlobalQueue.
|
|
3009
|
-
GlobalQueue.
|
|
3010
|
-
GlobalQueue.
|
|
3011
|
-
GlobalQueue.
|
|
3012
|
-
GlobalQueue.
|
|
3013
|
-
GlobalQueue.
|
|
3014
|
-
GlobalQueue.Ge = laneAsyncSettled;
|
|
3015
|
-
GlobalQueue.Le = trackOptimisticStore;
|
|
2926
|
+
if (GlobalQueue.Oe !== null) return;
|
|
2927
|
+
GlobalQueue.Oe = optimisticWrite;
|
|
2928
|
+
GlobalQueue.Re = resolveOptimisticNodes;
|
|
2929
|
+
GlobalQueue.Ie = transitionBlocked;
|
|
2930
|
+
GlobalQueue.Ae = cleanupCompletedLanes;
|
|
2931
|
+
GlobalQueue.he = runLaneEffects;
|
|
2932
|
+
GlobalQueue.Ne = gatedRead;
|
|
2933
|
+
GlobalQueue.ye = laneSuspends;
|
|
2934
|
+
GlobalQueue.Ce = laneReadsCommitted;
|
|
2935
|
+
GlobalQueue.Pe = recomputeLane;
|
|
2936
|
+
GlobalQueue.ge = laneAsyncPending;
|
|
2937
|
+
GlobalQueue.be = laneAsyncSettled;
|
|
2938
|
+
GlobalQueue.De = trackOptimisticStore;
|
|
3016
2939
|
}
|
|
3017
2940
|
|
|
3018
2941
|
/**
|
|
@@ -3045,7 +2968,7 @@ let pendingProbe = null;
|
|
|
3045
2968
|
function collectPendingSources(e) {
|
|
3046
2969
|
if (!pendingProbe) return;
|
|
3047
2970
|
pendingProbe.sources.add(e);
|
|
3048
|
-
const t = e.
|
|
2971
|
+
const t = e.Be || e;
|
|
3049
2972
|
if (t !== e) pendingProbe.sources.add(t);
|
|
3050
2973
|
}
|
|
3051
2974
|
|
|
@@ -3059,34 +2982,76 @@ function collectPendingSources(e) {
|
|
|
3059
2982
|
pendingProbe?.sources.add(e);
|
|
3060
2983
|
}
|
|
3061
2984
|
|
|
2985
|
+
/**
|
|
2986
|
+
* The affects() coverage walk — the read half of the dedicated mark channel.
|
|
2987
|
+
* A node is covered by a live mark iff it carries one (`_affectsCount`) or
|
|
2988
|
+
* derives, through its CURRENT deps (hopping store firewalls), from a node
|
|
2989
|
+
* that does. Pull-based coverage means graph rewires, mid-window recomputes,
|
|
2990
|
+
* and probe-triggered recomputes can never strand or strip a mark — there is
|
|
2991
|
+
* nothing stored downstream to corrupt. Probe-created links
|
|
2992
|
+
* (`_pendingObserver`) are skipped so an `isPending` wrapper memo never
|
|
2993
|
+
* inherits the coverage it reports on.
|
|
2994
|
+
*/ function markWalk(e, t) {
|
|
2995
|
+
if (e.F) return true;
|
|
2996
|
+
// A real error outranks an inherited mark (A16/A24c): an errored node
|
|
2997
|
+
// answers probes with its error, not a coverage verdict, and coverage does
|
|
2998
|
+
// not flow through it — matching the rails' behavior, where propagation
|
|
2999
|
+
// stopped at errored nodes. A DIRECT mark on an errored node still reads
|
|
3000
|
+
// pending (the count check above), also matching.
|
|
3001
|
+
if (e.We & STATUS_ERROR) return false;
|
|
3002
|
+
if (t.has(e)) return false;
|
|
3003
|
+
t.add(e);
|
|
3004
|
+
const n = e.Be;
|
|
3005
|
+
if (n && markWalk(n, t)) return true;
|
|
3006
|
+
// Mid-recompute (the clearStatus companion poke runs before
|
|
3007
|
+
// trimStaleDeps), only the validated prefix [_deps.._depsTail] is this
|
|
3008
|
+
// pass's dependency set — walking past it would read dropped deps and
|
|
3009
|
+
// latch a stale verdict on the companion.
|
|
3010
|
+
const r = e;
|
|
3011
|
+
const i = r.Le & REACTIVE_RECOMPUTING_DEPS ? r.ut : undefined;
|
|
3012
|
+
if (i !== null) {
|
|
3013
|
+
for (let e = r.Ke ?? null; e !== null; e = e.Ye) {
|
|
3014
|
+
if (!e.Ot && markWalk(e.qe, t)) return true;
|
|
3015
|
+
if (e === i) break;
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
return false;
|
|
3019
|
+
}
|
|
3020
|
+
|
|
3021
|
+
/** Gated entry: apps with no live mark pay one integer compare. */ function markCovered(e) {
|
|
3022
|
+
return activeAffectsMarks !== 0 && markWalk(e, new Set);
|
|
3023
|
+
}
|
|
3024
|
+
|
|
3062
3025
|
function quietPending(e) {
|
|
3063
|
-
if (e
|
|
3064
|
-
for (const t of e
|
|
3026
|
+
if (e.$e) {
|
|
3027
|
+
for (const t of e.$e) if (!t.Nt) return false;
|
|
3065
3028
|
return true;
|
|
3066
3029
|
}
|
|
3067
|
-
return e.
|
|
3030
|
+
return e.Nt;
|
|
3068
3031
|
}
|
|
3069
3032
|
|
|
3070
3033
|
function newQuestionInFlight(e) {
|
|
3071
|
-
return !!(e
|
|
3034
|
+
return !!(e.We & STATUS_PENDING) && !(e.We & STATUS_UNINITIALIZED) && !quietPending(e);
|
|
3072
3035
|
}
|
|
3073
3036
|
|
|
3074
3037
|
function computePendingState(e) {
|
|
3075
3038
|
const t = e;
|
|
3076
|
-
if (t.
|
|
3077
|
-
|
|
3078
|
-
|
|
3039
|
+
if (t.Le & REACTIVE_DISPOSED) return false;
|
|
3040
|
+
// Mark coverage is transitive by dep-graph reachability: a latest() shadow
|
|
3041
|
+
// reaches its owner (and a store leaf its firewall) through its own deps,
|
|
3042
|
+
// so the one walk covers direct marks, derivation, and companion chains.
|
|
3043
|
+
if (markCovered(e)) return true;
|
|
3044
|
+
const n = e.Be;
|
|
3079
3045
|
if (e.t) {
|
|
3080
3046
|
const t = e.t;
|
|
3081
|
-
|
|
3082
|
-
const n = t.Je || t;
|
|
3047
|
+
const n = t.Be || t;
|
|
3083
3048
|
return newQuestionInFlight(n);
|
|
3084
3049
|
}
|
|
3085
3050
|
if (n && e.V !== NOT_PENDING && !hasActiveOverride(e)) {
|
|
3086
|
-
return !!(n.
|
|
3051
|
+
return !!(n.Le & REACTIVE_MANUAL_WRITE) || !n.lt && !(n.We & STATUS_PENDING) || !!(n.We & STATUS_PENDING) && quietPending(n);
|
|
3087
3052
|
}
|
|
3088
|
-
if (e.V !== NOT_PENDING && !(t
|
|
3089
|
-
if (hasActiveOverride(e)) return !e.
|
|
3053
|
+
if (e.V !== NOT_PENDING && !(t.We & STATUS_UNINITIALIZED)) {
|
|
3054
|
+
if (hasActiveOverride(e)) return !e.ht || !e.ht(e.V, unwrapOverride(e.A));
|
|
3090
3055
|
return true;
|
|
3091
3056
|
}
|
|
3092
3057
|
return newQuestionInFlight(t);
|
|
@@ -3105,19 +3070,28 @@ function updatePendingSignal(e) {
|
|
|
3105
3070
|
}
|
|
3106
3071
|
|
|
3107
3072
|
function updateChildCompanions(e) {
|
|
3108
|
-
for (let t = e.
|
|
3073
|
+
for (let t = e.rt; t !== null; t = t.it) {
|
|
3109
3074
|
if (t._ || t.O) updatePendingSignal(t);
|
|
3110
3075
|
}
|
|
3111
3076
|
}
|
|
3112
3077
|
|
|
3113
|
-
|
|
3114
|
-
|
|
3078
|
+
/**
|
|
3079
|
+
* Re-derive every verdict companion downstream of `el` (subs + firewall
|
|
3080
|
+
* children, dedup'd). The affects() channel's poke walk: registration and
|
|
3081
|
+
* re-ask flips use the live write path (companion setSignal — its own lane
|
|
3082
|
+
* lets the wake escape an incomplete transition's effect stash, #2887);
|
|
3083
|
+
* mark release passes `snap` because it runs inside queue finalization,
|
|
3084
|
+
* where companion writes must land committed (a setSignal there would open
|
|
3085
|
+
* a fresh override window that nothing settles).
|
|
3086
|
+
*/ function repollDownstreamVerdicts(e, t = false) {
|
|
3087
|
+
const n = t ? snapCompanionsToState : updatePendingSignal;
|
|
3088
|
+
const r = new Set;
|
|
3115
3089
|
const visit = e => {
|
|
3116
|
-
if (
|
|
3117
|
-
|
|
3118
|
-
if (e._ || e.O)
|
|
3119
|
-
for (let t = e.k; t !== null; t = t.
|
|
3120
|
-
for (let t = e.
|
|
3090
|
+
if (r.has(e)) return;
|
|
3091
|
+
r.add(e);
|
|
3092
|
+
if (e._ || e.O) n(e);
|
|
3093
|
+
for (let t = e.k; t !== null; t = t.Ge) visit(t.Ue);
|
|
3094
|
+
for (let t = e.rt ?? null; t !== null; t = t.it) {
|
|
3121
3095
|
visit(t);
|
|
3122
3096
|
}
|
|
3123
3097
|
};
|
|
@@ -3128,8 +3102,8 @@ function snapCompanionsToState(e) {
|
|
|
3128
3102
|
const t = e._;
|
|
3129
3103
|
if (t && (t.A === undefined || t.A === NOT_PENDING)) {
|
|
3130
3104
|
const n = computePendingState(e);
|
|
3131
|
-
if (t.
|
|
3132
|
-
t.
|
|
3105
|
+
if (t.xe !== n || t.V !== NOT_PENDING) {
|
|
3106
|
+
t.xe = n;
|
|
3133
3107
|
t.V = NOT_PENDING;
|
|
3134
3108
|
t.M = clock;
|
|
3135
3109
|
insertSubs(t);
|
|
@@ -3137,9 +3111,9 @@ function snapCompanionsToState(e) {
|
|
|
3137
3111
|
}
|
|
3138
3112
|
}
|
|
3139
3113
|
const n = e.O;
|
|
3140
|
-
if (n && !(n.
|
|
3141
|
-
if ((n.A === undefined || n.A === NOT_PENDING) && n.V === NOT_PENDING && !Object.is(n.
|
|
3142
|
-
n.
|
|
3114
|
+
if (n && !(n.Le & REACTIVE_DISPOSED)) {
|
|
3115
|
+
if ((n.A === undefined || n.A === NOT_PENDING) && n.V === NOT_PENDING && !Object.is(n.xe, e.xe) && !(n.Le & (REACTIVE_DIRTY | REACTIVE_CHECK))) {
|
|
3116
|
+
n.Le |= REACTIVE_DIRTY;
|
|
3143
3117
|
insertIntoHeap(n, queueFor(n));
|
|
3144
3118
|
insertSubs(n);
|
|
3145
3119
|
schedule();
|
|
@@ -3171,17 +3145,27 @@ function getLatestValueComputed(e) {
|
|
|
3171
3145
|
const t = getLatestValueComputed(e);
|
|
3172
3146
|
const n = latestReadActive;
|
|
3173
3147
|
setLatestReadActive(false);
|
|
3174
|
-
const r = e.A !== undefined && e.A !== NOT_PENDING ? unwrapOverride(e.A) : e.
|
|
3148
|
+
const r = e.A !== undefined && e.A !== NOT_PENDING ? unwrapOverride(e.A) : e.xe;
|
|
3175
3149
|
let i;
|
|
3176
3150
|
try {
|
|
3151
|
+
// An untracked latest() read has no reading context, so read() never
|
|
3152
|
+
// performs its mid-tick pull — a plain write queued between two latest()
|
|
3153
|
+
// calls left a still-subscribed shadow at its previous speculative value
|
|
3154
|
+
// until the flush (#2922). Mirror the tracked-read pull here: mark the
|
|
3155
|
+
// queued staleness through the graph, then bring the shadow up to date.
|
|
3156
|
+
const e = queueFor(t);
|
|
3157
|
+
if (t.ze >= e.C && !(t.Le & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
|
|
3158
|
+
markHeap(e);
|
|
3159
|
+
prepareComputed(t, true);
|
|
3160
|
+
}
|
|
3177
3161
|
i = read(t);
|
|
3178
3162
|
} catch (t) {
|
|
3179
|
-
if (t instanceof NotReadyError && (!context || !(e
|
|
3163
|
+
if (t instanceof NotReadyError && (!context || !(e.We & STATUS_UNINITIALIZED))) return r;
|
|
3180
3164
|
throw t;
|
|
3181
3165
|
} finally {
|
|
3182
3166
|
setLatestReadActive(n);
|
|
3183
3167
|
}
|
|
3184
|
-
if (t
|
|
3168
|
+
if (t.We & STATUS_PENDING) return r;
|
|
3185
3169
|
if (stale && currentOptimisticLane && t.i) {
|
|
3186
3170
|
const e = findLane(t.i);
|
|
3187
3171
|
const n = findLane(currentOptimisticLane);
|
|
@@ -3189,17 +3173,22 @@ function getLatestValueComputed(e) {
|
|
|
3189
3173
|
return r;
|
|
3190
3174
|
}
|
|
3191
3175
|
}
|
|
3176
|
+
// A shadow recomputed by the pull above (not at creation) holds its fresh
|
|
3177
|
+
// speculative value in _pendingValue; a contextless read() only surfaces
|
|
3178
|
+
// _value. Overrides stay authoritative (A17), and stale readers keep the
|
|
3179
|
+
// other transition's committed view, matching read()'s own selection.
|
|
3180
|
+
if (t.V !== NOT_PENDING && !hasActiveOverride(t) && !(stale && t.T && activeTransition !== t.T)) return t.V;
|
|
3192
3181
|
return i;
|
|
3193
3182
|
}
|
|
3194
3183
|
|
|
3195
3184
|
/** The isPending()-probe read path, installed as GlobalQueue._pendingCheck. */ function pendingCheckRead(e, t, n, r) {
|
|
3196
3185
|
setPendingCheckActive(false);
|
|
3197
|
-
if (typeof e.
|
|
3198
|
-
const i = n
|
|
3186
|
+
if (typeof e.Ve === "function") prepareComputed(e, true);
|
|
3187
|
+
const i = n.We;
|
|
3199
3188
|
if (t && i & STATUS_PENDING && i & STATUS_UNINITIALIZED) {
|
|
3200
3189
|
if (tracking && e !== t) link(e, t);
|
|
3201
3190
|
setPendingCheckActive(true);
|
|
3202
|
-
throw n.
|
|
3191
|
+
throw n.me;
|
|
3203
3192
|
}
|
|
3204
3193
|
collectPendingSources(e);
|
|
3205
3194
|
if (r) collectPendingSources(r);
|
|
@@ -3211,10 +3200,10 @@ function recordFreshRead(e, t) {
|
|
|
3211
3200
|
}
|
|
3212
3201
|
|
|
3213
3202
|
function applyReask(e, t) {
|
|
3214
|
-
const n = !!(e
|
|
3215
|
-
const r = t && !(n && !e.
|
|
3216
|
-
const i = n && e.
|
|
3217
|
-
e.
|
|
3203
|
+
const n = !!(e.We & STATUS_PENDING);
|
|
3204
|
+
const r = t && !(n && !e.Nt);
|
|
3205
|
+
const i = n && e.Nt !== r;
|
|
3206
|
+
e.Nt = r;
|
|
3218
3207
|
return i;
|
|
3219
3208
|
}
|
|
3220
3209
|
|
|
@@ -3254,7 +3243,7 @@ function isPending(e) {
|
|
|
3254
3243
|
} catch (e) {
|
|
3255
3244
|
collectPending();
|
|
3256
3245
|
if (e instanceof NotReadyError) {
|
|
3257
|
-
const t = !!(e.source
|
|
3246
|
+
const t = !!(e.source?.We & STATUS_UNINITIALIZED);
|
|
3258
3247
|
if (r.found && !t) return true;
|
|
3259
3248
|
if (context && t) throw e;
|
|
3260
3249
|
}
|
|
@@ -3268,25 +3257,25 @@ function isPending(e) {
|
|
|
3268
3257
|
// Hook installation (same late-binding pattern as GlobalQueue._update /
|
|
3269
3258
|
// _propagateAffects): core call sites fire these behind the same guards the
|
|
3270
3259
|
// direct calls used, so behavior is identical once this module loads.
|
|
3271
|
-
GlobalQueue.
|
|
3260
|
+
GlobalQueue.le = syncCompanions;
|
|
3272
3261
|
|
|
3273
|
-
GlobalQueue.
|
|
3262
|
+
GlobalQueue.ce = updatePendingSignal;
|
|
3274
3263
|
|
|
3275
|
-
GlobalQueue.
|
|
3264
|
+
GlobalQueue.ae = updateChildCompanions;
|
|
3276
3265
|
|
|
3277
|
-
GlobalQueue.
|
|
3266
|
+
GlobalQueue.fe = snapCompanionsToState;
|
|
3278
3267
|
|
|
3279
|
-
GlobalQueue.
|
|
3268
|
+
GlobalQueue.Ee = latestRead;
|
|
3280
3269
|
|
|
3281
|
-
GlobalQueue.
|
|
3270
|
+
GlobalQueue.Se = pendingCheckRead;
|
|
3282
3271
|
|
|
3283
|
-
GlobalQueue.
|
|
3272
|
+
GlobalQueue.de = recordFreshRead;
|
|
3284
3273
|
|
|
3285
|
-
GlobalQueue.
|
|
3274
|
+
GlobalQueue.Te = applyReask;
|
|
3286
3275
|
|
|
3287
|
-
GlobalQueue.
|
|
3276
|
+
GlobalQueue.pe = repollDownstreamVerdicts;
|
|
3288
3277
|
|
|
3289
|
-
GlobalQueue.
|
|
3278
|
+
GlobalQueue._e = witnessAffects;
|
|
3290
3279
|
|
|
3291
3280
|
/**
|
|
3292
3281
|
* Effects are the leaf nodes of our reactive graph. When their sources change, they are
|
|
@@ -3296,16 +3285,16 @@ GlobalQueue.Ie = witnessAffects;
|
|
|
3296
3285
|
const i = !!r?.user;
|
|
3297
3286
|
const o = createEffectNode(e, t, n, i ? EFFECT_USER : EFFECT_RENDER, notifyEffectStatus, r);
|
|
3298
3287
|
recompute(o, true);
|
|
3299
|
-
!r?.defer && (o.
|
|
3288
|
+
!r?.defer && (o.Qe === EFFECT_USER || r?.schedule ? o.Ze.enqueue(o.Qe, runEffect.bind(null, o)) : runEffect(o));
|
|
3300
3289
|
}
|
|
3301
3290
|
|
|
3302
3291
|
function notifyEffectStatus(e, t) {
|
|
3303
3292
|
// Use passed values if provided, otherwise read from node
|
|
3304
|
-
const n = e !== undefined ? e : this
|
|
3305
|
-
const r = t !== undefined ? t : this.
|
|
3293
|
+
const n = e !== undefined ? e : this.We;
|
|
3294
|
+
const r = t !== undefined ? t : this.me;
|
|
3306
3295
|
if (n & STATUS_ERROR) {
|
|
3307
|
-
this.
|
|
3308
|
-
if (this.
|
|
3296
|
+
this.Ze.notify(this, STATUS_PENDING, 0);
|
|
3297
|
+
if (this.Qe === EFFECT_USER) {
|
|
3309
3298
|
// The error handler is the error arm of the effect phase (#2840 ruling):
|
|
3310
3299
|
// queue it like the effect function. It runs in the same imperative,
|
|
3311
3300
|
// writable scope, throws escalate the same way, and a held transition
|
|
@@ -3315,23 +3304,23 @@ function notifyEffectStatus(e, t) {
|
|
|
3315
3304
|
// phase takes the success arm instead. Blocked forwards (explicit
|
|
3316
3305
|
// `status` arg without node-state writes) don't queue: the status
|
|
3317
3306
|
// re-propagates unblocked at commit.
|
|
3318
|
-
if (this
|
|
3319
|
-
this.
|
|
3320
|
-
this.
|
|
3307
|
+
if (this.We & STATUS_ERROR) {
|
|
3308
|
+
this.Fe = true;
|
|
3309
|
+
this.Ze.enqueue(this.Qe, this.Pt ??= runEffect.bind(null, this));
|
|
3321
3310
|
}
|
|
3322
3311
|
return;
|
|
3323
3312
|
}
|
|
3324
|
-
if (!this.
|
|
3313
|
+
if (!this.Ze.notify(this, STATUS_ERROR, STATUS_ERROR)) {
|
|
3325
3314
|
haltReactivity(unwrapStatusError(r));
|
|
3326
3315
|
throw r;
|
|
3327
3316
|
}
|
|
3328
|
-
} else if (this.
|
|
3329
|
-
this.
|
|
3317
|
+
} else if (this.Qe === EFFECT_RENDER) {
|
|
3318
|
+
this.Ze.notify(this, STATUS_PENDING | STATUS_ERROR, n, r);
|
|
3330
3319
|
}
|
|
3331
3320
|
}
|
|
3332
3321
|
|
|
3333
3322
|
function runEffect(e) {
|
|
3334
|
-
if (!e.
|
|
3323
|
+
if (!e.Fe || e.Le & REACTIVE_DISPOSED) return;
|
|
3335
3324
|
// Error arm (#2840), user effects only: a compute-phase error that is still
|
|
3336
3325
|
// the node's settled state at effect time runs the bundle's error handler in
|
|
3337
3326
|
// this same imperative, writable scope. Unwrap the StatusError used for
|
|
@@ -3342,42 +3331,42 @@ function runEffect(e) {
|
|
|
3342
3331
|
// Render effects bypass: their errors route to boundaries synchronously in
|
|
3343
3332
|
// notifyEffectStatus, and a runner queued by an earlier valueChanged in the
|
|
3344
3333
|
// same flush must not be hijacked by a later-arriving error status.
|
|
3345
|
-
if (e
|
|
3346
|
-
const t = unwrapStatusError(e.
|
|
3347
|
-
e.
|
|
3348
|
-
e.
|
|
3334
|
+
if (e.We & STATUS_ERROR && e.Qe === EFFECT_USER) {
|
|
3335
|
+
const t = unwrapStatusError(e.me);
|
|
3336
|
+
e.bt = e.xe;
|
|
3337
|
+
e.Fe = false;
|
|
3349
3338
|
try {
|
|
3350
|
-
e.
|
|
3351
|
-
const t = e.
|
|
3352
|
-
e.
|
|
3339
|
+
e.vt ? e.vt(t, () => {
|
|
3340
|
+
const t = e.Et;
|
|
3341
|
+
e.Et = undefined;
|
|
3353
3342
|
t?.();
|
|
3354
3343
|
}) : console.error(t);
|
|
3355
3344
|
} catch (t) {
|
|
3356
|
-
if (!e.
|
|
3345
|
+
if (!e.Ze.notify(e, STATUS_ERROR, STATUS_ERROR)) {
|
|
3357
3346
|
haltReactivity(t);
|
|
3358
3347
|
throw t;
|
|
3359
3348
|
}
|
|
3360
3349
|
}
|
|
3361
3350
|
return;
|
|
3362
3351
|
}
|
|
3363
|
-
const t = e.
|
|
3364
|
-
e.
|
|
3352
|
+
const t = e.Et;
|
|
3353
|
+
e.Et = undefined;
|
|
3365
3354
|
try {
|
|
3366
3355
|
t?.();
|
|
3367
|
-
const n = e.
|
|
3356
|
+
const n = e.Dt(e.xe, e.bt);
|
|
3368
3357
|
if (false && n !== undefined && typeof n !== "function") ;
|
|
3369
3358
|
// The final cleanup is invoked by disposeChildren at true disposal.
|
|
3370
|
-
e.
|
|
3359
|
+
e.Et = n;
|
|
3371
3360
|
} catch (t) {
|
|
3372
|
-
e.
|
|
3373
|
-
e
|
|
3374
|
-
if (!e.
|
|
3361
|
+
e.me = new StatusError(e, t);
|
|
3362
|
+
e.We |= STATUS_ERROR;
|
|
3363
|
+
if (!e.Ze.notify(e, STATUS_ERROR, STATUS_ERROR)) {
|
|
3375
3364
|
haltReactivity(t);
|
|
3376
3365
|
throw t;
|
|
3377
3366
|
}
|
|
3378
3367
|
} finally {
|
|
3379
|
-
e.
|
|
3380
|
-
e.
|
|
3368
|
+
e.bt = e.xe;
|
|
3369
|
+
e.Fe = false;
|
|
3381
3370
|
}
|
|
3382
3371
|
}
|
|
3383
3372
|
|
|
@@ -3389,39 +3378,39 @@ GlobalQueue.ee = runEffect;
|
|
|
3389
3378
|
* Uses stale reads.
|
|
3390
3379
|
*/ function trackedEffect(e, t) {
|
|
3391
3380
|
const run = () => {
|
|
3392
|
-
if (!n.
|
|
3381
|
+
if (!n.Fe || n.Le & REACTIVE_DISPOSED) return;
|
|
3393
3382
|
try {
|
|
3394
|
-
n.
|
|
3383
|
+
n.Fe = false;
|
|
3395
3384
|
recompute(n);
|
|
3396
3385
|
} finally {}
|
|
3397
3386
|
};
|
|
3398
3387
|
const n = computed(() => {
|
|
3399
|
-
const t = n.
|
|
3400
|
-
n.
|
|
3388
|
+
const t = n.Et;
|
|
3389
|
+
n.Et = undefined;
|
|
3401
3390
|
t?.();
|
|
3402
3391
|
const r = staleValues(e);
|
|
3403
|
-
n.
|
|
3392
|
+
n.Et = r;
|
|
3404
3393
|
}, {
|
|
3405
3394
|
...t,
|
|
3406
3395
|
lazy: true
|
|
3407
3396
|
});
|
|
3408
|
-
n.
|
|
3409
|
-
n.
|
|
3410
|
-
n.
|
|
3411
|
-
n.
|
|
3412
|
-
n.
|
|
3413
|
-
const r = e !== undefined ? e : n
|
|
3397
|
+
n.Et = undefined;
|
|
3398
|
+
n.ke = n.ke & ~CONFIG_AUTO_DISPOSE | CONFIG_CHILDREN_FORBIDDEN;
|
|
3399
|
+
n.Fe = true;
|
|
3400
|
+
n.Qe = EFFECT_TRACKED;
|
|
3401
|
+
n.yt = (e, t) => {
|
|
3402
|
+
const r = e !== undefined ? e : n.We;
|
|
3414
3403
|
if (r & STATUS_ERROR) {
|
|
3415
|
-
n.
|
|
3416
|
-
const e = t !== undefined ? t : n.
|
|
3417
|
-
if (!n.
|
|
3404
|
+
n.Ze.notify(n, STATUS_PENDING, 0);
|
|
3405
|
+
const e = t !== undefined ? t : n.me;
|
|
3406
|
+
if (!n.Ze.notify(n, STATUS_ERROR, STATUS_ERROR)) {
|
|
3418
3407
|
haltReactivity(unwrapStatusError(e));
|
|
3419
3408
|
throw e;
|
|
3420
3409
|
}
|
|
3421
3410
|
}
|
|
3422
3411
|
};
|
|
3423
|
-
n.
|
|
3424
|
-
n.
|
|
3412
|
+
n.Xe = run;
|
|
3413
|
+
n.Ze.enqueue(EFFECT_USER, run);
|
|
3425
3414
|
}
|
|
3426
3415
|
|
|
3427
3416
|
function restoreTransition(e, t) {
|
|
@@ -3432,6 +3421,22 @@ function restoreTransition(e, t) {
|
|
|
3432
3421
|
}
|
|
3433
3422
|
|
|
3434
3423
|
/**
|
|
3424
|
+
* The primitive for mutations: imperative async workflows whose *writes span
|
|
3425
|
+
* an async gap* — optimistic write, server round-trip, reconciling write —
|
|
3426
|
+
* where intermediate state must not leak and failure must revert cleanly
|
|
3427
|
+
* (pair with `createOptimistic` / `createOptimisticStore`).
|
|
3428
|
+
*
|
|
3429
|
+
* Navigation-shaped updates do not need an action. A plain setter call is
|
|
3430
|
+
* enough: reads pull the async, and downstream async computeds hold their
|
|
3431
|
+
* previous values per-node until the new ones are ready (`isPending` /
|
|
3432
|
+
* `latest` expose the in-flight state). Reach for `action` only when writes
|
|
3433
|
+
* happen *after* async work, not merely upstream of it.
|
|
3434
|
+
*
|
|
3435
|
+
* Framework-level actions (router form actions, server actions) are
|
|
3436
|
+
* specializations of this primitive: they are actions in exactly this sense —
|
|
3437
|
+
* the same transactional semantics — with form binding, serialization, and
|
|
3438
|
+
* submission tracking layered on top. The shared name is deliberate.
|
|
3439
|
+
*
|
|
3435
3440
|
* Wraps a generator function so each invocation runs as a single transaction
|
|
3436
3441
|
* (a "transition") that batches every signal/store write between yields. The
|
|
3437
3442
|
* surrounding UI sees one atomic update per yielded step; nothing is committed
|
|
@@ -3506,7 +3511,29 @@ function restoreTransition(e, t) {
|
|
|
3506
3511
|
};
|
|
3507
3512
|
const run = e => {
|
|
3508
3513
|
if (e.done) return done(e.value);
|
|
3509
|
-
|
|
3514
|
+
// Thenable assimilation can itself throw synchronously (a `then`
|
|
3515
|
+
// getter, or a `then()` method that throws — #2918). Match `await`
|
|
3516
|
+
// semantics: the failure is thrown back into the generator at the
|
|
3517
|
+
// yield point (catchable there); if uncaught, step()'s guard settles
|
|
3518
|
+
// the action so its iterator never leaks in the transition. The
|
|
3519
|
+
// settled flag implements A+ 2.3.3.3.4.1: a throw after the thenable
|
|
3520
|
+
// already called a callback is ignored.
|
|
3521
|
+
let t = false;
|
|
3522
|
+
try {
|
|
3523
|
+
if (isThenable(e.value)) return void e.value.then(e => {
|
|
3524
|
+
if (t) return;
|
|
3525
|
+
t = true;
|
|
3526
|
+
restoreTransition(o, () => step(e));
|
|
3527
|
+
}, e => {
|
|
3528
|
+
if (t) return;
|
|
3529
|
+
t = true;
|
|
3530
|
+
restoreTransition(o, () => step(e, true));
|
|
3531
|
+
});
|
|
3532
|
+
} catch (e) {
|
|
3533
|
+
if (t) return;
|
|
3534
|
+
t = true;
|
|
3535
|
+
return void restoreTransition(o, () => step(e, true));
|
|
3536
|
+
}
|
|
3510
3537
|
restoreTransition(o, () => step(e.value));
|
|
3511
3538
|
};
|
|
3512
3539
|
step();
|
|
@@ -3564,7 +3591,7 @@ function accessor(e) {
|
|
|
3564
3591
|
function createSignal(e, t) {
|
|
3565
3592
|
if (typeof e === "function") {
|
|
3566
3593
|
const n = computed(e, t);
|
|
3567
|
-
n.
|
|
3594
|
+
n.ke &= ~CONFIG_AUTO_DISPOSE;
|
|
3568
3595
|
return [ accessor(n), setMemo.bind(null, n) ];
|
|
3569
3596
|
}
|
|
3570
3597
|
const n = signal(e, t);
|
|
@@ -3738,6 +3765,12 @@ function createEffect(e, t, n) {
|
|
|
3738
3765
|
};
|
|
3739
3766
|
}
|
|
3740
3767
|
|
|
3768
|
+
/** Delivers effect applies on a microtask instead of queueing them (#2930). */ class MicrotaskQueue extends Queue {
|
|
3769
|
+
enqueue(e, t) {
|
|
3770
|
+
queueMicrotask(() => t(e));
|
|
3771
|
+
}
|
|
3772
|
+
}
|
|
3773
|
+
|
|
3741
3774
|
/**
|
|
3742
3775
|
* Awaits a reactive expression and returns its first fully-settled value as a
|
|
3743
3776
|
* `Promise`. Pending async reads (`createMemo` returning a promise, etc.) are
|
|
@@ -3761,11 +3794,23 @@ function createEffect(e, t, n) {
|
|
|
3761
3794
|
*/ function resolve(e) {
|
|
3762
3795
|
return new Promise((t, n) => {
|
|
3763
3796
|
createRoot(r => {
|
|
3797
|
+
// Deliver effect applies on a microtask instead of the owner queue: an
|
|
3798
|
+
// incomplete transition stashes its effect queues until it settles, but
|
|
3799
|
+
// an action yielding this promise is itself what keeps the transition
|
|
3800
|
+
// open — the stashed res() deadlocked the action (#2930). The compute
|
|
3801
|
+
// still runs in place (under the transaction's view when created inside
|
|
3802
|
+
// an action step), and status/boundary notifications keep their normal
|
|
3803
|
+
// route through the inherited queue.
|
|
3804
|
+
const i = getOwner();
|
|
3805
|
+
const o = new MicrotaskQueue;
|
|
3806
|
+
o.Z = i.Ze;
|
|
3807
|
+
// notify() forwards up the normal chain
|
|
3808
|
+
i.Ze = o;
|
|
3764
3809
|
// A user effect rather than a bare computed: computeds are pull-based and
|
|
3765
3810
|
// are only re-enqueued when a pending source *resolves* — a rejection just
|
|
3766
3811
|
// marks them errored, so nothing would re-run and the promise would never
|
|
3767
3812
|
// settle (#2842). The effect's error channel is notified on rejection.
|
|
3768
|
-
|
|
3813
|
+
effect(e, e => {
|
|
3769
3814
|
t(e);
|
|
3770
3815
|
r();
|
|
3771
3816
|
}, e => {
|
|
@@ -3787,7 +3832,7 @@ function createOptimistic(e, t) {
|
|
|
3787
3832
|
installOptimisticEngine();
|
|
3788
3833
|
if (typeof e === "function") {
|
|
3789
3834
|
const n = optimisticComputed(e, t);
|
|
3790
|
-
n.
|
|
3835
|
+
n.ke &= ~CONFIG_AUTO_DISPOSE;
|
|
3791
3836
|
return [ accessor(n), setSignal.bind(null, n) ];
|
|
3792
3837
|
}
|
|
3793
3838
|
const n = optimisticSignal(e, t);
|
|
@@ -3876,7 +3921,7 @@ function createOptimistic(e, t) {
|
|
|
3876
3921
|
* on owner disposal
|
|
3877
3922
|
*/ function onSettled(e) {
|
|
3878
3923
|
const t = getOwner();
|
|
3879
|
-
t && !(t.
|
|
3924
|
+
t && !(t.ke & CONFIG_CHILDREN_FORBIDDEN) ? createTrackedEffect(() => untrack(e), undefined) : globalQueue.enqueue(EFFECT_USER, () => {
|
|
3880
3925
|
// Unowned, out-of-band fire (no owner, or a children-forbidden one this
|
|
3881
3926
|
// one-shot must not bind to): a returned cleanup has no lifecycle to
|
|
3882
3927
|
// attach to. Reject it in dev; in production the return is simply
|
|
@@ -4030,9 +4075,9 @@ function applyArrayItem(e, t, n, r, i) {
|
|
|
4030
4075
|
if (!isWrappable(f)) continue;
|
|
4031
4076
|
const E = (u.get(f) ?? storeLookup.get(f))?.[$TARGET];
|
|
4032
4077
|
if (!E?.[STORE_DESC]) continue;
|
|
4033
|
-
const
|
|
4034
|
-
if (f ===
|
|
4035
|
-
applyState(
|
|
4078
|
+
const S = unwrap(t[a]);
|
|
4079
|
+
if (f === S || !isWrappable(S) || Array.isArray(f) !== Array.isArray(S) || i(f) != null && i(f) !== i(S)) continue;
|
|
4080
|
+
applyState(S, wrap(f, n), i);
|
|
4036
4081
|
}
|
|
4037
4082
|
}
|
|
4038
4083
|
|
|
@@ -4066,7 +4111,7 @@ function applyStateFast(e, t, n) {
|
|
|
4066
4111
|
let o = false;
|
|
4067
4112
|
const s = r.length;
|
|
4068
4113
|
if (e.length && s && isWrappable(e[0]) && n(e[0]) != null) {
|
|
4069
|
-
let u, l, c, a, f, E,
|
|
4114
|
+
let u, l, c, a, f, E, S, d;
|
|
4070
4115
|
for (c = 0, a = Math.min(s, e.length); c < a && keyedMatch(E = r[c], e[c], n); c++) {
|
|
4071
4116
|
isWrappable(E) && isWrappable(e[c]) && applyState(e[c], wrap(E, t), n);
|
|
4072
4117
|
}
|
|
@@ -4089,22 +4134,22 @@ function applyStateFast(e, t, n) {
|
|
|
4089
4134
|
s !== e.length && i?.length && setSignal(i.length, e.length);
|
|
4090
4135
|
return;
|
|
4091
4136
|
}
|
|
4092
|
-
|
|
4137
|
+
S = new Array(f + 1);
|
|
4093
4138
|
for (l = f; l >= c; l--) {
|
|
4094
4139
|
E = e[l];
|
|
4095
|
-
|
|
4096
|
-
u = p.get(
|
|
4097
|
-
|
|
4098
|
-
p.set(
|
|
4140
|
+
d = itemKey(E, n);
|
|
4141
|
+
u = p.get(d);
|
|
4142
|
+
S[l] = u === undefined ? -1 : u;
|
|
4143
|
+
p.set(d, l);
|
|
4099
4144
|
}
|
|
4100
4145
|
for (u = c; u <= a; u++) {
|
|
4101
4146
|
E = r[u];
|
|
4102
|
-
|
|
4103
|
-
l = p.get(
|
|
4147
|
+
d = itemKey(E, n);
|
|
4148
|
+
l = p.get(d);
|
|
4104
4149
|
if (l !== undefined && l !== -1) {
|
|
4105
4150
|
T[l] = E;
|
|
4106
|
-
l =
|
|
4107
|
-
p.set(
|
|
4151
|
+
l = S[l];
|
|
4152
|
+
p.set(d, l);
|
|
4108
4153
|
}
|
|
4109
4154
|
}
|
|
4110
4155
|
for (l = c; l < e.length; l++) {
|
|
@@ -4173,17 +4218,17 @@ function applyStateSlow(e, t, n) {
|
|
|
4173
4218
|
let u = false;
|
|
4174
4219
|
const l = getOverrideValue(r, i, "length", o);
|
|
4175
4220
|
if (e.length && l && isWrappable(e[0]) && n(e[0]) != null) {
|
|
4176
|
-
let c, a, f, E,
|
|
4177
|
-
for (f = 0, E = Math.min(l, e.length); f < E && keyedMatch(
|
|
4178
|
-
isWrappable(
|
|
4221
|
+
let c, a, f, E, S, d, T, p;
|
|
4222
|
+
for (f = 0, E = Math.min(l, e.length); f < E && keyedMatch(d = getOverrideValue(r, i, f, o), e[f], n); f++) {
|
|
4223
|
+
isWrappable(d) && isWrappable(e[f]) && applyState(e[f], wrap(d, t), n);
|
|
4179
4224
|
}
|
|
4180
4225
|
const _ = new Array(e.length), O = new Map;
|
|
4181
|
-
for (E = l - 1,
|
|
4182
|
-
|
|
4183
|
-
_[
|
|
4226
|
+
for (E = l - 1, S = e.length - 1; E >= f && S >= f && keyedMatch(d = getOverrideValue(r, i, E, o), e[S], n); E--,
|
|
4227
|
+
S--) {
|
|
4228
|
+
_[S] = d;
|
|
4184
4229
|
}
|
|
4185
|
-
if (f >
|
|
4186
|
-
for (a = f; a <=
|
|
4230
|
+
if (f > S || f > E) {
|
|
4231
|
+
for (a = f; a <= S; a++) {
|
|
4187
4232
|
u = true;
|
|
4188
4233
|
s?.[a] && setSignal(s[a], wrapValue(e[a], t));
|
|
4189
4234
|
}
|
|
@@ -4197,20 +4242,20 @@ function applyStateSlow(e, t, n) {
|
|
|
4197
4242
|
l !== r && s?.length && setSignal(s.length, r);
|
|
4198
4243
|
return;
|
|
4199
4244
|
}
|
|
4200
|
-
T = new Array(
|
|
4201
|
-
for (a =
|
|
4202
|
-
|
|
4203
|
-
p = itemKey(
|
|
4245
|
+
T = new Array(S + 1);
|
|
4246
|
+
for (a = S; a >= f; a--) {
|
|
4247
|
+
d = e[a];
|
|
4248
|
+
p = itemKey(d, n);
|
|
4204
4249
|
c = O.get(p);
|
|
4205
4250
|
T[a] = c === undefined ? -1 : c;
|
|
4206
4251
|
O.set(p, a);
|
|
4207
4252
|
}
|
|
4208
4253
|
for (c = f; c <= E; c++) {
|
|
4209
|
-
|
|
4210
|
-
p = itemKey(
|
|
4254
|
+
d = getOverrideValue(r, i, c, o);
|
|
4255
|
+
p = itemKey(d, n);
|
|
4211
4256
|
a = O.get(p);
|
|
4212
4257
|
if (a !== undefined && a !== -1) {
|
|
4213
|
-
_[a] =
|
|
4258
|
+
_[a] = d;
|
|
4214
4259
|
a = T[a];
|
|
4215
4260
|
O.set(p, a);
|
|
4216
4261
|
}
|
|
@@ -4248,12 +4293,12 @@ function applyStateSlow(e, t, n) {
|
|
|
4248
4293
|
const a = l[c];
|
|
4249
4294
|
const f = s[a];
|
|
4250
4295
|
const E = unwrap(getOverrideValue(r, i, a, o));
|
|
4251
|
-
let
|
|
4252
|
-
if (E ===
|
|
4253
|
-
if (!E || !isWrappable(E) || !isWrappable(
|
|
4296
|
+
let S = unwrap(e[a]);
|
|
4297
|
+
if (E === S) continue;
|
|
4298
|
+
if (!E || !isWrappable(E) || !isWrappable(S) || Array.isArray(E) !== Array.isArray(S) || n(E) != null && n(E) !== n(S)) {
|
|
4254
4299
|
u && setSignal(u, void 0);
|
|
4255
|
-
f && setSignal(f, isWrappable(
|
|
4256
|
-
} else applyState(
|
|
4300
|
+
f && setSignal(f, isWrappable(S) ? wrap(S, t) : S);
|
|
4301
|
+
} else applyState(S, wrap(E, t), n);
|
|
4257
4302
|
}
|
|
4258
4303
|
}
|
|
4259
4304
|
if (!u && t[STORE_DESC]) applyDescendants(r, e, t, s, n, i, o);
|
|
@@ -4323,7 +4368,7 @@ function createProjectionInternal(e, t, n) {
|
|
|
4323
4368
|
if (!r) r = getOwner();
|
|
4324
4369
|
runProjectionComputed(o, e, n?.key || "id");
|
|
4325
4370
|
}, undefined);
|
|
4326
|
-
r.
|
|
4371
|
+
r.ke &= ~CONFIG_AUTO_DISPOSE;
|
|
4327
4372
|
return {
|
|
4328
4373
|
store: o,
|
|
4329
4374
|
node: r
|
|
@@ -4392,7 +4437,7 @@ function createProjectionInternal(e, t, n) {
|
|
|
4392
4437
|
const o = getOwner();
|
|
4393
4438
|
let s = false;
|
|
4394
4439
|
let u;
|
|
4395
|
-
const l = new Proxy(e, createWriteTraps(() => !s || o.
|
|
4440
|
+
const l = new Proxy(e, createWriteTraps(() => !s || o.lt === u, i));
|
|
4396
4441
|
storeSetter(l, i => {
|
|
4397
4442
|
u = t(i);
|
|
4398
4443
|
s = true;
|
|
@@ -4599,7 +4644,7 @@ function ownEnumerableKeysPlain(e) {
|
|
|
4599
4644
|
* The value a store leaf's backing signal currently shows to readers: active
|
|
4600
4645
|
* override, else held pending value, else committed value.
|
|
4601
4646
|
*/ function visibleNodeValue(e) {
|
|
4602
|
-
return e.A !== undefined && e.A !== NOT_PENDING ? unwrapOverride(e.A) : e.V !== NOT_PENDING ? e.V : e.
|
|
4647
|
+
return e.A !== undefined && e.A !== NOT_PENDING ? unwrapOverride(e.A) : e.V !== NOT_PENDING ? e.V : e.xe;
|
|
4603
4648
|
}
|
|
4604
4649
|
|
|
4605
4650
|
function hasOwnStoreProperty(e, t) {
|
|
@@ -4654,7 +4699,7 @@ function getNode(e, t, n, r, i = isEqual, o) {
|
|
|
4654
4699
|
}
|
|
4655
4700
|
if (o && n in o) {
|
|
4656
4701
|
const e = o[n];
|
|
4657
|
-
s.
|
|
4702
|
+
s.we = e === undefined ? NO_SNAPSHOT : e;
|
|
4658
4703
|
snapshotSources?.add(s);
|
|
4659
4704
|
}
|
|
4660
4705
|
if (typeof n === "symbol" && n !== $TRACK && n !== $AFFECTS) symbolKeyedRecords.add(t);
|
|
@@ -4684,7 +4729,7 @@ function getNode(e, t, n, r, i = isEqual, o) {
|
|
|
4684
4729
|
// A live scope exists, so affects.ts already installed the mark engine.
|
|
4685
4730
|
for (const [r, i] of affectsScopes) {
|
|
4686
4731
|
if (r.F && i.scope.has(t) && (i.key === undefined || i.key === n)) {
|
|
4687
|
-
GlobalQueue.
|
|
4732
|
+
GlobalQueue.ie(e);
|
|
4688
4733
|
i.inherited.push(e);
|
|
4689
4734
|
}
|
|
4690
4735
|
}
|
|
@@ -4772,11 +4817,11 @@ i) {
|
|
|
4772
4817
|
// Callers guard on `pendingCheckActive`, which only flips inside
|
|
4773
4818
|
// isPending() — the verdict layer is loaded and its hook installed.
|
|
4774
4819
|
const n = e[STORE_NODE]?.[$AFFECTS];
|
|
4775
|
-
if (n?.F) GlobalQueue.
|
|
4820
|
+
if (n?.F) GlobalQueue._e(n);
|
|
4776
4821
|
if (affectsScopes.size) {
|
|
4777
4822
|
const r = e[STORE_VALUE];
|
|
4778
4823
|
for (const [e, i] of affectsScopes) {
|
|
4779
|
-
if (e !== n && e.F && i.scope.has(r) && (i.key === undefined || i.key === t)) GlobalQueue.
|
|
4824
|
+
if (e !== n && e.F && i.scope.has(r) && (i.key === undefined || i.key === t)) GlobalQueue._e(e);
|
|
4780
4825
|
}
|
|
4781
4826
|
}
|
|
4782
4827
|
}
|
|
@@ -4797,7 +4842,7 @@ i) {
|
|
|
4797
4842
|
const t = affectsScopes.get(e);
|
|
4798
4843
|
if (!t) return;
|
|
4799
4844
|
affectsScopes.delete(e);
|
|
4800
|
-
for (let e = 0; e < t.inherited.length; e++) GlobalQueue.
|
|
4845
|
+
for (let e = 0; e < t.inherited.length; e++) GlobalQueue.oe(t.inherited[e]);
|
|
4801
4846
|
};
|
|
4802
4847
|
if (t === undefined) {
|
|
4803
4848
|
const t = getNode(e, n, $AFFECTS, undefined, false);
|
|
@@ -4924,7 +4969,7 @@ function prepareStoreWrite(e, t, n) {
|
|
|
4924
4969
|
}
|
|
4925
4970
|
const r = e[STORE_VALUE];
|
|
4926
4971
|
const i = r[n];
|
|
4927
|
-
if (snapshotCaptureActive && typeof n !== "symbol" && !((e[STORE_FIREWALL]
|
|
4972
|
+
if (snapshotCaptureActive && typeof n !== "symbol" && !((e[STORE_FIREWALL]?.We ?? 0) & STATUS_PENDING)) {
|
|
4928
4973
|
if (!e[STORE_SNAPSHOT_PROPS]) {
|
|
4929
4974
|
e[STORE_SNAPSHOT_PROPS] = Object.create(null);
|
|
4930
4975
|
snapshotSources?.add(e);
|
|
@@ -4952,7 +4997,7 @@ function prepareStoreWrite(e, t, n) {
|
|
|
4952
4997
|
// STORE_OPTIMISTIC is only set by createOptimisticStore, which installs the
|
|
4953
4998
|
// optimistic engine before wrapping.
|
|
4954
4999
|
if (e[STORE_OPTIMISTIC] && !projectionWriteActive) {
|
|
4955
|
-
GlobalQueue.
|
|
5000
|
+
GlobalQueue.De(t);
|
|
4956
5001
|
}
|
|
4957
5002
|
}
|
|
4958
5003
|
|
|
@@ -5025,7 +5070,7 @@ let Writing = null;
|
|
|
5025
5070
|
* itself (the derive function works its own draft while uninitialized).
|
|
5026
5071
|
*/ function throwIfUninitialized(e) {
|
|
5027
5072
|
const t = e[STORE_FIREWALL];
|
|
5028
|
-
if (t && t
|
|
5073
|
+
if (t && t.We & STATUS_UNINITIALIZED) throw t.me ?? new NotReadyError(t);
|
|
5029
5074
|
}
|
|
5030
5075
|
|
|
5031
5076
|
const storeTraps = {
|
|
@@ -5118,9 +5163,9 @@ const storeTraps = {
|
|
|
5118
5163
|
// throws). #2769
|
|
5119
5164
|
const f = typeof t === "string" ? Number(t) : -1;
|
|
5120
5165
|
const E = Array.isArray(s) && Number.isInteger(f) && f >= 0 && f < 4294967295 && String(f) === t;
|
|
5121
|
-
const
|
|
5122
|
-
const
|
|
5123
|
-
const T = E &&
|
|
5166
|
+
const S = E ? f + 1 : 0;
|
|
5167
|
+
const d = E && (getOverlayLayer(e, "length") ?? s).length;
|
|
5168
|
+
const T = E && S > d ? S : undefined;
|
|
5124
5169
|
if (l === a && T === undefined) return true;
|
|
5125
5170
|
armOptimisticStoreWrite(e, r);
|
|
5126
5171
|
if (a !== undefined && a === i && T === undefined) {
|
|
@@ -5156,7 +5201,7 @@ const storeTraps = {
|
|
|
5156
5201
|
if (t.length) {
|
|
5157
5202
|
setSignal(t.length, T);
|
|
5158
5203
|
} else if (!projectionWriteActive && !e[STORE_OPTIMISTIC]) {
|
|
5159
|
-
const n = upsertStoreNode(e, t, "length",
|
|
5204
|
+
const n = upsertStoreNode(e, t, "length", d, e[STORE_SNAPSHOT_PROPS]);
|
|
5160
5205
|
setSignal(n, T);
|
|
5161
5206
|
}
|
|
5162
5207
|
}
|
|
@@ -5312,126 +5357,76 @@ function createStore(e, t, n) {
|
|
|
5312
5357
|
} : e => storeSetter(i, e) ];
|
|
5313
5358
|
}
|
|
5314
5359
|
|
|
5315
|
-
/**
|
|
5316
|
-
* The pending-source identity of a live `affects()` mark on `node` (lazy,
|
|
5317
|
-
* one per node, shared by overlapping registrations via the refcount).
|
|
5318
|
-
*
|
|
5319
|
-
* A mark rides the SAME status rails as real in-flight async — downstream
|
|
5320
|
-
* subscribers hold the sentinel in `_pendingSources` — but under its own
|
|
5321
|
-
* identity so the two channels can't clear each other:
|
|
5322
|
-
* - `_reask` is permanently `false`: a mark is by definition a declared
|
|
5323
|
-
* value change, so `quietPending` never silences a window it participates
|
|
5324
|
-
* in — even when the mark rides over an otherwise-quiet `refresh()`
|
|
5325
|
-
* re-ask of the same node (the whole point of declaring one).
|
|
5326
|
-
* - A landing on the marked node settles only the node's OWN source entry;
|
|
5327
|
-
* the sentinel entry survives until the mark's transaction releases it.
|
|
5328
|
-
* - The sentinel itself never carries `STATUS_PENDING`, so
|
|
5329
|
-
* `transitionComplete` never counts a mark as a blocker of its own
|
|
5330
|
-
* transaction (release happens AT settle — self-blocking would deadlock),
|
|
5331
|
-
* and reads of the marked node never throw (marks are value-transparent
|
|
5332
|
-
* at the source; pendingness is what propagates).
|
|
5333
|
-
*/ function getAffectsSentinel(e) {
|
|
5334
|
-
return e.Vt ??= {
|
|
5335
|
-
Qt: undefined,
|
|
5336
|
-
// Brand + backref: lets the scheduler's settlement checks recognize
|
|
5337
|
-
// mark-sourced pending (which must never block its own transaction —
|
|
5338
|
-
// release happens AT settle).
|
|
5339
|
-
Dt: e,
|
|
5340
|
-
Qe: 0,
|
|
5341
|
-
$e: 0,
|
|
5342
|
-
gt: false,
|
|
5343
|
-
Ue: undefined,
|
|
5344
|
-
k: null,
|
|
5345
|
-
Ze: null
|
|
5346
|
-
};
|
|
5347
|
-
}
|
|
5348
|
-
|
|
5349
|
-
/**
|
|
5350
|
-
* Push a live mark's pendingness downstream from the marked node through the
|
|
5351
|
-
* normal status rails. Runs on every registration (dedup in `notifyStatus`
|
|
5352
|
-
* stops re-descent at already-covered subscribers). Subscribers that
|
|
5353
|
-
* recompute mid-window shed this via `clearStatus` and re-acquire it through
|
|
5354
|
-
* the read path (`applyAffectsReads` for direct readers of the marked node,
|
|
5355
|
-
* `collectMarkSources` transitively) — the mark analogue of real async's
|
|
5356
|
-
* re-throw-on-read. Subscribers are deliberately NOT queued as pending nodes
|
|
5357
|
-
* (#2893): they hold no value needing a transition-scheduled commit, and
|
|
5358
|
-
* queueing would stamp the marking action's transaction onto them — from then
|
|
5359
|
-
* on ANY write dirtying one of them (including writes to unmarked signals
|
|
5360
|
-
* that merely share a downstream memo) would be captured and frozen until the
|
|
5361
|
-
* action settles.
|
|
5362
|
-
*/ function propagateAffectsMark(e) {
|
|
5363
|
-
if (!e.k && !e.ut) return;
|
|
5364
|
-
const t = getAffectsSentinel(e);
|
|
5365
|
-
const n = new NotReadyError(t);
|
|
5366
|
-
forEachDependent(e, e => {
|
|
5367
|
-
if (!e.Be?.has(t)) {
|
|
5368
|
-
notifyStatus(e, STATUS_PENDING, n);
|
|
5369
|
-
}
|
|
5370
|
-
});
|
|
5371
|
-
}
|
|
5372
|
-
|
|
5373
|
-
/**
|
|
5374
|
-
* Re-establish mark pendingness on a computed that read marked sources
|
|
5375
|
-
* during its recompute (`clearStatus` at the top of the commit path wiped
|
|
5376
|
-
* any sentinel entries it held). Called by `recompute` after the commit —
|
|
5377
|
-
* not before, because setting `_error` earlier would make the commit path
|
|
5378
|
-
* treat the node as errored and skip the value write.
|
|
5379
|
-
*/ function applyAffectsReads(e, t) {
|
|
5380
|
-
let n = false;
|
|
5381
|
-
for (let r = 0; r < t.length; r++) {
|
|
5382
|
-
const i = t[r];
|
|
5383
|
-
if (!i.F) continue;
|
|
5384
|
-
const o = getAffectsSentinel(i);
|
|
5385
|
-
if (addPendingSource(e, o)) {
|
|
5386
|
-
e.$e |= STATUS_PENDING;
|
|
5387
|
-
setPendingError(e, o);
|
|
5388
|
-
n = true;
|
|
5389
|
-
}
|
|
5390
|
-
}
|
|
5391
|
-
if (n && GlobalQueue.Ee !== null) GlobalQueue.Ee(e);
|
|
5392
|
-
}
|
|
5393
|
-
|
|
5394
5360
|
/**
|
|
5395
5361
|
* The counting half of a mark, shared by direct registration and store-scope
|
|
5396
|
-
* inheritance (a node created inside a live keyless mark's identity scope)
|
|
5397
|
-
*
|
|
5398
|
-
*
|
|
5362
|
+
* inheritance (a node created inside a live keyless mark's identity scope).
|
|
5363
|
+
* A mark is ONLY this count: coverage of everything derived from the node is
|
|
5364
|
+
* pull-derived by the verdict layer's `markWalk` (dep-graph reachability), so
|
|
5365
|
+
* nothing is stored downstream and nothing can be stranded or stripped by
|
|
5366
|
+
* mid-window recomputes.
|
|
5399
5367
|
*/ function markAffects(e) {
|
|
5400
5368
|
e.F = (e.F || 0) + 1;
|
|
5401
5369
|
shiftAffectsMarks(1);
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5370
|
+
}
|
|
5371
|
+
|
|
5372
|
+
/**
|
|
5373
|
+
* Boundary visual channel: within a transaction, a live mark holds Loading
|
|
5374
|
+
* fallbacks / reveal ordering the way real in-flight async would — but the
|
|
5375
|
+
* notification is tagged visibility-only at the source (`_markVisual`), so
|
|
5376
|
+
* the root queue never registers reporters from it: marks are invisible to
|
|
5377
|
+
* ALL completion and settlement accounting by construction. Boundaries hold
|
|
5378
|
+
* the marked node in `_sources` while `_affectsCount` is live; the release
|
|
5379
|
+
* sweep (finalizePureQueue re-checks boundary children after mark release)
|
|
5380
|
+
* is the display-state update point. Ambient marks release inside the same
|
|
5381
|
+
* flush that would surface them, before effects run — verdict-only, netting
|
|
5382
|
+
* no visual change.
|
|
5383
|
+
*/ function notifyMarkBoundaries(e) {
|
|
5384
|
+
if (!e.k && !e.rt) return;
|
|
5385
|
+
const t = new NotReadyError(e);
|
|
5386
|
+
t.ve = true;
|
|
5387
|
+
const n = new Set;
|
|
5388
|
+
const visit = e => {
|
|
5389
|
+
if (n.has(e)) return;
|
|
5390
|
+
n.add(e);
|
|
5391
|
+
// Display consumers (render effects, boundary computeds) act on the
|
|
5392
|
+
// notification; descent stops there, exactly like the status rails.
|
|
5393
|
+
if (e.yt) {
|
|
5394
|
+
e.yt(STATUS_PENDING, t);
|
|
5395
|
+
return;
|
|
5396
|
+
}
|
|
5397
|
+
forEachDependent(e, visit);
|
|
5398
|
+
};
|
|
5399
|
+
forEachDependent(e, visit);
|
|
5405
5400
|
}
|
|
5406
5401
|
|
|
5407
5402
|
/**
|
|
5408
5403
|
* Registers one `affects()` mark on a node: counts it, records the
|
|
5409
5404
|
* registration with the current transaction (after initTransition the queue's
|
|
5410
|
-
* batch IS the active transition, mirroring `_optimisticNodes`),
|
|
5411
|
-
*
|
|
5412
|
-
*
|
|
5413
|
-
* registration (not just the first): subscribers gained since an
|
|
5414
|
-
* overlapping registration get covered, and dedup stops re-descent
|
|
5405
|
+
* batch IS the active transition, mirroring `_optimisticNodes`), re-derives
|
|
5406
|
+
* every downstream verdict companion (the mark channel's only push — verdict
|
|
5407
|
+
* pokes, not state), and notifies boundary display state. Both walks run on
|
|
5408
|
+
* every registration (not just the first): subscribers gained since an
|
|
5409
|
+
* earlier overlapping registration get covered, and dedup stops re-descent.
|
|
5415
5410
|
*/ function registerAffectsMark(e) {
|
|
5416
5411
|
markAffects(e);
|
|
5417
5412
|
globalQueue.D.L.push(e);
|
|
5418
|
-
|
|
5413
|
+
// Companions only exist once the verdict layer (isPending/latest) loaded;
|
|
5414
|
+
// without them there is no materialized verdict to poke.
|
|
5415
|
+
GlobalQueue.pe !== null && GlobalQueue.pe(e);
|
|
5416
|
+
notifyMarkBoundaries(e);
|
|
5419
5417
|
schedule();
|
|
5420
5418
|
}
|
|
5421
5419
|
|
|
5422
5420
|
/**
|
|
5423
|
-
* Releases one registration. When the node's last mark drops,
|
|
5424
|
-
*
|
|
5425
|
-
*
|
|
5426
|
-
*
|
|
5427
|
-
* can't open a fresh override window that would itself need settlement.
|
|
5421
|
+
* Releases one registration. When the node's last mark drops, re-derives
|
|
5422
|
+
* every downstream verdict through the settlement snap (committed, not
|
|
5423
|
+
* transition-scoped — release runs inside queue finalization, where a
|
|
5424
|
+
* setSignal would open a fresh override window that nothing settles).
|
|
5428
5425
|
*/ function releaseAffectsMark(e) {
|
|
5429
5426
|
shiftAffectsMarks(-1);
|
|
5430
5427
|
e.F--;
|
|
5431
5428
|
if (!e.F) {
|
|
5432
|
-
|
|
5433
|
-
if (t) settlePendingSource(e, t, true);
|
|
5434
|
-
GlobalQueue.Se !== null && GlobalQueue.Se(e);
|
|
5429
|
+
GlobalQueue.pe !== null && GlobalQueue.pe(e, true);
|
|
5435
5430
|
GlobalQueue.ne?.(e);
|
|
5436
5431
|
}
|
|
5437
5432
|
}
|
|
@@ -5444,58 +5439,16 @@ function createStore(e, t, n) {
|
|
|
5444
5439
|
e.length = 0;
|
|
5445
5440
|
}
|
|
5446
5441
|
|
|
5447
|
-
/**
|
|
5448
|
-
* True when a node's pending status comes ONLY from affects() sentinels. A
|
|
5449
|
-
* mark is a promise of change, not an absence of value: reads of mark-pended
|
|
5450
|
-
* derived nodes stay value-transparent (verdicts report pending; the read
|
|
5451
|
-
* path must not suspend). Any real async source among the pending sources
|
|
5452
|
-
* keeps normal suspension semantics. Core's read path reaches this through
|
|
5453
|
-
* `GlobalQueue._onlyMarkPending`, gated on the `activeAffectsMarks` counter.
|
|
5454
|
-
*/ function onlyMarkPending(e) {
|
|
5455
|
-
const t = e.Be;
|
|
5456
|
-
if (t) {
|
|
5457
|
-
for (const e of t) if (!e.Dt) return false;
|
|
5458
|
-
return true;
|
|
5459
|
-
}
|
|
5460
|
-
return false;
|
|
5461
|
-
}
|
|
5462
|
-
|
|
5463
|
-
/**
|
|
5464
|
-
* Collect the still-live marked nodes behind a pended owner's sentinel
|
|
5465
|
-
* sources into a recompute's `affectsReads`. This is the transitive half of
|
|
5466
|
-
* read-path re-establishment (#2893): real async re-establishes at every
|
|
5467
|
-
* derivation level because its re-throw on read re-registers the source, but
|
|
5468
|
-
* mark-pended reads are value-transparent — without this, pendingness dies on
|
|
5469
|
-
* the first mid-window recompute past depth one, and the isPending() probe
|
|
5470
|
-
* itself (whose prepare step recomputes retryable NotReady holders) strips
|
|
5471
|
-
* the very status it reports on. Reached through
|
|
5472
|
-
* `GlobalQueue._collectMarkSources`, gated on `activeAffectsMarks`.
|
|
5473
|
-
*/ function collectMarkSources(e, t) {
|
|
5474
|
-
if (e.Be) {
|
|
5475
|
-
for (const n of e.Be) {
|
|
5476
|
-
const e = n.Dt;
|
|
5477
|
-
if (e && e.F) t.push(e);
|
|
5478
|
-
}
|
|
5479
|
-
}
|
|
5480
|
-
}
|
|
5481
|
-
|
|
5482
5442
|
// Late installation (same pattern as `GlobalQueue._update`): the mark engine
|
|
5483
5443
|
// lives with the feature so graphs that never declare a mark never ship it.
|
|
5484
|
-
// Each call site is gated by state only this module creates (
|
|
5485
|
-
//
|
|
5486
|
-
//
|
|
5487
|
-
|
|
5488
|
-
GlobalQueue.re = applyAffectsReads;
|
|
5489
|
-
|
|
5490
|
-
GlobalQueue.ie = releaseAffectsMarks;
|
|
5491
|
-
|
|
5492
|
-
GlobalQueue.oe = markAffects;
|
|
5444
|
+
// Each call site is gated by state only this module creates (a non-empty
|
|
5445
|
+
// `_affectsNodes` batch, a live scope in the store's `affectsScopes`), so the
|
|
5446
|
+
// hooks are installed before the first time any of them can fire.
|
|
5447
|
+
GlobalQueue.re = releaseAffectsMarks;
|
|
5493
5448
|
|
|
5494
|
-
GlobalQueue.
|
|
5449
|
+
GlobalQueue.ie = markAffects;
|
|
5495
5450
|
|
|
5496
|
-
GlobalQueue.
|
|
5497
|
-
|
|
5498
|
-
GlobalQueue.le = collectMarkSources;
|
|
5451
|
+
GlobalQueue.oe = releaseAffectsMark;
|
|
5499
5452
|
|
|
5500
5453
|
function affects(e, t) {
|
|
5501
5454
|
const n = e?.[$TARGET];
|
|
@@ -5595,8 +5548,8 @@ function clearOptimisticStores(e, t) {
|
|
|
5595
5548
|
c.A = NOT_PENDING;
|
|
5596
5549
|
c.R = null;
|
|
5597
5550
|
c.V = NOT_PENDING;
|
|
5598
|
-
c.
|
|
5599
|
-
if (!c.
|
|
5551
|
+
c.xe = i;
|
|
5552
|
+
if (!c.ht || !c.ht(o, i)) {
|
|
5600
5553
|
insertSubs(c, true);
|
|
5601
5554
|
schedule();
|
|
5602
5555
|
}
|
|
@@ -5668,7 +5621,7 @@ function createOptimisticProjectionInternal(e, t, n) {
|
|
|
5668
5621
|
setProjectionWriteActive(false);
|
|
5669
5622
|
}
|
|
5670
5623
|
}, undefined);
|
|
5671
|
-
r.
|
|
5624
|
+
r.ke &= ~CONFIG_AUTO_DISPOSE;
|
|
5672
5625
|
}
|
|
5673
5626
|
return {
|
|
5674
5627
|
store: o,
|
|
@@ -6080,24 +6033,24 @@ function mapArray(e, t, n) {
|
|
|
6080
6033
|
const i = t.length > 1;
|
|
6081
6034
|
const o = t;
|
|
6082
6035
|
const s = {
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6036
|
+
wt: createOwner(),
|
|
6037
|
+
Gt: 0,
|
|
6038
|
+
Lt: e,
|
|
6039
|
+
Ut: [],
|
|
6040
|
+
kt: o,
|
|
6041
|
+
Vt: [],
|
|
6042
|
+
xt: [],
|
|
6043
|
+
Qt: r,
|
|
6044
|
+
Ft: r || n?.keyed === false ? [] : undefined,
|
|
6045
|
+
Wt: i && n?.keyed !== false ? [] : undefined,
|
|
6046
|
+
Mt: n?.keyed === false,
|
|
6047
|
+
Ht: n?.fallback
|
|
6095
6048
|
};
|
|
6096
6049
|
const u = computed(updateKeyedMap.bind(s));
|
|
6097
6050
|
// Untracked reads inside the internal owner resolve via _parentComputed; routing
|
|
6098
6051
|
// them through node lets store-proxy lookups see pending writes (not stale _value).
|
|
6099
|
-
s.
|
|
6100
|
-
u.
|
|
6052
|
+
s.wt.et = u;
|
|
6053
|
+
u.ke &= ~CONFIG_AUTO_DISPOSE;
|
|
6101
6054
|
return accessor(u);
|
|
6102
6055
|
}
|
|
6103
6056
|
|
|
@@ -6115,52 +6068,52 @@ const pureOptions = {
|
|
|
6115
6068
|
// strong-abort ordering: removed rows now dispose AFTER the pass's new rows
|
|
6116
6069
|
// are created (you cannot destroy state before knowing the pass will land).
|
|
6117
6070
|
function updateKeyedMap() {
|
|
6118
|
-
const e = this.
|
|
6071
|
+
const e = this.Lt() || [], t = e.length;
|
|
6119
6072
|
e[$TRACK];
|
|
6120
6073
|
// top level tracking
|
|
6121
|
-
runWithOwner(this.
|
|
6074
|
+
runWithOwner(this.wt, () => {
|
|
6122
6075
|
let n, r, i, o,
|
|
6123
6076
|
// Mappers write freshly-created row/index signals into the STAGE
|
|
6124
6077
|
// arrays (`rows`/`indexes`), never into `this._rows`/`this._indexes`.
|
|
6125
|
-
s = this.
|
|
6078
|
+
s = this.Ft ? this.Mt ? () => {
|
|
6126
6079
|
i[r] = signal(e[r], pureOptions);
|
|
6127
|
-
return this.
|
|
6080
|
+
return this.kt(accessor(i[r]), r);
|
|
6128
6081
|
} : () => {
|
|
6129
6082
|
i[r] = signal(e[r], pureOptions);
|
|
6130
6083
|
o && (o[r] = signal(r, pureOptions));
|
|
6131
|
-
return this.
|
|
6132
|
-
} : this.
|
|
6084
|
+
return this.kt(accessor(i[r]), o ? accessor(o[r]) : undefined);
|
|
6085
|
+
} : this.Wt ? () => {
|
|
6133
6086
|
const t = e[r];
|
|
6134
6087
|
o[r] = signal(r, pureOptions);
|
|
6135
|
-
return this.
|
|
6088
|
+
return this.kt(t, accessor(o[r]));
|
|
6136
6089
|
} : () => {
|
|
6137
6090
|
const t = e[r];
|
|
6138
|
-
return this.
|
|
6091
|
+
return this.kt(t);
|
|
6139
6092
|
};
|
|
6140
6093
|
// fast path for empty arrays
|
|
6141
6094
|
if (t === 0) {
|
|
6142
|
-
if (this.
|
|
6143
|
-
this.
|
|
6144
|
-
this
|
|
6145
|
-
this.
|
|
6146
|
-
this.
|
|
6147
|
-
this.
|
|
6148
|
-
this.
|
|
6149
|
-
this.
|
|
6095
|
+
if (this.Gt !== 0) {
|
|
6096
|
+
this.wt.dispose(false);
|
|
6097
|
+
this.xt = [];
|
|
6098
|
+
this.Ut = [];
|
|
6099
|
+
this.Vt = [];
|
|
6100
|
+
this.Gt = 0;
|
|
6101
|
+
this.Ft && (this.Ft = []);
|
|
6102
|
+
this.Wt && (this.Wt = []);
|
|
6150
6103
|
}
|
|
6151
|
-
if (this.
|
|
6104
|
+
if (this.Ht && !this.Vt[0]) {
|
|
6152
6105
|
// an aborted fallback attempt leaves an owner without a mapping;
|
|
6153
6106
|
// dispose it before re-creating
|
|
6154
|
-
this
|
|
6155
|
-
this.
|
|
6107
|
+
this.xt[0]?.dispose();
|
|
6108
|
+
this.Vt[0] = runWithOwner(this.xt[0] = createOwner(), this.Ht);
|
|
6156
6109
|
}
|
|
6157
6110
|
}
|
|
6158
6111
|
// fast path for new create
|
|
6159
|
-
else if (this.
|
|
6112
|
+
else if (this.Gt === 0) {
|
|
6160
6113
|
const u = new Array(t);
|
|
6161
6114
|
const l = new Array(t);
|
|
6162
|
-
i = this.
|
|
6163
|
-
o = this.
|
|
6115
|
+
i = this.Ft && new Array(t);
|
|
6116
|
+
o = this.Wt && new Array(t);
|
|
6164
6117
|
try {
|
|
6165
6118
|
for (r = 0; r < t; r++) u[r] = runWithOwner(l[r] = createOwner(), s);
|
|
6166
6119
|
} catch (e) {
|
|
@@ -6168,53 +6121,53 @@ function updateKeyedMap() {
|
|
|
6168
6121
|
throw e;
|
|
6169
6122
|
}
|
|
6170
6123
|
// commit
|
|
6171
|
-
if (this
|
|
6124
|
+
if (this.xt[0]) this.xt[0].dispose();
|
|
6172
6125
|
// previous fallback
|
|
6173
|
-
this.
|
|
6174
|
-
this
|
|
6175
|
-
i && (this.
|
|
6176
|
-
o && (this.
|
|
6177
|
-
this.
|
|
6178
|
-
this.
|
|
6126
|
+
this.Vt = u;
|
|
6127
|
+
this.xt = l;
|
|
6128
|
+
i && (this.Ft = i);
|
|
6129
|
+
o && (this.Wt = o);
|
|
6130
|
+
this.Ut = e.slice(0);
|
|
6131
|
+
this.Gt = t;
|
|
6179
6132
|
} else {
|
|
6180
|
-
let u, l, c, a, f, E,
|
|
6181
|
-
i = this.
|
|
6182
|
-
o = this.
|
|
6133
|
+
let u, l, c, a, f, E, S, d, T, p = new Array(t), _ = new Array(t);
|
|
6134
|
+
i = this.Ft ? new Array(t) : undefined;
|
|
6135
|
+
o = this.Wt ? new Array(t) : undefined;
|
|
6183
6136
|
// skip common prefix
|
|
6184
|
-
for (u = 0, l = Math.min(this.
|
|
6185
|
-
if (this.
|
|
6137
|
+
for (u = 0, l = Math.min(this.Gt, t); u < l && (this.Ut[u] === e[u] || this.Ft && compare(this.Qt, this.Ut[u], e[u])); u++) {
|
|
6138
|
+
if (this.Ft) setSignal(this.Ft[u], e[u]);
|
|
6186
6139
|
}
|
|
6187
6140
|
// common suffix
|
|
6188
|
-
for (l = this.
|
|
6141
|
+
for (l = this.Gt - 1, c = t - 1; l >= u && c >= u && (this.Ut[l] === e[c] || this.Ft && compare(this.Qt, this.Ut[l], e[c])); l--,
|
|
6189
6142
|
c--) {
|
|
6190
|
-
p[c] = this.
|
|
6191
|
-
_[c] = this
|
|
6192
|
-
i && (i[c] = this.
|
|
6193
|
-
o && (o[c] = this.
|
|
6143
|
+
p[c] = this.Vt[l];
|
|
6144
|
+
_[c] = this.xt[l];
|
|
6145
|
+
i && (i[c] = this.Ft[l]);
|
|
6146
|
+
o && (o[c] = this.Wt[l]);
|
|
6194
6147
|
}
|
|
6195
6148
|
// 0) prepare a map of all indices in newItems, scanning backwards so we encounter them in natural order
|
|
6196
6149
|
E = new Map;
|
|
6197
|
-
|
|
6150
|
+
S = new Array(c + 1);
|
|
6198
6151
|
for (r = c; r >= u; r--) {
|
|
6199
6152
|
a = e[r];
|
|
6200
|
-
f = this.
|
|
6153
|
+
f = this.Qt ? this.Qt(a) : a;
|
|
6201
6154
|
n = E.get(f);
|
|
6202
|
-
|
|
6155
|
+
S[r] = n === undefined ? -1 : n;
|
|
6203
6156
|
E.set(f, r);
|
|
6204
6157
|
}
|
|
6205
6158
|
// 1) step through all old items and see if they can be found in the new set; if so, save them in a temp array and mark them moved; if not, queue them for disposal at commit
|
|
6206
6159
|
for (n = u; n <= l; n++) {
|
|
6207
|
-
a = this.
|
|
6208
|
-
f = this.
|
|
6160
|
+
a = this.Ut[n];
|
|
6161
|
+
f = this.Qt ? this.Qt(a) : a;
|
|
6209
6162
|
r = E.get(f);
|
|
6210
6163
|
if (r !== undefined && r !== -1) {
|
|
6211
|
-
p[r] = this.
|
|
6212
|
-
_[r] = this
|
|
6213
|
-
i && (i[r] = this.
|
|
6214
|
-
o && (o[r] = this.
|
|
6215
|
-
r =
|
|
6164
|
+
p[r] = this.Vt[n];
|
|
6165
|
+
_[r] = this.xt[n];
|
|
6166
|
+
i && (i[r] = this.Ft[n]);
|
|
6167
|
+
o && (o[r] = this.Wt[n]);
|
|
6168
|
+
r = S[r];
|
|
6216
6169
|
E.set(f, r);
|
|
6217
|
-
} else (
|
|
6170
|
+
} else (d ??= []).push(this.xt[n]);
|
|
6218
6171
|
}
|
|
6219
6172
|
// 2) create new rows into the temp arrays; an abort disposes only these
|
|
6220
6173
|
try {
|
|
@@ -6229,25 +6182,25 @@ function updateKeyedMap() {
|
|
|
6229
6182
|
}
|
|
6230
6183
|
// 3) commit: land positions, then dispose exited rows
|
|
6231
6184
|
for (r = u; r < t; r++) {
|
|
6232
|
-
this.
|
|
6233
|
-
this
|
|
6185
|
+
this.Vt[r] = p[r];
|
|
6186
|
+
this.xt[r] = _[r];
|
|
6234
6187
|
if (i) {
|
|
6235
|
-
this.
|
|
6236
|
-
setSignal(this.
|
|
6188
|
+
this.Ft[r] = i[r];
|
|
6189
|
+
setSignal(this.Ft[r], e[r]);
|
|
6237
6190
|
}
|
|
6238
6191
|
if (o) {
|
|
6239
|
-
this.
|
|
6240
|
-
setSignal(this.
|
|
6192
|
+
this.Wt[r] = o[r];
|
|
6193
|
+
setSignal(this.Wt[r], r);
|
|
6241
6194
|
}
|
|
6242
6195
|
}
|
|
6243
|
-
if (
|
|
6196
|
+
if (d) for (n = 0; n < d.length; n++) d[n].dispose();
|
|
6244
6197
|
// 4) in case the new set is shorter than the old, set the length of the mapped array
|
|
6245
|
-
this.
|
|
6198
|
+
this.Vt = this.Vt.slice(0, this.Gt = t);
|
|
6246
6199
|
// 5) save a copy of the mapped items for the next update
|
|
6247
|
-
this.
|
|
6200
|
+
this.Ut = e.slice(0);
|
|
6248
6201
|
}
|
|
6249
6202
|
});
|
|
6250
|
-
return this.
|
|
6203
|
+
return this.Vt;
|
|
6251
6204
|
}
|
|
6252
6205
|
|
|
6253
6206
|
/**
|
|
@@ -6267,22 +6220,22 @@ function updateKeyedMap() {
|
|
|
6267
6220
|
*/ function repeat(e, t, n) {
|
|
6268
6221
|
const r = t;
|
|
6269
6222
|
const i = {
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6223
|
+
wt: createOwner(),
|
|
6224
|
+
Gt: 0,
|
|
6225
|
+
jt: 0,
|
|
6226
|
+
$t: e,
|
|
6227
|
+
kt: r,
|
|
6228
|
+
xt: [],
|
|
6229
|
+
Vt: [],
|
|
6230
|
+
Kt: n?.from,
|
|
6231
|
+
Ht: n?.fallback
|
|
6279
6232
|
};
|
|
6280
6233
|
const o = computed(updateRepeat.bind(i));
|
|
6281
6234
|
// Same as mapArray: untracked reads inside the internal owner resolve via
|
|
6282
6235
|
// _parentComputed, so async reads in row callbacks register with the node
|
|
6283
6236
|
// (pending tracking + post-settle retry) instead of vanishing.
|
|
6284
|
-
i.
|
|
6285
|
-
o.
|
|
6237
|
+
i.wt.et = o;
|
|
6238
|
+
o.ke &= ~CONFIG_AUTO_DISPOSE;
|
|
6286
6239
|
return accessor(o);
|
|
6287
6240
|
}
|
|
6288
6241
|
|
|
@@ -6294,55 +6247,55 @@ function updateKeyedMap() {
|
|
|
6294
6247
|
// for the post-settle retry. The overlap math also subsumes the previous
|
|
6295
6248
|
// disjoint-window/front-clear/end-clear/shift special cases.
|
|
6296
6249
|
function updateRepeat() {
|
|
6297
|
-
const e = this
|
|
6298
|
-
const t = this.
|
|
6299
|
-
runWithOwner(this.
|
|
6250
|
+
const e = this.$t();
|
|
6251
|
+
const t = this.Kt?.() || 0;
|
|
6252
|
+
runWithOwner(this.wt, () => {
|
|
6300
6253
|
if (e === 0) {
|
|
6301
|
-
if (this.
|
|
6302
|
-
this.
|
|
6303
|
-
this
|
|
6304
|
-
this.
|
|
6305
|
-
this.
|
|
6254
|
+
if (this.Gt !== 0) {
|
|
6255
|
+
this.wt.dispose(false);
|
|
6256
|
+
this.xt = [];
|
|
6257
|
+
this.Vt = [];
|
|
6258
|
+
this.Gt = 0;
|
|
6306
6259
|
// Reset offset to match the cleared data (#2767, repro 2).
|
|
6307
|
-
this.
|
|
6260
|
+
this.jt = 0;
|
|
6308
6261
|
}
|
|
6309
|
-
if (this.
|
|
6262
|
+
if (this.Ht && !this.Vt[0]) {
|
|
6310
6263
|
// an aborted fallback attempt leaves an owner without a mapping;
|
|
6311
6264
|
// dispose it before re-creating
|
|
6312
|
-
this
|
|
6313
|
-
this.
|
|
6265
|
+
this.xt[0]?.dispose();
|
|
6266
|
+
this.Vt[0] = runWithOwner(this.xt[0] = createOwner(), this.Ht);
|
|
6314
6267
|
}
|
|
6315
6268
|
return;
|
|
6316
6269
|
}
|
|
6317
6270
|
const n = t + e;
|
|
6318
|
-
const r = this.
|
|
6271
|
+
const r = this.jt + this.Gt;
|
|
6319
6272
|
// Retained overlap [keepStart, keepEnd) in global indexes; empty when the
|
|
6320
6273
|
// windows are disjoint or when coming from empty/fallback.
|
|
6321
|
-
const i = Math.max(t, this.
|
|
6274
|
+
const i = Math.max(t, this.jt);
|
|
6322
6275
|
const o = Math.min(n, r);
|
|
6323
6276
|
const s = new Array(e);
|
|
6324
6277
|
const u = new Array(e);
|
|
6325
6278
|
for (let e = i; e < o; e++) {
|
|
6326
|
-
u[e - t] = this
|
|
6327
|
-
s[e - t] = this.
|
|
6279
|
+
u[e - t] = this.xt[e - this.jt];
|
|
6280
|
+
s[e - t] = this.Vt[e - this.jt];
|
|
6328
6281
|
}
|
|
6329
6282
|
try {
|
|
6330
6283
|
for (let e = t; e < n; e++) {
|
|
6331
6284
|
if (e >= i && e < o) continue;
|
|
6332
|
-
s[e - t] = runWithOwner(u[e - t] = createOwner(), () => this.
|
|
6285
|
+
s[e - t] = runWithOwner(u[e - t] = createOwner(), () => this.kt(e));
|
|
6333
6286
|
}
|
|
6334
6287
|
} catch (e) {
|
|
6335
6288
|
for (let e = t; e < n; e++) if ((e < i || e >= o) && u[e - t]) u[e - t].dispose();
|
|
6336
6289
|
throw e;
|
|
6337
6290
|
}
|
|
6338
6291
|
// commit: dispose the previous fallback or the rows leaving the window
|
|
6339
|
-
if (this.
|
|
6340
|
-
this.
|
|
6341
|
-
this
|
|
6342
|
-
this.
|
|
6343
|
-
this.
|
|
6292
|
+
if (this.Gt === 0) this.xt[0]?.dispose(); else for (let e = this.jt; e < r; e++) if (e < t || e >= n) this.xt[e - this.jt].dispose();
|
|
6293
|
+
this.Vt = s;
|
|
6294
|
+
this.xt = u;
|
|
6295
|
+
this.jt = t;
|
|
6296
|
+
this.Gt = e;
|
|
6344
6297
|
});
|
|
6345
|
-
return this.
|
|
6298
|
+
return this.Vt;
|
|
6346
6299
|
}
|
|
6347
6300
|
|
|
6348
6301
|
function compare(e, t, n) {
|
|
@@ -6353,23 +6306,23 @@ function boundaryComputed(e, t) {
|
|
|
6353
6306
|
const n = computed(e, {
|
|
6354
6307
|
lazy: true
|
|
6355
6308
|
});
|
|
6356
|
-
n.
|
|
6309
|
+
n.yt = (e, t) => {
|
|
6357
6310
|
// Use passed values if provided, otherwise read from node
|
|
6358
|
-
const r = e !== undefined ? e : n
|
|
6359
|
-
const i = t !== undefined ? t : n.
|
|
6311
|
+
const r = e !== undefined ? e : n.We;
|
|
6312
|
+
const i = t !== undefined ? t : n.me;
|
|
6360
6313
|
// Notify both status dimensions like a render effect does; the queue chain
|
|
6361
6314
|
// consumes this boundary's own type and forwards the remainder upward until
|
|
6362
6315
|
// a boundary that handles it is found.
|
|
6363
|
-
n
|
|
6364
|
-
const o = n.
|
|
6316
|
+
n.We &= ~n.Yt;
|
|
6317
|
+
const o = n.Ze.notify(n, STATUS_PENDING | STATUS_ERROR, r, i);
|
|
6365
6318
|
// The queue is the only propagation channel: a foreign status must not stay
|
|
6366
6319
|
// reader-visible on the tree, or reads re-throw it across the boundary and
|
|
6367
6320
|
// link unrelated ambient contexts (the #2809 nested-boundary loop). Deps are
|
|
6368
6321
|
// untouched, so the tree still recomputes when the foreign source settles.
|
|
6369
|
-
const s = r & ~n.
|
|
6322
|
+
const s = r & ~n.Yt & (STATUS_PENDING | STATUS_ERROR);
|
|
6370
6323
|
if (s) {
|
|
6371
|
-
n
|
|
6372
|
-
if (n.
|
|
6324
|
+
n.We &= ~s;
|
|
6325
|
+
if (n.me === i && !(n.We & (STATUS_PENDING | STATUS_ERROR))) n.me = undefined;
|
|
6373
6326
|
}
|
|
6374
6327
|
// An ERROR the chain could not deliver to any boundary is uncaught. The
|
|
6375
6328
|
// scrub above already removed it from reader-visible state, so without
|
|
@@ -6380,16 +6333,16 @@ function boundaryComputed(e, t) {
|
|
|
6380
6333
|
throw i;
|
|
6381
6334
|
}
|
|
6382
6335
|
};
|
|
6383
|
-
n.
|
|
6384
|
-
n.
|
|
6336
|
+
n.Yt = t;
|
|
6337
|
+
n.ke &= ~CONFIG_AUTO_DISPOSE;
|
|
6385
6338
|
recompute(n, true);
|
|
6386
6339
|
return n;
|
|
6387
6340
|
}
|
|
6388
6341
|
|
|
6389
6342
|
function createBoundChildren(e, t, n, r) {
|
|
6390
|
-
const i = e.
|
|
6391
|
-
i.addChild(e.
|
|
6392
|
-
cleanup(() => i.removeChild(e.
|
|
6343
|
+
const i = e.Ze;
|
|
6344
|
+
i.addChild(e.Ze = n);
|
|
6345
|
+
cleanup(() => i.removeChild(e.Ze));
|
|
6393
6346
|
return runWithOwner(e, () => {
|
|
6394
6347
|
const e = computed(t);
|
|
6395
6348
|
return boundaryComputed(() => flatten(read(e)), r);
|
|
@@ -6411,53 +6364,53 @@ function isRevealController(e) {
|
|
|
6411
6364
|
}
|
|
6412
6365
|
|
|
6413
6366
|
function isSlotReady(e) {
|
|
6414
|
-
return isRevealController(e) ? e.
|
|
6367
|
+
return isRevealController(e) ? e.qt() : e.Bt.size === 0 && !e.Zt;
|
|
6415
6368
|
}
|
|
6416
6369
|
|
|
6417
6370
|
function isSlotMinimallyReady(e) {
|
|
6418
|
-
return isRevealController(e) ? e.
|
|
6371
|
+
return isRevealController(e) ? e.Xt() : isSlotReady(e);
|
|
6419
6372
|
}
|
|
6420
6373
|
|
|
6421
6374
|
function setSlotState(e, t, n, r) {
|
|
6422
|
-
setSignal(e.
|
|
6423
|
-
setSignal(e.
|
|
6375
|
+
setSignal(e.zt, n);
|
|
6376
|
+
setSignal(e.Jt, r);
|
|
6424
6377
|
if (isRevealController(e)) {
|
|
6425
|
-
if (!n && e.
|
|
6426
|
-
return e.
|
|
6378
|
+
if (!n && e.en === t) e.en = undefined;
|
|
6379
|
+
return e.tn(n, r);
|
|
6427
6380
|
}
|
|
6428
|
-
if (!n && e.
|
|
6381
|
+
if (!n && e.nn === t && e.rn) e.nn = undefined;
|
|
6429
6382
|
}
|
|
6430
6383
|
|
|
6431
6384
|
class RevealController {
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6385
|
+
sn;
|
|
6386
|
+
un;
|
|
6387
|
+
ln=[];
|
|
6388
|
+
en;
|
|
6389
|
+
zt=signal(false, {
|
|
6437
6390
|
ownedWrite: true,
|
|
6438
|
-
|
|
6391
|
+
gt: true
|
|
6439
6392
|
});
|
|
6440
|
-
|
|
6393
|
+
Jt=signal(false, {
|
|
6441
6394
|
ownedWrite: true,
|
|
6442
|
-
|
|
6395
|
+
gt: true
|
|
6443
6396
|
});
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6397
|
+
cn=true;
|
|
6398
|
+
an=true;
|
|
6399
|
+
fn=false;
|
|
6447
6400
|
constructor(e, t) {
|
|
6448
|
-
this.
|
|
6449
|
-
this.
|
|
6401
|
+
this.sn = e;
|
|
6402
|
+
this.un = t;
|
|
6450
6403
|
}
|
|
6451
|
-
|
|
6452
|
-
for (let t = 0; t < this.
|
|
6453
|
-
const n = this.
|
|
6454
|
-
if ((isRevealController(n) ? n.
|
|
6404
|
+
En(e) {
|
|
6405
|
+
for (let t = 0; t < this.ln.length; t++) {
|
|
6406
|
+
const n = this.ln[t];
|
|
6407
|
+
if ((isRevealController(n) ? n.en : n.nn) !== this) continue;
|
|
6455
6408
|
if (e(n) === false) return false;
|
|
6456
6409
|
}
|
|
6457
6410
|
return true;
|
|
6458
6411
|
}
|
|
6459
|
-
|
|
6460
|
-
return this.
|
|
6412
|
+
qt() {
|
|
6413
|
+
return this.En(isSlotReady);
|
|
6461
6414
|
}
|
|
6462
6415
|
/**
|
|
6463
6416
|
* "Minimally ready" = this group has something visible to show under its own policy.
|
|
@@ -6465,13 +6418,13 @@ class RevealController {
|
|
|
6465
6418
|
* - `together`: every direct slot is minimally ready.
|
|
6466
6419
|
* - `sequential`: the first owned slot is minimally ready (frontier can advance).
|
|
6467
6420
|
* - `natural`: any owned slot is minimally ready.
|
|
6468
|
-
*/
|
|
6469
|
-
const e = untrack(this.
|
|
6470
|
-
if (e === "together") return this.
|
|
6421
|
+
*/ Xt() {
|
|
6422
|
+
const e = untrack(this.sn);
|
|
6423
|
+
if (e === "together") return this.En(isSlotMinimallyReady);
|
|
6471
6424
|
if (e === "natural") {
|
|
6472
6425
|
let e = false;
|
|
6473
6426
|
let t = false;
|
|
6474
|
-
this.
|
|
6427
|
+
this.En(n => {
|
|
6475
6428
|
e = true;
|
|
6476
6429
|
if (isSlotMinimallyReady(n)) {
|
|
6477
6430
|
t = true;
|
|
@@ -6482,45 +6435,45 @@ class RevealController {
|
|
|
6482
6435
|
}
|
|
6483
6436
|
// sequential: only the first owned slot matters.
|
|
6484
6437
|
let t = true;
|
|
6485
|
-
this.
|
|
6438
|
+
this.En(e => {
|
|
6486
6439
|
t = isSlotMinimallyReady(e);
|
|
6487
6440
|
return false;
|
|
6488
6441
|
});
|
|
6489
6442
|
return t;
|
|
6490
6443
|
}
|
|
6491
|
-
|
|
6492
|
-
if (this.
|
|
6493
|
-
this.
|
|
6494
|
-
const t = untrack(this.
|
|
6495
|
-
setSignal(e.
|
|
6496
|
-
untrack(() => this.
|
|
6497
|
-
}
|
|
6498
|
-
|
|
6499
|
-
const t = this.
|
|
6500
|
-
if (t >= 0) this.
|
|
6501
|
-
untrack(() => this.
|
|
6502
|
-
}
|
|
6503
|
-
|
|
6504
|
-
if (this.
|
|
6505
|
-
this.
|
|
6506
|
-
const n = this.
|
|
6507
|
-
const r = this.
|
|
6444
|
+
Sn(e) {
|
|
6445
|
+
if (this.ln.includes(e)) return;
|
|
6446
|
+
this.ln.push(e);
|
|
6447
|
+
const t = untrack(this.sn);
|
|
6448
|
+
setSignal(e.zt, true), setSignal(e.Jt, t === "sequential" ? !!untrack(this.un) : false);
|
|
6449
|
+
untrack(() => this.tn());
|
|
6450
|
+
}
|
|
6451
|
+
dn(e) {
|
|
6452
|
+
const t = this.ln.indexOf(e);
|
|
6453
|
+
if (t >= 0) this.ln.splice(t, 1);
|
|
6454
|
+
untrack(() => this.tn());
|
|
6455
|
+
}
|
|
6456
|
+
tn(e, t) {
|
|
6457
|
+
if (this.fn) return;
|
|
6458
|
+
this.fn = true;
|
|
6459
|
+
const n = this.cn;
|
|
6460
|
+
const r = this.an;
|
|
6508
6461
|
try {
|
|
6509
|
-
const n = e ?? read(this.
|
|
6462
|
+
const n = e ?? read(this.zt), r = untrack(this.sn), i = r === "sequential" && !!untrack(this.un), o = t ?? i;
|
|
6510
6463
|
if (n) {
|
|
6511
6464
|
// Held by an outer group. Propagate the hold (and whatever collapsed policy
|
|
6512
6465
|
// the outer asked for) down the whole subtree. Inner order is ignored while
|
|
6513
6466
|
// held; it resumes once the outer releases us.
|
|
6514
|
-
this.
|
|
6467
|
+
this.En(e => setSlotState(e, this, true, o));
|
|
6515
6468
|
} else if (r === "natural") {
|
|
6516
6469
|
// Each child reveals based on its own readiness. A nested controller slot
|
|
6517
6470
|
// is released to run its own order locally — we bypass setSlotState for it
|
|
6518
6471
|
// so the parent backpointer survives for upward readiness notifications.
|
|
6519
|
-
this.
|
|
6472
|
+
this.En(e => {
|
|
6520
6473
|
if (isRevealController(e)) {
|
|
6521
|
-
setSignal(e.
|
|
6522
|
-
setSignal(e.
|
|
6523
|
-
e.
|
|
6474
|
+
setSignal(e.Jt, false);
|
|
6475
|
+
setSignal(e.zt, false);
|
|
6476
|
+
e.tn(false, false);
|
|
6524
6477
|
} else {
|
|
6525
6478
|
setSlotState(e, this, !isSlotReady(e), false);
|
|
6526
6479
|
}
|
|
@@ -6531,11 +6484,11 @@ class RevealController {
|
|
|
6531
6484
|
// sequential's first slot being ready is minimally ready; natural having any
|
|
6532
6485
|
// ready child is minimally ready. This lets `together` guarantee a single
|
|
6533
6486
|
// cohesive reveal without waiting for every grandchild.
|
|
6534
|
-
const e = this.
|
|
6535
|
-
this.
|
|
6487
|
+
const e = this.En(isSlotMinimallyReady);
|
|
6488
|
+
this.En(t => setSlotState(t, this, !e, false));
|
|
6536
6489
|
} else {
|
|
6537
6490
|
let e = false;
|
|
6538
|
-
this.
|
|
6491
|
+
this.En(t => {
|
|
6539
6492
|
if (e) return setSlotState(t, this, true, i);
|
|
6540
6493
|
if (isSlotReady(t)) return setSlotState(t, this, false, false);
|
|
6541
6494
|
e = true;
|
|
@@ -6546,63 +6499,63 @@ class RevealController {
|
|
|
6546
6499
|
// advancing past this slot, and we bypass setSlotState so the parent
|
|
6547
6500
|
// backpointer survives for upward readiness notifications.
|
|
6548
6501
|
if (isRevealController(t)) {
|
|
6549
|
-
setSignal(t.
|
|
6550
|
-
setSignal(t.
|
|
6551
|
-
t.
|
|
6502
|
+
setSignal(t.Jt, false);
|
|
6503
|
+
setSignal(t.zt, false);
|
|
6504
|
+
t.tn(false, false);
|
|
6552
6505
|
} else {
|
|
6553
6506
|
setSlotState(t, this, true, false);
|
|
6554
6507
|
}
|
|
6555
6508
|
});
|
|
6556
6509
|
}
|
|
6557
6510
|
} finally {
|
|
6558
|
-
this.
|
|
6559
|
-
this.
|
|
6560
|
-
this.
|
|
6511
|
+
this.cn = this.qt();
|
|
6512
|
+
this.an = this.Xt();
|
|
6513
|
+
this.fn = false;
|
|
6561
6514
|
}
|
|
6562
|
-
if (this.
|
|
6515
|
+
if (this.en && (n !== this.cn || r !== this.an)) this.en.tn();
|
|
6563
6516
|
}
|
|
6564
6517
|
}
|
|
6565
6518
|
|
|
6566
6519
|
class CollectionQueue extends Queue {
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6520
|
+
Tn;
|
|
6521
|
+
Bt=new Set;
|
|
6522
|
+
pn;
|
|
6523
|
+
Zt=true;
|
|
6524
|
+
zt=signal(false, {
|
|
6572
6525
|
ownedWrite: true,
|
|
6573
|
-
|
|
6526
|
+
gt: true
|
|
6574
6527
|
});
|
|
6575
|
-
|
|
6576
|
-
|
|
6528
|
+
me;
|
|
6529
|
+
Jt=signal(false, {
|
|
6577
6530
|
ownedWrite: true,
|
|
6578
|
-
|
|
6531
|
+
gt: true
|
|
6579
6532
|
});
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6533
|
+
nn;
|
|
6534
|
+
rn=false;
|
|
6535
|
+
_n;
|
|
6536
|
+
On=ON_INIT;
|
|
6584
6537
|
constructor(e) {
|
|
6585
6538
|
super();
|
|
6586
|
-
this.
|
|
6539
|
+
this.Tn = e;
|
|
6587
6540
|
}
|
|
6588
6541
|
run(e) {
|
|
6589
|
-
if (!e || read(this.
|
|
6542
|
+
if (!e || read(this.zt) && (!_revealUsed || read(this.Jt))) return;
|
|
6590
6543
|
return super.run(e);
|
|
6591
6544
|
}
|
|
6592
6545
|
notify(e, t, n, r) {
|
|
6593
|
-
if (!(t & this.
|
|
6594
|
-
if (this.
|
|
6546
|
+
if (!(t & this.Tn)) return super.notify(e, t, n, r);
|
|
6547
|
+
if (this.rn && this._n) {
|
|
6595
6548
|
const e = untrack(() => {
|
|
6596
6549
|
try {
|
|
6597
|
-
return this.
|
|
6550
|
+
return this._n();
|
|
6598
6551
|
} catch {
|
|
6599
6552
|
return ON_INIT;
|
|
6600
6553
|
}
|
|
6601
6554
|
});
|
|
6602
|
-
if (e !== this.
|
|
6603
|
-
this.
|
|
6604
|
-
this.
|
|
6605
|
-
this.
|
|
6555
|
+
if (e !== this.On) {
|
|
6556
|
+
this.On = e;
|
|
6557
|
+
this.rn = false;
|
|
6558
|
+
this.Bt.clear();
|
|
6606
6559
|
}
|
|
6607
6560
|
}
|
|
6608
6561
|
// Routing is dimension-independent: each boundary consumes only its own
|
|
@@ -6615,43 +6568,47 @@ class CollectionQueue extends Queue {
|
|
|
6615
6568
|
// dimension, while a status already caught by an inner boundary arrives with
|
|
6616
6569
|
// its dimension consumed from the mask and is correctly not re-routed
|
|
6617
6570
|
// (the Loading > Errored > content composition escape, #2856).
|
|
6618
|
-
if (this.
|
|
6619
|
-
if (n & this.
|
|
6620
|
-
this.
|
|
6621
|
-
const t = r?.source || e.
|
|
6571
|
+
if (this.Tn & STATUS_PENDING && this.rn) return super.notify(e, t, n, r);
|
|
6572
|
+
if (n & this.Tn) {
|
|
6573
|
+
this.Zt = true;
|
|
6574
|
+
const t = r?.source || e.me?.source;
|
|
6622
6575
|
if (t) {
|
|
6623
|
-
const e = this.
|
|
6624
|
-
this.
|
|
6625
|
-
if (e) setSignal(this.
|
|
6626
|
-
if (this.
|
|
6627
|
-
setSignal(this.
|
|
6576
|
+
const e = this.Bt.size === 0;
|
|
6577
|
+
this.Bt.add(t);
|
|
6578
|
+
if (e) setSignal(this.zt, true);
|
|
6579
|
+
if (this.Tn & STATUS_ERROR) {
|
|
6580
|
+
setSignal(this.me, unwrapStatusError(t.me));
|
|
6628
6581
|
}
|
|
6629
6582
|
}
|
|
6630
6583
|
}
|
|
6631
|
-
t &= ~this.
|
|
6584
|
+
t &= ~this.Tn;
|
|
6632
6585
|
return t ? super.notify(e, t, n, r) : true;
|
|
6633
6586
|
}
|
|
6634
|
-
|
|
6635
|
-
for (const e of this.
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6587
|
+
je() {
|
|
6588
|
+
for (const e of this.Bt) {
|
|
6589
|
+
// A source with a live affects() mark holds display state for the
|
|
6590
|
+
// mark's lifetime (the visual channel): the marked node carries no
|
|
6591
|
+
// status of its own, so the count is the liveness test. The release
|
|
6592
|
+
// sweep (finalizePureQueue after mark release) re-runs this check.
|
|
6593
|
+
if (e.Le & REACTIVE_DISPOSED || !e.F && !(e.We & this.Tn) && !(this.Tn & STATUS_ERROR && e.We & STATUS_PENDING)) this.Bt.delete(e);
|
|
6594
|
+
}
|
|
6595
|
+
if (!this.Bt.size) {
|
|
6596
|
+
if (this.Tn & STATUS_PENDING && this.Zt && !this.rn && this.pn) {
|
|
6597
|
+
this.Zt = !!(this.pn.We & this.Tn);
|
|
6641
6598
|
} else {
|
|
6642
|
-
this.
|
|
6599
|
+
this.Zt = false;
|
|
6643
6600
|
}
|
|
6644
|
-
if (!this.
|
|
6645
|
-
setSignal(this.
|
|
6646
|
-
if (this.
|
|
6601
|
+
if (!this.Zt) {
|
|
6602
|
+
setSignal(this.zt, false);
|
|
6603
|
+
if (this._n) {
|
|
6647
6604
|
try {
|
|
6648
|
-
this.
|
|
6605
|
+
this.On = untrack(() => this._n());
|
|
6649
6606
|
} catch {
|
|
6650
6607
|
/* value not yet committed — _prevOn stays stale, next notify will reset */}
|
|
6651
6608
|
}
|
|
6652
6609
|
}
|
|
6653
6610
|
}
|
|
6654
|
-
if (_revealUsed) this.
|
|
6611
|
+
if (_revealUsed) this.nn?.tn();
|
|
6655
6612
|
}
|
|
6656
6613
|
}
|
|
6657
6614
|
|
|
@@ -6659,12 +6616,12 @@ function createCollectionBoundary(e, t, n, r) {
|
|
|
6659
6616
|
const i = createOwner();
|
|
6660
6617
|
if (_revealUsed) setContext(RevealControllerContext, null, i);
|
|
6661
6618
|
const o = new CollectionQueue(e);
|
|
6662
|
-
if (e === STATUS_ERROR) o.
|
|
6619
|
+
if (e === STATUS_ERROR) o.me = signal(undefined, {
|
|
6663
6620
|
ownedWrite: true,
|
|
6664
|
-
|
|
6621
|
+
gt: true
|
|
6665
6622
|
});
|
|
6666
|
-
if (r) o.
|
|
6667
|
-
const s = o.
|
|
6623
|
+
if (r) o._n = r;
|
|
6624
|
+
const s = o.pn = createBoundChildren(i, t, o, e);
|
|
6668
6625
|
// Prime source tracking so reveal registration sees pending sources.
|
|
6669
6626
|
untrack(() => {
|
|
6670
6627
|
let t = false;
|
|
@@ -6673,23 +6630,23 @@ function createCollectionBoundary(e, t, n, r) {
|
|
|
6673
6630
|
} catch (e) {
|
|
6674
6631
|
if (e instanceof NotReadyError) t = true; else throw e;
|
|
6675
6632
|
}
|
|
6676
|
-
o.
|
|
6633
|
+
o.Zt = t || !!(s.We & e) || s.me instanceof NotReadyError;
|
|
6677
6634
|
});
|
|
6678
6635
|
const u = _revealUsed && e === STATUS_PENDING ? getContext(RevealControllerContext) : null;
|
|
6679
6636
|
if (u) {
|
|
6680
|
-
o.
|
|
6681
|
-
u.
|
|
6682
|
-
cleanup(() => u.
|
|
6637
|
+
o.nn = u;
|
|
6638
|
+
u.Sn(o);
|
|
6639
|
+
cleanup(() => u.dn(o));
|
|
6683
6640
|
}
|
|
6684
6641
|
return accessor(computed(() => {
|
|
6685
|
-
if (!read(o.
|
|
6642
|
+
if (!read(o.zt)) {
|
|
6686
6643
|
const e = read(s);
|
|
6687
|
-
if (!untrack(() => read(o.
|
|
6644
|
+
if (!untrack(() => read(o.zt))) return o.rn = true, e;
|
|
6688
6645
|
}
|
|
6689
6646
|
// Collapsed reveal slots suppress their own output entirely; the
|
|
6690
6647
|
// renderer treats the hole as empty, so the cast never leaks to users
|
|
6691
6648
|
// outside a `createRevealOrder` scope.
|
|
6692
|
-
if (_revealUsed && read(o.
|
|
6649
|
+
if (_revealUsed && read(o.Jt)) return undefined;
|
|
6693
6650
|
return n(o);
|
|
6694
6651
|
},
|
|
6695
6652
|
// Boundary structure, not a user source: its value is fallback-or-content and
|
|
@@ -6697,7 +6654,7 @@ function createCollectionBoundary(e, t, n, r) {
|
|
|
6697
6654
|
// by snapshot capture. The tree no longer carries foreign status flags, so
|
|
6698
6655
|
// capture can't rely on PENDING to skip this node the way it used to.
|
|
6699
6656
|
{
|
|
6700
|
-
|
|
6657
|
+
gt: true
|
|
6701
6658
|
}));
|
|
6702
6659
|
}
|
|
6703
6660
|
|
|
@@ -6750,8 +6707,8 @@ function createCollectionBoundary(e, t, n, r) {
|
|
|
6750
6707
|
* }
|
|
6751
6708
|
* ```
|
|
6752
6709
|
*/ function createErrorBoundary(e, t) {
|
|
6753
|
-
return createCollectionBoundary(STATUS_ERROR, e, e => t(accessor(e.
|
|
6754
|
-
for (const t of e.
|
|
6710
|
+
return createCollectionBoundary(STATUS_ERROR, e, e => t(accessor(e.me), () => {
|
|
6711
|
+
for (const t of e.Bt) recompute(t);
|
|
6755
6712
|
schedule();
|
|
6756
6713
|
}));
|
|
6757
6714
|
}
|
|
@@ -6808,12 +6765,12 @@ function createCollectionBoundary(e, t, n, r) {
|
|
|
6808
6765
|
computed(() => {
|
|
6809
6766
|
i();
|
|
6810
6767
|
o();
|
|
6811
|
-
s.
|
|
6768
|
+
s.tn();
|
|
6812
6769
|
});
|
|
6813
6770
|
if (r) {
|
|
6814
|
-
s.
|
|
6815
|
-
r.
|
|
6816
|
-
cleanup(() => r.
|
|
6771
|
+
s.en = r;
|
|
6772
|
+
r.Sn(s);
|
|
6773
|
+
cleanup(() => r.dn(s));
|
|
6817
6774
|
}
|
|
6818
6775
|
return t;
|
|
6819
6776
|
});
|