@solidjs/signals 2.0.0-rc.7 → 2.0.0-rc.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dev-shared.js +6357 -0
- package/dist/dev.attribution.js +2485 -0
- package/dist/dev.js +2851 -7027
- package/dist/observe/affects.js +129 -0
- package/dist/observe/attribution.js +7 -0
- package/dist/observe/boundaries.js +641 -0
- package/dist/observe/core/action.js +177 -0
- package/dist/observe/core/async.js +846 -0
- package/dist/observe/core/attribution-costs.js +66 -0
- package/dist/observe/core/attribution-feedback.js +282 -0
- package/dist/observe/core/attribution-hooks.js +82 -0
- package/dist/observe/core/attribution-queries.js +28 -0
- package/dist/observe/core/attribution.js +2095 -0
- package/dist/observe/core/constants.js +248 -0
- package/dist/{types-cjs/core/context.d.cts → observe/core/context.js} +37 -12
- package/dist/observe/core/core.js +1789 -0
- package/dist/observe/core/dev.js +293 -0
- package/dist/observe/core/effect.js +195 -0
- package/dist/observe/core/error-hooks.js +71 -0
- package/dist/observe/core/error.js +80 -0
- package/dist/observe/core/external.js +98 -0
- package/dist/observe/core/graph.js +155 -0
- package/dist/observe/core/heap.js +147 -0
- package/dist/observe/core/invariants.js +45 -0
- package/dist/observe/core/lanes.js +205 -0
- package/dist/observe/core/optimistic.js +585 -0
- package/dist/observe/core/owner.js +322 -0
- package/dist/observe/core/scheduler.js +1428 -0
- package/dist/observe/core/verdict.js +669 -0
- package/dist/observe/index.js +59 -0
- package/dist/observe/map.js +484 -0
- package/dist/{types-cjs/signals.d.cts → observe/signals.js} +311 -317
- package/dist/observe/store/index.js +40 -0
- package/dist/observe/store/next/optimistic.js +660 -0
- package/dist/observe/store/next/projection.js +255 -0
- package/dist/observe/store/next/reconcile.js +375 -0
- package/dist/observe/store/next/store.js +2327 -0
- package/dist/observe/store/next/target.js +51 -0
- package/dist/observe/store/store.js +325 -0
- package/dist/observe/store/storePath.js +103 -0
- package/dist/observe/store/utils.js +1023 -0
- package/dist/prod/attribution.js +41 -0
- package/dist/prod/boundaries.js +128 -76
- package/dist/prod/core/action.js +65 -37
- package/dist/prod/core/async.js +324 -172
- package/dist/prod/core/constants.js +57 -1
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +1003 -364
- package/dist/prod/core/dev.js +17 -1
- package/dist/prod/core/effect.js +76 -42
- package/dist/prod/core/error-hooks.js +71 -0
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +37 -37
- package/dist/prod/core/heap.js +61 -56
- package/dist/prod/core/lanes.js +107 -63
- package/dist/prod/core/optimistic.js +374 -93
- package/dist/prod/core/owner.js +62 -46
- package/dist/prod/core/scheduler.js +653 -286
- package/dist/prod/core/verdict.js +245 -127
- package/dist/prod/index.js +15 -5
- package/dist/prod/map.js +292 -141
- package/dist/prod/signals.js +29 -16
- package/dist/prod/store/next/optimistic.js +77 -55
- package/dist/prod/store/next/projection.js +31 -20
- package/dist/prod/store/next/reconcile.js +87 -84
- package/dist/prod/store/next/store.js +718 -401
- package/dist/prod/store/next/target.js +32 -10
- package/dist/prod/store/store.js +12 -14
- package/dist/prod/store/utils.js +950 -130
- package/dist/types/attribution.d.ts +20 -0
- package/dist/types/attribution.prod.d.ts +22 -0
- package/dist/types/boundaries.d.ts +10 -1
- package/dist/types/core/action.d.ts +12 -5
- package/dist/types/core/async.d.ts +1 -1
- package/dist/types/core/attribution-costs.d.ts +35 -0
- package/dist/types/core/attribution-feedback.d.ts +133 -0
- package/dist/types/core/attribution-hooks.d.ts +147 -9
- package/dist/types/core/attribution-queries.d.ts +10 -0
- package/dist/types/core/attribution.d.ts +216 -182
- package/dist/types/core/constants.d.ts +56 -0
- package/dist/types/core/core.d.ts +190 -2
- package/dist/types/core/dev.d.ts +246 -28
- package/dist/types/core/effect.d.ts +8 -1
- package/dist/types/core/error-hooks.d.ts +71 -0
- package/dist/types/core/index.d.ts +4 -1
- package/dist/types/core/invariants.d.ts +4 -0
- package/dist/types/core/lanes.d.ts +39 -5
- package/dist/types/core/scheduler.d.ts +153 -0
- package/dist/types/core/types.d.ts +32 -9
- package/dist/types/index.d.ts +10 -3
- package/dist/types/map.d.ts +2 -0
- package/dist/types/signals.d.ts +8 -0
- package/dist/types/store/index.d.ts +2 -0
- package/dist/types/store/next/optimistic.d.ts +1 -1
- package/dist/types/store/next/store.d.ts +11 -5
- package/dist/types/store/next/target.d.ts +37 -10
- package/dist/types/store/utils.d.ts +178 -0
- package/package.json +21 -17
- package/dist/node.cjs +0 -10541
- package/dist/node.dev.cjs +0 -13724
- package/dist/types-cjs/affects.d.cts +0 -47
- package/dist/types-cjs/boundaries.d.cts +0 -175
- package/dist/types-cjs/core/action.d.cts +0 -64
- package/dist/types-cjs/core/async.d.cts +0 -23
- package/dist/types-cjs/core/attribution-hooks.d.cts +0 -127
- package/dist/types-cjs/core/attribution.d.cts +0 -495
- package/dist/types-cjs/core/constants.d.cts +0 -159
- package/dist/types-cjs/core/core.d.cts +0 -185
- package/dist/types-cjs/core/dev.d.cts +0 -136
- package/dist/types-cjs/core/effect.d.cts +0 -30
- package/dist/types-cjs/core/error.d.cts +0 -56
- package/dist/types-cjs/core/external.d.cts +0 -15
- package/dist/types-cjs/core/graph.d.cts +0 -28
- package/dist/types-cjs/core/heap.d.cts +0 -24
- package/dist/types-cjs/core/index.d.cts +0 -13
- package/dist/types-cjs/core/invariants.d.cts +0 -59
- package/dist/types-cjs/core/lanes.d.cts +0 -54
- package/dist/types-cjs/core/optimistic.d.cts +0 -6
- package/dist/types-cjs/core/owner.d.cts +0 -124
- package/dist/types-cjs/core/scheduler.d.cts +0 -236
- package/dist/types-cjs/core/types.d.cts +0 -204
- package/dist/types-cjs/core/verdict.d.cts +0 -2
- package/dist/types-cjs/index.d.cts +0 -10
- package/dist/types-cjs/map.d.cts +0 -69
- package/dist/types-cjs/package.json +0 -3
- package/dist/types-cjs/store/index.d.cts +0 -18
- package/dist/types-cjs/store/next/optimistic.d.cts +0 -25
- package/dist/types-cjs/store/next/projection.d.cts +0 -8
- package/dist/types-cjs/store/next/reconcile.d.cts +0 -10
- package/dist/types-cjs/store/next/store.d.cts +0 -121
- package/dist/types-cjs/store/next/target.d.cts +0 -166
- package/dist/types-cjs/store/store.d.cts +0 -143
- package/dist/types-cjs/store/storePath.d.cts +0 -58
- package/dist/types-cjs/store/utils.d.cts +0 -74
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { EFFECT_RENDER, EFFECT_USER, STATUS_PENDING, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_DISPOSED, CONFIG_HELD_TRUTH, REACTIVE_ZOMBIE, CONFIG_HAS_LANE, CONFIG_HAS_SNAPSHOT, REACTIVE_RECOMPUTING_DEPS, REACTIVE_MISSED_WAKE, CONFIG_IN_SNAPSHOT_SCOPE, REACTIVE_SNAPSHOT_STALE,
|
|
1
|
+
import { EFFECT_RENDER, EFFECT_USER, STATUS_PENDING, CONFIG_ADOPTED_UNFLUSHED, NOT_PENDING, REACTIVE_MANUAL_WRITE, STATUS_UNINITIALIZED, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_DISPOSED, CONFIG_HELD_TRUTH, CONFIG_HAS_COMPANIONS, EFFECT_TRACKED, CONFIG_INPUTS_PUBLISHED, REACTIVE_ZOMBIE, CONFIG_HAS_LANE, CONFIG_HAS_SNAPSHOT, REACTIVE_RECOMPUTING_DEPS, REACTIVE_MISSED_WAKE, CONFIG_IN_SNAPSHOT_SCOPE, REACTIVE_SNAPSHOT_STALE, CONFIG_SLOT_NODE, CONFIG_HELD_CHILDREN, REACTIVE_REASK, CONFIG_AUTHORITATIVE_READ } from "./constants.js";
|
|
2
2
|
|
|
3
|
-
import { currentOptimisticLane, slotUnobservedHook } from "./core.js";
|
|
3
|
+
import { resyncUnflushedCompanions, currentOptimisticLane, slotUnobservedHook, markUnflushedStaged, hasActiveOverride } from "./core.js";
|
|
4
4
|
|
|
5
5
|
import { DEV } from "./dev.js";
|
|
6
6
|
|
|
7
7
|
import { NotReadyError } from "./error.js";
|
|
8
8
|
|
|
9
|
-
import { sweepDormant } from "./graph.js";
|
|
9
|
+
import { sweepDormant, trimStaleDeps } from "./graph.js";
|
|
10
10
|
|
|
11
11
|
import { runHeap, deleteFromHeap, enqueueSub } from "./heap.js";
|
|
12
12
|
|
|
13
|
-
import { activeLanes, assignOrMergeLane, findLane
|
|
13
|
+
import { activeLanes, assignOrMergeLane, findLane } from "./lanes.js";
|
|
14
14
|
|
|
15
15
|
export { getOrCreateLane, mergeLanes, resolveLane } from "./lanes.js";
|
|
16
16
|
|
|
@@ -21,23 +21,31 @@ const transitions = new Set;
|
|
|
21
21
|
const dirtyQueue = {
|
|
22
22
|
eE: new Array(2e3).fill(undefined),
|
|
23
23
|
tE: false,
|
|
24
|
-
|
|
24
|
+
et: 0,
|
|
25
25
|
EE: 0
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
const zombieQueue = {
|
|
29
29
|
eE: new Array(2e3).fill(undefined),
|
|
30
30
|
tE: false,
|
|
31
|
-
|
|
31
|
+
et: 0,
|
|
32
32
|
EE: 0
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
/** runHeap callback that discards a queued zombie recompute instead of running
|
|
36
36
|
* it: unlink pure recompute entries; strip just the recompute bit from dirtied
|
|
37
|
-
* height-adjust entries so their height work still happens.
|
|
38
|
-
|
|
37
|
+
* height-adjust entries so their height work still happens. A zombie dirtied
|
|
38
|
+
* through the lane channel (OPTIMISTIC_DIRTY — an override or a `latest()`
|
|
39
|
+
* companion) runs instead (#3444): a zombie renders mainline until the commit
|
|
40
|
+
* disposes it, and the lane's values ARE the mainline frame — the still-visible
|
|
41
|
+
* branch a held `Show` is removing showed the old `latest(count)` beside the
|
|
42
|
+
* new one outside. Its pass runs under the lane and its run lands on the
|
|
43
|
+
* lane's effect queue, so a held lane defers it exactly as it defers every
|
|
44
|
+
* other reader's. */ function cancelZombieRecompute(e) {
|
|
45
|
+
if (e.ue & REACTIVE_OPTIMISTIC_DIRTY) return GlobalQueue.We(e);
|
|
46
|
+
if (e.ue & REACTIVE_IN_HEAP_HEIGHT) e.ue &= -12; else {
|
|
39
47
|
deleteFromHeap(e, zombieQueue);
|
|
40
|
-
e.
|
|
48
|
+
e.ue &= -4;
|
|
41
49
|
}
|
|
42
50
|
}
|
|
43
51
|
|
|
@@ -55,15 +63,34 @@ let syncDepth = 0;
|
|
|
55
63
|
|
|
56
64
|
let projectionWriteActive = false;
|
|
57
65
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
66
|
+
/** > 0 while an action's generator body is on the stack (the synchronous
|
|
67
|
+
* slice between yields). Maintained by action.ts around `it.next()`. */ let actionStepDepth = 0;
|
|
68
|
+
|
|
69
|
+
function enterActionStep() {
|
|
70
|
+
actionStepDepth++;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function exitActionStep() {
|
|
74
|
+
actionStepDepth--;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Store property nodes whose last subscriber left while they carried state
|
|
78
|
+
// the backing cannot reconstruct — an optimistic override (overrides live on
|
|
79
|
+
// nodes, over a clone the setter discards) or a staged write. Releasing the
|
|
80
|
+
// slot then would drop the override: an optimistic store key read `0` the
|
|
81
|
+
// moment its only reader gated away while the action was live (S7). Swept
|
|
82
|
+
// after each flush — a node still without subs whose override and staging
|
|
83
|
+
// have resolved is released through the slot hook; one that regained a
|
|
84
|
+
// subscriber leaves the set.
|
|
62
85
|
const transientStoreNodes = new Set;
|
|
63
86
|
|
|
87
|
+
/** Slot hook's deferral: release this node when its carried state resolves. */ function deferSlotRelease(e) {
|
|
88
|
+
transientStoreNodes.add(e);
|
|
89
|
+
}
|
|
90
|
+
|
|
64
91
|
function canUseSimpleSyncFlush(e) {
|
|
65
92
|
const t = e.m;
|
|
66
|
-
return transitions.size === 0 && activeLanes.size === 0 && e.
|
|
93
|
+
return transitions.size === 0 && activeLanes.size === 0 && e.hn.length === 0 && t.rt.length === 0 && t.A.length === 0 && t.dn.size === 0 && transientStoreNodes.size === 0;
|
|
67
94
|
}
|
|
68
95
|
|
|
69
96
|
function sweepTransientStoreNodes() {
|
|
@@ -73,14 +100,14 @@ function sweepTransientStoreNodes() {
|
|
|
73
100
|
transientStoreNodes.delete(e);
|
|
74
101
|
continue;
|
|
75
102
|
}
|
|
76
|
-
if (e.
|
|
77
|
-
if (e.o?.
|
|
103
|
+
if (e.ve !== NOT_PENDING) continue;
|
|
104
|
+
if (e.o?.Ce !== undefined && e.o?.Ce !== NOT_PENDING) continue;
|
|
78
105
|
// A live affects() mark keeps the node addressable: sweeping it would
|
|
79
106
|
// detach the refcount from the slot (a fresh probe would upsert a new,
|
|
80
107
|
// unmarked node for the same property).
|
|
81
108
|
if (e.o?.t) continue;
|
|
82
109
|
transientStoreNodes.delete(e);
|
|
83
|
-
if (e.T & CONFIG_SLOT_NODE) slotUnobservedHook(e); else e.o?.
|
|
110
|
+
if (e.T & CONFIG_SLOT_NODE) slotUnobservedHook(e); else e.o?.Pt?.();
|
|
84
111
|
}
|
|
85
112
|
}
|
|
86
113
|
|
|
@@ -107,32 +134,34 @@ function setProjectionWriteActive(e) {
|
|
|
107
134
|
* land there directly — no per-field aliasing.
|
|
108
135
|
*/ function createBatch() {
|
|
109
136
|
return {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
137
|
+
Pe: clock,
|
|
138
|
+
Ot: [],
|
|
139
|
+
oe: new Map,
|
|
140
|
+
rt: [],
|
|
114
141
|
A: [],
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
142
|
+
dn: new Set,
|
|
143
|
+
pe: [],
|
|
144
|
+
Sn: {
|
|
145
|
+
mn: [ [], [] ],
|
|
146
|
+
hn: []
|
|
120
147
|
},
|
|
121
|
-
|
|
122
|
-
|
|
148
|
+
Tt: false,
|
|
149
|
+
ct: new Set,
|
|
150
|
+
St: null
|
|
123
151
|
};
|
|
124
152
|
}
|
|
125
153
|
|
|
126
154
|
function mergeTransitionState(e, t) {
|
|
127
|
-
t.
|
|
128
|
-
e.
|
|
129
|
-
|
|
130
|
-
if (t
|
|
155
|
+
t.Tt = e;
|
|
156
|
+
e.pe.push(...t.pe);
|
|
157
|
+
e.he ||= t.he;
|
|
158
|
+
for (const n of activeLanes) if (n.Ge === t) n.Ge = e;
|
|
159
|
+
if (t.rt.length) {
|
|
131
160
|
// Move (don't copy): the global queue's batch may still be the outgoing
|
|
132
161
|
// transition, and the adoption pass in initTransition would re-push its
|
|
133
162
|
// contents into the target — duplicating every entry.
|
|
134
|
-
e.
|
|
135
|
-
t.
|
|
163
|
+
e.rt.push(...t.rt);
|
|
164
|
+
t.rt.length = 0;
|
|
136
165
|
}
|
|
137
166
|
if (t.A.length) {
|
|
138
167
|
// Move (don't copy): the global queue's batch may still be the outgoing
|
|
@@ -141,13 +170,14 @@ function mergeTransitionState(e, t) {
|
|
|
141
170
|
e.A.push(...t.A);
|
|
142
171
|
t.A.length = 0;
|
|
143
172
|
}
|
|
144
|
-
for (const
|
|
145
|
-
for (const [
|
|
146
|
-
let t = e.
|
|
147
|
-
if (!t) e.
|
|
148
|
-
for (const e of
|
|
173
|
+
for (const n of t.dn) e.dn.add(n);
|
|
174
|
+
for (const [n, i] of t.oe) {
|
|
175
|
+
let t = e.oe.get(n);
|
|
176
|
+
if (!t) e.oe.set(n, t = new Set);
|
|
177
|
+
for (const e of i) t.add(e);
|
|
149
178
|
}
|
|
150
|
-
for (const
|
|
179
|
+
for (const n of t.ct) e.ct.add(n);
|
|
180
|
+
if (t.St) (e.St ??= []).push(...t.St);
|
|
151
181
|
}
|
|
152
182
|
|
|
153
183
|
/**
|
|
@@ -168,7 +198,7 @@ function mergeTransitionState(e, t) {
|
|
|
168
198
|
* `until()` retain this.
|
|
169
199
|
*/ function entangleConfirmingTransitions(e, t) {
|
|
170
200
|
t = currentTransition(t);
|
|
171
|
-
if (t.
|
|
201
|
+
if (t.Tt === true) return;
|
|
172
202
|
// The confirming evidence is a dep whose value is STAGED (pending,
|
|
173
203
|
// uncommitted) at flip evaluation — committed deps are public already and
|
|
174
204
|
// carry nothing to entangle. A staged dep lives in one of two carriers: a
|
|
@@ -180,22 +210,22 @@ function mergeTransitionState(e, t) {
|
|
|
180
210
|
// and stashes are its own future (a live stream's flight must not chain
|
|
181
211
|
// the awaiting transaction to landings that haven't happened; a merged
|
|
182
212
|
// reporter deadlocked exactly that way).
|
|
183
|
-
let
|
|
184
|
-
for (let
|
|
185
|
-
const
|
|
186
|
-
if (
|
|
187
|
-
const e =
|
|
188
|
-
const
|
|
213
|
+
let n = false;
|
|
214
|
+
for (let i = e.Se; i !== null; i = i.de) {
|
|
215
|
+
const s = i.Ee;
|
|
216
|
+
if (s.ve !== NOT_PENDING) {
|
|
217
|
+
const e = s.Ge;
|
|
218
|
+
const i = e != null ? currentTransition(e) : null;
|
|
189
219
|
// Skip the awaiting transaction's own cargo (t === target: a
|
|
190
220
|
// fold-staged landing or a write the action itself issued — hold and
|
|
191
221
|
// reveal already correct) and dead carriers. Ambient-batch staging
|
|
192
222
|
// (t === null) must leave the batch NOW — it commits at this flush's
|
|
193
223
|
// end, which would reveal the confirmation under the live optimism
|
|
194
224
|
// it just confirmed.
|
|
195
|
-
const
|
|
196
|
-
if (
|
|
225
|
+
const r = i === null ? currentBatch.Ot : i !== t && i.Tt !== true ? i.Ot : null;
|
|
226
|
+
if (r !== null) n = stealEntangledCargo(r, t) || n;
|
|
197
227
|
}
|
|
198
|
-
if (
|
|
228
|
+
if (i === e.ot) break;
|
|
199
229
|
}
|
|
200
230
|
// The steal never activates the awaiting transaction: the predicate can
|
|
201
231
|
// flip inside another transaction's finalize heap, and adopting the queue
|
|
@@ -224,20 +254,20 @@ function mergeTransitionState(e, t) {
|
|
|
224
254
|
* committed value — while re-running them would re-derive the OLD world
|
|
225
255
|
* and re-stage it over the held truth. The reveal re-notifies
|
|
226
256
|
* (commitPendingNodes), which is when they re-derive for real. */ function stealEntangledCargo(e, t) {
|
|
227
|
-
if (e === t.
|
|
228
|
-
for (let
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
t.
|
|
257
|
+
if (e === t.Ot || e.length === 0) return false;
|
|
258
|
+
for (let n = 0; n < e.length; n++) {
|
|
259
|
+
const i = e[n];
|
|
260
|
+
i.Ge = t;
|
|
261
|
+
t.Ot.push(i);
|
|
232
262
|
// Override-covered nodes stay silent AND unmasked: the override is the
|
|
233
263
|
// display (A17 — its staging never notified, its revert will), so their
|
|
234
264
|
// subs saw nothing and re-running one would break the silence with a
|
|
235
265
|
// duplicate fire of an unchanged view.
|
|
236
|
-
if (!hasActiveOverride(
|
|
237
|
-
|
|
238
|
-
for (let e =
|
|
239
|
-
const t = e.
|
|
240
|
-
if (t.
|
|
266
|
+
if (!hasActiveOverride(i)) {
|
|
267
|
+
i.T |= CONFIG_HELD_TRUTH;
|
|
268
|
+
for (let e = i.u; e !== null; e = e.Ne) {
|
|
269
|
+
const t = e._e;
|
|
270
|
+
if (t.Le && !(t.T & CONFIG_AUTHORITATIVE_READ)) enqueueSub(t);
|
|
241
271
|
}
|
|
242
272
|
}
|
|
243
273
|
}
|
|
@@ -253,21 +283,66 @@ function schedule() {
|
|
|
253
283
|
}
|
|
254
284
|
if (scheduled) return;
|
|
255
285
|
scheduled = true;
|
|
256
|
-
if (!syncDepth && !globalQueue.
|
|
286
|
+
if (!syncDepth && !globalQueue.Kt && !projectionWriteActive) queueMicrotask(flush);
|
|
257
287
|
}
|
|
258
288
|
|
|
289
|
+
/**
|
|
290
|
+
* Parked transactions whose reporter set changed without a write. A
|
|
291
|
+
* transaction completes when nothing live reports a flight it waits on, but
|
|
292
|
+
* the flush only judges the ACTIVE transaction: a parked one is re-entered by
|
|
293
|
+
* a stamped node's landing or an action's resume. A reporter that stops
|
|
294
|
+
* counting for another reason — its loading boundary flipped to the fallback
|
|
295
|
+
* (#3375), or it was disposed by ambient work (#3372) — is neither: the
|
|
296
|
+
* pruning in `reporterBlocksSource` would drop it at the next check, but no
|
|
297
|
+
* check comes, and the writes held with it stay staged. Such sites record the
|
|
298
|
+
* transaction here (deduped: one idle pass per transaction, however many
|
|
299
|
+
* reporters changed); the flush re-enters it on an otherwise idle pass, so
|
|
300
|
+
* the re-evaluation adopts no unrelated ambient work.
|
|
301
|
+
*/ const wokenTransitions = [];
|
|
302
|
+
|
|
303
|
+
/** Wake every parked transaction — for a site that knows a reporter stopped
|
|
304
|
+
* counting but not whose (a boundary reset). */ function wakeParked() {
|
|
305
|
+
for (const e of transitions) wokenTransitions.includes(e) || wokenTransitions.push(e);
|
|
306
|
+
schedule();
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** Transactions a mainline tick has PROPOSED against (A34, #3494): a write to a
|
|
310
|
+
* node one of them holds — the same value or another — is a second proposal
|
|
311
|
+
* on a contested node, and the tick reveals with the hold ("both are
|
|
312
|
+
* suggesting a value; if one finished before the other that would be odd").
|
|
313
|
+
* Entered at the next flush's start, where the ambient batch is adopted;
|
|
314
|
+
* never from the write itself, which left `activeTransition` set across the
|
|
315
|
+
* caller's block and made creation after the write the transaction's (A29). */ const batchJoins = [];
|
|
316
|
+
|
|
259
317
|
/**
|
|
260
318
|
* Permanently halts the reactive system. Called when a user error escapes
|
|
261
319
|
* every boundary — app state is undefined at that point, so scheduling stops
|
|
262
320
|
* entirely rather than limping along with a half-applied update.
|
|
263
|
-
*/
|
|
321
|
+
*/
|
|
322
|
+
/**
|
|
323
|
+
* The key a root owner carries its client error hook under (`render`'s
|
|
324
|
+
* `onError`) — registered, so a runtime writes it with no import of the hook
|
|
325
|
+
* module (core/error-hooks.ts) and no property mangling in the way. Defined
|
|
326
|
+
* HERE, not there: a runtime that only writes the key must not retain the
|
|
327
|
+
* hook machinery (pay-for-use).
|
|
328
|
+
*/ const ROOT_ERROR_HOOK = Symbol.for("solid-js/root-error-hook");
|
|
329
|
+
|
|
330
|
+
function haltReactivity(e) {
|
|
264
331
|
if (halted) return;
|
|
265
332
|
halted = true;
|
|
266
333
|
let t = "[REACTIVITY_HALTED]";
|
|
267
|
-
//
|
|
334
|
+
// Surface the cause here too: callers rethrow it, but a creation-time throw
|
|
268
335
|
// unwinds through ancestor recomputes that convert it to status instead of
|
|
269
336
|
// surfacing it (#2884), so the rethrow alone cannot guarantee visibility.
|
|
270
|
-
|
|
337
|
+
// Where the platform has one, hand the cause to its uncaught-error channel
|
|
338
|
+
// (`reportError` → `error` event → window.onerror / error monitoring): a
|
|
339
|
+
// halt that only reaches console.error leaves a page that LOOKS alive with
|
|
340
|
+
// nothing an app or its telemetry can act on (#3338 — an uncaught throw
|
|
341
|
+
// during the hydration render). The rethrow may reach the top as well in
|
|
342
|
+
// the non-swallowed cases; a duplicate report beats a silent one.
|
|
343
|
+
const n = e !== undefined && globalThis.reportError;
|
|
344
|
+
n || e === undefined ? console.error(t) : console.error(t, e);
|
|
345
|
+
n && n(e);
|
|
271
346
|
}
|
|
272
347
|
|
|
273
348
|
// Logs on the first write after a halt so a frozen interaction is traceable.
|
|
@@ -287,30 +362,30 @@ function notifyHalted() {
|
|
|
287
362
|
let queueRunToken = 0;
|
|
288
363
|
|
|
289
364
|
class Queue {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
365
|
+
_parent=null;
|
|
366
|
+
mn=[ [], [] ];
|
|
367
|
+
hn=[];
|
|
368
|
+
An=0;
|
|
294
369
|
created=clock;
|
|
295
370
|
addChild(e) {
|
|
296
|
-
this.
|
|
297
|
-
e.
|
|
371
|
+
this.hn.push(e);
|
|
372
|
+
e._parent = this;
|
|
298
373
|
}
|
|
299
374
|
removeChild(e) {
|
|
300
|
-
const t = this.
|
|
375
|
+
const t = this.hn.indexOf(e);
|
|
301
376
|
if (t >= 0) {
|
|
302
|
-
this.
|
|
303
|
-
e.
|
|
377
|
+
this.hn.splice(t, 1);
|
|
378
|
+
e._parent = null;
|
|
304
379
|
}
|
|
305
380
|
}
|
|
306
|
-
notify(e, t,
|
|
307
|
-
if (this.
|
|
381
|
+
notify(e, t, n, i) {
|
|
382
|
+
if (this._parent) return this._parent.notify(e, t, n, i);
|
|
308
383
|
return false;
|
|
309
384
|
}
|
|
310
385
|
run(e) {
|
|
311
|
-
if (this.
|
|
312
|
-
const t = this.
|
|
313
|
-
this.
|
|
386
|
+
if (this.mn[e - 1].length) {
|
|
387
|
+
const t = this.mn[e - 1];
|
|
388
|
+
this.mn[e - 1] = [];
|
|
314
389
|
runQueue(t, e);
|
|
315
390
|
}
|
|
316
391
|
// Effects run here can dispose owners, and disposal removes queues from
|
|
@@ -320,70 +395,70 @@ class Queue {
|
|
|
320
395
|
// can be recovered by rescanning from the front and every child still runs
|
|
321
396
|
// exactly once. Children appended mid-pass carry a stale stamp and run,
|
|
322
397
|
// matching the previous live-array behaviour.
|
|
323
|
-
const t = this.
|
|
324
|
-
const
|
|
325
|
-
for (let
|
|
326
|
-
const
|
|
327
|
-
if (
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if (t[
|
|
331
|
-
|
|
398
|
+
const t = this.hn;
|
|
399
|
+
const n = ++queueRunToken;
|
|
400
|
+
for (let i = 0; i < t.length; ) {
|
|
401
|
+
const s = t[i];
|
|
402
|
+
if (s.An !== n) {
|
|
403
|
+
s.An = n;
|
|
404
|
+
s.run?.(e);
|
|
405
|
+
if (t[i] !== s) {
|
|
406
|
+
i = 0;
|
|
332
407
|
continue;
|
|
333
408
|
}
|
|
334
409
|
}
|
|
335
|
-
|
|
410
|
+
i++;
|
|
336
411
|
}
|
|
337
412
|
}
|
|
338
413
|
enqueue(e, t) {
|
|
339
414
|
if (e) {
|
|
340
415
|
// Route to lane's effect queue if we're in an optimistic recomputation
|
|
341
416
|
if (currentOptimisticLane) {
|
|
342
|
-
const
|
|
343
|
-
|
|
417
|
+
const n = findLane(currentOptimisticLane);
|
|
418
|
+
n.fn[e - 1].push(t);
|
|
344
419
|
} else {
|
|
345
|
-
this.
|
|
420
|
+
this.mn[e - 1].push(t);
|
|
346
421
|
}
|
|
347
422
|
}
|
|
348
423
|
schedule();
|
|
349
424
|
}
|
|
350
425
|
stashQueues(e) {
|
|
351
|
-
e.
|
|
352
|
-
e.
|
|
353
|
-
this.
|
|
354
|
-
for (let t = 0; t < this.
|
|
355
|
-
let
|
|
356
|
-
let
|
|
357
|
-
if (!
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
426
|
+
e.mn[0].push(...this.mn[0]);
|
|
427
|
+
e.mn[1].push(...this.mn[1]);
|
|
428
|
+
this.mn = [ [], [] ];
|
|
429
|
+
for (let t = 0; t < this.hn.length; t++) {
|
|
430
|
+
let n = this.hn[t];
|
|
431
|
+
let i = e.hn[t];
|
|
432
|
+
if (!i) {
|
|
433
|
+
i = {
|
|
434
|
+
mn: [ [], [] ],
|
|
435
|
+
hn: []
|
|
361
436
|
};
|
|
362
|
-
e.
|
|
437
|
+
e.hn[t] = i;
|
|
363
438
|
}
|
|
364
|
-
|
|
439
|
+
n.stashQueues(i);
|
|
365
440
|
}
|
|
366
441
|
}
|
|
367
442
|
restoreQueues(e) {
|
|
368
|
-
this.
|
|
369
|
-
this.
|
|
370
|
-
for (let t = 0; t < e.
|
|
371
|
-
const
|
|
372
|
-
let
|
|
373
|
-
if (
|
|
443
|
+
this.mn[0].push(...e.mn[0]);
|
|
444
|
+
this.mn[1].push(...e.mn[1]);
|
|
445
|
+
for (let t = 0; t < e.hn.length; t++) {
|
|
446
|
+
const n = e.hn[t];
|
|
447
|
+
let i = this.hn[t];
|
|
448
|
+
if (i) i.restoreQueues(n);
|
|
374
449
|
}
|
|
375
450
|
}
|
|
376
451
|
}
|
|
377
452
|
|
|
378
453
|
class GlobalQueue extends Queue {
|
|
379
|
-
|
|
454
|
+
Kt=false;
|
|
380
455
|
// The current transaction-shaped batch: a plain ambient batch while no
|
|
381
456
|
// transition is active, the active transition itself after initTransition.
|
|
382
457
|
m=createBatch();
|
|
383
|
-
static
|
|
384
|
-
static
|
|
385
|
-
static
|
|
386
|
-
static
|
|
458
|
+
static We;
|
|
459
|
+
static Be;
|
|
460
|
+
static Et;
|
|
461
|
+
static Cn=null;
|
|
387
462
|
// Store-side hook: drops a keyless affects() mark's identity scope when the
|
|
388
463
|
// carrier node's last registration releases (wired by store.ts, mirroring
|
|
389
464
|
// _clearOptimisticStore).
|
|
@@ -397,27 +472,27 @@ class GlobalQueue extends Queue {
|
|
|
397
472
|
static N=null;
|
|
398
473
|
// External-source bridge (wired by enableExternalSource(); null while no
|
|
399
474
|
// config is active — including after _resetExternalSourceConfig()).
|
|
400
|
-
static
|
|
401
|
-
static
|
|
475
|
+
static wt=null;
|
|
476
|
+
static Yt=null;
|
|
402
477
|
// Verdict-layer hooks (wired by verdict.ts when isPending()/latest() are
|
|
403
478
|
// imported; null in apps that never use them). Call sites either guard for
|
|
404
479
|
// null or sit behind state only the verdict layer can create (`!` is safe
|
|
405
480
|
// there: `_pendingSignal`/`_latestValueComputed` are only ever assigned by
|
|
406
481
|
// verdict.ts, and `pendingCheckActive`/`latestReadActive` only flip inside
|
|
407
482
|
// isPending()/latest()).
|
|
408
|
-
static Ue=null;
|
|
409
|
-
static de=null;
|
|
410
483
|
static me=null;
|
|
411
|
-
static
|
|
412
|
-
static
|
|
413
|
-
static
|
|
414
|
-
static Lt=null;
|
|
415
|
-
static nt=null;
|
|
416
|
-
static k=null;
|
|
484
|
+
static Oe=null;
|
|
485
|
+
static Me=null;
|
|
486
|
+
static En=null;
|
|
417
487
|
static zt=null;
|
|
488
|
+
static Jt=null;
|
|
489
|
+
static nn=null;
|
|
490
|
+
static Nt=null;
|
|
491
|
+
static k=null;
|
|
492
|
+
static pn=null;
|
|
418
493
|
// Re-asks probes whose verdict was provisionally suppressed by a fresh read
|
|
419
494
|
// of a held value, once the transaction gains an async blocker (#3028).
|
|
420
|
-
static
|
|
495
|
+
static vn=null;
|
|
421
496
|
// Optimistic-engine hooks (wired by core/optimistic.ts via
|
|
422
497
|
// installOptimisticEngine(), called from verdict.ts / createOptimistic /
|
|
423
498
|
// createOptimisticStore — every module that can create optimistic state).
|
|
@@ -425,61 +500,124 @@ class GlobalQueue extends Queue {
|
|
|
425
500
|
// `_overrideValue` slot, a lane in `activeLanes`, an `_optimisticNodes`
|
|
426
501
|
// entry, or a non-null `currentOptimisticLane`, so `!` invocations are safe
|
|
427
502
|
// once the gate holds.
|
|
428
|
-
static
|
|
429
|
-
static
|
|
430
|
-
static dn=null;
|
|
431
|
-
static In=null;
|
|
503
|
+
static ln=null;
|
|
504
|
+
static On=null;
|
|
432
505
|
static Nn=null;
|
|
506
|
+
static Rn=null;
|
|
507
|
+
static _n=null;
|
|
433
508
|
/** Patch-channel optimistic drain (next/patch.ts): optimistic emissions
|
|
434
509
|
* apply at lane-effect timing — visible in flight, unlike the regular
|
|
435
510
|
* effect queues an action stashes. Injected; null when unused. */
|
|
436
|
-
static
|
|
437
|
-
static
|
|
438
|
-
static
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
511
|
+
static In=null;
|
|
512
|
+
static tn=null;
|
|
513
|
+
static $t=null;
|
|
514
|
+
/** Is the node routed through a LIVE lane (`resolveLane`)? read()'s reveal
|
|
515
|
+
* carve-out asks before showing a foreign-held pending node's committed
|
|
516
|
+
* value: a lane-derived flight's inputs are already revealed through the
|
|
517
|
+
* lane (#3334). Gated on CONFIG_HAS_LANE, which only the engine sets. */
|
|
518
|
+
static Xt=null;
|
|
519
|
+
static Bt=null;
|
|
520
|
+
static st=null;
|
|
521
|
+
static _t=null;
|
|
442
522
|
/** Authoritative-view reader wakeup: installed by until() and refresh() before
|
|
443
523
|
* their first read. Call sites are gated by CONFIG_AUTHORITATIVE_OBSERVED, which
|
|
444
524
|
* only such a reader's carve-out read can set, so `!` invocations are safe once
|
|
445
525
|
* the gate holds (#3303). */
|
|
446
|
-
static
|
|
447
|
-
static
|
|
448
|
-
|
|
526
|
+
static Zt=null;
|
|
527
|
+
static ft=null;
|
|
528
|
+
/** A18 supersession (#3331): own-source truth `value` landed under an active
|
|
529
|
+
* override. The engine decides whether the graph re-derives — the value
|
|
530
|
+
* differs from the override and is not a stale (older-action) answer (mark
|
|
531
|
+
* the node, demote its lane cascade, notify), or returns to it after an
|
|
532
|
+
* earlier differing arrival (clear the mark, notify) — and owns the
|
|
533
|
+
* authoritative-observer wake for a silent confirm. Installed with the
|
|
534
|
+
* optimistic engine; only reachable on a node that has an override. */
|
|
535
|
+
static we=null;
|
|
536
|
+
/** The flush's pre-verdict step (#3427): once the transaction's action
|
|
537
|
+
* bodies have all ended and nothing authoritative is left in flight, the
|
|
538
|
+
* engine supersedes every override still in force with the truth it
|
|
539
|
+
* reverts to, so the graph re-derives from it now, as the transaction's
|
|
540
|
+
* held work, instead of after the flights the overrides fed have landed.
|
|
541
|
+
* True when it superseded something: the caller re-runs the heap ahead of
|
|
542
|
+
* the verdict. The engine owns every gate (acted, actions drained, has
|
|
543
|
+
* overrides, no store edits, no authoritative flight); null without it. */
|
|
544
|
+
static Tn=null;
|
|
545
|
+
/** read()'s value for a TRACKED reader of a superseded node (#3331): the
|
|
546
|
+
* staged truth, unless the reader is a stale (render) reader of another
|
|
547
|
+
* transaction — then the displayed override, as it keeps a foreign
|
|
548
|
+
* transaction's committed value over its staged write. */
|
|
549
|
+
/** A tracked read of an active override: the lane outside-view rule
|
|
550
|
+
* (#3460) and the A18 supersession selection (#3331) — see optimistic.ts. */
|
|
551
|
+
static en=null;
|
|
552
|
+
/** A lane pass's publish for a memo (#3479, lanes stage): the speculative
|
|
553
|
+
* result becomes a DERIVED override, `_value` stays committed — see
|
|
554
|
+
* optimistic.ts laneOverride. Set with the engine, which a lane implies. */
|
|
555
|
+
static Ve=null;
|
|
556
|
+
/** Verdict-layer recompute in progress (companion creation, latest()/
|
|
557
|
+
* isPending() pulls): never born held — see core.ts enterStagedRead. */
|
|
558
|
+
static jt=false;
|
|
559
|
+
/** setSignal's authoritative (projection-write) landing on an override-
|
|
560
|
+
* covered node (#3331 store twin): stage the truth for its transaction's
|
|
561
|
+
* commit whatever its relation to the committed value — a landing equal to
|
|
562
|
+
* committed still differs from the override — then _supersedeOverride
|
|
563
|
+
* decides. Installed with the optimistic engine; only reachable on a node
|
|
564
|
+
* that has an override. */
|
|
565
|
+
static un=null;
|
|
566
|
+
static Dn=null;
|
|
449
567
|
flush() {
|
|
450
|
-
if (this.
|
|
568
|
+
if (this.Kt) return;
|
|
451
569
|
// Fast drain: nothing in flight but plain pending commits — no dirty
|
|
452
570
|
// computeds, no queued effects, no child queues, no transitions/lanes/
|
|
453
571
|
// optimistic state. Commit and go; anything a commit hook schedules
|
|
454
572
|
// (companion snaps, store folds notifying subs) re-arms `scheduled`
|
|
455
573
|
// below and the outer drain loop takes the full spine next round.
|
|
456
|
-
if (activeTransition === null && dirtyQueue.EE < dirtyQueue.
|
|
457
|
-
|
|
574
|
+
if (activeTransition === null && dirtyQueue.EE < dirtyQueue.et && this.mn[0].length === 0 && this.mn[1].length === 0 && this.hn.length === 0 && !wokenTransitions.length && !batchJoins.length && // a join must drain in its own tick (#3519 review)
|
|
575
|
+
canUseSimpleSyncFlush(this)) {
|
|
576
|
+
this.Kt = true;
|
|
458
577
|
try {
|
|
578
|
+
// A28: companions of nodes written since the last flush mirror the
|
|
579
|
+
// flushed world — re-synced inside the running window (the write is
|
|
580
|
+
// "flushed" from here on).
|
|
581
|
+
resyncUnflushedCompanions();
|
|
459
582
|
// Sweep first: unobserved() pulls swept nodes out of the dirty heap,
|
|
460
583
|
// so a dormant memo dirtied in the same tick is reclaimed instead of
|
|
461
584
|
// recomputed (matching the old inline dispose-on-read counts).
|
|
462
|
-
|
|
585
|
+
sweepDormant();
|
|
463
586
|
commitPendingNodes();
|
|
464
587
|
} finally {
|
|
465
|
-
this.
|
|
588
|
+
this.Kt = false;
|
|
466
589
|
}
|
|
467
590
|
clock++;
|
|
468
|
-
scheduled = dirtyQueue.EE >= dirtyQueue.
|
|
591
|
+
scheduled = dirtyQueue.EE >= dirtyQueue.et || this.mn[0].length !== 0 || this.mn[1].length !== 0 || this.m.Ot.length !== 0;
|
|
469
592
|
return;
|
|
470
593
|
}
|
|
471
|
-
this.
|
|
472
|
-
|
|
594
|
+
this.Kt = true;
|
|
595
|
+
resyncUnflushedCompanions();
|
|
596
|
+
// A28, see above
|
|
597
|
+
try {
|
|
598
|
+
// The tick proposed against a hold (#3494): adopt its batch into it.
|
|
599
|
+
// Inside the try: the adoption runs user comparators (the no-proposal
|
|
600
|
+
// drop), and a throw there must not leave `_running` set.
|
|
601
|
+
while (batchJoins.length) this.initTransition(batchJoins.pop());
|
|
473
602
|
if (false) ;
|
|
474
603
|
// Before runHeap for the same reason as the fast drain above; late
|
|
475
604
|
// subscribers (an effect reading a swept memo this flush) revive it,
|
|
476
605
|
// which is the pay-for-use contract.
|
|
477
606
|
sweepDormant();
|
|
478
|
-
runHeap(dirtyQueue, GlobalQueue.
|
|
607
|
+
runHeap(dirtyQueue, GlobalQueue.We);
|
|
479
608
|
if (activeTransition) {
|
|
609
|
+
// The action bodies are over: the overrides they leave in force
|
|
610
|
+
// revert at this settle, and the correction is this transaction's
|
|
611
|
+
// held work — re-derived here, under it, ahead of the verdict — not a
|
|
612
|
+
// waterfall after the flights the overrides fed (#3427). After the
|
|
613
|
+
// heap, not before: a synchronous body's own writes (a refresh that
|
|
614
|
+
// puts an override node's source in flight) are judged applied.
|
|
615
|
+
if (GlobalQueue.Tn?.(activeTransition)) runHeap(dirtyQueue, GlobalQueue.We);
|
|
480
616
|
const e = transitionComplete(activeTransition);
|
|
481
617
|
if (!e) {
|
|
482
618
|
const e = activeTransition;
|
|
619
|
+
// Parked: the unchanged passes' inputs are held; their tails stay (A30).
|
|
620
|
+
heldTrims.length = 0;
|
|
483
621
|
// When the parking batch IS the transition, all of its writes commit
|
|
484
622
|
// only with it — every zombie recompute they queued would run against
|
|
485
623
|
// a world the zombie never displays (zombies render mainline until
|
|
@@ -488,7 +626,7 @@ class GlobalQueue extends Queue {
|
|
|
488
626
|
// zombies here. Height-adjust entries still process normally: a
|
|
489
627
|
// dirtied one keeps its height flag and falls through to runHeap's
|
|
490
628
|
// adjustHeight path on the next pass of the bucket.
|
|
491
|
-
runHeap(zombieQueue, this.m === e ? cancelZombieRecompute : GlobalQueue.
|
|
629
|
+
runHeap(zombieQueue, this.m === e ? cancelZombieRecompute : GlobalQueue.We);
|
|
492
630
|
// Detach: the stashed transition keeps its batch; ambient work that
|
|
493
631
|
// follows lands in a fresh one. If the batch is already a separate
|
|
494
632
|
// ambient one — action done() restored activeTransition without
|
|
@@ -498,73 +636,88 @@ class GlobalQueue extends Queue {
|
|
|
498
636
|
if (this.m === e) currentBatch = this.m = createBatch();
|
|
499
637
|
// Run lane effects immediately (before stashing) - lanes with no pending async
|
|
500
638
|
if (activeLanes.size) {
|
|
501
|
-
GlobalQueue.
|
|
502
|
-
GlobalQueue.
|
|
639
|
+
GlobalQueue._n(EFFECT_RENDER);
|
|
640
|
+
GlobalQueue._n(EFFECT_USER);
|
|
503
641
|
}
|
|
504
|
-
this.stashQueues(e.
|
|
642
|
+
this.stashQueues(e.Sn);
|
|
505
643
|
clock++;
|
|
506
644
|
// A kept ambient batch may hold pending nodes (#2916): stay
|
|
507
645
|
// scheduled so the outer drain loop commits them via the plain
|
|
508
646
|
// flush path instead of leaving them until the next natural flush.
|
|
509
|
-
scheduled = dirtyQueue.EE >= dirtyQueue.
|
|
510
|
-
reassignPendingTransition(e.
|
|
647
|
+
scheduled = dirtyQueue.EE >= dirtyQueue.et || this.m.Ot.length > 0;
|
|
648
|
+
reassignPendingTransition(e.Ot);
|
|
511
649
|
activeTransition = null;
|
|
512
650
|
finalizePureQueue(null, true);
|
|
513
651
|
return;
|
|
514
652
|
}
|
|
515
653
|
const t = activeTransition;
|
|
516
|
-
const
|
|
517
|
-
|
|
518
|
-
this.restoreQueues(t.
|
|
654
|
+
const n = this.m;
|
|
655
|
+
n !== t && n.Ot.push(...t.Ot);
|
|
656
|
+
this.restoreQueues(t.Sn);
|
|
519
657
|
transitions.delete(t);
|
|
520
658
|
activeTransition = null;
|
|
521
|
-
reassignPendingTransition(
|
|
659
|
+
reassignPendingTransition(n.Ot);
|
|
522
660
|
finalizePureQueue(t);
|
|
523
|
-
if (
|
|
661
|
+
if (n === t) {
|
|
524
662
|
// Drop the dead Transition wrapper but keep its (drained) containers
|
|
525
663
|
// as the ambient batch — late registrations during finalization live
|
|
526
664
|
// there and must survive to the next flush.
|
|
527
665
|
const e = createBatch();
|
|
528
|
-
e.
|
|
529
|
-
e.
|
|
530
|
-
e.A =
|
|
531
|
-
e.
|
|
666
|
+
e.Ot = n.Ot;
|
|
667
|
+
e.rt = n.rt;
|
|
668
|
+
e.A = n.A;
|
|
669
|
+
e.dn = n.dn;
|
|
532
670
|
currentBatch = this.m = e;
|
|
533
671
|
}
|
|
534
672
|
} else {
|
|
535
673
|
if (canUseSimpleSyncFlush(this)) {
|
|
536
674
|
commitPendingNodes();
|
|
537
|
-
if (dirtyQueue.EE >= dirtyQueue.
|
|
538
|
-
runHeap(dirtyQueue, GlobalQueue.
|
|
675
|
+
if (dirtyQueue.EE >= dirtyQueue.et) {
|
|
676
|
+
runHeap(dirtyQueue, GlobalQueue.We);
|
|
539
677
|
commitPendingNodes();
|
|
540
678
|
}
|
|
541
679
|
} else {
|
|
542
|
-
if (transitions.size) runHeap(zombieQueue, GlobalQueue.
|
|
680
|
+
if (transitions.size) runHeap(zombieQueue, GlobalQueue.We);
|
|
543
681
|
finalizePureQueue();
|
|
544
682
|
}
|
|
545
683
|
}
|
|
546
684
|
clock++;
|
|
547
|
-
// Check if finalization added items to the heap (from optimistic reversion)
|
|
548
|
-
|
|
685
|
+
// Check if finalization added items to the heap (from optimistic reversion).
|
|
686
|
+
// Finalization may also have ENTERED a transaction (a commit hook, boundary
|
|
687
|
+
// sweep or recompute wrote a node it owns): effects computed under it
|
|
688
|
+
// since are its to apply, not this flush's — runEffect leaves them queued
|
|
689
|
+
// and the next pass parks them with it (#3319). Everything computed
|
|
690
|
+
// mainline applies now.
|
|
691
|
+
scheduled = dirtyQueue.EE >= dirtyQueue.et || activeTransition !== null;
|
|
549
692
|
// Run lane effects first (for ready lanes), then regular effects
|
|
550
|
-
activeLanes.size && GlobalQueue.
|
|
693
|
+
activeLanes.size && GlobalQueue._n(EFFECT_RENDER);
|
|
551
694
|
this.run(EFFECT_RENDER);
|
|
552
|
-
activeLanes.size && GlobalQueue.
|
|
695
|
+
activeLanes.size && GlobalQueue._n(EFFECT_USER);
|
|
553
696
|
this.run(EFFECT_USER);
|
|
554
697
|
if (false) ;
|
|
555
698
|
if (false && !scheduled && !activeTransition && transitions.size === 0 && activeLanes.size === 0) ;
|
|
556
699
|
if (false) ;
|
|
557
700
|
} finally {
|
|
558
|
-
|
|
701
|
+
// Re-enter a woken transaction (see wokenTransitions) only from an
|
|
702
|
+
// idle pass: entering adopts the ambient batch, and staged or dirty
|
|
703
|
+
// ambient work would be held behind flights it never read. `scheduled`
|
|
704
|
+
// is that test here — after the park exit as well as the normal one:
|
|
705
|
+
// it was recomputed from the heap this pass, every write since re-armed
|
|
706
|
+
// it, and optimistic ambient nodes reverted with the finalize — so a
|
|
707
|
+
// wake in a pass with work simply falls to the next. Entering re-arms
|
|
708
|
+
// it itself; a dead (completed) wake is a bare return in
|
|
709
|
+
// initTransition, and the loop moves on to the next.
|
|
710
|
+
while (!scheduled && !activeTransition && wokenTransitions.length) this.initTransition(wokenTransitions.pop());
|
|
711
|
+
this.Kt = false;
|
|
559
712
|
}
|
|
560
713
|
}
|
|
561
|
-
notify(e, t,
|
|
714
|
+
notify(e, t, n, i) {
|
|
562
715
|
// Only track async if the boundary is propagating STATUS_PENDING (not caught by boundary)
|
|
563
716
|
if (t & STATUS_PENDING) {
|
|
564
|
-
if (
|
|
717
|
+
if (n & STATUS_PENDING) {
|
|
565
718
|
// Callers pass either nothing or this node's own `_x._error`, so `??`
|
|
566
719
|
// is exact (a null error falls back to the same null).
|
|
567
|
-
const t =
|
|
720
|
+
const t = i ?? e.o?._;
|
|
568
721
|
// A visibility-only mark notification (the affects() boundary
|
|
569
722
|
// channel) updates display state on its way up but must be invisible
|
|
570
723
|
// to completion accounting BY CONSTRUCTION: it never registers a
|
|
@@ -576,16 +729,16 @@ class GlobalQueue extends Queue {
|
|
|
576
729
|
// new. Fresh/reset loading boundaries consume pending before it
|
|
577
730
|
// reaches here. A reader already parked in a transition must not
|
|
578
731
|
// open a second one.
|
|
579
|
-
if (!activeTransition && !e.
|
|
732
|
+
if (!activeTransition && !e.Ge && currentBatch.Ot.length) this.initTransition();
|
|
580
733
|
if (activeTransition) {
|
|
581
|
-
const
|
|
582
|
-
let
|
|
583
|
-
if (!
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
if (
|
|
734
|
+
const n = t.source;
|
|
735
|
+
let i = activeTransition.oe.get(n);
|
|
736
|
+
if (!i) activeTransition.oe.set(n, i = new Set);
|
|
737
|
+
const s = i.size;
|
|
738
|
+
i.add(e);
|
|
739
|
+
if (i.size !== s) {
|
|
587
740
|
schedule();
|
|
588
|
-
GlobalQueue.
|
|
741
|
+
GlobalQueue.vn?.(activeTransition);
|
|
589
742
|
}
|
|
590
743
|
}
|
|
591
744
|
}
|
|
@@ -604,9 +757,9 @@ class GlobalQueue extends Queue {
|
|
|
604
757
|
// new transaction identity each pass. Stamps are cleared at commit, so
|
|
605
758
|
// this is a belt for paths that hand over a chased-dead reference
|
|
606
759
|
// (merged chains, async settles racing completion).
|
|
607
|
-
if (e.
|
|
760
|
+
if (e.Tt === true || e === activeTransition) return;
|
|
608
761
|
}
|
|
609
|
-
if (!e && activeTransition && activeTransition.
|
|
762
|
+
if (!e && activeTransition && activeTransition.Pe === clock) return;
|
|
610
763
|
if (!activeTransition) {
|
|
611
764
|
activeTransition = e ?? createBatch();
|
|
612
765
|
} else if (e) {
|
|
@@ -616,12 +769,12 @@ class GlobalQueue extends Queue {
|
|
|
616
769
|
// now: back onto the live queue, where this flush parks them under
|
|
617
770
|
// `transition` or runs them at its completion. The outgoing stash is
|
|
618
771
|
// never read again — the transaction is dead (#3310).
|
|
619
|
-
this.restoreQueues(t.
|
|
772
|
+
this.restoreQueues(t.Sn);
|
|
620
773
|
transitions.delete(t);
|
|
621
774
|
activeTransition = e;
|
|
622
775
|
}
|
|
623
776
|
transitions.add(activeTransition);
|
|
624
|
-
activeTransition.
|
|
777
|
+
activeTransition.Pe = clock;
|
|
625
778
|
const t = this.m;
|
|
626
779
|
if (t !== activeTransition) {
|
|
627
780
|
// Adopt the ambient batch into the transaction, then make the
|
|
@@ -631,28 +784,57 @@ class GlobalQueue extends Queue {
|
|
|
631
784
|
// must not entangle unrelated writes to it; the same rule holds one hop
|
|
632
785
|
// downstream: propagation never queues pended subscribers as pending
|
|
633
786
|
// nodes, see propagateAffectsMark, #2893.
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
787
|
+
// Adopted outside a flush: the staging is still unflushed — the stamp
|
|
788
|
+
// must not make it read as held-and-carried (CONFIG_ADOPTED_UNFLUSHED;
|
|
789
|
+
// the carrying flush clears it in reassignPendingTransition).
|
|
790
|
+
const e = this.Kt ? 0 : CONFIG_ADOPTED_UNFLUSHED;
|
|
791
|
+
for (let n = 0; n < t.Ot.length; n++) {
|
|
792
|
+
const i = t.Ot[n];
|
|
793
|
+
// A tick that nets to the committed value proposed nothing (A34, #3494):
|
|
794
|
+
// `setShow(false); setShow(true)` beside a write that opens a hold
|
|
795
|
+
// left `show` staged at its own value, stamped, pending to the
|
|
796
|
+
// verdict, and its next mainline write held by a flight it never
|
|
797
|
+
// derived from. Unstage it here — its subscribers were walked at the
|
|
798
|
+
// write and re-derive the same value. Writes only: a signal's staging
|
|
799
|
+
// is always one, a computed's only under REACTIVE_MANUAL_WRITE
|
|
800
|
+
// (`createSignal(fn)`'s setter, #3519 review) — otherwise it is its
|
|
801
|
+
// pass's result, which may equal an uninitialized `undefined` (a
|
|
802
|
+
// born-held first pass). `_equals: false` opts out. The unstaging is
|
|
803
|
+
// the commit's own path (commitPendingNode with nothing staged): the
|
|
804
|
+
// manual-write flag, companions and the rest are cleaned up as a
|
|
805
|
+
// commit would, and the node is stamped nowhere. Unstamped
|
|
806
|
+
// only: a node already a transaction's — arriving here as a parked
|
|
807
|
+
// batch folds into a merge — carries a FLUSHED proposal a later
|
|
808
|
+
// rewrite brought back to the committed value; it is held, not
|
|
809
|
+
// proposal-free. Dropped, it kept the dead stamp, and the next write
|
|
810
|
+
// to it queued under the merged transaction a value the commit then
|
|
811
|
+
// skipped as another's (fuzzer latest-2 #1470, S3).
|
|
812
|
+
if (i.Ge === null && i.ve !== NOT_PENDING && (!i.ce || i.ue & REACTIVE_MANUAL_WRITE && !(i.S & STATUS_UNINITIALIZED)) && i.Fe && i.Fe(i.Qe, i.ve)) {
|
|
813
|
+
i.ve = NOT_PENDING;
|
|
814
|
+
commitPendingNode(i);
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
i.Ge = activeTransition;
|
|
818
|
+
i.T |= e;
|
|
819
|
+
activeTransition.Ot.push(i);
|
|
638
820
|
}
|
|
639
|
-
for (let e = 0; e < t.
|
|
640
|
-
const
|
|
641
|
-
|
|
642
|
-
activeTransition.
|
|
821
|
+
for (let e = 0; e < t.rt.length; e++) {
|
|
822
|
+
const n = t.rt[e];
|
|
823
|
+
n.Ge = activeTransition;
|
|
824
|
+
activeTransition.rt.push(n);
|
|
643
825
|
}
|
|
644
826
|
if (t.A.length) activeTransition.A.push(...t.A);
|
|
645
|
-
for (const e of t.
|
|
827
|
+
for (const e of t.dn) activeTransition.dn.add(e);
|
|
646
828
|
// Gated readers recorded against the ambient batch move with it: their
|
|
647
829
|
// replay-at-commit now happens at the transaction's completion.
|
|
648
|
-
if (t.
|
|
649
|
-
for (const e of t.
|
|
650
|
-
t.
|
|
830
|
+
if (t.ct.size) {
|
|
831
|
+
for (const e of t.ct) activeTransition.ct.add(e);
|
|
832
|
+
t.ct.clear();
|
|
651
833
|
}
|
|
652
834
|
currentBatch = this.m = activeTransition;
|
|
653
835
|
}
|
|
654
836
|
for (const e of activeLanes) {
|
|
655
|
-
if (!e.
|
|
837
|
+
if (!e.Ge) e.Ge = activeTransition;
|
|
656
838
|
}
|
|
657
839
|
// A transaction's ambient window is one flush. Entering must therefore
|
|
658
840
|
// guarantee a flush: a transaction opened with no writes (an action whose
|
|
@@ -667,7 +849,9 @@ class GlobalQueue extends Queue {
|
|
|
667
849
|
}
|
|
668
850
|
|
|
669
851
|
function queuePendingNode(e) {
|
|
670
|
-
currentBatch.
|
|
852
|
+
currentBatch.Ot.push(e);
|
|
853
|
+
if (!globalQueue.Kt) markUnflushedStaged();
|
|
854
|
+
// A28
|
|
671
855
|
}
|
|
672
856
|
|
|
673
857
|
// Sticky: flips true on the first refresh() ever (the only setter of
|
|
@@ -688,25 +872,45 @@ function armReaskClear() {
|
|
|
688
872
|
reaskArmed = true;
|
|
689
873
|
}
|
|
690
874
|
|
|
875
|
+
/** Provenance of the work currently running (A18 supersession, #3331): the
|
|
876
|
+
* invocation sequence of the action whose ambient window this is — set by
|
|
877
|
+
* action() for each slice; the flush that ends the window clears it — or,
|
|
878
|
+
* inside an async landing, the sequence captured when that flight was
|
|
879
|
+
* registered (asyncWrite sets it for the landing's synchronous propagation,
|
|
880
|
+
* so a sync recompute downstream of the landing — an optimistic wrapper over
|
|
881
|
+
* the async source — derives under the flight's provenance, and flights it
|
|
882
|
+
* registers inherit it). 0 is mainline: no action, always the current
|
|
883
|
+
* question. An override stamps this at its write (`_overrideStamp`); an
|
|
884
|
+
* answer whose flight an OLDER action issued is a stale question the user
|
|
885
|
+
* has since changed — it holds silently to commit instead of superseding. A
|
|
886
|
+
* slow source must not leak back in over a newer intent. Transactions merge,
|
|
887
|
+
* so the transition object cannot say WHICH action asked; this can. */ let origin = 0;
|
|
888
|
+
|
|
889
|
+
function setOrigin(e) {
|
|
890
|
+
const t = origin;
|
|
891
|
+
origin = e;
|
|
892
|
+
return t;
|
|
893
|
+
}
|
|
894
|
+
|
|
691
895
|
function insertSubs(e, t = false) {
|
|
692
896
|
// §12d: stamp before walking — setSignal's staged-rewrite fast path skips
|
|
693
897
|
// the next walk for this node while the epoch holds (marking is idempotent).
|
|
694
|
-
e.
|
|
898
|
+
e.ht = notifyEpoch;
|
|
695
899
|
// Get source lane: prefer node's own lane over current context
|
|
696
900
|
// This is important for isPending signals which need their own lane to flush immediately
|
|
697
901
|
// Presence bits gate the optional-slot probes (see constants.ts): one
|
|
698
902
|
// masked read of the always-present _config instead of missing-property
|
|
699
903
|
// lookups in the hottest notify loop. Bits are sticky — the field read
|
|
700
904
|
// stays authoritative when a bit is set.
|
|
701
|
-
const
|
|
702
|
-
const
|
|
703
|
-
const
|
|
704
|
-
const
|
|
705
|
-
for (let
|
|
706
|
-
const e =
|
|
905
|
+
const n = e.T;
|
|
906
|
+
const i = (n & CONFIG_HAS_LANE ? e.o?.Ue : undefined) || currentOptimisticLane;
|
|
907
|
+
const s = (n & CONFIG_HAS_SNAPSHOT) !== 0 && e.o?.nt !== undefined;
|
|
908
|
+
const r = reaskArmed;
|
|
909
|
+
for (let n = e.u; n !== null; n = n.Ne) {
|
|
910
|
+
const e = n._e;
|
|
707
911
|
// A value-change notification is a new question for the subscriber: any
|
|
708
912
|
// pending re-ask mark (refresh) it carried is superseded.
|
|
709
|
-
if (
|
|
913
|
+
if (r) e.ue &= ~REACTIVE_REASK;
|
|
710
914
|
// Missed-wake latch (#3037): this write is landing while the subscriber
|
|
711
915
|
// is mid-recompute (a nested pull committing beneath its reads), and the
|
|
712
916
|
// heap refuses RECOMPUTING nodes. A gen-current link means the pass
|
|
@@ -715,18 +919,18 @@ function insertSubs(e, t = false) {
|
|
|
715
919
|
// pass either re-reads them fresh or trims them), and neither does the
|
|
716
920
|
// tail link: it is the read IN FLIGHT — read() links before it pulls, so
|
|
717
921
|
// this very commit is what that read returns.
|
|
718
|
-
if (e.
|
|
719
|
-
if (
|
|
720
|
-
e.
|
|
922
|
+
if (e.ue & REACTIVE_RECOMPUTING_DEPS && n.qe === e.Ze && n !== e.ot) e.ue |= REACTIVE_MISSED_WAKE;
|
|
923
|
+
if (s && e.T & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
924
|
+
e.ue |= REACTIVE_SNAPSHOT_STALE;
|
|
721
925
|
continue;
|
|
722
926
|
}
|
|
723
|
-
if (t &&
|
|
724
|
-
e.
|
|
725
|
-
assignOrMergeLane(e,
|
|
927
|
+
if (t && i) {
|
|
928
|
+
e.ue |= REACTIVE_OPTIMISTIC_DIRTY;
|
|
929
|
+
assignOrMergeLane(e, i);
|
|
726
930
|
} else if (t) {
|
|
727
|
-
e.
|
|
931
|
+
e.ue |= REACTIVE_OPTIMISTIC_DIRTY;
|
|
728
932
|
// No source lane means reversion - clear subscriber's lane so effects go to regular queue
|
|
729
|
-
if (e.o) e.o
|
|
933
|
+
if (e.o) e.o.Ue = undefined;
|
|
730
934
|
}
|
|
731
935
|
enqueueSub(e);
|
|
732
936
|
}
|
|
@@ -734,34 +938,48 @@ function insertSubs(e, t = false) {
|
|
|
734
938
|
|
|
735
939
|
function commitPendingNode(e) {
|
|
736
940
|
const t = e;
|
|
737
|
-
if (!t.
|
|
738
|
-
if (e.
|
|
739
|
-
e.
|
|
740
|
-
e.
|
|
941
|
+
if (!t.ce) {
|
|
942
|
+
if (e.ve !== NOT_PENDING) {
|
|
943
|
+
e.Qe = e.ve;
|
|
944
|
+
e.ve = NOT_PENDING;
|
|
741
945
|
}
|
|
742
|
-
if (e.T & CONFIG_HAS_COMPANIONS) GlobalQueue.
|
|
946
|
+
if (e.T & CONFIG_HAS_COMPANIONS) GlobalQueue.En(e);
|
|
743
947
|
return;
|
|
744
948
|
}
|
|
745
|
-
if (e.
|
|
746
|
-
e.
|
|
747
|
-
e.
|
|
949
|
+
if (e.ve !== NOT_PENDING) {
|
|
950
|
+
e.Qe = e.ve;
|
|
951
|
+
e.ve = NOT_PENDING;
|
|
952
|
+
// A node born held (recompute) initializes at this commit.
|
|
953
|
+
t.S &= ~STATUS_UNINITIALIZED;
|
|
748
954
|
// Set _modified for effects, but not for tracked effects (they handle their own scheduling)
|
|
749
|
-
if (e.
|
|
955
|
+
if (e.Le && e.Le !== EFFECT_TRACKED) e.Ye = true;
|
|
750
956
|
// A quiet re-ask classification preserved through a held landing dies
|
|
751
957
|
// with the value commit — the commit IS the reveal (#3178). Gated on the
|
|
752
958
|
// staged value: status propagation queues pending nodes whose windows
|
|
753
959
|
// are still OPEN (no staged value), and their live classification must
|
|
754
960
|
// survive this sweep.
|
|
755
|
-
if (e.o) e.o.
|
|
961
|
+
if (e.o) e.o.be = false;
|
|
756
962
|
}
|
|
757
963
|
// The committed hold is the first observable answer for a loading-window
|
|
758
964
|
// node — the window closes here, not at compute time (#2990). Unconditional
|
|
759
965
|
// store to an always-present computed slot.
|
|
760
|
-
t.
|
|
761
|
-
t.
|
|
966
|
+
t.ge = false;
|
|
967
|
+
t.ue &= ~REACTIVE_MANUAL_WRITE;
|
|
968
|
+
// The children this commit publishes are the frame's now (#3404) — and so
|
|
969
|
+
// are the dependencies of the pass that produced the value: the previous
|
|
970
|
+
// frame's tail goes (A30, #3410; `recompute` left it for a staged pass). Only
|
|
971
|
+
// after a clean pass: `_error` is cleared by a clean pass or by the node's
|
|
972
|
+
// own landing (whose pass was clean), so a set `_error` means the last pass
|
|
973
|
+
// threw, kept its full list, and `_depsTail` marks where it stopped.
|
|
974
|
+
if (t.o?._ == null) trimStaleDeps(t);
|
|
975
|
+
t.T &= ~CONFIG_HELD_CHILDREN;
|
|
762
976
|
if (!(t.S & STATUS_PENDING)) t.S &= ~STATUS_UNINITIALIZED;
|
|
763
|
-
|
|
764
|
-
|
|
977
|
+
// A flight this commit leaves in the air (unobserved, or observed only by
|
|
978
|
+
// a boundary) now has PUBLISHED inputs: its committed value is stale
|
|
979
|
+
// against the frame. read()'s reveal carve-out keys on the mark (#3305).
|
|
980
|
+
else e.T |= CONFIG_INPUTS_PUBLISHED;
|
|
981
|
+
if (t.o != null && (t.o.lt !== null || t.o.it !== null)) GlobalQueue.Be(t, false, true);
|
|
982
|
+
if (e.T & CONFIG_HAS_COMPANIONS) GlobalQueue.En(e);
|
|
765
983
|
}
|
|
766
984
|
|
|
767
985
|
// Store commit hook (INTERNALS-STORE-STATE.md §3): installed by the store
|
|
@@ -782,11 +1000,21 @@ function setStoreCommitHook(e) {
|
|
|
782
1000
|
* confirming truth is committed and the override still displays, a torn
|
|
783
1001
|
* frame no timeline contains. */ const heldRevealed = [];
|
|
784
1002
|
|
|
1003
|
+
/** Unchanged passes with a stale dependency tail, waiting on this flush's
|
|
1004
|
+
* verdict (A30, #3469). A pass that changed nothing replaced nothing either —
|
|
1005
|
+
* and cannot know at its own tail whether the flush that ran it will park:
|
|
1006
|
+
* parked, its inputs are held and the committed frame still derives from the
|
|
1007
|
+
* tail (`b() ? b() : a()` computed `1` from the held `b`, equal to the `1` it
|
|
1008
|
+
* had from `a` — with `a` trimmed, the mainline `a = 2` never reached it).
|
|
1009
|
+
* Trimmed when the flush commits; dropped with a park, the tail stays linked
|
|
1010
|
+
* until a committing pass trims it (one spurious recompute at most). */ const heldTrims = [];
|
|
1011
|
+
|
|
785
1012
|
function commitPendingNodes() {
|
|
786
|
-
|
|
1013
|
+
while (heldTrims.length) trimStaleDeps(heldTrims.pop());
|
|
1014
|
+
const e = currentBatch.Ot;
|
|
787
1015
|
for (let t = 0; t < e.length; t++) {
|
|
788
|
-
const
|
|
789
|
-
commitPendingNode(
|
|
1016
|
+
const n = e[t];
|
|
1017
|
+
commitPendingNode(n);
|
|
790
1018
|
// The stamp dies with the commit (#3143) — symmetric with
|
|
791
1019
|
// resolveOptimisticNodes clearing optimistic stamps. A stamp outliving
|
|
792
1020
|
// its transaction let any later write (even a value-equal no-op, which
|
|
@@ -798,10 +1026,10 @@ function commitPendingNodes() {
|
|
|
798
1026
|
// that old view and cached it), and commits are otherwise silent
|
|
799
1027
|
// (staging already notified), so without a wake they'd hold the old
|
|
800
1028
|
// world forever.
|
|
801
|
-
|
|
802
|
-
if (
|
|
803
|
-
|
|
804
|
-
heldRevealed.push(
|
|
1029
|
+
n.Ge = null;
|
|
1030
|
+
if (n.T & CONFIG_HELD_TRUTH) {
|
|
1031
|
+
n.T &= ~CONFIG_HELD_TRUTH;
|
|
1032
|
+
heldRevealed.push(n);
|
|
805
1033
|
}
|
|
806
1034
|
}
|
|
807
1035
|
e.length = 0;
|
|
@@ -811,28 +1039,56 @@ function commitPendingNodes() {
|
|
|
811
1039
|
function finalizePureQueue(e = null, t = false) {
|
|
812
1040
|
// For incomplete transitions, skip pending resolution and optimistic reversion
|
|
813
1041
|
// For completing transitions or no-transition, resolve pending and revert optimistic
|
|
1042
|
+
const n = currentBatch;
|
|
814
1043
|
const i = !t;
|
|
815
1044
|
if (i) commitPendingNodes();
|
|
816
|
-
if (!t && globalQueue.
|
|
817
|
-
|
|
818
|
-
|
|
1045
|
+
if (!t && globalQueue.hn.length) checkBoundaryChildren(globalQueue);
|
|
1046
|
+
// Contested effects (#3322) re-derive from the world this commit just
|
|
1047
|
+
// produced. Ahead of the heap run — not the post-heap gated replay — so
|
|
1048
|
+
// the recompute and the effect phase land in this same pass and the value
|
|
1049
|
+
// the other transaction wrote into the slot is never published.
|
|
1050
|
+
// (No clear: a completed transition is never finalized again.)
|
|
1051
|
+
// A transaction whose settle reverts optimism re-derives them post-revert
|
|
1052
|
+
// instead (below, with the gated replay): between commitPendingNodes and
|
|
1053
|
+
// _resolveOptimistic the truth is committed but the overrides still
|
|
1054
|
+
// display, and a re-derive here would compose the two — the #3164 tear,
|
|
1055
|
+
// one window later. The slot meanwhile holds the frame that is on screen.
|
|
1056
|
+
const s = e?.St;
|
|
1057
|
+
const r = i && (e ?? n).rt.length !== 0;
|
|
1058
|
+
if (s && !r) for (const e of s) if (!(e.ue & REACTIVE_DISPOSED)) enqueueSub(e);
|
|
1059
|
+
const o = dirtyQueue.EE >= dirtyQueue.et;
|
|
1060
|
+
if (o) runHeap(dirtyQueue, GlobalQueue.We);
|
|
819
1061
|
if (i) {
|
|
820
|
-
|
|
1062
|
+
// Boundary checks, commit hooks and recomputes can enter a transaction,
|
|
1063
|
+
// which adopts the batch this finalize was settling: nothing batch-derived
|
|
1064
|
+
// may be committed or reverted here — the entered transaction owns it now
|
|
1065
|
+
// (#3319). A completing transaction's OWN containers are a different
|
|
1066
|
+
// matter: when the ambient batch was separate from it (the #2916 shape),
|
|
1067
|
+
// adoption never touched them and it must still settle them; when the
|
|
1068
|
+
// batch WAS the completing transaction, adoption re-stamped its contents
|
|
1069
|
+
// into the entered one and there is nothing left to settle.
|
|
1070
|
+
if (currentBatch !== n) {
|
|
1071
|
+
if (e === null || e === n) return;
|
|
1072
|
+
} else if (o) commitPendingNodes();
|
|
821
1073
|
// The settling batch: the completing transaction's, or the ambient one.
|
|
822
|
-
const t = e ??
|
|
1074
|
+
const t = e ?? n;
|
|
823
1075
|
// Optimistic reversion: a non-empty batch means _optimisticWrite ran,
|
|
824
1076
|
// which installed the engine's hooks.
|
|
825
|
-
if (t.
|
|
1077
|
+
if (t.rt.length) GlobalQueue.On(t.rt);
|
|
1078
|
+
if (s && r) {
|
|
1079
|
+
for (const e of s) if (!(e.ue & REACTIVE_DISPOSED)) enqueueSub(e);
|
|
1080
|
+
schedule();
|
|
1081
|
+
}
|
|
826
1082
|
// Replay entanglement: subs recorded by the read-time gate get rescheduled
|
|
827
1083
|
// so they re-run with the now-committed values visible. The ambient batch
|
|
828
1084
|
// replays too — laneReadsCommitted records readers whose committed-view
|
|
829
1085
|
// read hid a same-tick plain write that just committed above (#2963).
|
|
830
|
-
if (t.
|
|
831
|
-
for (const e of t.
|
|
832
|
-
if (e.
|
|
1086
|
+
if (t.ct.size) {
|
|
1087
|
+
for (const e of t.ct) {
|
|
1088
|
+
if (e.ue & REACTIVE_DISPOSED) continue;
|
|
833
1089
|
enqueueSub(e);
|
|
834
1090
|
}
|
|
835
|
-
t.
|
|
1091
|
+
t.ct.clear();
|
|
836
1092
|
// A completing transition keeps the outer flush loop alive by itself;
|
|
837
1093
|
// the ambient batch needs the re-arm or the replay sits in the heap
|
|
838
1094
|
// until the next unrelated write.
|
|
@@ -846,13 +1102,13 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
846
1102
|
// (the earlier sweep above ran while the marks were still live).
|
|
847
1103
|
if (t.A.length) {
|
|
848
1104
|
GlobalQueue.G(t.A);
|
|
849
|
-
if (globalQueue.
|
|
1105
|
+
if (globalQueue.hn.length) checkBoundaryChildren(globalQueue);
|
|
850
1106
|
}
|
|
851
1107
|
// A non-empty set means trackOptimisticStore ran, which installed the
|
|
852
1108
|
// hook; the hook iterates, clears, and schedules (keeping the loop out of
|
|
853
1109
|
// core lets esbuild shake it — rollup already folds the null guard). The
|
|
854
1110
|
// completing transition scopes the clear to its own layer keys (#2899).
|
|
855
|
-
if (t.
|
|
1111
|
+
if (t.dn.size) GlobalQueue.Cn(t.dn, e);
|
|
856
1112
|
// Held-truth reveal wake (#3164), post-revert by construction: this
|
|
857
1113
|
// finalize committed confirming truth whose subscribers were masked all
|
|
858
1114
|
// hold — some re-derived against the committed view (the staging, or a
|
|
@@ -864,20 +1120,20 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
864
1120
|
// committed but the settling transaction's overrides still display.
|
|
865
1121
|
if (heldRevealed.length !== 0) {
|
|
866
1122
|
while (heldRevealed.length) insertSubs(heldRevealed.pop());
|
|
867
|
-
if (dirtyQueue.EE >= dirtyQueue.
|
|
868
|
-
runHeap(dirtyQueue, GlobalQueue.
|
|
1123
|
+
if (dirtyQueue.EE >= dirtyQueue.et) {
|
|
1124
|
+
runHeap(dirtyQueue, GlobalQueue.We);
|
|
869
1125
|
commitPendingNodes();
|
|
870
1126
|
}
|
|
871
1127
|
}
|
|
872
1128
|
sweepTransientStoreNodes();
|
|
873
1129
|
// Lanes only enter activeLanes through the engine's getOrCreateLane.
|
|
874
|
-
if (activeLanes.size) GlobalQueue.
|
|
1130
|
+
if (activeLanes.size) GlobalQueue.Rn(e);
|
|
875
1131
|
}
|
|
876
1132
|
}
|
|
877
1133
|
|
|
878
1134
|
function checkBoundaryChildren(e) {
|
|
879
|
-
for (const t of e.
|
|
880
|
-
t.
|
|
1135
|
+
for (const t of e.hn) {
|
|
1136
|
+
t.fe?.();
|
|
881
1137
|
checkBoundaryChildren(t);
|
|
882
1138
|
}
|
|
883
1139
|
}
|
|
@@ -900,8 +1156,10 @@ function checkBoundaryChildren(e) {
|
|
|
900
1156
|
|
|
901
1157
|
function reassignPendingTransition(e) {
|
|
902
1158
|
for (let t = 0; t < e.length; t++) {
|
|
903
|
-
e[t].
|
|
904
|
-
|
|
1159
|
+
e[t].Ge = activeTransition;
|
|
1160
|
+
e[t].T &= ~CONFIG_ADOPTED_UNFLUSHED;
|
|
1161
|
+
// this flush carried it
|
|
1162
|
+
}
|
|
905
1163
|
}
|
|
906
1164
|
|
|
907
1165
|
const globalQueue = new GlobalQueue;
|
|
@@ -914,6 +1172,16 @@ const globalQueue = new GlobalQueue;
|
|
|
914
1172
|
let currentBatch = globalQueue.m;
|
|
915
1173
|
|
|
916
1174
|
function flush(e) {
|
|
1175
|
+
// Inside an action body the drain is incoherent (#3333): the action's
|
|
1176
|
+
// writes are held by its transaction until it settles, so a drain can't
|
|
1177
|
+
// reveal them — and the loop below only exits once `activeTransition` is
|
|
1178
|
+
// null, so it would PARK the transaction mid-slice and every write after it
|
|
1179
|
+
// in the body would land as a plain, committed write. The reporter's
|
|
1180
|
+
// "leading flush()" workaround was exactly that leak. Prod: run `fn` if
|
|
1181
|
+
// given (its writes stay in the transaction) and skip the drain.
|
|
1182
|
+
if (actionStepDepth > 0) {
|
|
1183
|
+
return e ? e() : undefined;
|
|
1184
|
+
}
|
|
917
1185
|
if (e) {
|
|
918
1186
|
syncDepth++;
|
|
919
1187
|
try {
|
|
@@ -928,7 +1196,7 @@ function flush(e) {
|
|
|
928
1196
|
}
|
|
929
1197
|
}
|
|
930
1198
|
}
|
|
931
|
-
if (globalQueue.
|
|
1199
|
+
if (globalQueue.Kt) {
|
|
932
1200
|
return;
|
|
933
1201
|
}
|
|
934
1202
|
if (halted) return;
|
|
@@ -937,41 +1205,115 @@ function flush(e) {
|
|
|
937
1205
|
while (scheduled || activeTransition) {
|
|
938
1206
|
globalQueue.flush();
|
|
939
1207
|
}
|
|
1208
|
+
// Provenance ends with the drain: every ambient window (an action's first
|
|
1209
|
+
// slice, a landing's propagation) runs to this flush.
|
|
1210
|
+
origin = 0;
|
|
940
1211
|
}
|
|
941
1212
|
|
|
942
1213
|
function runQueue(e, t) {
|
|
943
|
-
for (let
|
|
1214
|
+
for (let n = 0; n < e.length; n++) e[n](t);
|
|
944
1215
|
}
|
|
945
1216
|
|
|
946
|
-
function reporterBlocksSource(e, t) {
|
|
947
|
-
|
|
948
|
-
if (
|
|
949
|
-
|
|
950
|
-
|
|
1217
|
+
function reporterBlocksSource(e, t, n) {
|
|
1218
|
+
const i = e.ue;
|
|
1219
|
+
if (i & REACTIVE_DISPOSED) return false;
|
|
1220
|
+
// A zombie renders until the commit that disposes it (#3463): while its
|
|
1221
|
+
// removal is staged in a live transaction it is still on screen, and what
|
|
1222
|
+
// it displays must stay consistent with the frame — a held `Show`'s
|
|
1223
|
+
// `Details: 0` beside the lane's `Value: 1` otherwise. Its say is moot for
|
|
1224
|
+
// the verdict of the transaction that stages the removal (`verdict`): done,
|
|
1225
|
+
// and the commit disposes it; not done, and it stays parked regardless. A
|
|
1226
|
+
// zombie whose removal commits this flush (owner's pass not held) is dead.
|
|
1227
|
+
// The owner is stamped when the flush parks; held in this flush, its
|
|
1228
|
+
// staging transaction is the active one.
|
|
1229
|
+
if (i & REACTIVE_ZOMBIE) {
|
|
1230
|
+
let t = e;
|
|
1231
|
+
while (t && t.ue & REACTIVE_ZOMBIE) t = t._parent;
|
|
1232
|
+
let i = t && (t.Ge || (t.T & CONFIG_HELD_CHILDREN ? activeTransition : null));
|
|
1233
|
+
if (!i || (i = currentTransition(i)).Tt === true || i === n) return false;
|
|
1234
|
+
}
|
|
1235
|
+
// Fallback-caught async holds nothing. A collecting loading boundary
|
|
1236
|
+
// consumes the notification, so a reader under a fallback never registers —
|
|
1237
|
+
// but a reader registered while its boundary showed content stays
|
|
1238
|
+
// registered when the boundary's `on` later changes and it flips to the
|
|
1239
|
+
// fallback. The reader is behind the fallback now; if nothing outside the
|
|
1240
|
+
// boundary consumes the flight, the hold is over (A33, ruled 2026-09-12, #3375).
|
|
1241
|
+
for (let t = e.C; t; t = t._parent) if (t.ee & STATUS_PENDING && !t.L) return false;
|
|
1242
|
+
// "Still derives from the source" is a question about THIS pass's reads:
|
|
1243
|
+
// the deps up to `_depsTail`. Past it lie the committed frame's — kept
|
|
1244
|
+
// linked by A30 until the commit trims them (a staged pass, an errored
|
|
1245
|
+
// one). Reading them here made a reporter whose pass had stopped reading
|
|
1246
|
+
// the source (a gate closed in the same flush as the write) look live, and
|
|
1247
|
+
// the hold it kept was the commit that would have trimmed the dep that
|
|
1248
|
+
// kept it (spec O3, same-flush form; fuzzer #3446 P1 cases 21/79). A
|
|
1249
|
+
// trimmed list ends at `_depsTail`, so the bound is free there; a pass
|
|
1250
|
+
// that read nothing has a null tail and derives from nothing.
|
|
1251
|
+
// The registration is trusted: a pending mark rides only this pass's links
|
|
1252
|
+
// (notifyStatus skips the kept tail), so a registered reporter read the
|
|
1253
|
+
// source, or threw on it.
|
|
1254
|
+
if (e.o?.ae?.has(t)) return true;
|
|
1255
|
+
const s = e.ot;
|
|
1256
|
+
for (let n = s === null ? null : e.Se; n; n = n === s ? null : n.de) {
|
|
1257
|
+
let e = n.Ee;
|
|
951
1258
|
while (e) {
|
|
952
|
-
|
|
953
|
-
|
|
1259
|
+
// Or through a memo pending on the flight (#3494): a stale reader
|
|
1260
|
+
// served a held memo's committed value never turned pending itself, so
|
|
1261
|
+
// its only trace of the flight is the memo between them — `copy` of
|
|
1262
|
+
// `details`. Judged dead, its transaction released `count=1` beside
|
|
1263
|
+
// the `Copy: 0` it displays. `_pendingSources` is transitive, so one
|
|
1264
|
+
// hop covers any depth.
|
|
1265
|
+
if (e === t || e.Te === t || e.o?.ae?.has(t)) return true;
|
|
1266
|
+
e = e.o?.Ht;
|
|
954
1267
|
}
|
|
955
1268
|
}
|
|
956
1269
|
return !!(e.S & STATUS_PENDING && e.o?._ instanceof NotReadyError && e.o?._.source === t);
|
|
957
1270
|
}
|
|
958
1271
|
|
|
1272
|
+
/**
|
|
1273
|
+
* Does a live reporter of `transition` still observe `source` pending? Dead
|
|
1274
|
+
* reporters (disposed, behind a fallback, no longer reading the source) are
|
|
1275
|
+
* pruned as they are found, and the source's entry with them. Shared by the
|
|
1276
|
+
* settle verdict and the lane's hold check (`waitingTransition`): a live
|
|
1277
|
+
* action parks its transaction without a verdict, so this prune is the only
|
|
1278
|
+
* one an optimistic lane whose last async reader unmounted mid-action ever
|
|
1279
|
+
* gets — without it the lane held on the dead reporter's registration until
|
|
1280
|
+
* the flight it no longer observed landed (#3426).
|
|
1281
|
+
*/ function sourceObserved(e, t, n) {
|
|
1282
|
+
const i = e.oe.get(t);
|
|
1283
|
+
let s = false;
|
|
1284
|
+
for (const e of i ?? []) {
|
|
1285
|
+
if (reporterBlocksSource(e, t, n)) return true;
|
|
1286
|
+
// A zombie the verdict passes over is kept, not pruned (#3463): moot for
|
|
1287
|
+
// this verdict, it still holds a lane's reveal while the transaction
|
|
1288
|
+
// stays parked on something else.
|
|
1289
|
+
if (n && e.ue & REACTIVE_ZOMBIE) s = true; else i.delete(e);
|
|
1290
|
+
}
|
|
1291
|
+
if (!s) e.oe.delete(t);
|
|
1292
|
+
return false;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
959
1295
|
function transitionComplete(e) {
|
|
960
|
-
if (e.
|
|
961
|
-
if (e.
|
|
1296
|
+
if (e.Tt) return true;
|
|
1297
|
+
if (e.pe.length) {
|
|
962
1298
|
return false;
|
|
963
1299
|
}
|
|
964
1300
|
let t = true;
|
|
965
|
-
for (const
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1301
|
+
for (const n of e.oe.keys()) {
|
|
1302
|
+
// The source blocks while its OWN flight is up — the self entry in its
|
|
1303
|
+
// pending sources (added by notifyStatus's source path, with status;
|
|
1304
|
+
// retired by the landing and the supersede sweep, so it implies
|
|
1305
|
+
// STATUS_PENDING). `_error.source` is not that test: propagation from an
|
|
1306
|
+
// input that went pending later overwrites it with the input (#3375 — a
|
|
1307
|
+
// boundary-consumed load re-asked under a held derivation), and the
|
|
1308
|
+
// still-flying source read as settled, committing the writes it was
|
|
1309
|
+
// asked with ahead of its answer.
|
|
1310
|
+
// Not the self entry alone: a source whose own flight an upstream re-ask
|
|
1311
|
+
// superseded is still pending — on that re-ask (#3462). Its reader cannot
|
|
1312
|
+
// render until the chain lands, and the landing folds this transaction in
|
|
1313
|
+
// (enterWaiting). Judged complete instead, a re-entry between the two (a
|
|
1314
|
+
// repeated write to a held signal) committed the held writes beside the
|
|
1315
|
+
// reader's stale frame.
|
|
1316
|
+
if (sourceObserved(e, n, e) && n.o?.ae?.size) {
|
|
975
1317
|
t = false;
|
|
976
1318
|
break;
|
|
977
1319
|
}
|
|
@@ -979,8 +1321,8 @@ function transitionComplete(e) {
|
|
|
979
1321
|
// Override blockage lives with the engine (absent hook = "no optimistic
|
|
980
1322
|
// blockage"); the hook's loops over _optimisticNodes/_optimisticStores are
|
|
981
1323
|
// no-ops when the transition holds neither, so no pre-check is needed.
|
|
982
|
-
if (t && GlobalQueue.
|
|
983
|
-
t && (e.
|
|
1324
|
+
if (t && GlobalQueue.Nn?.(e)) t = false;
|
|
1325
|
+
t && (e.Tt = true);
|
|
984
1326
|
return t;
|
|
985
1327
|
}
|
|
986
1328
|
|
|
@@ -992,17 +1334,42 @@ function transitionComplete(e) {
|
|
|
992
1334
|
}
|
|
993
1335
|
|
|
994
1336
|
function currentTransition(e) {
|
|
995
|
-
while (e.
|
|
1337
|
+
while (e.Tt && typeof e.Tt === "object") e = e.Tt;
|
|
996
1338
|
return e;
|
|
997
1339
|
}
|
|
998
1340
|
|
|
1341
|
+
/**
|
|
1342
|
+
* The live transition blocked on `source` — the one whose render reader
|
|
1343
|
+
* observed it pending (INV-3 records the observation in whichever transaction
|
|
1344
|
+
* was active when the reader was notified). The observation is a fact about
|
|
1345
|
+
* the node, so a hold check must not assume it was recorded in the transaction
|
|
1346
|
+
* it happens to hold — lanes merge across transactions (#2912), and a merged
|
|
1347
|
+
* root's transaction knows nothing of the async its members' transactions
|
|
1348
|
+
* observed (#3335). Null when nobody is waiting — a registration whose every
|
|
1349
|
+
* reporter has since died is nobody (#3426).
|
|
1350
|
+
*/ function waitingTransition(e) {
|
|
1351
|
+
for (const t of transitions) if (sourceObserved(t, e)) return t;
|
|
1352
|
+
return null;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
/** A landing enters EVERY parked transaction still waiting on `source`, folding
|
|
1356
|
+
* them into the active one (A15: each reveal that discovered the flight
|
|
1357
|
+
* completes at its landing). The fold used to happen as the waiters' stamped
|
|
1358
|
+
* readers recomputed under the landing — recompute re-entering an effect's
|
|
1359
|
+
* stamp — which also folded in writes those readers merely shared a hole
|
|
1360
|
+
* with (#3407); effects no longer re-enter, so the landing folds explicitly.
|
|
1361
|
+
* Live iteration is safe: a merge deletes the outgoing (active) entry and
|
|
1362
|
+
* re-adds the visited one. */ function enterWaiting(e) {
|
|
1363
|
+
for (const t of transitions) if (sourceObserved(t, e)) globalQueue.initTransition(t);
|
|
1364
|
+
}
|
|
1365
|
+
|
|
999
1366
|
function runInTransition(e, t) {
|
|
1000
|
-
const
|
|
1367
|
+
const n = activeTransition;
|
|
1001
1368
|
try {
|
|
1002
1369
|
activeTransition = currentTransition(e);
|
|
1003
1370
|
return t();
|
|
1004
1371
|
} finally {
|
|
1005
|
-
activeTransition =
|
|
1372
|
+
activeTransition = n;
|
|
1006
1373
|
}
|
|
1007
1374
|
}
|
|
1008
1375
|
|
|
@@ -1017,16 +1384,16 @@ function runInTransition(e, t) {
|
|
|
1017
1384
|
* initTransition is the wrong tool here: it MERGES the currently ambient
|
|
1018
1385
|
* transaction into the target, entangling whatever the interrupted window
|
|
1019
1386
|
* belonged to. */ function runAsTransitionBatch(e, t) {
|
|
1020
|
-
const
|
|
1021
|
-
const
|
|
1387
|
+
const n = activeTransition;
|
|
1388
|
+
const i = globalQueue.m;
|
|
1022
1389
|
try {
|
|
1023
1390
|
activeTransition = currentTransition(e);
|
|
1024
1391
|
currentBatch = globalQueue.m = activeTransition;
|
|
1025
1392
|
return t();
|
|
1026
1393
|
} finally {
|
|
1027
|
-
activeTransition =
|
|
1028
|
-
currentBatch = globalQueue.m =
|
|
1394
|
+
activeTransition = n;
|
|
1395
|
+
currentBatch = globalQueue.m = i;
|
|
1029
1396
|
}
|
|
1030
1397
|
}
|
|
1031
1398
|
|
|
1032
|
-
export { GlobalQueue, Queue, activeAffectsMarks, activeLanes, activeTransition, armReaskClear, assignOrMergeLane, bumpNotifyEpoch, clock, createTransition, currentTransition, dirtyQueue, enforceLoadingBoundary, entangleConfirmingTransitions, finalizePureQueue, findLane, flush, globalQueue, haltReactivity, hasActiveOverride, insertSubs, notifyEpoch, projectionWriteActive, queuePendingNode, reaskArmed, resetErrorHalt, runAsTransitionBatch, runInTransition, schedule, setProjectionWriteActive, setStoreCommitHook, shiftAffectsMarks, storeCommitHook, zombieQueue };
|
|
1399
|
+
export { GlobalQueue, Queue, ROOT_ERROR_HOOK, actionStepDepth, activeAffectsMarks, activeLanes, activeTransition, armReaskClear, assignOrMergeLane, batchJoins, bumpNotifyEpoch, clock, createTransition, currentTransition, deferSlotRelease, dirtyQueue, enforceLoadingBoundary, entangleConfirmingTransitions, enterActionStep, enterWaiting, exitActionStep, finalizePureQueue, findLane, flush, globalQueue, haltReactivity, hasActiveOverride, heldTrims, insertSubs, notifyEpoch, origin, projectionWriteActive, queuePendingNode, reaskArmed, resetErrorHalt, runAsTransitionBatch, runInTransition, schedule, setOrigin, setProjectionWriteActive, setStoreCommitHook, shiftAffectsMarks, sourceObserved, storeCommitHook, transitions, waitingTransition, wakeParked, wokenTransitions, zombieQueue };
|