@solidjs/signals 2.0.0-rc.6 → 2.0.0-rc.8
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 +5243 -0
- package/dist/dev.attribution.js +2333 -0
- package/dist/dev.js +1758 -6621
- package/dist/observe/affects.js +127 -0
- package/dist/observe/attribution.js +1 -0
- package/dist/observe/boundaries.js +586 -0
- package/dist/observe/core/action.js +167 -0
- package/dist/observe/core/async.js +736 -0
- package/dist/observe/core/attribution-hooks.js +60 -0
- package/dist/observe/core/attribution.js +2318 -0
- package/dist/{types-cjs/core/constants.d.cts → observe/core/constants.js} +135 -71
- package/dist/{types-cjs/core/context.d.cts → observe/core/context.js} +37 -12
- package/dist/observe/core/core.js +1289 -0
- package/dist/observe/core/dev.js +232 -0
- package/dist/observe/core/effect.js +179 -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 +43 -0
- package/dist/observe/core/lanes.js +168 -0
- package/dist/observe/core/optimistic.js +438 -0
- package/dist/observe/core/owner.js +308 -0
- package/dist/observe/core/scheduler.js +1190 -0
- package/dist/observe/core/verdict.js +551 -0
- package/dist/observe/index.js +55 -0
- package/dist/observe/map.js +482 -0
- package/dist/{types-cjs/signals.d.cts → observe/signals.js} +305 -317
- package/dist/observe/store/index.js +38 -0
- package/dist/observe/store/next/optimistic.js +651 -0
- package/dist/observe/store/next/projection.js +242 -0
- package/dist/observe/store/next/reconcile.js +373 -0
- package/dist/observe/store/next/store.js +2213 -0
- package/dist/observe/store/next/target.js +51 -0
- package/dist/observe/store/store.js +323 -0
- package/dist/observe/store/storePath.js +103 -0
- package/dist/observe/store/utils.js +210 -0
- package/dist/prod/attribution.js +32 -0
- package/dist/prod/boundaries.js +3 -1
- package/dist/prod/core/action.js +52 -32
- package/dist/prod/core/async.js +177 -128
- package/dist/prod/core/attribution-hooks.js +3 -0
- package/dist/prod/core/constants.js +32 -1
- package/dist/prod/core/context.js +10 -16
- package/dist/prod/core/core.js +505 -286
- package/dist/prod/core/dev.js +2 -0
- package/dist/prod/core/effect.js +72 -43
- package/dist/prod/core/external.js +2 -2
- package/dist/prod/core/graph.js +40 -36
- package/dist/prod/core/heap.js +50 -51
- package/dist/prod/core/lanes.js +43 -22
- package/dist/prod/core/optimistic.js +224 -80
- package/dist/prod/core/owner.js +43 -39
- package/dist/prod/core/scheduler.js +347 -232
- package/dist/prod/core/verdict.js +71 -74
- package/dist/prod/index.js +9 -5
- package/dist/prod/map.js +311 -150
- package/dist/prod/signals.js +52 -38
- package/dist/prod/store/next/optimistic.js +127 -131
- package/dist/prod/store/next/projection.js +3 -3
- package/dist/prod/store/next/reconcile.js +149 -294
- package/dist/prod/store/next/store.js +694 -380
- package/dist/prod/store/next/target.js +32 -10
- package/dist/prod/store/store.js +12 -14
- package/dist/prod/store/utils.js +36 -27
- package/dist/types/attribution.d.ts +15 -0
- package/dist/types/attribution.prod.d.ts +13 -0
- package/dist/types/core/async.d.ts +1 -1
- package/dist/types/core/attribution-hooks.d.ts +183 -6
- package/dist/types/core/attribution.d.ts +435 -24
- package/dist/types/core/constants.d.ts +31 -0
- package/dist/types/core/core.d.ts +19 -3
- package/dist/types/core/dev.d.ts +165 -26
- package/dist/types/core/effect.d.ts +8 -1
- package/dist/types/core/heap.d.ts +5 -3
- package/dist/types/core/index.d.ts +2 -1
- package/dist/types/core/invariants.d.ts +1 -1
- package/dist/types/core/lanes.d.ts +17 -0
- package/dist/types/core/scheduler.d.ts +69 -4
- package/dist/types/core/types.d.ts +29 -9
- package/dist/types/index.d.ts +9 -2
- package/dist/types/map.d.ts +2 -0
- package/dist/types/signals.d.ts +10 -0
- package/dist/types/store/index.d.ts +4 -6
- package/dist/types/store/next/optimistic.d.ts +4 -2
- package/dist/types/store/next/reconcile.d.ts +5 -12
- package/dist/types/store/next/store.d.ts +8 -9
- package/dist/types/store/next/target.d.ts +56 -55
- package/dist/types/store/store.d.ts +14 -9
- package/dist/types/store/utils.d.ts +7 -0
- package/package.json +21 -16
- package/dist/node.cjs +0 -11121
- package/dist/prod/store/next/patch-hooks.js +0 -13
- package/dist/prod/store/next/patch.js +0 -614
- package/dist/types/store/next/patch-hooks.d.ts +0 -41
- package/dist/types/store/next/patch.d.ts +0 -91
- 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 -63
- package/dist/types-cjs/core/attribution.d.cts +0 -239
- package/dist/types-cjs/core/core.d.cts +0 -176
- package/dist/types-cjs/core/dev.d.cts +0 -95
- 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 -22
- 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 -44
- 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 -231
- 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 -21
- package/dist/types-cjs/store/next/optimistic.d.cts +0 -23
- package/dist/types-cjs/store/next/patch-hooks.d.cts +0 -41
- package/dist/types-cjs/store/next/patch.d.cts +0 -91
- package/dist/types-cjs/store/next/projection.d.cts +0 -8
- package/dist/types-cjs/store/next/reconcile.d.cts +0 -17
- package/dist/types-cjs/store/next/store.d.cts +0 -127
- package/dist/types-cjs/store/next/target.d.cts +0 -192
- package/dist/types-cjs/store/store.d.cts +0 -138
- package/dist/types-cjs/store/storePath.d.cts +0 -58
- package/dist/types-cjs/store/utils.d.cts +0 -74
|
@@ -1,6 +1,6 @@
|
|
|
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, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, CONFIG_HAS_COMPANIONS, EFFECT_TRACKED, REACTIVE_REASK, REACTIVE_MANUAL_WRITE, STATUS_UNINITIALIZED, CONFIG_AUTHORITATIVE_READ } from "./constants.js";
|
|
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, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, CONFIG_SLOT_NODE, CONFIG_HAS_COMPANIONS, EFFECT_TRACKED, CONFIG_INPUTS_PUBLISHED, REACTIVE_REASK, REACTIVE_MANUAL_WRITE, STATUS_UNINITIALIZED, CONFIG_AUTHORITATIVE_READ } from "./constants.js";
|
|
2
2
|
|
|
3
|
-
import { currentOptimisticLane } from "./core.js";
|
|
3
|
+
import { currentOptimisticLane, slotUnobservedHook } from "./core.js";
|
|
4
4
|
|
|
5
5
|
import { DEV } from "./dev.js";
|
|
6
6
|
|
|
@@ -21,14 +21,14 @@ const transitions = new Set;
|
|
|
21
21
|
const dirtyQueue = {
|
|
22
22
|
eE: new Array(2e3).fill(undefined),
|
|
23
23
|
tE: false,
|
|
24
|
-
|
|
24
|
+
Ke: 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
|
+
Ke: 0,
|
|
32
32
|
EE: 0
|
|
33
33
|
};
|
|
34
34
|
|
|
@@ -55,6 +55,17 @@ let syncDepth = 0;
|
|
|
55
55
|
|
|
56
56
|
let projectionWriteActive = false;
|
|
57
57
|
|
|
58
|
+
/** > 0 while an action's generator body is on the stack (the synchronous
|
|
59
|
+
* slice between yields). Maintained by action.ts around `it.next()`. */ let actionStepDepth = 0;
|
|
60
|
+
|
|
61
|
+
function enterActionStep() {
|
|
62
|
+
actionStepDepth++;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function exitActionStep() {
|
|
66
|
+
actionStepDepth--;
|
|
67
|
+
}
|
|
68
|
+
|
|
58
69
|
// Store property nodes that were created solely to carry a pending write (no
|
|
59
70
|
// subscribers at write time). Swept after each flush that commits pending
|
|
60
71
|
// values — any still without subs get disposed via their `_unobserved` hook,
|
|
@@ -63,7 +74,7 @@ const transientStoreNodes = new Set;
|
|
|
63
74
|
|
|
64
75
|
function canUseSimpleSyncFlush(e) {
|
|
65
76
|
const t = e.m;
|
|
66
|
-
return transitions.size === 0 && activeLanes.size === 0 && e.
|
|
77
|
+
return transitions.size === 0 && activeLanes.size === 0 && e.Xt.length === 0 && t.it.length === 0 && t.A.length === 0 && t.En.size === 0 && transientStoreNodes.size === 0;
|
|
67
78
|
}
|
|
68
79
|
|
|
69
80
|
function sweepTransientStoreNodes() {
|
|
@@ -73,14 +84,14 @@ function sweepTransientStoreNodes() {
|
|
|
73
84
|
transientStoreNodes.delete(e);
|
|
74
85
|
continue;
|
|
75
86
|
}
|
|
76
|
-
if (e.
|
|
77
|
-
if (e.o?.
|
|
87
|
+
if (e.Ge !== NOT_PENDING) continue;
|
|
88
|
+
if (e.o?.be !== undefined && e.o?.be !== NOT_PENDING) continue;
|
|
78
89
|
// A live affects() mark keeps the node addressable: sweeping it would
|
|
79
90
|
// detach the refcount from the slot (a fresh probe would upsert a new,
|
|
80
91
|
// unmarked node for the same property).
|
|
81
92
|
if (e.o?.t) continue;
|
|
82
93
|
transientStoreNodes.delete(e);
|
|
83
|
-
e.o?.
|
|
94
|
+
if (e.T & CONFIG_SLOT_NODE) slotUnobservedHook(e); else e.o?.Ct?.();
|
|
84
95
|
}
|
|
85
96
|
}
|
|
86
97
|
|
|
@@ -107,32 +118,33 @@ function setProjectionWriteActive(e) {
|
|
|
107
118
|
* land there directly — no per-field aliasing.
|
|
108
119
|
*/ function createBatch() {
|
|
109
120
|
return {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
121
|
+
_e: clock,
|
|
122
|
+
$t: [],
|
|
123
|
+
Re: new Map,
|
|
124
|
+
it: [],
|
|
114
125
|
A: [],
|
|
115
126
|
En: new Set,
|
|
116
127
|
ue: [],
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
128
|
+
ei: {
|
|
129
|
+
ti: [ [], [] ],
|
|
130
|
+
Xt: []
|
|
120
131
|
},
|
|
121
|
-
|
|
122
|
-
|
|
132
|
+
ft: false,
|
|
133
|
+
lt: new Set,
|
|
134
|
+
Et: null
|
|
123
135
|
};
|
|
124
136
|
}
|
|
125
137
|
|
|
126
138
|
function mergeTransitionState(e, t) {
|
|
127
|
-
t.
|
|
139
|
+
t.ft = e;
|
|
128
140
|
e.ue.push(...t.ue);
|
|
129
|
-
for (const i of activeLanes) if (i.
|
|
130
|
-
if (t.
|
|
141
|
+
for (const i of activeLanes) if (i.ge === t) i.ge = e;
|
|
142
|
+
if (t.it.length) {
|
|
131
143
|
// Move (don't copy): the global queue's batch may still be the outgoing
|
|
132
144
|
// transition, and the adoption pass in initTransition would re-push its
|
|
133
145
|
// contents into the target — duplicating every entry.
|
|
134
|
-
e.
|
|
135
|
-
t.
|
|
146
|
+
e.it.push(...t.it);
|
|
147
|
+
t.it.length = 0;
|
|
136
148
|
}
|
|
137
149
|
if (t.A.length) {
|
|
138
150
|
// Move (don't copy): the global queue's batch may still be the outgoing
|
|
@@ -142,32 +154,13 @@ function mergeTransitionState(e, t) {
|
|
|
142
154
|
t.A.length = 0;
|
|
143
155
|
}
|
|
144
156
|
for (const i of t.En) e.En.add(i);
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
// so a stranded sidecar would silently drop its patches. Move (don't
|
|
149
|
-
// copy), same aliasing rule as the collections above. The field is an
|
|
150
|
-
// expando so this module stays free of patch imports (pay-for-use).
|
|
151
|
-
const i = t.wt;
|
|
152
|
-
if (i !== undefined) {
|
|
153
|
-
t.wt = undefined;
|
|
154
|
-
let n = e.wt;
|
|
155
|
-
if (n !== undefined) n.push(...i); else n = e.wt = i;
|
|
156
|
-
// Retarget the entries' coalescing stamps to the surviving stash
|
|
157
|
-
// (opaque backref contract with store/next/patch.ts): without this a
|
|
158
|
-
// post-merge emission misses the stamp and pushes a SECOND entry —
|
|
159
|
-
// the record's patch applies twice at commit (re-audit 5, P1-2).
|
|
160
|
-
for (let e = 0; e < i.length; e++) {
|
|
161
|
-
const t = i[e].pc;
|
|
162
|
-
if (t !== undefined && t.qe === i[e]) t.qa = n;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
for (const [i, n] of t._e) {
|
|
166
|
-
let t = e._e.get(i);
|
|
167
|
-
if (!t) e._e.set(i, t = new Set);
|
|
157
|
+
for (const [i, n] of t.Re) {
|
|
158
|
+
let t = e.Re.get(i);
|
|
159
|
+
if (!t) e.Re.set(i, t = new Set);
|
|
168
160
|
for (const e of n) t.add(e);
|
|
169
161
|
}
|
|
170
|
-
for (const i of t.
|
|
162
|
+
for (const i of t.lt) e.lt.add(i);
|
|
163
|
+
if (t.Et) (e.Et ??= []).push(...t.Et);
|
|
171
164
|
}
|
|
172
165
|
|
|
173
166
|
/**
|
|
@@ -188,7 +181,7 @@ function mergeTransitionState(e, t) {
|
|
|
188
181
|
* `until()` retain this.
|
|
189
182
|
*/ function entangleConfirmingTransitions(e, t) {
|
|
190
183
|
t = currentTransition(t);
|
|
191
|
-
if (t.
|
|
184
|
+
if (t.ft === true) return;
|
|
192
185
|
// The confirming evidence is a dep whose value is STAGED (pending,
|
|
193
186
|
// uncommitted) at flip evaluation — committed deps are public already and
|
|
194
187
|
// carry nothing to entangle. A staged dep lives in one of two carriers: a
|
|
@@ -201,10 +194,10 @@ function mergeTransitionState(e, t) {
|
|
|
201
194
|
// the awaiting transaction to landings that haven't happened; a merged
|
|
202
195
|
// reporter deadlocked exactly that way).
|
|
203
196
|
let i = false;
|
|
204
|
-
for (let n = e.
|
|
205
|
-
const r = n.
|
|
206
|
-
if (r.
|
|
207
|
-
const e = r.
|
|
197
|
+
for (let n = e.fe; n !== null; n = n.ae) {
|
|
198
|
+
const r = n.Se;
|
|
199
|
+
if (r.Ge !== NOT_PENDING) {
|
|
200
|
+
const e = r.ge;
|
|
208
201
|
const n = e != null ? currentTransition(e) : null;
|
|
209
202
|
// Skip the awaiting transaction's own cargo (t === target: a
|
|
210
203
|
// fold-staged landing or a write the action itself issued — hold and
|
|
@@ -212,10 +205,10 @@ function mergeTransitionState(e, t) {
|
|
|
212
205
|
// (t === null) must leave the batch NOW — it commits at this flush's
|
|
213
206
|
// end, which would reveal the confirmation under the live optimism
|
|
214
207
|
// it just confirmed.
|
|
215
|
-
const s = n === null ? currentBatch
|
|
208
|
+
const s = n === null ? currentBatch.$t : n !== t && n.ft !== true ? n.$t : null;
|
|
216
209
|
if (s !== null) i = stealEntangledCargo(s, t) || i;
|
|
217
210
|
}
|
|
218
|
-
if (n === e.
|
|
211
|
+
if (n === e.et) break;
|
|
219
212
|
}
|
|
220
213
|
// The steal never activates the awaiting transaction: the predicate can
|
|
221
214
|
// flip inside another transaction's finalize heap, and adopting the queue
|
|
@@ -244,20 +237,20 @@ function mergeTransitionState(e, t) {
|
|
|
244
237
|
* committed value — while re-running them would re-derive the OLD world
|
|
245
238
|
* and re-stage it over the held truth. The reveal re-notifies
|
|
246
239
|
* (commitPendingNodes), which is when they re-derive for real. */ function stealEntangledCargo(e, t) {
|
|
247
|
-
if (e === t
|
|
240
|
+
if (e === t.$t || e.length === 0) return false;
|
|
248
241
|
for (let i = 0; i < e.length; i++) {
|
|
249
242
|
const n = e[i];
|
|
250
|
-
n.
|
|
251
|
-
t.
|
|
243
|
+
n.ge = t;
|
|
244
|
+
t.$t.push(n);
|
|
252
245
|
// Override-covered nodes stay silent AND unmasked: the override is the
|
|
253
246
|
// display (A17 — its staging never notified, its revert will), so their
|
|
254
247
|
// subs saw nothing and re-running one would break the silence with a
|
|
255
248
|
// duplicate fire of an unchanged view.
|
|
256
249
|
if (!hasActiveOverride(n)) {
|
|
257
250
|
n.T |= CONFIG_HELD_TRUTH;
|
|
258
|
-
for (let e = n.u; e !== null; e = e.
|
|
259
|
-
const t = e.
|
|
260
|
-
if (t.
|
|
251
|
+
for (let e = n.u; e !== null; e = e.Te) {
|
|
252
|
+
const t = e.Ie;
|
|
253
|
+
if (t.Ce && !(t.T & CONFIG_AUTHORITATIVE_READ)) enqueueSub(t);
|
|
261
254
|
}
|
|
262
255
|
}
|
|
263
256
|
}
|
|
@@ -273,7 +266,7 @@ function schedule() {
|
|
|
273
266
|
}
|
|
274
267
|
if (scheduled) return;
|
|
275
268
|
scheduled = true;
|
|
276
|
-
if (!syncDepth && !globalQueue.
|
|
269
|
+
if (!syncDepth && !globalQueue.sn && !projectionWriteActive) queueMicrotask(flush);
|
|
277
270
|
}
|
|
278
271
|
|
|
279
272
|
/**
|
|
@@ -284,10 +277,18 @@ function schedule() {
|
|
|
284
277
|
if (halted) return;
|
|
285
278
|
halted = true;
|
|
286
279
|
let t = "[REACTIVITY_HALTED]";
|
|
287
|
-
//
|
|
280
|
+
// Surface the cause here too: callers rethrow it, but a creation-time throw
|
|
288
281
|
// unwinds through ancestor recomputes that convert it to status instead of
|
|
289
282
|
// surfacing it (#2884), so the rethrow alone cannot guarantee visibility.
|
|
290
|
-
|
|
283
|
+
// Where the platform has one, hand the cause to its uncaught-error channel
|
|
284
|
+
// (`reportError` → `error` event → window.onerror / error monitoring): a
|
|
285
|
+
// halt that only reaches console.error leaves a page that LOOKS alive with
|
|
286
|
+
// nothing an app or its telemetry can act on (#3338 — an uncaught throw
|
|
287
|
+
// during the hydration render). The rethrow may reach the top as well in
|
|
288
|
+
// the non-swallowed cases; a duplicate report beats a silent one.
|
|
289
|
+
const i = e !== undefined && globalThis.reportError;
|
|
290
|
+
i || e === undefined ? console.error(t) : console.error(t, e);
|
|
291
|
+
i && i(e);
|
|
291
292
|
}
|
|
292
293
|
|
|
293
294
|
// Logs on the first write after a halt so a frozen interaction is traceable.
|
|
@@ -307,30 +308,30 @@ function notifyHalted() {
|
|
|
307
308
|
let queueRunToken = 0;
|
|
308
309
|
|
|
309
310
|
class Queue {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
311
|
+
qe=null;
|
|
312
|
+
ti=[ [], [] ];
|
|
313
|
+
Xt=[];
|
|
314
|
+
ii=0;
|
|
314
315
|
created=clock;
|
|
315
316
|
addChild(e) {
|
|
316
|
-
this.
|
|
317
|
-
e.
|
|
317
|
+
this.Xt.push(e);
|
|
318
|
+
e.qe = this;
|
|
318
319
|
}
|
|
319
320
|
removeChild(e) {
|
|
320
|
-
const t = this.
|
|
321
|
+
const t = this.Xt.indexOf(e);
|
|
321
322
|
if (t >= 0) {
|
|
322
|
-
this.
|
|
323
|
-
e.
|
|
323
|
+
this.Xt.splice(t, 1);
|
|
324
|
+
e.qe = null;
|
|
324
325
|
}
|
|
325
326
|
}
|
|
326
327
|
notify(e, t, i, n) {
|
|
327
|
-
if (this.
|
|
328
|
+
if (this.qe) return this.qe.notify(e, t, i, n);
|
|
328
329
|
return false;
|
|
329
330
|
}
|
|
330
331
|
run(e) {
|
|
331
|
-
if (this.
|
|
332
|
-
const t = this.
|
|
333
|
-
this.
|
|
332
|
+
if (this.ti[e - 1].length) {
|
|
333
|
+
const t = this.ti[e - 1];
|
|
334
|
+
this.ti[e - 1] = [];
|
|
334
335
|
runQueue(t, e);
|
|
335
336
|
}
|
|
336
337
|
// Effects run here can dispose owners, and disposal removes queues from
|
|
@@ -340,12 +341,12 @@ class Queue {
|
|
|
340
341
|
// can be recovered by rescanning from the front and every child still runs
|
|
341
342
|
// exactly once. Children appended mid-pass carry a stale stamp and run,
|
|
342
343
|
// matching the previous live-array behaviour.
|
|
343
|
-
const t = this.
|
|
344
|
+
const t = this.Xt;
|
|
344
345
|
const i = ++queueRunToken;
|
|
345
346
|
for (let n = 0; n < t.length; ) {
|
|
346
347
|
const r = t[n];
|
|
347
|
-
if (r.
|
|
348
|
-
r.
|
|
348
|
+
if (r.ii !== i) {
|
|
349
|
+
r.ii = i;
|
|
349
350
|
r.run?.(e);
|
|
350
351
|
if (t[n] !== r) {
|
|
351
352
|
n = 0;
|
|
@@ -362,48 +363,48 @@ class Queue {
|
|
|
362
363
|
const i = findLane(currentOptimisticLane);
|
|
363
364
|
i.tn[e - 1].push(t);
|
|
364
365
|
} else {
|
|
365
|
-
this.
|
|
366
|
+
this.ti[e - 1].push(t);
|
|
366
367
|
}
|
|
367
368
|
}
|
|
368
369
|
schedule();
|
|
369
370
|
}
|
|
370
371
|
stashQueues(e) {
|
|
371
|
-
e.
|
|
372
|
-
e.
|
|
373
|
-
this.
|
|
374
|
-
for (let t = 0; t < this.
|
|
375
|
-
let i = this.
|
|
376
|
-
let n = e.
|
|
372
|
+
e.ti[0].push(...this.ti[0]);
|
|
373
|
+
e.ti[1].push(...this.ti[1]);
|
|
374
|
+
this.ti = [ [], [] ];
|
|
375
|
+
for (let t = 0; t < this.Xt.length; t++) {
|
|
376
|
+
let i = this.Xt[t];
|
|
377
|
+
let n = e.Xt[t];
|
|
377
378
|
if (!n) {
|
|
378
379
|
n = {
|
|
379
|
-
|
|
380
|
-
|
|
380
|
+
ti: [ [], [] ],
|
|
381
|
+
Xt: []
|
|
381
382
|
};
|
|
382
|
-
e.
|
|
383
|
+
e.Xt[t] = n;
|
|
383
384
|
}
|
|
384
385
|
i.stashQueues(n);
|
|
385
386
|
}
|
|
386
387
|
}
|
|
387
388
|
restoreQueues(e) {
|
|
388
|
-
this.
|
|
389
|
-
this.
|
|
390
|
-
for (let t = 0; t < e.
|
|
391
|
-
const i = e.
|
|
392
|
-
let n = this.
|
|
389
|
+
this.ti[0].push(...e.ti[0]);
|
|
390
|
+
this.ti[1].push(...e.ti[1]);
|
|
391
|
+
for (let t = 0; t < e.Xt.length; t++) {
|
|
392
|
+
const i = e.Xt[t];
|
|
393
|
+
let n = this.Xt[t];
|
|
393
394
|
if (n) n.restoreQueues(i);
|
|
394
395
|
}
|
|
395
396
|
}
|
|
396
397
|
}
|
|
397
398
|
|
|
398
399
|
class GlobalQueue extends Queue {
|
|
399
|
-
|
|
400
|
+
sn=false;
|
|
400
401
|
// The current transaction-shaped batch: a plain ambient batch while no
|
|
401
402
|
// transition is active, the active transition itself after initTransition.
|
|
402
403
|
m=createBatch();
|
|
403
404
|
static Fe;
|
|
404
|
-
static
|
|
405
|
-
static
|
|
406
|
-
static
|
|
405
|
+
static We;
|
|
406
|
+
static ct;
|
|
407
|
+
static ni=null;
|
|
407
408
|
// Store-side hook: drops a keyless affects() mark's identity scope when the
|
|
408
409
|
// carrier node's last registration releases (wired by store.ts, mirroring
|
|
409
410
|
// _clearOptimisticStore).
|
|
@@ -417,27 +418,27 @@ class GlobalQueue extends Queue {
|
|
|
417
418
|
static N=null;
|
|
418
419
|
// External-source bridge (wired by enableExternalSource(); null while no
|
|
419
420
|
// config is active — including after _resetExternalSourceConfig()).
|
|
420
|
-
static
|
|
421
|
-
static
|
|
421
|
+
static kt=null;
|
|
422
|
+
static Lt=null;
|
|
422
423
|
// Verdict-layer hooks (wired by verdict.ts when isPending()/latest() are
|
|
423
424
|
// imported; null in apps that never use them). Call sites either guard for
|
|
424
425
|
// null or sit behind state only the verdict layer can create (`!` is safe
|
|
425
426
|
// there: `_pendingSignal`/`_latestValueComputed` are only ever assigned by
|
|
426
427
|
// verdict.ts, and `pendingCheckActive`/`latestReadActive` only flip inside
|
|
427
428
|
// isPending()/latest()).
|
|
428
|
-
static
|
|
429
|
-
static
|
|
430
|
-
static
|
|
429
|
+
static pe=null;
|
|
430
|
+
static Ne=null;
|
|
431
|
+
static ke=null;
|
|
431
432
|
static un=null;
|
|
432
|
-
static
|
|
433
|
-
static Ht=null;
|
|
434
|
-
static kt=null;
|
|
435
|
-
static et=null;
|
|
436
|
-
static k=null;
|
|
433
|
+
static wt=null;
|
|
437
434
|
static Wt=null;
|
|
435
|
+
static jt=null;
|
|
436
|
+
static st=null;
|
|
437
|
+
static k=null;
|
|
438
|
+
static ri=null;
|
|
438
439
|
// Re-asks probes whose verdict was provisionally suppressed by a fresh read
|
|
439
440
|
// of a held value, once the transaction gains an async blocker (#3028).
|
|
440
|
-
static
|
|
441
|
+
static si=null;
|
|
441
442
|
// Optimistic-engine hooks (wired by core/optimistic.ts via
|
|
442
443
|
// installOptimisticEngine(), called from verdict.ts / createOptimistic /
|
|
443
444
|
// createOptimisticStore — every module that can create optimistic state).
|
|
@@ -445,35 +446,61 @@ class GlobalQueue extends Queue {
|
|
|
445
446
|
// `_overrideValue` slot, a lane in `activeLanes`, an `_optimisticNodes`
|
|
446
447
|
// entry, or a non-null `currentOptimisticLane`, so `!` invocations are safe
|
|
447
448
|
// once the gate holds.
|
|
448
|
-
static
|
|
449
|
-
static
|
|
449
|
+
static Bt=null;
|
|
450
|
+
static fn=null;
|
|
451
|
+
static cn=null;
|
|
450
452
|
static dn=null;
|
|
451
453
|
static In=null;
|
|
452
|
-
static Nn=null;
|
|
453
454
|
/** Patch-channel optimistic drain (next/patch.ts): optimistic emissions
|
|
454
455
|
* apply at lane-effect timing — visible in flight, unlike the regular
|
|
455
456
|
* effect queues an action stashes. Injected; null when unused. */
|
|
456
457
|
static ln=null;
|
|
457
|
-
static
|
|
458
|
-
static
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
static
|
|
466
|
-
static
|
|
467
|
-
|
|
458
|
+
static Zt=null;
|
|
459
|
+
static qt=null;
|
|
460
|
+
/** Is the node routed through a LIVE lane (`resolveLane`)? read()'s reveal
|
|
461
|
+
* carve-out asks before showing a foreign-held pending node's committed
|
|
462
|
+
* value: a lane-derived flight's inputs are already revealed through the
|
|
463
|
+
* lane (#3334). Gated on CONFIG_HAS_LANE, which only the engine sets. */
|
|
464
|
+
static Mt=null;
|
|
465
|
+
static Kt=null;
|
|
466
|
+
static nt=null;
|
|
467
|
+
static ot=null;
|
|
468
|
+
/** Authoritative-view reader wakeup: installed by until() and refresh() before
|
|
469
|
+
* their first read. Call sites are gated by CONFIG_AUTHORITATIVE_OBSERVED, which
|
|
470
|
+
* only such a reader's carve-out read can set, so `!` invocations are safe once
|
|
471
|
+
* the gate holds (#3303). */
|
|
472
|
+
static Qt=null;
|
|
473
|
+
static ut=null;
|
|
474
|
+
/** A18 supersession (#3331): own-source truth `value` landed under an active
|
|
475
|
+
* override. The engine decides whether the graph re-derives — the value
|
|
476
|
+
* differs from the override and is not a stale (older-action) answer (mark
|
|
477
|
+
* the node, demote its lane cascade, notify), or returns to it after an
|
|
478
|
+
* earlier differing arrival (clear the mark, notify) — and owns the
|
|
479
|
+
* authoritative-observer wake for a silent confirm. Installed with the
|
|
480
|
+
* optimistic engine; only reachable on a node that has an override. */
|
|
481
|
+
static ye=null;
|
|
482
|
+
/** read()'s value for a TRACKED reader of a superseded node (#3331): the
|
|
483
|
+
* staged truth, unless the reader is a stale (render) reader of another
|
|
484
|
+
* transaction — then the displayed override, as it keeps a foreign
|
|
485
|
+
* transaction's committed value over its staged write. */
|
|
486
|
+
static Yt=null;
|
|
487
|
+
/** setSignal's authoritative (projection-write) landing on an override-
|
|
488
|
+
* covered node (#3331 store twin): stage the truth for its transaction's
|
|
489
|
+
* commit whatever its relation to the committed value — a landing equal to
|
|
490
|
+
* committed still differs from the override — then _supersedeOverride
|
|
491
|
+
* decides. Installed with the optimistic engine; only reachable on a node
|
|
492
|
+
* that has an override. */
|
|
493
|
+
static zt=null;
|
|
494
|
+
static Nn=null;
|
|
468
495
|
flush() {
|
|
469
|
-
if (this.
|
|
496
|
+
if (this.sn) return;
|
|
470
497
|
// Fast drain: nothing in flight but plain pending commits — no dirty
|
|
471
498
|
// computeds, no queued effects, no child queues, no transitions/lanes/
|
|
472
499
|
// optimistic state. Commit and go; anything a commit hook schedules
|
|
473
500
|
// (companion snaps, store folds notifying subs) re-arms `scheduled`
|
|
474
501
|
// below and the outer drain loop takes the full spine next round.
|
|
475
|
-
if (activeTransition === null && dirtyQueue.EE < dirtyQueue.
|
|
476
|
-
this.
|
|
502
|
+
if (activeTransition === null && dirtyQueue.EE < dirtyQueue.Ke && this.ti[0].length === 0 && this.ti[1].length === 0 && this.Xt.length === 0 && canUseSimpleSyncFlush(this)) {
|
|
503
|
+
this.sn = true;
|
|
477
504
|
try {
|
|
478
505
|
// Sweep first: unobserved() pulls swept nodes out of the dirty heap,
|
|
479
506
|
// so a dormant memo dirtied in the same tick is reclaimed instead of
|
|
@@ -481,13 +508,13 @@ class GlobalQueue extends Queue {
|
|
|
481
508
|
sweepDormant();
|
|
482
509
|
commitPendingNodes();
|
|
483
510
|
} finally {
|
|
484
|
-
this.
|
|
511
|
+
this.sn = false;
|
|
485
512
|
}
|
|
486
513
|
clock++;
|
|
487
|
-
scheduled = dirtyQueue.EE >= dirtyQueue.
|
|
514
|
+
scheduled = dirtyQueue.EE >= dirtyQueue.Ke || this.ti[0].length !== 0 || this.ti[1].length !== 0 || this.m.$t.length !== 0;
|
|
488
515
|
return;
|
|
489
516
|
}
|
|
490
|
-
this.
|
|
517
|
+
this.sn = true;
|
|
491
518
|
try {
|
|
492
519
|
if (false) ;
|
|
493
520
|
// Before runHeap for the same reason as the fast drain above; late
|
|
@@ -517,35 +544,35 @@ class GlobalQueue extends Queue {
|
|
|
517
544
|
if (this.m === e) currentBatch = this.m = createBatch();
|
|
518
545
|
// Run lane effects immediately (before stashing) - lanes with no pending async
|
|
519
546
|
if (activeLanes.size) {
|
|
520
|
-
GlobalQueue.
|
|
521
|
-
GlobalQueue.
|
|
547
|
+
GlobalQueue.In(EFFECT_RENDER);
|
|
548
|
+
GlobalQueue.In(EFFECT_USER);
|
|
522
549
|
}
|
|
523
|
-
this.stashQueues(e.
|
|
550
|
+
this.stashQueues(e.ei);
|
|
524
551
|
clock++;
|
|
525
552
|
// A kept ambient batch may hold pending nodes (#2916): stay
|
|
526
553
|
// scheduled so the outer drain loop commits them via the plain
|
|
527
554
|
// flush path instead of leaving them until the next natural flush.
|
|
528
|
-
scheduled = dirtyQueue.EE >= dirtyQueue.
|
|
529
|
-
reassignPendingTransition(e
|
|
555
|
+
scheduled = dirtyQueue.EE >= dirtyQueue.Ke || this.m.$t.length > 0;
|
|
556
|
+
reassignPendingTransition(e.$t);
|
|
530
557
|
activeTransition = null;
|
|
531
558
|
finalizePureQueue(null, true);
|
|
532
559
|
return;
|
|
533
560
|
}
|
|
534
561
|
const t = activeTransition;
|
|
535
562
|
const i = this.m;
|
|
536
|
-
i !== t && i.
|
|
537
|
-
this.restoreQueues(t.
|
|
563
|
+
i !== t && i.$t.push(...t.$t);
|
|
564
|
+
this.restoreQueues(t.ei);
|
|
538
565
|
transitions.delete(t);
|
|
539
566
|
activeTransition = null;
|
|
540
|
-
reassignPendingTransition(i
|
|
567
|
+
reassignPendingTransition(i.$t);
|
|
541
568
|
finalizePureQueue(t);
|
|
542
569
|
if (i === t) {
|
|
543
570
|
// Drop the dead Transition wrapper but keep its (drained) containers
|
|
544
571
|
// as the ambient batch — late registrations during finalization live
|
|
545
572
|
// there and must survive to the next flush.
|
|
546
573
|
const e = createBatch();
|
|
547
|
-
e
|
|
548
|
-
e.
|
|
574
|
+
e.$t = i.$t;
|
|
575
|
+
e.it = i.it;
|
|
549
576
|
e.A = i.A;
|
|
550
577
|
e.En = i.En;
|
|
551
578
|
currentBatch = this.m = e;
|
|
@@ -553,7 +580,7 @@ class GlobalQueue extends Queue {
|
|
|
553
580
|
} else {
|
|
554
581
|
if (canUseSimpleSyncFlush(this)) {
|
|
555
582
|
commitPendingNodes();
|
|
556
|
-
if (dirtyQueue.EE >= dirtyQueue.
|
|
583
|
+
if (dirtyQueue.EE >= dirtyQueue.Ke) {
|
|
557
584
|
runHeap(dirtyQueue, GlobalQueue.Fe);
|
|
558
585
|
commitPendingNodes();
|
|
559
586
|
}
|
|
@@ -563,39 +590,54 @@ class GlobalQueue extends Queue {
|
|
|
563
590
|
}
|
|
564
591
|
}
|
|
565
592
|
clock++;
|
|
566
|
-
// Check if finalization added items to the heap (from optimistic reversion)
|
|
567
|
-
|
|
593
|
+
// Check if finalization added items to the heap (from optimistic reversion).
|
|
594
|
+
// Finalization may also have ENTERED a transaction (a commit hook, boundary
|
|
595
|
+
// sweep or recompute wrote a node it owns): effects computed under it
|
|
596
|
+
// since are its to apply, not this flush's — runEffect leaves them queued
|
|
597
|
+
// and the next pass parks them with it (#3319). Everything computed
|
|
598
|
+
// mainline applies now.
|
|
599
|
+
scheduled = dirtyQueue.EE >= dirtyQueue.Ke || activeTransition !== null;
|
|
568
600
|
// Run lane effects first (for ready lanes), then regular effects
|
|
569
|
-
activeLanes.size && GlobalQueue.
|
|
601
|
+
activeLanes.size && GlobalQueue.In(EFFECT_RENDER);
|
|
570
602
|
this.run(EFFECT_RENDER);
|
|
571
|
-
activeLanes.size && GlobalQueue.
|
|
603
|
+
activeLanes.size && GlobalQueue.In(EFFECT_USER);
|
|
572
604
|
this.run(EFFECT_USER);
|
|
573
605
|
if (false) ;
|
|
574
606
|
if (false && !scheduled && !activeTransition && transitions.size === 0 && activeLanes.size === 0) ;
|
|
575
607
|
if (false) ;
|
|
576
608
|
} finally {
|
|
577
|
-
this.
|
|
609
|
+
this.sn = false;
|
|
578
610
|
}
|
|
579
611
|
}
|
|
580
612
|
notify(e, t, i, n) {
|
|
581
613
|
// Only track async if the boundary is propagating STATUS_PENDING (not caught by boundary)
|
|
582
614
|
if (t & STATUS_PENDING) {
|
|
583
615
|
if (i & STATUS_PENDING) {
|
|
584
|
-
|
|
616
|
+
// Callers pass either nothing or this node's own `_x._error`, so `??`
|
|
617
|
+
// is exact (a null error falls back to the same null).
|
|
618
|
+
const t = n ?? e.o?._;
|
|
585
619
|
// A visibility-only mark notification (the affects() boundary
|
|
586
620
|
// channel) updates display state on its way up but must be invisible
|
|
587
621
|
// to completion accounting BY CONSTRUCTION: it never registers a
|
|
588
622
|
// reporter and never counts toward the loading-boundary diagnostic.
|
|
589
623
|
if (t?.l) return true;
|
|
590
|
-
if (
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
if (
|
|
597
|
-
|
|
598
|
-
|
|
624
|
+
if (t) {
|
|
625
|
+
// A reveal can discover a flight started in an earlier flush. Hold
|
|
626
|
+
// the staged writes with that reader (A15), even if the reader is
|
|
627
|
+
// new. Fresh/reset loading boundaries consume pending before it
|
|
628
|
+
// reaches here. A reader already parked in a transition must not
|
|
629
|
+
// open a second one.
|
|
630
|
+
if (!activeTransition && !e.ge && currentBatch.$t.length) this.initTransition();
|
|
631
|
+
if (activeTransition) {
|
|
632
|
+
const i = t.source;
|
|
633
|
+
let n = activeTransition.Re.get(i);
|
|
634
|
+
if (!n) activeTransition.Re.set(i, n = new Set);
|
|
635
|
+
const r = n.size;
|
|
636
|
+
n.add(e);
|
|
637
|
+
if (n.size !== r) {
|
|
638
|
+
schedule();
|
|
639
|
+
GlobalQueue.si?.(activeTransition);
|
|
640
|
+
}
|
|
599
641
|
}
|
|
600
642
|
}
|
|
601
643
|
}
|
|
@@ -613,19 +655,24 @@ class GlobalQueue extends Queue {
|
|
|
613
655
|
// new transaction identity each pass. Stamps are cleared at commit, so
|
|
614
656
|
// this is a belt for paths that hand over a chased-dead reference
|
|
615
657
|
// (merged chains, async settles racing completion).
|
|
616
|
-
if (e.
|
|
658
|
+
if (e.ft === true || e === activeTransition) return;
|
|
617
659
|
}
|
|
618
|
-
if (!e && activeTransition && activeTransition.
|
|
660
|
+
if (!e && activeTransition && activeTransition._e === clock) return;
|
|
619
661
|
if (!activeTransition) {
|
|
620
662
|
activeTransition = e ?? createBatch();
|
|
621
663
|
} else if (e) {
|
|
622
664
|
const t = activeTransition;
|
|
623
665
|
mergeTransitionState(e, t);
|
|
666
|
+
// Effects the outgoing transaction parked belong to the surviving one
|
|
667
|
+
// now: back onto the live queue, where this flush parks them under
|
|
668
|
+
// `transition` or runs them at its completion. The outgoing stash is
|
|
669
|
+
// never read again — the transaction is dead (#3310).
|
|
670
|
+
this.restoreQueues(t.ei);
|
|
624
671
|
transitions.delete(t);
|
|
625
672
|
activeTransition = e;
|
|
626
673
|
}
|
|
627
674
|
transitions.add(activeTransition);
|
|
628
|
-
activeTransition.
|
|
675
|
+
activeTransition._e = clock;
|
|
629
676
|
const t = this.m;
|
|
630
677
|
if (t !== activeTransition) {
|
|
631
678
|
// Adopt the ambient batch into the transaction, then make the
|
|
@@ -635,28 +682,28 @@ class GlobalQueue extends Queue {
|
|
|
635
682
|
// must not entangle unrelated writes to it; the same rule holds one hop
|
|
636
683
|
// downstream: propagation never queues pended subscribers as pending
|
|
637
684
|
// nodes, see propagateAffectsMark, #2893.
|
|
638
|
-
for (let e = 0; e < t.
|
|
639
|
-
const i = t
|
|
640
|
-
i.
|
|
641
|
-
activeTransition.
|
|
685
|
+
for (let e = 0; e < t.$t.length; e++) {
|
|
686
|
+
const i = t.$t[e];
|
|
687
|
+
i.ge = activeTransition;
|
|
688
|
+
activeTransition.$t.push(i);
|
|
642
689
|
}
|
|
643
|
-
for (let e = 0; e < t.
|
|
644
|
-
const i = t.
|
|
645
|
-
i.
|
|
646
|
-
activeTransition.
|
|
690
|
+
for (let e = 0; e < t.it.length; e++) {
|
|
691
|
+
const i = t.it[e];
|
|
692
|
+
i.ge = activeTransition;
|
|
693
|
+
activeTransition.it.push(i);
|
|
647
694
|
}
|
|
648
695
|
if (t.A.length) activeTransition.A.push(...t.A);
|
|
649
696
|
for (const e of t.En) activeTransition.En.add(e);
|
|
650
697
|
// Gated readers recorded against the ambient batch move with it: their
|
|
651
698
|
// replay-at-commit now happens at the transaction's completion.
|
|
652
|
-
if (t.
|
|
653
|
-
for (const e of t.
|
|
654
|
-
t.
|
|
699
|
+
if (t.lt.size) {
|
|
700
|
+
for (const e of t.lt) activeTransition.lt.add(e);
|
|
701
|
+
t.lt.clear();
|
|
655
702
|
}
|
|
656
703
|
currentBatch = this.m = activeTransition;
|
|
657
704
|
}
|
|
658
705
|
for (const e of activeLanes) {
|
|
659
|
-
if (!e.
|
|
706
|
+
if (!e.ge) e.ge = activeTransition;
|
|
660
707
|
}
|
|
661
708
|
// A transaction's ambient window is one flush. Entering must therefore
|
|
662
709
|
// guarantee a flush: a transaction opened with no writes (an action whose
|
|
@@ -671,7 +718,7 @@ class GlobalQueue extends Queue {
|
|
|
671
718
|
}
|
|
672
719
|
|
|
673
720
|
function queuePendingNode(e) {
|
|
674
|
-
currentBatch.
|
|
721
|
+
currentBatch.$t.push(e);
|
|
675
722
|
}
|
|
676
723
|
|
|
677
724
|
// Sticky: flips true on the first refresh() ever (the only setter of
|
|
@@ -692,10 +739,30 @@ function armReaskClear() {
|
|
|
692
739
|
reaskArmed = true;
|
|
693
740
|
}
|
|
694
741
|
|
|
742
|
+
/** Provenance of the work currently running (A18 supersession, #3331): the
|
|
743
|
+
* invocation sequence of the action whose ambient window this is — set by
|
|
744
|
+
* action() for each slice; the flush that ends the window clears it — or,
|
|
745
|
+
* inside an async landing, the sequence captured when that flight was
|
|
746
|
+
* registered (asyncWrite sets it for the landing's synchronous propagation,
|
|
747
|
+
* so a sync recompute downstream of the landing — an optimistic wrapper over
|
|
748
|
+
* the async source — derives under the flight's provenance, and flights it
|
|
749
|
+
* registers inherit it). 0 is mainline: no action, always the current
|
|
750
|
+
* question. An override stamps this at its write (`_overrideStamp`); an
|
|
751
|
+
* answer whose flight an OLDER action issued is a stale question the user
|
|
752
|
+
* has since changed — it holds silently to commit instead of superseding. A
|
|
753
|
+
* slow source must not leak back in over a newer intent. Transactions merge,
|
|
754
|
+
* so the transition object cannot say WHICH action asked; this can. */ let origin = 0;
|
|
755
|
+
|
|
756
|
+
function setOrigin(e) {
|
|
757
|
+
const t = origin;
|
|
758
|
+
origin = e;
|
|
759
|
+
return t;
|
|
760
|
+
}
|
|
761
|
+
|
|
695
762
|
function insertSubs(e, t = false) {
|
|
696
763
|
// §12d: stamp before walking — setSignal's staged-rewrite fast path skips
|
|
697
764
|
// the next walk for this node while the epoch holds (marking is idempotent).
|
|
698
|
-
e.
|
|
765
|
+
e.At = notifyEpoch;
|
|
699
766
|
// Get source lane: prefer node's own lane over current context
|
|
700
767
|
// This is important for isPending signals which need their own lane to flush immediately
|
|
701
768
|
// Presence bits gate the optional-slot probes (see constants.ts): one
|
|
@@ -703,11 +770,11 @@ function insertSubs(e, t = false) {
|
|
|
703
770
|
// lookups in the hottest notify loop. Bits are sticky — the field read
|
|
704
771
|
// stays authoritative when a bit is set.
|
|
705
772
|
const i = e.T;
|
|
706
|
-
const n = (i & CONFIG_HAS_LANE ? e.o?.
|
|
707
|
-
const r = (i & CONFIG_HAS_SNAPSHOT) !== 0 && e.o?.
|
|
773
|
+
const n = (i & CONFIG_HAS_LANE ? e.o?.Oe : undefined) || currentOptimisticLane;
|
|
774
|
+
const r = (i & CONFIG_HAS_SNAPSHOT) !== 0 && e.o?.ze !== undefined;
|
|
708
775
|
const s = reaskArmed;
|
|
709
|
-
for (let i = e.u; i !== null; i = i.
|
|
710
|
-
const e = i.
|
|
776
|
+
for (let i = e.u; i !== null; i = i.Te) {
|
|
777
|
+
const e = i.Ie;
|
|
711
778
|
// A value-change notification is a new question for the subscriber: any
|
|
712
779
|
// pending re-ask mark (refresh) it carried is superseded.
|
|
713
780
|
if (s) e.ie &= ~REACTIVE_REASK;
|
|
@@ -719,7 +786,7 @@ function insertSubs(e, t = false) {
|
|
|
719
786
|
// pass either re-reads them fresh or trims them), and neither does the
|
|
720
787
|
// tail link: it is the read IN FLIGHT — read() links before it pulls, so
|
|
721
788
|
// this very commit is what that read returns.
|
|
722
|
-
if (e.ie & REACTIVE_RECOMPUTING_DEPS && i.
|
|
789
|
+
if (e.ie & REACTIVE_RECOMPUTING_DEPS && i.yt === e.tt && i !== e.et) e.ie |= REACTIVE_MISSED_WAKE;
|
|
723
790
|
if (r && e.T & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
724
791
|
e.ie |= REACTIVE_SNAPSHOT_STALE;
|
|
725
792
|
continue;
|
|
@@ -730,7 +797,7 @@ function insertSubs(e, t = false) {
|
|
|
730
797
|
} else if (t) {
|
|
731
798
|
e.ie |= REACTIVE_OPTIMISTIC_DIRTY;
|
|
732
799
|
// No source lane means reversion - clear subscriber's lane so effects go to regular queue
|
|
733
|
-
if (e.o) e.o.
|
|
800
|
+
if (e.o) e.o.Oe = undefined;
|
|
734
801
|
}
|
|
735
802
|
enqueueSub(e);
|
|
736
803
|
}
|
|
@@ -739,32 +806,36 @@ function insertSubs(e, t = false) {
|
|
|
739
806
|
function commitPendingNode(e) {
|
|
740
807
|
const t = e;
|
|
741
808
|
if (!t.oe) {
|
|
742
|
-
if (e.
|
|
743
|
-
e.
|
|
744
|
-
e.
|
|
809
|
+
if (e.Ge !== NOT_PENDING) {
|
|
810
|
+
e.me = e.Ge;
|
|
811
|
+
e.Ge = NOT_PENDING;
|
|
745
812
|
}
|
|
746
813
|
if (e.T & CONFIG_HAS_COMPANIONS) GlobalQueue.un(e);
|
|
747
814
|
return;
|
|
748
815
|
}
|
|
749
|
-
if (e.
|
|
750
|
-
e.
|
|
751
|
-
e.
|
|
816
|
+
if (e.Ge !== NOT_PENDING) {
|
|
817
|
+
e.me = e.Ge;
|
|
818
|
+
e.Ge = NOT_PENDING;
|
|
752
819
|
// Set _modified for effects, but not for tracked effects (they handle their own scheduling)
|
|
753
|
-
if (e.
|
|
820
|
+
if (e.Ce && e.Ce !== EFFECT_TRACKED) e.He = true;
|
|
754
821
|
// A quiet re-ask classification preserved through a held landing dies
|
|
755
822
|
// with the value commit — the commit IS the reveal (#3178). Gated on the
|
|
756
823
|
// staged value: status propagation queues pending nodes whose windows
|
|
757
824
|
// are still OPEN (no staged value), and their live classification must
|
|
758
825
|
// survive this sweep.
|
|
759
|
-
if (e.o) e.o.
|
|
826
|
+
if (e.o) e.o.Ue = false;
|
|
760
827
|
}
|
|
761
828
|
// The committed hold is the first observable answer for a loading-window
|
|
762
829
|
// node — the window closes here, not at compute time (#2990). Unconditional
|
|
763
830
|
// store to an always-present computed slot.
|
|
764
|
-
t.
|
|
831
|
+
t.Ae = false;
|
|
765
832
|
t.ie &= ~REACTIVE_MANUAL_WRITE;
|
|
766
833
|
if (!(t.S & STATUS_PENDING)) t.S &= ~STATUS_UNINITIALIZED;
|
|
767
|
-
|
|
834
|
+
// A flight this commit leaves in the air (unobserved, or observed only by
|
|
835
|
+
// a boundary) now has PUBLISHED inputs: its committed value is stale
|
|
836
|
+
// against the frame. read()'s reveal carve-out keys on the mark (#3305).
|
|
837
|
+
else e.T |= CONFIG_INPUTS_PUBLISHED;
|
|
838
|
+
if (t.o != null && (t.o.Xe !== null || t.o.Je !== null)) GlobalQueue.We(t, false, true);
|
|
768
839
|
if (e.T & CONFIG_HAS_COMPANIONS) GlobalQueue.un(e);
|
|
769
840
|
}
|
|
770
841
|
|
|
@@ -779,17 +850,6 @@ function setStoreCommitHook(e) {
|
|
|
779
850
|
storeCommitHook = e;
|
|
780
851
|
}
|
|
781
852
|
|
|
782
|
-
/** Patch-channel release hook (next/patch.ts): transition-stamped patch
|
|
783
|
-
* emissions are released when THEIR batch commits. Transitions never
|
|
784
|
-
* abort: failed actions still commit (only optimistic overrides revert),
|
|
785
|
-
* and merged-away transitions hand their stash to the survivor
|
|
786
|
-
* (mergeTransitionState) — every stash drains exactly once. Injected like
|
|
787
|
-
* storeCommitHook to stay tree-shakeable. */ let patchCommitHook = null;
|
|
788
|
-
|
|
789
|
-
function setPatchCommitHook(e) {
|
|
790
|
-
patchCommitHook = e;
|
|
791
|
-
}
|
|
792
|
-
|
|
793
853
|
/** Held truth committed this finalize, awaiting its post-revert wake (see
|
|
794
854
|
* finalizePureQueue): the commit IS the reveal, but subscribers must not
|
|
795
855
|
* re-derive until the settling transaction's optimistic overrides have
|
|
@@ -798,7 +858,7 @@ function setPatchCommitHook(e) {
|
|
|
798
858
|
* frame no timeline contains. */ const heldRevealed = [];
|
|
799
859
|
|
|
800
860
|
function commitPendingNodes() {
|
|
801
|
-
const e = currentBatch
|
|
861
|
+
const e = currentBatch.$t;
|
|
802
862
|
for (let t = 0; t < e.length; t++) {
|
|
803
863
|
const i = e[t];
|
|
804
864
|
commitPendingNode(i);
|
|
@@ -813,7 +873,7 @@ function commitPendingNodes() {
|
|
|
813
873
|
// that old view and cached it), and commits are otherwise silent
|
|
814
874
|
// (staging already notified), so without a wake they'd hold the old
|
|
815
875
|
// world forever.
|
|
816
|
-
i.
|
|
876
|
+
i.ge = null;
|
|
817
877
|
if (i.T & CONFIG_HELD_TRUTH) {
|
|
818
878
|
i.T &= ~CONFIG_HELD_TRUTH;
|
|
819
879
|
heldRevealed.push(i);
|
|
@@ -821,34 +881,61 @@ function commitPendingNodes() {
|
|
|
821
881
|
}
|
|
822
882
|
e.length = 0;
|
|
823
883
|
storeCommitHook?.();
|
|
824
|
-
patchCommitHook?.(currentBatch);
|
|
825
884
|
}
|
|
826
885
|
|
|
827
886
|
function finalizePureQueue(e = null, t = false) {
|
|
828
887
|
// For incomplete transitions, skip pending resolution and optimistic reversion
|
|
829
888
|
// For completing transitions or no-transition, resolve pending and revert optimistic
|
|
830
|
-
const i =
|
|
831
|
-
|
|
832
|
-
if (
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
889
|
+
const i = currentBatch;
|
|
890
|
+
const n = !t;
|
|
891
|
+
if (n) commitPendingNodes();
|
|
892
|
+
if (!t && globalQueue.Xt.length) checkBoundaryChildren(globalQueue);
|
|
893
|
+
// Contested effects (#3322) re-derive from the world this commit just
|
|
894
|
+
// produced. Ahead of the heap run — not the post-heap gated replay — so
|
|
895
|
+
// the recompute and the effect phase land in this same pass and the value
|
|
896
|
+
// the other transaction wrote into the slot is never published.
|
|
897
|
+
// (No clear: a completed transition is never finalized again.)
|
|
898
|
+
// A transaction whose settle reverts optimism re-derives them post-revert
|
|
899
|
+
// instead (below, with the gated replay): between commitPendingNodes and
|
|
900
|
+
// _resolveOptimistic the truth is committed but the overrides still
|
|
901
|
+
// display, and a re-derive here would compose the two — the #3164 tear,
|
|
902
|
+
// one window later. The slot meanwhile holds the frame that is on screen.
|
|
903
|
+
const r = e?.Et;
|
|
904
|
+
const s = n && (e ?? i).it.length !== 0;
|
|
905
|
+
if (r && !s) for (const e of r) if (!(e.ie & REACTIVE_DISPOSED)) enqueueSub(e);
|
|
906
|
+
const o = dirtyQueue.EE >= dirtyQueue.Ke;
|
|
907
|
+
if (o) runHeap(dirtyQueue, GlobalQueue.Fe);
|
|
908
|
+
if (n) {
|
|
909
|
+
// Boundary checks, commit hooks and recomputes can enter a transaction,
|
|
910
|
+
// which adopts the batch this finalize was settling: nothing batch-derived
|
|
911
|
+
// may be committed or reverted here — the entered transaction owns it now
|
|
912
|
+
// (#3319). A completing transaction's OWN containers are a different
|
|
913
|
+
// matter: when the ambient batch was separate from it (the #2916 shape),
|
|
914
|
+
// adoption never touched them and it must still settle them; when the
|
|
915
|
+
// batch WAS the completing transaction, adoption re-stamped its contents
|
|
916
|
+
// into the entered one and there is nothing left to settle.
|
|
917
|
+
if (currentBatch !== i) {
|
|
918
|
+
if (e === null || e === i) return;
|
|
919
|
+
} else if (o) commitPendingNodes();
|
|
837
920
|
// The settling batch: the completing transaction's, or the ambient one.
|
|
838
|
-
const t = e ??
|
|
921
|
+
const t = e ?? i;
|
|
839
922
|
// Optimistic reversion: a non-empty batch means _optimisticWrite ran,
|
|
840
923
|
// which installed the engine's hooks.
|
|
841
|
-
if (t.
|
|
924
|
+
if (t.it.length) GlobalQueue.fn(t.it);
|
|
925
|
+
if (r && s) {
|
|
926
|
+
for (const e of r) if (!(e.ie & REACTIVE_DISPOSED)) enqueueSub(e);
|
|
927
|
+
schedule();
|
|
928
|
+
}
|
|
842
929
|
// Replay entanglement: subs recorded by the read-time gate get rescheduled
|
|
843
930
|
// so they re-run with the now-committed values visible. The ambient batch
|
|
844
931
|
// replays too — laneReadsCommitted records readers whose committed-view
|
|
845
932
|
// read hid a same-tick plain write that just committed above (#2963).
|
|
846
|
-
if (t.
|
|
847
|
-
for (const e of t.
|
|
933
|
+
if (t.lt.size) {
|
|
934
|
+
for (const e of t.lt) {
|
|
848
935
|
if (e.ie & REACTIVE_DISPOSED) continue;
|
|
849
936
|
enqueueSub(e);
|
|
850
937
|
}
|
|
851
|
-
t.
|
|
938
|
+
t.lt.clear();
|
|
852
939
|
// A completing transition keeps the outer flush loop alive by itself;
|
|
853
940
|
// the ambient batch needs the re-arm or the replay sits in the heap
|
|
854
941
|
// until the next unrelated write.
|
|
@@ -862,13 +949,13 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
862
949
|
// (the earlier sweep above ran while the marks were still live).
|
|
863
950
|
if (t.A.length) {
|
|
864
951
|
GlobalQueue.G(t.A);
|
|
865
|
-
if (globalQueue.
|
|
952
|
+
if (globalQueue.Xt.length) checkBoundaryChildren(globalQueue);
|
|
866
953
|
}
|
|
867
954
|
// A non-empty set means trackOptimisticStore ran, which installed the
|
|
868
955
|
// hook; the hook iterates, clears, and schedules (keeping the loop out of
|
|
869
956
|
// core lets esbuild shake it — rollup already folds the null guard). The
|
|
870
957
|
// completing transition scopes the clear to its own layer keys (#2899).
|
|
871
|
-
if (t.En.size) GlobalQueue.
|
|
958
|
+
if (t.En.size) GlobalQueue.ni(t.En, e);
|
|
872
959
|
// Held-truth reveal wake (#3164), post-revert by construction: this
|
|
873
960
|
// finalize committed confirming truth whose subscribers were masked all
|
|
874
961
|
// hold — some re-derived against the committed view (the staging, or a
|
|
@@ -880,19 +967,19 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
880
967
|
// committed but the settling transaction's overrides still display.
|
|
881
968
|
if (heldRevealed.length !== 0) {
|
|
882
969
|
while (heldRevealed.length) insertSubs(heldRevealed.pop());
|
|
883
|
-
if (dirtyQueue.EE >= dirtyQueue.
|
|
970
|
+
if (dirtyQueue.EE >= dirtyQueue.Ke) {
|
|
884
971
|
runHeap(dirtyQueue, GlobalQueue.Fe);
|
|
885
972
|
commitPendingNodes();
|
|
886
973
|
}
|
|
887
974
|
}
|
|
888
975
|
sweepTransientStoreNodes();
|
|
889
976
|
// Lanes only enter activeLanes through the engine's getOrCreateLane.
|
|
890
|
-
if (activeLanes.size) GlobalQueue.
|
|
977
|
+
if (activeLanes.size) GlobalQueue.dn(e);
|
|
891
978
|
}
|
|
892
979
|
}
|
|
893
980
|
|
|
894
981
|
function checkBoundaryChildren(e) {
|
|
895
|
-
for (const t of e.
|
|
982
|
+
for (const t of e.Xt) {
|
|
896
983
|
t.se?.();
|
|
897
984
|
checkBoundaryChildren(t);
|
|
898
985
|
}
|
|
@@ -916,7 +1003,7 @@ function checkBoundaryChildren(e) {
|
|
|
916
1003
|
|
|
917
1004
|
function reassignPendingTransition(e) {
|
|
918
1005
|
for (let t = 0; t < e.length; t++) {
|
|
919
|
-
e[t].
|
|
1006
|
+
e[t].ge = activeTransition;
|
|
920
1007
|
}
|
|
921
1008
|
}
|
|
922
1009
|
|
|
@@ -930,6 +1017,16 @@ const globalQueue = new GlobalQueue;
|
|
|
930
1017
|
let currentBatch = globalQueue.m;
|
|
931
1018
|
|
|
932
1019
|
function flush(e) {
|
|
1020
|
+
// Inside an action body the drain is incoherent (#3333): the action's
|
|
1021
|
+
// writes are held by its transaction until it settles, so a drain can't
|
|
1022
|
+
// reveal them — and the loop below only exits once `activeTransition` is
|
|
1023
|
+
// null, so it would PARK the transaction mid-slice and every write after it
|
|
1024
|
+
// in the body would land as a plain, committed write. The reporter's
|
|
1025
|
+
// "leading flush()" workaround was exactly that leak. Prod: run `fn` if
|
|
1026
|
+
// given (its writes stay in the transaction) and skip the drain.
|
|
1027
|
+
if (actionStepDepth > 0) {
|
|
1028
|
+
return e ? e() : undefined;
|
|
1029
|
+
}
|
|
933
1030
|
if (e) {
|
|
934
1031
|
syncDepth++;
|
|
935
1032
|
try {
|
|
@@ -944,7 +1041,7 @@ function flush(e) {
|
|
|
944
1041
|
}
|
|
945
1042
|
}
|
|
946
1043
|
}
|
|
947
|
-
if (globalQueue.
|
|
1044
|
+
if (globalQueue.sn) {
|
|
948
1045
|
return;
|
|
949
1046
|
}
|
|
950
1047
|
if (halted) return;
|
|
@@ -953,6 +1050,9 @@ function flush(e) {
|
|
|
953
1050
|
while (scheduled || activeTransition) {
|
|
954
1051
|
globalQueue.flush();
|
|
955
1052
|
}
|
|
1053
|
+
// Provenance ends with the drain: every ambient window (an action's first
|
|
1054
|
+
// slice, a landing's propagation) runs to this flush.
|
|
1055
|
+
origin = 0;
|
|
956
1056
|
}
|
|
957
1057
|
|
|
958
1058
|
function runQueue(e, t) {
|
|
@@ -962,21 +1062,23 @@ function runQueue(e, t) {
|
|
|
962
1062
|
function reporterBlocksSource(e, t) {
|
|
963
1063
|
if (e.ie & (REACTIVE_ZOMBIE | REACTIVE_DISPOSED)) return false;
|
|
964
1064
|
if (e.o?.le?.has(t)) return true;
|
|
965
|
-
for (let i = e.
|
|
966
|
-
let e = i.
|
|
1065
|
+
for (let i = e.fe; i; i = i.ae) {
|
|
1066
|
+
let e = i.Se;
|
|
967
1067
|
while (e) {
|
|
968
|
-
if (e === t || e.
|
|
969
|
-
e = e.o?.
|
|
1068
|
+
if (e === t || e.ce === t) return true;
|
|
1069
|
+
e = e.o?.Gt;
|
|
970
1070
|
}
|
|
971
1071
|
}
|
|
972
1072
|
return !!(e.S & STATUS_PENDING && e.o?._ instanceof NotReadyError && e.o?._.source === t);
|
|
973
1073
|
}
|
|
974
1074
|
|
|
975
1075
|
function transitionComplete(e) {
|
|
976
|
-
if (e.
|
|
977
|
-
if (e.ue.length)
|
|
1076
|
+
if (e.ft) return true;
|
|
1077
|
+
if (e.ue.length) {
|
|
1078
|
+
return false;
|
|
1079
|
+
}
|
|
978
1080
|
let t = true;
|
|
979
|
-
for (const [i, n] of e.
|
|
1081
|
+
for (const [i, n] of e.Re) {
|
|
980
1082
|
let r = false;
|
|
981
1083
|
for (const e of n) {
|
|
982
1084
|
if (reporterBlocksSource(e, i)) {
|
|
@@ -985,7 +1087,7 @@ function transitionComplete(e) {
|
|
|
985
1087
|
}
|
|
986
1088
|
n.delete(e);
|
|
987
1089
|
}
|
|
988
|
-
if (!r) e.
|
|
1090
|
+
if (!r) e.Re.delete(i); else if (i.S & STATUS_PENDING && i.o?._?.source === i) {
|
|
989
1091
|
t = false;
|
|
990
1092
|
break;
|
|
991
1093
|
}
|
|
@@ -993,8 +1095,8 @@ function transitionComplete(e) {
|
|
|
993
1095
|
// Override blockage lives with the engine (absent hook = "no optimistic
|
|
994
1096
|
// blockage"); the hook's loops over _optimisticNodes/_optimisticStores are
|
|
995
1097
|
// no-ops when the transition holds neither, so no pre-check is needed.
|
|
996
|
-
if (t && GlobalQueue.
|
|
997
|
-
t && (e.
|
|
1098
|
+
if (t && GlobalQueue.cn?.(e)) t = false;
|
|
1099
|
+
t && (e.ft = true);
|
|
998
1100
|
return t;
|
|
999
1101
|
}
|
|
1000
1102
|
|
|
@@ -1006,10 +1108,23 @@ function transitionComplete(e) {
|
|
|
1006
1108
|
}
|
|
1007
1109
|
|
|
1008
1110
|
function currentTransition(e) {
|
|
1009
|
-
while (e.
|
|
1111
|
+
while (e.ft && typeof e.ft === "object") e = e.ft;
|
|
1010
1112
|
return e;
|
|
1011
1113
|
}
|
|
1012
1114
|
|
|
1115
|
+
/**
|
|
1116
|
+
* The live transition blocked on `source` — the one whose render reader
|
|
1117
|
+
* observed it pending (INV-3 records the observation in whichever transaction
|
|
1118
|
+
* was active when the reader was notified). The observation is a fact about
|
|
1119
|
+
* the node, so a hold check must not assume it was recorded in the transaction
|
|
1120
|
+
* it happens to hold — lanes merge across transactions (#2912), and a merged
|
|
1121
|
+
* root's transaction knows nothing of the async its members' transactions
|
|
1122
|
+
* observed (#3335). Null when nobody is waiting.
|
|
1123
|
+
*/ function waitingTransition(e) {
|
|
1124
|
+
for (const t of transitions) if (t.Re.has(e)) return t;
|
|
1125
|
+
return null;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1013
1128
|
function runInTransition(e, t) {
|
|
1014
1129
|
const i = activeTransition;
|
|
1015
1130
|
try {
|
|
@@ -1043,4 +1158,4 @@ function runInTransition(e, t) {
|
|
|
1043
1158
|
}
|
|
1044
1159
|
}
|
|
1045
1160
|
|
|
1046
|
-
export { GlobalQueue, Queue, activeAffectsMarks, activeLanes, activeTransition, armReaskClear, assignOrMergeLane, bumpNotifyEpoch, clock, createTransition, currentTransition, dirtyQueue, enforceLoadingBoundary, entangleConfirmingTransitions, finalizePureQueue, findLane, flush, globalQueue, haltReactivity, hasActiveOverride, insertSubs, notifyEpoch,
|
|
1161
|
+
export { GlobalQueue, Queue, actionStepDepth, activeAffectsMarks, activeLanes, activeTransition, armReaskClear, assignOrMergeLane, bumpNotifyEpoch, clock, createTransition, currentTransition, dirtyQueue, enforceLoadingBoundary, entangleConfirmingTransitions, enterActionStep, exitActionStep, finalizePureQueue, findLane, flush, globalQueue, haltReactivity, hasActiveOverride, insertSubs, notifyEpoch, origin, projectionWriteActive, queuePendingNode, reaskArmed, resetErrorHalt, runAsTransitionBatch, runInTransition, schedule, setOrigin, setProjectionWriteActive, setStoreCommitHook, shiftAffectsMarks, storeCommitHook, waitingTransition, zombieQueue };
|