@solidjs/signals 2.0.0-rc.3 → 2.0.0-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dev.js +2494 -271
- package/dist/node.cjs +3691 -1595
- package/dist/prod/affects.js +13 -12
- package/dist/prod/boundaries.js +43 -35
- package/dist/prod/core/action.js +3 -3
- package/dist/prod/core/async.js +137 -106
- package/dist/prod/core/constants.js +55 -1
- package/dist/prod/core/core.js +354 -247
- package/dist/prod/core/effect.js +47 -50
- package/dist/prod/core/error.js +13 -1
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +88 -52
- package/dist/prod/core/heap.js +38 -38
- package/dist/prod/core/lanes.js +34 -34
- package/dist/prod/core/optimistic.js +61 -58
- package/dist/prod/core/owner.js +38 -38
- package/dist/prod/core/scheduler.js +401 -174
- package/dist/prod/core/verdict.js +132 -65
- package/dist/prod/index.js +7 -3
- package/dist/prod/map.js +106 -106
- package/dist/prod/signals.js +253 -25
- package/dist/prod/store/index.js +2 -0
- package/dist/prod/store/next/optimistic.js +314 -121
- package/dist/prod/store/next/patch-hooks.js +13 -0
- package/dist/prod/store/next/patch.js +614 -0
- package/dist/prod/store/next/projection.js +23 -19
- package/dist/prod/store/next/reconcile.js +307 -120
- package/dist/prod/store/next/store.js +440 -117
- package/dist/prod/store/next/target.js +13 -4
- package/dist/prod/store/store.js +5 -5
- package/dist/types/core/async.d.ts +2 -0
- package/dist/types/core/attribution.d.ts +9 -4
- package/dist/types/core/constants.d.ts +54 -0
- package/dist/types/core/core.d.ts +34 -21
- package/dist/types/core/dev.d.ts +8 -0
- package/dist/types/core/error.d.ts +9 -0
- package/dist/types/core/graph.d.ts +22 -0
- package/dist/types/core/index.d.ts +2 -2
- package/dist/types/core/scheduler.d.ts +46 -0
- package/dist/types/core/types.d.ts +12 -0
- package/dist/types/index.d.ts +3 -3
- package/dist/types/signals.d.ts +108 -0
- package/dist/types/store/index.d.ts +2 -0
- package/dist/types/store/next/optimistic.d.ts +13 -10
- package/dist/types/store/next/patch-hooks.d.ts +41 -0
- package/dist/types/store/next/patch.d.ts +91 -0
- package/dist/types/store/next/projection.d.ts +1 -1
- package/dist/types/store/next/reconcile.d.ts +14 -0
- package/dist/types/store/next/store.d.ts +52 -2
- package/dist/types/store/next/target.d.ts +73 -8
- package/dist/types-cjs/core/async.d.cts +2 -0
- package/dist/types-cjs/core/attribution.d.cts +9 -4
- package/dist/types-cjs/core/constants.d.cts +54 -0
- package/dist/types-cjs/core/core.d.cts +34 -21
- package/dist/types-cjs/core/dev.d.cts +8 -0
- package/dist/types-cjs/core/error.d.cts +9 -0
- package/dist/types-cjs/core/graph.d.cts +22 -0
- package/dist/types-cjs/core/index.d.cts +2 -2
- package/dist/types-cjs/core/scheduler.d.cts +46 -0
- package/dist/types-cjs/core/types.d.cts +12 -0
- package/dist/types-cjs/index.d.cts +3 -3
- package/dist/types-cjs/signals.d.cts +108 -0
- package/dist/types-cjs/store/index.d.cts +2 -0
- package/dist/types-cjs/store/next/optimistic.d.cts +13 -10
- package/dist/types-cjs/store/next/patch-hooks.d.cts +41 -0
- package/dist/types-cjs/store/next/patch.d.cts +91 -0
- package/dist/types-cjs/store/next/projection.d.cts +1 -1
- package/dist/types-cjs/store/next/reconcile.d.cts +14 -0
- package/dist/types-cjs/store/next/store.d.cts +52 -2
- package/dist/types-cjs/store/next/target.d.cts +73 -8
- package/package.json +2 -2
package/dist/prod/core/lanes.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CONFIG_HAS_LANE, NOT_PENDING } from "./constants.js";
|
|
2
2
|
|
|
3
3
|
import { ext } from "./core.js";
|
|
4
4
|
|
|
@@ -20,16 +20,16 @@ const activeLanes = new Set;
|
|
|
20
20
|
}
|
|
21
21
|
// Detect parent lane: _parentSource chains from pendingSignal → pendingValueComputed → original.
|
|
22
22
|
// The child lane should not merge with the parent lane.
|
|
23
|
-
const i = n.o?.
|
|
24
|
-
const t = i?.o?.
|
|
23
|
+
const i = n.o?.Tt;
|
|
24
|
+
const t = i?.o?.Je;
|
|
25
25
|
const r = t ? findLane(t) : null;
|
|
26
26
|
e = {
|
|
27
27
|
nn: n,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
De: new Set,
|
|
29
|
+
tn: [ [], [] ],
|
|
30
|
+
rn: null,
|
|
31
|
+
Ae: activeTransition,
|
|
32
|
+
an: r
|
|
33
33
|
};
|
|
34
34
|
signalLanes.set(n, e);
|
|
35
35
|
activeLanes.add(e);
|
|
@@ -39,8 +39,8 @@ const activeLanes = new Set;
|
|
|
39
39
|
// parent-child is a property of the nodes, not of write order — otherwise
|
|
40
40
|
// the owner's write merges the companion's subscribers into this lane and
|
|
41
41
|
// their effects wait on its async instead of flushing immediately.
|
|
42
|
-
adoptCompanionLane(n.o?.
|
|
43
|
-
adoptCompanionLane(n.o?.
|
|
42
|
+
adoptCompanionLane(n.o?.ye, e);
|
|
43
|
+
adoptCompanionLane(n.o?.Ce, e);
|
|
44
44
|
return e;
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -51,13 +51,13 @@ function adoptCompanionLane(n, e) {
|
|
|
51
51
|
const t = findLane(i);
|
|
52
52
|
// Only the companion's own unmerged root is safely re-parentable: a root
|
|
53
53
|
// that absorbed other lanes carries work that is not a child of this owner.
|
|
54
|
-
if (t !== e && t.nn === n && !t.
|
|
54
|
+
if (t !== e && t.nn === n && !t.an) t.an = e;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* Union-find: find the root lane.
|
|
59
59
|
*/ function findLane(n) {
|
|
60
|
-
while (n.
|
|
60
|
+
while (n.rn) n = n.rn;
|
|
61
61
|
return n;
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -67,27 +67,27 @@ function adoptCompanionLane(n, e) {
|
|
|
67
67
|
n = findLane(n);
|
|
68
68
|
e = findLane(e);
|
|
69
69
|
if (n === e) return n;
|
|
70
|
-
e.
|
|
70
|
+
e.rn = n;
|
|
71
71
|
// Move (not copy) the merged lane's work: after the merge all routing goes
|
|
72
72
|
// through findLane() to the root, so anything left behind here is dead —
|
|
73
73
|
// and anything *added* here later is a routing bug (INV-5).
|
|
74
|
-
for (const i of e.
|
|
75
|
-
e.
|
|
76
|
-
n.
|
|
77
|
-
n.
|
|
78
|
-
e.
|
|
79
|
-
e.
|
|
74
|
+
for (const i of e.De) n.De.add(i);
|
|
75
|
+
e.De.clear();
|
|
76
|
+
n.tn[0].push(...e.tn[0]);
|
|
77
|
+
n.tn[1].push(...e.tn[1]);
|
|
78
|
+
e.tn[0].length = 0;
|
|
79
|
+
e.tn[1].length = 0;
|
|
80
80
|
return n;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
84
|
* Resolve a node's lane: follow union-find chain, verify active, clear if stale.
|
|
85
85
|
*/ function resolveLane(n) {
|
|
86
|
-
const e = n.o?.
|
|
86
|
+
const e = n.o?.Je;
|
|
87
87
|
if (!e) return undefined;
|
|
88
88
|
const i = findLane(e);
|
|
89
89
|
if (activeLanes.has(i)) return i;
|
|
90
|
-
if (n.o !== null) n.o.
|
|
90
|
+
if (n.o !== null) n.o.Je = undefined;
|
|
91
91
|
return undefined;
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -97,19 +97,19 @@ function resolveTransition(n) {
|
|
|
97
97
|
// transactions (#2912) — the merged root's _transition would hand this
|
|
98
98
|
// node's override to whichever action wrote last through the shared
|
|
99
99
|
// reader. Chase merge chains; a dead owner settled through another path.
|
|
100
|
-
if (hasActiveOverride(n) && n.o?.
|
|
101
|
-
const e = ext(n).
|
|
102
|
-
if (e.
|
|
103
|
-
if (n.o !== null) n.o.
|
|
100
|
+
if (hasActiveOverride(n) && n.o?.Nt) {
|
|
101
|
+
const e = ext(n).Nt = currentTransition(n.o?.Nt);
|
|
102
|
+
if (e.sn !== true) return e;
|
|
103
|
+
if (n.o !== null) n.o.Nt = null;
|
|
104
104
|
}
|
|
105
|
-
return resolveLane(n)?.
|
|
105
|
+
return resolveLane(n)?.Ae ?? n.Ae;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
/**
|
|
109
109
|
* Check if a node has an active optimistic override.
|
|
110
110
|
*/ function hasActiveOverride(n) {
|
|
111
111
|
const e = n.o;
|
|
112
|
-
return e !== null && e.
|
|
112
|
+
return e !== null && e.Oe !== undefined && e.Oe !== NOT_PENDING;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
/**
|
|
@@ -117,13 +117,13 @@ function resolveTransition(n) {
|
|
|
117
117
|
* a different active lane), merge unless the node has an active override.
|
|
118
118
|
*/ function assignOrMergeLane(n, e) {
|
|
119
119
|
const i = findLane(e);
|
|
120
|
-
const t = n.o?.
|
|
120
|
+
const t = n.o?.Je;
|
|
121
121
|
if (t) {
|
|
122
122
|
// If the subscriber's lane was merged into another lane, it's stale —
|
|
123
123
|
// replace it with the new source lane instead of following the merge chain
|
|
124
124
|
// (which would incorrectly merge the new lane into the old group)
|
|
125
|
-
if (t.
|
|
126
|
-
ext(n).
|
|
125
|
+
if (t.rn) {
|
|
126
|
+
ext(n).Je = e;
|
|
127
127
|
n.T |= CONFIG_HAS_LANE;
|
|
128
128
|
return;
|
|
129
129
|
}
|
|
@@ -132,15 +132,15 @@ function resolveTransition(n) {
|
|
|
132
132
|
if (r !== i && !hasActiveOverride(n)) {
|
|
133
133
|
// Parent-child lanes stay independent so isPending resolves without
|
|
134
134
|
// waiting for the parent's async. The child keeps ownership.
|
|
135
|
-
if (i.
|
|
136
|
-
ext(n).
|
|
135
|
+
if (i.an && findLane(i.an) === r) {
|
|
136
|
+
ext(n).Je = e;
|
|
137
137
|
n.T |= CONFIG_HAS_LANE;
|
|
138
|
-
} else if (r.
|
|
138
|
+
} else if (r.an && findLane(r.an) === i) ; else mergeLanes(i, r);
|
|
139
139
|
}
|
|
140
140
|
return;
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
|
-
ext(n).
|
|
143
|
+
ext(n).Je = e;
|
|
144
144
|
n.T |= CONFIG_HAS_LANE;
|
|
145
145
|
}
|
|
146
146
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NOT_PENDING, unwrapOverride, STATUS_UNINITIALIZED, REACTIVE_DIRTY, REACTIVE_CHECK, CONFIG_HAS_LANE, OVERRIDE_UNDEFINED, STATUS_PENDING, REACTIVE_MANUAL_WRITE, REACTIVE_OPTIMISTIC_DIRTY,
|
|
1
|
+
import { NOT_PENDING, unwrapOverride, STATUS_UNINITIALIZED, REACTIVE_DIRTY, REACTIVE_CHECK, CONFIG_HAS_LANE, OVERRIDE_UNDEFINED, STATUS_PENDING, EFFECT_RENDER, REACTIVE_MANUAL_WRITE, REACTIVE_OPTIMISTIC_DIRTY, EFFECT_USER } from "./constants.js";
|
|
2
2
|
|
|
3
3
|
import { ext, latestReadActive, currentOptimisticLane, stale } from "./core.js";
|
|
4
4
|
|
|
@@ -24,8 +24,8 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
|
|
|
24
24
|
* once the gate holds — the same late-binding contract as verdict.ts.
|
|
25
25
|
*/
|
|
26
26
|
/** The optimistic half of setSignal, fired when `_overrideValue !== undefined`. */ function optimisticWrite(e, n) {
|
|
27
|
-
const t = e.o?.
|
|
28
|
-
const i = t ? unwrapOverride(e.o?.
|
|
27
|
+
const t = e.o?.Oe !== NOT_PENDING;
|
|
28
|
+
const i = t ? unwrapOverride(e.o?.Oe) : e.be;
|
|
29
29
|
if (typeof n === "function") n = n(i);
|
|
30
30
|
const u = !!(e.S & STATUS_UNINITIALIZED) ||
|
|
31
31
|
// A dirty node's _value is stale (its queued recompute hasn't run — e.g.
|
|
@@ -34,7 +34,7 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
|
|
|
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.ie ?? 0) & (REACTIVE_DIRTY | REACTIVE_CHECK)) || !e.
|
|
37
|
+
!!((e.ie ?? 0) & (REACTIVE_DIRTY | REACTIVE_CHECK)) || !e.ge || !e.ge(i, n);
|
|
38
38
|
if (!u) {
|
|
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.
|
|
@@ -48,22 +48,22 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
|
|
|
48
48
|
// No revert target is stashed: while the override is active every reader
|
|
49
49
|
// sees it (A17), so authoritative arrivals commit silently into _value and
|
|
50
50
|
// reverting is just dropping the override — _value is already correct.
|
|
51
|
-
else globalQueue.
|
|
51
|
+
else globalQueue.m.ze.push(e);
|
|
52
52
|
// Stamp ownership on the node (post-merge, so entangled writers share the
|
|
53
53
|
// joint root). resolveTransition prefers this over the lane's _transition,
|
|
54
54
|
// which a shared subscriber can merge across transactions (#2912).
|
|
55
|
-
ext(e).
|
|
55
|
+
ext(e).Nt = activeTransition;
|
|
56
56
|
const l = getOrCreateLane(e);
|
|
57
|
-
ext(e).
|
|
57
|
+
ext(e).Je = l;
|
|
58
58
|
e.T |= CONFIG_HAS_LANE;
|
|
59
59
|
// Literal undefined must not land raw: the slot doubles as the optimistic
|
|
60
60
|
// brand, and erasing it makes the write invisible and routes follow-up
|
|
61
61
|
// writes off the optimistic path into permanent commits (#2898).
|
|
62
|
-
ext(e).
|
|
62
|
+
ext(e).Oe = n === undefined ? OVERRIDE_UNDEFINED : n;
|
|
63
63
|
// syncCompanions only pokes _pendingSignal/_latestValueComputed — with
|
|
64
64
|
// neither companion present the call is a guaranteed no-op.
|
|
65
|
-
(e.o?.
|
|
66
|
-
if (e.
|
|
65
|
+
(e.o?.ye !== undefined || e.o?.Ce !== undefined) && GlobalQueue.Re !== null && GlobalQueue.Re(e, n);
|
|
66
|
+
if (e.oe !== undefined) e.Te = clock;
|
|
67
67
|
// §12e: computed-only slot
|
|
68
68
|
insertSubs(e, true);
|
|
69
69
|
schedule();
|
|
@@ -75,8 +75,8 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
|
|
|
75
75
|
* while one of its optimistic nodes holds an active override that is still
|
|
76
76
|
* pending on real (non-affects-sentinel) async.
|
|
77
77
|
*/ function transitionBlocked(e) {
|
|
78
|
-
for (let n = 0; n < e.
|
|
79
|
-
const t = e.
|
|
78
|
+
for (let n = 0; n < e.ze.length; n++) {
|
|
79
|
+
const t = e.ze[n];
|
|
80
80
|
if (hasActiveOverride(t) && "S" in t && t.S & STATUS_PENDING && t.o?._ instanceof NotReadyError) {
|
|
81
81
|
return true;
|
|
82
82
|
}
|
|
@@ -91,25 +91,25 @@ function resolveOptimisticNodes(e) {
|
|
|
91
91
|
const n = e.length;
|
|
92
92
|
for (let t = 0; t < n; t++) {
|
|
93
93
|
const n = e[t];
|
|
94
|
-
if (n.o !== null) n.o.
|
|
94
|
+
if (n.o !== null) n.o.Je = undefined;
|
|
95
95
|
// Revert is a pure drop: there is no revert target to commit —
|
|
96
96
|
// override-covered authoritative values hold in _pendingValue and
|
|
97
97
|
// elevate on their OWN transition's schedule (A18 as re-ruled 2026-07-07).
|
|
98
98
|
if (!(n.S & STATUS_PENDING)) n.S &= ~STATUS_UNINITIALIZED;
|
|
99
|
-
const i = n.o?.
|
|
100
|
-
ext(n).
|
|
99
|
+
const i = n.o?.Oe;
|
|
100
|
+
ext(n).Oe = NOT_PENDING;
|
|
101
101
|
if (i !== NOT_PENDING && n.be !== unwrapOverride(i)) insertSubs(n, true);
|
|
102
|
-
n.
|
|
103
|
-
if (n.o !== null) n.o.
|
|
102
|
+
n.Ae = null;
|
|
103
|
+
if (n.o !== null) n.o.Nt = null;
|
|
104
104
|
}
|
|
105
105
|
// Settlement checkpoint (#2838): companions caught in this batch (or owned
|
|
106
106
|
// by a node in it) re-derive from committed state, so verdicts survive the
|
|
107
107
|
// transition that produced them (A19 — pending is a property of the data).
|
|
108
108
|
for (let t = 0; t < n; t++) {
|
|
109
109
|
const n = e[t];
|
|
110
|
-
if (n.o?.
|
|
111
|
-
const i = n.o?.
|
|
112
|
-
if (i && (i.o?.
|
|
110
|
+
if (n.o?.ye || n.o?.Ce) GlobalQueue.un(n);
|
|
111
|
+
const i = n.o?.Tt;
|
|
112
|
+
if (i && (i.o?.ye === n || i.o?.Ce === n)) GlobalQueue.un(i);
|
|
113
113
|
}
|
|
114
114
|
e.splice(0, n);
|
|
115
115
|
}
|
|
@@ -122,27 +122,30 @@ function runQueue(e, n) {
|
|
|
122
122
|
* Run effects from all lanes that are ready (no pending async).
|
|
123
123
|
*/ function runLaneEffects(e) {
|
|
124
124
|
for (const n of activeLanes) {
|
|
125
|
-
if (n.
|
|
126
|
-
const t = n.
|
|
125
|
+
if (n.rn || n.De.size > 0) continue;
|
|
126
|
+
const t = n.tn[e - 1];
|
|
127
127
|
if (t.length) {
|
|
128
|
-
n.
|
|
128
|
+
n.tn[e - 1] = [];
|
|
129
129
|
runQueue(t, e);
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
|
+
// Optimistic patch applications ride the same visibility slot as lane
|
|
133
|
+
// effects (in-flight DOM updates); no-op unless patches registered.
|
|
134
|
+
if (e === EFFECT_RENDER) GlobalQueue.ln?.();
|
|
132
135
|
}
|
|
133
136
|
|
|
134
137
|
function cleanupCompletedLanes(e) {
|
|
135
138
|
for (const n of activeLanes) {
|
|
136
|
-
const t = e ? n.
|
|
139
|
+
const t = e ? n.Ae === e : !n.Ae;
|
|
137
140
|
if (!t) continue;
|
|
138
|
-
if (!n.
|
|
139
|
-
if (n.
|
|
140
|
-
if (n.
|
|
141
|
+
if (!n.rn) {
|
|
142
|
+
if (n.tn[0].length) runQueue(n.tn[0], EFFECT_RENDER);
|
|
143
|
+
if (n.tn[1].length) runQueue(n.tn[1], EFFECT_USER);
|
|
141
144
|
}
|
|
142
|
-
if (n.nn.o?.
|
|
143
|
-
n.
|
|
144
|
-
n.
|
|
145
|
-
n.
|
|
145
|
+
if (n.nn.o?.Je === n) if (n.nn.o !== null) n.nn.o.Je = undefined;
|
|
146
|
+
n.De.clear();
|
|
147
|
+
n.tn[0].length = 0;
|
|
148
|
+
n.tn[1].length = 0;
|
|
146
149
|
activeLanes.delete(n);
|
|
147
150
|
signalLanes.delete(n.nn);
|
|
148
151
|
}
|
|
@@ -152,7 +155,7 @@ function cleanupCompletedLanes(e) {
|
|
|
152
155
|
// Per-lane suspension: only throw if in same lane as pending async
|
|
153
156
|
// AND the node doesn't have an active override (overrides are the visible value,
|
|
154
157
|
// downstream in the lane should read the override, not throw)
|
|
155
|
-
const n = e.o?.
|
|
158
|
+
const n = e.o?.Je;
|
|
156
159
|
if (!n) return false;
|
|
157
160
|
return findLane(n) === findLane(currentOptimisticLane) && !hasActiveOverride(e);
|
|
158
161
|
}
|
|
@@ -162,10 +165,10 @@ function cleanupCompletedLanes(e) {
|
|
|
162
165
|
* that reads a pending mid-transition write sees the committed value; the sub
|
|
163
166
|
* is recorded for replay at commit.
|
|
164
167
|
*/ function gatedRead(e, n, t) {
|
|
165
|
-
if (latestReadActive || e.Pe === NOT_PENDING || e.
|
|
168
|
+
if (latestReadActive || e.Pe === NOT_PENDING || e.oe || n !== e && !(n.ie & REACTIVE_MANUAL_WRITE)) {
|
|
166
169
|
return false;
|
|
167
170
|
}
|
|
168
|
-
activeTransition.
|
|
171
|
+
activeTransition.cn.add(t);
|
|
169
172
|
return true;
|
|
170
173
|
}
|
|
171
174
|
|
|
@@ -173,7 +176,7 @@ function cleanupCompletedLanes(e) {
|
|
|
173
176
|
* read()'s value selection under a lane: return the committed `_value` for
|
|
174
177
|
* optimistic/lane-assigned signals, stale-mode reads, and pending owners.
|
|
175
178
|
*/ function laneReadsCommitted(e, n, t) {
|
|
176
|
-
if (e.o?.
|
|
179
|
+
if (e.o?.Oe !== undefined || !!e.o?.Je || !!(n.S & STATUS_PENDING)) {
|
|
177
180
|
// The committed view hides a staged in-flight value that will promote
|
|
178
181
|
// silently (commitPendingNode never re-notifies). gatedRead records plain
|
|
179
182
|
// signals for replay at commit; async memos are excluded from it by the
|
|
@@ -182,10 +185,10 @@ function cleanupCompletedLanes(e) {
|
|
|
182
185
|
// committed value to a reader that never re-ran after the landing, so a
|
|
183
186
|
// pending-gated branch stayed one value behind permanently (#3041
|
|
184
187
|
// follow-up). Record the reader under the same replay contract.
|
|
185
|
-
if (e.Pe !== NOT_PENDING) (activeTransition ?? globalQueue.
|
|
188
|
+
if (e.Pe !== NOT_PENDING) (activeTransition ?? globalQueue.m).cn.add(t);
|
|
186
189
|
return true;
|
|
187
190
|
}
|
|
188
|
-
if (n === e && stale && t.o?.
|
|
191
|
+
if (n === e && stale && t.o?.Tt !== e) {
|
|
189
192
|
// The committed view can hide a staged write (a lane member — even just
|
|
190
193
|
// an isPending companion flip — puts the reader "under a lane"). The
|
|
191
194
|
// staged value commits with no re-delivery (commitPendingNode never
|
|
@@ -197,7 +200,7 @@ function cleanupCompletedLanes(e) {
|
|
|
197
200
|
// into the transaction (#3041 follow-up: a pending-gated branch that
|
|
198
201
|
// first read its async source during the landing flush stayed one value
|
|
199
202
|
// behind permanently); with none, into the ambient batch.
|
|
200
|
-
if (e.Pe !== NOT_PENDING) (activeTransition ?? globalQueue.
|
|
203
|
+
if (e.Pe !== NOT_PENDING) (activeTransition ?? globalQueue.m).cn.add(t);
|
|
201
204
|
return true;
|
|
202
205
|
}
|
|
203
206
|
return false;
|
|
@@ -220,14 +223,14 @@ function cleanupCompletedLanes(e) {
|
|
|
220
223
|
// recompute() runs plain: the value stages and commits with the flush.
|
|
221
224
|
// (el's own override slot excludes companions themselves; a lane merged
|
|
222
225
|
// into a real optimistic lane resolves to a non-companion source.)
|
|
223
|
-
if (!globalQueue.
|
|
224
|
-
if (e.o !== null) e.o.
|
|
226
|
+
if (!globalQueue.fn && !activeTransition && !n.Ae && n.nn.o?.Tt !== undefined && e.o?.Oe === undefined) {
|
|
227
|
+
if (e.o !== null) e.o.Je = undefined;
|
|
225
228
|
return false;
|
|
226
229
|
}
|
|
227
230
|
return n;
|
|
228
231
|
}
|
|
229
|
-
for (let n = e.
|
|
230
|
-
const t = n.
|
|
232
|
+
for (let n = e.ut; n; n = n.lt) {
|
|
233
|
+
const t = n.ot;
|
|
231
234
|
if (t.ie & REACTIVE_OPTIMISTIC_DIRTY) {
|
|
232
235
|
const n = resolveLane(t);
|
|
233
236
|
if (n) {
|
|
@@ -243,8 +246,8 @@ function cleanupCompletedLanes(e) {
|
|
|
243
246
|
/** recompute()'s catch path: track pending async in the current lane. */ function laneAsyncPending(e) {
|
|
244
247
|
const n = findLane(currentOptimisticLane);
|
|
245
248
|
if (n.nn !== e) {
|
|
246
|
-
n.
|
|
247
|
-
ext(e).
|
|
249
|
+
n.De.add(e);
|
|
250
|
+
ext(e).Je = n;
|
|
248
251
|
e.T |= CONFIG_HAS_LANE;
|
|
249
252
|
GlobalQueue.de !== null && GlobalQueue.de(n.nn);
|
|
250
253
|
}
|
|
@@ -253,14 +256,14 @@ function cleanupCompletedLanes(e) {
|
|
|
253
256
|
/** recompute()'s success path: the node's async settled, clear it from its lane. */ function laneAsyncSettled(e) {
|
|
254
257
|
const n = resolveLane(e);
|
|
255
258
|
if (n) {
|
|
256
|
-
n.
|
|
259
|
+
n.De.delete(e);
|
|
257
260
|
GlobalQueue.de !== null && GlobalQueue.de(n.nn);
|
|
258
261
|
}
|
|
259
262
|
}
|
|
260
263
|
|
|
261
264
|
function trackOptimisticStore(e) {
|
|
262
265
|
// After initTransition, globalQueue._batch IS activeTransition (same reference)
|
|
263
|
-
globalQueue.
|
|
266
|
+
globalQueue.m.En.add(e);
|
|
264
267
|
schedule();
|
|
265
268
|
}
|
|
266
269
|
|
|
@@ -269,19 +272,19 @@ function trackOptimisticStore(e) {
|
|
|
269
272
|
* create optimistic state (verdict.ts at module top level, createOptimistic
|
|
270
273
|
* and createOptimisticStore at first call) BEFORE any optimistic node exists.
|
|
271
274
|
*/ function installOptimisticEngine() {
|
|
272
|
-
if (GlobalQueue.
|
|
273
|
-
GlobalQueue.
|
|
274
|
-
GlobalQueue.
|
|
275
|
-
GlobalQueue.
|
|
276
|
-
GlobalQueue.
|
|
277
|
-
GlobalQueue.
|
|
278
|
-
GlobalQueue.
|
|
279
|
-
GlobalQueue.
|
|
280
|
-
GlobalQueue.
|
|
281
|
-
GlobalQueue.
|
|
275
|
+
if (GlobalQueue.xt !== null) return;
|
|
276
|
+
GlobalQueue.xt = optimisticWrite;
|
|
277
|
+
GlobalQueue.Tn = resolveOptimisticNodes;
|
|
278
|
+
GlobalQueue.dn = transitionBlocked;
|
|
279
|
+
GlobalQueue.In = cleanupCompletedLanes;
|
|
280
|
+
GlobalQueue.Nn = runLaneEffects;
|
|
281
|
+
GlobalQueue.vt = gatedRead;
|
|
282
|
+
GlobalQueue.Vt = laneSuspends;
|
|
283
|
+
GlobalQueue.bt = laneReadsCommitted;
|
|
284
|
+
GlobalQueue.Be = recomputeLane;
|
|
282
285
|
GlobalQueue.$e = laneAsyncPending;
|
|
283
|
-
GlobalQueue.
|
|
284
|
-
GlobalQueue.
|
|
286
|
+
GlobalQueue.Xe = laneAsyncSettled;
|
|
287
|
+
GlobalQueue._n = trackOptimisticStore;
|
|
285
288
|
}
|
|
286
289
|
|
|
287
290
|
export { installOptimisticEngine };
|
package/dist/prod/core/owner.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { REACTIVE_DISPOSED, REACTIVE_ZOMBIE, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, CONFIG_TRANSPARENT, defaultContext
|
|
1
|
+
import { REACTIVE_DISPOSED, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, CONFIG_TRANSPARENT, defaultContext } from "./constants.js";
|
|
2
2
|
|
|
3
3
|
import { context, runWithOwner, pendingCheckActive, latestReadActive, tracking } from "./core.js";
|
|
4
4
|
|
|
@@ -12,7 +12,7 @@ const PENDING_OWNER = {};
|
|
|
12
12
|
|
|
13
13
|
// Dummy owner to trigger store's read() path
|
|
14
14
|
function markDisposal(e) {
|
|
15
|
-
let t = e.
|
|
15
|
+
let t = e.xe;
|
|
16
16
|
while (t) {
|
|
17
17
|
const e = t.ie;
|
|
18
18
|
t.ie = e | REACTIVE_ZOMBIE;
|
|
@@ -26,7 +26,7 @@ function markDisposal(e) {
|
|
|
26
26
|
if (e & REACTIVE_IN_HEAP) insertIntoHeap(t, zombieQueue); else insertIntoHeapHeight(t, zombieQueue);
|
|
27
27
|
}
|
|
28
28
|
markDisposal(t);
|
|
29
|
-
t = t.
|
|
29
|
+
t = t.Le;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -52,12 +52,12 @@ function disposeChildren(e, t = false, n) {
|
|
|
52
52
|
// edge). Snap runs after the DISPOSED flag is set so the oracle reads
|
|
53
53
|
// false, and notifies subscribers still watching the companion.
|
|
54
54
|
const t = e;
|
|
55
|
-
if (t.o?.
|
|
55
|
+
if (t.o?.ye || t.o?.Ce) GlobalQueue.un(t);
|
|
56
56
|
}
|
|
57
|
-
if (t && e.
|
|
58
|
-
let o = n ? e.o?.
|
|
57
|
+
if (t && e.oe && e.o !== null) e.o.Ie = null;
|
|
58
|
+
let o = n ? e.o?.Ye ?? null : e.xe;
|
|
59
59
|
while (o) {
|
|
60
|
-
const e = o.
|
|
60
|
+
const e = o.Le;
|
|
61
61
|
const t = o;
|
|
62
62
|
// Owner teardown is death regardless of the child's own lifecycle
|
|
63
63
|
// (#3024): strip AUTO_DISPOSE so a post-disposal read freezes at the
|
|
@@ -80,34 +80,34 @@ function disposeChildren(e, t = false, n) {
|
|
|
80
80
|
o = e;
|
|
81
81
|
}
|
|
82
82
|
if (n) {
|
|
83
|
-
if (e.o !== null) e.o.
|
|
83
|
+
if (e.o !== null) e.o.Ye = null;
|
|
84
84
|
} else {
|
|
85
|
-
e.
|
|
86
|
-
e.
|
|
85
|
+
e.xe = null;
|
|
86
|
+
e.Ze = 0;
|
|
87
87
|
}
|
|
88
88
|
// O(1) splice out of parent's chain on individual dispose. Skipped during
|
|
89
89
|
// batch dispose (parent already disposed) and zombie disposal (node sits on
|
|
90
90
|
// parent's _pendingFirstChild). We leave node._nextSibling intact so outer
|
|
91
91
|
// walks that already advanced past us still reach later siblings.
|
|
92
|
-
if (t && !n && !(i & REACTIVE_ZOMBIE) && e.
|
|
93
|
-
const t = e.
|
|
94
|
-
const n = e.
|
|
95
|
-
if (t !== null) t.
|
|
96
|
-
if (n !== null) n.
|
|
97
|
-
e.
|
|
92
|
+
if (t && !n && !(i & REACTIVE_ZOMBIE) && e.ke !== null && !(e.ke.ie & REACTIVE_DISPOSED)) {
|
|
93
|
+
const t = e.ft;
|
|
94
|
+
const n = e.Le;
|
|
95
|
+
if (t !== null) t.Le = n; else e.ke.xe = n;
|
|
96
|
+
if (n !== null) n.ft = t;
|
|
97
|
+
e.ft = null;
|
|
98
98
|
}
|
|
99
99
|
runDisposal(e, n);
|
|
100
100
|
// Final effect-returned cleanup fires at true disposal, after `_disposal`
|
|
101
101
|
// to mirror rerun ordering (compute-phase teardown first, cleanup last).
|
|
102
|
-
if (t && e.
|
|
103
|
-
const t = e.
|
|
104
|
-
e.
|
|
102
|
+
if (t && e.Rt) {
|
|
103
|
+
const t = e.Rt;
|
|
104
|
+
e.Rt = undefined;
|
|
105
105
|
t();
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
function runDisposal(e, t) {
|
|
110
|
-
let n = t ? e.o?.
|
|
110
|
+
let n = t ? e.o?.qe : e.pe;
|
|
111
111
|
if (!n) return;
|
|
112
112
|
if (Array.isArray(n)) {
|
|
113
113
|
for (let e = 0; e < n.length; e++) {
|
|
@@ -118,14 +118,14 @@ function runDisposal(e, t) {
|
|
|
118
118
|
n.call(n);
|
|
119
119
|
}
|
|
120
120
|
if (t) {
|
|
121
|
-
if (e.o !== null) e.o.
|
|
122
|
-
} else e.
|
|
121
|
+
if (e.o !== null) e.o.qe = null;
|
|
122
|
+
} else e.pe = null;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
function childId(e, t) {
|
|
126
126
|
let n = e;
|
|
127
|
-
while (n.T & CONFIG_TRANSPARENT && n.
|
|
128
|
-
if (n.id != null) return formatId(n.id, t ? n.
|
|
127
|
+
while (n.T & CONFIG_TRANSPARENT && n.ke) n = n.ke;
|
|
128
|
+
if (n.id != null) return formatId(n.id, t ? n.Ze++ : n.Ze);
|
|
129
129
|
throw new Error("");
|
|
130
130
|
}
|
|
131
131
|
|
|
@@ -206,7 +206,7 @@ function formatId(e, t) {
|
|
|
206
206
|
* checks. `cleanup()` is the unchecked primitive used by internals.
|
|
207
207
|
*/ function cleanup(e) {
|
|
208
208
|
if (!context) return e;
|
|
209
|
-
if (!context.
|
|
209
|
+
if (!context.pe) context.pe = e; else if (Array.isArray(context.pe)) context.pe.push(e); else context.pe = [ context.pe, e ];
|
|
210
210
|
return e;
|
|
211
211
|
}
|
|
212
212
|
|
|
@@ -246,27 +246,27 @@ function disposeRootSelf(e = true) {
|
|
|
246
246
|
const i = {
|
|
247
247
|
id: inheritId(e, n, t),
|
|
248
248
|
T: n ? CONFIG_TRANSPARENT : 0,
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
249
|
+
Gt: true,
|
|
250
|
+
Dt: t?.Gt ? t.Dt : t,
|
|
251
|
+
xe: null,
|
|
252
|
+
Le: null,
|
|
253
|
+
ft: null,
|
|
254
|
+
pe: null,
|
|
255
255
|
C: t?.C ?? globalQueue,
|
|
256
256
|
we: t?.we || defaultContext,
|
|
257
|
-
|
|
257
|
+
Ze: 0,
|
|
258
258
|
o: null,
|
|
259
|
-
|
|
259
|
+
ke: t,
|
|
260
260
|
dispose: disposeRootSelf
|
|
261
261
|
};
|
|
262
262
|
if (t) {
|
|
263
|
-
const e = t.
|
|
263
|
+
const e = t.xe;
|
|
264
264
|
if (e === null) {
|
|
265
|
-
t.
|
|
265
|
+
t.xe = i;
|
|
266
266
|
} else {
|
|
267
|
-
i.
|
|
268
|
-
e.
|
|
269
|
-
t.
|
|
267
|
+
i.Le = e;
|
|
268
|
+
e.ft = i;
|
|
269
|
+
t.xe = i;
|
|
270
270
|
}
|
|
271
271
|
}
|
|
272
272
|
return i;
|