@solidjs/signals 2.0.0-rc.2 → 2.0.0-rc.4

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 (57) hide show
  1. package/dist/dev.js +1454 -118
  2. package/dist/node.cjs +2709 -1396
  3. package/dist/prod/affects.js +13 -12
  4. package/dist/prod/boundaries.js +39 -34
  5. package/dist/prod/core/action.js +3 -3
  6. package/dist/prod/core/async.js +48 -46
  7. package/dist/prod/core/core.js +99 -67
  8. package/dist/prod/core/effect.js +25 -28
  9. package/dist/prod/core/external.js +2 -2
  10. package/dist/prod/core/graph.js +85 -49
  11. package/dist/prod/core/heap.js +10 -10
  12. package/dist/prod/core/lanes.js +19 -19
  13. package/dist/prod/core/optimistic.js +66 -41
  14. package/dist/prod/core/owner.js +13 -13
  15. package/dist/prod/core/scheduler.js +131 -85
  16. package/dist/prod/core/verdict.js +36 -15
  17. package/dist/prod/index.js +4 -0
  18. package/dist/prod/map.js +101 -101
  19. package/dist/prod/signals.js +1 -1
  20. package/dist/prod/store/index.js +2 -0
  21. package/dist/prod/store/next/optimistic.js +65 -11
  22. package/dist/prod/store/next/patch-hooks.js +13 -0
  23. package/dist/prod/store/next/patch.js +614 -0
  24. package/dist/prod/store/next/projection.js +107 -45
  25. package/dist/prod/store/next/reconcile.js +307 -120
  26. package/dist/prod/store/next/store.js +321 -92
  27. package/dist/prod/store/next/target.js +13 -4
  28. package/dist/prod/store/store.js +5 -5
  29. package/dist/types/core/core.d.ts +15 -1
  30. package/dist/types/core/dev.d.ts +8 -0
  31. package/dist/types/core/graph.d.ts +22 -0
  32. package/dist/types/core/invariants.d.ts +1 -1
  33. package/dist/types/core/scheduler.d.ts +12 -0
  34. package/dist/types/store/index.d.ts +2 -0
  35. package/dist/types/store/next/patch-hooks.d.ts +41 -0
  36. package/dist/types/store/next/patch.d.ts +91 -0
  37. package/dist/types/store/next/reconcile.d.ts +14 -0
  38. package/dist/types/store/next/store.d.ts +30 -2
  39. package/dist/types/store/next/target.d.ts +58 -8
  40. package/dist/types-cjs/core/core.d.cts +15 -1
  41. package/dist/types-cjs/core/dev.d.cts +8 -0
  42. package/dist/types-cjs/core/graph.d.cts +22 -0
  43. package/dist/types-cjs/core/invariants.d.cts +1 -1
  44. package/dist/types-cjs/core/scheduler.d.cts +12 -0
  45. package/dist/types-cjs/store/index.d.cts +2 -0
  46. package/dist/types-cjs/store/next/patch-hooks.d.cts +41 -0
  47. package/dist/types-cjs/store/next/patch.d.cts +91 -0
  48. package/dist/types-cjs/store/next/reconcile.d.cts +14 -0
  49. package/dist/types-cjs/store/next/store.d.cts +30 -2
  50. package/dist/types-cjs/store/next/target.d.cts +58 -8
  51. package/package.json +14 -14
  52. package/dist/types/store/optimistic.d.ts +0 -45
  53. package/dist/types/store/projection.d.ts +0 -70
  54. package/dist/types/store/reconcile.d.ts +0 -46
  55. package/dist/types-cjs/store/optimistic.d.cts +0 -45
  56. package/dist/types-cjs/store/projection.d.cts +0 -70
  57. package/dist/types-cjs/store/reconcile.d.cts +0 -46
@@ -6,6 +6,8 @@ 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
13
  import { activeLanes, assignOrMergeLane, findLane } from "./lanes.js";
@@ -60,8 +62,8 @@ let projectionWriteActive = false;
60
62
  const transientStoreNodes = new Set;
61
63
 
62
64
  function canUseSimpleSyncFlush(e) {
63
- const t = e.k;
64
- return transitions.size === 0 && activeLanes.size === 0 && e.Qt.length === 0 && t.Ke.length === 0 && t.m.length === 0 && t.cn.size === 0 && transientStoreNodes.size === 0;
65
+ const t = e.m;
66
+ return transitions.size === 0 && activeLanes.size === 0 && e.Qt.length === 0 && t.Ke.length === 0 && t.A.length === 0 && t.Tn.size === 0 && transientStoreNodes.size === 0;
65
67
  }
66
68
 
67
69
  function sweepTransientStoreNodes() {
@@ -109,21 +111,21 @@ function setProjectionWriteActive(e) {
109
111
  yt: [],
110
112
  Ne: new Map,
111
113
  Ke: [],
112
- m: [],
113
- cn: new Set,
114
- oe: [],
114
+ A: [],
115
+ Tn: new Set,
116
+ ue: [],
115
117
  bt: {
116
118
  Lt: [ [], [] ],
117
119
  Qt: []
118
120
  },
119
- an: false,
120
- ln: new Set
121
+ fn: false,
122
+ cn: new Set
121
123
  };
122
124
  }
123
125
 
124
126
  function mergeTransitionState(e, t) {
125
- t.an = e;
126
- e.oe.push(...t.oe);
127
+ t.fn = e;
128
+ e.ue.push(...t.ue);
127
129
  for (const i of activeLanes) if (i._e === t) i._e = e;
128
130
  if (t.Ke.length) {
129
131
  // Move (don't copy): the global queue's batch may still be the outgoing
@@ -132,20 +134,40 @@ function mergeTransitionState(e, t) {
132
134
  e.Ke.push(...t.Ke);
133
135
  t.Ke.length = 0;
134
136
  }
135
- if (t.m.length) {
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.m.push(...t.m);
140
- t.m.length = 0;
141
+ e.A.push(...t.A);
142
+ t.A.length = 0;
143
+ }
144
+ for (const i of t.Tn) e.Tn.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.Mt;
152
+ if (i !== undefined) {
153
+ t.Mt = undefined;
154
+ let n = e.Mt;
155
+ if (n !== undefined) n.push(...i); else n = e.Mt = 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
+ }
141
164
  }
142
- for (const i of t.cn) e.cn.add(i);
143
165
  for (const [i, n] of t.Ne) {
144
166
  let t = e.Ne.get(i);
145
167
  if (!t) e.Ne.set(i, t = new Set);
146
168
  for (const e of n) t.add(e);
147
169
  }
148
- for (const i of t.ln) e.ln.add(i);
170
+ for (const i of t.cn) e.cn.add(i);
149
171
  }
150
172
 
151
173
  function schedule() {
@@ -155,7 +177,7 @@ function schedule() {
155
177
  }
156
178
  if (scheduled) return;
157
179
  scheduled = true;
158
- if (!syncDepth && !globalQueue.sn && !projectionWriteActive) queueMicrotask(flush);
180
+ if (!syncDepth && !globalQueue.En && !projectionWriteActive) queueMicrotask(flush);
159
181
  }
160
182
 
161
183
  /**
@@ -189,24 +211,24 @@ function notifyHalted() {
189
211
  let queueRunToken = 0;
190
212
 
191
213
  class Queue {
192
- ve=null;
214
+ ke=null;
193
215
  Lt=[ [], [] ];
194
216
  Qt=[];
195
- Mt=0;
217
+ Vt=0;
196
218
  created=clock;
197
219
  addChild(e) {
198
220
  this.Qt.push(e);
199
- e.ve = this;
221
+ e.ke = this;
200
222
  }
201
223
  removeChild(e) {
202
224
  const t = this.Qt.indexOf(e);
203
225
  if (t >= 0) {
204
226
  this.Qt.splice(t, 1);
205
- e.ve = null;
227
+ e.ke = null;
206
228
  }
207
229
  }
208
230
  notify(e, t, i, n) {
209
- if (this.ve) return this.ve.notify(e, t, i, n);
231
+ if (this.ke) return this.ke.notify(e, t, i, n);
210
232
  return false;
211
233
  }
212
234
  run(e) {
@@ -226,8 +248,8 @@ class Queue {
226
248
  const i = ++queueRunToken;
227
249
  for (let n = 0; n < t.length; ) {
228
250
  const s = t[n];
229
- if (s.Mt !== i) {
230
- s.Mt = i;
251
+ if (s.Vt !== i) {
252
+ s.Vt = i;
231
253
  s.run?.(e);
232
254
  if (t[n] !== s) {
233
255
  n = 0;
@@ -242,7 +264,7 @@ class Queue {
242
264
  // Route to lane's effect queue if we're in an optimistic recomputation
243
265
  if (currentOptimisticLane) {
244
266
  const i = findLane(currentOptimisticLane);
245
- i.en[e - 1].push(t);
267
+ i.rn[e - 1].push(t);
246
268
  } else {
247
269
  this.Lt[e - 1].push(t);
248
270
  }
@@ -278,25 +300,25 @@ class Queue {
278
300
  }
279
301
 
280
302
  class GlobalQueue extends Queue {
281
- sn=false;
303
+ En=false;
282
304
  // The current transaction-shaped batch: a plain ambient batch while no
283
305
  // transition is active, the active transition itself after initTransition.
284
- k=createBatch();
306
+ m=createBatch();
285
307
  static Ce;
286
308
  static Fe;
287
309
  static tt;
288
- static Vt=null;
310
+ static Bt=null;
289
311
  // Store-side hook: drops a keyless affects() mark's identity scope when the
290
312
  // carrier node's last registration releases (wired by store.ts, mirroring
291
313
  // _clearOptimisticStore).
292
- static G=null;
314
+ static p=null;
293
315
  // affects()-side hooks (wired by affects.ts, mirroring _update): the mark
294
316
  // engine — count/register/release — lives with the feature. Every call site
295
317
  // is gated by state only that module creates, so `!` invocations are safe
296
318
  // once the gate holds.
319
+ static G=null;
297
320
  static M=null;
298
- static h=null;
299
- static j=null;
321
+ static N=null;
300
322
  // External-source bridge (wired by enableExternalSource(); null while no
301
323
  // config is active — including after _resetExternalSourceConfig()).
302
324
  static Rt=null;
@@ -315,11 +337,11 @@ class GlobalQueue extends Queue {
315
337
  static Dt=null;
316
338
  static Ht=null;
317
339
  static Je=null;
318
- static p=null;
319
- static Bt=null;
340
+ static k=null;
341
+ static wt=null;
320
342
  // Re-asks probes whose verdict was provisionally suppressed by a fresh read
321
343
  // of a held value, once the transaction gains an async blocker (#3028).
322
- static wt=null;
344
+ static jt=null;
323
345
  // Optimistic-engine hooks (wired by core/optimistic.ts via
324
346
  // installOptimisticEngine(), called from verdict.ts / createOptimistic /
325
347
  // createOptimisticStore — every module that can create optimistic state).
@@ -327,39 +349,51 @@ class GlobalQueue extends Queue {
327
349
  // `_overrideValue` slot, a lane in `activeLanes`, an `_optimisticNodes`
328
350
  // entry, or a non-null `currentOptimisticLane`, so `!` invocations are safe
329
351
  // once the gate holds.
330
- static vt=null;
331
- static fn=null;
352
+ static kt=null;
332
353
  static dn=null;
333
- static En=null;
334
- static Tn=null;
354
+ static In=null;
355
+ static Nn=null;
356
+ static _n=null;
357
+ /** Patch-channel optimistic drain (next/patch.ts): optimistic emissions
358
+ * apply at lane-effect timing — visible in flight, unlike the regular
359
+ * effect queues an action stashes. Injected; null when unused. */
360
+ static ln=null;
335
361
  static Ft=null;
336
362
  static ht=null;
337
363
  static gt=null;
338
364
  static je=null;
339
365
  static $e=null;
340
366
  static ze=null;
341
- static Nn=null;
367
+ static An=null;
342
368
  flush() {
343
- if (this.sn) return;
369
+ if (this.En) return;
344
370
  // Fast drain: nothing in flight but plain pending commits — no dirty
345
371
  // computeds, no queued effects, no child queues, no transitions/lanes/
346
372
  // optimistic state. Commit and go; anything a commit hook schedules
347
373
  // (companion snaps, store folds notifying subs) re-arms `scheduled`
348
374
  // below and the outer drain loop takes the full spine next round.
349
375
  if (activeTransition === null && dirtyQueue.EE < dirtyQueue.xe && this.Lt[0].length === 0 && this.Lt[1].length === 0 && this.Qt.length === 0 && canUseSimpleSyncFlush(this)) {
350
- this.sn = true;
376
+ this.En = true;
351
377
  try {
378
+ // Sweep first: unobserved() pulls swept nodes out of the dirty heap,
379
+ // so a dormant memo dirtied in the same tick is reclaimed instead of
380
+ // recomputed (matching the old inline dispose-on-read counts).
381
+ sweepDormant();
352
382
  commitPendingNodes();
353
383
  } finally {
354
- this.sn = false;
384
+ this.En = false;
355
385
  }
356
386
  clock++;
357
- scheduled = dirtyQueue.EE >= dirtyQueue.xe || this.Lt[0].length !== 0 || this.Lt[1].length !== 0 || this.k.yt.length !== 0;
387
+ scheduled = dirtyQueue.EE >= dirtyQueue.xe || this.Lt[0].length !== 0 || this.Lt[1].length !== 0 || this.m.yt.length !== 0;
358
388
  return;
359
389
  }
360
- this.sn = true;
390
+ this.En = true;
361
391
  try {
362
392
  if (false) ;
393
+ // Before runHeap for the same reason as the fast drain above; late
394
+ // subscribers (an effect reading a swept memo this flush) revive it,
395
+ // which is the pay-for-use contract.
396
+ sweepDormant();
363
397
  runHeap(dirtyQueue, GlobalQueue.Ce);
364
398
  if (activeTransition) {
365
399
  const e = transitionComplete(activeTransition);
@@ -373,32 +407,32 @@ class GlobalQueue extends Queue {
373
407
  // zombies here. Height-adjust entries still process normally: a
374
408
  // dirtied one keeps its height flag and falls through to runHeap's
375
409
  // adjustHeight path on the next pass of the bucket.
376
- runHeap(zombieQueue, this.k === e ? cancelZombieRecompute : GlobalQueue.Ce);
410
+ runHeap(zombieQueue, this.m === e ? cancelZombieRecompute : GlobalQueue.Ce);
377
411
  // Detach: the stashed transition keeps its batch; ambient work that
378
412
  // follows lands in a fresh one. If the batch is already a separate
379
413
  // ambient one — action done() restored activeTransition without
380
414
  // adopting the batch, and an ordinary write landed there before
381
415
  // the scheduled flush (#2916) — keep it: replacing it would strand
382
416
  // its queued pending nodes with held _pendingValues forever.
383
- if (this.k === e) currentBatch = this.k = createBatch();
417
+ if (this.m === e) currentBatch = this.m = createBatch();
384
418
  // Run lane effects immediately (before stashing) - lanes with no pending async
385
419
  if (activeLanes.size) {
386
- GlobalQueue.Tn(EFFECT_RENDER);
387
- GlobalQueue.Tn(EFFECT_USER);
420
+ GlobalQueue._n(EFFECT_RENDER);
421
+ GlobalQueue._n(EFFECT_USER);
388
422
  }
389
423
  this.stashQueues(e.bt);
390
424
  clock++;
391
425
  // A kept ambient batch may hold pending nodes (#2916): stay
392
426
  // scheduled so the outer drain loop commits them via the plain
393
427
  // flush path instead of leaving them until the next natural flush.
394
- scheduled = dirtyQueue.EE >= dirtyQueue.xe || this.k.yt.length > 0;
428
+ scheduled = dirtyQueue.EE >= dirtyQueue.xe || this.m.yt.length > 0;
395
429
  reassignPendingTransition(e.yt);
396
430
  activeTransition = null;
397
431
  finalizePureQueue(null, true);
398
432
  return;
399
433
  }
400
434
  const t = activeTransition;
401
- const i = this.k;
435
+ const i = this.m;
402
436
  i !== t && i.yt.push(...t.yt);
403
437
  this.restoreQueues(t.bt);
404
438
  transitions.delete(t);
@@ -412,9 +446,9 @@ class GlobalQueue extends Queue {
412
446
  const e = createBatch();
413
447
  e.yt = i.yt;
414
448
  e.Ke = i.Ke;
415
- e.m = i.m;
416
- e.cn = i.cn;
417
- currentBatch = this.k = e;
449
+ e.A = i.A;
450
+ e.Tn = i.Tn;
451
+ currentBatch = this.m = e;
418
452
  }
419
453
  } else {
420
454
  if (canUseSimpleSyncFlush(this)) {
@@ -432,15 +466,15 @@ class GlobalQueue extends Queue {
432
466
  // Check if finalization added items to the heap (from optimistic reversion)
433
467
  scheduled = dirtyQueue.EE >= dirtyQueue.xe;
434
468
  // Run lane effects first (for ready lanes), then regular effects
435
- activeLanes.size && GlobalQueue.Tn(EFFECT_RENDER);
469
+ activeLanes.size && GlobalQueue._n(EFFECT_RENDER);
436
470
  this.run(EFFECT_RENDER);
437
- activeLanes.size && GlobalQueue.Tn(EFFECT_USER);
471
+ activeLanes.size && GlobalQueue._n(EFFECT_USER);
438
472
  this.run(EFFECT_USER);
439
473
  if (false) ;
440
474
  if (false && !scheduled && !activeTransition && transitions.size === 0 && activeLanes.size === 0) ;
441
475
  if (false) ;
442
476
  } finally {
443
- this.sn = false;
477
+ this.En = false;
444
478
  }
445
479
  }
446
480
  notify(e, t, i, n) {
@@ -452,7 +486,7 @@ class GlobalQueue extends Queue {
452
486
  // channel) updates display state on its way up but must be invisible
453
487
  // to completion accounting BY CONSTRUCTION: it never registers a
454
488
  // reporter and never counts toward the loading-boundary diagnostic.
455
- if (t?.i) return true;
489
+ if (t?.l) return true;
456
490
  if (activeTransition && t) {
457
491
  const i = t.source;
458
492
  let n = activeTransition.Ne.get(i);
@@ -461,7 +495,7 @@ class GlobalQueue extends Queue {
461
495
  n.add(e);
462
496
  if (n.size !== s) {
463
497
  schedule();
464
- GlobalQueue.wt?.(activeTransition);
498
+ GlobalQueue.jt?.(activeTransition);
465
499
  }
466
500
  }
467
501
  }
@@ -483,7 +517,7 @@ class GlobalQueue extends Queue {
483
517
  }
484
518
  transitions.add(activeTransition);
485
519
  activeTransition.Te = clock;
486
- const t = this.k;
520
+ const t = this.m;
487
521
  if (t !== activeTransition) {
488
522
  // Adopt the ambient batch into the transaction, then make the
489
523
  // transaction the batch so later registrations land there directly.
@@ -502,15 +536,15 @@ class GlobalQueue extends Queue {
502
536
  i._e = activeTransition;
503
537
  activeTransition.Ke.push(i);
504
538
  }
505
- if (t.m.length) activeTransition.m.push(...t.m);
506
- for (const e of t.cn) activeTransition.cn.add(e);
539
+ if (t.A.length) activeTransition.A.push(...t.A);
540
+ for (const e of t.Tn) activeTransition.Tn.add(e);
507
541
  // Gated readers recorded against the ambient batch move with it: their
508
542
  // replay-at-commit now happens at the transaction's completion.
509
- if (t.ln.size) {
510
- for (const e of t.ln) activeTransition.ln.add(e);
511
- t.ln.clear();
543
+ if (t.cn.size) {
544
+ for (const e of t.cn) activeTransition.cn.add(e);
545
+ t.cn.clear();
512
546
  }
513
- currentBatch = this.k = activeTransition;
547
+ currentBatch = this.m = activeTransition;
514
548
  }
515
549
  for (const e of activeLanes) {
516
550
  if (!e._e) e._e = activeTransition;
@@ -554,8 +588,8 @@ function insertSubs(e, t = false) {
554
588
  const n = (i & CONFIG_HAS_LANE ? e.o?.Be : undefined) || currentOptimisticLane;
555
589
  const s = (i & CONFIG_HAS_SNAPSHOT) !== 0 && e.o?.Qe !== undefined;
556
590
  const o = reaskArmed;
557
- for (let i = e.u; i !== null; i = i.fe) {
558
- const e = i.ae;
591
+ for (let i = e.u; i !== null; i = i.ae) {
592
+ const e = i.ce;
559
593
  // A value-change notification is a new question for the subscriber: any
560
594
  // pending re-ask mark (refresh) it carried is superseded.
561
595
  if (o) e.ie &= ~REACTIVE_REASK;
@@ -567,7 +601,7 @@ function insertSubs(e, t = false) {
567
601
  // pass either re-reads them fresh or trims them), and neither does the
568
602
  // tail link: it is the read IN FLIGHT — read() links before it pulls, so
569
603
  // this very commit is what that read returns.
570
- if (e.ie & REACTIVE_RECOMPUTING_DEPS && i.ll === e.Ze && i !== e.Ye) e.ie |= REACTIVE_MISSED_WAKE;
604
+ if (e.ie & REACTIVE_RECOMPUTING_DEPS && i.nn === e.Ze && i !== e.Ye) e.ie |= REACTIVE_MISSED_WAKE;
571
605
  if (s && e.T & CONFIG_IN_SNAPSHOT_SCOPE) {
572
606
  e.ie |= REACTIVE_SNAPSHOT_STALE;
573
607
  continue;
@@ -586,7 +620,7 @@ function insertSubs(e, t = false) {
586
620
 
587
621
  function commitPendingNode(e) {
588
622
  const t = e;
589
- if (!t.Se) {
623
+ if (!t.oe) {
590
624
  if (e.Pe !== NOT_PENDING) {
591
625
  e.be = e.Pe;
592
626
  e.Pe = NOT_PENDING;
@@ -621,6 +655,17 @@ function setStoreCommitHook(e) {
621
655
  storeCommitHook = e;
622
656
  }
623
657
 
658
+ /** Patch-channel release hook (next/patch.ts): transition-stamped patch
659
+ * emissions are released when THEIR batch commits. Transitions never
660
+ * abort: failed actions still commit (only optimistic overrides revert),
661
+ * and merged-away transitions hand their stash to the survivor
662
+ * (mergeTransitionState) — every stash drains exactly once. Injected like
663
+ * storeCommitHook to stay tree-shakeable. */ let patchCommitHook = null;
664
+
665
+ function setPatchCommitHook(e) {
666
+ patchCommitHook = e;
667
+ }
668
+
624
669
  function commitPendingNodes() {
625
670
  const e = currentBatch.yt;
626
671
  for (let t = 0; t < e.length; t++) {
@@ -628,6 +673,7 @@ function commitPendingNodes() {
628
673
  }
629
674
  e.length = 0;
630
675
  storeCommitHook?.();
676
+ patchCommitHook?.(currentBatch);
631
677
  }
632
678
 
633
679
  function finalizePureQueue(e = null, t = false) {
@@ -641,20 +687,20 @@ function finalizePureQueue(e = null, t = false) {
641
687
  if (i) {
642
688
  if (n) commitPendingNodes();
643
689
  // The settling batch: the completing transaction's, or the ambient one.
644
- const t = e ?? globalQueue.k;
690
+ const t = e ?? globalQueue.m;
645
691
  // Optimistic reversion: a non-empty batch means _optimisticWrite ran,
646
692
  // which installed the engine's hooks.
647
- if (t.Ke.length) GlobalQueue.fn(t.Ke);
693
+ if (t.Ke.length) GlobalQueue.dn(t.Ke);
648
694
  // Replay entanglement: subs recorded by the read-time gate get rescheduled
649
695
  // so they re-run with the now-committed values visible. The ambient batch
650
696
  // replays too — laneReadsCommitted records readers whose committed-view
651
697
  // read hid a same-tick plain write that just committed above (#2963).
652
- if (t.ln.size) {
653
- for (const e of t.ln) {
698
+ if (t.cn.size) {
699
+ for (const e of t.cn) {
654
700
  if (e.ie & REACTIVE_DISPOSED) continue;
655
701
  enqueueSub(e);
656
702
  }
657
- t.ln.clear();
703
+ t.cn.clear();
658
704
  // A completing transition keeps the outer flush loop alive by itself;
659
705
  // the ambient batch needs the re-arm or the replay sits in the heap
660
706
  // until the next unrelated write.
@@ -666,18 +712,18 @@ function finalizePureQueue(e = null, t = false) {
666
712
  // held boundary display state through the visual channel, and their
667
713
  // release is the display-state update point — re-run the boundary sweep
668
714
  // (the earlier sweep above ran while the marks were still live).
669
- if (t.m.length) {
670
- GlobalQueue.M(t.m);
715
+ if (t.A.length) {
716
+ GlobalQueue.G(t.A);
671
717
  if (globalQueue.Qt.length) checkBoundaryChildren(globalQueue);
672
718
  }
673
719
  // A non-empty set means trackOptimisticStore ran, which installed the
674
720
  // hook; the hook iterates, clears, and schedules (keeping the loop out of
675
721
  // core lets esbuild shake it — rollup already folds the null guard). The
676
722
  // completing transition scopes the clear to its own layer keys (#2899).
677
- if (t.cn.size) GlobalQueue.Vt(t.cn, e);
723
+ if (t.Tn.size) GlobalQueue.Bt(t.Tn, e);
678
724
  sweepTransientStoreNodes();
679
725
  // Lanes only enter activeLanes through the engine's getOrCreateLane.
680
- if (activeLanes.size) GlobalQueue.En(e);
726
+ if (activeLanes.size) GlobalQueue.Nn(e);
681
727
  }
682
728
  }
683
729
 
@@ -717,7 +763,7 @@ const globalQueue = new GlobalQueue;
717
763
  // property hop through `_batch` was a measured instruction-count regression
718
764
  // (CodSpeed update1to1, PR #2905). The field stays authoritative for
719
765
  // cross-module readers; every `_batch` assignment updates both.
720
- let currentBatch = globalQueue.k;
766
+ let currentBatch = globalQueue.m;
721
767
 
722
768
  function flush(e) {
723
769
  if (e) {
@@ -734,7 +780,7 @@ function flush(e) {
734
780
  }
735
781
  }
736
782
  }
737
- if (globalQueue.sn) {
783
+ if (globalQueue.En) {
738
784
  return;
739
785
  }
740
786
  if (halted) return;
@@ -756,15 +802,15 @@ function reporterBlocksSource(e, t) {
756
802
  let e = i.ut;
757
803
  while (e) {
758
804
  if (e === t || e.lt === t) return true;
759
- e = e.o?.It;
805
+ e = e.o?.Et;
760
806
  }
761
807
  }
762
808
  return !!(e.S & STATUS_PENDING && e.o?._ instanceof NotReadyError && e.o?._.source === t);
763
809
  }
764
810
 
765
811
  function transitionComplete(e) {
766
- if (e.an) return true;
767
- if (e.oe.length) return false;
812
+ if (e.fn) return true;
813
+ if (e.ue.length) return false;
768
814
  let t = true;
769
815
  for (const [i, n] of e.Ne) {
770
816
  let s = false;
@@ -783,13 +829,13 @@ function transitionComplete(e) {
783
829
  // Override blockage lives with the engine (absent hook = "no optimistic
784
830
  // blockage"); the hook's loops over _optimisticNodes/_optimisticStores are
785
831
  // no-ops when the transition holds neither, so no pre-check is needed.
786
- if (t && GlobalQueue.dn?.(e)) t = false;
787
- t && (e.an = true);
832
+ if (t && GlobalQueue.In?.(e)) t = false;
833
+ t && (e.fn = true);
788
834
  return t;
789
835
  }
790
836
 
791
837
  function currentTransition(e) {
792
- while (e.an && typeof e.an === "object") e = e.an;
838
+ while (e.fn && typeof e.fn === "object") e = e.fn;
793
839
  return e;
794
840
  }
795
841
 
@@ -803,4 +849,4 @@ function runInTransition(e, t) {
803
849
  }
804
850
  }
805
851
 
806
- export { GlobalQueue, Queue, activeAffectsMarks, activeLanes, activeTransition, armReaskClear, assignOrMergeLane, bumpNotifyEpoch, clock, currentTransition, dirtyQueue, enforceLoadingBoundary, finalizePureQueue, findLane, flush, globalQueue, haltReactivity, insertSubs, notifyEpoch, projectionWriteActive, queuePendingNode, reaskArmed, resetErrorHalt, runInTransition, schedule, setProjectionWriteActive, setStoreCommitHook, shiftAffectsMarks, storeCommitHook, zombieQueue };
852
+ export { GlobalQueue, Queue, activeAffectsMarks, activeLanes, activeTransition, armReaskClear, assignOrMergeLane, bumpNotifyEpoch, clock, currentTransition, dirtyQueue, enforceLoadingBoundary, finalizePureQueue, findLane, flush, globalQueue, haltReactivity, insertSubs, notifyEpoch, patchCommitHook, projectionWriteActive, queuePendingNode, reaskArmed, resetErrorHalt, runInTransition, schedule, setPatchCommitHook, setProjectionWriteActive, setStoreCommitHook, shiftAffectsMarks, storeCommitHook, zombieQueue };
@@ -50,7 +50,7 @@ let pendingProbe = null;
50
50
  const t = e.lt;
51
51
  if (!t) return;
52
52
  t.T |= CONFIG_CHILD_COMPANIONS;
53
- (ext(t).Nt ??= new Set).add(e);
53
+ (ext(t).It ??= new Set).add(e);
54
54
  }
55
55
 
56
56
  function getPendingSignal(e) {
@@ -63,7 +63,7 @@ function getPendingSignal(e) {
63
63
  ext(e).Ge = t;
64
64
  e.T |= CONFIG_HAS_COMPANIONS;
65
65
  markFirewallChildCompanions(e);
66
- ext(t).It = e;
66
+ ext(t).Et = e;
67
67
  if (computePendingState(e)) setSignal(t, true);
68
68
  }
69
69
  return t;
@@ -153,8 +153,8 @@ function computePendingState(e) {
153
153
  // so the one walk covers direct marks, derivation, and companion chains.
154
154
  if (markCovered(e)) return true;
155
155
  const n = e.lt;
156
- if (e.o?.It) {
157
- const t = e.o?.It;
156
+ if (e.o?.Et) {
157
+ const t = e.o?.Et;
158
158
  const n = t.lt || t;
159
159
  return newQuestionInFlight(n);
160
160
  }
@@ -185,7 +185,7 @@ function updatePendingSignal(e) {
185
185
  }
186
186
 
187
187
  function updateChildCompanions(e) {
188
- const t = e.o?.Nt;
188
+ const t = e.o?.It;
189
189
  if (t === undefined) return;
190
190
  for (const e of t) updatePendingSignal(e);
191
191
  }
@@ -205,8 +205,8 @@ function updateChildCompanions(e) {
205
205
  if (i.has(e)) return;
206
206
  i.add(e);
207
207
  if (e.o?.Ge || e.o?.ge) n(e);
208
- for (let t = e.u; t !== null; t = t.fe) visit(t.ae);
209
- for (let t = e.o?.l ?? null; t !== null; t = t.ce) {
208
+ for (let t = e.u; t !== null; t = t.ae) visit(t.ce);
209
+ for (let t = e.o?.i ?? null; t !== null; t = t.Se) {
210
210
  visit(t);
211
211
  }
212
212
  };
@@ -273,6 +273,13 @@ function snapCompanionsToState(e) {
273
273
 
274
274
  function getLatestValueComputed(e) {
275
275
  let t = e.o?.ge;
276
+ // A shadow disposed while unobserved (its gated reader unmounted at a
277
+ // landing) is a corpse: sync writes into it equality-swallow against its
278
+ // frozen _value, and a later read revives it via recompute — clearing
279
+ // DISPOSED and re-deriving from the committed view, so the banner showed
280
+ // the previous transition's target (#3041 follow-up). Treat it as absent;
281
+ // recreation backfills from the in-flight write below.
282
+ if (t && t.ie & REACTIVE_DISPOSED) t = undefined;
276
283
  if (!t) {
277
284
  const n = latestReadActive;
278
285
  setLatestReadActive(false);
@@ -285,7 +292,7 @@ function getLatestValueComputed(e) {
285
292
  ext(e).ge = t;
286
293
  e.T |= CONFIG_HAS_COMPANIONS;
287
294
  markFirewallChildCompanions(e);
288
- ext(t).It = e;
295
+ ext(t).Et = e;
289
296
  // Parent-child lane relationship
290
297
  // Backfill an in-flight write (mirrors getPendingSignal): the companion is
291
298
  // created lazily, possibly after the write was processed — syncCompanions
@@ -341,7 +348,7 @@ function getLatestValueComputed(e) {
341
348
 
342
349
  /** The isPending()-probe read path, installed as GlobalQueue._pendingCheck. */ function pendingCheckRead(e, t, n, i) {
343
350
  setPendingCheckActive(false);
344
- if (typeof e.Se === "function") prepareComputed(e, true);
351
+ if (typeof e.oe === "function") prepareComputed(e, true);
345
352
  const r = n.S;
346
353
  if (t && r & STATUS_PENDING && r & STATUS_UNINITIALIZED) {
347
354
  if (tracking && e !== t) link(e, t);
@@ -362,8 +369,22 @@ function getLatestValueComputed(e) {
362
369
  * (#3028).
363
370
  */ function heldAwaitingAsync(e) {
364
371
  const t = e._e;
365
- const n = t ? currentTransition(t) : null;
366
- if (!n || n.an) return false;
372
+ const n = t ? currentTransition(t) : activeTransition;
373
+ if (!n || n.fn) return false;
374
+ // A plain staged write (a signal/store leaf — no _fn) held while an action
375
+ // is still running is an INPUT to a computation still in flight (#3078):
376
+ // the pairing rule must not suppress the verdict, or a memo recomputing
377
+ // mid-action reads the staged value, gets told "not pending", and
378
+ // disagrees with a direct isPending() probe for the whole action window.
379
+ // A computed's staged value is the opposite case — a LANDED answer
380
+ // awaiting reveal — where the pairing rule stands even inside an open
381
+ // action (#2831: a reader that saw the new value must not also see
382
+ // pending); still-computing answers are covered by the reporter scan.
383
+ if (n.ue.length && !e.oe) return true;
384
+ // A node not yet stamped with a transition only qualifies through the
385
+ // action check above; the reporter scan below is for transition-held
386
+ // writes whose source async is still computing.
387
+ if (!t) return false;
367
388
  for (const [e, t] of n.Ne) {
368
389
  if (t.size && e.S & STATUS_PENDING && e.o?._?.source === e) return true;
369
390
  }
@@ -422,7 +443,7 @@ function isPending(e) {
422
443
  // this flush (a downstream async pends and holds it), the suppression was
423
444
  // wrong and the wrapper must re-ask (#3028). Remember who to wake — the
424
445
  // async registration (GlobalQueue.notify) triggers wakeSuppressedProbes.
425
- if (!i.found && i.suppressed.length && context && typeof context.Se === "function") {
446
+ if (!i.found && i.suppressed.length && context && typeof context.oe === "function") {
426
447
  for (const e of i.suppressed) {
427
448
  let t = suppressedProbes.get(e);
428
449
  if (!t) suppressedProbes.set(e, t = new Set);
@@ -467,10 +488,10 @@ GlobalQueue.Ht = recordFreshRead;
467
488
 
468
489
  GlobalQueue.Je = applyReask;
469
490
 
470
- GlobalQueue.p = repollDownstreamVerdicts;
491
+ GlobalQueue.k = repollDownstreamVerdicts;
471
492
 
472
- GlobalQueue.Bt = witnessAffects;
493
+ GlobalQueue.wt = witnessAffects;
473
494
 
474
- GlobalQueue.wt = wakeSuppressedProbes;
495
+ GlobalQueue.jt = wakeSuppressedProbes;
475
496
 
476
497
  export { isPending, latest };
@@ -38,6 +38,10 @@ export { createProjectionNext as createProjection } from "./store/next/projectio
38
38
 
39
39
  export { merge, omit } from "./store/utils.js";
40
40
 
41
+ export { patchableRaw, registerPatch, registerRowOps, registerSlotPatchNext as registerSlotPatch } from "./store/next/patch.js";
42
+
43
+ export { storeHasFamily, storeHasOptimisticFamily, storeIsShallow } from "./store/next/store.js";
44
+
41
45
  export { storePath } from "./store/storePath.js";
42
46
 
43
47
  const DEV = undefined;