@solidjs/signals 2.0.0-rc.10 → 2.0.0-rc.11
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 +28 -11
- package/dist/dev.js +79 -10
- package/dist/observe/core/optimistic.js +60 -18
- package/dist/observe/core/scheduler.js +20 -6
- package/dist/observe/map.js +241 -218
- 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/types/core/scheduler.d.ts +5 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NOT_PENDING, unwrapOverride, STATUS_UNINITIALIZED, REACTIVE_DIRTY, REACTIVE_CHECK, CONFIG_HAS_LANE, OVERRIDE_UNDEFINED, STATUS_PENDING, CONFIG_DERIVED_OVERRIDE, CONFIG_OPTIMISTIC, CONFIG_OVERRIDE_SUPERSEDED, EFFECT_RENDER, CONFIG_AUTHORITATIVE_OBSERVED, REACTIVE_MANUAL_WRITE, REACTIVE_OPTIMISTIC_DIRTY, LANE_RUN, EFFECT_USER } from "./constants.js";
|
|
2
2
|
|
|
3
|
-
import { ext, hasActiveOverride, stale, currentOptimisticLane, enterStagedRead, latestReadActive } from "./core.js";
|
|
3
|
+
import { ext, hasActiveOverride, stale, currentOptimisticLane, enterStagedRead, setSignal, latestReadActive } from "./core.js";
|
|
4
4
|
|
|
5
5
|
import { NotReadyError } from "./error.js";
|
|
6
6
|
|
|
@@ -25,17 +25,17 @@ import { GlobalQueue, activeTransition, globalQueue, origin, clock, insertSubs,
|
|
|
25
25
|
*/
|
|
26
26
|
/** The optimistic half of setSignal, fired when `_overrideValue !== undefined`. */ function optimisticWrite(e, n) {
|
|
27
27
|
const i = e.o?.Fe !== NOT_PENDING;
|
|
28
|
-
const
|
|
29
|
-
if (typeof n === "function") n = n(
|
|
30
|
-
const
|
|
28
|
+
const r = i ? unwrapOverride(e.o?.Fe) : e.ce;
|
|
29
|
+
if (typeof n === "function") n = n(r);
|
|
30
|
+
const t = !!(e.h & STATUS_UNINITIALIZED) ||
|
|
31
31
|
// A dirty node's _value is stale (its queued recompute hasn't run — e.g.
|
|
32
32
|
// a latest() shadow marked by the previous landing's companion snap), so
|
|
33
33
|
// equality against it must not swallow the write. Without this, a sync
|
|
34
34
|
// push returning the shadow to that stale value was dropped, the snap
|
|
35
35
|
// recompute then committed the parent's old value, and the banner showed
|
|
36
36
|
// the previous transition's target (#3041 follow-up).
|
|
37
|
-
!!((e.oe ?? 0) & (REACTIVE_DIRTY | REACTIVE_CHECK)) || !e.xe || !e.xe(
|
|
38
|
-
if (!
|
|
37
|
+
!!((e.oe ?? 0) & (REACTIVE_DIRTY | REACTIVE_CHECK)) || !e.xe || !e.xe(r, n);
|
|
38
|
+
if (!t) {
|
|
39
39
|
// Same-value write with an active override still entangles the current
|
|
40
40
|
// action's transition — the hold must outlast all overlapping actions —
|
|
41
41
|
// and renews the override's PROVENANCE: the newer action re-asks the
|
|
@@ -162,7 +162,7 @@ function resolveOptimisticNodes(e) {
|
|
|
162
162
|
// override-covered authoritative values hold in _pendingValue and
|
|
163
163
|
// elevate on their OWN transition's schedule (A18 as re-ruled 2026-07-07).
|
|
164
164
|
if (!(n.h & STATUS_PENDING)) n.h &= ~STATUS_UNINITIALIZED;
|
|
165
|
-
const
|
|
165
|
+
const r = n.o?.Fe;
|
|
166
166
|
// A derived override (lanes stage, #3479) has no truth of its own: the
|
|
167
167
|
// slot disarms — the memo is plain again — and the override PROMOTES to
|
|
168
168
|
// `_value`. Not superseded, nothing it derives from told it otherwise
|
|
@@ -172,15 +172,15 @@ function resolveOptimisticNodes(e) {
|
|
|
172
172
|
// the override IS what a recompute from the truth yields. Re-deriving
|
|
173
173
|
// instead re-asked an async member's flight and held the transaction on
|
|
174
174
|
// it — a waterfall after the reveal.
|
|
175
|
-
const
|
|
176
|
-
ext(n).Fe =
|
|
175
|
+
const t = n.C & CONFIG_DERIVED_OVERRIDE;
|
|
176
|
+
ext(n).Fe = t && !(n.C & CONFIG_OPTIMISTIC) ? undefined : NOT_PENDING;
|
|
177
177
|
// A superseded override's subscribers already re-derived from the truth
|
|
178
178
|
// when it arrived (#3331) — the drop changes nothing they read. Everyone
|
|
179
179
|
// else learns of the correction here: this drop IS their notification.
|
|
180
180
|
const u = (n.C & CONFIG_OVERRIDE_SUPERSEDED) !== 0;
|
|
181
181
|
n.C &= -8912897;
|
|
182
|
-
if (!u &&
|
|
183
|
-
if (
|
|
182
|
+
if (!u && r !== NOT_PENDING && n.ce !== unwrapOverride(r)) {
|
|
183
|
+
if (t) n.ce = unwrapOverride(r); else {
|
|
184
184
|
insertSubs(n, true);
|
|
185
185
|
}
|
|
186
186
|
}
|
|
@@ -192,9 +192,9 @@ function resolveOptimisticNodes(e) {
|
|
|
192
192
|
// transition that produced them (A19 — pending is a property of the data).
|
|
193
193
|
for (let i = 0; i < n; i++) {
|
|
194
194
|
const n = e[i];
|
|
195
|
-
if (n.o?.Ze || n.o?.He) GlobalQueue.
|
|
196
|
-
const
|
|
197
|
-
if (
|
|
195
|
+
if (n.o?.Ze || n.o?.He) GlobalQueue.ri(n);
|
|
196
|
+
const r = n.o?.kn;
|
|
197
|
+
if (r && (r.o?.Ze === n || r.o?.He === n)) GlobalQueue.ri(r);
|
|
198
198
|
}
|
|
199
199
|
e.splice(0, n);
|
|
200
200
|
}
|
|
@@ -248,14 +248,14 @@ function resolveOptimisticNodes(e) {
|
|
|
248
248
|
const n = e.o?.me;
|
|
249
249
|
if (n) {
|
|
250
250
|
const i = findLane(n);
|
|
251
|
-
const
|
|
252
|
-
while (
|
|
253
|
-
const e =
|
|
251
|
+
const r = [ e ];
|
|
252
|
+
while (r.length) {
|
|
253
|
+
const e = r.pop();
|
|
254
254
|
const n = e.o?.me;
|
|
255
255
|
if (!n || findLane(n) !== i) continue;
|
|
256
256
|
e.o.me = undefined;
|
|
257
257
|
i.Qe.delete(e);
|
|
258
|
-
for (let n = e.u; n !== null; n = n.Ae)
|
|
258
|
+
for (let n = e.u; n !== null; n = n.Ae) r.push(n.ge);
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
261
|
}
|
|
@@ -297,7 +297,7 @@ function resolveOptimisticNodes(e) {
|
|
|
297
297
|
* the optimistic path so it flushes ahead of the hold — but they answer for
|
|
298
298
|
* their owner and snap at settlement (`_snapCompanions`), not here.
|
|
299
299
|
*/ function endOptimism(e) {
|
|
300
|
-
if (!e.Te || e.pe.length || !e.cn.length || e.
|
|
300
|
+
if (!e.Te || e.pe.length || !e.cn.length || e.ti.size || transitionBlocked(e)) return false;
|
|
301
301
|
for (const n of e.le.keys()) if (sourceObserved(e, n, e) && n.o?.ue?.has(n) && !resolveLane(n)) return false;
|
|
302
302
|
let n = false;
|
|
303
303
|
for (const i of e.cn) {
|
|
@@ -393,7 +393,49 @@ function resolveOptimisticNodes(e) {
|
|
|
393
393
|
* action provenance, or an A17-silent confirmation (#3331). Before this the
|
|
394
394
|
* landing took setSignal's plain path: a differing truth staged silently
|
|
395
395
|
* under the override and the graph never moved until the commit.
|
|
396
|
+
*
|
|
397
|
+
* Slot arm (list-matrix F1): `mapArray`'s writes to its per-slot signals —
|
|
398
|
+
* the row accessors of index mode, the index accessors of keyed mode; never
|
|
399
|
+
* CONFIG_OPTIMISTIC, which is how the arm tells them from the store landings
|
|
400
|
+
* above — once a lane pass has run over the map. The list's frame lives in
|
|
401
|
+
* these writes as much as in the computed's result. Under a LANE pass the
|
|
402
|
+
* write is the lane's frame, as the computed's result is (lanes stage,
|
|
403
|
+
* `laneOverride`): the slot joins the lane carrying a DERIVED override —
|
|
404
|
+
* `_value` stays the committed row, the lane's readers and untracked reads
|
|
405
|
+
* see the override (A17), an off-lane render effect sees the committed frame
|
|
406
|
+
* (#3460), the revert promotes or drops it with the lane's transaction. The
|
|
407
|
+
* gate compares against the slot the pass publishes to (INV-11): the override
|
|
408
|
+
* when armed, the committed row otherwise. A plain `setSignal` here staged
|
|
409
|
+
* the write into the ACTION's transaction: `<For>` without `keyed` showed the
|
|
410
|
+
* pre-action list for the whole action while the keyed modes showed the
|
|
411
|
+
* optimistic one. A PLAIN pass over a slot still carrying the lane's frame —
|
|
412
|
+
* the landing, the reversion, a mainline re-pass — is the truth arriving
|
|
413
|
+
* under an override and takes the landing path below: a differing truth
|
|
414
|
+
* supersedes, an equal one confirms and the revert promotes. Every pass
|
|
415
|
+
* rewrites every slot whose value differs from the previous frame's (index
|
|
416
|
+
* mode rewrites every surviving slot), so a slot the landing does NOT write
|
|
417
|
+
* holds, by construction, what the truth yields. A slot with neither a lane
|
|
418
|
+
* nor an override is the plain write.
|
|
396
419
|
*/ function landOnOverride(e, n) {
|
|
420
|
+
if (!(e.C & CONFIG_OPTIMISTIC)) {
|
|
421
|
+
const i = currentOptimisticLane;
|
|
422
|
+
if (i === null) {
|
|
423
|
+
if (!hasActiveOverride(e)) return setSignal(e, n);
|
|
424
|
+
} else {
|
|
425
|
+
// INV-11: the gate compares against the slot this pass publishes to —
|
|
426
|
+
// the override when one is armed, the committed row otherwise.
|
|
427
|
+
if (!e.xe || !e.xe(hasActiveOverride(e) ? unwrapOverride(e.o.Fe) : e.ce, n)) {
|
|
428
|
+
// Membership before the publish: `laneOverride` files the slot under
|
|
429
|
+
// the lane's transaction, and the read path routes a tracked reader
|
|
430
|
+
// of a lane member through `overrideRead` (CONFIG_HAS_LANE).
|
|
431
|
+
assignOrMergeLane(e, i);
|
|
432
|
+
laneOverride(e, n, i);
|
|
433
|
+
insertSubs(e, true);
|
|
434
|
+
schedule();
|
|
435
|
+
}
|
|
436
|
+
return n;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
397
439
|
const i = e._e === NOT_PENDING ? e.ce : e._e;
|
|
398
440
|
if (typeof n === "function") n = n(i);
|
|
399
441
|
if (e._e === NOT_PENDING) queuePendingNode(e);
|
|
@@ -583,7 +625,7 @@ function cleanupCompletedLanes(e) {
|
|
|
583
625
|
|
|
584
626
|
function trackOptimisticStore(e) {
|
|
585
627
|
// After initTransition, globalQueue._batch IS activeTransition (same reference)
|
|
586
|
-
globalQueue.m.
|
|
628
|
+
globalQueue.m.ti.add(e);
|
|
587
629
|
schedule();
|
|
588
630
|
}
|
|
589
631
|
|
package/dist/prod/core/owner.js
CHANGED
|
@@ -58,13 +58,13 @@ function disposeChildren(e, n = false, t) {
|
|
|
58
58
|
// edge). Snap runs after the DISPOSED flag is set so the oracle reads
|
|
59
59
|
// false, and notifies subscribers still watching the companion.
|
|
60
60
|
const n = e;
|
|
61
|
-
if (n.o?.Ze || n.o?.He) GlobalQueue.
|
|
61
|
+
if (n.o?.Ze || n.o?.He) GlobalQueue.ri(n);
|
|
62
62
|
// A firewall's leaves have no lifecycle of their own, so a companion on
|
|
63
63
|
// one of them outlives its source the same way (INV-9's rationale). The
|
|
64
64
|
// firewall knows which leaves carry companions (CONFIG_CHILD_COMPANIONS,
|
|
65
65
|
// #3038): snap them with it — the snap retires a shadow whose firewall is
|
|
66
66
|
// disposed (spec O5).
|
|
67
|
-
if (n.C & CONFIG_CHILD_COMPANIONS) n.o.Un.forEach(GlobalQueue.
|
|
67
|
+
if (n.C & CONFIG_CHILD_COMPANIONS) n.o.Un.forEach(GlobalQueue.ri);
|
|
68
68
|
// A pending reader parked in a transaction may be the only thing holding
|
|
69
69
|
// it (#3372): its death is a completion event the transaction must be
|
|
70
70
|
// re-judged for, and nothing else re-enters a parked transaction.
|
|
@@ -100,7 +100,7 @@ const transientStoreNodes = new Set;
|
|
|
100
100
|
|
|
101
101
|
function canUseSimpleSyncFlush(e) {
|
|
102
102
|
const t = e.m;
|
|
103
|
-
return transitions.size === 0 && activeLanes.size === 0 && e.ft.length === 0 && t.cn.length === 0 && t.A.length === 0 && t.
|
|
103
|
+
return transitions.size === 0 && activeLanes.size === 0 && e.ft.length === 0 && t.cn.length === 0 && t.A.length === 0 && t.ti.size === 0 && transientStoreNodes.size === 0 && pendingRearms.size === 0;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
function sweepTransientStoreNodes() {
|
|
@@ -149,7 +149,7 @@ function setProjectionWriteActive(e) {
|
|
|
149
149
|
le: new Map,
|
|
150
150
|
cn: [],
|
|
151
151
|
A: [],
|
|
152
|
-
|
|
152
|
+
ti: new Set,
|
|
153
153
|
pe: [],
|
|
154
154
|
dt: {
|
|
155
155
|
Tt: [ [], [] ],
|
|
@@ -180,7 +180,7 @@ function mergeTransitionState(e, t) {
|
|
|
180
180
|
e.A.push(...t.A);
|
|
181
181
|
t.A.length = 0;
|
|
182
182
|
}
|
|
183
|
-
for (const n of t.
|
|
183
|
+
for (const n of t.ti) e.ti.add(n);
|
|
184
184
|
for (const [n, i] of t.le) {
|
|
185
185
|
let t = e.le.get(n);
|
|
186
186
|
if (!t) e.le.set(n, t = new Set);
|
|
@@ -547,7 +547,7 @@ class GlobalQueue extends Queue {
|
|
|
547
547
|
static Ve=null;
|
|
548
548
|
static Ge=null;
|
|
549
549
|
static We=null;
|
|
550
|
-
static
|
|
550
|
+
static ri=null;
|
|
551
551
|
static et=null;
|
|
552
552
|
static nt=null;
|
|
553
553
|
static rt=null;
|
|
@@ -625,7 +625,11 @@ class GlobalQueue extends Queue {
|
|
|
625
625
|
* commit whatever its relation to the committed value — a landing equal to
|
|
626
626
|
* committed still differs from the override — then _supersedeOverride
|
|
627
627
|
* decides. Installed with the optimistic engine; only reachable on a node
|
|
628
|
-
* that has an override
|
|
628
|
+
* that has an override — or, from `mapArray` once a lane pass has run over
|
|
629
|
+
* the map, on a per-slot signal (never CONFIG_OPTIMISTIC): the slot arm
|
|
630
|
+
* publishes a lane pass's write as the slot's derived override, lands a
|
|
631
|
+
* plain pass's write over one, and is the plain `setSignal` otherwise (F1,
|
|
632
|
+
* see optimistic.ts landOnOverride). */
|
|
629
633
|
static st=null;
|
|
630
634
|
static fi=null;
|
|
631
635
|
flush() {
|
|
@@ -751,7 +755,7 @@ class GlobalQueue extends Queue {
|
|
|
751
755
|
e.Gn = n.Gn;
|
|
752
756
|
e.cn = n.cn;
|
|
753
757
|
e.A = n.A;
|
|
754
|
-
e.
|
|
758
|
+
e.ti = n.ti;
|
|
755
759
|
currentBatch = this.m = e;
|
|
756
760
|
}
|
|
757
761
|
} else {
|
|
@@ -787,8 +791,13 @@ class GlobalQueue extends Queue {
|
|
|
787
791
|
// sweep or recompute wrote a node it owns): effects computed under it
|
|
788
792
|
// since are its to apply, not this flush's — runEffect leaves them queued
|
|
789
793
|
// and the next pass parks them with it (#3319). Everything computed
|
|
790
|
-
// mainline applies now.
|
|
791
|
-
|
|
794
|
+
// mainline applies now. A write the finalize staged in the ambient
|
|
795
|
+
// batch with no subscriber to dirty (an optimistic store settle's
|
|
796
|
+
// keyset bump under a reader that never tracks the key set) is work
|
|
797
|
+
// too — the fast drain and the park exit already count it — so the
|
|
798
|
+
// next round commits it and the woken re-entry below does not adopt
|
|
799
|
+
// it into a parked transaction it never belonged to (matrix F6).
|
|
800
|
+
scheduled = dirtyQueue.EE >= dirtyQueue.ln || activeTransition !== null || this.m.Gn.length !== 0;
|
|
792
801
|
// Run lane effects first (for ready lanes), then regular effects
|
|
793
802
|
activeLanes.size && GlobalQueue.si(EFFECT_RENDER);
|
|
794
803
|
this.run(EFFECT_RENDER);
|
|
@@ -802,9 +811,14 @@ class GlobalQueue extends Queue {
|
|
|
802
811
|
// idle pass: entering adopts the ambient batch, and staged or dirty
|
|
803
812
|
// ambient work would be held behind flights it never read. `scheduled`
|
|
804
813
|
// is that test here — after the park exit as well as the normal one:
|
|
805
|
-
// it was recomputed from the heap
|
|
806
|
-
//
|
|
807
|
-
//
|
|
814
|
+
// it was recomputed from the heap and the ambient batch's staged nodes
|
|
815
|
+
// this pass, every write since re-armed it, and optimistic ambient
|
|
816
|
+
// nodes reverted with the finalize — so a wake in a pass with work
|
|
817
|
+
// simply falls to the next. (A staged node with no subscriber — the
|
|
818
|
+
// finalize's keyset bump under a length-only reader — used to be
|
|
819
|
+
// missed here: the wake adopted it, stamped it, and a later ambient
|
|
820
|
+
// write to the same node joined the parked transaction and never
|
|
821
|
+
// reverted; matrix F6.) Entering re-arms
|
|
808
822
|
// it itself; a dead (completed) wake is a bare return in
|
|
809
823
|
// initTransition, and the loop moves on to the next.
|
|
810
824
|
while (!scheduled && !activeTransition && wokenTransitions.length) this.initTransition(wokenTransitions.pop());
|
|
@@ -924,7 +938,7 @@ class GlobalQueue extends Queue {
|
|
|
924
938
|
activeTransition.cn.push(n);
|
|
925
939
|
}
|
|
926
940
|
if (t.A.length) activeTransition.A.push(...t.A);
|
|
927
|
-
for (const e of t.
|
|
941
|
+
for (const e of t.ti) activeTransition.ti.add(e);
|
|
928
942
|
// Gated readers recorded against the ambient batch move with it: their
|
|
929
943
|
// replay-at-commit now happens at the transaction's completion.
|
|
930
944
|
if (t.In.size) {
|
|
@@ -1043,7 +1057,7 @@ function commitPendingNode(e) {
|
|
|
1043
1057
|
e.ce = e._e;
|
|
1044
1058
|
e._e = NOT_PENDING;
|
|
1045
1059
|
}
|
|
1046
|
-
if (e.C & CONFIG_HAS_COMPANIONS) GlobalQueue.
|
|
1060
|
+
if (e.C & CONFIG_HAS_COMPANIONS) GlobalQueue.ri(e);
|
|
1047
1061
|
return;
|
|
1048
1062
|
}
|
|
1049
1063
|
if (e._e !== NOT_PENDING) {
|
|
@@ -1081,7 +1095,7 @@ function commitPendingNode(e) {
|
|
|
1081
1095
|
// against the frame. read()'s reveal carve-out keys on the mark (#3305).
|
|
1082
1096
|
else e.C |= CONFIG_INPUTS_PUBLISHED;
|
|
1083
1097
|
if (t.o != null && (t.o._n !== null || t.o.fn !== null)) GlobalQueue.en(t, false, true);
|
|
1084
|
-
if (e.C & CONFIG_HAS_COMPANIONS) GlobalQueue.
|
|
1098
|
+
if (e.C & CONFIG_HAS_COMPANIONS) GlobalQueue.ri(e);
|
|
1085
1099
|
}
|
|
1086
1100
|
|
|
1087
1101
|
// Store commit hook (INTERNALS-STORE-STATE.md §3): installed by the store
|
|
@@ -1213,7 +1227,7 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
1213
1227
|
// hook; the hook iterates, clears, and schedules (keeping the loop out of
|
|
1214
1228
|
// core lets esbuild shake it — rollup already folds the null guard). The
|
|
1215
1229
|
// completing transition scopes the clear to its own layer keys (#2899).
|
|
1216
|
-
if (t.
|
|
1230
|
+
if (t.ti.size) GlobalQueue._t(t.ti, e);
|
|
1217
1231
|
// Held-truth reveal wake (#3164), post-revert by construction: this
|
|
1218
1232
|
// finalize committed confirming truth whose subscribers were masked all
|
|
1219
1233
|
// hold — some re-derived against the committed view (the staging, or a
|