@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/effect.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
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
|
-
import { createEffectNode, recompute, computed, staleValues } from "./core.js";
|
|
3
|
+
import { ext, createEffectNode, recompute, computed, staleValues } from "./core.js";
|
|
4
4
|
|
|
5
5
|
import { unwrapStatusError, StatusError } from "./error.js";
|
|
6
6
|
|
|
@@ -10,18 +10,18 @@ import { GlobalQueue, haltReactivity } from "./scheduler.js";
|
|
|
10
10
|
* Effects are the leaf nodes of our reactive graph. When their sources change, they are
|
|
11
11
|
* automatically added to the queue of effects to re-execute, which will cause them to fetch their
|
|
12
12
|
* sources and recompute
|
|
13
|
-
*/ function effect(t,
|
|
13
|
+
*/ function effect(t, e, E, R) {
|
|
14
14
|
const r = !!R?.user;
|
|
15
|
-
const f = createEffectNode(t,
|
|
15
|
+
const f = createEffectNode(t, e, E, r ? EFFECT_USER : EFFECT_RENDER, notifyEffectStatus, R);
|
|
16
16
|
recompute(f, true);
|
|
17
17
|
!R?.defer && (f.Re === EFFECT_USER || R?.schedule ? f.C.enqueue(f.Re, runEffect.bind(null, f)) : runEffect(f));
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
function notifyEffectStatus(t,
|
|
20
|
+
function notifyEffectStatus(t, e) {
|
|
21
21
|
// Use passed values if provided, otherwise read from node
|
|
22
|
-
const
|
|
23
|
-
const R =
|
|
24
|
-
if (
|
|
22
|
+
const E = t !== undefined ? t : this.S;
|
|
23
|
+
const R = e !== undefined ? e : this.o?._;
|
|
24
|
+
if (E & STATUS_ERROR) {
|
|
25
25
|
this.C.notify(this, STATUS_PENDING, 0);
|
|
26
26
|
if (this.Re === EFFECT_USER) {
|
|
27
27
|
// The error handler is the error arm of the effect phase (#2840 ruling):
|
|
@@ -34,8 +34,8 @@ function notifyEffectStatus(t, E) {
|
|
|
34
34
|
// `status` arg without node-state writes) don't queue: the status
|
|
35
35
|
// re-propagates unblocked at commit.
|
|
36
36
|
if (this.S & STATUS_ERROR) {
|
|
37
|
-
this.
|
|
38
|
-
this.C.enqueue(this.Re, this.
|
|
37
|
+
this.Xe = true;
|
|
38
|
+
this.C.enqueue(this.Re, this.et ??= runEffect.bind(null, this));
|
|
39
39
|
}
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
@@ -44,12 +44,12 @@ function notifyEffectStatus(t, E) {
|
|
|
44
44
|
throw R;
|
|
45
45
|
}
|
|
46
46
|
} else if (this.Re === EFFECT_RENDER) {
|
|
47
|
-
this.C.notify(this, STATUS_PENDING | STATUS_ERROR,
|
|
47
|
+
this.C.notify(this, STATUS_PENDING | STATUS_ERROR, E, R);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
function runEffect(t) {
|
|
52
|
-
if (!t.
|
|
52
|
+
if (!t.Xe || t.ie & REACTIVE_DISPOSED) return;
|
|
53
53
|
// Error arm (#2840), user effects only: a compute-phase error that is still
|
|
54
54
|
// the node's settled state at effect time runs the bundle's error handler in
|
|
55
55
|
// this same imperative, writable scope. Unwrap the StatusError used for
|
|
@@ -61,85 +61,85 @@ function runEffect(t) {
|
|
|
61
61
|
// notifyEffectStatus, and a runner queued by an earlier valueChanged in the
|
|
62
62
|
// same flush must not be hijacked by a later-arriving error status.
|
|
63
63
|
if (t.S & STATUS_ERROR && t.Re === EFFECT_USER) {
|
|
64
|
-
const
|
|
65
|
-
t.
|
|
66
|
-
t.
|
|
64
|
+
const e = unwrapStatusError(t.o?._);
|
|
65
|
+
t.Tt = t.be;
|
|
66
|
+
t.Xe = false;
|
|
67
67
|
try {
|
|
68
|
-
t.
|
|
69
|
-
const
|
|
70
|
-
t.
|
|
71
|
-
|
|
72
|
-
}) : console.error(
|
|
73
|
-
} catch (
|
|
68
|
+
t.St ? t.St(e, () => {
|
|
69
|
+
const e = t.At;
|
|
70
|
+
t.At = undefined;
|
|
71
|
+
e?.();
|
|
72
|
+
}) : console.error(e);
|
|
73
|
+
} catch (e) {
|
|
74
74
|
if (!t.C.notify(t, STATUS_ERROR, STATUS_ERROR)) {
|
|
75
|
-
haltReactivity(
|
|
76
|
-
throw
|
|
75
|
+
haltReactivity(e);
|
|
76
|
+
throw e;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
|
-
const
|
|
82
|
-
t.
|
|
81
|
+
const e = t.At;
|
|
82
|
+
t.At = undefined;
|
|
83
83
|
try {
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
if (false &&
|
|
84
|
+
e?.();
|
|
85
|
+
const E = t.dt(t.be, t.Tt);
|
|
86
|
+
if (false && E !== undefined && typeof E !== "function") ;
|
|
87
87
|
// The final cleanup is invoked by disposeChildren at true disposal.
|
|
88
|
-
t.
|
|
89
|
-
} catch (
|
|
90
|
-
t._ = new StatusError(t,
|
|
88
|
+
t.At = E;
|
|
89
|
+
} catch (e) {
|
|
90
|
+
ext(t)._ = new StatusError(t, e);
|
|
91
91
|
t.S |= STATUS_ERROR;
|
|
92
92
|
if (!t.C.notify(t, STATUS_ERROR, STATUS_ERROR)) {
|
|
93
|
-
haltReactivity(
|
|
94
|
-
throw
|
|
93
|
+
haltReactivity(e);
|
|
94
|
+
throw e;
|
|
95
95
|
}
|
|
96
96
|
} finally {
|
|
97
|
-
t.
|
|
98
|
-
t.
|
|
97
|
+
t.Tt = t.be;
|
|
98
|
+
t.Xe = false;
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
GlobalQueue.
|
|
102
|
+
GlobalQueue.tt = runEffect;
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
105
|
* Internal tracked effect - bypasses heap, goes directly to effect queue.
|
|
106
106
|
* Runs as a leaf owner: child primitives and onCleanup are forbidden (false throws).
|
|
107
107
|
* Uses stale reads.
|
|
108
|
-
*/ function trackedEffect(t,
|
|
108
|
+
*/ function trackedEffect(t, e) {
|
|
109
109
|
const run = () => {
|
|
110
|
-
if (!
|
|
110
|
+
if (!E.Xe || E.ie & REACTIVE_DISPOSED) return;
|
|
111
111
|
try {
|
|
112
|
-
|
|
113
|
-
recompute(
|
|
112
|
+
E.Xe = false;
|
|
113
|
+
recompute(E);
|
|
114
114
|
} finally {}
|
|
115
115
|
};
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
const E = computed(() => {
|
|
117
|
+
const e = E.At;
|
|
118
|
+
E.At = undefined;
|
|
119
|
+
e?.();
|
|
120
120
|
const R = staleValues(t);
|
|
121
|
-
|
|
121
|
+
E.At = R;
|
|
122
122
|
}, {
|
|
123
|
-
...
|
|
123
|
+
...e,
|
|
124
124
|
lazy: true
|
|
125
125
|
});
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const R = t !== undefined ? t :
|
|
126
|
+
E.At = undefined;
|
|
127
|
+
E.T = E.T & ~CONFIG_AUTO_DISPOSE | CONFIG_CHILDREN_FORBIDDEN;
|
|
128
|
+
E.Xe = true;
|
|
129
|
+
E.Re = EFFECT_TRACKED;
|
|
130
|
+
ext(E).A = (t, e) => {
|
|
131
|
+
const R = t !== undefined ? t : E.S;
|
|
132
132
|
if (R & STATUS_ERROR) {
|
|
133
|
-
|
|
134
|
-
const t =
|
|
135
|
-
if (!
|
|
133
|
+
E.C.notify(E, STATUS_PENDING, 0);
|
|
134
|
+
const t = e !== undefined ? e : E.o?._;
|
|
135
|
+
if (!E.C.notify(E, STATUS_ERROR, STATUS_ERROR)) {
|
|
136
136
|
haltReactivity(unwrapStatusError(t));
|
|
137
137
|
throw t;
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
};
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
E.Ut = run;
|
|
142
|
+
E.C.enqueue(EFFECT_USER, run);
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
export { effect, trackedEffect };
|
|
@@ -46,11 +46,11 @@ function wireExternalSource(e) {
|
|
|
46
46
|
equals: false,
|
|
47
47
|
ownedWrite: true
|
|
48
48
|
});
|
|
49
|
-
const r = externalSourceConfig.factory(e.
|
|
49
|
+
const r = externalSourceConfig.factory(e.Se, () => {
|
|
50
50
|
setSignal(n, undefined);
|
|
51
51
|
});
|
|
52
52
|
cleanup(() => r.dispose());
|
|
53
|
-
e.
|
|
53
|
+
e.Se = e => {
|
|
54
54
|
read(n);
|
|
55
55
|
return r.track(e);
|
|
56
56
|
};
|
|
@@ -64,8 +64,8 @@ function externalUntrack(e) {
|
|
|
64
64
|
// removed on reset) so core's null checks stay equivalent to the old
|
|
65
65
|
// `externalSourceConfig` truthiness checks.
|
|
66
66
|
function syncExternalHooks() {
|
|
67
|
-
GlobalQueue.
|
|
68
|
-
GlobalQueue.
|
|
67
|
+
GlobalQueue.Rt = externalSourceConfig ? wireExternalSource : null;
|
|
68
|
+
GlobalQueue.Gt = externalSourceConfig ? externalUntrack : null;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
function enableExternalSource(e) {
|
package/dist/prod/core/graph.js
CHANGED
|
@@ -4,75 +4,84 @@ import { deleteFromHeap, queueFor } from "./heap.js";
|
|
|
4
4
|
|
|
5
5
|
import { disposeChildren } from "./owner.js";
|
|
6
6
|
|
|
7
|
-
import "./scheduler.js";
|
|
7
|
+
import { bumpNotifyEpoch } from "./scheduler.js";
|
|
8
8
|
|
|
9
9
|
// https://github.com/stackblitz/alien-signals/blob/v2.0.3/src/system.ts#L100
|
|
10
|
-
function unlinkSubs(
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const u =
|
|
14
|
-
const s =
|
|
15
|
-
if (u !== null) u.
|
|
16
|
-
if (s !== null) s.
|
|
17
|
-
|
|
10
|
+
function unlinkSubs(e) {
|
|
11
|
+
const l = e.ut;
|
|
12
|
+
const n = e.it;
|
|
13
|
+
const u = e.fe;
|
|
14
|
+
const s = e.el;
|
|
15
|
+
if (u !== null) u.el = s; else l.rt = s;
|
|
16
|
+
if (s !== null) s.fe = u; else {
|
|
17
|
+
l.u = u;
|
|
18
18
|
if (u === null) {
|
|
19
|
-
|
|
19
|
+
l.o?.ft?.();
|
|
20
20
|
// No more subscribers; only tear down if CONFIG_AUTO_DISPOSE is set.
|
|
21
21
|
// A pending node is exempt: its in-flight async work (or the
|
|
22
22
|
// transition holding it) is an observer — tearing down would orphan
|
|
23
23
|
// the work and re-execute it on the next read. The settle path runs
|
|
24
24
|
// this same last-one-out check when that observer releases (the
|
|
25
25
|
// untracked-read dispose in core.ts guards on pending identically).
|
|
26
|
-
const
|
|
27
|
-
|
|
26
|
+
const e = l;
|
|
27
|
+
e.Se && e.T & CONFIG_AUTO_DISPOSE && !(e.ie & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
return
|
|
30
|
+
return n;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
function trimStaleDeps(
|
|
34
|
-
const
|
|
35
|
-
let
|
|
36
|
-
if (
|
|
33
|
+
function trimStaleDeps(e) {
|
|
34
|
+
const l = e.Ye;
|
|
35
|
+
let n = l !== null ? l.it : e.nt;
|
|
36
|
+
if (n !== null) {
|
|
37
37
|
do {
|
|
38
|
-
|
|
39
|
-
} while (
|
|
40
|
-
if (
|
|
38
|
+
n = unlinkSubs(n);
|
|
39
|
+
} while (n !== null);
|
|
40
|
+
if (l !== null) l.it = null; else e.nt = null;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
l
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
// Shared by unobserved() and the disposeChildren child loop. The truthy guard
|
|
45
|
+
// (not `!== null`) matters: plain Owners in a child chain have no _deps field,
|
|
46
|
+
// and skipping early also avoids adding one (hidden-class churn) via the
|
|
47
|
+
// null-out below.
|
|
48
|
+
function clearDeps(e) {
|
|
49
|
+
let l = e.nt;
|
|
50
|
+
if (!l) return;
|
|
51
|
+
do {
|
|
52
|
+
l = unlinkSubs(l);
|
|
53
|
+
} while (l !== null);
|
|
54
|
+
e.nt = null;
|
|
55
|
+
e.Ye = null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function unobserved(e) {
|
|
59
|
+
deleteFromHeap(e, queueFor(e));
|
|
60
|
+
clearDeps(e);
|
|
61
|
+
disposeChildren(e, true);
|
|
53
62
|
}
|
|
54
63
|
|
|
55
64
|
// https://github.com/stackblitz/alien-signals/blob/v2.0.3/src/system.ts#L52
|
|
56
|
-
function link(l, n
|
|
65
|
+
function link(e, l, n = false) {
|
|
57
66
|
// Repeat touches within one pass AND-combine `_pendingObserver`: a probe
|
|
58
67
|
// read (`isPending(() => x())`) beside a value read of the same dep must
|
|
59
68
|
// not relabel the value dependency as probe-only — the value read is what
|
|
60
69
|
// real-error propagation and affects() coverage key off, regardless of
|
|
61
70
|
// read order within the computation.
|
|
62
|
-
const u =
|
|
63
|
-
if (u !== null && u.
|
|
64
|
-
u.
|
|
71
|
+
const u = l.Ye;
|
|
72
|
+
if (u !== null && u.ut === e) {
|
|
73
|
+
u.me &&= n;
|
|
65
74
|
return;
|
|
66
75
|
}
|
|
67
76
|
let s = null;
|
|
68
|
-
const
|
|
69
|
-
if (
|
|
70
|
-
s = u !== null ? u.
|
|
71
|
-
if (s !== null && s.
|
|
72
|
-
s.
|
|
73
|
-
|
|
77
|
+
const o = l.ie & REACTIVE_RECOMPUTING_DEPS;
|
|
78
|
+
if (o) {
|
|
79
|
+
s = u !== null ? u.it : l.nt;
|
|
80
|
+
if (s !== null && s.ut === e) {
|
|
81
|
+
s.ll = l.Ze;
|
|
82
|
+
l.Ye = s;
|
|
74
83
|
// First touch of this pass: the previous pass's label is stale.
|
|
75
|
-
s.
|
|
84
|
+
s.me = n;
|
|
76
85
|
return;
|
|
77
86
|
}
|
|
78
87
|
}
|
|
@@ -81,24 +90,26 @@ function link(l, n, e = false) {
|
|
|
81
90
|
// [deps.._depsTail] prefix — the O(1) equivalent of scanning the dep list
|
|
82
91
|
// (the old alien-signals `isValidLink` walk, O(n²) when a computation
|
|
83
92
|
// re-reads earlier deps non-consecutively, e.g. store leaf reads).
|
|
84
|
-
const
|
|
85
|
-
if (
|
|
93
|
+
const t = e.rt;
|
|
94
|
+
if (t !== null && t.ae === l && (!o || t.ll === l.Ze)) {
|
|
86
95
|
// Gen-matched during a recompute = repeat touch this pass (AND); outside
|
|
87
96
|
// a recompute there is no pass boundary, so the latest read labels it.
|
|
88
|
-
if (
|
|
97
|
+
if (o) t.me &&= n; else t.me = n;
|
|
89
98
|
return;
|
|
90
99
|
}
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
100
|
+
const i = l.Ye = e.rt = {
|
|
101
|
+
ut: e,
|
|
102
|
+
ae: l,
|
|
103
|
+
it: s,
|
|
104
|
+
el: t,
|
|
105
|
+
fe: null,
|
|
106
|
+
ll: l.Ze,
|
|
107
|
+
me: n
|
|
99
108
|
};
|
|
100
|
-
if (u !== null) u.
|
|
101
|
-
if (
|
|
109
|
+
if (u !== null) u.it = i; else l.nt = i;
|
|
110
|
+
if (t !== null) t.fe = i; else e.u = i;
|
|
111
|
+
// New subscriber edge: staged-rewrite skips (§12d) must not miss it.
|
|
112
|
+
bumpNotifyEpoch();
|
|
102
113
|
}
|
|
103
114
|
|
|
104
|
-
export { link, trimStaleDeps, unlinkSubs, unobserved };
|
|
115
|
+
export { clearDeps, link, trimStaleDeps, unlinkSubs, unobserved };
|
package/dist/prod/core/heap.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "./core.js";
|
|
2
|
+
|
|
3
|
+
import { REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, EFFECT_TRACKED, EFFECT_USER, REACTIVE_RECOMPUTING_DEPS, REACTIVE_MANUAL_WRITE, REACTIVE_ZOMBIE, REACTIVE_CHECK, REACTIVE_DIRTY, CONFIG_FW_CHILDREN } from "./constants.js";
|
|
2
4
|
|
|
3
5
|
import { zombieQueue, dirtyQueue } from "./scheduler.js";
|
|
4
6
|
|
|
5
7
|
/** The queue a node belongs to, picked from its own zombie flag. */ function queueFor(e) {
|
|
6
|
-
return e.
|
|
8
|
+
return e.ie & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue;
|
|
7
9
|
}
|
|
8
10
|
|
|
9
11
|
/**
|
|
@@ -13,21 +15,21 @@ import { zombieQueue, dirtyQueue } from "./scheduler.js";
|
|
|
13
15
|
*/ function enqueueSub(e) {
|
|
14
16
|
if (e.Re === EFFECT_TRACKED) {
|
|
15
17
|
const E = e;
|
|
16
|
-
if (!E.
|
|
17
|
-
E.
|
|
18
|
-
E.C.enqueue(EFFECT_USER, E.
|
|
18
|
+
if (!E.Xe) {
|
|
19
|
+
E.Xe = true;
|
|
20
|
+
E.C.enqueue(EFFECT_USER, E.Ut);
|
|
19
21
|
}
|
|
20
22
|
return;
|
|
21
23
|
}
|
|
22
24
|
const E = queueFor(e);
|
|
23
|
-
if (E.
|
|
25
|
+
if (E.xe > e.Le) E.xe = e.Le;
|
|
24
26
|
insertIntoHeap(e, E);
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
function actualInsertIntoHeap(e, E) {
|
|
28
|
-
const t = (e.
|
|
29
|
-
if (t >= e.
|
|
30
|
-
const n = e.
|
|
30
|
+
const t = (e.ve?.Ct ? e.ve.Ot?.Le : e.ve?.Le) ?? -1;
|
|
31
|
+
if (t >= e.Le) e.Le = t + 1;
|
|
32
|
+
const n = e.Le;
|
|
31
33
|
const I = E.eE[n];
|
|
32
34
|
if (I === undefined) E.eE[n] = e; else {
|
|
33
35
|
const E = I.st;
|
|
@@ -39,12 +41,15 @@ function actualInsertIntoHeap(e, E) {
|
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
function insertIntoHeap(e, E) {
|
|
42
|
-
let t = e.
|
|
43
|
-
|
|
44
|
+
let t = e.ie;
|
|
45
|
+
// RECOMPUTING refusals are not always losses: a genuinely missed wake (a
|
|
46
|
+
// write to a link this pass already validated) is latched link-side in
|
|
47
|
+
// insertSubs as REACTIVE_MISSED_WAKE for recompute's tail (#3037).
|
|
48
|
+
if (t & (REACTIVE_IN_HEAP | REACTIVE_RECOMPUTING_DEPS | REACTIVE_MANUAL_WRITE)) return;
|
|
44
49
|
if (t & REACTIVE_CHECK) {
|
|
45
|
-
e.
|
|
50
|
+
e.ie = t & -4 | REACTIVE_DIRTY | REACTIVE_IN_HEAP;
|
|
46
51
|
} else {
|
|
47
|
-
e.
|
|
52
|
+
e.ie = t | REACTIVE_IN_HEAP;
|
|
48
53
|
// An unmarked node entering a marked heap invalidates the markHeap memo:
|
|
49
54
|
// `_marked` is only reset by runHeap, so a write between two mid-tick
|
|
50
55
|
// pulls (read-time markHeap + updateIfNecessary) would otherwise leave
|
|
@@ -56,17 +61,17 @@ function insertIntoHeap(e, E) {
|
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
function insertIntoHeapHeight(e, E) {
|
|
59
|
-
let t = e.
|
|
64
|
+
let t = e.ie;
|
|
60
65
|
if (t & (REACTIVE_IN_HEAP | REACTIVE_RECOMPUTING_DEPS | REACTIVE_IN_HEAP_HEIGHT | REACTIVE_MANUAL_WRITE)) return;
|
|
61
|
-
e.
|
|
66
|
+
e.ie = t | REACTIVE_IN_HEAP_HEIGHT;
|
|
62
67
|
actualInsertIntoHeap(e, E);
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
function deleteFromHeap(e, E) {
|
|
66
|
-
const t = e.
|
|
71
|
+
const t = e.ie;
|
|
67
72
|
if (!(t & (REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT))) return;
|
|
68
|
-
e.
|
|
69
|
-
const n = e.
|
|
73
|
+
e.ie = t & -25;
|
|
74
|
+
const n = e.Le;
|
|
70
75
|
if (e.st === e) E.eE[n] = undefined; else {
|
|
71
76
|
const t = e.ot;
|
|
72
77
|
const I = E.eE[n];
|
|
@@ -83,22 +88,25 @@ function markHeap(e) {
|
|
|
83
88
|
e.tE = true;
|
|
84
89
|
for (let E = 0; E <= e.EE; E++) {
|
|
85
90
|
for (let t = e.eE[E]; t !== undefined; t = t.ot) {
|
|
86
|
-
if (t.
|
|
91
|
+
if (t.ie & REACTIVE_IN_HEAP) markNode(t);
|
|
87
92
|
}
|
|
88
93
|
}
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
function markNode(e, E = REACTIVE_DIRTY) {
|
|
92
|
-
const t = e.
|
|
97
|
+
const t = e.ie;
|
|
93
98
|
if ((t & (REACTIVE_CHECK | REACTIVE_DIRTY)) >= E) return;
|
|
94
|
-
e.
|
|
95
|
-
for (let E = e.
|
|
96
|
-
markNode(E.
|
|
99
|
+
e.ie = t & -4 | E;
|
|
100
|
+
for (let E = e.u; E !== null; E = E.fe) {
|
|
101
|
+
markNode(E.ae, REACTIVE_CHECK);
|
|
97
102
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
103
|
+
// Firewall children (projection machinery only): gate the cold-extension
|
|
104
|
+
// deref on the config bit — markNode runs per sub edge per write, and an
|
|
105
|
+
// unconditional _x chase here taxed every propagation (diamond -22%).
|
|
106
|
+
if (e.T & CONFIG_FW_CHILDREN) {
|
|
107
|
+
for (let E = e.o.l; E !== null; E = E.ce) {
|
|
108
|
+
for (let e = E.u; e !== null; e = e.fe) {
|
|
109
|
+
markNode(e.ae, REACTIVE_CHECK);
|
|
102
110
|
}
|
|
103
111
|
}
|
|
104
112
|
}
|
|
@@ -106,11 +114,11 @@ function markNode(e, E = REACTIVE_DIRTY) {
|
|
|
106
114
|
|
|
107
115
|
function runHeap(e, E) {
|
|
108
116
|
e.tE = false;
|
|
109
|
-
for (e.
|
|
110
|
-
let t = e.eE[e.
|
|
117
|
+
for (e.xe = 0; e.xe <= e.EE; e.xe++) {
|
|
118
|
+
let t = e.eE[e.xe];
|
|
111
119
|
while (t !== undefined) {
|
|
112
|
-
if (t.
|
|
113
|
-
t = e.eE[e.
|
|
120
|
+
if (t.ie & REACTIVE_IN_HEAP) E(t); else adjustHeight(t, e);
|
|
121
|
+
t = e.eE[e.xe];
|
|
114
122
|
}
|
|
115
123
|
}
|
|
116
124
|
e.EE = 0;
|
|
@@ -118,21 +126,21 @@ function runHeap(e, E) {
|
|
|
118
126
|
|
|
119
127
|
function adjustHeight(e, E) {
|
|
120
128
|
deleteFromHeap(e, E);
|
|
121
|
-
let t = e.
|
|
122
|
-
for (let E = e.
|
|
123
|
-
const e = E.
|
|
129
|
+
let t = e.Le;
|
|
130
|
+
for (let E = e.nt; E; E = E.it) {
|
|
131
|
+
const e = E.ut;
|
|
124
132
|
const n = e.lt || e;
|
|
125
|
-
if (n.
|
|
133
|
+
if (n.Se && n.Le >= t) t = n.Le + 1;
|
|
126
134
|
}
|
|
127
|
-
if (e.
|
|
128
|
-
e.
|
|
129
|
-
for (let E = e.
|
|
135
|
+
if (e.Le !== t) {
|
|
136
|
+
e.Le = t;
|
|
137
|
+
for (let E = e.u; E !== null; E = E.fe) {
|
|
130
138
|
// Route each subscriber by its own zombie flag, mirroring the
|
|
131
139
|
// post-recompute height-adjust path. Inserting into the running `heap`
|
|
132
140
|
// unconditionally can park a zombie in `dirtyQueue` (or a live node in
|
|
133
141
|
// `zombieQueue`), breaking the flag/queue invariant `deleteFromHeap`
|
|
134
142
|
// relies on — the same corruption class as #2759.
|
|
135
|
-
insertIntoHeapHeight(E.
|
|
143
|
+
insertIntoHeapHeight(E.ae, queueFor(E.ae));
|
|
136
144
|
}
|
|
137
145
|
}
|
|
138
146
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import "./core.js";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* INV-2: a node with an *active* override must be registered for reversion in
|
|
3
5
|
* the queue's or a transition's `_optimisticNodes`. An unregistered active
|
|
@@ -6,8 +8,7 @@
|
|
|
6
8
|
* (There is no revert-target requirement: authoritative values commit
|
|
7
9
|
* silently into `_value` under the override mask — A17 — so reverting is
|
|
8
10
|
* just dropping the override.)
|
|
9
|
-
*/
|
|
10
|
-
function devCheckActiveOverrides(e) {
|
|
11
|
+
*/ function devCheckActiveOverrides(e) {
|
|
11
12
|
return;
|
|
12
13
|
}
|
|
13
14
|
|