@solidjs/signals 2.0.0-rc.3 → 2.0.0-rc.5
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 +2494 -271
- package/dist/node.cjs +3691 -1595
- package/dist/prod/affects.js +13 -12
- package/dist/prod/boundaries.js +43 -35
- package/dist/prod/core/action.js +3 -3
- package/dist/prod/core/async.js +137 -106
- package/dist/prod/core/constants.js +55 -1
- package/dist/prod/core/core.js +354 -247
- package/dist/prod/core/effect.js +47 -50
- package/dist/prod/core/error.js +13 -1
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +88 -52
- package/dist/prod/core/heap.js +38 -38
- package/dist/prod/core/lanes.js +34 -34
- package/dist/prod/core/optimistic.js +61 -58
- package/dist/prod/core/owner.js +38 -38
- package/dist/prod/core/scheduler.js +401 -174
- package/dist/prod/core/verdict.js +132 -65
- package/dist/prod/index.js +7 -3
- package/dist/prod/map.js +106 -106
- package/dist/prod/signals.js +253 -25
- package/dist/prod/store/index.js +2 -0
- package/dist/prod/store/next/optimistic.js +314 -121
- package/dist/prod/store/next/patch-hooks.js +13 -0
- package/dist/prod/store/next/patch.js +614 -0
- package/dist/prod/store/next/projection.js +23 -19
- package/dist/prod/store/next/reconcile.js +307 -120
- package/dist/prod/store/next/store.js +440 -117
- package/dist/prod/store/next/target.js +13 -4
- package/dist/prod/store/store.js +5 -5
- package/dist/types/core/async.d.ts +2 -0
- package/dist/types/core/attribution.d.ts +9 -4
- package/dist/types/core/constants.d.ts +54 -0
- package/dist/types/core/core.d.ts +34 -21
- package/dist/types/core/dev.d.ts +8 -0
- package/dist/types/core/error.d.ts +9 -0
- package/dist/types/core/graph.d.ts +22 -0
- package/dist/types/core/index.d.ts +2 -2
- package/dist/types/core/scheduler.d.ts +46 -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 -0
- package/dist/types/store/index.d.ts +2 -0
- package/dist/types/store/next/optimistic.d.ts +13 -10
- package/dist/types/store/next/patch-hooks.d.ts +41 -0
- package/dist/types/store/next/patch.d.ts +91 -0
- package/dist/types/store/next/projection.d.ts +1 -1
- package/dist/types/store/next/reconcile.d.ts +14 -0
- package/dist/types/store/next/store.d.ts +52 -2
- package/dist/types/store/next/target.d.ts +73 -8
- package/dist/types-cjs/core/async.d.cts +2 -0
- package/dist/types-cjs/core/attribution.d.cts +9 -4
- package/dist/types-cjs/core/constants.d.cts +54 -0
- package/dist/types-cjs/core/core.d.cts +34 -21
- package/dist/types-cjs/core/dev.d.cts +8 -0
- package/dist/types-cjs/core/error.d.cts +9 -0
- package/dist/types-cjs/core/graph.d.cts +22 -0
- package/dist/types-cjs/core/index.d.cts +2 -2
- package/dist/types-cjs/core/scheduler.d.cts +46 -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 -0
- package/dist/types-cjs/store/index.d.cts +2 -0
- package/dist/types-cjs/store/next/optimistic.d.cts +13 -10
- package/dist/types-cjs/store/next/patch-hooks.d.cts +41 -0
- package/dist/types-cjs/store/next/patch.d.cts +91 -0
- package/dist/types-cjs/store/next/projection.d.cts +1 -1
- package/dist/types-cjs/store/next/reconcile.d.cts +14 -0
- package/dist/types-cjs/store/next/store.d.cts +52 -2
- package/dist/types-cjs/store/next/target.d.cts +73 -8
- package/package.json +2 -2
|
@@ -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
|
|
|
@@ -6,11 +6,13 @@ import { DEV } from "./dev.js";
|
|
|
6
6
|
|
|
7
7
|
import { NotReadyError } from "./error.js";
|
|
8
8
|
|
|
9
|
+
import { sweepDormant } from "./graph.js";
|
|
10
|
+
|
|
9
11
|
import { runHeap, deleteFromHeap, enqueueSub } from "./heap.js";
|
|
10
12
|
|
|
11
|
-
import { activeLanes, assignOrMergeLane, findLane } from "./lanes.js";
|
|
13
|
+
import { activeLanes, assignOrMergeLane, findLane, hasActiveOverride } from "./lanes.js";
|
|
12
14
|
|
|
13
|
-
export { getOrCreateLane,
|
|
15
|
+
export { getOrCreateLane, mergeLanes, resolveLane } from "./lanes.js";
|
|
14
16
|
|
|
15
17
|
import { devCheckFlushStart, devCheckActiveOverrides, devCensusCompanions, devCheckQuiescent } from "./invariants.js";
|
|
16
18
|
|
|
@@ -19,14 +21,14 @@ const transitions = new Set;
|
|
|
19
21
|
const dirtyQueue = {
|
|
20
22
|
eE: new Array(2e3).fill(undefined),
|
|
21
23
|
tE: false,
|
|
22
|
-
|
|
24
|
+
Qe: 0,
|
|
23
25
|
EE: 0
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
const zombieQueue = {
|
|
27
29
|
eE: new Array(2e3).fill(undefined),
|
|
28
30
|
tE: false,
|
|
29
|
-
|
|
31
|
+
Qe: 0,
|
|
30
32
|
EE: 0
|
|
31
33
|
};
|
|
32
34
|
|
|
@@ -60,8 +62,8 @@ let projectionWriteActive = false;
|
|
|
60
62
|
const transientStoreNodes = new Set;
|
|
61
63
|
|
|
62
64
|
function canUseSimpleSyncFlush(e) {
|
|
63
|
-
const t = e.
|
|
64
|
-
return transitions.size === 0 && activeLanes.size === 0 && e.Qt.length === 0 && t.
|
|
65
|
+
const t = e.m;
|
|
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;
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
function sweepTransientStoreNodes() {
|
|
@@ -72,13 +74,13 @@ function sweepTransientStoreNodes() {
|
|
|
72
74
|
continue;
|
|
73
75
|
}
|
|
74
76
|
if (e.Pe !== NOT_PENDING) continue;
|
|
75
|
-
if (e.o?.
|
|
77
|
+
if (e.o?.Oe !== undefined && e.o?.Oe !== NOT_PENDING) continue;
|
|
76
78
|
// A live affects() mark keeps the node addressable: sweeping it would
|
|
77
79
|
// detach the refcount from the slot (a fresh probe would upsert a new,
|
|
78
80
|
// unmarked node for the same property).
|
|
79
81
|
if (e.o?.t) continue;
|
|
80
82
|
transientStoreNodes.delete(e);
|
|
81
|
-
e.o?.
|
|
83
|
+
e.o?.Et?.();
|
|
82
84
|
}
|
|
83
85
|
}
|
|
84
86
|
|
|
@@ -106,46 +108,162 @@ function setProjectionWriteActive(e) {
|
|
|
106
108
|
*/ function createBatch() {
|
|
107
109
|
return {
|
|
108
110
|
Te: clock,
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
111
|
+
Lt: [],
|
|
112
|
+
_e: new Map,
|
|
113
|
+
ze: [],
|
|
114
|
+
A: [],
|
|
115
|
+
En: new Set,
|
|
116
|
+
ue: [],
|
|
117
|
+
Bt: {
|
|
118
|
+
Mt: [ [], [] ],
|
|
117
119
|
Qt: []
|
|
118
120
|
},
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
sn: false,
|
|
122
|
+
cn: new Set
|
|
121
123
|
};
|
|
122
124
|
}
|
|
123
125
|
|
|
124
126
|
function mergeTransitionState(e, t) {
|
|
125
|
-
t.
|
|
126
|
-
e.
|
|
127
|
-
for (const i of activeLanes) if (i.
|
|
128
|
-
if (t.
|
|
127
|
+
t.sn = e;
|
|
128
|
+
e.ue.push(...t.ue);
|
|
129
|
+
for (const i of activeLanes) if (i.Ae === t) i.Ae = e;
|
|
130
|
+
if (t.ze.length) {
|
|
129
131
|
// Move (don't copy): the global queue's batch may still be the outgoing
|
|
130
132
|
// transition, and the adoption pass in initTransition would re-push its
|
|
131
133
|
// contents into the target — duplicating every entry.
|
|
132
|
-
e.
|
|
133
|
-
t.
|
|
134
|
+
e.ze.push(...t.ze);
|
|
135
|
+
t.ze.length = 0;
|
|
134
136
|
}
|
|
135
|
-
if (t.
|
|
137
|
+
if (t.A.length) {
|
|
136
138
|
// Move (don't copy): the global queue's batch may still be the outgoing
|
|
137
139
|
// transition, and the adoption pass in initTransition would re-push its
|
|
138
140
|
// contents into the target — double-releasing every mark.
|
|
139
|
-
e.
|
|
140
|
-
t.
|
|
141
|
+
e.A.push(...t.A);
|
|
142
|
+
t.A.length = 0;
|
|
141
143
|
}
|
|
142
|
-
for (const i of t.
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
for (const i of t.En) e.En.add(i);
|
|
145
|
+
// Patch-channel stash (store/next/patch.ts): entries held for the outgoing
|
|
146
|
+
// transition must ride the merge like every other per-transition
|
|
147
|
+
// collection — releaseBatch only reads the COMMITTING transition's stash,
|
|
148
|
+
// so a stranded sidecar would silently drop its patches. Move (don't
|
|
149
|
+
// copy), same aliasing rule as the collections above. The field is an
|
|
150
|
+
// expando so this module stays free of patch imports (pay-for-use).
|
|
151
|
+
const i = t.wt;
|
|
152
|
+
if (i !== undefined) {
|
|
153
|
+
t.wt = undefined;
|
|
154
|
+
let n = e.wt;
|
|
155
|
+
if (n !== undefined) n.push(...i); else n = e.wt = i;
|
|
156
|
+
// Retarget the entries' coalescing stamps to the surviving stash
|
|
157
|
+
// (opaque backref contract with store/next/patch.ts): without this a
|
|
158
|
+
// post-merge emission misses the stamp and pushes a SECOND entry —
|
|
159
|
+
// the record's patch applies twice at commit (re-audit 5, P1-2).
|
|
160
|
+
for (let e = 0; e < i.length; e++) {
|
|
161
|
+
const t = i[e].pc;
|
|
162
|
+
if (t !== undefined && t.qe === i[e]) t.qa = n;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
for (const [i, n] of t._e) {
|
|
166
|
+
let t = e._e.get(i);
|
|
167
|
+
if (!t) e._e.set(i, t = new Set);
|
|
146
168
|
for (const e of n) t.add(e);
|
|
147
169
|
}
|
|
148
|
-
for (const i of t.
|
|
170
|
+
for (const i of t.cn) e.cn.add(i);
|
|
171
|
+
}
|
|
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 s = n.ot;
|
|
206
|
+
if (s.Pe !== NOT_PENDING) {
|
|
207
|
+
const e = s.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 r = n === null ? currentBatch.Lt : n !== t && n.sn !== true ? n.Lt : null;
|
|
216
|
+
if (r !== null) i = stealEntangledCargo(r, 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.he && !(t.T & CONFIG_AUTHORITATIVE_READ)) enqueueSub(t);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
e.length = 0;
|
|
265
|
+
transitions.add(t);
|
|
266
|
+
return true;
|
|
149
267
|
}
|
|
150
268
|
|
|
151
269
|
function schedule() {
|
|
@@ -155,7 +273,7 @@ function schedule() {
|
|
|
155
273
|
}
|
|
156
274
|
if (scheduled) return;
|
|
157
275
|
scheduled = true;
|
|
158
|
-
if (!syncDepth && !globalQueue.
|
|
276
|
+
if (!syncDepth && !globalQueue.fn && !projectionWriteActive) queueMicrotask(flush);
|
|
159
277
|
}
|
|
160
278
|
|
|
161
279
|
/**
|
|
@@ -189,30 +307,30 @@ function notifyHalted() {
|
|
|
189
307
|
let queueRunToken = 0;
|
|
190
308
|
|
|
191
309
|
class Queue {
|
|
192
|
-
|
|
193
|
-
|
|
310
|
+
ke=null;
|
|
311
|
+
Mt=[ [], [] ];
|
|
194
312
|
Qt=[];
|
|
195
|
-
|
|
313
|
+
jt=0;
|
|
196
314
|
created=clock;
|
|
197
315
|
addChild(e) {
|
|
198
316
|
this.Qt.push(e);
|
|
199
|
-
e.
|
|
317
|
+
e.ke = this;
|
|
200
318
|
}
|
|
201
319
|
removeChild(e) {
|
|
202
320
|
const t = this.Qt.indexOf(e);
|
|
203
321
|
if (t >= 0) {
|
|
204
322
|
this.Qt.splice(t, 1);
|
|
205
|
-
e.
|
|
323
|
+
e.ke = null;
|
|
206
324
|
}
|
|
207
325
|
}
|
|
208
326
|
notify(e, t, i, n) {
|
|
209
|
-
if (this.
|
|
327
|
+
if (this.ke) return this.ke.notify(e, t, i, n);
|
|
210
328
|
return false;
|
|
211
329
|
}
|
|
212
330
|
run(e) {
|
|
213
|
-
if (this.
|
|
214
|
-
const t = this.
|
|
215
|
-
this.
|
|
331
|
+
if (this.Mt[e - 1].length) {
|
|
332
|
+
const t = this.Mt[e - 1];
|
|
333
|
+
this.Mt[e - 1] = [];
|
|
216
334
|
runQueue(t, e);
|
|
217
335
|
}
|
|
218
336
|
// Effects run here can dispose owners, and disposal removes queues from
|
|
@@ -226,8 +344,8 @@ class Queue {
|
|
|
226
344
|
const i = ++queueRunToken;
|
|
227
345
|
for (let n = 0; n < t.length; ) {
|
|
228
346
|
const s = t[n];
|
|
229
|
-
if (s.
|
|
230
|
-
s.
|
|
347
|
+
if (s.jt !== i) {
|
|
348
|
+
s.jt = i;
|
|
231
349
|
s.run?.(e);
|
|
232
350
|
if (t[n] !== s) {
|
|
233
351
|
n = 0;
|
|
@@ -242,23 +360,23 @@ class Queue {
|
|
|
242
360
|
// Route to lane's effect queue if we're in an optimistic recomputation
|
|
243
361
|
if (currentOptimisticLane) {
|
|
244
362
|
const i = findLane(currentOptimisticLane);
|
|
245
|
-
i.
|
|
363
|
+
i.tn[e - 1].push(t);
|
|
246
364
|
} else {
|
|
247
|
-
this.
|
|
365
|
+
this.Mt[e - 1].push(t);
|
|
248
366
|
}
|
|
249
367
|
}
|
|
250
368
|
schedule();
|
|
251
369
|
}
|
|
252
370
|
stashQueues(e) {
|
|
253
|
-
e.
|
|
254
|
-
e.
|
|
255
|
-
this.
|
|
371
|
+
e.Mt[0].push(...this.Mt[0]);
|
|
372
|
+
e.Mt[1].push(...this.Mt[1]);
|
|
373
|
+
this.Mt = [ [], [] ];
|
|
256
374
|
for (let t = 0; t < this.Qt.length; t++) {
|
|
257
375
|
let i = this.Qt[t];
|
|
258
376
|
let n = e.Qt[t];
|
|
259
377
|
if (!n) {
|
|
260
378
|
n = {
|
|
261
|
-
|
|
379
|
+
Mt: [ [], [] ],
|
|
262
380
|
Qt: []
|
|
263
381
|
};
|
|
264
382
|
e.Qt[t] = n;
|
|
@@ -267,8 +385,8 @@ class Queue {
|
|
|
267
385
|
}
|
|
268
386
|
}
|
|
269
387
|
restoreQueues(e) {
|
|
270
|
-
this.
|
|
271
|
-
this.
|
|
388
|
+
this.Mt[0].push(...e.Mt[0]);
|
|
389
|
+
this.Mt[1].push(...e.Mt[1]);
|
|
272
390
|
for (let t = 0; t < e.Qt.length; t++) {
|
|
273
391
|
const i = e.Qt[t];
|
|
274
392
|
let n = this.Qt[t];
|
|
@@ -278,48 +396,48 @@ class Queue {
|
|
|
278
396
|
}
|
|
279
397
|
|
|
280
398
|
class GlobalQueue extends Queue {
|
|
281
|
-
|
|
399
|
+
fn=false;
|
|
282
400
|
// The current transaction-shaped batch: a plain ambient batch while no
|
|
283
401
|
// transition is active, the active transition itself after initTransition.
|
|
284
|
-
|
|
285
|
-
static Ce;
|
|
402
|
+
m=createBatch();
|
|
286
403
|
static Fe;
|
|
287
|
-
static
|
|
288
|
-
static
|
|
404
|
+
static He;
|
|
405
|
+
static it;
|
|
406
|
+
static qt=null;
|
|
289
407
|
// Store-side hook: drops a keyless affects() mark's identity scope when the
|
|
290
408
|
// carrier node's last registration releases (wired by store.ts, mirroring
|
|
291
409
|
// _clearOptimisticStore).
|
|
292
|
-
static
|
|
410
|
+
static p=null;
|
|
293
411
|
// affects()-side hooks (wired by affects.ts, mirroring _update): the mark
|
|
294
412
|
// engine — count/register/release — lives with the feature. Every call site
|
|
295
413
|
// is gated by state only that module creates, so `!` invocations are safe
|
|
296
414
|
// once the gate holds.
|
|
415
|
+
static G=null;
|
|
297
416
|
static M=null;
|
|
298
|
-
static
|
|
299
|
-
static j=null;
|
|
417
|
+
static N=null;
|
|
300
418
|
// External-source bridge (wired by enableExternalSource(); null while no
|
|
301
419
|
// config is active — including after _resetExternalSourceConfig()).
|
|
302
|
-
static
|
|
303
|
-
static
|
|
420
|
+
static Pt=null;
|
|
421
|
+
static ht=null;
|
|
304
422
|
// Verdict-layer hooks (wired by verdict.ts when isPending()/latest() are
|
|
305
423
|
// imported; null in apps that never use them). Call sites either guard for
|
|
306
424
|
// null or sit behind state only the verdict layer can create (`!` is safe
|
|
307
425
|
// there: `_pendingSignal`/`_latestValueComputed` are only ever assigned by
|
|
308
426
|
// verdict.ts, and `pendingCheckActive`/`latestReadActive` only flip inside
|
|
309
427
|
// isPending()/latest()).
|
|
310
|
-
static
|
|
428
|
+
static Re=null;
|
|
311
429
|
static de=null;
|
|
312
|
-
static
|
|
430
|
+
static me=null;
|
|
313
431
|
static un=null;
|
|
314
|
-
static
|
|
315
|
-
static Dt=null;
|
|
432
|
+
static gt=null;
|
|
316
433
|
static Ht=null;
|
|
317
|
-
static
|
|
318
|
-
static
|
|
319
|
-
static
|
|
434
|
+
static kt=null;
|
|
435
|
+
static et=null;
|
|
436
|
+
static k=null;
|
|
437
|
+
static Wt=null;
|
|
320
438
|
// Re-asks probes whose verdict was provisionally suppressed by a fresh read
|
|
321
439
|
// of a held value, once the transaction gains an async blocker (#3028).
|
|
322
|
-
static
|
|
440
|
+
static zt=null;
|
|
323
441
|
// Optimistic-engine hooks (wired by core/optimistic.ts via
|
|
324
442
|
// installOptimisticEngine(), called from verdict.ts / createOptimistic /
|
|
325
443
|
// createOptimisticStore — every module that can create optimistic state).
|
|
@@ -327,40 +445,56 @@ class GlobalQueue extends Queue {
|
|
|
327
445
|
// `_overrideValue` slot, a lane in `activeLanes`, an `_optimisticNodes`
|
|
328
446
|
// entry, or a non-null `currentOptimisticLane`, so `!` invocations are safe
|
|
329
447
|
// once the gate holds.
|
|
330
|
-
static
|
|
331
|
-
static fn=null;
|
|
332
|
-
static En=null;
|
|
448
|
+
static xt=null;
|
|
333
449
|
static Tn=null;
|
|
334
450
|
static dn=null;
|
|
335
|
-
static Ft=null;
|
|
336
|
-
static ht=null;
|
|
337
|
-
static gt=null;
|
|
338
|
-
static je=null;
|
|
339
|
-
static $e=null;
|
|
340
|
-
static ze=null;
|
|
341
451
|
static In=null;
|
|
452
|
+
static Nn=null;
|
|
453
|
+
/** Patch-channel optimistic drain (next/patch.ts): optimistic emissions
|
|
454
|
+
* apply at lane-effect timing — visible in flight, unlike the regular
|
|
455
|
+
* effect queues an action stashes. Injected; null when unused. */
|
|
456
|
+
static ln=null;
|
|
457
|
+
static vt=null;
|
|
458
|
+
static Vt=null;
|
|
459
|
+
static bt=null;
|
|
460
|
+
static Be=null;
|
|
461
|
+
static $e=null;
|
|
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 Ue=null;
|
|
466
|
+
static Xe=null;
|
|
467
|
+
static _n=null;
|
|
342
468
|
flush() {
|
|
343
|
-
if (this.
|
|
469
|
+
if (this.fn) return;
|
|
344
470
|
// Fast drain: nothing in flight but plain pending commits — no dirty
|
|
345
471
|
// computeds, no queued effects, no child queues, no transitions/lanes/
|
|
346
472
|
// optimistic state. Commit and go; anything a commit hook schedules
|
|
347
473
|
// (companion snaps, store folds notifying subs) re-arms `scheduled`
|
|
348
474
|
// below and the outer drain loop takes the full spine next round.
|
|
349
|
-
if (activeTransition === null && dirtyQueue.EE < dirtyQueue.
|
|
350
|
-
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;
|
|
351
477
|
try {
|
|
478
|
+
// Sweep first: unobserved() pulls swept nodes out of the dirty heap,
|
|
479
|
+
// so a dormant memo dirtied in the same tick is reclaimed instead of
|
|
480
|
+
// recomputed (matching the old inline dispose-on-read counts).
|
|
481
|
+
sweepDormant();
|
|
352
482
|
commitPendingNodes();
|
|
353
483
|
} finally {
|
|
354
|
-
this.
|
|
484
|
+
this.fn = false;
|
|
355
485
|
}
|
|
356
486
|
clock++;
|
|
357
|
-
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;
|
|
358
488
|
return;
|
|
359
489
|
}
|
|
360
|
-
this.
|
|
490
|
+
this.fn = true;
|
|
361
491
|
try {
|
|
362
492
|
if (false) ;
|
|
363
|
-
runHeap
|
|
493
|
+
// Before runHeap for the same reason as the fast drain above; late
|
|
494
|
+
// subscribers (an effect reading a swept memo this flush) revive it,
|
|
495
|
+
// which is the pay-for-use contract.
|
|
496
|
+
sweepDormant();
|
|
497
|
+
runHeap(dirtyQueue, GlobalQueue.Fe);
|
|
364
498
|
if (activeTransition) {
|
|
365
499
|
const e = transitionComplete(activeTransition);
|
|
366
500
|
if (!e) {
|
|
@@ -373,74 +507,74 @@ class GlobalQueue extends Queue {
|
|
|
373
507
|
// zombies here. Height-adjust entries still process normally: a
|
|
374
508
|
// dirtied one keeps its height flag and falls through to runHeap's
|
|
375
509
|
// adjustHeight path on the next pass of the bucket.
|
|
376
|
-
runHeap(zombieQueue, this.
|
|
510
|
+
runHeap(zombieQueue, this.m === e ? cancelZombieRecompute : GlobalQueue.Fe);
|
|
377
511
|
// Detach: the stashed transition keeps its batch; ambient work that
|
|
378
512
|
// follows lands in a fresh one. If the batch is already a separate
|
|
379
513
|
// ambient one — action done() restored activeTransition without
|
|
380
514
|
// adopting the batch, and an ordinary write landed there before
|
|
381
515
|
// the scheduled flush (#2916) — keep it: replacing it would strand
|
|
382
516
|
// its queued pending nodes with held _pendingValues forever.
|
|
383
|
-
if (this.
|
|
517
|
+
if (this.m === e) currentBatch = this.m = createBatch();
|
|
384
518
|
// Run lane effects immediately (before stashing) - lanes with no pending async
|
|
385
519
|
if (activeLanes.size) {
|
|
386
|
-
GlobalQueue.
|
|
387
|
-
GlobalQueue.
|
|
520
|
+
GlobalQueue.Nn(EFFECT_RENDER);
|
|
521
|
+
GlobalQueue.Nn(EFFECT_USER);
|
|
388
522
|
}
|
|
389
|
-
this.stashQueues(e.
|
|
523
|
+
this.stashQueues(e.Bt);
|
|
390
524
|
clock++;
|
|
391
525
|
// A kept ambient batch may hold pending nodes (#2916): stay
|
|
392
526
|
// scheduled so the outer drain loop commits them via the plain
|
|
393
527
|
// flush path instead of leaving them until the next natural flush.
|
|
394
|
-
scheduled = dirtyQueue.EE >= dirtyQueue.
|
|
395
|
-
reassignPendingTransition(e.
|
|
528
|
+
scheduled = dirtyQueue.EE >= dirtyQueue.Qe || this.m.Lt.length > 0;
|
|
529
|
+
reassignPendingTransition(e.Lt);
|
|
396
530
|
activeTransition = null;
|
|
397
531
|
finalizePureQueue(null, true);
|
|
398
532
|
return;
|
|
399
533
|
}
|
|
400
534
|
const t = activeTransition;
|
|
401
|
-
const i = this.
|
|
402
|
-
i !== t && i.
|
|
403
|
-
this.restoreQueues(t.
|
|
535
|
+
const i = this.m;
|
|
536
|
+
i !== t && i.Lt.push(...t.Lt);
|
|
537
|
+
this.restoreQueues(t.Bt);
|
|
404
538
|
transitions.delete(t);
|
|
405
539
|
activeTransition = null;
|
|
406
|
-
reassignPendingTransition(i.
|
|
540
|
+
reassignPendingTransition(i.Lt);
|
|
407
541
|
finalizePureQueue(t);
|
|
408
542
|
if (i === t) {
|
|
409
543
|
// Drop the dead Transition wrapper but keep its (drained) containers
|
|
410
544
|
// as the ambient batch — late registrations during finalization live
|
|
411
545
|
// there and must survive to the next flush.
|
|
412
546
|
const e = createBatch();
|
|
413
|
-
e.
|
|
414
|
-
e.
|
|
415
|
-
e.
|
|
416
|
-
e.
|
|
417
|
-
currentBatch = this.
|
|
547
|
+
e.Lt = i.Lt;
|
|
548
|
+
e.ze = i.ze;
|
|
549
|
+
e.A = i.A;
|
|
550
|
+
e.En = i.En;
|
|
551
|
+
currentBatch = this.m = e;
|
|
418
552
|
}
|
|
419
553
|
} else {
|
|
420
554
|
if (canUseSimpleSyncFlush(this)) {
|
|
421
555
|
commitPendingNodes();
|
|
422
|
-
if (dirtyQueue.EE >= dirtyQueue.
|
|
423
|
-
runHeap(dirtyQueue, GlobalQueue.
|
|
556
|
+
if (dirtyQueue.EE >= dirtyQueue.Qe) {
|
|
557
|
+
runHeap(dirtyQueue, GlobalQueue.Fe);
|
|
424
558
|
commitPendingNodes();
|
|
425
559
|
}
|
|
426
560
|
} else {
|
|
427
|
-
if (transitions.size) runHeap(zombieQueue, GlobalQueue.
|
|
561
|
+
if (transitions.size) runHeap(zombieQueue, GlobalQueue.Fe);
|
|
428
562
|
finalizePureQueue();
|
|
429
563
|
}
|
|
430
564
|
}
|
|
431
565
|
clock++;
|
|
432
566
|
// Check if finalization added items to the heap (from optimistic reversion)
|
|
433
|
-
scheduled = dirtyQueue.EE >= dirtyQueue.
|
|
567
|
+
scheduled = dirtyQueue.EE >= dirtyQueue.Qe;
|
|
434
568
|
// Run lane effects first (for ready lanes), then regular effects
|
|
435
|
-
activeLanes.size && GlobalQueue.
|
|
569
|
+
activeLanes.size && GlobalQueue.Nn(EFFECT_RENDER);
|
|
436
570
|
this.run(EFFECT_RENDER);
|
|
437
|
-
activeLanes.size && GlobalQueue.
|
|
571
|
+
activeLanes.size && GlobalQueue.Nn(EFFECT_USER);
|
|
438
572
|
this.run(EFFECT_USER);
|
|
439
573
|
if (false) ;
|
|
440
574
|
if (false && !scheduled && !activeTransition && transitions.size === 0 && activeLanes.size === 0) ;
|
|
441
575
|
if (false) ;
|
|
442
576
|
} finally {
|
|
443
|
-
this.
|
|
577
|
+
this.fn = false;
|
|
444
578
|
}
|
|
445
579
|
}
|
|
446
580
|
notify(e, t, i, n) {
|
|
@@ -452,16 +586,16 @@ class GlobalQueue extends Queue {
|
|
|
452
586
|
// channel) updates display state on its way up but must be invisible
|
|
453
587
|
// to completion accounting BY CONSTRUCTION: it never registers a
|
|
454
588
|
// reporter and never counts toward the loading-boundary diagnostic.
|
|
455
|
-
if (t?.
|
|
589
|
+
if (t?.l) return true;
|
|
456
590
|
if (activeTransition && t) {
|
|
457
591
|
const i = t.source;
|
|
458
|
-
let n = activeTransition.
|
|
459
|
-
if (!n) activeTransition.
|
|
592
|
+
let n = activeTransition._e.get(i);
|
|
593
|
+
if (!n) activeTransition._e.set(i, n = new Set);
|
|
460
594
|
const s = n.size;
|
|
461
595
|
n.add(e);
|
|
462
596
|
if (n.size !== s) {
|
|
463
597
|
schedule();
|
|
464
|
-
GlobalQueue.
|
|
598
|
+
GlobalQueue.zt?.(activeTransition);
|
|
465
599
|
}
|
|
466
600
|
}
|
|
467
601
|
}
|
|
@@ -470,8 +604,17 @@ class GlobalQueue extends Queue {
|
|
|
470
604
|
return false;
|
|
471
605
|
}
|
|
472
606
|
initTransition(e) {
|
|
473
|
-
if (e)
|
|
474
|
-
|
|
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
|
+
}
|
|
475
618
|
if (!e && activeTransition && activeTransition.Te === clock) return;
|
|
476
619
|
if (!activeTransition) {
|
|
477
620
|
activeTransition = e ?? createBatch();
|
|
@@ -483,7 +626,7 @@ class GlobalQueue extends Queue {
|
|
|
483
626
|
}
|
|
484
627
|
transitions.add(activeTransition);
|
|
485
628
|
activeTransition.Te = clock;
|
|
486
|
-
const t = this.
|
|
629
|
+
const t = this.m;
|
|
487
630
|
if (t !== activeTransition) {
|
|
488
631
|
// Adopt the ambient batch into the transaction, then make the
|
|
489
632
|
// transaction the batch so later registrations land there directly.
|
|
@@ -492,34 +635,43 @@ class GlobalQueue extends Queue {
|
|
|
492
635
|
// must not entangle unrelated writes to it; the same rule holds one hop
|
|
493
636
|
// downstream: propagation never queues pended subscribers as pending
|
|
494
637
|
// nodes, see propagateAffectsMark, #2893.
|
|
495
|
-
for (let e = 0; e < t.
|
|
496
|
-
const i = t.
|
|
497
|
-
i.
|
|
498
|
-
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);
|
|
499
642
|
}
|
|
500
|
-
for (let e = 0; e < t.
|
|
501
|
-
const i = t.
|
|
502
|
-
i.
|
|
503
|
-
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);
|
|
504
647
|
}
|
|
505
|
-
if (t.
|
|
506
|
-
for (const e of t.
|
|
648
|
+
if (t.A.length) activeTransition.A.push(...t.A);
|
|
649
|
+
for (const e of t.En) activeTransition.En.add(e);
|
|
507
650
|
// Gated readers recorded against the ambient batch move with it: their
|
|
508
651
|
// replay-at-commit now happens at the transaction's completion.
|
|
509
|
-
if (t.
|
|
510
|
-
for (const e of t.
|
|
511
|
-
t.
|
|
652
|
+
if (t.cn.size) {
|
|
653
|
+
for (const e of t.cn) activeTransition.cn.add(e);
|
|
654
|
+
t.cn.clear();
|
|
512
655
|
}
|
|
513
|
-
currentBatch = this.
|
|
656
|
+
currentBatch = this.m = activeTransition;
|
|
514
657
|
}
|
|
515
658
|
for (const e of activeLanes) {
|
|
516
|
-
if (!e.
|
|
659
|
+
if (!e.Ae) e.Ae = activeTransition;
|
|
517
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();
|
|
518
670
|
}
|
|
519
671
|
}
|
|
520
672
|
|
|
521
673
|
function queuePendingNode(e) {
|
|
522
|
-
currentBatch.
|
|
674
|
+
currentBatch.Lt.push(e);
|
|
523
675
|
}
|
|
524
676
|
|
|
525
677
|
// Sticky: flips true on the first refresh() ever (the only setter of
|
|
@@ -543,7 +695,7 @@ function armReaskClear() {
|
|
|
543
695
|
function insertSubs(e, t = false) {
|
|
544
696
|
// §12d: stamp before walking — setSignal's staged-rewrite fast path skips
|
|
545
697
|
// the next walk for this node while the epoch holds (marking is idempotent).
|
|
546
|
-
e.
|
|
698
|
+
e.It = notifyEpoch;
|
|
547
699
|
// Get source lane: prefer node's own lane over current context
|
|
548
700
|
// This is important for isPending signals which need their own lane to flush immediately
|
|
549
701
|
// Presence bits gate the optional-slot probes (see constants.ts): one
|
|
@@ -551,14 +703,14 @@ function insertSubs(e, t = false) {
|
|
|
551
703
|
// lookups in the hottest notify loop. Bits are sticky — the field read
|
|
552
704
|
// stays authoritative when a bit is set.
|
|
553
705
|
const i = e.T;
|
|
554
|
-
const n = (i & CONFIG_HAS_LANE ? e.o?.
|
|
555
|
-
const s = (i & CONFIG_HAS_SNAPSHOT) !== 0 && e.o?.
|
|
556
|
-
const
|
|
557
|
-
for (let i = e.u; i !== null; i = i.
|
|
558
|
-
const e = i.
|
|
706
|
+
const n = (i & CONFIG_HAS_LANE ? e.o?.Je : undefined) || currentOptimisticLane;
|
|
707
|
+
const s = (i & CONFIG_HAS_SNAPSHOT) !== 0 && e.o?.We !== undefined;
|
|
708
|
+
const r = reaskArmed;
|
|
709
|
+
for (let i = e.u; i !== null; i = i.ae) {
|
|
710
|
+
const e = i.ce;
|
|
559
711
|
// A value-change notification is a new question for the subscriber: any
|
|
560
712
|
// pending re-ask mark (refresh) it carried is superseded.
|
|
561
|
-
if (
|
|
713
|
+
if (r) e.ie &= ~REACTIVE_REASK;
|
|
562
714
|
// Missed-wake latch (#3037): this write is landing while the subscriber
|
|
563
715
|
// is mid-recompute (a nested pull committing beneath its reads), and the
|
|
564
716
|
// heap refuses RECOMPUTING nodes. A gen-current link means the pass
|
|
@@ -567,7 +719,7 @@ function insertSubs(e, t = false) {
|
|
|
567
719
|
// pass either re-reads them fresh or trims them), and neither does the
|
|
568
720
|
// tail link: it is the read IN FLIGHT — read() links before it pulls, so
|
|
569
721
|
// this very commit is what that read returns.
|
|
570
|
-
if (e.ie & REACTIVE_RECOMPUTING_DEPS && i.
|
|
722
|
+
if (e.ie & REACTIVE_RECOMPUTING_DEPS && i.Ft === e.Ke && i !== e.je) e.ie |= REACTIVE_MISSED_WAKE;
|
|
571
723
|
if (s && e.T & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
572
724
|
e.ie |= REACTIVE_SNAPSHOT_STALE;
|
|
573
725
|
continue;
|
|
@@ -578,7 +730,7 @@ function insertSubs(e, t = false) {
|
|
|
578
730
|
} else if (t) {
|
|
579
731
|
e.ie |= REACTIVE_OPTIMISTIC_DIRTY;
|
|
580
732
|
// No source lane means reversion - clear subscriber's lane so effects go to regular queue
|
|
581
|
-
if (e.o) e.o.
|
|
733
|
+
if (e.o) e.o.Je = undefined;
|
|
582
734
|
}
|
|
583
735
|
enqueueSub(e);
|
|
584
736
|
}
|
|
@@ -586,7 +738,7 @@ function insertSubs(e, t = false) {
|
|
|
586
738
|
|
|
587
739
|
function commitPendingNode(e) {
|
|
588
740
|
const t = e;
|
|
589
|
-
if (!t.
|
|
741
|
+
if (!t.oe) {
|
|
590
742
|
if (e.Pe !== NOT_PENDING) {
|
|
591
743
|
e.be = e.Pe;
|
|
592
744
|
e.Pe = NOT_PENDING;
|
|
@@ -598,15 +750,15 @@ function commitPendingNode(e) {
|
|
|
598
750
|
e.be = e.Pe;
|
|
599
751
|
e.Pe = NOT_PENDING;
|
|
600
752
|
// Set _modified for effects, but not for tracked effects (they handle their own scheduling)
|
|
601
|
-
if (e.
|
|
753
|
+
if (e.he && e.he !== EFFECT_TRACKED) e.tt = true;
|
|
602
754
|
}
|
|
603
755
|
// The committed hold is the first observable answer for a loading-window
|
|
604
756
|
// node — the window closes here, not at compute time (#2990). Unconditional
|
|
605
757
|
// store to an always-present computed slot.
|
|
606
|
-
t.
|
|
758
|
+
t.Ne = false;
|
|
607
759
|
t.ie &= ~REACTIVE_MANUAL_WRITE;
|
|
608
760
|
if (!(t.S & STATUS_PENDING)) t.S &= ~STATUS_UNINITIALIZED;
|
|
609
|
-
if (t.o != null && (t.o.
|
|
761
|
+
if (t.o != null && (t.o.Ye !== null || t.o.qe !== null)) GlobalQueue.He(t, false, true);
|
|
610
762
|
if (e.T & CONFIG_HAS_COMPANIONS) GlobalQueue.un(e);
|
|
611
763
|
}
|
|
612
764
|
|
|
@@ -621,13 +773,49 @@ function setStoreCommitHook(e) {
|
|
|
621
773
|
storeCommitHook = e;
|
|
622
774
|
}
|
|
623
775
|
|
|
776
|
+
/** Patch-channel release hook (next/patch.ts): transition-stamped patch
|
|
777
|
+
* emissions are released when THEIR batch commits. Transitions never
|
|
778
|
+
* abort: failed actions still commit (only optimistic overrides revert),
|
|
779
|
+
* and merged-away transitions hand their stash to the survivor
|
|
780
|
+
* (mergeTransitionState) — every stash drains exactly once. Injected like
|
|
781
|
+
* storeCommitHook to stay tree-shakeable. */ let patchCommitHook = null;
|
|
782
|
+
|
|
783
|
+
function setPatchCommitHook(e) {
|
|
784
|
+
patchCommitHook = e;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/** Held truth committed this finalize, awaiting its post-revert wake (see
|
|
788
|
+
* finalizePureQueue): the commit IS the reveal, but subscribers must not
|
|
789
|
+
* re-derive until the settling transaction's optimistic overrides have
|
|
790
|
+
* reverted — a commit-time wake recomputes them in the window where
|
|
791
|
+
* confirming truth is committed and the override still displays, a torn
|
|
792
|
+
* frame no timeline contains. */ const heldRevealed = [];
|
|
793
|
+
|
|
624
794
|
function commitPendingNodes() {
|
|
625
|
-
const e = currentBatch.
|
|
795
|
+
const e = currentBatch.Lt;
|
|
626
796
|
for (let t = 0; t < e.length; t++) {
|
|
627
|
-
|
|
797
|
+
const i = e[t];
|
|
798
|
+
commitPendingNode(i);
|
|
799
|
+
// The stamp dies with the commit (#3143) — symmetric with
|
|
800
|
+
// resolveOptimisticNodes clearing optimistic stamps. A stamp outliving
|
|
801
|
+
// its transaction let any later write (even a value-equal no-op, which
|
|
802
|
+
// re-opens before the equality bail) resurrect the finished transaction;
|
|
803
|
+
// a boundary flag rewritten every finalize pass then spun the drain loop
|
|
804
|
+
// forever (#3140). The held-truth mark dies the same death — the commit
|
|
805
|
+
// IS the reveal — but its wake defers to the post-revert pass: ordinary
|
|
806
|
+
// subscribers were masked to committed all hold (some re-derived against
|
|
807
|
+
// that old view and cached it), and commits are otherwise silent
|
|
808
|
+
// (staging already notified), so without a wake they'd hold the old
|
|
809
|
+
// world forever.
|
|
810
|
+
i.Ae = null;
|
|
811
|
+
if (i.T & CONFIG_HELD_TRUTH) {
|
|
812
|
+
i.T &= ~CONFIG_HELD_TRUTH;
|
|
813
|
+
heldRevealed.push(i);
|
|
814
|
+
}
|
|
628
815
|
}
|
|
629
816
|
e.length = 0;
|
|
630
817
|
storeCommitHook?.();
|
|
818
|
+
patchCommitHook?.(currentBatch);
|
|
631
819
|
}
|
|
632
820
|
|
|
633
821
|
function finalizePureQueue(e = null, t = false) {
|
|
@@ -636,25 +824,25 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
636
824
|
const i = !t;
|
|
637
825
|
if (i) commitPendingNodes();
|
|
638
826
|
if (!t && globalQueue.Qt.length) checkBoundaryChildren(globalQueue);
|
|
639
|
-
const n = dirtyQueue.EE >= dirtyQueue.
|
|
640
|
-
if (n) runHeap(dirtyQueue, GlobalQueue.
|
|
827
|
+
const n = dirtyQueue.EE >= dirtyQueue.Qe;
|
|
828
|
+
if (n) runHeap(dirtyQueue, GlobalQueue.Fe);
|
|
641
829
|
if (i) {
|
|
642
830
|
if (n) commitPendingNodes();
|
|
643
831
|
// The settling batch: the completing transaction's, or the ambient one.
|
|
644
|
-
const t = e ?? globalQueue.
|
|
832
|
+
const t = e ?? globalQueue.m;
|
|
645
833
|
// Optimistic reversion: a non-empty batch means _optimisticWrite ran,
|
|
646
834
|
// which installed the engine's hooks.
|
|
647
|
-
if (t.
|
|
835
|
+
if (t.ze.length) GlobalQueue.Tn(t.ze);
|
|
648
836
|
// Replay entanglement: subs recorded by the read-time gate get rescheduled
|
|
649
837
|
// so they re-run with the now-committed values visible. The ambient batch
|
|
650
838
|
// replays too — laneReadsCommitted records readers whose committed-view
|
|
651
839
|
// read hid a same-tick plain write that just committed above (#2963).
|
|
652
|
-
if (t.
|
|
653
|
-
for (const e of t.
|
|
840
|
+
if (t.cn.size) {
|
|
841
|
+
for (const e of t.cn) {
|
|
654
842
|
if (e.ie & REACTIVE_DISPOSED) continue;
|
|
655
843
|
enqueueSub(e);
|
|
656
844
|
}
|
|
657
|
-
t.
|
|
845
|
+
t.cn.clear();
|
|
658
846
|
// A completing transition keeps the outer flush loop alive by itself;
|
|
659
847
|
// the ambient batch needs the re-arm or the replay sits in the heap
|
|
660
848
|
// until the next unrelated write.
|
|
@@ -666,18 +854,34 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
666
854
|
// held boundary display state through the visual channel, and their
|
|
667
855
|
// release is the display-state update point — re-run the boundary sweep
|
|
668
856
|
// (the earlier sweep above ran while the marks were still live).
|
|
669
|
-
if (t.
|
|
670
|
-
GlobalQueue.
|
|
857
|
+
if (t.A.length) {
|
|
858
|
+
GlobalQueue.G(t.A);
|
|
671
859
|
if (globalQueue.Qt.length) checkBoundaryChildren(globalQueue);
|
|
672
860
|
}
|
|
673
861
|
// A non-empty set means trackOptimisticStore ran, which installed the
|
|
674
862
|
// hook; the hook iterates, clears, and schedules (keeping the loop out of
|
|
675
863
|
// core lets esbuild shake it — rollup already folds the null guard). The
|
|
676
864
|
// completing transition scopes the clear to its own layer keys (#2899).
|
|
677
|
-
if (t.
|
|
865
|
+
if (t.En.size) GlobalQueue.qt(t.En, e);
|
|
866
|
+
// Held-truth reveal wake (#3164), post-revert by construction: this
|
|
867
|
+
// finalize committed confirming truth whose subscribers were masked all
|
|
868
|
+
// hold — some re-derived against the committed view (the staging, or a
|
|
869
|
+
// confirming carrier's landing, notified them as a plain write) and
|
|
870
|
+
// cached it, and stash-restored applies may carry those torn values.
|
|
871
|
+
// Waking and recomputing HERE — after _resolveOptimistic and the store
|
|
872
|
+
// clears above — means every apply paints the settled view; a wake at
|
|
873
|
+
// commit time would recompute them in the window where truth is
|
|
874
|
+
// committed but the settling transaction's overrides still display.
|
|
875
|
+
if (heldRevealed.length !== 0) {
|
|
876
|
+
while (heldRevealed.length) insertSubs(heldRevealed.pop());
|
|
877
|
+
if (dirtyQueue.EE >= dirtyQueue.Qe) {
|
|
878
|
+
runHeap(dirtyQueue, GlobalQueue.Fe);
|
|
879
|
+
commitPendingNodes();
|
|
880
|
+
}
|
|
881
|
+
}
|
|
678
882
|
sweepTransientStoreNodes();
|
|
679
883
|
// Lanes only enter activeLanes through the engine's getOrCreateLane.
|
|
680
|
-
if (activeLanes.size) GlobalQueue.
|
|
884
|
+
if (activeLanes.size) GlobalQueue.In(e);
|
|
681
885
|
}
|
|
682
886
|
}
|
|
683
887
|
|
|
@@ -706,7 +910,7 @@ function checkBoundaryChildren(e) {
|
|
|
706
910
|
|
|
707
911
|
function reassignPendingTransition(e) {
|
|
708
912
|
for (let t = 0; t < e.length; t++) {
|
|
709
|
-
e[t].
|
|
913
|
+
e[t].Ae = activeTransition;
|
|
710
914
|
}
|
|
711
915
|
}
|
|
712
916
|
|
|
@@ -717,7 +921,7 @@ const globalQueue = new GlobalQueue;
|
|
|
717
921
|
// property hop through `_batch` was a measured instruction-count regression
|
|
718
922
|
// (CodSpeed update1to1, PR #2905). The field stays authoritative for
|
|
719
923
|
// cross-module readers; every `_batch` assignment updates both.
|
|
720
|
-
let currentBatch = globalQueue.
|
|
924
|
+
let currentBatch = globalQueue.m;
|
|
721
925
|
|
|
722
926
|
function flush(e) {
|
|
723
927
|
if (e) {
|
|
@@ -734,7 +938,7 @@ function flush(e) {
|
|
|
734
938
|
}
|
|
735
939
|
}
|
|
736
940
|
}
|
|
737
|
-
if (globalQueue.
|
|
941
|
+
if (globalQueue.fn) {
|
|
738
942
|
return;
|
|
739
943
|
}
|
|
740
944
|
if (halted) return;
|
|
@@ -752,21 +956,21 @@ function runQueue(e, t) {
|
|
|
752
956
|
function reporterBlocksSource(e, t) {
|
|
753
957
|
if (e.ie & (REACTIVE_ZOMBIE | REACTIVE_DISPOSED)) return false;
|
|
754
958
|
if (e.o?.le?.has(t)) return true;
|
|
755
|
-
for (let i = e.
|
|
756
|
-
let e = i.
|
|
959
|
+
for (let i = e.ut; i; i = i.lt) {
|
|
960
|
+
let e = i.ot;
|
|
757
961
|
while (e) {
|
|
758
|
-
if (e === t || e.
|
|
759
|
-
e = e.o?.
|
|
962
|
+
if (e === t || e.st === t) return true;
|
|
963
|
+
e = e.o?.Tt;
|
|
760
964
|
}
|
|
761
965
|
}
|
|
762
966
|
return !!(e.S & STATUS_PENDING && e.o?._ instanceof NotReadyError && e.o?._.source === t);
|
|
763
967
|
}
|
|
764
968
|
|
|
765
969
|
function transitionComplete(e) {
|
|
766
|
-
if (e.
|
|
767
|
-
if (e.
|
|
970
|
+
if (e.sn) return true;
|
|
971
|
+
if (e.ue.length) return false;
|
|
768
972
|
let t = true;
|
|
769
|
-
for (const [i, n] of e.
|
|
973
|
+
for (const [i, n] of e._e) {
|
|
770
974
|
let s = false;
|
|
771
975
|
for (const e of n) {
|
|
772
976
|
if (reporterBlocksSource(e, i)) {
|
|
@@ -775,7 +979,7 @@ function transitionComplete(e) {
|
|
|
775
979
|
}
|
|
776
980
|
n.delete(e);
|
|
777
981
|
}
|
|
778
|
-
if (!s) e.
|
|
982
|
+
if (!s) e._e.delete(i); else if (i.S & STATUS_PENDING && i.o?._?.source === i) {
|
|
779
983
|
t = false;
|
|
780
984
|
break;
|
|
781
985
|
}
|
|
@@ -783,13 +987,13 @@ function transitionComplete(e) {
|
|
|
783
987
|
// Override blockage lives with the engine (absent hook = "no optimistic
|
|
784
988
|
// blockage"); the hook's loops over _optimisticNodes/_optimisticStores are
|
|
785
989
|
// no-ops when the transition holds neither, so no pre-check is needed.
|
|
786
|
-
if (t && GlobalQueue.
|
|
787
|
-
t && (e.
|
|
990
|
+
if (t && GlobalQueue.dn?.(e)) t = false;
|
|
991
|
+
t && (e.sn = true);
|
|
788
992
|
return t;
|
|
789
993
|
}
|
|
790
994
|
|
|
791
995
|
function currentTransition(e) {
|
|
792
|
-
while (e.
|
|
996
|
+
while (e.sn && typeof e.sn === "object") e = e.sn;
|
|
793
997
|
return e;
|
|
794
998
|
}
|
|
795
999
|
|
|
@@ -803,4 +1007,27 @@ function runInTransition(e, t) {
|
|
|
803
1007
|
}
|
|
804
1008
|
}
|
|
805
1009
|
|
|
806
|
-
|
|
1010
|
+
/** Run `fn` with `transition` as BOTH the ambient transaction and the
|
|
1011
|
+
* registration batch, restoring both after. runInTransition alone is not
|
|
1012
|
+
* enough for code that WRITES on behalf of a transaction from inside someone
|
|
1013
|
+
* else's window (optimistic replay re-arming a still-open action's edits
|
|
1014
|
+
* during a landing commit, #3123): registrations route through the queue's
|
|
1015
|
+
* batch pointer, and a bare activeTransition swap leaves them in the ambient
|
|
1016
|
+
* batch — a plain batch "completes" at the next flush and reverts optimistic
|
|
1017
|
+
* registrations that were supposed to live with the transaction.
|
|
1018
|
+
* initTransition is the wrong tool here: it MERGES the currently ambient
|
|
1019
|
+
* transaction into the target, entangling whatever the interrupted window
|
|
1020
|
+
* belonged to. */ function runAsTransitionBatch(e, t) {
|
|
1021
|
+
const i = activeTransition;
|
|
1022
|
+
const n = globalQueue.m;
|
|
1023
|
+
try {
|
|
1024
|
+
activeTransition = currentTransition(e);
|
|
1025
|
+
currentBatch = globalQueue.m = activeTransition;
|
|
1026
|
+
return t();
|
|
1027
|
+
} finally {
|
|
1028
|
+
activeTransition = i;
|
|
1029
|
+
currentBatch = globalQueue.m = n;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
export { GlobalQueue, Queue, activeAffectsMarks, activeLanes, activeTransition, armReaskClear, assignOrMergeLane, bumpNotifyEpoch, clock, 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 };
|