@solidjs/signals 2.0.0-rc.10 → 2.0.0-rc.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dev-shared.js +128 -60
- package/dist/dev.attribution.js +27 -12
- package/dist/dev.js +242 -32
- package/dist/observe/core/attribution.js +36 -21
- package/dist/observe/core/constants.js +21 -12
- package/dist/observe/core/core.js +44 -34
- package/dist/observe/core/dev.js +38 -15
- package/dist/observe/core/optimistic.js +65 -23
- package/dist/observe/core/owner.js +11 -11
- package/dist/observe/core/scheduler.js +26 -12
- package/dist/observe/core/verdict.js +2 -2
- package/dist/observe/map.js +241 -218
- package/dist/observe/store/next/store.js +159 -31
- package/dist/prod/core/constants.js +21 -12
- package/dist/prod/core/core.js +27 -17
- package/dist/prod/core/optimistic.js +62 -20
- package/dist/prod/core/owner.js +2 -2
- package/dist/prod/core/scheduler.js +29 -15
- package/dist/prod/core/verdict.js +1 -1
- package/dist/prod/map.js +200 -177
- package/dist/prod/store/next/optimistic.js +2 -2
- package/dist/prod/store/next/store.js +201 -73
- package/dist/types/core/attribution-hooks.d.ts +28 -0
- package/dist/types/core/attribution.d.ts +9 -0
- package/dist/types/core/constants.d.ts +21 -12
- package/dist/types/core/dev.d.ts +59 -6
- package/dist/types/core/index.d.ts +1 -1
- package/dist/types/core/scheduler.d.ts +5 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/signals.d.ts +18 -1
- package/dist/types/store/next/store.d.ts +4 -2
- package/package.json +1 -1
|
@@ -105,10 +105,10 @@ function releaseSnapshotScope(e) {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
function releaseSubtree(e) {
|
|
108
|
-
let t = e.
|
|
108
|
+
let t = e.On;
|
|
109
109
|
while (t) {
|
|
110
110
|
if (t.ot) {
|
|
111
|
-
t = t.
|
|
111
|
+
t = t._n;
|
|
112
112
|
continue;
|
|
113
113
|
}
|
|
114
114
|
if (t.Se) {
|
|
@@ -122,7 +122,7 @@ function releaseSubtree(e) {
|
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
releaseSubtree(t);
|
|
125
|
-
t = t.
|
|
125
|
+
t = t._n;
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
@@ -155,8 +155,8 @@ function recompute(e, t = false) {
|
|
|
155
155
|
let i = false;
|
|
156
156
|
if (attrHooks !== null) attrHooks.recomputeStart(e, t);
|
|
157
157
|
// Lane posture is resolved BEFORE the previous frame is parked below: a
|
|
158
|
-
// lane pass
|
|
159
|
-
// the decision must be known there. `lane` is applied to
|
|
158
|
+
// lane pass parks a lane frame, not a transaction zombie (#3662, #3698;
|
|
159
|
+
// see the parking site), so the decision must be known there. `lane` is applied to
|
|
160
160
|
// `currentOptimisticLane` further down, once the previous posture is saved.
|
|
161
161
|
let l = !!(e.oe & REACTIVE_OPTIMISTIC_DIRTY);
|
|
162
162
|
let u = null;
|
|
@@ -204,29 +204,39 @@ function recompute(e, t = false) {
|
|
|
204
204
|
// Tracked effects run after finalizePureQueue, so dispose immediately
|
|
205
205
|
// instead of deferring. Children built by an uncommitted recompute
|
|
206
206
|
// (CONFIG_HELD_CHILDREN) die immediately too: no frame ever showed them.
|
|
207
|
-
// Everything else is the committed frame's
|
|
208
|
-
//
|
|
209
|
-
//
|
|
210
|
-
//
|
|
211
|
-
//
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
// the
|
|
217
|
-
//
|
|
218
|
-
//
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
|
|
207
|
+
// Everything else is the committed frame's. Which frame retires it is
|
|
208
|
+
// decided by who owns the pass, never by the node's kind (A15 lane work
|
|
209
|
+
// and transaction work, ruled 2026-09-28, #3698). Transaction work — a
|
|
210
|
+
// pass under a held transaction — defers them as zombies until this
|
|
211
|
+
// node's commit, a transaction-owned node included (#3404): a parked
|
|
212
|
+
// node's children predate the hold, and tearing them down when the
|
|
213
|
+
// source lands ran cleanups before the transaction's atomic reveal.
|
|
214
|
+
// Lane work — a pass over a lane, effect or memo alike — parks a LANE
|
|
215
|
+
// frame instead (CONFIG_LANE_FRAME, #3662, #3698): the frame it replaces
|
|
216
|
+
// leaves the screen when the lane's queue applies this pass (A30) — not
|
|
217
|
+
// at the action's commit — and a held lane defers that with the frame
|
|
218
|
+
// still displayed. The drain is the lane's first render entry for this
|
|
219
|
+
// pass, pushed before the pass builds the new frame: cleanups before
|
|
220
|
+
// side effects — the retired frame's `onCleanup`s run ahead of every
|
|
221
|
+
// effect callback of the new frame. While the frame waits, the live
|
|
222
|
+
// children were never shown: a superseding pass disposes them here like
|
|
223
|
+
// held children, and the parked frame stays. Zombies exist for
|
|
224
|
+
// transaction work only: #3662 gated the lane frame on `isEffect`, and a
|
|
225
|
+
// memo's lane pass (its value a derived override, A17) parked a
|
|
226
|
+
// transaction zombie that made the memo a pending node of the action,
|
|
227
|
+
// stamped it, and sent its next mainline recompute through the
|
|
228
|
+
// stamped-memo arm above — a `Show` whose `when` getter owns a
|
|
229
|
+
// compiler-emitted memo held an unrelated sync write for the action's
|
|
230
|
+
// lifetime, against the #3460 ruling that a lane never holds a sync write.
|
|
231
|
+
if (n === EFFECT_TRACKED || e.C & (CONFIG_HELD_CHILDREN | CONFIG_LANE_FRAME)) disposeChildren(e); else if (e.On !== null || e.Ve !== null) {
|
|
222
232
|
markDisposal(e);
|
|
223
233
|
const t = ext(e);
|
|
224
234
|
t.Et = e.Ve;
|
|
225
|
-
t.dt = e.
|
|
235
|
+
t.dt = e.On;
|
|
226
236
|
e.Ve = null;
|
|
227
|
-
e.
|
|
237
|
+
e.On = null;
|
|
228
238
|
e.Nt = 0;
|
|
229
|
-
if (
|
|
239
|
+
if (u) {
|
|
230
240
|
e.C |= CONFIG_LANE_FRAME;
|
|
231
241
|
findLane(u).It[0].push(() => {
|
|
232
242
|
e.C &= ~CONFIG_LANE_FRAME;
|
|
@@ -798,9 +808,9 @@ function computed(e, t) {
|
|
|
798
808
|
u: null,
|
|
799
809
|
Ht: null,
|
|
800
810
|
_parent: context,
|
|
801
|
-
Fn: null,
|
|
802
|
-
kt: null,
|
|
803
811
|
_n: null,
|
|
812
|
+
kt: null,
|
|
813
|
+
On: null,
|
|
804
814
|
oe: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
|
|
805
815
|
S: i ? 0 : STATUS_UNINITIALIZED,
|
|
806
816
|
Ge: clock,
|
|
@@ -880,9 +890,9 @@ function computed(e, t) {
|
|
|
880
890
|
u: null,
|
|
881
891
|
Ht: null,
|
|
882
892
|
_parent: context,
|
|
883
|
-
Fn: null,
|
|
884
|
-
kt: null,
|
|
885
893
|
_n: null,
|
|
894
|
+
kt: null,
|
|
895
|
+
On: null,
|
|
886
896
|
oe: REACTIVE_LAZY,
|
|
887
897
|
S: STATUS_UNINITIALIZED,
|
|
888
898
|
Ge: clock,
|
|
@@ -1398,7 +1408,7 @@ function enterStagedRead(e, t = e.me) {
|
|
|
1398
1408
|
* False means the reader derives from the staged value and enters its
|
|
1399
1409
|
* transaction (enterStagedRead, A29).
|
|
1400
1410
|
*/ function readerSeesCommitted(e, t, n, i) {
|
|
1401
|
-
return !!(!t || currentOptimisticLane !== null && GlobalQueue.
|
|
1411
|
+
return !!(!t || currentOptimisticLane !== null && GlobalQueue.An(e, n, t) || e._e === NOT_PENDING || t.C & CONFIG_CHILDREN_FORBIDDEN || stale && !i && heldFromStale(e, t) || e.C & CONFIG_HELD_TRUTH && currentOptimisticLane !== null && !latestReadActive && !(t.C & CONFIG_AUTHORITATIVE_READ));
|
|
1402
1412
|
}
|
|
1403
1413
|
|
|
1404
1414
|
/** A28 — set when a node is staged (queuePendingNode) or a held node rewritten
|
|
@@ -1522,7 +1532,7 @@ function read(e) {
|
|
|
1522
1532
|
// Checked before isPending so that isPending(() => latest(x)) checks
|
|
1523
1533
|
// the _pendingSignal of _latestValueComputed (async in flight) rather
|
|
1524
1534
|
// than the original node (which stays "pending" while held in a transition).
|
|
1525
|
-
if (latestReadActive) return GlobalQueue.
|
|
1535
|
+
if (latestReadActive) return GlobalQueue.Cn(e);
|
|
1526
1536
|
let t = context;
|
|
1527
1537
|
if (t?.Zt) t = t.Yt;
|
|
1528
1538
|
const n = e;
|
|
@@ -1532,7 +1542,7 @@ function read(e) {
|
|
|
1532
1542
|
// Probe mode is suspended while preparing the node so nested reads during a
|
|
1533
1543
|
// recompute don't collect into the probe.
|
|
1534
1544
|
if (pendingCheckActive) {
|
|
1535
|
-
GlobalQueue.
|
|
1545
|
+
GlobalQueue.Rn(e, t, l, i);
|
|
1536
1546
|
} else if (typeof n.Se === "function") {
|
|
1537
1547
|
prepareComputed(e, false);
|
|
1538
1548
|
}
|
|
@@ -1584,13 +1594,13 @@ function read(e) {
|
|
|
1584
1594
|
// (firewall-backed store reads always did; plain memos since the #3043
|
|
1585
1595
|
// port): falling through served `undefined` as if settled and stranded
|
|
1586
1596
|
// the reader outside both transactions, so it never re-ran.
|
|
1587
|
-
if (t && !(stale && !(l.S & STATUS_UNINITIALIZED) && !(l.C & CONFIG_INPUTS_PUBLISHED) && !(l.C & CONFIG_HAS_LANE && GlobalQueue.
|
|
1597
|
+
if (t && !(stale && !(l.S & STATUS_UNINITIALIZED) && !(l.C & CONFIG_INPUTS_PUBLISHED) && !(l.C & CONFIG_HAS_LANE && GlobalQueue.Gn(l)) && heldFromStale(l, t))) {
|
|
1588
1598
|
// Per-lane suspension lives with the engine (a non-null lane implies it
|
|
1589
1599
|
// is installed): under a lane, only same-lane pending async without an
|
|
1590
1600
|
// active override throws — plus uninitialized sources regardless of
|
|
1591
1601
|
// lane (#3276); that check rides laneSuspends so floor bundles don't
|
|
1592
1602
|
// pay for it.
|
|
1593
|
-
if (currentOptimisticLane === null || GlobalQueue.
|
|
1603
|
+
if (currentOptimisticLane === null || GlobalQueue.Dn(l)) {
|
|
1594
1604
|
// An untracked read of a pending node still re-runs its reader when
|
|
1595
1605
|
// the node settles — unless nobody is reading (`spectating`, #3528)
|
|
1596
1606
|
// or the reader is the node itself.
|
|
@@ -1694,7 +1704,7 @@ function read(e) {
|
|
|
1694
1704
|
// a node whose own source answered with a DIFFERENT value hands a
|
|
1695
1705
|
// tracked reader the staged truth (A18 supersession, #3331). Untracked
|
|
1696
1706
|
// reads display the override.
|
|
1697
|
-
if (t && e.C & (CONFIG_HAS_LANE | CONFIG_OVERRIDE_SUPERSEDED)) return GlobalQueue.
|
|
1707
|
+
if (t && e.C & (CONFIG_HAS_LANE | CONFIG_OVERRIDE_SUPERSEDED)) return GlobalQueue.Pn(e, t);
|
|
1698
1708
|
return unwrapOverride(e.o?.we);
|
|
1699
1709
|
}
|
|
1700
1710
|
e.C |= CONFIG_AUTHORITATIVE_OBSERVED;
|
|
@@ -1706,7 +1716,7 @@ function read(e) {
|
|
|
1706
1716
|
// _pendingValue for correct fetching. The sub is recorded for replay at commit
|
|
1707
1717
|
// so it re-runs with the new committed view. (Gate details live with the
|
|
1708
1718
|
// engine — a non-null lane implies it is installed.)
|
|
1709
|
-
if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.
|
|
1719
|
+
if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.Fn(e, n, t)) {
|
|
1710
1720
|
return i;
|
|
1711
1721
|
}
|
|
1712
1722
|
// In optimistic lane context, return _value for optimistic/lane-assigned signals
|
package/dist/observe/core/dev.js
CHANGED
|
@@ -68,20 +68,31 @@ function recordsChannel() {
|
|
|
68
68
|
const e = globalThis;
|
|
69
69
|
if (e[RECORDS]) return e[RECORDS];
|
|
70
70
|
const n = new Map;
|
|
71
|
+
const t = new Map;
|
|
71
72
|
return e[RECORDS] = {
|
|
72
|
-
subscribe(e,
|
|
73
|
-
const
|
|
74
|
-
// Set semantics: one entry per function, however often it is passed
|
|
75
|
-
|
|
73
|
+
subscribe(e, o, r) {
|
|
74
|
+
const i = n.get(e);
|
|
75
|
+
// Set semantics: one entry per function, however often it is passed —
|
|
76
|
+
// the first subscription's options stand for it.
|
|
77
|
+
if (i === undefined || !i.includes(o)) {
|
|
78
|
+
n.set(e, i === undefined ? [ o ] : [ ...i, o ]);
|
|
79
|
+
if (r !== undefined && r.bodies) {
|
|
80
|
+
let n = t.get(e);
|
|
81
|
+
if (n === undefined) t.set(e, n = new Set);
|
|
82
|
+
n.add(o);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
76
85
|
return () => {
|
|
77
|
-
const
|
|
78
|
-
if (
|
|
79
|
-
const
|
|
80
|
-
if (
|
|
86
|
+
const r = n.get(e);
|
|
87
|
+
if (r === undefined || !r.includes(o)) return;
|
|
88
|
+
const i = r.filter(e => e !== o);
|
|
89
|
+
if (i.length > 0) n.set(e, i); else n.delete(e);
|
|
90
|
+
const s = t.get(e);
|
|
91
|
+
if (s !== undefined && s.delete(o) && s.size === 0) t.delete(e);
|
|
81
92
|
};
|
|
82
93
|
},
|
|
83
|
-
observed(e) {
|
|
84
|
-
return n.has(e);
|
|
94
|
+
observed(e, o) {
|
|
95
|
+
return o === "bodies" ? t.has(e) : n.has(e);
|
|
85
96
|
},
|
|
86
97
|
emit(e, t, o) {
|
|
87
98
|
const r = n.get(e);
|
|
@@ -108,9 +119,14 @@ const OBSERVE = {
|
|
|
108
119
|
// empty HERE and gains its members by augmentation downstream.
|
|
109
120
|
server: {},
|
|
110
121
|
exclude(e) {
|
|
111
|
-
|
|
122
|
+
markedOwners.set(e, true);
|
|
112
123
|
hasExclusions = true;
|
|
113
124
|
},
|
|
125
|
+
include(e) {
|
|
126
|
+
// No flag flip: with nothing excluded there is nothing to re-admit,
|
|
127
|
+
// and the walk stays short-circuited.
|
|
128
|
+
markedOwners.set(e, false);
|
|
129
|
+
},
|
|
114
130
|
isExcluded: isExcluded,
|
|
115
131
|
ownerPath: ownerPath
|
|
116
132
|
};
|
|
@@ -119,9 +135,13 @@ const OBSERVE = {
|
|
|
119
135
|
|
|
120
136
|
// An observer that lives inside the observed app (an adapter's panel,
|
|
121
137
|
// devtools) marks its root; both channels check the subject's owner chain —
|
|
122
|
-
// the same walk `ownerPath` already makes — and stay silent under it.
|
|
123
|
-
//
|
|
124
|
-
|
|
138
|
+
// the same walk `ownerPath` already makes — and stay silent under it. An
|
|
139
|
+
// observer that WRAPS the app (a toolbar around it) excludes its own root
|
|
140
|
+
// and includes the app's back: one map, `true` excluded / `false` included,
|
|
141
|
+
// and the nearest marker up the chain decides. The flag short-circuits the
|
|
142
|
+
// walk for the common case of no exclusions (an include with nothing
|
|
143
|
+
// excluded changes no verdict, so it does not flip it).
|
|
144
|
+
const markedOwners = new WeakMap;
|
|
125
145
|
|
|
126
146
|
let hasExclusions = false;
|
|
127
147
|
|
|
@@ -130,7 +150,10 @@ let hasExclusions = false;
|
|
|
130
150
|
function isExcluded(e) {
|
|
131
151
|
if (!hasExclusions || !e) return false;
|
|
132
152
|
let n = "_parent" in e ? e : e.ie ?? null;
|
|
133
|
-
for (;n !== null; n = n._parent)
|
|
153
|
+
for (;n !== null; n = n._parent) {
|
|
154
|
+
const e = markedOwners.get(n);
|
|
155
|
+
if (e !== undefined) return e;
|
|
156
|
+
}
|
|
134
157
|
return false;
|
|
135
158
|
}
|
|
136
159
|
|
|
@@ -2,7 +2,7 @@ import { NOT_PENDING, unwrapOverride, STATUS_UNINITIALIZED, REACTIVE_DIRTY, REAC
|
|
|
2
2
|
|
|
3
3
|
import { attrHooks } from "./attribution-hooks.js";
|
|
4
4
|
|
|
5
|
-
import { ext, hasActiveOverride, stale, currentOptimisticLane, enterStagedRead, latestReadActive } from "./core.js";
|
|
5
|
+
import { ext, hasActiveOverride, stale, currentOptimisticLane, enterStagedRead, setSignal, latestReadActive } from "./core.js";
|
|
6
6
|
|
|
7
7
|
import { NotReadyError } from "./error.js";
|
|
8
8
|
|
|
@@ -27,17 +27,17 @@ import { GlobalQueue, activeTransition, globalQueue, origin, clock, insertSubs,
|
|
|
27
27
|
*/
|
|
28
28
|
/** The optimistic half of setSignal, fired when `_overrideValue !== undefined`. */ function optimisticWrite(e, n) {
|
|
29
29
|
const i = e.o?.we !== NOT_PENDING;
|
|
30
|
-
const
|
|
31
|
-
if (typeof n === "function") n = n(
|
|
32
|
-
const
|
|
30
|
+
const r = i ? unwrapOverride(e.o?.we) : e.ce;
|
|
31
|
+
if (typeof n === "function") n = n(r);
|
|
32
|
+
const t = !!(e.S & STATUS_UNINITIALIZED) ||
|
|
33
33
|
// A dirty node's _value is stale (its queued recompute hasn't run — e.g.
|
|
34
34
|
// a latest() shadow marked by the previous landing's companion snap), so
|
|
35
35
|
// equality against it must not swallow the write. Without this, a sync
|
|
36
36
|
// push returning the shadow to that stale value was dropped, the snap
|
|
37
37
|
// recompute then committed the parent's old value, and the banner showed
|
|
38
38
|
// the previous transition's target (#3041 follow-up).
|
|
39
|
-
!!((e.oe ?? 0) & (REACTIVE_DIRTY | REACTIVE_CHECK)) || !e.Fe || !e.Fe(
|
|
40
|
-
if (!
|
|
39
|
+
!!((e.oe ?? 0) & (REACTIVE_DIRTY | REACTIVE_CHECK)) || !e.Fe || !e.Fe(r, n);
|
|
40
|
+
if (!t) {
|
|
41
41
|
// Same-value write with an active override still entangles the current
|
|
42
42
|
// action's transition — the hold must outlast all overlapping actions —
|
|
43
43
|
// and renews the override's PROVENANCE: the newer action re-asks the
|
|
@@ -164,7 +164,7 @@ function resolveOptimisticNodes(e) {
|
|
|
164
164
|
// override-covered authoritative values hold in _pendingValue and
|
|
165
165
|
// elevate on their OWN transition's schedule (A18 as re-ruled 2026-07-07).
|
|
166
166
|
if (!(n.S & STATUS_PENDING)) n.S &= ~STATUS_UNINITIALIZED;
|
|
167
|
-
const
|
|
167
|
+
const r = n.o?.we;
|
|
168
168
|
// A derived override (lanes stage, #3479) has no truth of its own: the
|
|
169
169
|
// slot disarms — the memo is plain again — and the override PROMOTES to
|
|
170
170
|
// `_value`. Not superseded, nothing it derives from told it otherwise
|
|
@@ -174,18 +174,18 @@ function resolveOptimisticNodes(e) {
|
|
|
174
174
|
// the override IS what a recompute from the truth yields. Re-deriving
|
|
175
175
|
// instead re-asked an async member's flight and held the transaction on
|
|
176
176
|
// it — a waterfall after the reveal.
|
|
177
|
-
const
|
|
178
|
-
ext(n).we =
|
|
177
|
+
const t = n.C & CONFIG_DERIVED_OVERRIDE;
|
|
178
|
+
ext(n).we = t && !(n.C & CONFIG_OPTIMISTIC) ? undefined : NOT_PENDING;
|
|
179
179
|
// A superseded override's subscribers already re-derived from the truth
|
|
180
180
|
// when it arrived (#3331) — the drop changes nothing they read. Everyone
|
|
181
181
|
// else learns of the correction here: this drop IS their notification.
|
|
182
182
|
const u = (n.C & CONFIG_OVERRIDE_SUPERSEDED) !== 0;
|
|
183
183
|
n.C &= -8912897;
|
|
184
|
-
if (!u &&
|
|
185
|
-
if (
|
|
184
|
+
if (!u && r !== NOT_PENDING && n.ce !== unwrapOverride(r)) {
|
|
185
|
+
if (t) n.ce = unwrapOverride(r); else {
|
|
186
186
|
// The guess lifts and what was beneath it differs: the screen
|
|
187
187
|
// changes from the override to the committed value.
|
|
188
|
-
if (attrHooks !== null) attrHooks.optimisticReverted(n, unwrapOverride(
|
|
188
|
+
if (attrHooks !== null) attrHooks.optimisticReverted(n, unwrapOverride(r), n.ce, "reverted");
|
|
189
189
|
insertSubs(n, true);
|
|
190
190
|
}
|
|
191
191
|
}
|
|
@@ -198,8 +198,8 @@ function resolveOptimisticNodes(e) {
|
|
|
198
198
|
for (let i = 0; i < n; i++) {
|
|
199
199
|
const n = e[i];
|
|
200
200
|
if (n.o?.xe || n.o?.Me) GlobalQueue.Qn(n);
|
|
201
|
-
const
|
|
202
|
-
if (
|
|
201
|
+
const r = n.o?.$n;
|
|
202
|
+
if (r && (r.o?.xe === n || r.o?.Me === n)) GlobalQueue.Qn(r);
|
|
203
203
|
}
|
|
204
204
|
e.splice(0, n);
|
|
205
205
|
}
|
|
@@ -257,14 +257,14 @@ function resolveOptimisticNodes(e) {
|
|
|
257
257
|
const i = e.o?.Ce;
|
|
258
258
|
if (i) {
|
|
259
259
|
const n = findLane(i);
|
|
260
|
-
const
|
|
261
|
-
while (
|
|
262
|
-
const e =
|
|
260
|
+
const r = [ e ];
|
|
261
|
+
while (r.length) {
|
|
262
|
+
const e = r.pop();
|
|
263
263
|
const i = e.o?.Ce;
|
|
264
264
|
if (!i || findLane(i) !== n) continue;
|
|
265
265
|
e.o.Ce = undefined;
|
|
266
266
|
n.ke.delete(e);
|
|
267
|
-
for (let n = e.u; n !== null; n = n.Pe)
|
|
267
|
+
for (let n = e.u; n !== null; n = n.Pe) r.push(n.Ae);
|
|
268
268
|
}
|
|
269
269
|
}
|
|
270
270
|
}
|
|
@@ -403,7 +403,49 @@ function resolveOptimisticNodes(e) {
|
|
|
403
403
|
* action provenance, or an A17-silent confirmation (#3331). Before this the
|
|
404
404
|
* landing took setSignal's plain path: a differing truth staged silently
|
|
405
405
|
* under the override and the graph never moved until the commit.
|
|
406
|
+
*
|
|
407
|
+
* Slot arm (list-matrix F1): `mapArray`'s writes to its per-slot signals —
|
|
408
|
+
* the row accessors of index mode, the index accessors of keyed mode; never
|
|
409
|
+
* CONFIG_OPTIMISTIC, which is how the arm tells them from the store landings
|
|
410
|
+
* above — once a lane pass has run over the map. The list's frame lives in
|
|
411
|
+
* these writes as much as in the computed's result. Under a LANE pass the
|
|
412
|
+
* write is the lane's frame, as the computed's result is (lanes stage,
|
|
413
|
+
* `laneOverride`): the slot joins the lane carrying a DERIVED override —
|
|
414
|
+
* `_value` stays the committed row, the lane's readers and untracked reads
|
|
415
|
+
* see the override (A17), an off-lane render effect sees the committed frame
|
|
416
|
+
* (#3460), the revert promotes or drops it with the lane's transaction. The
|
|
417
|
+
* gate compares against the slot the pass publishes to (INV-11): the override
|
|
418
|
+
* when armed, the committed row otherwise. A plain `setSignal` here staged
|
|
419
|
+
* the write into the ACTION's transaction: `<For>` without `keyed` showed the
|
|
420
|
+
* pre-action list for the whole action while the keyed modes showed the
|
|
421
|
+
* optimistic one. A PLAIN pass over a slot still carrying the lane's frame —
|
|
422
|
+
* the landing, the reversion, a mainline re-pass — is the truth arriving
|
|
423
|
+
* under an override and takes the landing path below: a differing truth
|
|
424
|
+
* supersedes, an equal one confirms and the revert promotes. Every pass
|
|
425
|
+
* rewrites every slot whose value differs from the previous frame's (index
|
|
426
|
+
* mode rewrites every surviving slot), so a slot the landing does NOT write
|
|
427
|
+
* holds, by construction, what the truth yields. A slot with neither a lane
|
|
428
|
+
* nor an override is the plain write.
|
|
406
429
|
*/ function landOnOverride(e, n) {
|
|
430
|
+
if (!(e.C & CONFIG_OPTIMISTIC)) {
|
|
431
|
+
const i = currentOptimisticLane;
|
|
432
|
+
if (i === null) {
|
|
433
|
+
if (!hasActiveOverride(e)) return setSignal(e, n);
|
|
434
|
+
} else {
|
|
435
|
+
// INV-11: the gate compares against the slot this pass publishes to —
|
|
436
|
+
// the override when one is armed, the committed row otherwise.
|
|
437
|
+
if (!e.Fe || !e.Fe(hasActiveOverride(e) ? unwrapOverride(e.o.we) : e.ce, n)) {
|
|
438
|
+
// Membership before the publish: `laneOverride` files the slot under
|
|
439
|
+
// the lane's transaction, and the read path routes a tracked reader
|
|
440
|
+
// of a lane member through `overrideRead` (CONFIG_HAS_LANE).
|
|
441
|
+
assignOrMergeLane(e, i);
|
|
442
|
+
laneOverride(e, n, i);
|
|
443
|
+
insertSubs(e, true);
|
|
444
|
+
schedule();
|
|
445
|
+
}
|
|
446
|
+
return n;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
407
449
|
const i = e._e === NOT_PENDING ? e.ce : e._e;
|
|
408
450
|
if (typeof n === "function") n = n(i);
|
|
409
451
|
if (attrHooks !== null) attrHooks.write(e, i, n);
|
|
@@ -611,13 +653,13 @@ function trackOptimisticStore(e) {
|
|
|
611
653
|
GlobalQueue.Kn = runLaneEffects;
|
|
612
654
|
GlobalQueue.Qe = supersedeOverride;
|
|
613
655
|
GlobalQueue.zn = endOptimism;
|
|
614
|
-
GlobalQueue.
|
|
656
|
+
GlobalQueue.Pn = overrideRead;
|
|
615
657
|
GlobalQueue.je = laneOverride;
|
|
616
658
|
GlobalQueue.Vn = landOnOverride;
|
|
617
|
-
GlobalQueue.
|
|
618
|
-
GlobalQueue.
|
|
619
|
-
GlobalQueue.
|
|
620
|
-
GlobalQueue.
|
|
659
|
+
GlobalQueue.Fn = gatedRead;
|
|
660
|
+
GlobalQueue.Dn = laneSuspends;
|
|
661
|
+
GlobalQueue.Gn = laneLive;
|
|
662
|
+
GlobalQueue.An = laneReadsCommitted;
|
|
621
663
|
GlobalQueue._t = recomputeLane;
|
|
622
664
|
GlobalQueue.Ot = laneAsyncPending;
|
|
623
665
|
GlobalQueue.St = laneAsyncSettled;
|
|
@@ -14,7 +14,7 @@ const PENDING_OWNER = {};
|
|
|
14
14
|
|
|
15
15
|
// Dummy owner to trigger store's read() path
|
|
16
16
|
function markDisposal(e) {
|
|
17
|
-
let n = e.
|
|
17
|
+
let n = e.On;
|
|
18
18
|
while (n) {
|
|
19
19
|
const e = n.oe;
|
|
20
20
|
n.oe = e | REACTIVE_ZOMBIE;
|
|
@@ -28,7 +28,7 @@ function markDisposal(e) {
|
|
|
28
28
|
if (e & REACTIVE_IN_HEAP) insertIntoHeap(n, zombieQueue); else insertIntoHeapHeight(n, zombieQueue);
|
|
29
29
|
}
|
|
30
30
|
markDisposal(n);
|
|
31
|
-
n = n.
|
|
31
|
+
n = n._n;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -76,8 +76,8 @@ function disposeChildren(e, n = false, t) {
|
|
|
76
76
|
schedule();
|
|
77
77
|
}
|
|
78
78
|
if (n && e.Se && e.o !== null) e.o.pe = null;
|
|
79
|
-
let o = t ? e.o?.dt ?? null : e.
|
|
80
|
-
if (!t) e.
|
|
79
|
+
let o = t ? e.o?.dt ?? null : e.On;
|
|
80
|
+
if (!t) e.On = null;
|
|
81
81
|
while (o) {
|
|
82
82
|
const e = o;
|
|
83
83
|
// Owner teardown is death regardless of the child's own lifecycle
|
|
@@ -107,7 +107,7 @@ function disposeChildren(e, n = false, t) {
|
|
|
107
107
|
// itself out of the detached chain, and a cleanup may then have linked
|
|
108
108
|
// it at the fresh head, which rewrote the `_nextSibling` a pre-read
|
|
109
109
|
// would still be holding.
|
|
110
|
-
o = o.
|
|
110
|
+
o = o._n;
|
|
111
111
|
}
|
|
112
112
|
if (t) {
|
|
113
113
|
if (e.o !== null) e.o.dt = null;
|
|
@@ -118,8 +118,8 @@ function disposeChildren(e, n = false, t) {
|
|
|
118
118
|
// walks that already advanced past us still reach later siblings.
|
|
119
119
|
if (n && !t && !(i & REACTIVE_ZOMBIE) && e._parent !== null && !(e._parent.oe & REACTIVE_DISPOSED)) {
|
|
120
120
|
const n = e.kt;
|
|
121
|
-
const t = e.
|
|
122
|
-
if (n !== null) n.
|
|
121
|
+
const t = e._n;
|
|
122
|
+
if (n !== null) n._n = t; else e._parent.On = t;
|
|
123
123
|
if (t !== null) t.kt = n;
|
|
124
124
|
e.kt = null;
|
|
125
125
|
}
|
|
@@ -134,11 +134,11 @@ function disposeChildren(e, n = false, t) {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
function linkChild(e, n) {
|
|
137
|
-
const t = e.
|
|
137
|
+
const t = e.On;
|
|
138
138
|
n.kt = null;
|
|
139
|
-
n.
|
|
139
|
+
n._n = t;
|
|
140
140
|
if (t !== null) t.kt = n;
|
|
141
|
-
e.
|
|
141
|
+
e.On = n;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
function runDisposal(e, n) {
|
|
@@ -295,8 +295,8 @@ function disposeRootSelf(e = true) {
|
|
|
295
295
|
C: t ? CONFIG_TRANSPARENT : 0,
|
|
296
296
|
Zt: true,
|
|
297
297
|
Yt: n?.Zt ? n.Yt : n,
|
|
298
|
+
On: null,
|
|
298
299
|
_n: null,
|
|
299
|
-
Fn: null,
|
|
300
300
|
kt: null,
|
|
301
301
|
Ve: null,
|
|
302
302
|
T: n?.T ?? globalQueue,
|
|
@@ -554,8 +554,8 @@ class GlobalQueue extends Queue {
|
|
|
554
554
|
static Ue=null;
|
|
555
555
|
static qe=null;
|
|
556
556
|
static Qn=null;
|
|
557
|
-
static An=null;
|
|
558
557
|
static Cn=null;
|
|
558
|
+
static Rn=null;
|
|
559
559
|
static Hn=null;
|
|
560
560
|
static At=null;
|
|
561
561
|
static k=null;
|
|
@@ -579,14 +579,14 @@ class GlobalQueue extends Queue {
|
|
|
579
579
|
* apply at lane-effect timing — visible in flight, unlike the regular
|
|
580
580
|
* effect queues an action stashes. Injected; null when unused. */
|
|
581
581
|
static Zn=null;
|
|
582
|
-
static
|
|
583
|
-
static
|
|
582
|
+
static Fn=null;
|
|
583
|
+
static Dn=null;
|
|
584
584
|
/** Is the node routed through a LIVE lane (`resolveLane`)? read()'s reveal
|
|
585
585
|
* carve-out asks before showing a foreign-held pending node's committed
|
|
586
586
|
* value: a lane-derived flight's inputs are already revealed through the
|
|
587
587
|
* lane (#3334). Gated on CONFIG_HAS_LANE, which only the engine sets. */
|
|
588
|
-
static
|
|
589
|
-
static
|
|
588
|
+
static Gn=null;
|
|
589
|
+
static An=null;
|
|
590
590
|
static _t=null;
|
|
591
591
|
static Ot=null;
|
|
592
592
|
/** Authoritative-view reader wakeup: installed by until() and refresh() before
|
|
@@ -618,7 +618,7 @@ class GlobalQueue extends Queue {
|
|
|
618
618
|
* transaction's committed value over its staged write. */
|
|
619
619
|
/** A tracked read of an active override: the lane outside-view rule
|
|
620
620
|
* (#3460) and the A18 supersession selection (#3331) — see optimistic.ts. */
|
|
621
|
-
static
|
|
621
|
+
static Pn=null;
|
|
622
622
|
/** A lane pass's publish for a memo (#3479, lanes stage): the speculative
|
|
623
623
|
* result becomes a DERIVED override, `_value` stays committed — see
|
|
624
624
|
* optimistic.ts laneOverride. Set with the engine, which a lane implies. */
|
|
@@ -631,7 +631,11 @@ class GlobalQueue extends Queue {
|
|
|
631
631
|
* commit whatever its relation to the committed value — a landing equal to
|
|
632
632
|
* committed still differs from the override — then _supersedeOverride
|
|
633
633
|
* decides. Installed with the optimistic engine; only reachable on a node
|
|
634
|
-
* that has an override
|
|
634
|
+
* that has an override — or, from `mapArray` once a lane pass has run over
|
|
635
|
+
* the map, on a per-slot signal (never CONFIG_OPTIMISTIC): the slot arm
|
|
636
|
+
* publishes a lane pass's write as the slot's derived override, lands a
|
|
637
|
+
* plain pass's write over one, and is the plain `setSignal` otherwise (F1,
|
|
638
|
+
* see optimistic.ts landOnOverride). */
|
|
635
639
|
static Vn=null;
|
|
636
640
|
static Jn=null;
|
|
637
641
|
flush() {
|
|
@@ -793,8 +797,13 @@ class GlobalQueue extends Queue {
|
|
|
793
797
|
// sweep or recompute wrote a node it owns): effects computed under it
|
|
794
798
|
// since are its to apply, not this flush's — runEffect leaves them queued
|
|
795
799
|
// and the next pass parks them with it (#3319). Everything computed
|
|
796
|
-
// mainline applies now.
|
|
797
|
-
|
|
800
|
+
// mainline applies now. A write the finalize staged in the ambient
|
|
801
|
+
// batch with no subscriber to dirty (an optimistic store settle's
|
|
802
|
+
// keyset bump under a reader that never tracks the key set) is work
|
|
803
|
+
// too — the fast drain and the park exit already count it — so the
|
|
804
|
+
// next round commits it and the woken re-entry below does not adopt
|
|
805
|
+
// it into a parked transaction it never belonged to (matrix F6).
|
|
806
|
+
scheduled = dirtyQueue.EE >= dirtyQueue.st || activeTransition !== null || this.m.In.length !== 0;
|
|
798
807
|
// Run lane effects first (for ready lanes), then regular effects
|
|
799
808
|
activeLanes.size && GlobalQueue.Kn(EFFECT_RENDER);
|
|
800
809
|
this.run(EFFECT_RENDER);
|
|
@@ -808,9 +817,14 @@ class GlobalQueue extends Queue {
|
|
|
808
817
|
// idle pass: entering adopts the ambient batch, and staged or dirty
|
|
809
818
|
// ambient work would be held behind flights it never read. `scheduled`
|
|
810
819
|
// is that test here — after the park exit as well as the normal one:
|
|
811
|
-
// it was recomputed from the heap
|
|
812
|
-
//
|
|
813
|
-
//
|
|
820
|
+
// it was recomputed from the heap and the ambient batch's staged nodes
|
|
821
|
+
// this pass, every write since re-armed it, and optimistic ambient
|
|
822
|
+
// nodes reverted with the finalize — so a wake in a pass with work
|
|
823
|
+
// simply falls to the next. (A staged node with no subscriber — the
|
|
824
|
+
// finalize's keyset bump under a length-only reader — used to be
|
|
825
|
+
// missed here: the wake adopted it, stamped it, and a later ambient
|
|
826
|
+
// write to the same node joined the parked transaction and never
|
|
827
|
+
// reverted; matrix F6.) Entering re-arms
|
|
814
828
|
// it itself; a dead (completed) wake is a bare return in
|
|
815
829
|
// initTransition, and the loop moves on to the next.
|
|
816
830
|
while (!scheduled && !activeTransition && wokenTransitions.length) this.initTransition(wokenTransitions.pop());
|
|
@@ -657,9 +657,9 @@ GlobalQueue.qe = updateChildCompanions;
|
|
|
657
657
|
|
|
658
658
|
GlobalQueue.Qn = snapCompanionsToState;
|
|
659
659
|
|
|
660
|
-
GlobalQueue.
|
|
660
|
+
GlobalQueue.Cn = latestRead;
|
|
661
661
|
|
|
662
|
-
GlobalQueue.
|
|
662
|
+
GlobalQueue.Rn = pendingCheckRead;
|
|
663
663
|
|
|
664
664
|
GlobalQueue.Hn = recordFreshRead;
|
|
665
665
|
|