@solidjs/signals 2.0.0-rc.0 → 2.0.0-rc.2
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 +1 -1
- package/dist/dev.js +4287 -2803
- package/dist/node.cjs +4563 -3756
- package/dist/prod/affects.js +16 -16
- package/dist/prod/boundaries.js +90 -90
- package/dist/prod/core/action.js +3 -3
- package/dist/prod/core/async.js +234 -178
- package/dist/prod/core/constants.js +39 -1
- package/dist/prod/core/core.js +362 -243
- package/dist/prod/core/effect.js +56 -56
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +65 -54
- package/dist/prod/core/heap.js +47 -39
- package/dist/prod/core/invariants.js +3 -2
- package/dist/prod/core/lanes.js +41 -34
- package/dist/prod/core/optimistic.js +78 -58
- package/dist/prod/core/owner.js +101 -100
- package/dist/prod/core/scheduler.js +249 -192
- package/dist/prod/core/verdict.js +185 -78
- package/dist/prod/index.js +7 -7
- package/dist/prod/map.js +108 -106
- package/dist/prod/signals.js +20 -1
- package/dist/prod/store/index.js +36 -0
- package/dist/prod/store/next/optimistic.js +385 -0
- package/dist/prod/store/next/projection.js +172 -0
- package/dist/prod/store/next/reconcile.js +331 -0
- package/dist/prod/store/next/store.js +1499 -0
- package/dist/prod/store/next/target.js +20 -0
- package/dist/prod/store/store.js +126 -882
- package/dist/prod/store/utils.js +59 -186
- package/dist/types/core/attribution-hooks.d.ts +52 -0
- package/dist/types/core/attribution.d.ts +186 -0
- package/dist/types/core/constants.d.ts +26 -0
- package/dist/types/core/core.d.ts +16 -1
- package/dist/types/core/dev.d.ts +20 -3
- package/dist/types/core/graph.d.ts +1 -0
- package/dist/types/core/lanes.d.ts +4 -16
- package/dist/types/core/scheduler.d.ts +12 -2
- package/dist/types/core/types.d.ts +85 -41
- package/dist/types/signals.d.ts +19 -0
- package/dist/types/store/index.d.ts +15 -5
- package/dist/types/store/next/optimistic.d.ts +20 -0
- package/dist/types/store/next/projection.d.ts +8 -0
- package/dist/types/store/next/reconcile.d.ts +3 -0
- package/dist/types/store/next/store.d.ts +77 -0
- package/dist/types/store/next/target.d.ts +117 -0
- package/dist/types/store/store.d.ts +26 -101
- package/dist/types/store/utils.d.ts +0 -40
- package/dist/types-cjs/core/attribution-hooks.d.cts +52 -0
- package/dist/types-cjs/core/attribution.d.cts +186 -0
- package/dist/types-cjs/core/constants.d.cts +26 -0
- package/dist/types-cjs/core/core.d.cts +16 -1
- package/dist/types-cjs/core/dev.d.cts +20 -3
- package/dist/types-cjs/core/graph.d.cts +1 -0
- package/dist/types-cjs/core/lanes.d.cts +4 -16
- package/dist/types-cjs/core/scheduler.d.cts +12 -2
- package/dist/types-cjs/core/types.d.cts +85 -41
- package/dist/types-cjs/signals.d.cts +19 -0
- package/dist/types-cjs/store/index.d.cts +15 -5
- package/dist/types-cjs/store/next/optimistic.d.cts +20 -0
- package/dist/types-cjs/store/next/projection.d.cts +8 -0
- package/dist/types-cjs/store/next/reconcile.d.cts +3 -0
- package/dist/types-cjs/store/next/store.d.cts +77 -0
- package/dist/types-cjs/store/next/target.d.cts +117 -0
- package/dist/types-cjs/store/store.d.cts +26 -101
- package/dist/types-cjs/store/utils.d.cts +0 -40
- package/package.json +2 -1
- package/dist/prod/store/optimistic.js +0 -217
- package/dist/prod/store/projection.js +0 -231
- package/dist/prod/store/reconcile.js +0 -707
package/dist/prod/core/lanes.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { NOT_PENDING } from "./constants.js";
|
|
1
|
+
import { NOT_PENDING, CONFIG_HAS_LANE } from "./constants.js";
|
|
2
|
+
|
|
3
|
+
import { ext } from "./core.js";
|
|
2
4
|
|
|
3
5
|
import { currentTransition, activeTransition } from "./scheduler.js";
|
|
4
6
|
|
|
@@ -18,15 +20,16 @@ const activeLanes = new Set;
|
|
|
18
20
|
}
|
|
19
21
|
// Detect parent lane: _parentSource chains from pendingSignal → pendingValueComputed → original.
|
|
20
22
|
// The child lane should not merge with the parent lane.
|
|
21
|
-
const i = n.
|
|
22
|
-
const
|
|
23
|
+
const i = n.o?.It;
|
|
24
|
+
const t = i?.o?.Be;
|
|
25
|
+
const r = t ? findLane(t) : null;
|
|
23
26
|
e = {
|
|
24
|
-
|
|
27
|
+
nn: n,
|
|
25
28
|
Ae: new Set,
|
|
26
|
-
|
|
29
|
+
en: [ [], [] ],
|
|
27
30
|
tn: null,
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
_e: activeTransition,
|
|
32
|
+
rn: r
|
|
30
33
|
};
|
|
31
34
|
signalLanes.set(n, e);
|
|
32
35
|
activeLanes.add(e);
|
|
@@ -36,8 +39,8 @@ const activeLanes = new Set;
|
|
|
36
39
|
// parent-child is a property of the nodes, not of write order — otherwise
|
|
37
40
|
// the owner's write merges the companion's subscribers into this lane and
|
|
38
41
|
// their effects wait on its async instead of flushing immediately.
|
|
39
|
-
adoptCompanionLane(n.
|
|
40
|
-
adoptCompanionLane(n.
|
|
42
|
+
adoptCompanionLane(n.o?.Ge, e);
|
|
43
|
+
adoptCompanionLane(n.o?.ge, e);
|
|
41
44
|
return e;
|
|
42
45
|
}
|
|
43
46
|
|
|
@@ -45,10 +48,10 @@ function adoptCompanionLane(n, e) {
|
|
|
45
48
|
if (!n) return;
|
|
46
49
|
const i = signalLanes.get(n);
|
|
47
50
|
if (!i) return;
|
|
48
|
-
const
|
|
51
|
+
const t = findLane(i);
|
|
49
52
|
// Only the companion's own unmerged root is safely re-parentable: a root
|
|
50
53
|
// that absorbed other lanes carries work that is not a child of this owner.
|
|
51
|
-
if (
|
|
54
|
+
if (t !== e && t.nn === n && !t.rn) t.rn = e;
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
/**
|
|
@@ -70,21 +73,21 @@ function adoptCompanionLane(n, e) {
|
|
|
70
73
|
// and anything *added* here later is a routing bug (INV-5).
|
|
71
74
|
for (const i of e.Ae) n.Ae.add(i);
|
|
72
75
|
e.Ae.clear();
|
|
73
|
-
n.
|
|
74
|
-
n.
|
|
75
|
-
e.
|
|
76
|
-
e.
|
|
76
|
+
n.en[0].push(...e.en[0]);
|
|
77
|
+
n.en[1].push(...e.en[1]);
|
|
78
|
+
e.en[0].length = 0;
|
|
79
|
+
e.en[1].length = 0;
|
|
77
80
|
return n;
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
/**
|
|
81
84
|
* Resolve a node's lane: follow union-find chain, verify active, clear if stale.
|
|
82
85
|
*/ function resolveLane(n) {
|
|
83
|
-
const e = n.
|
|
86
|
+
const e = n.o?.Be;
|
|
84
87
|
if (!e) return undefined;
|
|
85
88
|
const i = findLane(e);
|
|
86
89
|
if (activeLanes.has(i)) return i;
|
|
87
|
-
n.
|
|
90
|
+
if (n.o !== null) n.o.Be = undefined;
|
|
88
91
|
return undefined;
|
|
89
92
|
}
|
|
90
93
|
|
|
@@ -94,18 +97,19 @@ function resolveTransition(n) {
|
|
|
94
97
|
// transactions (#2912) — the merged root's _transition would hand this
|
|
95
98
|
// node's override to whichever action wrote last through the shared
|
|
96
99
|
// reader. Chase merge chains; a dead owner settled through another path.
|
|
97
|
-
if (hasActiveOverride(n) && n.
|
|
98
|
-
const e = n.
|
|
99
|
-
if (e.
|
|
100
|
-
n.
|
|
100
|
+
if (hasActiveOverride(n) && n.o?.Et) {
|
|
101
|
+
const e = ext(n).Et = currentTransition(n.o?.Et);
|
|
102
|
+
if (e.an !== true) return e;
|
|
103
|
+
if (n.o !== null) n.o.Et = null;
|
|
101
104
|
}
|
|
102
|
-
return resolveLane(n)?.
|
|
105
|
+
return resolveLane(n)?._e ?? n._e;
|
|
103
106
|
}
|
|
104
107
|
|
|
105
108
|
/**
|
|
106
109
|
* Check if a node has an active optimistic override.
|
|
107
110
|
*/ function hasActiveOverride(n) {
|
|
108
|
-
|
|
111
|
+
const e = n.o;
|
|
112
|
+
return e !== null && e.De !== undefined && e.De !== NOT_PENDING;
|
|
109
113
|
}
|
|
110
114
|
|
|
111
115
|
/**
|
|
@@ -113,28 +117,31 @@ function resolveTransition(n) {
|
|
|
113
117
|
* a different active lane), merge unless the node has an active override.
|
|
114
118
|
*/ function assignOrMergeLane(n, e) {
|
|
115
119
|
const i = findLane(e);
|
|
116
|
-
const
|
|
117
|
-
if (
|
|
120
|
+
const t = n.o?.Be;
|
|
121
|
+
if (t) {
|
|
118
122
|
// If the subscriber's lane was merged into another lane, it's stale —
|
|
119
123
|
// replace it with the new source lane instead of following the merge chain
|
|
120
124
|
// (which would incorrectly merge the new lane into the old group)
|
|
121
|
-
if (
|
|
122
|
-
n.
|
|
125
|
+
if (t.tn) {
|
|
126
|
+
ext(n).Be = e;
|
|
127
|
+
n.T |= CONFIG_HAS_LANE;
|
|
123
128
|
return;
|
|
124
129
|
}
|
|
125
|
-
const
|
|
126
|
-
if (activeLanes.has(
|
|
127
|
-
if (
|
|
130
|
+
const r = findLane(t);
|
|
131
|
+
if (activeLanes.has(r)) {
|
|
132
|
+
if (r !== i && !hasActiveOverride(n)) {
|
|
128
133
|
// Parent-child lanes stay independent so isPending resolves without
|
|
129
134
|
// waiting for the parent's async. The child keeps ownership.
|
|
130
|
-
if (i.
|
|
131
|
-
n.
|
|
132
|
-
|
|
135
|
+
if (i.rn && findLane(i.rn) === r) {
|
|
136
|
+
ext(n).Be = e;
|
|
137
|
+
n.T |= CONFIG_HAS_LANE;
|
|
138
|
+
} else if (r.rn && findLane(r.rn) === i) ; else mergeLanes(i, r);
|
|
133
139
|
}
|
|
134
140
|
return;
|
|
135
141
|
}
|
|
136
142
|
}
|
|
137
|
-
n.
|
|
143
|
+
ext(n).Be = e;
|
|
144
|
+
n.T |= CONFIG_HAS_LANE;
|
|
138
145
|
}
|
|
139
146
|
|
|
140
147
|
export { activeLanes, assignOrMergeLane, findLane, getOrCreateLane, hasActiveOverride, mergeLanes, resolveLane, resolveTransition, signalLanes };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { NOT_PENDING, unwrapOverride, STATUS_UNINITIALIZED, OVERRIDE_UNDEFINED, STATUS_PENDING, REACTIVE_MANUAL_WRITE, REACTIVE_OPTIMISTIC_DIRTY, EFFECT_RENDER, EFFECT_USER } from "./constants.js";
|
|
1
|
+
import { NOT_PENDING, unwrapOverride, STATUS_UNINITIALIZED, CONFIG_HAS_LANE, OVERRIDE_UNDEFINED, STATUS_PENDING, REACTIVE_MANUAL_WRITE, REACTIVE_OPTIMISTIC_DIRTY, EFFECT_RENDER, EFFECT_USER } from "./constants.js";
|
|
2
2
|
|
|
3
|
-
import { latestReadActive, currentOptimisticLane, stale } from "./core.js";
|
|
3
|
+
import { ext, latestReadActive, currentOptimisticLane, stale } from "./core.js";
|
|
4
4
|
|
|
5
5
|
import { NotReadyError } from "./error.js";
|
|
6
6
|
|
|
@@ -24,10 +24,10 @@ 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.
|
|
28
|
-
const i = t ? unwrapOverride(e.
|
|
27
|
+
const t = e.o?.De !== NOT_PENDING;
|
|
28
|
+
const i = t ? unwrapOverride(e.o?.De) : e.be;
|
|
29
29
|
if (typeof n === "function") n = n(i);
|
|
30
|
-
const u = !!(e.S & STATUS_UNINITIALIZED) || !e.
|
|
30
|
+
const u = !!(e.S & STATUS_UNINITIALIZED) || !e.Ue || !e.Ue(i, n);
|
|
31
31
|
if (!u) {
|
|
32
32
|
// Same-value write with an active override still entangles the current
|
|
33
33
|
// action's transition — the hold must outlast all overlapping actions.
|
|
@@ -41,22 +41,24 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
|
|
|
41
41
|
// No revert target is stashed: while the override is active every reader
|
|
42
42
|
// sees it (A17), so authoritative arrivals commit silently into _value and
|
|
43
43
|
// reverting is just dropping the override — _value is already correct.
|
|
44
|
-
else globalQueue.
|
|
44
|
+
else globalQueue.k.Ke.push(e);
|
|
45
45
|
// Stamp ownership on the node (post-merge, so entangled writers share the
|
|
46
46
|
// joint root). resolveTransition prefers this over the lane's _transition,
|
|
47
47
|
// which a shared subscriber can merge across transactions (#2912).
|
|
48
|
-
e.
|
|
49
|
-
const
|
|
50
|
-
e.
|
|
48
|
+
ext(e).Et = activeTransition;
|
|
49
|
+
const l = getOrCreateLane(e);
|
|
50
|
+
ext(e).Be = l;
|
|
51
|
+
e.T |= CONFIG_HAS_LANE;
|
|
51
52
|
// Literal undefined must not land raw: the slot doubles as the optimistic
|
|
52
53
|
// brand, and erasing it makes the write invisible and routes follow-up
|
|
53
54
|
// writes off the optimistic path into permanent commits (#2898).
|
|
54
|
-
e.
|
|
55
|
+
ext(e).De = n === undefined ? OVERRIDE_UNDEFINED : n;
|
|
55
56
|
// syncCompanions only pokes _pendingSignal/_latestValueComputed — with
|
|
56
57
|
// neither companion present the call is a guaranteed no-op.
|
|
57
|
-
(e.
|
|
58
|
-
e.
|
|
59
|
-
|
|
58
|
+
(e.o?.Ge !== undefined || e.o?.ge !== undefined) && GlobalQueue.Oe !== null && GlobalQueue.Oe(e, n);
|
|
59
|
+
if (e.Se !== undefined) e.Te = clock;
|
|
60
|
+
// §12e: computed-only slot
|
|
61
|
+
insertSubs(e, true);
|
|
60
62
|
schedule();
|
|
61
63
|
return n;
|
|
62
64
|
}
|
|
@@ -66,9 +68,9 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
|
|
|
66
68
|
* while one of its optimistic nodes holds an active override that is still
|
|
67
69
|
* pending on real (non-affects-sentinel) async.
|
|
68
70
|
*/ function transitionBlocked(e) {
|
|
69
|
-
for (let n = 0; n < e.
|
|
70
|
-
const t = e.
|
|
71
|
-
if (hasActiveOverride(t) && "S" in t && t.S & STATUS_PENDING && t._ instanceof NotReadyError) {
|
|
71
|
+
for (let n = 0; n < e.Ke.length; n++) {
|
|
72
|
+
const t = e.Ke[n];
|
|
73
|
+
if (hasActiveOverride(t) && "S" in t && t.S & STATUS_PENDING && t.o?._ instanceof NotReadyError) {
|
|
72
74
|
return true;
|
|
73
75
|
}
|
|
74
76
|
}
|
|
@@ -82,25 +84,25 @@ function resolveOptimisticNodes(e) {
|
|
|
82
84
|
const n = e.length;
|
|
83
85
|
for (let t = 0; t < n; t++) {
|
|
84
86
|
const n = e[t];
|
|
85
|
-
n.
|
|
87
|
+
if (n.o !== null) n.o.Be = undefined;
|
|
86
88
|
// Revert is a pure drop: there is no revert target to commit —
|
|
87
89
|
// override-covered authoritative values hold in _pendingValue and
|
|
88
90
|
// elevate on their OWN transition's schedule (A18 as re-ruled 2026-07-07).
|
|
89
91
|
if (!(n.S & STATUS_PENDING)) n.S &= ~STATUS_UNINITIALIZED;
|
|
90
|
-
const i = n.
|
|
91
|
-
n.
|
|
92
|
-
if (i !== NOT_PENDING && n.
|
|
93
|
-
n.
|
|
94
|
-
n.
|
|
92
|
+
const i = n.o?.De;
|
|
93
|
+
ext(n).De = NOT_PENDING;
|
|
94
|
+
if (i !== NOT_PENDING && n.be !== unwrapOverride(i)) insertSubs(n, true);
|
|
95
|
+
n._e = null;
|
|
96
|
+
if (n.o !== null) n.o.Et = null;
|
|
95
97
|
}
|
|
96
98
|
// Settlement checkpoint (#2838): companions caught in this batch (or owned
|
|
97
99
|
// by a node in it) re-derive from committed state, so verdicts survive the
|
|
98
100
|
// transition that produced them (A19 — pending is a property of the data).
|
|
99
101
|
for (let t = 0; t < n; t++) {
|
|
100
102
|
const n = e[t];
|
|
101
|
-
if (n.
|
|
102
|
-
const i = n.
|
|
103
|
-
if (i && (i.
|
|
103
|
+
if (n.o?.Ge || n.o?.ge) GlobalQueue.un(n);
|
|
104
|
+
const i = n.o?.It;
|
|
105
|
+
if (i && (i.o?.Ge === n || i.o?.ge === n)) GlobalQueue.un(i);
|
|
104
106
|
}
|
|
105
107
|
e.splice(0, n);
|
|
106
108
|
}
|
|
@@ -114,9 +116,9 @@ function runQueue(e, n) {
|
|
|
114
116
|
*/ function runLaneEffects(e) {
|
|
115
117
|
for (const n of activeLanes) {
|
|
116
118
|
if (n.tn || n.Ae.size > 0) continue;
|
|
117
|
-
const t = n.
|
|
119
|
+
const t = n.en[e - 1];
|
|
118
120
|
if (t.length) {
|
|
119
|
-
n.
|
|
121
|
+
n.en[e - 1] = [];
|
|
120
122
|
runQueue(t, e);
|
|
121
123
|
}
|
|
122
124
|
}
|
|
@@ -124,18 +126,18 @@ function runQueue(e, n) {
|
|
|
124
126
|
|
|
125
127
|
function cleanupCompletedLanes(e) {
|
|
126
128
|
for (const n of activeLanes) {
|
|
127
|
-
const t = e ? n.
|
|
129
|
+
const t = e ? n._e === e : !n._e;
|
|
128
130
|
if (!t) continue;
|
|
129
131
|
if (!n.tn) {
|
|
130
|
-
if (n.
|
|
131
|
-
if (n.
|
|
132
|
+
if (n.en[0].length) runQueue(n.en[0], EFFECT_RENDER);
|
|
133
|
+
if (n.en[1].length) runQueue(n.en[1], EFFECT_USER);
|
|
132
134
|
}
|
|
133
|
-
if (n.
|
|
135
|
+
if (n.nn.o?.Be === n) if (n.nn.o !== null) n.nn.o.Be = undefined;
|
|
134
136
|
n.Ae.clear();
|
|
135
|
-
n.
|
|
136
|
-
n.
|
|
137
|
+
n.en[0].length = 0;
|
|
138
|
+
n.en[1].length = 0;
|
|
137
139
|
activeLanes.delete(n);
|
|
138
|
-
signalLanes.delete(n.
|
|
140
|
+
signalLanes.delete(n.nn);
|
|
139
141
|
}
|
|
140
142
|
}
|
|
141
143
|
|
|
@@ -143,7 +145,7 @@ function cleanupCompletedLanes(e) {
|
|
|
143
145
|
// Per-lane suspension: only throw if in same lane as pending async
|
|
144
146
|
// AND the node doesn't have an active override (overrides are the visible value,
|
|
145
147
|
// downstream in the lane should read the override, not throw)
|
|
146
|
-
const n = e.
|
|
148
|
+
const n = e.o?.Be;
|
|
147
149
|
if (!n) return false;
|
|
148
150
|
return findLane(n) === findLane(currentOptimisticLane) && !hasActiveOverride(e);
|
|
149
151
|
}
|
|
@@ -153,7 +155,7 @@ function cleanupCompletedLanes(e) {
|
|
|
153
155
|
* that reads a pending mid-transition write sees the committed value; the sub
|
|
154
156
|
* is recorded for replay at commit.
|
|
155
157
|
*/ function gatedRead(e, n, t) {
|
|
156
|
-
if (latestReadActive || e.
|
|
158
|
+
if (latestReadActive || e.Pe === NOT_PENDING || e.Se || n !== e && !(n.ie & REACTIVE_MANUAL_WRITE)) {
|
|
157
159
|
return false;
|
|
158
160
|
}
|
|
159
161
|
activeTransition.ln.add(t);
|
|
@@ -164,8 +166,8 @@ function cleanupCompletedLanes(e) {
|
|
|
164
166
|
* read()'s value selection under a lane: return the committed `_value` for
|
|
165
167
|
* optimistic/lane-assigned signals, stale-mode reads, and pending owners.
|
|
166
168
|
*/ function laneReadsCommitted(e, n, t) {
|
|
167
|
-
if (e.
|
|
168
|
-
if (n === e && stale && t.
|
|
169
|
+
if (e.o?.De !== undefined || !!e.o?.Be || !!(n.S & STATUS_PENDING)) return true;
|
|
170
|
+
if (n === e && stale && t.o?.It !== e) {
|
|
169
171
|
// The committed view can hide a same-tick ambient write (a lane member —
|
|
170
172
|
// even just an isPending companion flip — puts the reader "under a lane"
|
|
171
173
|
// against unrelated plain writes). With no transaction the write commits
|
|
@@ -173,7 +175,7 @@ function cleanupCompletedLanes(e) {
|
|
|
173
175
|
// replay at commit — the same contract gatedRead provides when a
|
|
174
176
|
// transaction is active (#2963). If a transaction forms mid-flush,
|
|
175
177
|
// initTransition carries the recording over to it.
|
|
176
|
-
if (e.
|
|
178
|
+
if (e.Pe !== NOT_PENDING && activeTransition === null) globalQueue.k.ln.add(t);
|
|
177
179
|
return true;
|
|
178
180
|
}
|
|
179
181
|
return false;
|
|
@@ -184,13 +186,30 @@ function cleanupCompletedLanes(e) {
|
|
|
184
186
|
* a dependency's optimistic lane (own=false — parent-deeper-than-owned-child
|
|
185
187
|
* can run before its OPT-dirty child propagates).
|
|
186
188
|
*/ function recomputeLane(e, n) {
|
|
187
|
-
if (n)
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
189
|
+
if (n) {
|
|
190
|
+
const n = resolveLane(e);
|
|
191
|
+
if (!n) return null;
|
|
192
|
+
// Wake-only lane demotion (#3009): a plain write to a latest()-tracked
|
|
193
|
+
// source rides the optimistic channel only to wake verdict companions —
|
|
194
|
+
// its lane is sourced by the companion shadow (_parentSource set) and owns
|
|
195
|
+
// no transaction. When such a node is pulled mid-tick by a latest()/
|
|
196
|
+
// isPending() probe, lane posture would direct-commit _value, leaking the
|
|
197
|
+
// queued write into committed reads before the flush. Return `false` so
|
|
198
|
+
// recompute() runs plain: the value stages and commits with the flush.
|
|
199
|
+
// (el's own override slot excludes companions themselves; a lane merged
|
|
200
|
+
// into a real optimistic lane resolves to a non-companion source.)
|
|
201
|
+
if (!globalQueue.sn && !activeTransition && !n._e && n.nn.o?.It !== undefined && e.o?.De === undefined) {
|
|
202
|
+
if (e.o !== null) e.o.Be = undefined;
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
return n;
|
|
206
|
+
}
|
|
207
|
+
for (let n = e.nt; n; n = n.it) {
|
|
208
|
+
const t = n.ut;
|
|
209
|
+
if (t.ie & REACTIVE_OPTIMISTIC_DIRTY) {
|
|
191
210
|
const n = resolveLane(t);
|
|
192
211
|
if (n) {
|
|
193
|
-
e.
|
|
212
|
+
e.ie |= REACTIVE_OPTIMISTIC_DIRTY;
|
|
194
213
|
assignOrMergeLane(e, n);
|
|
195
214
|
return n;
|
|
196
215
|
}
|
|
@@ -201,10 +220,11 @@ function cleanupCompletedLanes(e) {
|
|
|
201
220
|
|
|
202
221
|
/** recompute()'s catch path: track pending async in the current lane. */ function laneAsyncPending(e) {
|
|
203
222
|
const n = findLane(currentOptimisticLane);
|
|
204
|
-
if (n.
|
|
223
|
+
if (n.nn !== e) {
|
|
205
224
|
n.Ae.add(e);
|
|
206
|
-
e.
|
|
207
|
-
|
|
225
|
+
ext(e).Be = n;
|
|
226
|
+
e.T |= CONFIG_HAS_LANE;
|
|
227
|
+
GlobalQueue.de !== null && GlobalQueue.de(n.nn);
|
|
208
228
|
}
|
|
209
229
|
}
|
|
210
230
|
|
|
@@ -212,13 +232,13 @@ function cleanupCompletedLanes(e) {
|
|
|
212
232
|
const n = resolveLane(e);
|
|
213
233
|
if (n) {
|
|
214
234
|
n.Ae.delete(e);
|
|
215
|
-
GlobalQueue.
|
|
235
|
+
GlobalQueue.de !== null && GlobalQueue.de(n.nn);
|
|
216
236
|
}
|
|
217
237
|
}
|
|
218
238
|
|
|
219
239
|
function trackOptimisticStore(e) {
|
|
220
240
|
// After initTransition, globalQueue._batch IS activeTransition (same reference)
|
|
221
|
-
globalQueue.
|
|
241
|
+
globalQueue.k.cn.add(e);
|
|
222
242
|
schedule();
|
|
223
243
|
}
|
|
224
244
|
|
|
@@ -227,15 +247,15 @@ function trackOptimisticStore(e) {
|
|
|
227
247
|
* create optimistic state (verdict.ts at module top level, createOptimistic
|
|
228
248
|
* and createOptimisticStore at first call) BEFORE any optimistic node exists.
|
|
229
249
|
*/ function installOptimisticEngine() {
|
|
230
|
-
if (GlobalQueue.
|
|
231
|
-
GlobalQueue.
|
|
232
|
-
GlobalQueue.
|
|
233
|
-
GlobalQueue.
|
|
234
|
-
GlobalQueue.
|
|
235
|
-
GlobalQueue.
|
|
236
|
-
GlobalQueue.
|
|
237
|
-
GlobalQueue.
|
|
238
|
-
GlobalQueue.
|
|
250
|
+
if (GlobalQueue.vt !== null) return;
|
|
251
|
+
GlobalQueue.vt = optimisticWrite;
|
|
252
|
+
GlobalQueue.fn = resolveOptimisticNodes;
|
|
253
|
+
GlobalQueue.dn = transitionBlocked;
|
|
254
|
+
GlobalQueue.En = cleanupCompletedLanes;
|
|
255
|
+
GlobalQueue.Tn = runLaneEffects;
|
|
256
|
+
GlobalQueue.Ft = gatedRead;
|
|
257
|
+
GlobalQueue.ht = laneSuspends;
|
|
258
|
+
GlobalQueue.gt = laneReadsCommitted;
|
|
239
259
|
GlobalQueue.je = recomputeLane;
|
|
240
260
|
GlobalQueue.$e = laneAsyncPending;
|
|
241
261
|
GlobalQueue.ze = laneAsyncSettled;
|