@solidjs/signals 2.0.0-beta.19 → 2.0.0-beta.20
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 +670 -371
- package/dist/node.cjs +1878 -1606
- package/dist/prod/affects.js +17 -21
- package/dist/prod/core/action.js +19 -6
- package/dist/prod/core/async.js +57 -71
- package/dist/prod/core/constants.js +15 -1
- package/dist/prod/core/core.js +39 -39
- package/dist/prod/core/effect.js +14 -14
- package/dist/prod/core/graph.js +1 -1
- package/dist/prod/core/heap.js +2 -2
- package/dist/prod/core/lanes.js +23 -13
- package/dist/prod/core/optimistic.js +107 -99
- package/dist/prod/core/owner.js +23 -23
- package/dist/prod/core/scheduler.js +180 -180
- package/dist/prod/core/verdict.js +12 -13
- package/dist/prod/map.js +196 -167
- package/dist/prod/signals.js +1 -1
- package/dist/prod/store/optimistic.js +103 -68
- package/dist/prod/store/reconcile.js +80 -46
- package/dist/prod/store/store.js +204 -73
- package/dist/prod/store/utils.js +61 -42
- package/dist/types/core/action.d.ts +19 -6
- package/dist/types/core/constants.d.ts +12 -0
- package/dist/types/core/dev.d.ts +7 -0
- package/dist/types/core/lanes.d.ts +2 -0
- package/dist/types/core/scheduler.d.ts +2 -6
- package/dist/types/core/types.d.ts +9 -1
- package/dist/types/store/store.d.ts +8 -2
- package/dist/types-cjs/core/action.d.cts +19 -6
- package/dist/types-cjs/core/constants.d.cts +12 -0
- package/dist/types-cjs/core/dev.d.cts +7 -0
- package/dist/types-cjs/core/lanes.d.cts +2 -0
- package/dist/types-cjs/core/scheduler.d.cts +2 -6
- package/dist/types-cjs/core/types.d.cts +9 -1
- package/dist/types-cjs/store/store.d.cts +8 -2
- package/package.json +1 -1
package/dist/prod/core/core.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { handleAsync, clearStatus, notifyStatus } from "./async.js";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { EFFECT_TRACKED, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, STATUS_UNINITIALIZED, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, CONFIG_SYNC, STATUS_PENDING, STATUS_ERROR, REACTIVE_NONE, REACTIVE_SNAPSHOT_STALE, unwrapOverride, OVERRIDE_UNDEFINED, CONFIG_AUTO_DISPOSE, REACTIVE_LAZY, REACTIVE_DISPOSED, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, defaultContext, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_NO_SNAPSHOT, $REFRESH, REACTIVE_MANUAL_WRITE, NO_SNAPSHOT, STORE_SNAPSHOT_PROPS, EFFECT_USER } from "./constants.js";
|
|
4
4
|
|
|
5
5
|
import { NotReadyError } from "./error.js";
|
|
6
6
|
|
|
7
7
|
import { trimStaleDeps, link, unobserved } from "./graph.js";
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { deleteFromHeap, queueFor, insertIntoHeapHeight, markNode, markHeap, insertIntoHeap } from "./heap.js";
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, queuePendingNode, activeAffectsMarks, runInTransition, schedule, dirtyQueue, projectionWriteActive, armReaskClear } from "./scheduler.js";
|
|
12
12
|
|
|
13
13
|
import "./invariants.js";
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import { disposeChildren, markDisposal, inheritId } from "./owner.js";
|
|
16
16
|
|
|
17
17
|
GlobalQueue.Ce = recompute;
|
|
18
18
|
|
|
@@ -60,7 +60,7 @@ let snapshotSources = null;
|
|
|
60
60
|
function ownerInSnapshotScope(e) {
|
|
61
61
|
while (e) {
|
|
62
62
|
if (e.ke) return true;
|
|
63
|
-
e = e.
|
|
63
|
+
e = e.Fe;
|
|
64
64
|
}
|
|
65
65
|
return false;
|
|
66
66
|
}
|
|
@@ -81,7 +81,7 @@ function releaseSnapshotScope(e) {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
function releaseSubtree(e) {
|
|
84
|
-
let t = e.
|
|
84
|
+
let t = e.He;
|
|
85
85
|
while (t) {
|
|
86
86
|
if (t.ke) {
|
|
87
87
|
t = t.Ve;
|
|
@@ -120,12 +120,12 @@ function recompute(e, t = false) {
|
|
|
120
120
|
deleteFromHeap(e, queueFor(e));
|
|
121
121
|
e.Ae = null;
|
|
122
122
|
// Tracked effects run after finalizePureQueue, so dispose immediately instead of deferring
|
|
123
|
-
if (e.ve || n === EFFECT_TRACKED) disposeChildren(e); else if (e.
|
|
123
|
+
if (e.ve || n === EFFECT_TRACKED) disposeChildren(e); else if (e.He !== null || e.Me !== null) {
|
|
124
124
|
markDisposal(e);
|
|
125
125
|
e.We = e.Me;
|
|
126
|
-
e.Ze = e.
|
|
126
|
+
e.Ze = e.He;
|
|
127
127
|
e.Me = null;
|
|
128
|
-
e.
|
|
128
|
+
e.He = null;
|
|
129
129
|
e.je = 0;
|
|
130
130
|
} else ;
|
|
131
131
|
}
|
|
@@ -165,9 +165,9 @@ function recompute(e, t = false) {
|
|
|
165
165
|
currentOptimisticLane = t;
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
if (
|
|
168
|
+
const N = n && n !== EFFECT_USER;
|
|
169
|
+
const T = stale;
|
|
170
|
+
if (N) stale = true;
|
|
171
171
|
try {
|
|
172
172
|
if (!false && e.U & CONFIG_SYNC) {
|
|
173
173
|
a = e.xe(a);
|
|
@@ -201,7 +201,7 @@ function recompute(e, t = false) {
|
|
|
201
201
|
if (n) GlobalQueue.nt(e);
|
|
202
202
|
} finally {
|
|
203
203
|
tracking = c;
|
|
204
|
-
if (
|
|
204
|
+
if (N) stale = T;
|
|
205
205
|
e.u = REACTIVE_NONE | (t ? e.u & REACTIVE_SNAPSHOT_STALE : 0);
|
|
206
206
|
context = o;
|
|
207
207
|
f = affectsReads;
|
|
@@ -209,7 +209,7 @@ function recompute(e, t = false) {
|
|
|
209
209
|
}
|
|
210
210
|
if (!e.k) {
|
|
211
211
|
trimStaleDeps(e);
|
|
212
|
-
const s = l ? e.be : e.ge === NOT_PENDING ? e.Ue : e.ge;
|
|
212
|
+
const s = l ? unwrapOverride(e.be) : e.ge === NOT_PENDING ? e.Ue : e.ge;
|
|
213
213
|
let o = false;
|
|
214
214
|
try {
|
|
215
215
|
o = !n && u || !e.Ge || !e.Ge(s, a);
|
|
@@ -240,7 +240,7 @@ function recompute(e, t = false) {
|
|
|
240
240
|
// own reveal schedule; drop any superseded older hold so its queued
|
|
241
241
|
// commit can't clobber the fresh value.
|
|
242
242
|
if (l && i) {
|
|
243
|
-
e.be = a;
|
|
243
|
+
e.be = a === undefined ? OVERRIDE_UNDEFINED : a;
|
|
244
244
|
e.ge = NOT_PENDING;
|
|
245
245
|
}
|
|
246
246
|
} else {
|
|
@@ -279,13 +279,13 @@ function recompute(e, t = false) {
|
|
|
279
279
|
// action's transaction onto it — freezing unrelated writes that share it.
|
|
280
280
|
// (Single mask test up front keeps the mark-free hot path at original cost.)
|
|
281
281
|
const I = e.i & (STATUS_PENDING | STATUS_UNINITIALIZED);
|
|
282
|
-
const
|
|
282
|
+
const p = e.ge !== NOT_PENDING || e.Ze !== null || e.We !== null || I !== 0 && (I !== STATUS_PENDING || activeAffectsMarks === 0 || !GlobalQueue.$(e));
|
|
283
283
|
// Override-covered holds (hasOverride) always queue: their commit belongs
|
|
284
284
|
// to their own transition's schedule (A18 re-rule) and is unobservable
|
|
285
285
|
// under the override (A17). Revert no longer commits anything, so an
|
|
286
286
|
// unqueued covered hold would leak (INV-7) once the revert clears
|
|
287
287
|
// _transition.
|
|
288
|
-
|
|
288
|
+
p && (!t || e.i & STATUS_PENDING) && (!e.ve || l) && queuePendingNode(e);
|
|
289
289
|
e.ve && n && activeTransition !== e.ve && runInTransition(e.ve, () => recompute(e));
|
|
290
290
|
}
|
|
291
291
|
|
|
@@ -330,10 +330,10 @@ function computed(e, t) {
|
|
|
330
330
|
ze: 0,
|
|
331
331
|
p: null,
|
|
332
332
|
Et: null,
|
|
333
|
-
|
|
333
|
+
Fe: context,
|
|
334
334
|
Ve: null,
|
|
335
|
-
|
|
336
|
-
|
|
335
|
+
Nt: null,
|
|
336
|
+
He: null,
|
|
337
337
|
u: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
|
|
338
338
|
i: STATUS_UNINITIALIZED,
|
|
339
339
|
Ie: clock,
|
|
@@ -375,10 +375,10 @@ function computed(e, t) {
|
|
|
375
375
|
ze: 0,
|
|
376
376
|
p: null,
|
|
377
377
|
Et: null,
|
|
378
|
-
|
|
378
|
+
Fe: context,
|
|
379
379
|
Ve: null,
|
|
380
|
-
|
|
381
|
-
|
|
380
|
+
Nt: null,
|
|
381
|
+
He: null,
|
|
382
382
|
u: REACTIVE_LAZY,
|
|
383
383
|
i: STATUS_UNINITIALIZED,
|
|
384
384
|
Ie: clock,
|
|
@@ -389,10 +389,10 @@ function computed(e, t) {
|
|
|
389
389
|
ve: null,
|
|
390
390
|
A: false,
|
|
391
391
|
it: false,
|
|
392
|
-
|
|
392
|
+
Tt: undefined,
|
|
393
393
|
It: t,
|
|
394
394
|
St: n,
|
|
395
|
-
|
|
395
|
+
dt: undefined,
|
|
396
396
|
De: i,
|
|
397
397
|
C: l
|
|
398
398
|
};
|
|
@@ -406,15 +406,15 @@ const lazyOptions = {
|
|
|
406
406
|
|
|
407
407
|
function setupComputedNode(e, t) {
|
|
408
408
|
e.ft = e;
|
|
409
|
-
const n = context?.
|
|
409
|
+
const n = context?.At ? context.Ct : context;
|
|
410
410
|
if (context) {
|
|
411
|
-
const t = context.
|
|
411
|
+
const t = context.He;
|
|
412
412
|
if (t === null) {
|
|
413
|
-
context.
|
|
413
|
+
context.He = e;
|
|
414
414
|
} else {
|
|
415
415
|
e.Ve = t;
|
|
416
|
-
t.
|
|
417
|
-
context.
|
|
416
|
+
t.Nt = e;
|
|
417
|
+
context.He = e;
|
|
418
418
|
}
|
|
419
419
|
}
|
|
420
420
|
if (n) e.qe = n.qe + 1;
|
|
@@ -520,7 +520,7 @@ function read(e) {
|
|
|
520
520
|
// than the original node (which stays "pending" while held in a transition).
|
|
521
521
|
if (latestReadActive) return GlobalQueue.Gt(e);
|
|
522
522
|
let t = context;
|
|
523
|
-
if (t?.
|
|
523
|
+
if (t?.At) t = t.Ct;
|
|
524
524
|
const n = e;
|
|
525
525
|
const i = e.rt;
|
|
526
526
|
const l = i || e;
|
|
@@ -538,7 +538,7 @@ function read(e) {
|
|
|
538
538
|
// A live mark on a read source flows to the reader (probe reads are
|
|
539
539
|
// excluded: the probe's own collection owns that verdict, and marking
|
|
540
540
|
// the enclosing memo would make an `isPending` wrapper itself pending).
|
|
541
|
-
if (activeAffectsMarks !== 0 && e.
|
|
541
|
+
if (activeAffectsMarks !== 0 && e.M && !pendingCheckActive) (affectsReads ??= []).push(e);
|
|
542
542
|
}
|
|
543
543
|
return !t || e.ge === NOT_PENDING ? e.Ue : e.ge;
|
|
544
544
|
}
|
|
@@ -550,7 +550,7 @@ function read(e) {
|
|
|
550
550
|
// to re-establish through, so without this a mid-window recompute sheds
|
|
551
551
|
// pendingness permanently past one derivation level.
|
|
552
552
|
if (activeAffectsMarks !== 0 && !pendingCheckActive) {
|
|
553
|
-
if (e.
|
|
553
|
+
if (e.M) (affectsReads ??= []).push(e);
|
|
554
554
|
if (l.i & STATUS_PENDING) GlobalQueue.I(l, affectsReads ??= []);
|
|
555
555
|
}
|
|
556
556
|
if (l.xe) {
|
|
@@ -562,7 +562,7 @@ function read(e) {
|
|
|
562
562
|
}
|
|
563
563
|
const i = l.qe;
|
|
564
564
|
// parent check is shallow, might need to be recursive
|
|
565
|
-
if (i >= t.qe && e.
|
|
565
|
+
if (i >= t.qe && e.Fe !== t) {
|
|
566
566
|
t.qe = i + 1;
|
|
567
567
|
}
|
|
568
568
|
}
|
|
@@ -611,8 +611,8 @@ function read(e) {
|
|
|
611
611
|
if (e.be !== undefined && e.be !== NOT_PENDING) {
|
|
612
612
|
// An active override means the engine is installed (A17: the override IS
|
|
613
613
|
// the value for every reader — that check itself stays right here).
|
|
614
|
-
if (t && stale && GlobalQueue.
|
|
615
|
-
return e.be;
|
|
614
|
+
if (t && stale && GlobalQueue.Dt(e)) return e.Ue;
|
|
615
|
+
return unwrapOverride(e.be);
|
|
616
616
|
}
|
|
617
617
|
// Entanglement gate: a reader recomputing under an optimistic lane that reads
|
|
618
618
|
// a pending mid-transition write sees the committed value. Projection-store
|
|
@@ -621,17 +621,17 @@ function read(e) {
|
|
|
621
621
|
// _pendingValue for correct fetching. The sub is recorded for replay at commit
|
|
622
622
|
// so it re-runs with the new committed view. (Gate details live with the
|
|
623
623
|
// engine — a non-null lane implies it is installed.)
|
|
624
|
-
if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.
|
|
624
|
+
if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.gt(e, l, t)) {
|
|
625
625
|
return e.Ue;
|
|
626
626
|
}
|
|
627
627
|
// In optimistic lane context, return _value for optimistic/lane-assigned signals
|
|
628
628
|
// and for regular signals in stale mode (render effects). Non-stale readers (user
|
|
629
629
|
// effects) see _pendingValue so that latest() and direct reads stay consistent.
|
|
630
630
|
// (The lane-context clause lives with the engine.)
|
|
631
|
-
const u = !t || currentOptimisticLane !== null && GlobalQueue.
|
|
631
|
+
const u = !t || currentOptimisticLane !== null && GlobalQueue.vt(e, l, t) || e.ge === NOT_PENDING || stale && e.ve && activeTransition !== e.ve ? e.Ue : e.ge;
|
|
632
632
|
// Record that this isPending() probe observed the fresh pending value, so
|
|
633
633
|
// the probe doesn't pair "pending" with the new value (#2831).
|
|
634
|
-
if (pendingCheckActive) GlobalQueue.
|
|
634
|
+
if (pendingCheckActive) GlobalQueue.kt(e, u);
|
|
635
635
|
if (!t && l === e && typeof n.xe === "function" && e.U & CONFIG_AUTO_DISPOSE && !(l.i & STATUS_PENDING) && !e.p) {
|
|
636
636
|
unobserved(e);
|
|
637
637
|
}
|
package/dist/prod/core/effect.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { EFFECT_USER, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, EFFECT_TRACKED,
|
|
1
|
+
import { REACTIVE_DISPOSED, STATUS_ERROR, EFFECT_USER, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, EFFECT_TRACKED, STATUS_PENDING, EFFECT_RENDER } from "./constants.js";
|
|
2
2
|
|
|
3
3
|
import { createEffectNode, recompute, computed, staleValues } from "./core.js";
|
|
4
4
|
|
|
5
5
|
import { unwrapStatusError, StatusError } from "./error.js";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { GlobalQueue, haltReactivity } from "./scheduler.js";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Effects are the leaf nodes of our reactive graph. When their sources change, they are
|
|
@@ -62,12 +62,12 @@ function runEffect(t) {
|
|
|
62
62
|
// same flush must not be hijacked by a later-arriving error status.
|
|
63
63
|
if (t.i & STATUS_ERROR && t.De === EFFECT_USER) {
|
|
64
64
|
const E = unwrapStatusError(t.k);
|
|
65
|
-
t.
|
|
65
|
+
t.Tt = t.Ue;
|
|
66
66
|
t.it = false;
|
|
67
67
|
try {
|
|
68
68
|
t.St ? t.St(E, () => {
|
|
69
|
-
const E = t.
|
|
70
|
-
t.
|
|
69
|
+
const E = t.dt;
|
|
70
|
+
t.dt = undefined;
|
|
71
71
|
E?.();
|
|
72
72
|
}) : console.error(E);
|
|
73
73
|
} catch (E) {
|
|
@@ -78,14 +78,14 @@ function runEffect(t) {
|
|
|
78
78
|
}
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
|
-
const E = t.
|
|
82
|
-
t.
|
|
81
|
+
const E = t.dt;
|
|
82
|
+
t.dt = undefined;
|
|
83
83
|
try {
|
|
84
84
|
E?.();
|
|
85
|
-
const e = t.It(t.Ue, t.
|
|
85
|
+
const e = t.It(t.Ue, t.Tt);
|
|
86
86
|
if (false && e !== undefined && typeof e !== "function") ;
|
|
87
87
|
// The final cleanup is invoked by disposeChildren at true disposal.
|
|
88
|
-
t.
|
|
88
|
+
t.dt = e;
|
|
89
89
|
} catch (E) {
|
|
90
90
|
t.k = new StatusError(t, E);
|
|
91
91
|
t.i |= STATUS_ERROR;
|
|
@@ -94,7 +94,7 @@ function runEffect(t) {
|
|
|
94
94
|
throw E;
|
|
95
95
|
}
|
|
96
96
|
} finally {
|
|
97
|
-
t.
|
|
97
|
+
t.Tt = t.Ue;
|
|
98
98
|
t.it = false;
|
|
99
99
|
}
|
|
100
100
|
}
|
|
@@ -114,16 +114,16 @@ GlobalQueue.ut = runEffect;
|
|
|
114
114
|
} finally {}
|
|
115
115
|
};
|
|
116
116
|
const e = computed(() => {
|
|
117
|
-
const E = e.
|
|
118
|
-
e.
|
|
117
|
+
const E = e.dt;
|
|
118
|
+
e.dt = undefined;
|
|
119
119
|
E?.();
|
|
120
120
|
const R = staleValues(t);
|
|
121
|
-
e.
|
|
121
|
+
e.dt = R;
|
|
122
122
|
}, {
|
|
123
123
|
...E,
|
|
124
124
|
lazy: true
|
|
125
125
|
});
|
|
126
|
-
e.
|
|
126
|
+
e.dt = undefined;
|
|
127
127
|
e.U = e.U & ~CONFIG_AUTO_DISPOSE | CONFIG_CHILDREN_FORBIDDEN;
|
|
128
128
|
e.it = true;
|
|
129
129
|
e.De = EFFECT_TRACKED;
|
package/dist/prod/core/graph.js
CHANGED
package/dist/prod/core/heap.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { REACTIVE_IN_HEAP,
|
|
1
|
+
import { REACTIVE_IN_HEAP, EFFECT_TRACKED, EFFECT_USER, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_RECOMPUTING_DEPS, REACTIVE_MANUAL_WRITE, REACTIVE_ZOMBIE, REACTIVE_CHECK, REACTIVE_DIRTY } from "./constants.js";
|
|
2
2
|
|
|
3
3
|
import { zombieQueue, dirtyQueue } from "./scheduler.js";
|
|
4
4
|
|
|
@@ -25,7 +25,7 @@ import { zombieQueue, dirtyQueue } from "./scheduler.js";
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
function actualInsertIntoHeap(e, E) {
|
|
28
|
-
const t = (e.
|
|
28
|
+
const t = (e.Fe?.At ? e.Fe.Ct?.qe : e.Fe?.qe) ?? -1;
|
|
29
29
|
if (t >= e.qe) e.qe = t + 1;
|
|
30
30
|
const n = e.qe;
|
|
31
31
|
const I = E.eE[n];
|
package/dist/prod/core/lanes.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NOT_PENDING } from "./constants.js";
|
|
2
2
|
|
|
3
|
-
import { activeTransition } from "./scheduler.js";
|
|
3
|
+
import { currentTransition, activeTransition } from "./scheduler.js";
|
|
4
4
|
|
|
5
5
|
// Map from optimistic signal to its lane (reused for multiple writes to same signal)
|
|
6
6
|
const signalLanes = new WeakMap;
|
|
@@ -19,14 +19,14 @@ const activeLanes = new Set;
|
|
|
19
19
|
// Detect parent lane: _parentSource chains from pendingSignal → pendingValueComputed → original.
|
|
20
20
|
// The child lane should not merge with the parent lane.
|
|
21
21
|
const i = n.en;
|
|
22
|
-
const
|
|
22
|
+
const r = i?.Je ? findLane(i.Je) : null;
|
|
23
23
|
e = {
|
|
24
|
-
|
|
24
|
+
rn: n,
|
|
25
25
|
Pe: new Set,
|
|
26
26
|
tn: [ [], [] ],
|
|
27
|
-
|
|
27
|
+
an: null,
|
|
28
28
|
ve: activeTransition,
|
|
29
|
-
sn:
|
|
29
|
+
sn: r
|
|
30
30
|
};
|
|
31
31
|
signalLanes.set(n, e);
|
|
32
32
|
activeLanes.add(e);
|
|
@@ -45,16 +45,16 @@ function adoptCompanionLane(n, e) {
|
|
|
45
45
|
if (!n) return;
|
|
46
46
|
const i = signalLanes.get(n);
|
|
47
47
|
if (!i) return;
|
|
48
|
-
const
|
|
48
|
+
const r = findLane(i);
|
|
49
49
|
// Only the companion's own unmerged root is safely re-parentable: a root
|
|
50
50
|
// that absorbed other lanes carries work that is not a child of this owner.
|
|
51
|
-
if (
|
|
51
|
+
if (r !== e && r.rn === n && !r.sn) r.sn = e;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
55
|
* Union-find: find the root lane.
|
|
56
56
|
*/ function findLane(n) {
|
|
57
|
-
while (n.
|
|
57
|
+
while (n.an) n = n.an;
|
|
58
58
|
return n;
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -64,7 +64,7 @@ function adoptCompanionLane(n, e) {
|
|
|
64
64
|
n = findLane(n);
|
|
65
65
|
e = findLane(e);
|
|
66
66
|
if (n === e) return n;
|
|
67
|
-
e.
|
|
67
|
+
e.an = n;
|
|
68
68
|
// Move (not copy) the merged lane's work: after the merge all routing goes
|
|
69
69
|
// through findLane() to the root, so anything left behind here is dead —
|
|
70
70
|
// and anything *added* here later is a routing bug (INV-5).
|
|
@@ -89,6 +89,16 @@ function adoptCompanionLane(n, e) {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
function resolveTransition(n) {
|
|
92
|
+
// An active override answers with its owner, not its lane: lanes are
|
|
93
|
+
// scheduling affinity and a shared subscriber merges them across
|
|
94
|
+
// transactions (#2912) — the merged root's _transition would hand this
|
|
95
|
+
// node's override to whichever action wrote last through the shared
|
|
96
|
+
// reader. Chase merge chains; a dead owner settled through another path.
|
|
97
|
+
if (hasActiveOverride(n) && n.fn) {
|
|
98
|
+
const e = n.fn = currentTransition(n.fn);
|
|
99
|
+
if (e.cn !== true) return e;
|
|
100
|
+
n.fn = null;
|
|
101
|
+
}
|
|
92
102
|
return resolveLane(n)?.ve ?? n.ve;
|
|
93
103
|
}
|
|
94
104
|
|
|
@@ -103,16 +113,16 @@ function resolveTransition(n) {
|
|
|
103
113
|
* a different active lane), merge unless the node has an active override.
|
|
104
114
|
*/ function assignOrMergeLane(n, e) {
|
|
105
115
|
const i = findLane(e);
|
|
106
|
-
const
|
|
107
|
-
if (
|
|
116
|
+
const r = n.Je;
|
|
117
|
+
if (r) {
|
|
108
118
|
// If the subscriber's lane was merged into another lane, it's stale —
|
|
109
119
|
// replace it with the new source lane instead of following the merge chain
|
|
110
120
|
// (which would incorrectly merge the new lane into the old group)
|
|
111
|
-
if (
|
|
121
|
+
if (r.an) {
|
|
112
122
|
n.Je = e;
|
|
113
123
|
return;
|
|
114
124
|
}
|
|
115
|
-
const t = findLane(
|
|
125
|
+
const t = findLane(r);
|
|
116
126
|
if (activeLanes.has(t)) {
|
|
117
127
|
if (t !== i && !hasActiveOverride(n)) {
|
|
118
128
|
// Parent-child lanes stay independent so isPending resolves without
|