@solidjs/signals 2.0.0-beta.19 → 2.0.0-beta.21
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/README.md +5 -1
- package/dist/dev.js +753 -378
- package/dist/node.cjs +1947 -1607
- package/dist/prod/affects.js +17 -21
- package/dist/prod/core/action.js +66 -15
- package/dist/prod/core/async.js +57 -71
- package/dist/prod/core/constants.js +15 -1
- package/dist/prod/core/core.js +39 -39
- package/dist/prod/core/effect.js +14 -14
- package/dist/prod/core/graph.js +1 -1
- package/dist/prod/core/heap.js +11 -3
- package/dist/prod/core/lanes.js +23 -13
- package/dist/prod/core/optimistic.js +107 -99
- package/dist/prod/core/owner.js +23 -23
- package/dist/prod/core/scheduler.js +188 -181
- package/dist/prod/core/verdict.js +29 -15
- package/dist/prod/map.js +196 -167
- package/dist/prod/signals.js +1 -1
- package/dist/prod/store/optimistic.js +103 -68
- package/dist/prod/store/reconcile.js +80 -46
- package/dist/prod/store/store.js +204 -73
- package/dist/prod/store/utils.js +61 -42
- package/dist/types/core/action.d.ts +35 -6
- package/dist/types/core/constants.d.ts +12 -0
- package/dist/types/core/dev.d.ts +7 -0
- package/dist/types/core/lanes.d.ts +2 -0
- package/dist/types/core/scheduler.d.ts +2 -6
- package/dist/types/core/types.d.ts +9 -1
- package/dist/types/store/store.d.ts +8 -2
- package/dist/types-cjs/core/action.d.cts +35 -6
- package/dist/types-cjs/core/constants.d.cts +12 -0
- package/dist/types-cjs/core/dev.d.cts +7 -0
- package/dist/types-cjs/core/lanes.d.cts +2 -0
- package/dist/types-cjs/core/scheduler.d.cts +2 -6
- package/dist/types-cjs/core/types.d.cts +9 -1
- package/dist/types-cjs/store/store.d.cts +8 -2
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EFFECT_RENDER, EFFECT_USER, STATUS_PENDING, REACTIVE_DISPOSED,
|
|
1
|
+
import { EFFECT_RENDER, EFFECT_USER, STATUS_PENDING, REACTIVE_DISPOSED, REACTIVE_ZOMBIE, NOT_PENDING, EFFECT_TRACKED, CONFIG_IN_SNAPSHOT_SCOPE, REACTIVE_SNAPSHOT_STALE, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_REASK, REACTIVE_MANUAL_WRITE, STATUS_UNINITIALIZED } from "./constants.js";
|
|
2
2
|
|
|
3
3
|
import { currentOptimisticLane } from "./core.js";
|
|
4
4
|
|
|
@@ -55,7 +55,8 @@ function registerTransientStoreNode(e) {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
function canUseSimpleSyncFlush(e) {
|
|
58
|
-
|
|
58
|
+
const t = e.N;
|
|
59
|
+
return transitions.size === 0 && activeLanes.size === 0 && e.Qt.length === 0 && t.Be.length === 0 && t._.length === 0 && t.dn.size === 0 && transientStoreNodes.size === 0;
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
function sweepTransientStoreNodes() {
|
|
@@ -70,7 +71,7 @@ function sweepTransientStoreNodes() {
|
|
|
70
71
|
// A live affects() mark keeps the node addressable: sweeping it would
|
|
71
72
|
// detach the refcount from the slot (a fresh probe would upsert a new,
|
|
72
73
|
// unmarked node for the same property).
|
|
73
|
-
if (e.
|
|
74
|
+
if (e.M) continue;
|
|
74
75
|
transientStoreNodes.delete(e);
|
|
75
76
|
e.ct?.();
|
|
76
77
|
}
|
|
@@ -89,31 +90,57 @@ function setProjectionWriteActive(e) {
|
|
|
89
90
|
projectionWriteActive = e;
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Ambient work IS a transaction: the global queue always carries one
|
|
95
|
+
* current-transaction-shaped batch (`globalQueue._batch`). With no transition
|
|
96
|
+
* active, registrations (pending commits, optimistic nodes, affects marks,
|
|
97
|
+
* optimistic stores) land in a plain ambient batch that the plain flush
|
|
98
|
+
* finalizes; when a transition initializes it adopts the ambient batch's
|
|
99
|
+
* contents and `_batch` becomes the transition itself, so later registrations
|
|
100
|
+
* land there directly — no per-field aliasing.
|
|
101
|
+
*/ function createBatch() {
|
|
102
|
+
return {
|
|
103
|
+
Ie: clock,
|
|
104
|
+
yt: [],
|
|
105
|
+
Lt: new Map,
|
|
106
|
+
Be: [],
|
|
107
|
+
_: [],
|
|
108
|
+
dn: new Set,
|
|
109
|
+
Te: [],
|
|
110
|
+
Bt: {
|
|
111
|
+
Mt: [ [], [] ],
|
|
112
|
+
Qt: []
|
|
113
|
+
},
|
|
114
|
+
cn: false,
|
|
115
|
+
un: new Set
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
92
119
|
function mergeTransitionState(e, t) {
|
|
93
|
-
t.
|
|
120
|
+
t.cn = e;
|
|
94
121
|
e.Te.push(...t.Te);
|
|
95
122
|
for (const i of activeLanes) if (i.ve === t) i.ve = e;
|
|
96
123
|
if (t.Be.length) {
|
|
97
|
-
// Move (don't copy): the global queue may still
|
|
98
|
-
//
|
|
124
|
+
// Move (don't copy): the global queue's batch may still be the outgoing
|
|
125
|
+
// transition, and the adoption pass in initTransition would re-push its
|
|
99
126
|
// contents into the target — duplicating every entry.
|
|
100
127
|
e.Be.push(...t.Be);
|
|
101
128
|
t.Be.length = 0;
|
|
102
129
|
}
|
|
103
|
-
if (t.
|
|
104
|
-
// Move (don't copy): the global queue may still
|
|
105
|
-
//
|
|
130
|
+
if (t._.length) {
|
|
131
|
+
// Move (don't copy): the global queue's batch may still be the outgoing
|
|
132
|
+
// transition, and the adoption pass in initTransition would re-push its
|
|
106
133
|
// contents into the target — double-releasing every mark.
|
|
107
|
-
e.
|
|
108
|
-
t.
|
|
134
|
+
e._.push(...t._);
|
|
135
|
+
t._.length = 0;
|
|
109
136
|
}
|
|
110
|
-
for (const i of t.
|
|
111
|
-
for (const [i, n] of t.
|
|
112
|
-
let t = e.
|
|
113
|
-
if (!t) e.
|
|
137
|
+
for (const i of t.dn) e.dn.add(i);
|
|
138
|
+
for (const [i, n] of t.Lt) {
|
|
139
|
+
let t = e.Lt.get(i);
|
|
140
|
+
if (!t) e.Lt.set(i, t = new Set);
|
|
114
141
|
for (const e of n) t.add(e);
|
|
115
142
|
}
|
|
116
|
-
for (const i of t.
|
|
143
|
+
for (const i of t.un) e.un.add(i);
|
|
117
144
|
}
|
|
118
145
|
|
|
119
146
|
function schedule() {
|
|
@@ -123,7 +150,7 @@ function schedule() {
|
|
|
123
150
|
}
|
|
124
151
|
if (scheduled) return;
|
|
125
152
|
scheduled = true;
|
|
126
|
-
if (!syncDepth && !globalQueue.
|
|
153
|
+
if (!syncDepth && !globalQueue.Ut && !projectionWriteActive) queueMicrotask(flush);
|
|
127
154
|
}
|
|
128
155
|
|
|
129
156
|
/**
|
|
@@ -153,32 +180,32 @@ function notifyHalted() {
|
|
|
153
180
|
}
|
|
154
181
|
|
|
155
182
|
class Queue {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
183
|
+
Fe=null;
|
|
184
|
+
Mt=[ [], [] ];
|
|
185
|
+
Qt=[];
|
|
159
186
|
created=clock;
|
|
160
187
|
addChild(e) {
|
|
161
|
-
this.
|
|
162
|
-
e.
|
|
188
|
+
this.Qt.push(e);
|
|
189
|
+
e.Fe = this;
|
|
163
190
|
}
|
|
164
191
|
removeChild(e) {
|
|
165
|
-
const t = this.
|
|
192
|
+
const t = this.Qt.indexOf(e);
|
|
166
193
|
if (t >= 0) {
|
|
167
|
-
this.
|
|
168
|
-
e.
|
|
194
|
+
this.Qt.splice(t, 1);
|
|
195
|
+
e.Fe = null;
|
|
169
196
|
}
|
|
170
197
|
}
|
|
171
198
|
notify(e, t, i, n) {
|
|
172
|
-
if (this.
|
|
199
|
+
if (this.Fe) return this.Fe.notify(e, t, i, n);
|
|
173
200
|
return false;
|
|
174
201
|
}
|
|
175
202
|
run(e) {
|
|
176
|
-
if (this.
|
|
177
|
-
const t = this.
|
|
178
|
-
this.
|
|
203
|
+
if (this.Mt[e - 1].length) {
|
|
204
|
+
const t = this.Mt[e - 1];
|
|
205
|
+
this.Mt[e - 1] = [];
|
|
179
206
|
runQueue(t, e);
|
|
180
207
|
}
|
|
181
|
-
for (let t = 0; t < this.
|
|
208
|
+
for (let t = 0; t < this.Qt.length; t++) this.Qt[t].run?.(e);
|
|
182
209
|
}
|
|
183
210
|
enqueue(e, t) {
|
|
184
211
|
if (e) {
|
|
@@ -187,50 +214,48 @@ class Queue {
|
|
|
187
214
|
const i = findLane(currentOptimisticLane);
|
|
188
215
|
i.tn[e - 1].push(t);
|
|
189
216
|
} else {
|
|
190
|
-
this.
|
|
217
|
+
this.Mt[e - 1].push(t);
|
|
191
218
|
}
|
|
192
219
|
}
|
|
193
220
|
schedule();
|
|
194
221
|
}
|
|
195
222
|
stashQueues(e) {
|
|
196
|
-
e.
|
|
197
|
-
e.
|
|
198
|
-
this.
|
|
199
|
-
for (let t = 0; t < this.
|
|
200
|
-
let i = this.
|
|
201
|
-
let n = e.
|
|
223
|
+
e.Mt[0].push(...this.Mt[0]);
|
|
224
|
+
e.Mt[1].push(...this.Mt[1]);
|
|
225
|
+
this.Mt = [ [], [] ];
|
|
226
|
+
for (let t = 0; t < this.Qt.length; t++) {
|
|
227
|
+
let i = this.Qt[t];
|
|
228
|
+
let n = e.Qt[t];
|
|
202
229
|
if (!n) {
|
|
203
230
|
n = {
|
|
204
|
-
|
|
205
|
-
|
|
231
|
+
Mt: [ [], [] ],
|
|
232
|
+
Qt: []
|
|
206
233
|
};
|
|
207
|
-
e.
|
|
234
|
+
e.Qt[t] = n;
|
|
208
235
|
}
|
|
209
236
|
i.stashQueues(n);
|
|
210
237
|
}
|
|
211
238
|
}
|
|
212
239
|
restoreQueues(e) {
|
|
213
|
-
this.
|
|
214
|
-
this.
|
|
215
|
-
for (let t = 0; t < e.
|
|
216
|
-
const i = e.
|
|
217
|
-
let n = this.
|
|
240
|
+
this.Mt[0].push(...e.Mt[0]);
|
|
241
|
+
this.Mt[1].push(...e.Mt[1]);
|
|
242
|
+
for (let t = 0; t < e.Qt.length; t++) {
|
|
243
|
+
const i = e.Qt[t];
|
|
244
|
+
let n = this.Qt[t];
|
|
218
245
|
if (n) n.restoreQueues(i);
|
|
219
246
|
}
|
|
220
247
|
}
|
|
221
248
|
}
|
|
222
249
|
|
|
223
250
|
class GlobalQueue extends Queue {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
N=[];
|
|
229
|
-
Lt=new Set;
|
|
251
|
+
Ut=false;
|
|
252
|
+
// The current transaction-shaped batch: a plain ambient batch while no
|
|
253
|
+
// transition is active, the active transition itself after initTransition.
|
|
254
|
+
N=createBatch();
|
|
230
255
|
static Ce;
|
|
231
256
|
static Oe;
|
|
232
257
|
static ut;
|
|
233
|
-
static
|
|
258
|
+
static Vt=null;
|
|
234
259
|
// Store-side hook: drops a keyless affects() mark's identity scope when the
|
|
235
260
|
// carrier node's last registration releases (wired by store.ts, mirroring
|
|
236
261
|
// _clearOptimisticStore).
|
|
@@ -256,15 +281,15 @@ class GlobalQueue extends Queue {
|
|
|
256
281
|
// verdict.ts, and `pendingCheckActive`/`latestReadActive` only flip inside
|
|
257
282
|
// isPending()/latest()).
|
|
258
283
|
static _e=null;
|
|
259
|
-
static
|
|
284
|
+
static P=null;
|
|
260
285
|
static me=null;
|
|
261
286
|
static R=null;
|
|
262
287
|
static Gt=null;
|
|
263
288
|
static Pt=null;
|
|
264
|
-
static
|
|
289
|
+
static kt=null;
|
|
265
290
|
static tt=null;
|
|
266
291
|
static nt=null;
|
|
267
|
-
static
|
|
292
|
+
static wt=null;
|
|
268
293
|
// Optimistic-engine hooks (wired by core/optimistic.ts via
|
|
269
294
|
// installOptimisticEngine(), called from verdict.ts / createOptimistic /
|
|
270
295
|
// createOptimisticStore — every module that can create optimistic state).
|
|
@@ -273,22 +298,22 @@ class GlobalQueue extends Queue {
|
|
|
273
298
|
// entry, or a non-null `currentOptimisticLane`, so `!` invocations are safe
|
|
274
299
|
// once the gate holds.
|
|
275
300
|
static bt=null;
|
|
276
|
-
static
|
|
277
|
-
static
|
|
278
|
-
static
|
|
279
|
-
static
|
|
280
|
-
static
|
|
281
|
-
static gt=null;
|
|
301
|
+
static En=null;
|
|
302
|
+
static Tn=null;
|
|
303
|
+
static In=null;
|
|
304
|
+
static Nn=null;
|
|
305
|
+
static On=null;
|
|
282
306
|
static Dt=null;
|
|
307
|
+
static gt=null;
|
|
283
308
|
static ht=null;
|
|
284
|
-
static
|
|
309
|
+
static vt=null;
|
|
285
310
|
static $e=null;
|
|
286
311
|
static et=null;
|
|
287
312
|
static Xe=null;
|
|
288
|
-
static
|
|
313
|
+
static mn=null;
|
|
289
314
|
flush() {
|
|
290
|
-
if (this.
|
|
291
|
-
this.
|
|
315
|
+
if (this.Ut) return;
|
|
316
|
+
this.Ut = true;
|
|
292
317
|
try {
|
|
293
318
|
if (false) ;
|
|
294
319
|
runHeap(dirtyQueue, GlobalQueue.Ce);
|
|
@@ -297,38 +322,55 @@ class GlobalQueue extends Queue {
|
|
|
297
322
|
if (!e) {
|
|
298
323
|
const e = activeTransition;
|
|
299
324
|
runHeap(zombieQueue, GlobalQueue.Ce);
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
325
|
+
// Detach: the stashed transition keeps its batch; ambient work that
|
|
326
|
+
// follows lands in a fresh one. If the batch is already a separate
|
|
327
|
+
// ambient one — action done() restored activeTransition without
|
|
328
|
+
// adopting the batch, and an ordinary write landed there before
|
|
329
|
+
// the scheduled flush (#2916) — keep it: replacing it would strand
|
|
330
|
+
// its queued pending nodes with held _pendingValues forever.
|
|
331
|
+
if (this.N === e) currentBatch = this.N = createBatch();
|
|
305
332
|
// Run lane effects immediately (before stashing) - lanes with no pending async
|
|
306
333
|
if (activeLanes.size) {
|
|
307
|
-
GlobalQueue.
|
|
308
|
-
GlobalQueue.
|
|
334
|
+
GlobalQueue.On(EFFECT_RENDER);
|
|
335
|
+
GlobalQueue.On(EFFECT_USER);
|
|
309
336
|
}
|
|
310
|
-
this.stashQueues(e.
|
|
337
|
+
this.stashQueues(e.Bt);
|
|
311
338
|
clock++;
|
|
312
|
-
|
|
313
|
-
|
|
339
|
+
// A kept ambient batch may hold pending nodes (#2916): stay
|
|
340
|
+
// scheduled so the outer drain loop commits them via the plain
|
|
341
|
+
// flush path instead of leaving them until the next natural flush.
|
|
342
|
+
scheduled = dirtyQueue.EE >= dirtyQueue.Le || this.N.yt.length > 0;
|
|
343
|
+
reassignPendingTransition(e.yt);
|
|
314
344
|
activeTransition = null;
|
|
315
345
|
// The stash pass (committed-view rerun of plain optimistic signals)
|
|
316
346
|
// wraps finalizePureQueue in the engine; a non-empty _optimisticNodes
|
|
317
347
|
// means _optimisticWrite ran, which installed the hook.
|
|
318
|
-
if (!e.Te.length && !e.
|
|
319
|
-
GlobalQueue.
|
|
348
|
+
if (!e.Te.length && !e.Lt.size && e.Be.length) {
|
|
349
|
+
GlobalQueue.Tn(e);
|
|
320
350
|
} else {
|
|
321
351
|
finalizePureQueue(null, true);
|
|
322
352
|
}
|
|
323
353
|
return;
|
|
324
354
|
}
|
|
325
|
-
this.Yt !== activeTransition.Yt && this.Yt.push(...activeTransition.Yt);
|
|
326
|
-
this.restoreQueues(activeTransition.Jt);
|
|
327
|
-
transitions.delete(activeTransition);
|
|
328
355
|
const t = activeTransition;
|
|
356
|
+
const i = this.N;
|
|
357
|
+
i !== t && i.yt.push(...t.yt);
|
|
358
|
+
this.restoreQueues(t.Bt);
|
|
359
|
+
transitions.delete(t);
|
|
329
360
|
activeTransition = null;
|
|
330
|
-
reassignPendingTransition(
|
|
361
|
+
reassignPendingTransition(i.yt);
|
|
331
362
|
finalizePureQueue(t);
|
|
363
|
+
if (i === t) {
|
|
364
|
+
// Drop the dead Transition wrapper but keep its (drained) containers
|
|
365
|
+
// as the ambient batch — late registrations during finalization live
|
|
366
|
+
// there and must survive to the next flush.
|
|
367
|
+
const e = createBatch();
|
|
368
|
+
e.yt = i.yt;
|
|
369
|
+
e.Be = i.Be;
|
|
370
|
+
e._ = i._;
|
|
371
|
+
e.dn = i.dn;
|
|
372
|
+
currentBatch = this.N = e;
|
|
373
|
+
}
|
|
332
374
|
} else {
|
|
333
375
|
if (canUseSimpleSyncFlush(this)) {
|
|
334
376
|
commitPendingNodes();
|
|
@@ -345,15 +387,15 @@ class GlobalQueue extends Queue {
|
|
|
345
387
|
// Check if finalization added items to the heap (from optimistic reversion)
|
|
346
388
|
scheduled = dirtyQueue.EE >= dirtyQueue.Le;
|
|
347
389
|
// Run lane effects first (for ready lanes), then regular effects
|
|
348
|
-
activeLanes.size && GlobalQueue.
|
|
390
|
+
activeLanes.size && GlobalQueue.On(EFFECT_RENDER);
|
|
349
391
|
this.run(EFFECT_RENDER);
|
|
350
|
-
activeLanes.size && GlobalQueue.
|
|
392
|
+
activeLanes.size && GlobalQueue.On(EFFECT_USER);
|
|
351
393
|
this.run(EFFECT_USER);
|
|
352
394
|
if (false) ;
|
|
353
395
|
if (false && !scheduled && !activeTransition && transitions.size === 0 && activeLanes.size === 0) ;
|
|
354
396
|
if (false) ;
|
|
355
397
|
} finally {
|
|
356
|
-
this.
|
|
398
|
+
this.Ut = false;
|
|
357
399
|
}
|
|
358
400
|
}
|
|
359
401
|
notify(e, t, i, n) {
|
|
@@ -363,8 +405,8 @@ class GlobalQueue extends Queue {
|
|
|
363
405
|
const t = n !== undefined ? n : e.k;
|
|
364
406
|
if (activeTransition && t) {
|
|
365
407
|
const i = t.source;
|
|
366
|
-
let n = activeTransition.
|
|
367
|
-
if (!n) activeTransition.
|
|
408
|
+
let n = activeTransition.Lt.get(i);
|
|
409
|
+
if (!n) activeTransition.Lt.set(i, n = new Set);
|
|
368
410
|
const s = n.size;
|
|
369
411
|
n.add(e);
|
|
370
412
|
if (n.size !== s) schedule();
|
|
@@ -379,21 +421,7 @@ class GlobalQueue extends Queue {
|
|
|
379
421
|
if (e && e === activeTransition) return;
|
|
380
422
|
if (!e && activeTransition && activeTransition.Ie === clock) return;
|
|
381
423
|
if (!activeTransition) {
|
|
382
|
-
activeTransition = e ??
|
|
383
|
-
Ie: clock,
|
|
384
|
-
Yt: [],
|
|
385
|
-
qt: new Map,
|
|
386
|
-
Be: [],
|
|
387
|
-
N: [],
|
|
388
|
-
Lt: new Set,
|
|
389
|
-
Te: [],
|
|
390
|
-
Jt: {
|
|
391
|
-
zt: [ [], [] ],
|
|
392
|
-
wt: []
|
|
393
|
-
},
|
|
394
|
-
Ht: false,
|
|
395
|
-
Qt: new Set
|
|
396
|
-
};
|
|
424
|
+
activeTransition = e ?? createBatch();
|
|
397
425
|
} else if (e) {
|
|
398
426
|
const t = activeTransition;
|
|
399
427
|
mergeTransitionState(e, t);
|
|
@@ -402,61 +430,37 @@ class GlobalQueue extends Queue {
|
|
|
402
430
|
}
|
|
403
431
|
transitions.add(activeTransition);
|
|
404
432
|
activeTransition.Ie = clock;
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
433
|
+
const t = this.N;
|
|
434
|
+
if (t !== activeTransition) {
|
|
435
|
+
// Adopt the ambient batch into the transaction, then make the
|
|
436
|
+
// transaction the batch so later registrations land there directly.
|
|
437
|
+
// Pending and optimistic nodes are re-stamped as the transaction's;
|
|
438
|
+
// marks don't hijack the node's _transition — a mark on a plain signal
|
|
439
|
+
// must not entangle unrelated writes to it; the same rule holds one hop
|
|
440
|
+
// downstream: propagation never queues pended subscribers as pending
|
|
441
|
+
// nodes, see propagateAffectsMark, #2893.
|
|
442
|
+
for (let e = 0; e < t.yt.length; e++) {
|
|
443
|
+
const i = t.yt[e];
|
|
444
|
+
i.ve = activeTransition;
|
|
445
|
+
activeTransition.yt.push(i);
|
|
415
446
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
const t = this.Be[e];
|
|
421
|
-
t.ve = activeTransition;
|
|
422
|
-
activeTransition.Be.push(t);
|
|
447
|
+
for (let e = 0; e < t.Be.length; e++) {
|
|
448
|
+
const i = t.Be[e];
|
|
449
|
+
i.ve = activeTransition;
|
|
450
|
+
activeTransition.Be.push(i);
|
|
423
451
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
// Adopt ambient marks into the transaction (marks don't hijack the
|
|
428
|
-
// node's _transition — a mark on a plain signal must not entangle
|
|
429
|
-
// unrelated writes to it; the same rule holds one hop downstream:
|
|
430
|
-
// propagation never queues pended subscribers as pending nodes, see
|
|
431
|
-
// propagateAffectsMark, #2893). After adoption the queue aliases the
|
|
432
|
-
// transition's array, so later registrations land there directly.
|
|
433
|
-
activeTransition.N.push(...this.N);
|
|
434
|
-
this.N = activeTransition.N;
|
|
452
|
+
if (t._.length) activeTransition._.push(...t._);
|
|
453
|
+
for (const e of t.dn) activeTransition.dn.add(e);
|
|
454
|
+
currentBatch = this.N = activeTransition;
|
|
435
455
|
}
|
|
436
456
|
for (const e of activeLanes) {
|
|
437
457
|
if (!e.ve) e.ve = activeTransition;
|
|
438
458
|
}
|
|
439
|
-
if (this.Lt !== activeTransition.Lt) {
|
|
440
|
-
for (const e of this.Lt) activeTransition.Lt.add(e);
|
|
441
|
-
this.Lt = activeTransition.Lt;
|
|
442
|
-
}
|
|
443
459
|
}
|
|
444
460
|
}
|
|
445
461
|
|
|
446
462
|
function queuePendingNode(e) {
|
|
447
|
-
|
|
448
|
-
globalQueue.Yt.push(e);
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
if (globalQueue.xt === null && globalQueue.Yt.length === 0) {
|
|
452
|
-
globalQueue.xt = e;
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
if (globalQueue.xt !== null) {
|
|
456
|
-
globalQueue.Yt.push(globalQueue.xt);
|
|
457
|
-
globalQueue.xt = null;
|
|
458
|
-
}
|
|
459
|
-
globalQueue.Yt.push(e);
|
|
463
|
+
currentBatch.yt.push(e);
|
|
460
464
|
}
|
|
461
465
|
|
|
462
466
|
// Sticky: flips true on the first refresh() ever (the only setter of
|
|
@@ -474,23 +478,23 @@ function insertSubs(e, t = false) {
|
|
|
474
478
|
const i = e.Je || currentOptimisticLane;
|
|
475
479
|
const n = e.Ye !== undefined;
|
|
476
480
|
const s = reaskArmed;
|
|
477
|
-
for (let
|
|
481
|
+
for (let r = e.p; r !== null; r = r.de) {
|
|
478
482
|
// A value-change notification is a new question for the subscriber: any
|
|
479
483
|
// pending re-ask mark (refresh) it carried is superseded.
|
|
480
|
-
if (s)
|
|
481
|
-
if (n &&
|
|
482
|
-
|
|
484
|
+
if (s) r.Ee.u &= ~REACTIVE_REASK;
|
|
485
|
+
if (n && r.Ee.U & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
486
|
+
r.Ee.u |= REACTIVE_SNAPSHOT_STALE;
|
|
483
487
|
continue;
|
|
484
488
|
}
|
|
485
489
|
if (t && i) {
|
|
486
|
-
|
|
487
|
-
assignOrMergeLane(
|
|
490
|
+
r.Ee.u |= REACTIVE_OPTIMISTIC_DIRTY;
|
|
491
|
+
assignOrMergeLane(r.Ee, i);
|
|
488
492
|
} else if (t) {
|
|
489
|
-
|
|
493
|
+
r.Ee.u |= REACTIVE_OPTIMISTIC_DIRTY;
|
|
490
494
|
// No source lane means reversion - clear subscriber's lane so effects go to regular queue
|
|
491
|
-
|
|
495
|
+
r.Ee.Je = undefined;
|
|
492
496
|
}
|
|
493
|
-
enqueueSub(
|
|
497
|
+
enqueueSub(r.Ee);
|
|
494
498
|
}
|
|
495
499
|
}
|
|
496
500
|
|
|
@@ -517,11 +521,7 @@ function commitPendingNode(e) {
|
|
|
517
521
|
}
|
|
518
522
|
|
|
519
523
|
function commitPendingNodes() {
|
|
520
|
-
|
|
521
|
-
commitPendingNode(globalQueue.xt);
|
|
522
|
-
globalQueue.xt = null;
|
|
523
|
-
}
|
|
524
|
-
const e = globalQueue.Yt;
|
|
524
|
+
const e = currentBatch.yt;
|
|
525
525
|
for (let t = 0; t < e.length; t++) {
|
|
526
526
|
commitPendingNode(e[t]);
|
|
527
527
|
}
|
|
@@ -533,42 +533,42 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
533
533
|
// For completing transitions or no-transition, resolve pending and revert optimistic
|
|
534
534
|
const i = !t;
|
|
535
535
|
if (i) commitPendingNodes();
|
|
536
|
-
if (!t && globalQueue.
|
|
536
|
+
if (!t && globalQueue.Qt.length) checkBoundaryChildren(globalQueue);
|
|
537
537
|
const n = dirtyQueue.EE >= dirtyQueue.Le;
|
|
538
538
|
if (n) runHeap(dirtyQueue, GlobalQueue.Ce);
|
|
539
539
|
if (i) {
|
|
540
540
|
if (n) commitPendingNodes();
|
|
541
|
+
// The settling batch: the completing transaction's, or the ambient one.
|
|
542
|
+
const t = e ?? globalQueue.N;
|
|
541
543
|
// Optimistic reversion: a non-empty batch means _optimisticWrite ran,
|
|
542
544
|
// which installed the engine's hooks.
|
|
543
|
-
|
|
544
|
-
if (t.length) GlobalQueue.Ut(t);
|
|
545
|
+
if (t.Be.length) GlobalQueue.En(t.Be);
|
|
545
546
|
// Replay entanglement: subs recorded by the read-time gate get rescheduled
|
|
546
547
|
// so they re-run with the now-committed values visible.
|
|
547
|
-
if (e && e.
|
|
548
|
-
for (const t of e.
|
|
548
|
+
if (e && e.un.size) {
|
|
549
|
+
for (const t of e.un) {
|
|
549
550
|
if (t.u & REACTIVE_DISPOSED) continue;
|
|
550
551
|
enqueueSub(t);
|
|
551
552
|
}
|
|
552
|
-
e.
|
|
553
|
+
e.un.clear();
|
|
553
554
|
}
|
|
554
555
|
// Declared motion ends with the transaction: settle (or plain flush end
|
|
555
556
|
// for ambient marks) releases each registration's refcount. A non-empty
|
|
556
557
|
// batch means registerAffectsMark ran, which installed the hook.
|
|
557
|
-
|
|
558
|
-
if (i.length) GlobalQueue.h(i);
|
|
559
|
-
const s = e ? e.Lt : globalQueue.Lt;
|
|
558
|
+
if (t._.length) GlobalQueue.h(t._);
|
|
560
559
|
// A non-empty set means trackOptimisticStore ran, which installed the
|
|
561
560
|
// hook; the hook iterates, clears, and schedules (keeping the loop out of
|
|
562
|
-
// core lets esbuild shake it — rollup already folds the null guard).
|
|
563
|
-
|
|
561
|
+
// core lets esbuild shake it — rollup already folds the null guard). The
|
|
562
|
+
// completing transition scopes the clear to its own layer keys (#2899).
|
|
563
|
+
if (t.dn.size) GlobalQueue.Vt(t.dn, e);
|
|
564
564
|
sweepTransientStoreNodes();
|
|
565
565
|
// Lanes only enter activeLanes through the engine's getOrCreateLane.
|
|
566
|
-
if (activeLanes.size) GlobalQueue.
|
|
566
|
+
if (activeLanes.size) GlobalQueue.Nn(e);
|
|
567
567
|
}
|
|
568
568
|
}
|
|
569
569
|
|
|
570
570
|
function checkBoundaryChildren(e) {
|
|
571
|
-
for (const t of e.
|
|
571
|
+
for (const t of e.Qt) {
|
|
572
572
|
t.Se?.();
|
|
573
573
|
checkBoundaryChildren(t);
|
|
574
574
|
}
|
|
@@ -598,6 +598,13 @@ function reassignPendingTransition(e) {
|
|
|
598
598
|
|
|
599
599
|
const globalQueue = new GlobalQueue;
|
|
600
600
|
|
|
601
|
+
// Hot-path mirror of `globalQueue._batch`: `queuePendingNode` runs once per
|
|
602
|
+
// staged write and `commitPendingNodes` once per flush, and the extra
|
|
603
|
+
// property hop through `_batch` was a measured instruction-count regression
|
|
604
|
+
// (CodSpeed update1to1, PR #2905). The field stays authoritative for
|
|
605
|
+
// cross-module readers; every `_batch` assignment updates both.
|
|
606
|
+
let currentBatch = globalQueue.N;
|
|
607
|
+
|
|
601
608
|
function flush(e) {
|
|
602
609
|
if (e) {
|
|
603
610
|
syncDepth++;
|
|
@@ -613,7 +620,7 @@ function flush(e) {
|
|
|
613
620
|
}
|
|
614
621
|
}
|
|
615
622
|
}
|
|
616
|
-
if (globalQueue.
|
|
623
|
+
if (globalQueue.Ut) {
|
|
617
624
|
return;
|
|
618
625
|
}
|
|
619
626
|
if (halted) return;
|
|
@@ -630,7 +637,7 @@ function runQueue(e, t) {
|
|
|
630
637
|
|
|
631
638
|
function reporterBlocksSource(e, t) {
|
|
632
639
|
if (e.u & (REACTIVE_ZOMBIE | REACTIVE_DISPOSED)) return false;
|
|
633
|
-
if (e.m
|
|
640
|
+
if (e.m?.has(t)) return true;
|
|
634
641
|
for (let i = e.S; i; i = i.st) {
|
|
635
642
|
let e = i.ot;
|
|
636
643
|
while (e) {
|
|
@@ -642,10 +649,10 @@ function reporterBlocksSource(e, t) {
|
|
|
642
649
|
}
|
|
643
650
|
|
|
644
651
|
function transitionComplete(e) {
|
|
645
|
-
if (e.
|
|
652
|
+
if (e.cn) return true;
|
|
646
653
|
if (e.Te.length) return false;
|
|
647
654
|
let t = true;
|
|
648
|
-
for (const [i, n] of e.
|
|
655
|
+
for (const [i, n] of e.Lt) {
|
|
649
656
|
let s = false;
|
|
650
657
|
for (const e of n) {
|
|
651
658
|
if (reporterBlocksSource(e, i)) {
|
|
@@ -654,7 +661,7 @@ function transitionComplete(e) {
|
|
|
654
661
|
}
|
|
655
662
|
n.delete(e);
|
|
656
663
|
}
|
|
657
|
-
if (!s) e.
|
|
664
|
+
if (!s) e.Lt.delete(i); else if (i.i & STATUS_PENDING && i.k?.source === i) {
|
|
658
665
|
t = false;
|
|
659
666
|
break;
|
|
660
667
|
}
|
|
@@ -662,13 +669,13 @@ function transitionComplete(e) {
|
|
|
662
669
|
// Override blockage lives with the engine. Absent hook = "no optimistic
|
|
663
670
|
// blockage", which is exact: only _optimisticWrite (engine) pushes to
|
|
664
671
|
// _optimisticNodes, so without the engine the loop was vacuous anyway.
|
|
665
|
-
if (t && e.Be.length && GlobalQueue.
|
|
666
|
-
t && (e.
|
|
672
|
+
if (t && e.Be.length && GlobalQueue.In(e)) t = false;
|
|
673
|
+
t && (e.cn = true);
|
|
667
674
|
return t;
|
|
668
675
|
}
|
|
669
676
|
|
|
670
677
|
function currentTransition(e) {
|
|
671
|
-
while (e.
|
|
678
|
+
while (e.cn && typeof e.cn === "object") e = e.cn;
|
|
672
679
|
return e;
|
|
673
680
|
}
|
|
674
681
|
|