@solidjs/signals 2.0.0-rc.7 → 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 +1229 -6834
- 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} +129 -73
- 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} +295 -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/core/action.js +52 -32
- package/dist/prod/core/async.js +171 -127
- package/dist/prod/core/constants.js +24 -1
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +434 -291
- package/dist/prod/core/effect.js +54 -34
- package/dist/prod/core/external.js +2 -2
- package/dist/prod/core/graph.js +32 -32
- package/dist/prod/core/heap.js +49 -44
- package/dist/prod/core/lanes.js +45 -38
- package/dist/prod/core/optimistic.js +207 -73
- package/dist/prod/core/owner.js +43 -39
- package/dist/prod/core/scheduler.js +319 -190
- package/dist/prod/core/verdict.js +67 -67
- package/dist/prod/index.js +9 -3
- package/dist/prod/map.js +292 -141
- package/dist/prod/signals.js +7 -10
- package/dist/prod/store/next/optimistic.js +140 -125
- package/dist/prod/store/next/projection.js +3 -3
- package/dist/prod/store/next/reconcile.js +87 -84
- package/dist/prod/store/next/store.js +524 -280
- 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 +119 -6
- package/dist/types/core/attribution.d.ts +208 -53
- package/dist/types/core/constants.d.ts +23 -0
- package/dist/types/core/core.d.ts +7 -0
- package/dist/types/core/dev.d.ts +124 -26
- package/dist/types/core/effect.d.ts +8 -1
- package/dist/types/core/index.d.ts +2 -1
- package/dist/types/core/lanes.d.ts +8 -1
- package/dist/types/core/scheduler.d.ts +60 -0
- 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/store/index.d.ts +1 -0
- package/dist/types/store/next/store.d.ts +5 -0
- package/dist/types/store/next/target.d.ts +36 -9
- package/dist/types/store/utils.d.ts +7 -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/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,4 +1,4 @@
|
|
|
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, 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
3
|
import { currentOptimisticLane, slotUnobservedHook } from "./core.js";
|
|
4
4
|
|
|
@@ -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
|
-
if (e.T & CONFIG_SLOT_NODE) slotUnobservedHook(e); else 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,12 +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
|
-
for (const [i, n] of t.
|
|
146
|
-
let t = e.
|
|
147
|
-
if (!t) e.
|
|
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);
|
|
148
160
|
for (const e of n) t.add(e);
|
|
149
161
|
}
|
|
150
|
-
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);
|
|
151
164
|
}
|
|
152
165
|
|
|
153
166
|
/**
|
|
@@ -168,7 +181,7 @@ function mergeTransitionState(e, t) {
|
|
|
168
181
|
* `until()` retain this.
|
|
169
182
|
*/ function entangleConfirmingTransitions(e, t) {
|
|
170
183
|
t = currentTransition(t);
|
|
171
|
-
if (t.
|
|
184
|
+
if (t.ft === true) return;
|
|
172
185
|
// The confirming evidence is a dep whose value is STAGED (pending,
|
|
173
186
|
// uncommitted) at flip evaluation — committed deps are public already and
|
|
174
187
|
// carry nothing to entangle. A staged dep lives in one of two carriers: a
|
|
@@ -181,10 +194,10 @@ function mergeTransitionState(e, t) {
|
|
|
181
194
|
// the awaiting transaction to landings that haven't happened; a merged
|
|
182
195
|
// reporter deadlocked exactly that way).
|
|
183
196
|
let i = false;
|
|
184
|
-
for (let n = e.
|
|
185
|
-
const r = n.
|
|
186
|
-
if (r.
|
|
187
|
-
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;
|
|
188
201
|
const n = e != null ? currentTransition(e) : null;
|
|
189
202
|
// Skip the awaiting transaction's own cargo (t === target: a
|
|
190
203
|
// fold-staged landing or a write the action itself issued — hold and
|
|
@@ -192,10 +205,10 @@ function mergeTransitionState(e, t) {
|
|
|
192
205
|
// (t === null) must leave the batch NOW — it commits at this flush's
|
|
193
206
|
// end, which would reveal the confirmation under the live optimism
|
|
194
207
|
// it just confirmed.
|
|
195
|
-
const s = n === null ? currentBatch
|
|
208
|
+
const s = n === null ? currentBatch.$t : n !== t && n.ft !== true ? n.$t : null;
|
|
196
209
|
if (s !== null) i = stealEntangledCargo(s, t) || i;
|
|
197
210
|
}
|
|
198
|
-
if (n === e.
|
|
211
|
+
if (n === e.et) break;
|
|
199
212
|
}
|
|
200
213
|
// The steal never activates the awaiting transaction: the predicate can
|
|
201
214
|
// flip inside another transaction's finalize heap, and adopting the queue
|
|
@@ -224,20 +237,20 @@ function mergeTransitionState(e, t) {
|
|
|
224
237
|
* committed value — while re-running them would re-derive the OLD world
|
|
225
238
|
* and re-stage it over the held truth. The reveal re-notifies
|
|
226
239
|
* (commitPendingNodes), which is when they re-derive for real. */ function stealEntangledCargo(e, t) {
|
|
227
|
-
if (e === t
|
|
240
|
+
if (e === t.$t || e.length === 0) return false;
|
|
228
241
|
for (let i = 0; i < e.length; i++) {
|
|
229
242
|
const n = e[i];
|
|
230
|
-
n.
|
|
231
|
-
t.
|
|
243
|
+
n.ge = t;
|
|
244
|
+
t.$t.push(n);
|
|
232
245
|
// Override-covered nodes stay silent AND unmasked: the override is the
|
|
233
246
|
// display (A17 — its staging never notified, its revert will), so their
|
|
234
247
|
// subs saw nothing and re-running one would break the silence with a
|
|
235
248
|
// duplicate fire of an unchanged view.
|
|
236
249
|
if (!hasActiveOverride(n)) {
|
|
237
250
|
n.T |= CONFIG_HELD_TRUTH;
|
|
238
|
-
for (let e = n.u; e !== null; e = e.
|
|
239
|
-
const t = e.
|
|
240
|
-
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);
|
|
241
254
|
}
|
|
242
255
|
}
|
|
243
256
|
}
|
|
@@ -253,7 +266,7 @@ function schedule() {
|
|
|
253
266
|
}
|
|
254
267
|
if (scheduled) return;
|
|
255
268
|
scheduled = true;
|
|
256
|
-
if (!syncDepth && !globalQueue.
|
|
269
|
+
if (!syncDepth && !globalQueue.sn && !projectionWriteActive) queueMicrotask(flush);
|
|
257
270
|
}
|
|
258
271
|
|
|
259
272
|
/**
|
|
@@ -264,10 +277,18 @@ function schedule() {
|
|
|
264
277
|
if (halted) return;
|
|
265
278
|
halted = true;
|
|
266
279
|
let t = "[REACTIVITY_HALTED]";
|
|
267
|
-
//
|
|
280
|
+
// Surface the cause here too: callers rethrow it, but a creation-time throw
|
|
268
281
|
// unwinds through ancestor recomputes that convert it to status instead of
|
|
269
282
|
// surfacing it (#2884), so the rethrow alone cannot guarantee visibility.
|
|
270
|
-
|
|
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);
|
|
271
292
|
}
|
|
272
293
|
|
|
273
294
|
// Logs on the first write after a halt so a frozen interaction is traceable.
|
|
@@ -287,30 +308,30 @@ function notifyHalted() {
|
|
|
287
308
|
let queueRunToken = 0;
|
|
288
309
|
|
|
289
310
|
class Queue {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
311
|
+
qe=null;
|
|
312
|
+
ti=[ [], [] ];
|
|
313
|
+
Xt=[];
|
|
314
|
+
ii=0;
|
|
294
315
|
created=clock;
|
|
295
316
|
addChild(e) {
|
|
296
|
-
this.
|
|
297
|
-
e.
|
|
317
|
+
this.Xt.push(e);
|
|
318
|
+
e.qe = this;
|
|
298
319
|
}
|
|
299
320
|
removeChild(e) {
|
|
300
|
-
const t = this.
|
|
321
|
+
const t = this.Xt.indexOf(e);
|
|
301
322
|
if (t >= 0) {
|
|
302
|
-
this.
|
|
303
|
-
e.
|
|
323
|
+
this.Xt.splice(t, 1);
|
|
324
|
+
e.qe = null;
|
|
304
325
|
}
|
|
305
326
|
}
|
|
306
327
|
notify(e, t, i, n) {
|
|
307
|
-
if (this.
|
|
328
|
+
if (this.qe) return this.qe.notify(e, t, i, n);
|
|
308
329
|
return false;
|
|
309
330
|
}
|
|
310
331
|
run(e) {
|
|
311
|
-
if (this.
|
|
312
|
-
const t = this.
|
|
313
|
-
this.
|
|
332
|
+
if (this.ti[e - 1].length) {
|
|
333
|
+
const t = this.ti[e - 1];
|
|
334
|
+
this.ti[e - 1] = [];
|
|
314
335
|
runQueue(t, e);
|
|
315
336
|
}
|
|
316
337
|
// Effects run here can dispose owners, and disposal removes queues from
|
|
@@ -320,12 +341,12 @@ class Queue {
|
|
|
320
341
|
// can be recovered by rescanning from the front and every child still runs
|
|
321
342
|
// exactly once. Children appended mid-pass carry a stale stamp and run,
|
|
322
343
|
// matching the previous live-array behaviour.
|
|
323
|
-
const t = this.
|
|
344
|
+
const t = this.Xt;
|
|
324
345
|
const i = ++queueRunToken;
|
|
325
346
|
for (let n = 0; n < t.length; ) {
|
|
326
347
|
const r = t[n];
|
|
327
|
-
if (r.
|
|
328
|
-
r.
|
|
348
|
+
if (r.ii !== i) {
|
|
349
|
+
r.ii = i;
|
|
329
350
|
r.run?.(e);
|
|
330
351
|
if (t[n] !== r) {
|
|
331
352
|
n = 0;
|
|
@@ -342,48 +363,48 @@ class Queue {
|
|
|
342
363
|
const i = findLane(currentOptimisticLane);
|
|
343
364
|
i.tn[e - 1].push(t);
|
|
344
365
|
} else {
|
|
345
|
-
this.
|
|
366
|
+
this.ti[e - 1].push(t);
|
|
346
367
|
}
|
|
347
368
|
}
|
|
348
369
|
schedule();
|
|
349
370
|
}
|
|
350
371
|
stashQueues(e) {
|
|
351
|
-
e.
|
|
352
|
-
e.
|
|
353
|
-
this.
|
|
354
|
-
for (let t = 0; t < this.
|
|
355
|
-
let i = this.
|
|
356
|
-
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];
|
|
357
378
|
if (!n) {
|
|
358
379
|
n = {
|
|
359
|
-
|
|
360
|
-
|
|
380
|
+
ti: [ [], [] ],
|
|
381
|
+
Xt: []
|
|
361
382
|
};
|
|
362
|
-
e.
|
|
383
|
+
e.Xt[t] = n;
|
|
363
384
|
}
|
|
364
385
|
i.stashQueues(n);
|
|
365
386
|
}
|
|
366
387
|
}
|
|
367
388
|
restoreQueues(e) {
|
|
368
|
-
this.
|
|
369
|
-
this.
|
|
370
|
-
for (let t = 0; t < e.
|
|
371
|
-
const i = e.
|
|
372
|
-
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];
|
|
373
394
|
if (n) n.restoreQueues(i);
|
|
374
395
|
}
|
|
375
396
|
}
|
|
376
397
|
}
|
|
377
398
|
|
|
378
399
|
class GlobalQueue extends Queue {
|
|
379
|
-
|
|
400
|
+
sn=false;
|
|
380
401
|
// The current transaction-shaped batch: a plain ambient batch while no
|
|
381
402
|
// transition is active, the active transition itself after initTransition.
|
|
382
403
|
m=createBatch();
|
|
383
404
|
static Fe;
|
|
384
|
-
static
|
|
385
|
-
static
|
|
386
|
-
static
|
|
405
|
+
static We;
|
|
406
|
+
static ct;
|
|
407
|
+
static ni=null;
|
|
387
408
|
// Store-side hook: drops a keyless affects() mark's identity scope when the
|
|
388
409
|
// carrier node's last registration releases (wired by store.ts, mirroring
|
|
389
410
|
// _clearOptimisticStore).
|
|
@@ -397,27 +418,27 @@ class GlobalQueue extends Queue {
|
|
|
397
418
|
static N=null;
|
|
398
419
|
// External-source bridge (wired by enableExternalSource(); null while no
|
|
399
420
|
// config is active — including after _resetExternalSourceConfig()).
|
|
400
|
-
static
|
|
401
|
-
static
|
|
421
|
+
static kt=null;
|
|
422
|
+
static Lt=null;
|
|
402
423
|
// Verdict-layer hooks (wired by verdict.ts when isPending()/latest() are
|
|
403
424
|
// imported; null in apps that never use them). Call sites either guard for
|
|
404
425
|
// null or sit behind state only the verdict layer can create (`!` is safe
|
|
405
426
|
// there: `_pendingSignal`/`_latestValueComputed` are only ever assigned by
|
|
406
427
|
// verdict.ts, and `pendingCheckActive`/`latestReadActive` only flip inside
|
|
407
428
|
// isPending()/latest()).
|
|
408
|
-
static
|
|
409
|
-
static
|
|
410
|
-
static
|
|
429
|
+
static pe=null;
|
|
430
|
+
static Ne=null;
|
|
431
|
+
static ke=null;
|
|
411
432
|
static un=null;
|
|
412
|
-
static
|
|
413
|
-
static
|
|
414
|
-
static
|
|
415
|
-
static
|
|
433
|
+
static wt=null;
|
|
434
|
+
static Wt=null;
|
|
435
|
+
static jt=null;
|
|
436
|
+
static st=null;
|
|
416
437
|
static k=null;
|
|
417
|
-
static
|
|
438
|
+
static ri=null;
|
|
418
439
|
// Re-asks probes whose verdict was provisionally suppressed by a fresh read
|
|
419
440
|
// of a held value, once the transaction gains an async blocker (#3028).
|
|
420
|
-
static
|
|
441
|
+
static si=null;
|
|
421
442
|
// Optimistic-engine hooks (wired by core/optimistic.ts via
|
|
422
443
|
// installOptimisticEngine(), called from verdict.ts / createOptimistic /
|
|
423
444
|
// createOptimisticStore — every module that can create optimistic state).
|
|
@@ -425,36 +446,61 @@ class GlobalQueue extends Queue {
|
|
|
425
446
|
// `_overrideValue` slot, a lane in `activeLanes`, an `_optimisticNodes`
|
|
426
447
|
// entry, or a non-null `currentOptimisticLane`, so `!` invocations are safe
|
|
427
448
|
// once the gate holds.
|
|
428
|
-
static
|
|
429
|
-
static
|
|
449
|
+
static Bt=null;
|
|
450
|
+
static fn=null;
|
|
451
|
+
static cn=null;
|
|
430
452
|
static dn=null;
|
|
431
453
|
static In=null;
|
|
432
|
-
static Nn=null;
|
|
433
454
|
/** Patch-channel optimistic drain (next/patch.ts): optimistic emissions
|
|
434
455
|
* apply at lane-effect timing — visible in flight, unlike the regular
|
|
435
456
|
* effect queues an action stashes. Injected; null when unused. */
|
|
436
457
|
static ln=null;
|
|
437
|
-
static
|
|
438
|
-
static
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
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;
|
|
442
468
|
/** Authoritative-view reader wakeup: installed by until() and refresh() before
|
|
443
469
|
* their first read. Call sites are gated by CONFIG_AUTHORITATIVE_OBSERVED, which
|
|
444
470
|
* only such a reader's carve-out read can set, so `!` invocations are safe once
|
|
445
471
|
* the gate holds (#3303). */
|
|
446
|
-
static
|
|
447
|
-
static
|
|
448
|
-
|
|
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;
|
|
449
495
|
flush() {
|
|
450
|
-
if (this.
|
|
496
|
+
if (this.sn) return;
|
|
451
497
|
// Fast drain: nothing in flight but plain pending commits — no dirty
|
|
452
498
|
// computeds, no queued effects, no child queues, no transitions/lanes/
|
|
453
499
|
// optimistic state. Commit and go; anything a commit hook schedules
|
|
454
500
|
// (companion snaps, store folds notifying subs) re-arms `scheduled`
|
|
455
501
|
// below and the outer drain loop takes the full spine next round.
|
|
456
|
-
if (activeTransition === null && dirtyQueue.EE < dirtyQueue.
|
|
457
|
-
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;
|
|
458
504
|
try {
|
|
459
505
|
// Sweep first: unobserved() pulls swept nodes out of the dirty heap,
|
|
460
506
|
// so a dormant memo dirtied in the same tick is reclaimed instead of
|
|
@@ -462,13 +508,13 @@ class GlobalQueue extends Queue {
|
|
|
462
508
|
sweepDormant();
|
|
463
509
|
commitPendingNodes();
|
|
464
510
|
} finally {
|
|
465
|
-
this.
|
|
511
|
+
this.sn = false;
|
|
466
512
|
}
|
|
467
513
|
clock++;
|
|
468
|
-
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;
|
|
469
515
|
return;
|
|
470
516
|
}
|
|
471
|
-
this.
|
|
517
|
+
this.sn = true;
|
|
472
518
|
try {
|
|
473
519
|
if (false) ;
|
|
474
520
|
// Before runHeap for the same reason as the fast drain above; late
|
|
@@ -498,35 +544,35 @@ class GlobalQueue extends Queue {
|
|
|
498
544
|
if (this.m === e) currentBatch = this.m = createBatch();
|
|
499
545
|
// Run lane effects immediately (before stashing) - lanes with no pending async
|
|
500
546
|
if (activeLanes.size) {
|
|
501
|
-
GlobalQueue.
|
|
502
|
-
GlobalQueue.
|
|
547
|
+
GlobalQueue.In(EFFECT_RENDER);
|
|
548
|
+
GlobalQueue.In(EFFECT_USER);
|
|
503
549
|
}
|
|
504
|
-
this.stashQueues(e.
|
|
550
|
+
this.stashQueues(e.ei);
|
|
505
551
|
clock++;
|
|
506
552
|
// A kept ambient batch may hold pending nodes (#2916): stay
|
|
507
553
|
// scheduled so the outer drain loop commits them via the plain
|
|
508
554
|
// flush path instead of leaving them until the next natural flush.
|
|
509
|
-
scheduled = dirtyQueue.EE >= dirtyQueue.
|
|
510
|
-
reassignPendingTransition(e
|
|
555
|
+
scheduled = dirtyQueue.EE >= dirtyQueue.Ke || this.m.$t.length > 0;
|
|
556
|
+
reassignPendingTransition(e.$t);
|
|
511
557
|
activeTransition = null;
|
|
512
558
|
finalizePureQueue(null, true);
|
|
513
559
|
return;
|
|
514
560
|
}
|
|
515
561
|
const t = activeTransition;
|
|
516
562
|
const i = this.m;
|
|
517
|
-
i !== t && i.
|
|
518
|
-
this.restoreQueues(t.
|
|
563
|
+
i !== t && i.$t.push(...t.$t);
|
|
564
|
+
this.restoreQueues(t.ei);
|
|
519
565
|
transitions.delete(t);
|
|
520
566
|
activeTransition = null;
|
|
521
|
-
reassignPendingTransition(i
|
|
567
|
+
reassignPendingTransition(i.$t);
|
|
522
568
|
finalizePureQueue(t);
|
|
523
569
|
if (i === t) {
|
|
524
570
|
// Drop the dead Transition wrapper but keep its (drained) containers
|
|
525
571
|
// as the ambient batch — late registrations during finalization live
|
|
526
572
|
// there and must survive to the next flush.
|
|
527
573
|
const e = createBatch();
|
|
528
|
-
e
|
|
529
|
-
e.
|
|
574
|
+
e.$t = i.$t;
|
|
575
|
+
e.it = i.it;
|
|
530
576
|
e.A = i.A;
|
|
531
577
|
e.En = i.En;
|
|
532
578
|
currentBatch = this.m = e;
|
|
@@ -534,7 +580,7 @@ class GlobalQueue extends Queue {
|
|
|
534
580
|
} else {
|
|
535
581
|
if (canUseSimpleSyncFlush(this)) {
|
|
536
582
|
commitPendingNodes();
|
|
537
|
-
if (dirtyQueue.EE >= dirtyQueue.
|
|
583
|
+
if (dirtyQueue.EE >= dirtyQueue.Ke) {
|
|
538
584
|
runHeap(dirtyQueue, GlobalQueue.Fe);
|
|
539
585
|
commitPendingNodes();
|
|
540
586
|
}
|
|
@@ -544,18 +590,23 @@ class GlobalQueue extends Queue {
|
|
|
544
590
|
}
|
|
545
591
|
}
|
|
546
592
|
clock++;
|
|
547
|
-
// Check if finalization added items to the heap (from optimistic reversion)
|
|
548
|
-
|
|
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;
|
|
549
600
|
// Run lane effects first (for ready lanes), then regular effects
|
|
550
|
-
activeLanes.size && GlobalQueue.
|
|
601
|
+
activeLanes.size && GlobalQueue.In(EFFECT_RENDER);
|
|
551
602
|
this.run(EFFECT_RENDER);
|
|
552
|
-
activeLanes.size && GlobalQueue.
|
|
603
|
+
activeLanes.size && GlobalQueue.In(EFFECT_USER);
|
|
553
604
|
this.run(EFFECT_USER);
|
|
554
605
|
if (false) ;
|
|
555
606
|
if (false && !scheduled && !activeTransition && transitions.size === 0 && activeLanes.size === 0) ;
|
|
556
607
|
if (false) ;
|
|
557
608
|
} finally {
|
|
558
|
-
this.
|
|
609
|
+
this.sn = false;
|
|
559
610
|
}
|
|
560
611
|
}
|
|
561
612
|
notify(e, t, i, n) {
|
|
@@ -576,16 +627,16 @@ class GlobalQueue extends Queue {
|
|
|
576
627
|
// new. Fresh/reset loading boundaries consume pending before it
|
|
577
628
|
// reaches here. A reader already parked in a transition must not
|
|
578
629
|
// open a second one.
|
|
579
|
-
if (!activeTransition && !e.
|
|
630
|
+
if (!activeTransition && !e.ge && currentBatch.$t.length) this.initTransition();
|
|
580
631
|
if (activeTransition) {
|
|
581
632
|
const i = t.source;
|
|
582
|
-
let n = activeTransition.
|
|
583
|
-
if (!n) activeTransition.
|
|
633
|
+
let n = activeTransition.Re.get(i);
|
|
634
|
+
if (!n) activeTransition.Re.set(i, n = new Set);
|
|
584
635
|
const r = n.size;
|
|
585
636
|
n.add(e);
|
|
586
637
|
if (n.size !== r) {
|
|
587
638
|
schedule();
|
|
588
|
-
GlobalQueue.
|
|
639
|
+
GlobalQueue.si?.(activeTransition);
|
|
589
640
|
}
|
|
590
641
|
}
|
|
591
642
|
}
|
|
@@ -604,9 +655,9 @@ class GlobalQueue extends Queue {
|
|
|
604
655
|
// new transaction identity each pass. Stamps are cleared at commit, so
|
|
605
656
|
// this is a belt for paths that hand over a chased-dead reference
|
|
606
657
|
// (merged chains, async settles racing completion).
|
|
607
|
-
if (e.
|
|
658
|
+
if (e.ft === true || e === activeTransition) return;
|
|
608
659
|
}
|
|
609
|
-
if (!e && activeTransition && activeTransition.
|
|
660
|
+
if (!e && activeTransition && activeTransition._e === clock) return;
|
|
610
661
|
if (!activeTransition) {
|
|
611
662
|
activeTransition = e ?? createBatch();
|
|
612
663
|
} else if (e) {
|
|
@@ -616,12 +667,12 @@ class GlobalQueue extends Queue {
|
|
|
616
667
|
// now: back onto the live queue, where this flush parks them under
|
|
617
668
|
// `transition` or runs them at its completion. The outgoing stash is
|
|
618
669
|
// never read again — the transaction is dead (#3310).
|
|
619
|
-
this.restoreQueues(t.
|
|
670
|
+
this.restoreQueues(t.ei);
|
|
620
671
|
transitions.delete(t);
|
|
621
672
|
activeTransition = e;
|
|
622
673
|
}
|
|
623
674
|
transitions.add(activeTransition);
|
|
624
|
-
activeTransition.
|
|
675
|
+
activeTransition._e = clock;
|
|
625
676
|
const t = this.m;
|
|
626
677
|
if (t !== activeTransition) {
|
|
627
678
|
// Adopt the ambient batch into the transaction, then make the
|
|
@@ -631,28 +682,28 @@ class GlobalQueue extends Queue {
|
|
|
631
682
|
// must not entangle unrelated writes to it; the same rule holds one hop
|
|
632
683
|
// downstream: propagation never queues pended subscribers as pending
|
|
633
684
|
// nodes, see propagateAffectsMark, #2893.
|
|
634
|
-
for (let e = 0; e < t.
|
|
635
|
-
const i = t
|
|
636
|
-
i.
|
|
637
|
-
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);
|
|
638
689
|
}
|
|
639
|
-
for (let e = 0; e < t.
|
|
640
|
-
const i = t.
|
|
641
|
-
i.
|
|
642
|
-
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);
|
|
643
694
|
}
|
|
644
695
|
if (t.A.length) activeTransition.A.push(...t.A);
|
|
645
696
|
for (const e of t.En) activeTransition.En.add(e);
|
|
646
697
|
// Gated readers recorded against the ambient batch move with it: their
|
|
647
698
|
// replay-at-commit now happens at the transaction's completion.
|
|
648
|
-
if (t.
|
|
649
|
-
for (const e of t.
|
|
650
|
-
t.
|
|
699
|
+
if (t.lt.size) {
|
|
700
|
+
for (const e of t.lt) activeTransition.lt.add(e);
|
|
701
|
+
t.lt.clear();
|
|
651
702
|
}
|
|
652
703
|
currentBatch = this.m = activeTransition;
|
|
653
704
|
}
|
|
654
705
|
for (const e of activeLanes) {
|
|
655
|
-
if (!e.
|
|
706
|
+
if (!e.ge) e.ge = activeTransition;
|
|
656
707
|
}
|
|
657
708
|
// A transaction's ambient window is one flush. Entering must therefore
|
|
658
709
|
// guarantee a flush: a transaction opened with no writes (an action whose
|
|
@@ -667,7 +718,7 @@ class GlobalQueue extends Queue {
|
|
|
667
718
|
}
|
|
668
719
|
|
|
669
720
|
function queuePendingNode(e) {
|
|
670
|
-
currentBatch.
|
|
721
|
+
currentBatch.$t.push(e);
|
|
671
722
|
}
|
|
672
723
|
|
|
673
724
|
// Sticky: flips true on the first refresh() ever (the only setter of
|
|
@@ -688,10 +739,30 @@ function armReaskClear() {
|
|
|
688
739
|
reaskArmed = true;
|
|
689
740
|
}
|
|
690
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
|
+
|
|
691
762
|
function insertSubs(e, t = false) {
|
|
692
763
|
// §12d: stamp before walking — setSignal's staged-rewrite fast path skips
|
|
693
764
|
// the next walk for this node while the epoch holds (marking is idempotent).
|
|
694
|
-
e.
|
|
765
|
+
e.At = notifyEpoch;
|
|
695
766
|
// Get source lane: prefer node's own lane over current context
|
|
696
767
|
// This is important for isPending signals which need their own lane to flush immediately
|
|
697
768
|
// Presence bits gate the optional-slot probes (see constants.ts): one
|
|
@@ -699,11 +770,11 @@ function insertSubs(e, t = false) {
|
|
|
699
770
|
// lookups in the hottest notify loop. Bits are sticky — the field read
|
|
700
771
|
// stays authoritative when a bit is set.
|
|
701
772
|
const i = e.T;
|
|
702
|
-
const n = (i & CONFIG_HAS_LANE ? e.o
|
|
703
|
-
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;
|
|
704
775
|
const s = reaskArmed;
|
|
705
|
-
for (let i = e.u; i !== null; i = i.
|
|
706
|
-
const e = i.
|
|
776
|
+
for (let i = e.u; i !== null; i = i.Te) {
|
|
777
|
+
const e = i.Ie;
|
|
707
778
|
// A value-change notification is a new question for the subscriber: any
|
|
708
779
|
// pending re-ask mark (refresh) it carried is superseded.
|
|
709
780
|
if (s) e.ie &= ~REACTIVE_REASK;
|
|
@@ -715,7 +786,7 @@ function insertSubs(e, t = false) {
|
|
|
715
786
|
// pass either re-reads them fresh or trims them), and neither does the
|
|
716
787
|
// tail link: it is the read IN FLIGHT — read() links before it pulls, so
|
|
717
788
|
// this very commit is what that read returns.
|
|
718
|
-
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;
|
|
719
790
|
if (r && e.T & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
720
791
|
e.ie |= REACTIVE_SNAPSHOT_STALE;
|
|
721
792
|
continue;
|
|
@@ -726,7 +797,7 @@ function insertSubs(e, t = false) {
|
|
|
726
797
|
} else if (t) {
|
|
727
798
|
e.ie |= REACTIVE_OPTIMISTIC_DIRTY;
|
|
728
799
|
// No source lane means reversion - clear subscriber's lane so effects go to regular queue
|
|
729
|
-
if (e.o) e.o
|
|
800
|
+
if (e.o) e.o.Oe = undefined;
|
|
730
801
|
}
|
|
731
802
|
enqueueSub(e);
|
|
732
803
|
}
|
|
@@ -735,32 +806,36 @@ function insertSubs(e, t = false) {
|
|
|
735
806
|
function commitPendingNode(e) {
|
|
736
807
|
const t = e;
|
|
737
808
|
if (!t.oe) {
|
|
738
|
-
if (e.
|
|
739
|
-
e.
|
|
740
|
-
e.
|
|
809
|
+
if (e.Ge !== NOT_PENDING) {
|
|
810
|
+
e.me = e.Ge;
|
|
811
|
+
e.Ge = NOT_PENDING;
|
|
741
812
|
}
|
|
742
813
|
if (e.T & CONFIG_HAS_COMPANIONS) GlobalQueue.un(e);
|
|
743
814
|
return;
|
|
744
815
|
}
|
|
745
|
-
if (e.
|
|
746
|
-
e.
|
|
747
|
-
e.
|
|
816
|
+
if (e.Ge !== NOT_PENDING) {
|
|
817
|
+
e.me = e.Ge;
|
|
818
|
+
e.Ge = NOT_PENDING;
|
|
748
819
|
// Set _modified for effects, but not for tracked effects (they handle their own scheduling)
|
|
749
|
-
if (e.
|
|
820
|
+
if (e.Ce && e.Ce !== EFFECT_TRACKED) e.He = true;
|
|
750
821
|
// A quiet re-ask classification preserved through a held landing dies
|
|
751
822
|
// with the value commit — the commit IS the reveal (#3178). Gated on the
|
|
752
823
|
// staged value: status propagation queues pending nodes whose windows
|
|
753
824
|
// are still OPEN (no staged value), and their live classification must
|
|
754
825
|
// survive this sweep.
|
|
755
|
-
if (e.o) e.o.
|
|
826
|
+
if (e.o) e.o.Ue = false;
|
|
756
827
|
}
|
|
757
828
|
// The committed hold is the first observable answer for a loading-window
|
|
758
829
|
// node — the window closes here, not at compute time (#2990). Unconditional
|
|
759
830
|
// store to an always-present computed slot.
|
|
760
|
-
t.
|
|
831
|
+
t.Ae = false;
|
|
761
832
|
t.ie &= ~REACTIVE_MANUAL_WRITE;
|
|
762
833
|
if (!(t.S & STATUS_PENDING)) t.S &= ~STATUS_UNINITIALIZED;
|
|
763
|
-
|
|
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);
|
|
764
839
|
if (e.T & CONFIG_HAS_COMPANIONS) GlobalQueue.un(e);
|
|
765
840
|
}
|
|
766
841
|
|
|
@@ -783,7 +858,7 @@ function setStoreCommitHook(e) {
|
|
|
783
858
|
* frame no timeline contains. */ const heldRevealed = [];
|
|
784
859
|
|
|
785
860
|
function commitPendingNodes() {
|
|
786
|
-
const e = currentBatch
|
|
861
|
+
const e = currentBatch.$t;
|
|
787
862
|
for (let t = 0; t < e.length; t++) {
|
|
788
863
|
const i = e[t];
|
|
789
864
|
commitPendingNode(i);
|
|
@@ -798,7 +873,7 @@ function commitPendingNodes() {
|
|
|
798
873
|
// that old view and cached it), and commits are otherwise silent
|
|
799
874
|
// (staging already notified), so without a wake they'd hold the old
|
|
800
875
|
// world forever.
|
|
801
|
-
i.
|
|
876
|
+
i.ge = null;
|
|
802
877
|
if (i.T & CONFIG_HELD_TRUTH) {
|
|
803
878
|
i.T &= ~CONFIG_HELD_TRUTH;
|
|
804
879
|
heldRevealed.push(i);
|
|
@@ -811,28 +886,56 @@ function commitPendingNodes() {
|
|
|
811
886
|
function finalizePureQueue(e = null, t = false) {
|
|
812
887
|
// For incomplete transitions, skip pending resolution and optimistic reversion
|
|
813
888
|
// For completing transitions or no-transition, resolve pending and revert optimistic
|
|
814
|
-
const i =
|
|
815
|
-
|
|
816
|
-
if (
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
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();
|
|
821
920
|
// The settling batch: the completing transaction's, or the ambient one.
|
|
822
|
-
const t = e ??
|
|
921
|
+
const t = e ?? i;
|
|
823
922
|
// Optimistic reversion: a non-empty batch means _optimisticWrite ran,
|
|
824
923
|
// which installed the engine's hooks.
|
|
825
|
-
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
|
+
}
|
|
826
929
|
// Replay entanglement: subs recorded by the read-time gate get rescheduled
|
|
827
930
|
// so they re-run with the now-committed values visible. The ambient batch
|
|
828
931
|
// replays too — laneReadsCommitted records readers whose committed-view
|
|
829
932
|
// read hid a same-tick plain write that just committed above (#2963).
|
|
830
|
-
if (t.
|
|
831
|
-
for (const e of t.
|
|
933
|
+
if (t.lt.size) {
|
|
934
|
+
for (const e of t.lt) {
|
|
832
935
|
if (e.ie & REACTIVE_DISPOSED) continue;
|
|
833
936
|
enqueueSub(e);
|
|
834
937
|
}
|
|
835
|
-
t.
|
|
938
|
+
t.lt.clear();
|
|
836
939
|
// A completing transition keeps the outer flush loop alive by itself;
|
|
837
940
|
// the ambient batch needs the re-arm or the replay sits in the heap
|
|
838
941
|
// until the next unrelated write.
|
|
@@ -846,13 +949,13 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
846
949
|
// (the earlier sweep above ran while the marks were still live).
|
|
847
950
|
if (t.A.length) {
|
|
848
951
|
GlobalQueue.G(t.A);
|
|
849
|
-
if (globalQueue.
|
|
952
|
+
if (globalQueue.Xt.length) checkBoundaryChildren(globalQueue);
|
|
850
953
|
}
|
|
851
954
|
// A non-empty set means trackOptimisticStore ran, which installed the
|
|
852
955
|
// hook; the hook iterates, clears, and schedules (keeping the loop out of
|
|
853
956
|
// core lets esbuild shake it — rollup already folds the null guard). The
|
|
854
957
|
// completing transition scopes the clear to its own layer keys (#2899).
|
|
855
|
-
if (t.En.size) GlobalQueue.
|
|
958
|
+
if (t.En.size) GlobalQueue.ni(t.En, e);
|
|
856
959
|
// Held-truth reveal wake (#3164), post-revert by construction: this
|
|
857
960
|
// finalize committed confirming truth whose subscribers were masked all
|
|
858
961
|
// hold — some re-derived against the committed view (the staging, or a
|
|
@@ -864,19 +967,19 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
864
967
|
// committed but the settling transaction's overrides still display.
|
|
865
968
|
if (heldRevealed.length !== 0) {
|
|
866
969
|
while (heldRevealed.length) insertSubs(heldRevealed.pop());
|
|
867
|
-
if (dirtyQueue.EE >= dirtyQueue.
|
|
970
|
+
if (dirtyQueue.EE >= dirtyQueue.Ke) {
|
|
868
971
|
runHeap(dirtyQueue, GlobalQueue.Fe);
|
|
869
972
|
commitPendingNodes();
|
|
870
973
|
}
|
|
871
974
|
}
|
|
872
975
|
sweepTransientStoreNodes();
|
|
873
976
|
// Lanes only enter activeLanes through the engine's getOrCreateLane.
|
|
874
|
-
if (activeLanes.size) GlobalQueue.
|
|
977
|
+
if (activeLanes.size) GlobalQueue.dn(e);
|
|
875
978
|
}
|
|
876
979
|
}
|
|
877
980
|
|
|
878
981
|
function checkBoundaryChildren(e) {
|
|
879
|
-
for (const t of e.
|
|
982
|
+
for (const t of e.Xt) {
|
|
880
983
|
t.se?.();
|
|
881
984
|
checkBoundaryChildren(t);
|
|
882
985
|
}
|
|
@@ -900,7 +1003,7 @@ function checkBoundaryChildren(e) {
|
|
|
900
1003
|
|
|
901
1004
|
function reassignPendingTransition(e) {
|
|
902
1005
|
for (let t = 0; t < e.length; t++) {
|
|
903
|
-
e[t].
|
|
1006
|
+
e[t].ge = activeTransition;
|
|
904
1007
|
}
|
|
905
1008
|
}
|
|
906
1009
|
|
|
@@ -914,6 +1017,16 @@ const globalQueue = new GlobalQueue;
|
|
|
914
1017
|
let currentBatch = globalQueue.m;
|
|
915
1018
|
|
|
916
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
|
+
}
|
|
917
1030
|
if (e) {
|
|
918
1031
|
syncDepth++;
|
|
919
1032
|
try {
|
|
@@ -928,7 +1041,7 @@ function flush(e) {
|
|
|
928
1041
|
}
|
|
929
1042
|
}
|
|
930
1043
|
}
|
|
931
|
-
if (globalQueue.
|
|
1044
|
+
if (globalQueue.sn) {
|
|
932
1045
|
return;
|
|
933
1046
|
}
|
|
934
1047
|
if (halted) return;
|
|
@@ -937,6 +1050,9 @@ function flush(e) {
|
|
|
937
1050
|
while (scheduled || activeTransition) {
|
|
938
1051
|
globalQueue.flush();
|
|
939
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;
|
|
940
1056
|
}
|
|
941
1057
|
|
|
942
1058
|
function runQueue(e, t) {
|
|
@@ -946,23 +1062,23 @@ function runQueue(e, t) {
|
|
|
946
1062
|
function reporterBlocksSource(e, t) {
|
|
947
1063
|
if (e.ie & (REACTIVE_ZOMBIE | REACTIVE_DISPOSED)) return false;
|
|
948
1064
|
if (e.o?.le?.has(t)) return true;
|
|
949
|
-
for (let i = e.
|
|
950
|
-
let e = i.
|
|
1065
|
+
for (let i = e.fe; i; i = i.ae) {
|
|
1066
|
+
let e = i.Se;
|
|
951
1067
|
while (e) {
|
|
952
|
-
if (e === t || e.
|
|
953
|
-
e = e.o?.
|
|
1068
|
+
if (e === t || e.ce === t) return true;
|
|
1069
|
+
e = e.o?.Gt;
|
|
954
1070
|
}
|
|
955
1071
|
}
|
|
956
1072
|
return !!(e.S & STATUS_PENDING && e.o?._ instanceof NotReadyError && e.o?._.source === t);
|
|
957
1073
|
}
|
|
958
1074
|
|
|
959
1075
|
function transitionComplete(e) {
|
|
960
|
-
if (e.
|
|
1076
|
+
if (e.ft) return true;
|
|
961
1077
|
if (e.ue.length) {
|
|
962
1078
|
return false;
|
|
963
1079
|
}
|
|
964
1080
|
let t = true;
|
|
965
|
-
for (const [i, n] of e.
|
|
1081
|
+
for (const [i, n] of e.Re) {
|
|
966
1082
|
let r = false;
|
|
967
1083
|
for (const e of n) {
|
|
968
1084
|
if (reporterBlocksSource(e, i)) {
|
|
@@ -971,7 +1087,7 @@ function transitionComplete(e) {
|
|
|
971
1087
|
}
|
|
972
1088
|
n.delete(e);
|
|
973
1089
|
}
|
|
974
|
-
if (!r) e.
|
|
1090
|
+
if (!r) e.Re.delete(i); else if (i.S & STATUS_PENDING && i.o?._?.source === i) {
|
|
975
1091
|
t = false;
|
|
976
1092
|
break;
|
|
977
1093
|
}
|
|
@@ -979,8 +1095,8 @@ function transitionComplete(e) {
|
|
|
979
1095
|
// Override blockage lives with the engine (absent hook = "no optimistic
|
|
980
1096
|
// blockage"); the hook's loops over _optimisticNodes/_optimisticStores are
|
|
981
1097
|
// no-ops when the transition holds neither, so no pre-check is needed.
|
|
982
|
-
if (t && GlobalQueue.
|
|
983
|
-
t && (e.
|
|
1098
|
+
if (t && GlobalQueue.cn?.(e)) t = false;
|
|
1099
|
+
t && (e.ft = true);
|
|
984
1100
|
return t;
|
|
985
1101
|
}
|
|
986
1102
|
|
|
@@ -992,10 +1108,23 @@ function transitionComplete(e) {
|
|
|
992
1108
|
}
|
|
993
1109
|
|
|
994
1110
|
function currentTransition(e) {
|
|
995
|
-
while (e.
|
|
1111
|
+
while (e.ft && typeof e.ft === "object") e = e.ft;
|
|
996
1112
|
return e;
|
|
997
1113
|
}
|
|
998
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
|
+
|
|
999
1128
|
function runInTransition(e, t) {
|
|
1000
1129
|
const i = activeTransition;
|
|
1001
1130
|
try {
|
|
@@ -1029,4 +1158,4 @@ function runInTransition(e, t) {
|
|
|
1029
1158
|
}
|
|
1030
1159
|
}
|
|
1031
1160
|
|
|
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 };
|
|
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 };
|