@solidjs/signals 2.0.0-beta.19 → 2.0.0-beta.20

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.
Files changed (36) hide show
  1. package/dist/dev.js +670 -371
  2. package/dist/node.cjs +1878 -1606
  3. package/dist/prod/affects.js +17 -21
  4. package/dist/prod/core/action.js +19 -6
  5. package/dist/prod/core/async.js +57 -71
  6. package/dist/prod/core/constants.js +15 -1
  7. package/dist/prod/core/core.js +39 -39
  8. package/dist/prod/core/effect.js +14 -14
  9. package/dist/prod/core/graph.js +1 -1
  10. package/dist/prod/core/heap.js +2 -2
  11. package/dist/prod/core/lanes.js +23 -13
  12. package/dist/prod/core/optimistic.js +107 -99
  13. package/dist/prod/core/owner.js +23 -23
  14. package/dist/prod/core/scheduler.js +180 -180
  15. package/dist/prod/core/verdict.js +12 -13
  16. package/dist/prod/map.js +196 -167
  17. package/dist/prod/signals.js +1 -1
  18. package/dist/prod/store/optimistic.js +103 -68
  19. package/dist/prod/store/reconcile.js +80 -46
  20. package/dist/prod/store/store.js +204 -73
  21. package/dist/prod/store/utils.js +61 -42
  22. package/dist/types/core/action.d.ts +19 -6
  23. package/dist/types/core/constants.d.ts +12 -0
  24. package/dist/types/core/dev.d.ts +7 -0
  25. package/dist/types/core/lanes.d.ts +2 -0
  26. package/dist/types/core/scheduler.d.ts +2 -6
  27. package/dist/types/core/types.d.ts +9 -1
  28. package/dist/types/store/store.d.ts +8 -2
  29. package/dist/types-cjs/core/action.d.cts +19 -6
  30. package/dist/types-cjs/core/constants.d.cts +12 -0
  31. package/dist/types-cjs/core/dev.d.cts +7 -0
  32. package/dist/types-cjs/core/lanes.d.cts +2 -0
  33. package/dist/types-cjs/core/scheduler.d.cts +2 -6
  34. package/dist/types-cjs/core/types.d.cts +9 -1
  35. package/dist/types-cjs/store/store.d.cts +8 -2
  36. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { EFFECT_RENDER, EFFECT_USER, STATUS_PENDING, REACTIVE_DISPOSED, REACTIVE_REASK, CONFIG_IN_SNAPSHOT_SCOPE, REACTIVE_SNAPSHOT_STALE, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_ZOMBIE, NOT_PENDING, EFFECT_TRACKED, REACTIVE_MANUAL_WRITE, STATUS_UNINITIALIZED } from "./constants.js";
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
- return transitions.size === 0 && activeLanes.size === 0 && e.wt.length === 0 && e.Be.length === 0 && e.N.length === 0 && e.Lt.size === 0 && transientStoreNodes.size === 0;
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.P) continue;
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.Ht = e;
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 alias the outgoing
98
- // array, and the adoption pass in initTransition would re-push its
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.N.length) {
104
- // Move (don't copy): the global queue may still alias the outgoing
105
- // array, and the adoption pass in initTransition would re-push its
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.N.push(...t.N);
108
- t.N.length = 0;
134
+ e._.push(...t._);
135
+ t._.length = 0;
109
136
  }
110
- for (const i of t.Lt) e.Lt.add(i);
111
- for (const [i, n] of t.qt) {
112
- let t = e.qt.get(i);
113
- if (!t) e.qt.set(i, t = new Set);
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.Qt) e.Qt.add(i);
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.Bt && !projectionWriteActive) queueMicrotask(flush);
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
- He=null;
157
- zt=[ [], [] ];
158
- wt=[];
183
+ Fe=null;
184
+ Mt=[ [], [] ];
185
+ Qt=[];
159
186
  created=clock;
160
187
  addChild(e) {
161
- this.wt.push(e);
162
- e.He = this;
188
+ this.Qt.push(e);
189
+ e.Fe = this;
163
190
  }
164
191
  removeChild(e) {
165
- const t = this.wt.indexOf(e);
192
+ const t = this.Qt.indexOf(e);
166
193
  if (t >= 0) {
167
- this.wt.splice(t, 1);
168
- e.He = null;
194
+ this.Qt.splice(t, 1);
195
+ e.Fe = null;
169
196
  }
170
197
  }
171
198
  notify(e, t, i, n) {
172
- if (this.He) return this.He.notify(e, t, i, n);
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.zt[e - 1].length) {
177
- const t = this.zt[e - 1];
178
- this.zt[e - 1] = [];
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.wt.length; t++) this.wt[t].run?.(e);
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.zt[e - 1].push(t);
217
+ this.Mt[e - 1].push(t);
191
218
  }
192
219
  }
193
220
  schedule();
194
221
  }
195
222
  stashQueues(e) {
196
- e.zt[0].push(...this.zt[0]);
197
- e.zt[1].push(...this.zt[1]);
198
- this.zt = [ [], [] ];
199
- for (let t = 0; t < this.wt.length; t++) {
200
- let i = this.wt[t];
201
- let n = e.wt[t];
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
- zt: [ [], [] ],
205
- wt: []
231
+ Mt: [ [], [] ],
232
+ Qt: []
206
233
  };
207
- e.wt[t] = n;
234
+ e.Qt[t] = n;
208
235
  }
209
236
  i.stashQueues(n);
210
237
  }
211
238
  }
212
239
  restoreQueues(e) {
213
- this.zt[0].push(...e.zt[0]);
214
- this.zt[1].push(...e.zt[1]);
215
- for (let t = 0; t < e.wt.length; t++) {
216
- const i = e.wt[t];
217
- let n = this.wt[t];
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
- Bt=false;
225
- xt=null;
226
- Yt=[];
227
- Be=[];
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 Kt=null;
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 _=null;
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 vt=null;
289
+ static kt=null;
265
290
  static tt=null;
266
291
  static nt=null;
267
- static Zt=null;
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 Ut=null;
277
- static yt=null;
278
- static Wt=null;
279
- static jt=null;
280
- static Mt=null;
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 kt=null;
309
+ static vt=null;
285
310
  static $e=null;
286
311
  static et=null;
287
312
  static Xe=null;
288
- static Vt=null;
313
+ static mn=null;
289
314
  flush() {
290
- if (this.Bt) return;
291
- this.Bt = true;
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,48 @@ class GlobalQueue extends Queue {
297
322
  if (!e) {
298
323
  const e = activeTransition;
299
324
  runHeap(zombieQueue, GlobalQueue.Ce);
300
- this.xt = null;
301
- this.Yt = [];
302
- this.Be = [];
303
- this.N = [];
304
- this.Lt = new Set;
325
+ // Detach: the stashed transition keeps its batch; ambient work that
326
+ // follows lands in a fresh one.
327
+ currentBatch = this.N = createBatch();
305
328
  // Run lane effects immediately (before stashing) - lanes with no pending async
306
329
  if (activeLanes.size) {
307
- GlobalQueue.Mt(EFFECT_RENDER);
308
- GlobalQueue.Mt(EFFECT_USER);
330
+ GlobalQueue.On(EFFECT_RENDER);
331
+ GlobalQueue.On(EFFECT_USER);
309
332
  }
310
- this.stashQueues(e.Jt);
333
+ this.stashQueues(e.Bt);
311
334
  clock++;
312
335
  scheduled = dirtyQueue.EE >= dirtyQueue.Le;
313
- reassignPendingTransition(e.Yt);
336
+ reassignPendingTransition(e.yt);
314
337
  activeTransition = null;
315
338
  // The stash pass (committed-view rerun of plain optimistic signals)
316
339
  // wraps finalizePureQueue in the engine; a non-empty _optimisticNodes
317
340
  // means _optimisticWrite ran, which installed the hook.
318
- if (!e.Te.length && !e.qt.size && e.Be.length) {
319
- GlobalQueue.yt(e);
341
+ if (!e.Te.length && !e.Lt.size && e.Be.length) {
342
+ GlobalQueue.Tn(e);
320
343
  } else {
321
344
  finalizePureQueue(null, true);
322
345
  }
323
346
  return;
324
347
  }
325
- this.Yt !== activeTransition.Yt && this.Yt.push(...activeTransition.Yt);
326
- this.restoreQueues(activeTransition.Jt);
327
- transitions.delete(activeTransition);
328
348
  const t = activeTransition;
349
+ const i = this.N;
350
+ i !== t && i.yt.push(...t.yt);
351
+ this.restoreQueues(t.Bt);
352
+ transitions.delete(t);
329
353
  activeTransition = null;
330
- reassignPendingTransition(this.Yt);
354
+ reassignPendingTransition(i.yt);
331
355
  finalizePureQueue(t);
356
+ if (i === t) {
357
+ // Drop the dead Transition wrapper but keep its (drained) containers
358
+ // as the ambient batch — late registrations during finalization live
359
+ // there and must survive to the next flush.
360
+ const e = createBatch();
361
+ e.yt = i.yt;
362
+ e.Be = i.Be;
363
+ e._ = i._;
364
+ e.dn = i.dn;
365
+ currentBatch = this.N = e;
366
+ }
332
367
  } else {
333
368
  if (canUseSimpleSyncFlush(this)) {
334
369
  commitPendingNodes();
@@ -345,15 +380,15 @@ class GlobalQueue extends Queue {
345
380
  // Check if finalization added items to the heap (from optimistic reversion)
346
381
  scheduled = dirtyQueue.EE >= dirtyQueue.Le;
347
382
  // Run lane effects first (for ready lanes), then regular effects
348
- activeLanes.size && GlobalQueue.Mt(EFFECT_RENDER);
383
+ activeLanes.size && GlobalQueue.On(EFFECT_RENDER);
349
384
  this.run(EFFECT_RENDER);
350
- activeLanes.size && GlobalQueue.Mt(EFFECT_USER);
385
+ activeLanes.size && GlobalQueue.On(EFFECT_USER);
351
386
  this.run(EFFECT_USER);
352
387
  if (false) ;
353
388
  if (false && !scheduled && !activeTransition && transitions.size === 0 && activeLanes.size === 0) ;
354
389
  if (false) ;
355
390
  } finally {
356
- this.Bt = false;
391
+ this.Ut = false;
357
392
  }
358
393
  }
359
394
  notify(e, t, i, n) {
@@ -363,8 +398,8 @@ class GlobalQueue extends Queue {
363
398
  const t = n !== undefined ? n : e.k;
364
399
  if (activeTransition && t) {
365
400
  const i = t.source;
366
- let n = activeTransition.qt.get(i);
367
- if (!n) activeTransition.qt.set(i, n = new Set);
401
+ let n = activeTransition.Lt.get(i);
402
+ if (!n) activeTransition.Lt.set(i, n = new Set);
368
403
  const s = n.size;
369
404
  n.add(e);
370
405
  if (n.size !== s) schedule();
@@ -379,21 +414,7 @@ class GlobalQueue extends Queue {
379
414
  if (e && e === activeTransition) return;
380
415
  if (!e && activeTransition && activeTransition.Ie === clock) return;
381
416
  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
- };
417
+ activeTransition = e ?? createBatch();
397
418
  } else if (e) {
398
419
  const t = activeTransition;
399
420
  mergeTransitionState(e, t);
@@ -402,61 +423,37 @@ class GlobalQueue extends Queue {
402
423
  }
403
424
  transitions.add(activeTransition);
404
425
  activeTransition.Ie = clock;
405
- if (this.xt !== null) {
406
- this.xt.ve = activeTransition;
407
- activeTransition.Yt.push(this.xt);
408
- this.xt = null;
409
- }
410
- if (this.Yt !== activeTransition.Yt) {
411
- for (let e = 0; e < this.Yt.length; e++) {
412
- const t = this.Yt[e];
413
- t.ve = activeTransition;
414
- activeTransition.Yt.push(t);
426
+ const t = this.N;
427
+ if (t !== activeTransition) {
428
+ // Adopt the ambient batch into the transaction, then make the
429
+ // transaction the batch so later registrations land there directly.
430
+ // Pending and optimistic nodes are re-stamped as the transaction's;
431
+ // marks don't hijack the node's _transition — a mark on a plain signal
432
+ // must not entangle unrelated writes to it; the same rule holds one hop
433
+ // downstream: propagation never queues pended subscribers as pending
434
+ // nodes, see propagateAffectsMark, #2893.
435
+ for (let e = 0; e < t.yt.length; e++) {
436
+ const i = t.yt[e];
437
+ i.ve = activeTransition;
438
+ activeTransition.yt.push(i);
415
439
  }
416
- this.Yt = activeTransition.Yt;
417
- }
418
- if (this.Be !== activeTransition.Be) {
419
- for (let e = 0; e < this.Be.length; e++) {
420
- const t = this.Be[e];
421
- t.ve = activeTransition;
422
- activeTransition.Be.push(t);
440
+ for (let e = 0; e < t.Be.length; e++) {
441
+ const i = t.Be[e];
442
+ i.ve = activeTransition;
443
+ activeTransition.Be.push(i);
423
444
  }
424
- this.Be = activeTransition.Be;
425
- }
426
- if (this.N !== activeTransition.N) {
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;
445
+ if (t._.length) activeTransition._.push(...t._);
446
+ for (const e of t.dn) activeTransition.dn.add(e);
447
+ currentBatch = this.N = activeTransition;
435
448
  }
436
449
  for (const e of activeLanes) {
437
450
  if (!e.ve) e.ve = activeTransition;
438
451
  }
439
- if (this.Lt !== activeTransition.Lt) {
440
- for (const e of this.Lt) activeTransition.Lt.add(e);
441
- this.Lt = activeTransition.Lt;
442
- }
443
452
  }
444
453
  }
445
454
 
446
455
  function queuePendingNode(e) {
447
- if (activeTransition) {
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);
456
+ currentBatch.yt.push(e);
460
457
  }
461
458
 
462
459
  // Sticky: flips true on the first refresh() ever (the only setter of
@@ -474,23 +471,23 @@ function insertSubs(e, t = false) {
474
471
  const i = e.Je || currentOptimisticLane;
475
472
  const n = e.Ye !== undefined;
476
473
  const s = reaskArmed;
477
- for (let a = e.p; a !== null; a = a.de) {
474
+ for (let r = e.p; r !== null; r = r.de) {
478
475
  // A value-change notification is a new question for the subscriber: any
479
476
  // pending re-ask mark (refresh) it carried is superseded.
480
- if (s) a.Ee.u &= ~REACTIVE_REASK;
481
- if (n && a.Ee.U & CONFIG_IN_SNAPSHOT_SCOPE) {
482
- a.Ee.u |= REACTIVE_SNAPSHOT_STALE;
477
+ if (s) r.Ee.u &= ~REACTIVE_REASK;
478
+ if (n && r.Ee.U & CONFIG_IN_SNAPSHOT_SCOPE) {
479
+ r.Ee.u |= REACTIVE_SNAPSHOT_STALE;
483
480
  continue;
484
481
  }
485
482
  if (t && i) {
486
- a.Ee.u |= REACTIVE_OPTIMISTIC_DIRTY;
487
- assignOrMergeLane(a.Ee, i);
483
+ r.Ee.u |= REACTIVE_OPTIMISTIC_DIRTY;
484
+ assignOrMergeLane(r.Ee, i);
488
485
  } else if (t) {
489
- a.Ee.u |= REACTIVE_OPTIMISTIC_DIRTY;
486
+ r.Ee.u |= REACTIVE_OPTIMISTIC_DIRTY;
490
487
  // No source lane means reversion - clear subscriber's lane so effects go to regular queue
491
- a.Ee.Je = undefined;
488
+ r.Ee.Je = undefined;
492
489
  }
493
- enqueueSub(a.Ee);
490
+ enqueueSub(r.Ee);
494
491
  }
495
492
  }
496
493
 
@@ -517,11 +514,7 @@ function commitPendingNode(e) {
517
514
  }
518
515
 
519
516
  function commitPendingNodes() {
520
- if (globalQueue.xt !== null) {
521
- commitPendingNode(globalQueue.xt);
522
- globalQueue.xt = null;
523
- }
524
- const e = globalQueue.Yt;
517
+ const e = currentBatch.yt;
525
518
  for (let t = 0; t < e.length; t++) {
526
519
  commitPendingNode(e[t]);
527
520
  }
@@ -533,42 +526,42 @@ function finalizePureQueue(e = null, t = false) {
533
526
  // For completing transitions or no-transition, resolve pending and revert optimistic
534
527
  const i = !t;
535
528
  if (i) commitPendingNodes();
536
- if (!t && globalQueue.wt.length) checkBoundaryChildren(globalQueue);
529
+ if (!t && globalQueue.Qt.length) checkBoundaryChildren(globalQueue);
537
530
  const n = dirtyQueue.EE >= dirtyQueue.Le;
538
531
  if (n) runHeap(dirtyQueue, GlobalQueue.Ce);
539
532
  if (i) {
540
533
  if (n) commitPendingNodes();
534
+ // The settling batch: the completing transaction's, or the ambient one.
535
+ const t = e ?? globalQueue.N;
541
536
  // Optimistic reversion: a non-empty batch means _optimisticWrite ran,
542
537
  // which installed the engine's hooks.
543
- const t = e ? e.Be : globalQueue.Be;
544
- if (t.length) GlobalQueue.Ut(t);
538
+ if (t.Be.length) GlobalQueue.En(t.Be);
545
539
  // Replay entanglement: subs recorded by the read-time gate get rescheduled
546
540
  // so they re-run with the now-committed values visible.
547
- if (e && e.Qt.size) {
548
- for (const t of e.Qt) {
541
+ if (e && e.un.size) {
542
+ for (const t of e.un) {
549
543
  if (t.u & REACTIVE_DISPOSED) continue;
550
544
  enqueueSub(t);
551
545
  }
552
- e.Qt.clear();
546
+ e.un.clear();
553
547
  }
554
548
  // Declared motion ends with the transaction: settle (or plain flush end
555
549
  // for ambient marks) releases each registration's refcount. A non-empty
556
550
  // batch means registerAffectsMark ran, which installed the hook.
557
- const i = e ? e.N : globalQueue.N;
558
- if (i.length) GlobalQueue.h(i);
559
- const s = e ? e.Lt : globalQueue.Lt;
551
+ if (t._.length) GlobalQueue.h(t._);
560
552
  // A non-empty set means trackOptimisticStore ran, which installed the
561
553
  // 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
- if (s.size) GlobalQueue.Kt(s);
554
+ // core lets esbuild shake it — rollup already folds the null guard). The
555
+ // completing transition scopes the clear to its own layer keys (#2899).
556
+ if (t.dn.size) GlobalQueue.Vt(t.dn, e);
564
557
  sweepTransientStoreNodes();
565
558
  // Lanes only enter activeLanes through the engine's getOrCreateLane.
566
- if (activeLanes.size) GlobalQueue.jt(e);
559
+ if (activeLanes.size) GlobalQueue.Nn(e);
567
560
  }
568
561
  }
569
562
 
570
563
  function checkBoundaryChildren(e) {
571
- for (const t of e.wt) {
564
+ for (const t of e.Qt) {
572
565
  t.Se?.();
573
566
  checkBoundaryChildren(t);
574
567
  }
@@ -598,6 +591,13 @@ function reassignPendingTransition(e) {
598
591
 
599
592
  const globalQueue = new GlobalQueue;
600
593
 
594
+ // Hot-path mirror of `globalQueue._batch`: `queuePendingNode` runs once per
595
+ // staged write and `commitPendingNodes` once per flush, and the extra
596
+ // property hop through `_batch` was a measured instruction-count regression
597
+ // (CodSpeed update1to1, PR #2905). The field stays authoritative for
598
+ // cross-module readers; every `_batch` assignment updates both.
599
+ let currentBatch = globalQueue.N;
600
+
601
601
  function flush(e) {
602
602
  if (e) {
603
603
  syncDepth++;
@@ -613,7 +613,7 @@ function flush(e) {
613
613
  }
614
614
  }
615
615
  }
616
- if (globalQueue.Bt) {
616
+ if (globalQueue.Ut) {
617
617
  return;
618
618
  }
619
619
  if (halted) return;
@@ -630,7 +630,7 @@ function runQueue(e, t) {
630
630
 
631
631
  function reporterBlocksSource(e, t) {
632
632
  if (e.u & (REACTIVE_ZOMBIE | REACTIVE_DISPOSED)) return false;
633
- if (e.m === t || e.M?.has(t)) return true;
633
+ if (e.m?.has(t)) return true;
634
634
  for (let i = e.S; i; i = i.st) {
635
635
  let e = i.ot;
636
636
  while (e) {
@@ -642,10 +642,10 @@ function reporterBlocksSource(e, t) {
642
642
  }
643
643
 
644
644
  function transitionComplete(e) {
645
- if (e.Ht) return true;
645
+ if (e.cn) return true;
646
646
  if (e.Te.length) return false;
647
647
  let t = true;
648
- for (const [i, n] of e.qt) {
648
+ for (const [i, n] of e.Lt) {
649
649
  let s = false;
650
650
  for (const e of n) {
651
651
  if (reporterBlocksSource(e, i)) {
@@ -654,7 +654,7 @@ function transitionComplete(e) {
654
654
  }
655
655
  n.delete(e);
656
656
  }
657
- if (!s) e.qt.delete(i); else if (i.i & STATUS_PENDING && i.k?.source === i) {
657
+ if (!s) e.Lt.delete(i); else if (i.i & STATUS_PENDING && i.k?.source === i) {
658
658
  t = false;
659
659
  break;
660
660
  }
@@ -662,13 +662,13 @@ function transitionComplete(e) {
662
662
  // Override blockage lives with the engine. Absent hook = "no optimistic
663
663
  // blockage", which is exact: only _optimisticWrite (engine) pushes to
664
664
  // _optimisticNodes, so without the engine the loop was vacuous anyway.
665
- if (t && e.Be.length && GlobalQueue.Wt(e)) t = false;
666
- t && (e.Ht = true);
665
+ if (t && e.Be.length && GlobalQueue.In(e)) t = false;
666
+ t && (e.cn = true);
667
667
  return t;
668
668
  }
669
669
 
670
670
  function currentTransition(e) {
671
- while (e.Ht && typeof e.Ht === "object") e = e.Ht;
671
+ while (e.cn && typeof e.cn === "object") e = e.cn;
672
672
  return e;
673
673
  }
674
674