@solidjs/signals 2.0.0-rc.4 → 2.0.0-rc.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dev.js +1618 -232
- package/dist/node.cjs +2262 -1199
- package/dist/prod/boundaries.js +4 -1
- package/dist/prod/core/async.js +152 -101
- package/dist/prod/core/constants.js +55 -1
- package/dist/prod/core/core.js +305 -234
- package/dist/prod/core/effect.js +28 -28
- package/dist/prod/core/error.js +13 -1
- package/dist/prod/core/external.js +2 -2
- package/dist/prod/core/graph.js +27 -27
- package/dist/prod/core/heap.js +30 -30
- package/dist/prod/core/lanes.js +32 -32
- package/dist/prod/core/optimistic.js +57 -57
- package/dist/prod/core/owner.js +34 -34
- package/dist/prod/core/scheduler.js +346 -152
- package/dist/prod/core/verdict.js +122 -65
- package/dist/prod/index.js +3 -3
- package/dist/prod/map.js +106 -106
- package/dist/prod/signals.js +321 -26
- package/dist/prod/store/next/optimistic.js +363 -151
- package/dist/prod/store/next/patch.js +6 -6
- package/dist/prod/store/next/projection.js +25 -21
- package/dist/prod/store/next/store.js +223 -113
- package/dist/prod/store/store.js +2 -2
- package/dist/types/core/async.d.ts +2 -0
- package/dist/types/core/attribution-hooks.d.ts +11 -0
- package/dist/types/core/attribution.d.ts +57 -4
- package/dist/types/core/constants.d.ts +54 -0
- package/dist/types/core/core.d.ts +19 -20
- package/dist/types/core/dev.d.ts +8 -2
- package/dist/types/core/error.d.ts +9 -0
- package/dist/types/core/index.d.ts +2 -2
- package/dist/types/core/scheduler.d.ts +39 -0
- package/dist/types/core/types.d.ts +12 -0
- package/dist/types/index.d.ts +3 -3
- package/dist/types/signals.d.ts +108 -11
- package/dist/types/store/next/optimistic.d.ts +13 -10
- package/dist/types/store/next/projection.d.ts +1 -1
- package/dist/types/store/next/store.d.ts +22 -0
- package/dist/types/store/next/target.d.ts +25 -0
- package/dist/types-cjs/core/async.d.cts +2 -0
- package/dist/types-cjs/core/attribution-hooks.d.cts +11 -0
- package/dist/types-cjs/core/attribution.d.cts +57 -4
- package/dist/types-cjs/core/constants.d.cts +54 -0
- package/dist/types-cjs/core/core.d.cts +19 -20
- package/dist/types-cjs/core/dev.d.cts +8 -2
- package/dist/types-cjs/core/error.d.cts +9 -0
- package/dist/types-cjs/core/index.d.cts +2 -2
- package/dist/types-cjs/core/scheduler.d.cts +39 -0
- package/dist/types-cjs/core/types.d.cts +12 -0
- package/dist/types-cjs/index.d.cts +3 -3
- package/dist/types-cjs/signals.d.cts +108 -11
- package/dist/types-cjs/store/next/optimistic.d.cts +13 -10
- package/dist/types-cjs/store/next/projection.d.cts +1 -1
- package/dist/types-cjs/store/next/store.d.cts +22 -0
- package/dist/types-cjs/store/next/target.d.cts +25 -0
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EFFECT_RENDER, EFFECT_USER, STATUS_PENDING, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_DISPOSED,
|
|
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";
|
|
2
2
|
|
|
3
3
|
import { currentOptimisticLane } from "./core.js";
|
|
4
4
|
|
|
@@ -10,9 +10,9 @@ import { sweepDormant } from "./graph.js";
|
|
|
10
10
|
|
|
11
11
|
import { runHeap, deleteFromHeap, enqueueSub } from "./heap.js";
|
|
12
12
|
|
|
13
|
-
import { activeLanes, assignOrMergeLane, findLane } from "./lanes.js";
|
|
13
|
+
import { activeLanes, assignOrMergeLane, findLane, hasActiveOverride } from "./lanes.js";
|
|
14
14
|
|
|
15
|
-
export { getOrCreateLane,
|
|
15
|
+
export { getOrCreateLane, mergeLanes, resolveLane } from "./lanes.js";
|
|
16
16
|
|
|
17
17
|
import { devCheckFlushStart, devCheckActiveOverrides, devCensusCompanions, devCheckQuiescent } from "./invariants.js";
|
|
18
18
|
|
|
@@ -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
|
+
Qe: 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
|
+
Qe: 0,
|
|
32
32
|
EE: 0
|
|
33
33
|
};
|
|
34
34
|
|
|
@@ -63,7 +63,7 @@ const transientStoreNodes = new Set;
|
|
|
63
63
|
|
|
64
64
|
function canUseSimpleSyncFlush(e) {
|
|
65
65
|
const t = e.m;
|
|
66
|
-
return transitions.size === 0 && activeLanes.size === 0 && e.Qt.length === 0 && t.
|
|
66
|
+
return transitions.size === 0 && activeLanes.size === 0 && e.Qt.length === 0 && t.ze.length === 0 && t.A.length === 0 && t.En.size === 0 && transientStoreNodes.size === 0;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
function sweepTransientStoreNodes() {
|
|
@@ -73,14 +73,14 @@ function sweepTransientStoreNodes() {
|
|
|
73
73
|
transientStoreNodes.delete(e);
|
|
74
74
|
continue;
|
|
75
75
|
}
|
|
76
|
-
if (e.
|
|
77
|
-
if (e.o?.
|
|
76
|
+
if (e.Re !== NOT_PENDING) continue;
|
|
77
|
+
if (e.o?.Pe !== undefined && e.o?.Pe !== NOT_PENDING) continue;
|
|
78
78
|
// A live affects() mark keeps the node addressable: sweeping it would
|
|
79
79
|
// detach the refcount from the slot (a fresh probe would upsert a new,
|
|
80
80
|
// unmarked node for the same property).
|
|
81
81
|
if (e.o?.t) continue;
|
|
82
82
|
transientStoreNodes.delete(e);
|
|
83
|
-
e.o?.
|
|
83
|
+
e.o?.Et?.();
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -108,31 +108,31 @@ function setProjectionWriteActive(e) {
|
|
|
108
108
|
*/ function createBatch() {
|
|
109
109
|
return {
|
|
110
110
|
Te: clock,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
Lt: [],
|
|
112
|
+
_e: new Map,
|
|
113
|
+
ze: [],
|
|
114
114
|
A: [],
|
|
115
|
-
|
|
115
|
+
En: new Set,
|
|
116
116
|
ue: [],
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
Bt: {
|
|
118
|
+
Mt: [ [], [] ],
|
|
119
119
|
Qt: []
|
|
120
120
|
},
|
|
121
|
-
|
|
121
|
+
sn: false,
|
|
122
122
|
cn: new Set
|
|
123
123
|
};
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
function mergeTransitionState(e, t) {
|
|
127
|
-
t.
|
|
127
|
+
t.sn = e;
|
|
128
128
|
e.ue.push(...t.ue);
|
|
129
|
-
for (const i of activeLanes) if (i.
|
|
130
|
-
if (t.
|
|
129
|
+
for (const i of activeLanes) if (i.Ae === t) i.Ae = e;
|
|
130
|
+
if (t.ze.length) {
|
|
131
131
|
// Move (don't copy): the global queue's batch may still be the outgoing
|
|
132
132
|
// transition, and the adoption pass in initTransition would re-push its
|
|
133
133
|
// contents into the target — duplicating every entry.
|
|
134
|
-
e.
|
|
135
|
-
t.
|
|
134
|
+
e.ze.push(...t.ze);
|
|
135
|
+
t.ze.length = 0;
|
|
136
136
|
}
|
|
137
137
|
if (t.A.length) {
|
|
138
138
|
// Move (don't copy): the global queue's batch may still be the outgoing
|
|
@@ -141,18 +141,18 @@ function mergeTransitionState(e, t) {
|
|
|
141
141
|
e.A.push(...t.A);
|
|
142
142
|
t.A.length = 0;
|
|
143
143
|
}
|
|
144
|
-
for (const i of t.
|
|
144
|
+
for (const i of t.En) e.En.add(i);
|
|
145
145
|
// Patch-channel stash (store/next/patch.ts): entries held for the outgoing
|
|
146
146
|
// transition must ride the merge like every other per-transition
|
|
147
147
|
// collection — releaseBatch only reads the COMMITTING transition's stash,
|
|
148
148
|
// so a stranded sidecar would silently drop its patches. Move (don't
|
|
149
149
|
// copy), same aliasing rule as the collections above. The field is an
|
|
150
150
|
// expando so this module stays free of patch imports (pay-for-use).
|
|
151
|
-
const i = t.
|
|
151
|
+
const i = t.wt;
|
|
152
152
|
if (i !== undefined) {
|
|
153
|
-
t.
|
|
154
|
-
let n = e.
|
|
155
|
-
if (n !== undefined) n.push(...i); else n = e.
|
|
153
|
+
t.wt = undefined;
|
|
154
|
+
let n = e.wt;
|
|
155
|
+
if (n !== undefined) n.push(...i); else n = e.wt = i;
|
|
156
156
|
// Retarget the entries' coalescing stamps to the surviving stash
|
|
157
157
|
// (opaque backref contract with store/next/patch.ts): without this a
|
|
158
158
|
// post-merge emission misses the stamp and pushes a SECOND entry —
|
|
@@ -162,14 +162,110 @@ function mergeTransitionState(e, t) {
|
|
|
162
162
|
if (t !== undefined && t.qe === i[e]) t.qa = n;
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
|
-
for (const [i, n] of t.
|
|
166
|
-
let t = e.
|
|
167
|
-
if (!t) e.
|
|
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);
|
|
168
168
|
for (const e of n) t.add(e);
|
|
169
169
|
}
|
|
170
170
|
for (const i of t.cn) e.cn.add(i);
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Flip-entanglement (#3164 follow-up): `until()` is a declaration of
|
|
175
|
+
* relatedness — the predicate names the condition that confirms the awaiting
|
|
176
|
+
* transaction. When the predicate settles truthy, every live foreign
|
|
177
|
+
* transition whose staged write it read IS the confirming event by the
|
|
178
|
+
* user's own definition, so it merges into the awaiting transaction and
|
|
179
|
+
* reveals at the joint settle — the cross-primitive twin of the family fold
|
|
180
|
+
* (a landing on an optimism-carrying family joins the retaining
|
|
181
|
+
* transaction). Non-flipping updates never pass through here: falsy
|
|
182
|
+
* evaluations don't entangle, so unrelated traffic on the watched sources
|
|
183
|
+
* reveals freely on its own schedule.
|
|
184
|
+
*
|
|
185
|
+
* Runs inside the predicate's compute (pure phase) — the confirming
|
|
186
|
+
* transition's stamps are still live and its commit decision hasn't run, so
|
|
187
|
+
* the merge lands before any reveal. Only the tree-shaken graphs that call
|
|
188
|
+
* `until()` retain this.
|
|
189
|
+
*/ function entangleConfirmingTransitions(e, t) {
|
|
190
|
+
t = currentTransition(t);
|
|
191
|
+
if (t.sn === true) return;
|
|
192
|
+
// The confirming evidence is a dep whose value is STAGED (pending,
|
|
193
|
+
// uncommitted) at flip evaluation — committed deps are public already and
|
|
194
|
+
// carry nothing to entangle. A staged dep lives in one of two carriers: a
|
|
195
|
+
// stamped transition, or the queue's current batch (ambient registrations
|
|
196
|
+
// don't stamp; "ambient work IS a transaction" — the batch is the
|
|
197
|
+
// carrier). The entangle STEALS the carrier's staged cargo — its pending
|
|
198
|
+
// nodes move (re-stamped) into the awaiting transaction and reveal at its
|
|
199
|
+
// settle — but never the carrier itself: its async reporters, actions,
|
|
200
|
+
// and stashes are its own future (a live stream's flight must not chain
|
|
201
|
+
// the awaiting transaction to landings that haven't happened; a merged
|
|
202
|
+
// reporter deadlocked exactly that way).
|
|
203
|
+
let i = false;
|
|
204
|
+
for (let n = e.ut; n !== null; n = n.lt) {
|
|
205
|
+
const r = n.ot;
|
|
206
|
+
if (r.Re !== NOT_PENDING) {
|
|
207
|
+
const e = r.Ae;
|
|
208
|
+
const n = e != null ? currentTransition(e) : null;
|
|
209
|
+
// Skip the awaiting transaction's own cargo (t === target: a
|
|
210
|
+
// fold-staged landing or a write the action itself issued — hold and
|
|
211
|
+
// reveal already correct) and dead carriers. Ambient-batch staging
|
|
212
|
+
// (t === null) must leave the batch NOW — it commits at this flush's
|
|
213
|
+
// end, which would reveal the confirmation under the live optimism
|
|
214
|
+
// it just confirmed.
|
|
215
|
+
const s = n === null ? currentBatch.Lt : n !== t && n.sn !== true ? n.Lt : null;
|
|
216
|
+
if (s !== null) i = stealEntangledCargo(s, t) || i;
|
|
217
|
+
}
|
|
218
|
+
if (n === e.je) break;
|
|
219
|
+
}
|
|
220
|
+
// The steal never activates the awaiting transaction: the predicate can
|
|
221
|
+
// flip inside another transaction's finalize heap, and adopting the queue
|
|
222
|
+
// batch there hands the stolen cargo to that finalize's commit sweep — a
|
|
223
|
+
// premature reveal at a foreign settle. Subscribers that computed against
|
|
224
|
+
// the pre-steal world were re-dirtied by the steal itself, so this
|
|
225
|
+
// flush's applies paint the masked (mid-hold) view; the cargo commits at
|
|
226
|
+
// the awaiting transaction's own settle.
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Move a confirming carrier's staged nodes into the awaiting transaction:
|
|
230
|
+
* re-stamp and arm the held-truth mask (override-covered nodes skip it —
|
|
231
|
+
* the override already hides their staged value per A17, and is usually
|
|
232
|
+
* the very optimism this confirmation settles); the mask's commit
|
|
233
|
+
* registers the settle-side post-revert wake. The carrier's array is
|
|
234
|
+
* emptied so its own commit point commits none of the stolen cargo.
|
|
235
|
+
*
|
|
236
|
+
* EFFECT subs of stolen nodes re-run: any that recomputed against the
|
|
237
|
+
* staging BEFORE the steal (the carrier's landing notified them as a plain
|
|
238
|
+
* write) hold a private torn result — override composed with confirming
|
|
239
|
+
* truth — that the next paint gate (stash-point lane run, a foreign
|
|
240
|
+
* flush's completion drain) would show. Re-running them under the mask
|
|
241
|
+
* re-derives the mid-hold view in this same heap pass. PURE computeds are
|
|
242
|
+
* deliberately NOT re-run: a torn staged value of theirs is itself stolen
|
|
243
|
+
* cargo — masked at read, so ordinary readers already serve their
|
|
244
|
+
* committed value — while re-running them would re-derive the OLD world
|
|
245
|
+
* and re-stage it over the held truth. The reveal re-notifies
|
|
246
|
+
* (commitPendingNodes), which is when they re-derive for real. */ function stealEntangledCargo(e, t) {
|
|
247
|
+
if (e === t.Lt || e.length === 0) return false;
|
|
248
|
+
for (let i = 0; i < e.length; i++) {
|
|
249
|
+
const n = e[i];
|
|
250
|
+
n.Ae = t;
|
|
251
|
+
t.Lt.push(n);
|
|
252
|
+
// Override-covered nodes stay silent AND unmasked: the override is the
|
|
253
|
+
// display (A17 — its staging never notified, its revert will), so their
|
|
254
|
+
// subs saw nothing and re-running one would break the silence with a
|
|
255
|
+
// duplicate fire of an unchanged view.
|
|
256
|
+
if (!hasActiveOverride(n)) {
|
|
257
|
+
n.T |= CONFIG_HELD_TRUTH;
|
|
258
|
+
for (let e = n.u; e !== null; e = e.ae) {
|
|
259
|
+
const t = e.ce;
|
|
260
|
+
if (t.ge && !(t.T & CONFIG_AUTHORITATIVE_READ)) enqueueSub(t);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
e.length = 0;
|
|
265
|
+
transitions.add(t);
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
|
|
173
269
|
function schedule() {
|
|
174
270
|
if (halted) {
|
|
175
271
|
notifyHalted();
|
|
@@ -177,7 +273,7 @@ function schedule() {
|
|
|
177
273
|
}
|
|
178
274
|
if (scheduled) return;
|
|
179
275
|
scheduled = true;
|
|
180
|
-
if (!syncDepth && !globalQueue.
|
|
276
|
+
if (!syncDepth && !globalQueue.fn && !projectionWriteActive) queueMicrotask(flush);
|
|
181
277
|
}
|
|
182
278
|
|
|
183
279
|
/**
|
|
@@ -212,9 +308,9 @@ let queueRunToken = 0;
|
|
|
212
308
|
|
|
213
309
|
class Queue {
|
|
214
310
|
ke=null;
|
|
215
|
-
|
|
311
|
+
Mt=[ [], [] ];
|
|
216
312
|
Qt=[];
|
|
217
|
-
|
|
313
|
+
jt=0;
|
|
218
314
|
created=clock;
|
|
219
315
|
addChild(e) {
|
|
220
316
|
this.Qt.push(e);
|
|
@@ -232,9 +328,9 @@ class Queue {
|
|
|
232
328
|
return false;
|
|
233
329
|
}
|
|
234
330
|
run(e) {
|
|
235
|
-
if (this.
|
|
236
|
-
const t = this.
|
|
237
|
-
this.
|
|
331
|
+
if (this.Mt[e - 1].length) {
|
|
332
|
+
const t = this.Mt[e - 1];
|
|
333
|
+
this.Mt[e - 1] = [];
|
|
238
334
|
runQueue(t, e);
|
|
239
335
|
}
|
|
240
336
|
// Effects run here can dispose owners, and disposal removes queues from
|
|
@@ -247,11 +343,11 @@ class Queue {
|
|
|
247
343
|
const t = this.Qt;
|
|
248
344
|
const i = ++queueRunToken;
|
|
249
345
|
for (let n = 0; n < t.length; ) {
|
|
250
|
-
const
|
|
251
|
-
if (
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (t[n] !==
|
|
346
|
+
const r = t[n];
|
|
347
|
+
if (r.jt !== i) {
|
|
348
|
+
r.jt = i;
|
|
349
|
+
r.run?.(e);
|
|
350
|
+
if (t[n] !== r) {
|
|
255
351
|
n = 0;
|
|
256
352
|
continue;
|
|
257
353
|
}
|
|
@@ -264,23 +360,23 @@ class Queue {
|
|
|
264
360
|
// Route to lane's effect queue if we're in an optimistic recomputation
|
|
265
361
|
if (currentOptimisticLane) {
|
|
266
362
|
const i = findLane(currentOptimisticLane);
|
|
267
|
-
i.
|
|
363
|
+
i.tn[e - 1].push(t);
|
|
268
364
|
} else {
|
|
269
|
-
this.
|
|
365
|
+
this.Mt[e - 1].push(t);
|
|
270
366
|
}
|
|
271
367
|
}
|
|
272
368
|
schedule();
|
|
273
369
|
}
|
|
274
370
|
stashQueues(e) {
|
|
275
|
-
e.
|
|
276
|
-
e.
|
|
277
|
-
this.
|
|
371
|
+
e.Mt[0].push(...this.Mt[0]);
|
|
372
|
+
e.Mt[1].push(...this.Mt[1]);
|
|
373
|
+
this.Mt = [ [], [] ];
|
|
278
374
|
for (let t = 0; t < this.Qt.length; t++) {
|
|
279
375
|
let i = this.Qt[t];
|
|
280
376
|
let n = e.Qt[t];
|
|
281
377
|
if (!n) {
|
|
282
378
|
n = {
|
|
283
|
-
|
|
379
|
+
Mt: [ [], [] ],
|
|
284
380
|
Qt: []
|
|
285
381
|
};
|
|
286
382
|
e.Qt[t] = n;
|
|
@@ -289,8 +385,8 @@ class Queue {
|
|
|
289
385
|
}
|
|
290
386
|
}
|
|
291
387
|
restoreQueues(e) {
|
|
292
|
-
this.
|
|
293
|
-
this.
|
|
388
|
+
this.Mt[0].push(...e.Mt[0]);
|
|
389
|
+
this.Mt[1].push(...e.Mt[1]);
|
|
294
390
|
for (let t = 0; t < e.Qt.length; t++) {
|
|
295
391
|
const i = e.Qt[t];
|
|
296
392
|
let n = this.Qt[t];
|
|
@@ -300,14 +396,14 @@ class Queue {
|
|
|
300
396
|
}
|
|
301
397
|
|
|
302
398
|
class GlobalQueue extends Queue {
|
|
303
|
-
|
|
399
|
+
fn=false;
|
|
304
400
|
// The current transaction-shaped batch: a plain ambient batch while no
|
|
305
401
|
// transition is active, the active transition itself after initTransition.
|
|
306
402
|
m=createBatch();
|
|
307
|
-
static Ce;
|
|
308
403
|
static Fe;
|
|
309
|
-
static
|
|
310
|
-
static
|
|
404
|
+
static He;
|
|
405
|
+
static it;
|
|
406
|
+
static qt=null;
|
|
311
407
|
// Store-side hook: drops a keyless affects() mark's identity scope when the
|
|
312
408
|
// carrier node's last registration releases (wired by store.ts, mirroring
|
|
313
409
|
// _clearOptimisticStore).
|
|
@@ -321,27 +417,27 @@ class GlobalQueue extends Queue {
|
|
|
321
417
|
static N=null;
|
|
322
418
|
// External-source bridge (wired by enableExternalSource(); null while no
|
|
323
419
|
// config is active — including after _resetExternalSourceConfig()).
|
|
324
|
-
static
|
|
325
|
-
static
|
|
420
|
+
static Pt=null;
|
|
421
|
+
static ht=null;
|
|
326
422
|
// Verdict-layer hooks (wired by verdict.ts when isPending()/latest() are
|
|
327
423
|
// imported; null in apps that never use them). Call sites either guard for
|
|
328
424
|
// null or sit behind state only the verdict layer can create (`!` is safe
|
|
329
425
|
// there: `_pendingSignal`/`_latestValueComputed` are only ever assigned by
|
|
330
426
|
// verdict.ts, and `pendingCheckActive`/`latestReadActive` only flip inside
|
|
331
427
|
// isPending()/latest()).
|
|
332
|
-
static
|
|
428
|
+
static Ue=null;
|
|
333
429
|
static de=null;
|
|
334
|
-
static
|
|
430
|
+
static me=null;
|
|
335
431
|
static un=null;
|
|
336
|
-
static
|
|
337
|
-
static Dt=null;
|
|
432
|
+
static gt=null;
|
|
338
433
|
static Ht=null;
|
|
339
|
-
static
|
|
434
|
+
static kt=null;
|
|
435
|
+
static et=null;
|
|
340
436
|
static k=null;
|
|
341
|
-
static
|
|
437
|
+
static Wt=null;
|
|
342
438
|
// Re-asks probes whose verdict was provisionally suppressed by a fresh read
|
|
343
439
|
// of a held value, once the transaction gains an async blocker (#3028).
|
|
344
|
-
static
|
|
440
|
+
static zt=null;
|
|
345
441
|
// Optimistic-engine hooks (wired by core/optimistic.ts via
|
|
346
442
|
// installOptimisticEngine(), called from verdict.ts / createOptimistic /
|
|
347
443
|
// createOptimisticStore — every module that can create optimistic state).
|
|
@@ -349,31 +445,35 @@ class GlobalQueue extends Queue {
|
|
|
349
445
|
// `_overrideValue` slot, a lane in `activeLanes`, an `_optimisticNodes`
|
|
350
446
|
// entry, or a non-null `currentOptimisticLane`, so `!` invocations are safe
|
|
351
447
|
// once the gate holds.
|
|
352
|
-
static
|
|
448
|
+
static xt=null;
|
|
449
|
+
static Tn=null;
|
|
353
450
|
static dn=null;
|
|
354
451
|
static In=null;
|
|
355
452
|
static Nn=null;
|
|
356
|
-
static _n=null;
|
|
357
453
|
/** Patch-channel optimistic drain (next/patch.ts): optimistic emissions
|
|
358
454
|
* apply at lane-effect timing — visible in flight, unlike the regular
|
|
359
455
|
* effect queues an action stashes. Injected; null when unused. */
|
|
360
456
|
static ln=null;
|
|
361
|
-
static
|
|
362
|
-
static
|
|
363
|
-
static
|
|
364
|
-
static
|
|
457
|
+
static vt=null;
|
|
458
|
+
static Vt=null;
|
|
459
|
+
static bt=null;
|
|
460
|
+
static Be=null;
|
|
365
461
|
static $e=null;
|
|
366
|
-
|
|
367
|
-
|
|
462
|
+
/** Authoritative-view reader wakeup (until()): installed at first until() call.
|
|
463
|
+
* Call sites are gated by CONFIG_AUTHORITATIVE_OBSERVED, which only until()'s
|
|
464
|
+
* carve-out read can set, so `!` invocations are safe once the gate holds. */
|
|
465
|
+
static he=null;
|
|
466
|
+
static Xe=null;
|
|
467
|
+
static _n=null;
|
|
368
468
|
flush() {
|
|
369
|
-
if (this.
|
|
469
|
+
if (this.fn) return;
|
|
370
470
|
// Fast drain: nothing in flight but plain pending commits — no dirty
|
|
371
471
|
// computeds, no queued effects, no child queues, no transitions/lanes/
|
|
372
472
|
// optimistic state. Commit and go; anything a commit hook schedules
|
|
373
473
|
// (companion snaps, store folds notifying subs) re-arms `scheduled`
|
|
374
474
|
// below and the outer drain loop takes the full spine next round.
|
|
375
|
-
if (activeTransition === null && dirtyQueue.EE < dirtyQueue.
|
|
376
|
-
this.
|
|
475
|
+
if (activeTransition === null && dirtyQueue.EE < dirtyQueue.Qe && this.Mt[0].length === 0 && this.Mt[1].length === 0 && this.Qt.length === 0 && canUseSimpleSyncFlush(this)) {
|
|
476
|
+
this.fn = true;
|
|
377
477
|
try {
|
|
378
478
|
// Sweep first: unobserved() pulls swept nodes out of the dirty heap,
|
|
379
479
|
// so a dormant memo dirtied in the same tick is reclaimed instead of
|
|
@@ -381,20 +481,20 @@ class GlobalQueue extends Queue {
|
|
|
381
481
|
sweepDormant();
|
|
382
482
|
commitPendingNodes();
|
|
383
483
|
} finally {
|
|
384
|
-
this.
|
|
484
|
+
this.fn = false;
|
|
385
485
|
}
|
|
386
486
|
clock++;
|
|
387
|
-
scheduled = dirtyQueue.EE >= dirtyQueue.
|
|
487
|
+
scheduled = dirtyQueue.EE >= dirtyQueue.Qe || this.Mt[0].length !== 0 || this.Mt[1].length !== 0 || this.m.Lt.length !== 0;
|
|
388
488
|
return;
|
|
389
489
|
}
|
|
390
|
-
this.
|
|
490
|
+
this.fn = true;
|
|
391
491
|
try {
|
|
392
492
|
if (false) ;
|
|
393
493
|
// Before runHeap for the same reason as the fast drain above; late
|
|
394
494
|
// subscribers (an effect reading a swept memo this flush) revive it,
|
|
395
495
|
// which is the pay-for-use contract.
|
|
396
496
|
sweepDormant();
|
|
397
|
-
runHeap(dirtyQueue, GlobalQueue.
|
|
497
|
+
runHeap(dirtyQueue, GlobalQueue.Fe);
|
|
398
498
|
if (activeTransition) {
|
|
399
499
|
const e = transitionComplete(activeTransition);
|
|
400
500
|
if (!e) {
|
|
@@ -407,7 +507,7 @@ class GlobalQueue extends Queue {
|
|
|
407
507
|
// zombies here. Height-adjust entries still process normally: a
|
|
408
508
|
// dirtied one keeps its height flag and falls through to runHeap's
|
|
409
509
|
// adjustHeight path on the next pass of the bucket.
|
|
410
|
-
runHeap(zombieQueue, this.m === e ? cancelZombieRecompute : GlobalQueue.
|
|
510
|
+
runHeap(zombieQueue, this.m === e ? cancelZombieRecompute : GlobalQueue.Fe);
|
|
411
511
|
// Detach: the stashed transition keeps its batch; ambient work that
|
|
412
512
|
// follows lands in a fresh one. If the batch is already a separate
|
|
413
513
|
// ambient one — action done() restored activeTransition without
|
|
@@ -417,64 +517,64 @@ class GlobalQueue extends Queue {
|
|
|
417
517
|
if (this.m === e) currentBatch = this.m = createBatch();
|
|
418
518
|
// Run lane effects immediately (before stashing) - lanes with no pending async
|
|
419
519
|
if (activeLanes.size) {
|
|
420
|
-
GlobalQueue.
|
|
421
|
-
GlobalQueue.
|
|
520
|
+
GlobalQueue.Nn(EFFECT_RENDER);
|
|
521
|
+
GlobalQueue.Nn(EFFECT_USER);
|
|
422
522
|
}
|
|
423
|
-
this.stashQueues(e.
|
|
523
|
+
this.stashQueues(e.Bt);
|
|
424
524
|
clock++;
|
|
425
525
|
// A kept ambient batch may hold pending nodes (#2916): stay
|
|
426
526
|
// scheduled so the outer drain loop commits them via the plain
|
|
427
527
|
// flush path instead of leaving them until the next natural flush.
|
|
428
|
-
scheduled = dirtyQueue.EE >= dirtyQueue.
|
|
429
|
-
reassignPendingTransition(e.
|
|
528
|
+
scheduled = dirtyQueue.EE >= dirtyQueue.Qe || this.m.Lt.length > 0;
|
|
529
|
+
reassignPendingTransition(e.Lt);
|
|
430
530
|
activeTransition = null;
|
|
431
531
|
finalizePureQueue(null, true);
|
|
432
532
|
return;
|
|
433
533
|
}
|
|
434
534
|
const t = activeTransition;
|
|
435
535
|
const i = this.m;
|
|
436
|
-
i !== t && i.
|
|
437
|
-
this.restoreQueues(t.
|
|
536
|
+
i !== t && i.Lt.push(...t.Lt);
|
|
537
|
+
this.restoreQueues(t.Bt);
|
|
438
538
|
transitions.delete(t);
|
|
439
539
|
activeTransition = null;
|
|
440
|
-
reassignPendingTransition(i.
|
|
540
|
+
reassignPendingTransition(i.Lt);
|
|
441
541
|
finalizePureQueue(t);
|
|
442
542
|
if (i === t) {
|
|
443
543
|
// Drop the dead Transition wrapper but keep its (drained) containers
|
|
444
544
|
// as the ambient batch — late registrations during finalization live
|
|
445
545
|
// there and must survive to the next flush.
|
|
446
546
|
const e = createBatch();
|
|
447
|
-
e.
|
|
448
|
-
e.
|
|
547
|
+
e.Lt = i.Lt;
|
|
548
|
+
e.ze = i.ze;
|
|
449
549
|
e.A = i.A;
|
|
450
|
-
e.
|
|
550
|
+
e.En = i.En;
|
|
451
551
|
currentBatch = this.m = e;
|
|
452
552
|
}
|
|
453
553
|
} else {
|
|
454
554
|
if (canUseSimpleSyncFlush(this)) {
|
|
455
555
|
commitPendingNodes();
|
|
456
|
-
if (dirtyQueue.EE >= dirtyQueue.
|
|
457
|
-
runHeap(dirtyQueue, GlobalQueue.
|
|
556
|
+
if (dirtyQueue.EE >= dirtyQueue.Qe) {
|
|
557
|
+
runHeap(dirtyQueue, GlobalQueue.Fe);
|
|
458
558
|
commitPendingNodes();
|
|
459
559
|
}
|
|
460
560
|
} else {
|
|
461
|
-
if (transitions.size) runHeap(zombieQueue, GlobalQueue.
|
|
561
|
+
if (transitions.size) runHeap(zombieQueue, GlobalQueue.Fe);
|
|
462
562
|
finalizePureQueue();
|
|
463
563
|
}
|
|
464
564
|
}
|
|
465
565
|
clock++;
|
|
466
566
|
// Check if finalization added items to the heap (from optimistic reversion)
|
|
467
|
-
scheduled = dirtyQueue.EE >= dirtyQueue.
|
|
567
|
+
scheduled = dirtyQueue.EE >= dirtyQueue.Qe;
|
|
468
568
|
// Run lane effects first (for ready lanes), then regular effects
|
|
469
|
-
activeLanes.size && GlobalQueue.
|
|
569
|
+
activeLanes.size && GlobalQueue.Nn(EFFECT_RENDER);
|
|
470
570
|
this.run(EFFECT_RENDER);
|
|
471
|
-
activeLanes.size && GlobalQueue.
|
|
571
|
+
activeLanes.size && GlobalQueue.Nn(EFFECT_USER);
|
|
472
572
|
this.run(EFFECT_USER);
|
|
473
573
|
if (false) ;
|
|
474
574
|
if (false && !scheduled && !activeTransition && transitions.size === 0 && activeLanes.size === 0) ;
|
|
475
575
|
if (false) ;
|
|
476
576
|
} finally {
|
|
477
|
-
this.
|
|
577
|
+
this.fn = false;
|
|
478
578
|
}
|
|
479
579
|
}
|
|
480
580
|
notify(e, t, i, n) {
|
|
@@ -489,13 +589,13 @@ class GlobalQueue extends Queue {
|
|
|
489
589
|
if (t?.l) return true;
|
|
490
590
|
if (activeTransition && t) {
|
|
491
591
|
const i = t.source;
|
|
492
|
-
let n = activeTransition.
|
|
493
|
-
if (!n) activeTransition.
|
|
494
|
-
const
|
|
592
|
+
let n = activeTransition._e.get(i);
|
|
593
|
+
if (!n) activeTransition._e.set(i, n = new Set);
|
|
594
|
+
const r = n.size;
|
|
495
595
|
n.add(e);
|
|
496
|
-
if (n.size !==
|
|
596
|
+
if (n.size !== r) {
|
|
497
597
|
schedule();
|
|
498
|
-
GlobalQueue.
|
|
598
|
+
GlobalQueue.zt?.(activeTransition);
|
|
499
599
|
}
|
|
500
600
|
}
|
|
501
601
|
}
|
|
@@ -504,8 +604,17 @@ class GlobalQueue extends Queue {
|
|
|
504
604
|
return false;
|
|
505
605
|
}
|
|
506
606
|
initTransition(e) {
|
|
507
|
-
if (e)
|
|
508
|
-
|
|
607
|
+
if (e) {
|
|
608
|
+
e = currentTransition(e);
|
|
609
|
+
// A finished transaction cannot be re-entered: its state is committed
|
|
610
|
+
// or reverted, so "rejoining" it (A26) is meaningless and re-activating
|
|
611
|
+
// it spins the drain loop (#3140). The refusal must be a bare return —
|
|
612
|
+
// redirecting the caller to a fresh batch would re-arm the loop with a
|
|
613
|
+
// new transaction identity each pass. Stamps are cleared at commit, so
|
|
614
|
+
// this is a belt for paths that hand over a chased-dead reference
|
|
615
|
+
// (merged chains, async settles racing completion).
|
|
616
|
+
if (e.sn === true || e === activeTransition) return;
|
|
617
|
+
}
|
|
509
618
|
if (!e && activeTransition && activeTransition.Te === clock) return;
|
|
510
619
|
if (!activeTransition) {
|
|
511
620
|
activeTransition = e ?? createBatch();
|
|
@@ -526,18 +635,18 @@ class GlobalQueue extends Queue {
|
|
|
526
635
|
// must not entangle unrelated writes to it; the same rule holds one hop
|
|
527
636
|
// downstream: propagation never queues pended subscribers as pending
|
|
528
637
|
// nodes, see propagateAffectsMark, #2893.
|
|
529
|
-
for (let e = 0; e < t.
|
|
530
|
-
const i = t.
|
|
531
|
-
i.
|
|
532
|
-
activeTransition.
|
|
638
|
+
for (let e = 0; e < t.Lt.length; e++) {
|
|
639
|
+
const i = t.Lt[e];
|
|
640
|
+
i.Ae = activeTransition;
|
|
641
|
+
activeTransition.Lt.push(i);
|
|
533
642
|
}
|
|
534
|
-
for (let e = 0; e < t.
|
|
535
|
-
const i = t.
|
|
536
|
-
i.
|
|
537
|
-
activeTransition.
|
|
643
|
+
for (let e = 0; e < t.ze.length; e++) {
|
|
644
|
+
const i = t.ze[e];
|
|
645
|
+
i.Ae = activeTransition;
|
|
646
|
+
activeTransition.ze.push(i);
|
|
538
647
|
}
|
|
539
648
|
if (t.A.length) activeTransition.A.push(...t.A);
|
|
540
|
-
for (const e of t.
|
|
649
|
+
for (const e of t.En) activeTransition.En.add(e);
|
|
541
650
|
// Gated readers recorded against the ambient batch move with it: their
|
|
542
651
|
// replay-at-commit now happens at the transaction's completion.
|
|
543
652
|
if (t.cn.size) {
|
|
@@ -547,13 +656,22 @@ class GlobalQueue extends Queue {
|
|
|
547
656
|
currentBatch = this.m = activeTransition;
|
|
548
657
|
}
|
|
549
658
|
for (const e of activeLanes) {
|
|
550
|
-
if (!e.
|
|
659
|
+
if (!e.Ae) e.Ae = activeTransition;
|
|
551
660
|
}
|
|
661
|
+
// A transaction's ambient window is one flush. Entering must therefore
|
|
662
|
+
// guarantee a flush: a transaction opened with no writes (an action whose
|
|
663
|
+
// first statements only await) otherwise leaves activeTransition and the
|
|
664
|
+
// adopted batch armed across the async gap, and the next unrelated work
|
|
665
|
+
// to arrive — an optimistic store's authoritative landing, a plain async
|
|
666
|
+
// settle — is adopted into a transaction it has nothing to do with
|
|
667
|
+
// (#3141). The scheduled flush parks the incomplete transaction through
|
|
668
|
+
// the normal machinery and detaches the ambient slots first.
|
|
669
|
+
schedule();
|
|
552
670
|
}
|
|
553
671
|
}
|
|
554
672
|
|
|
555
673
|
function queuePendingNode(e) {
|
|
556
|
-
currentBatch.
|
|
674
|
+
currentBatch.Lt.push(e);
|
|
557
675
|
}
|
|
558
676
|
|
|
559
677
|
// Sticky: flips true on the first refresh() ever (the only setter of
|
|
@@ -577,7 +695,7 @@ function armReaskClear() {
|
|
|
577
695
|
function insertSubs(e, t = false) {
|
|
578
696
|
// §12d: stamp before walking — setSignal's staged-rewrite fast path skips
|
|
579
697
|
// the next walk for this node while the epoch holds (marking is idempotent).
|
|
580
|
-
e.
|
|
698
|
+
e.It = notifyEpoch;
|
|
581
699
|
// Get source lane: prefer node's own lane over current context
|
|
582
700
|
// This is important for isPending signals which need their own lane to flush immediately
|
|
583
701
|
// Presence bits gate the optional-slot probes (see constants.ts): one
|
|
@@ -585,14 +703,14 @@ function insertSubs(e, t = false) {
|
|
|
585
703
|
// lookups in the hottest notify loop. Bits are sticky — the field read
|
|
586
704
|
// stays authoritative when a bit is set.
|
|
587
705
|
const i = e.T;
|
|
588
|
-
const n = (i & CONFIG_HAS_LANE ? e.o?.
|
|
589
|
-
const
|
|
590
|
-
const
|
|
706
|
+
const n = (i & CONFIG_HAS_LANE ? e.o?.Je : undefined) || currentOptimisticLane;
|
|
707
|
+
const r = (i & CONFIG_HAS_SNAPSHOT) !== 0 && e.o?.We !== undefined;
|
|
708
|
+
const s = reaskArmed;
|
|
591
709
|
for (let i = e.u; i !== null; i = i.ae) {
|
|
592
710
|
const e = i.ce;
|
|
593
711
|
// A value-change notification is a new question for the subscriber: any
|
|
594
712
|
// pending re-ask mark (refresh) it carried is superseded.
|
|
595
|
-
if (
|
|
713
|
+
if (s) e.ie &= ~REACTIVE_REASK;
|
|
596
714
|
// Missed-wake latch (#3037): this write is landing while the subscriber
|
|
597
715
|
// is mid-recompute (a nested pull committing beneath its reads), and the
|
|
598
716
|
// heap refuses RECOMPUTING nodes. A gen-current link means the pass
|
|
@@ -601,8 +719,8 @@ function insertSubs(e, t = false) {
|
|
|
601
719
|
// pass either re-reads them fresh or trims them), and neither does the
|
|
602
720
|
// tail link: it is the read IN FLIGHT — read() links before it pulls, so
|
|
603
721
|
// this very commit is what that read returns.
|
|
604
|
-
if (e.ie & REACTIVE_RECOMPUTING_DEPS && i.
|
|
605
|
-
if (
|
|
722
|
+
if (e.ie & REACTIVE_RECOMPUTING_DEPS && i.Ft === e.Ke && i !== e.je) e.ie |= REACTIVE_MISSED_WAKE;
|
|
723
|
+
if (r && e.T & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
606
724
|
e.ie |= REACTIVE_SNAPSHOT_STALE;
|
|
607
725
|
continue;
|
|
608
726
|
}
|
|
@@ -612,7 +730,7 @@ function insertSubs(e, t = false) {
|
|
|
612
730
|
} else if (t) {
|
|
613
731
|
e.ie |= REACTIVE_OPTIMISTIC_DIRTY;
|
|
614
732
|
// No source lane means reversion - clear subscriber's lane so effects go to regular queue
|
|
615
|
-
if (e.o) e.o.
|
|
733
|
+
if (e.o) e.o.Je = undefined;
|
|
616
734
|
}
|
|
617
735
|
enqueueSub(e);
|
|
618
736
|
}
|
|
@@ -621,26 +739,32 @@ function insertSubs(e, t = false) {
|
|
|
621
739
|
function commitPendingNode(e) {
|
|
622
740
|
const t = e;
|
|
623
741
|
if (!t.oe) {
|
|
624
|
-
if (e.
|
|
625
|
-
e.be = e.
|
|
626
|
-
e.
|
|
742
|
+
if (e.Re !== NOT_PENDING) {
|
|
743
|
+
e.be = e.Re;
|
|
744
|
+
e.Re = NOT_PENDING;
|
|
627
745
|
}
|
|
628
746
|
if (e.T & CONFIG_HAS_COMPANIONS) GlobalQueue.un(e);
|
|
629
747
|
return;
|
|
630
748
|
}
|
|
631
|
-
if (e.
|
|
632
|
-
e.be = e.
|
|
633
|
-
e.
|
|
749
|
+
if (e.Re !== NOT_PENDING) {
|
|
750
|
+
e.be = e.Re;
|
|
751
|
+
e.Re = NOT_PENDING;
|
|
634
752
|
// Set _modified for effects, but not for tracked effects (they handle their own scheduling)
|
|
635
|
-
if (e.
|
|
753
|
+
if (e.ge && e.ge !== EFFECT_TRACKED) e.tt = true;
|
|
754
|
+
// A quiet re-ask classification preserved through a held landing dies
|
|
755
|
+
// with the value commit — the commit IS the reveal (#3178). Gated on the
|
|
756
|
+
// staged value: status propagation queues pending nodes whose windows
|
|
757
|
+
// are still OPEN (no staged value), and their live classification must
|
|
758
|
+
// survive this sweep.
|
|
759
|
+
if (e.o) e.o.De = false;
|
|
636
760
|
}
|
|
637
761
|
// The committed hold is the first observable answer for a loading-window
|
|
638
762
|
// node — the window closes here, not at compute time (#2990). Unconditional
|
|
639
763
|
// store to an always-present computed slot.
|
|
640
|
-
t.
|
|
764
|
+
t.Ne = false;
|
|
641
765
|
t.ie &= ~REACTIVE_MANUAL_WRITE;
|
|
642
766
|
if (!(t.S & STATUS_PENDING)) t.S &= ~STATUS_UNINITIALIZED;
|
|
643
|
-
if (t.o != null && (t.o.
|
|
767
|
+
if (t.o != null && (t.o.Ye !== null || t.o.qe !== null)) GlobalQueue.He(t, false, true);
|
|
644
768
|
if (e.T & CONFIG_HAS_COMPANIONS) GlobalQueue.un(e);
|
|
645
769
|
}
|
|
646
770
|
|
|
@@ -666,10 +790,34 @@ function setPatchCommitHook(e) {
|
|
|
666
790
|
patchCommitHook = e;
|
|
667
791
|
}
|
|
668
792
|
|
|
793
|
+
/** Held truth committed this finalize, awaiting its post-revert wake (see
|
|
794
|
+
* finalizePureQueue): the commit IS the reveal, but subscribers must not
|
|
795
|
+
* re-derive until the settling transaction's optimistic overrides have
|
|
796
|
+
* reverted — a commit-time wake recomputes them in the window where
|
|
797
|
+
* confirming truth is committed and the override still displays, a torn
|
|
798
|
+
* frame no timeline contains. */ const heldRevealed = [];
|
|
799
|
+
|
|
669
800
|
function commitPendingNodes() {
|
|
670
|
-
const e = currentBatch.
|
|
801
|
+
const e = currentBatch.Lt;
|
|
671
802
|
for (let t = 0; t < e.length; t++) {
|
|
672
|
-
|
|
803
|
+
const i = e[t];
|
|
804
|
+
commitPendingNode(i);
|
|
805
|
+
// The stamp dies with the commit (#3143) — symmetric with
|
|
806
|
+
// resolveOptimisticNodes clearing optimistic stamps. A stamp outliving
|
|
807
|
+
// its transaction let any later write (even a value-equal no-op, which
|
|
808
|
+
// re-opens before the equality bail) resurrect the finished transaction;
|
|
809
|
+
// a boundary flag rewritten every finalize pass then spun the drain loop
|
|
810
|
+
// forever (#3140). The held-truth mark dies the same death — the commit
|
|
811
|
+
// IS the reveal — but its wake defers to the post-revert pass: ordinary
|
|
812
|
+
// subscribers were masked to committed all hold (some re-derived against
|
|
813
|
+
// that old view and cached it), and commits are otherwise silent
|
|
814
|
+
// (staging already notified), so without a wake they'd hold the old
|
|
815
|
+
// world forever.
|
|
816
|
+
i.Ae = null;
|
|
817
|
+
if (i.T & CONFIG_HELD_TRUTH) {
|
|
818
|
+
i.T &= ~CONFIG_HELD_TRUTH;
|
|
819
|
+
heldRevealed.push(i);
|
|
820
|
+
}
|
|
673
821
|
}
|
|
674
822
|
e.length = 0;
|
|
675
823
|
storeCommitHook?.();
|
|
@@ -682,15 +830,15 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
682
830
|
const i = !t;
|
|
683
831
|
if (i) commitPendingNodes();
|
|
684
832
|
if (!t && globalQueue.Qt.length) checkBoundaryChildren(globalQueue);
|
|
685
|
-
const n = dirtyQueue.EE >= dirtyQueue.
|
|
686
|
-
if (n) runHeap(dirtyQueue, GlobalQueue.
|
|
833
|
+
const n = dirtyQueue.EE >= dirtyQueue.Qe;
|
|
834
|
+
if (n) runHeap(dirtyQueue, GlobalQueue.Fe);
|
|
687
835
|
if (i) {
|
|
688
836
|
if (n) commitPendingNodes();
|
|
689
837
|
// The settling batch: the completing transaction's, or the ambient one.
|
|
690
838
|
const t = e ?? globalQueue.m;
|
|
691
839
|
// Optimistic reversion: a non-empty batch means _optimisticWrite ran,
|
|
692
840
|
// which installed the engine's hooks.
|
|
693
|
-
if (t.
|
|
841
|
+
if (t.ze.length) GlobalQueue.Tn(t.ze);
|
|
694
842
|
// Replay entanglement: subs recorded by the read-time gate get rescheduled
|
|
695
843
|
// so they re-run with the now-committed values visible. The ambient batch
|
|
696
844
|
// replays too — laneReadsCommitted records readers whose committed-view
|
|
@@ -720,10 +868,26 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
720
868
|
// hook; the hook iterates, clears, and schedules (keeping the loop out of
|
|
721
869
|
// core lets esbuild shake it — rollup already folds the null guard). The
|
|
722
870
|
// completing transition scopes the clear to its own layer keys (#2899).
|
|
723
|
-
if (t.
|
|
871
|
+
if (t.En.size) GlobalQueue.qt(t.En, e);
|
|
872
|
+
// Held-truth reveal wake (#3164), post-revert by construction: this
|
|
873
|
+
// finalize committed confirming truth whose subscribers were masked all
|
|
874
|
+
// hold — some re-derived against the committed view (the staging, or a
|
|
875
|
+
// confirming carrier's landing, notified them as a plain write) and
|
|
876
|
+
// cached it, and stash-restored applies may carry those torn values.
|
|
877
|
+
// Waking and recomputing HERE — after _resolveOptimistic and the store
|
|
878
|
+
// clears above — means every apply paints the settled view; a wake at
|
|
879
|
+
// commit time would recompute them in the window where truth is
|
|
880
|
+
// committed but the settling transaction's overrides still display.
|
|
881
|
+
if (heldRevealed.length !== 0) {
|
|
882
|
+
while (heldRevealed.length) insertSubs(heldRevealed.pop());
|
|
883
|
+
if (dirtyQueue.EE >= dirtyQueue.Qe) {
|
|
884
|
+
runHeap(dirtyQueue, GlobalQueue.Fe);
|
|
885
|
+
commitPendingNodes();
|
|
886
|
+
}
|
|
887
|
+
}
|
|
724
888
|
sweepTransientStoreNodes();
|
|
725
889
|
// Lanes only enter activeLanes through the engine's getOrCreateLane.
|
|
726
|
-
if (activeLanes.size) GlobalQueue.
|
|
890
|
+
if (activeLanes.size) GlobalQueue.In(e);
|
|
727
891
|
}
|
|
728
892
|
}
|
|
729
893
|
|
|
@@ -752,7 +916,7 @@ function checkBoundaryChildren(e) {
|
|
|
752
916
|
|
|
753
917
|
function reassignPendingTransition(e) {
|
|
754
918
|
for (let t = 0; t < e.length; t++) {
|
|
755
|
-
e[t].
|
|
919
|
+
e[t].Ae = activeTransition;
|
|
756
920
|
}
|
|
757
921
|
}
|
|
758
922
|
|
|
@@ -780,7 +944,7 @@ function flush(e) {
|
|
|
780
944
|
}
|
|
781
945
|
}
|
|
782
946
|
}
|
|
783
|
-
if (globalQueue.
|
|
947
|
+
if (globalQueue.fn) {
|
|
784
948
|
return;
|
|
785
949
|
}
|
|
786
950
|
if (halted) return;
|
|
@@ -798,30 +962,30 @@ function runQueue(e, t) {
|
|
|
798
962
|
function reporterBlocksSource(e, t) {
|
|
799
963
|
if (e.ie & (REACTIVE_ZOMBIE | REACTIVE_DISPOSED)) return false;
|
|
800
964
|
if (e.o?.le?.has(t)) return true;
|
|
801
|
-
for (let i = e.
|
|
802
|
-
let e = i.
|
|
965
|
+
for (let i = e.ut; i; i = i.lt) {
|
|
966
|
+
let e = i.ot;
|
|
803
967
|
while (e) {
|
|
804
|
-
if (e === t || e.
|
|
805
|
-
e = e.o?.
|
|
968
|
+
if (e === t || e.st === t) return true;
|
|
969
|
+
e = e.o?.Tt;
|
|
806
970
|
}
|
|
807
971
|
}
|
|
808
972
|
return !!(e.S & STATUS_PENDING && e.o?._ instanceof NotReadyError && e.o?._.source === t);
|
|
809
973
|
}
|
|
810
974
|
|
|
811
975
|
function transitionComplete(e) {
|
|
812
|
-
if (e.
|
|
976
|
+
if (e.sn) return true;
|
|
813
977
|
if (e.ue.length) return false;
|
|
814
978
|
let t = true;
|
|
815
|
-
for (const [i, n] of e.
|
|
816
|
-
let
|
|
979
|
+
for (const [i, n] of e._e) {
|
|
980
|
+
let r = false;
|
|
817
981
|
for (const e of n) {
|
|
818
982
|
if (reporterBlocksSource(e, i)) {
|
|
819
|
-
|
|
983
|
+
r = true;
|
|
820
984
|
break;
|
|
821
985
|
}
|
|
822
986
|
n.delete(e);
|
|
823
987
|
}
|
|
824
|
-
if (!
|
|
988
|
+
if (!r) e._e.delete(i); else if (i.S & STATUS_PENDING && i.o?._?.source === i) {
|
|
825
989
|
t = false;
|
|
826
990
|
break;
|
|
827
991
|
}
|
|
@@ -829,13 +993,20 @@ function transitionComplete(e) {
|
|
|
829
993
|
// Override blockage lives with the engine (absent hook = "no optimistic
|
|
830
994
|
// blockage"); the hook's loops over _optimisticNodes/_optimisticStores are
|
|
831
995
|
// no-ops when the transition holds neither, so no pre-check is needed.
|
|
832
|
-
if (t && GlobalQueue.
|
|
833
|
-
t && (e.
|
|
996
|
+
if (t && GlobalQueue.dn?.(e)) t = false;
|
|
997
|
+
t && (e.sn = true);
|
|
834
998
|
return t;
|
|
835
999
|
}
|
|
836
1000
|
|
|
1001
|
+
/** A fresh, unentered transaction (#3146): the optimistic store's truth
|
|
1002
|
+
* flight DECLARES an owned transaction instead of relying on whatever the
|
|
1003
|
+
* ambient adoption machinery stamped on its firewall. Activate it with
|
|
1004
|
+
* initTransition; it is a plain batch until then. */ function createTransition() {
|
|
1005
|
+
return createBatch();
|
|
1006
|
+
}
|
|
1007
|
+
|
|
837
1008
|
function currentTransition(e) {
|
|
838
|
-
while (e.
|
|
1009
|
+
while (e.sn && typeof e.sn === "object") e = e.sn;
|
|
839
1010
|
return e;
|
|
840
1011
|
}
|
|
841
1012
|
|
|
@@ -849,4 +1020,27 @@ function runInTransition(e, t) {
|
|
|
849
1020
|
}
|
|
850
1021
|
}
|
|
851
1022
|
|
|
852
|
-
|
|
1023
|
+
/** Run `fn` with `transition` as BOTH the ambient transaction and the
|
|
1024
|
+
* registration batch, restoring both after. runInTransition alone is not
|
|
1025
|
+
* enough for code that WRITES on behalf of a transaction from inside someone
|
|
1026
|
+
* else's window (optimistic replay re-arming a still-open action's edits
|
|
1027
|
+
* during a landing commit, #3123): registrations route through the queue's
|
|
1028
|
+
* batch pointer, and a bare activeTransition swap leaves them in the ambient
|
|
1029
|
+
* batch — a plain batch "completes" at the next flush and reverts optimistic
|
|
1030
|
+
* registrations that were supposed to live with the transaction.
|
|
1031
|
+
* initTransition is the wrong tool here: it MERGES the currently ambient
|
|
1032
|
+
* transaction into the target, entangling whatever the interrupted window
|
|
1033
|
+
* belonged to. */ function runAsTransitionBatch(e, t) {
|
|
1034
|
+
const i = activeTransition;
|
|
1035
|
+
const n = globalQueue.m;
|
|
1036
|
+
try {
|
|
1037
|
+
activeTransition = currentTransition(e);
|
|
1038
|
+
currentBatch = globalQueue.m = activeTransition;
|
|
1039
|
+
return t();
|
|
1040
|
+
} finally {
|
|
1041
|
+
activeTransition = i;
|
|
1042
|
+
currentBatch = globalQueue.m = n;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
export { GlobalQueue, Queue, activeAffectsMarks, activeLanes, activeTransition, armReaskClear, assignOrMergeLane, bumpNotifyEpoch, clock, createTransition, currentTransition, dirtyQueue, enforceLoadingBoundary, entangleConfirmingTransitions, finalizePureQueue, findLane, flush, globalQueue, haltReactivity, hasActiveOverride, insertSubs, notifyEpoch, patchCommitHook, projectionWriteActive, queuePendingNode, reaskArmed, resetErrorHalt, runAsTransitionBatch, runInTransition, schedule, setPatchCommitHook, setProjectionWriteActive, setStoreCommitHook, shiftAffectsMarks, storeCommitHook, zombieQueue };
|