@solidjs/signals 2.0.0-rc.5 → 2.0.0-rc.7

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 (66) hide show
  1. package/dist/dev.js +2467 -1322
  2. package/dist/node.cjs +2037 -2428
  3. package/dist/node.dev.cjs +13724 -0
  4. package/dist/prod/boundaries.js +3 -1
  5. package/dist/prod/core/async.js +47 -20
  6. package/dist/prod/core/attribution-hooks.js +3 -0
  7. package/dist/prod/core/constants.js +9 -1
  8. package/dist/prod/core/context.js +10 -16
  9. package/dist/prod/core/core.js +260 -188
  10. package/dist/prod/core/dev.js +2 -0
  11. package/dist/prod/core/effect.js +41 -32
  12. package/dist/prod/core/external.js +2 -2
  13. package/dist/prod/core/graph.js +35 -31
  14. package/dist/prod/core/heap.js +34 -40
  15. package/dist/prod/core/lanes.js +44 -30
  16. package/dist/prod/core/optimistic.js +55 -45
  17. package/dist/prod/core/owner.js +35 -35
  18. package/dist/prod/core/scheduler.js +176 -177
  19. package/dist/prod/core/verdict.js +53 -52
  20. package/dist/prod/index.js +0 -2
  21. package/dist/prod/map.js +104 -94
  22. package/dist/prod/signals.js +119 -35
  23. package/dist/prod/store/next/optimistic.js +217 -163
  24. package/dist/prod/store/next/projection.js +2 -2
  25. package/dist/prod/store/next/reconcile.js +140 -288
  26. package/dist/prod/store/next/store.js +338 -252
  27. package/dist/prod/store/store.js +2 -2
  28. package/dist/types/core/attribution-hooks.d.ts +75 -0
  29. package/dist/types/core/attribution.d.ts +313 -9
  30. package/dist/types/core/constants.d.ts +8 -0
  31. package/dist/types/core/core.d.ts +12 -3
  32. package/dist/types/core/dev.d.ts +56 -9
  33. package/dist/types/core/heap.d.ts +5 -3
  34. package/dist/types/core/invariants.d.ts +1 -1
  35. package/dist/types/core/lanes.d.ts +10 -0
  36. package/dist/types/core/scheduler.d.ts +14 -4
  37. package/dist/types/signals.d.ts +10 -11
  38. package/dist/types/store/index.d.ts +3 -6
  39. package/dist/types/store/next/optimistic.d.ts +4 -2
  40. package/dist/types/store/next/reconcile.d.ts +5 -12
  41. package/dist/types/store/next/store.d.ts +3 -9
  42. package/dist/types/store/next/target.d.ts +30 -46
  43. package/dist/types/store/store.d.ts +14 -9
  44. package/dist/types-cjs/core/attribution-hooks.d.cts +75 -0
  45. package/dist/types-cjs/core/attribution.d.cts +313 -9
  46. package/dist/types-cjs/core/constants.d.cts +8 -0
  47. package/dist/types-cjs/core/core.d.cts +12 -3
  48. package/dist/types-cjs/core/dev.d.cts +56 -9
  49. package/dist/types-cjs/core/heap.d.cts +5 -3
  50. package/dist/types-cjs/core/invariants.d.cts +1 -1
  51. package/dist/types-cjs/core/lanes.d.cts +10 -0
  52. package/dist/types-cjs/core/scheduler.d.cts +14 -4
  53. package/dist/types-cjs/signals.d.cts +10 -11
  54. package/dist/types-cjs/store/index.d.cts +3 -6
  55. package/dist/types-cjs/store/next/optimistic.d.cts +4 -2
  56. package/dist/types-cjs/store/next/reconcile.d.cts +5 -12
  57. package/dist/types-cjs/store/next/store.d.cts +3 -9
  58. package/dist/types-cjs/store/next/target.d.cts +30 -46
  59. package/dist/types-cjs/store/store.d.cts +14 -9
  60. package/package.json +3 -2
  61. package/dist/prod/store/next/patch-hooks.js +0 -13
  62. package/dist/prod/store/next/patch.js +0 -614
  63. package/dist/types/store/next/patch-hooks.d.ts +0 -41
  64. package/dist/types/store/next/patch.d.ts +0 -91
  65. package/dist/types-cjs/store/next/patch-hooks.d.cts +0 -41
  66. package/dist/types-cjs/store/next/patch.d.cts +0 -91
@@ -1,6 +1,6 @@
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";
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_SLOT_NODE, CONFIG_HAS_COMPANIONS, EFFECT_TRACKED, REACTIVE_REASK, REACTIVE_MANUAL_WRITE, STATUS_UNINITIALIZED, CONFIG_AUTHORITATIVE_READ } from "./constants.js";
2
2
 
3
- import { currentOptimisticLane } from "./core.js";
3
+ import { currentOptimisticLane, slotUnobservedHook } from "./core.js";
4
4
 
5
5
  import { DEV } from "./dev.js";
6
6
 
@@ -21,14 +21,14 @@ const transitions = new Set;
21
21
  const dirtyQueue = {
22
22
  eE: new Array(2e3).fill(undefined),
23
23
  tE: false,
24
- Qe: 0,
24
+ Me: 0,
25
25
  EE: 0
26
26
  };
27
27
 
28
28
  const zombieQueue = {
29
29
  eE: new Array(2e3).fill(undefined),
30
30
  tE: false,
31
- Qe: 0,
31
+ Me: 0,
32
32
  EE: 0
33
33
  };
34
34
 
@@ -63,7 +63,7 @@ const transientStoreNodes = new Set;
63
63
 
64
64
  function canUseSimpleSyncFlush(e) {
65
65
  const t = e.m;
66
- return transitions.size === 0 && activeLanes.size === 0 && e.Qt.length === 0 && t.ze.length === 0 && t.A.length === 0 && t.En.size === 0 && transientStoreNodes.size === 0;
66
+ return transitions.size === 0 && activeLanes.size === 0 && e.Bt.length === 0 && t.Xe.length === 0 && t.A.length === 0 && t.En.size === 0 && transientStoreNodes.size === 0;
67
67
  }
68
68
 
69
69
  function sweepTransientStoreNodes() {
@@ -73,14 +73,14 @@ function sweepTransientStoreNodes() {
73
73
  transientStoreNodes.delete(e);
74
74
  continue;
75
75
  }
76
- if (e.Pe !== NOT_PENDING) continue;
77
- if (e.o?.Oe !== undefined && e.o?.Oe !== NOT_PENDING) continue;
76
+ if (e.Re !== NOT_PENDING) continue;
77
+ if (e.o?.Pe !== undefined && e.o?.Pe !== NOT_PENDING) continue;
78
78
  // A live affects() mark keeps the node addressable: sweeping it would
79
79
  // detach the refcount from the slot (a fresh probe would upsert a new,
80
80
  // unmarked node for the same property).
81
81
  if (e.o?.t) continue;
82
82
  transientStoreNodes.delete(e);
83
- e.o?.Et?.();
83
+ if (e.T & CONFIG_SLOT_NODE) slotUnobservedHook(e); else e.o?.Nt?.();
84
84
  }
85
85
  }
86
86
 
@@ -108,15 +108,15 @@ function setProjectionWriteActive(e) {
108
108
  */ function createBatch() {
109
109
  return {
110
110
  Te: clock,
111
- Lt: [],
111
+ Mt: [],
112
112
  _e: new Map,
113
- ze: [],
113
+ Xe: [],
114
114
  A: [],
115
115
  En: new Set,
116
116
  ue: [],
117
- Bt: {
118
- Mt: [ [], [] ],
119
- Qt: []
117
+ wt: {
118
+ jt: [ [], [] ],
119
+ Bt: []
120
120
  },
121
121
  sn: false,
122
122
  cn: new Set
@@ -127,12 +127,12 @@ function mergeTransitionState(e, t) {
127
127
  t.sn = e;
128
128
  e.ue.push(...t.ue);
129
129
  for (const i of activeLanes) if (i.Ae === t) i.Ae = e;
130
- if (t.ze.length) {
130
+ if (t.Xe.length) {
131
131
  // Move (don't copy): the global queue's batch may still be the outgoing
132
132
  // transition, and the adoption pass in initTransition would re-push its
133
133
  // contents into the target — duplicating every entry.
134
- e.ze.push(...t.ze);
135
- t.ze.length = 0;
134
+ e.Xe.push(...t.Xe);
135
+ t.Xe.length = 0;
136
136
  }
137
137
  if (t.A.length) {
138
138
  // Move (don't copy): the global queue's batch may still be the outgoing
@@ -142,26 +142,6 @@ function mergeTransitionState(e, t) {
142
142
  t.A.length = 0;
143
143
  }
144
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
145
  for (const [i, n] of t._e) {
166
146
  let t = e._e.get(i);
167
147
  if (!t) e._e.set(i, t = new Set);
@@ -201,10 +181,10 @@ function mergeTransitionState(e, t) {
201
181
  // the awaiting transaction to landings that haven't happened; a merged
202
182
  // reporter deadlocked exactly that way).
203
183
  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;
184
+ for (let n = e.lt; n !== null; n = n.ot) {
185
+ const r = n.st;
186
+ if (r.Re !== NOT_PENDING) {
187
+ const e = r.Ae;
208
188
  const n = e != null ? currentTransition(e) : null;
209
189
  // Skip the awaiting transaction's own cargo (t === target: a
210
190
  // fold-staged landing or a write the action itself issued — hold and
@@ -212,10 +192,10 @@ function mergeTransitionState(e, t) {
212
192
  // (t === null) must leave the batch NOW — it commits at this flush's
213
193
  // end, which would reveal the confirmation under the live optimism
214
194
  // 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;
195
+ const s = n === null ? currentBatch.Mt : n !== t && n.sn !== true ? n.Mt : null;
196
+ if (s !== null) i = stealEntangledCargo(s, t) || i;
217
197
  }
218
- if (n === e.je) break;
198
+ if (n === e.Be) break;
219
199
  }
220
200
  // The steal never activates the awaiting transaction: the predicate can
221
201
  // flip inside another transaction's finalize heap, and adopting the queue
@@ -244,11 +224,11 @@ function mergeTransitionState(e, t) {
244
224
  * committed value — while re-running them would re-derive the OLD world
245
225
  * and re-stage it over the held truth. The reveal re-notifies
246
226
  * (commitPendingNodes), which is when they re-derive for real. */ function stealEntangledCargo(e, t) {
247
- if (e === t.Lt || e.length === 0) return false;
227
+ if (e === t.Mt || e.length === 0) return false;
248
228
  for (let i = 0; i < e.length; i++) {
249
229
  const n = e[i];
250
230
  n.Ae = t;
251
- t.Lt.push(n);
231
+ t.Mt.push(n);
252
232
  // Override-covered nodes stay silent AND unmasked: the override is the
253
233
  // display (A17 — its staging never notified, its revert will), so their
254
234
  // subs saw nothing and re-running one would break the silence with a
@@ -257,7 +237,7 @@ function mergeTransitionState(e, t) {
257
237
  n.T |= CONFIG_HELD_TRUTH;
258
238
  for (let e = n.u; e !== null; e = e.ae) {
259
239
  const t = e.ce;
260
- if (t.he && !(t.T & CONFIG_AUTHORITATIVE_READ)) enqueueSub(t);
240
+ if (t.ge && !(t.T & CONFIG_AUTHORITATIVE_READ)) enqueueSub(t);
261
241
  }
262
242
  }
263
243
  }
@@ -307,30 +287,30 @@ function notifyHalted() {
307
287
  let queueRunToken = 0;
308
288
 
309
289
  class Queue {
310
- ke=null;
311
- Mt=[ [], [] ];
312
- Qt=[];
313
- jt=0;
290
+ Le=null;
291
+ jt=[ [], [] ];
292
+ Bt=[];
293
+ qt=0;
314
294
  created=clock;
315
295
  addChild(e) {
316
- this.Qt.push(e);
317
- e.ke = this;
296
+ this.Bt.push(e);
297
+ e.Le = this;
318
298
  }
319
299
  removeChild(e) {
320
- const t = this.Qt.indexOf(e);
300
+ const t = this.Bt.indexOf(e);
321
301
  if (t >= 0) {
322
- this.Qt.splice(t, 1);
323
- e.ke = null;
302
+ this.Bt.splice(t, 1);
303
+ e.Le = null;
324
304
  }
325
305
  }
326
306
  notify(e, t, i, n) {
327
- if (this.ke) return this.ke.notify(e, t, i, n);
307
+ if (this.Le) return this.Le.notify(e, t, i, n);
328
308
  return false;
329
309
  }
330
310
  run(e) {
331
- if (this.Mt[e - 1].length) {
332
- const t = this.Mt[e - 1];
333
- this.Mt[e - 1] = [];
311
+ if (this.jt[e - 1].length) {
312
+ const t = this.jt[e - 1];
313
+ this.jt[e - 1] = [];
334
314
  runQueue(t, e);
335
315
  }
336
316
  // Effects run here can dispose owners, and disposal removes queues from
@@ -340,14 +320,14 @@ class Queue {
340
320
  // can be recovered by rescanning from the front and every child still runs
341
321
  // exactly once. Children appended mid-pass carry a stale stamp and run,
342
322
  // matching the previous live-array behaviour.
343
- const t = this.Qt;
323
+ const t = this.Bt;
344
324
  const i = ++queueRunToken;
345
325
  for (let n = 0; n < t.length; ) {
346
- const s = t[n];
347
- if (s.jt !== i) {
348
- s.jt = i;
349
- s.run?.(e);
350
- if (t[n] !== s) {
326
+ const r = t[n];
327
+ if (r.qt !== i) {
328
+ r.qt = i;
329
+ r.run?.(e);
330
+ if (t[n] !== r) {
351
331
  n = 0;
352
332
  continue;
353
333
  }
@@ -362,34 +342,34 @@ class Queue {
362
342
  const i = findLane(currentOptimisticLane);
363
343
  i.tn[e - 1].push(t);
364
344
  } else {
365
- this.Mt[e - 1].push(t);
345
+ this.jt[e - 1].push(t);
366
346
  }
367
347
  }
368
348
  schedule();
369
349
  }
370
350
  stashQueues(e) {
371
- e.Mt[0].push(...this.Mt[0]);
372
- e.Mt[1].push(...this.Mt[1]);
373
- this.Mt = [ [], [] ];
374
- for (let t = 0; t < this.Qt.length; t++) {
375
- let i = this.Qt[t];
376
- let n = e.Qt[t];
351
+ e.jt[0].push(...this.jt[0]);
352
+ e.jt[1].push(...this.jt[1]);
353
+ this.jt = [ [], [] ];
354
+ for (let t = 0; t < this.Bt.length; t++) {
355
+ let i = this.Bt[t];
356
+ let n = e.Bt[t];
377
357
  if (!n) {
378
358
  n = {
379
- Mt: [ [], [] ],
380
- Qt: []
359
+ jt: [ [], [] ],
360
+ Bt: []
381
361
  };
382
- e.Qt[t] = n;
362
+ e.Bt[t] = n;
383
363
  }
384
364
  i.stashQueues(n);
385
365
  }
386
366
  }
387
367
  restoreQueues(e) {
388
- this.Mt[0].push(...e.Mt[0]);
389
- this.Mt[1].push(...e.Mt[1]);
390
- for (let t = 0; t < e.Qt.length; t++) {
391
- const i = e.Qt[t];
392
- let n = this.Qt[t];
368
+ this.jt[0].push(...e.jt[0]);
369
+ this.jt[1].push(...e.jt[1]);
370
+ for (let t = 0; t < e.Bt.length; t++) {
371
+ const i = e.Bt[t];
372
+ let n = this.Bt[t];
393
373
  if (n) n.restoreQueues(i);
394
374
  }
395
375
  }
@@ -401,9 +381,9 @@ class GlobalQueue extends Queue {
401
381
  // transition is active, the active transition itself after initTransition.
402
382
  m=createBatch();
403
383
  static Fe;
404
- static He;
405
- static it;
406
- static qt=null;
384
+ static ke;
385
+ static ut;
386
+ static Wt=null;
407
387
  // Store-side hook: drops a keyless affects() mark's identity scope when the
408
388
  // carrier node's last registration releases (wired by store.ts, mirroring
409
389
  // _clearOptimisticStore).
@@ -417,27 +397,27 @@ class GlobalQueue extends Queue {
417
397
  static N=null;
418
398
  // External-source bridge (wired by enableExternalSource(); null while no
419
399
  // config is active — including after _resetExternalSourceConfig()).
420
- static Pt=null;
421
- static ht=null;
400
+ static Ft=null;
401
+ static gt=null;
422
402
  // Verdict-layer hooks (wired by verdict.ts when isPending()/latest() are
423
403
  // imported; null in apps that never use them). Call sites either guard for
424
404
  // null or sit behind state only the verdict layer can create (`!` is safe
425
405
  // there: `_pendingSignal`/`_latestValueComputed` are only ever assigned by
426
406
  // verdict.ts, and `pendingCheckActive`/`latestReadActive` only flip inside
427
407
  // isPending()/latest()).
428
- static Re=null;
408
+ static Ue=null;
429
409
  static de=null;
430
410
  static me=null;
431
411
  static un=null;
432
- static gt=null;
433
- static Ht=null;
434
- static kt=null;
435
- static et=null;
412
+ static Vt=null;
413
+ static bt=null;
414
+ static Lt=null;
415
+ static nt=null;
436
416
  static k=null;
437
- static Wt=null;
417
+ static zt=null;
438
418
  // Re-asks probes whose verdict was provisionally suppressed by a fresh read
439
419
  // of a held value, once the transaction gains an async blocker (#3028).
440
- static zt=null;
420
+ static Kt=null;
441
421
  // Optimistic-engine hooks (wired by core/optimistic.ts via
442
422
  // installOptimisticEngine(), called from verdict.ts / createOptimistic /
443
423
  // createOptimisticStore — every module that can create optimistic state).
@@ -445,7 +425,7 @@ class GlobalQueue extends Queue {
445
425
  // `_overrideValue` slot, a lane in `activeLanes`, an `_optimisticNodes`
446
426
  // entry, or a non-null `currentOptimisticLane`, so `!` invocations are safe
447
427
  // once the gate holds.
448
- static xt=null;
428
+ static yt=null;
449
429
  static Tn=null;
450
430
  static dn=null;
451
431
  static In=null;
@@ -454,16 +434,17 @@ class GlobalQueue extends Queue {
454
434
  * apply at lane-effect timing — visible in flight, unlike the regular
455
435
  * effect queues an action stashes. Injected; null when unused. */
456
436
  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;
437
+ static Ut=null;
438
+ static kt=null;
439
+ static xt=null;
440
+ static Je=null;
441
+ static tt=null;
442
+ /** Authoritative-view reader wakeup: installed by until() and refresh() before
443
+ * their first read. Call sites are gated by CONFIG_AUTHORITATIVE_OBSERVED, which
444
+ * only such a reader's carve-out read can set, so `!` invocations are safe once
445
+ * the gate holds (#3303). */
446
+ static he=null;
447
+ static et=null;
467
448
  static _n=null;
468
449
  flush() {
469
450
  if (this.fn) return;
@@ -472,7 +453,7 @@ class GlobalQueue extends Queue {
472
453
  // optimistic state. Commit and go; anything a commit hook schedules
473
454
  // (companion snaps, store folds notifying subs) re-arms `scheduled`
474
455
  // below and the outer drain loop takes the full spine next round.
475
- if (activeTransition === null && dirtyQueue.EE < dirtyQueue.Qe && this.Mt[0].length === 0 && this.Mt[1].length === 0 && this.Qt.length === 0 && canUseSimpleSyncFlush(this)) {
456
+ if (activeTransition === null && dirtyQueue.EE < dirtyQueue.Me && this.jt[0].length === 0 && this.jt[1].length === 0 && this.Bt.length === 0 && canUseSimpleSyncFlush(this)) {
476
457
  this.fn = true;
477
458
  try {
478
459
  // Sweep first: unobserved() pulls swept nodes out of the dirty heap,
@@ -484,7 +465,7 @@ class GlobalQueue extends Queue {
484
465
  this.fn = false;
485
466
  }
486
467
  clock++;
487
- scheduled = dirtyQueue.EE >= dirtyQueue.Qe || this.Mt[0].length !== 0 || this.Mt[1].length !== 0 || this.m.Lt.length !== 0;
468
+ scheduled = dirtyQueue.EE >= dirtyQueue.Me || this.jt[0].length !== 0 || this.jt[1].length !== 0 || this.m.Mt.length !== 0;
488
469
  return;
489
470
  }
490
471
  this.fn = true;
@@ -520,32 +501,32 @@ class GlobalQueue extends Queue {
520
501
  GlobalQueue.Nn(EFFECT_RENDER);
521
502
  GlobalQueue.Nn(EFFECT_USER);
522
503
  }
523
- this.stashQueues(e.Bt);
504
+ this.stashQueues(e.wt);
524
505
  clock++;
525
506
  // A kept ambient batch may hold pending nodes (#2916): stay
526
507
  // scheduled so the outer drain loop commits them via the plain
527
508
  // flush path instead of leaving them until the next natural flush.
528
- scheduled = dirtyQueue.EE >= dirtyQueue.Qe || this.m.Lt.length > 0;
529
- reassignPendingTransition(e.Lt);
509
+ scheduled = dirtyQueue.EE >= dirtyQueue.Me || this.m.Mt.length > 0;
510
+ reassignPendingTransition(e.Mt);
530
511
  activeTransition = null;
531
512
  finalizePureQueue(null, true);
532
513
  return;
533
514
  }
534
515
  const t = activeTransition;
535
516
  const i = this.m;
536
- i !== t && i.Lt.push(...t.Lt);
537
- this.restoreQueues(t.Bt);
517
+ i !== t && i.Mt.push(...t.Mt);
518
+ this.restoreQueues(t.wt);
538
519
  transitions.delete(t);
539
520
  activeTransition = null;
540
- reassignPendingTransition(i.Lt);
521
+ reassignPendingTransition(i.Mt);
541
522
  finalizePureQueue(t);
542
523
  if (i === t) {
543
524
  // Drop the dead Transition wrapper but keep its (drained) containers
544
525
  // as the ambient batch — late registrations during finalization live
545
526
  // there and must survive to the next flush.
546
527
  const e = createBatch();
547
- e.Lt = i.Lt;
548
- e.ze = i.ze;
528
+ e.Mt = i.Mt;
529
+ e.Xe = i.Xe;
549
530
  e.A = i.A;
550
531
  e.En = i.En;
551
532
  currentBatch = this.m = e;
@@ -553,7 +534,7 @@ class GlobalQueue extends Queue {
553
534
  } else {
554
535
  if (canUseSimpleSyncFlush(this)) {
555
536
  commitPendingNodes();
556
- if (dirtyQueue.EE >= dirtyQueue.Qe) {
537
+ if (dirtyQueue.EE >= dirtyQueue.Me) {
557
538
  runHeap(dirtyQueue, GlobalQueue.Fe);
558
539
  commitPendingNodes();
559
540
  }
@@ -564,7 +545,7 @@ class GlobalQueue extends Queue {
564
545
  }
565
546
  clock++;
566
547
  // Check if finalization added items to the heap (from optimistic reversion)
567
- scheduled = dirtyQueue.EE >= dirtyQueue.Qe;
548
+ scheduled = dirtyQueue.EE >= dirtyQueue.Me;
568
549
  // Run lane effects first (for ready lanes), then regular effects
569
550
  activeLanes.size && GlobalQueue.Nn(EFFECT_RENDER);
570
551
  this.run(EFFECT_RENDER);
@@ -581,21 +562,31 @@ class GlobalQueue extends Queue {
581
562
  // Only track async if the boundary is propagating STATUS_PENDING (not caught by boundary)
582
563
  if (t & STATUS_PENDING) {
583
564
  if (i & STATUS_PENDING) {
584
- const t = n !== undefined ? n : e.o?._;
565
+ // Callers pass either nothing or this node's own `_x._error`, so `??`
566
+ // is exact (a null error falls back to the same null).
567
+ const t = n ?? e.o?._;
585
568
  // A visibility-only mark notification (the affects() boundary
586
569
  // channel) updates display state on its way up but must be invisible
587
570
  // to completion accounting BY CONSTRUCTION: it never registers a
588
571
  // reporter and never counts toward the loading-boundary diagnostic.
589
572
  if (t?.l) return true;
590
- if (activeTransition && t) {
591
- const i = t.source;
592
- let n = activeTransition._e.get(i);
593
- if (!n) activeTransition._e.set(i, n = new Set);
594
- const s = n.size;
595
- n.add(e);
596
- if (n.size !== s) {
597
- schedule();
598
- GlobalQueue.zt?.(activeTransition);
573
+ if (t) {
574
+ // A reveal can discover a flight started in an earlier flush. Hold
575
+ // the staged writes with that reader (A15), even if the reader is
576
+ // new. Fresh/reset loading boundaries consume pending before it
577
+ // reaches here. A reader already parked in a transition must not
578
+ // open a second one.
579
+ if (!activeTransition && !e.Ae && currentBatch.Mt.length) this.initTransition();
580
+ if (activeTransition) {
581
+ const i = t.source;
582
+ let n = activeTransition._e.get(i);
583
+ if (!n) activeTransition._e.set(i, n = new Set);
584
+ const r = n.size;
585
+ n.add(e);
586
+ if (n.size !== r) {
587
+ schedule();
588
+ GlobalQueue.Kt?.(activeTransition);
589
+ }
599
590
  }
600
591
  }
601
592
  }
@@ -621,6 +612,11 @@ class GlobalQueue extends Queue {
621
612
  } else if (e) {
622
613
  const t = activeTransition;
623
614
  mergeTransitionState(e, t);
615
+ // Effects the outgoing transaction parked belong to the surviving one
616
+ // now: back onto the live queue, where this flush parks them under
617
+ // `transition` or runs them at its completion. The outgoing stash is
618
+ // never read again — the transaction is dead (#3310).
619
+ this.restoreQueues(t.wt);
624
620
  transitions.delete(t);
625
621
  activeTransition = e;
626
622
  }
@@ -635,15 +631,15 @@ class GlobalQueue extends Queue {
635
631
  // must not entangle unrelated writes to it; the same rule holds one hop
636
632
  // downstream: propagation never queues pended subscribers as pending
637
633
  // nodes, see propagateAffectsMark, #2893.
638
- for (let e = 0; e < t.Lt.length; e++) {
639
- const i = t.Lt[e];
634
+ for (let e = 0; e < t.Mt.length; e++) {
635
+ const i = t.Mt[e];
640
636
  i.Ae = activeTransition;
641
- activeTransition.Lt.push(i);
637
+ activeTransition.Mt.push(i);
642
638
  }
643
- for (let e = 0; e < t.ze.length; e++) {
644
- const i = t.ze[e];
639
+ for (let e = 0; e < t.Xe.length; e++) {
640
+ const i = t.Xe[e];
645
641
  i.Ae = activeTransition;
646
- activeTransition.ze.push(i);
642
+ activeTransition.Xe.push(i);
647
643
  }
648
644
  if (t.A.length) activeTransition.A.push(...t.A);
649
645
  for (const e of t.En) activeTransition.En.add(e);
@@ -671,7 +667,7 @@ class GlobalQueue extends Queue {
671
667
  }
672
668
 
673
669
  function queuePendingNode(e) {
674
- currentBatch.Lt.push(e);
670
+ currentBatch.Mt.push(e);
675
671
  }
676
672
 
677
673
  // Sticky: flips true on the first refresh() ever (the only setter of
@@ -695,7 +691,7 @@ function armReaskClear() {
695
691
  function insertSubs(e, t = false) {
696
692
  // §12d: stamp before walking — setSignal's staged-rewrite fast path skips
697
693
  // the next walk for this node while the epoch holds (marking is idempotent).
698
- e.It = notifyEpoch;
694
+ e.Et = notifyEpoch;
699
695
  // Get source lane: prefer node's own lane over current context
700
696
  // This is important for isPending signals which need their own lane to flush immediately
701
697
  // Presence bits gate the optional-slot probes (see constants.ts): one
@@ -703,14 +699,14 @@ function insertSubs(e, t = false) {
703
699
  // lookups in the hottest notify loop. Bits are sticky — the field read
704
700
  // stays authoritative when a bit is set.
705
701
  const i = e.T;
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;
702
+ const n = (i & CONFIG_HAS_LANE ? e.o?.$e : undefined) || currentOptimisticLane;
703
+ const r = (i & CONFIG_HAS_SNAPSHOT) !== 0 && e.o?.Ye !== undefined;
704
+ const s = reaskArmed;
709
705
  for (let i = e.u; i !== null; i = i.ae) {
710
706
  const e = i.ce;
711
707
  // A value-change notification is a new question for the subscriber: any
712
708
  // pending re-ask mark (refresh) it carried is superseded.
713
- if (r) e.ie &= ~REACTIVE_REASK;
709
+ if (s) e.ie &= ~REACTIVE_REASK;
714
710
  // Missed-wake latch (#3037): this write is landing while the subscriber
715
711
  // is mid-recompute (a nested pull committing beneath its reads), and the
716
712
  // heap refuses RECOMPUTING nodes. A gen-current link means the pass
@@ -719,8 +715,8 @@ function insertSubs(e, t = false) {
719
715
  // pass either re-reads them fresh or trims them), and neither does the
720
716
  // tail link: it is the read IN FLIGHT — read() links before it pulls, so
721
717
  // this very commit is what that read returns.
722
- if (e.ie & REACTIVE_RECOMPUTING_DEPS && i.Ft === e.Ke && i !== e.je) e.ie |= REACTIVE_MISSED_WAKE;
723
- if (s && e.T & CONFIG_IN_SNAPSHOT_SCOPE) {
718
+ if (e.ie & REACTIVE_RECOMPUTING_DEPS && i.vt === e.ze && i !== e.Be) e.ie |= REACTIVE_MISSED_WAKE;
719
+ if (r && e.T & CONFIG_IN_SNAPSHOT_SCOPE) {
724
720
  e.ie |= REACTIVE_SNAPSHOT_STALE;
725
721
  continue;
726
722
  }
@@ -730,7 +726,7 @@ function insertSubs(e, t = false) {
730
726
  } else if (t) {
731
727
  e.ie |= REACTIVE_OPTIMISTIC_DIRTY;
732
728
  // No source lane means reversion - clear subscriber's lane so effects go to regular queue
733
- if (e.o) e.o.Je = undefined;
729
+ if (e.o) e.o.$e = undefined;
734
730
  }
735
731
  enqueueSub(e);
736
732
  }
@@ -739,18 +735,24 @@ function insertSubs(e, t = false) {
739
735
  function commitPendingNode(e) {
740
736
  const t = e;
741
737
  if (!t.oe) {
742
- if (e.Pe !== NOT_PENDING) {
743
- e.be = e.Pe;
744
- e.Pe = NOT_PENDING;
738
+ if (e.Re !== NOT_PENDING) {
739
+ e.be = e.Re;
740
+ e.Re = NOT_PENDING;
745
741
  }
746
742
  if (e.T & CONFIG_HAS_COMPANIONS) GlobalQueue.un(e);
747
743
  return;
748
744
  }
749
- if (e.Pe !== NOT_PENDING) {
750
- e.be = e.Pe;
751
- e.Pe = NOT_PENDING;
745
+ if (e.Re !== NOT_PENDING) {
746
+ e.be = e.Re;
747
+ e.Re = NOT_PENDING;
752
748
  // Set _modified for effects, but not for tracked effects (they handle their own scheduling)
753
- if (e.he && e.he !== EFFECT_TRACKED) e.tt = true;
749
+ if (e.ge && e.ge !== EFFECT_TRACKED) e.He = true;
750
+ // A quiet re-ask classification preserved through a held landing dies
751
+ // with the value commit — the commit IS the reveal (#3178). Gated on the
752
+ // staged value: status propagation queues pending nodes whose windows
753
+ // are still OPEN (no staged value), and their live classification must
754
+ // survive this sweep.
755
+ if (e.o) e.o.De = false;
754
756
  }
755
757
  // The committed hold is the first observable answer for a loading-window
756
758
  // node — the window closes here, not at compute time (#2990). Unconditional
@@ -758,7 +760,7 @@ function commitPendingNode(e) {
758
760
  t.Ne = false;
759
761
  t.ie &= ~REACTIVE_MANUAL_WRITE;
760
762
  if (!(t.S & STATUS_PENDING)) t.S &= ~STATUS_UNINITIALIZED;
761
- if (t.o != null && (t.o.Ye !== null || t.o.qe !== null)) GlobalQueue.He(t, false, true);
763
+ if (t.o != null && (t.o.Ke !== null || t.o.Ze !== null)) GlobalQueue.ke(t, false, true);
762
764
  if (e.T & CONFIG_HAS_COMPANIONS) GlobalQueue.un(e);
763
765
  }
764
766
 
@@ -773,17 +775,6 @@ function setStoreCommitHook(e) {
773
775
  storeCommitHook = e;
774
776
  }
775
777
 
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
778
  /** Held truth committed this finalize, awaiting its post-revert wake (see
788
779
  * finalizePureQueue): the commit IS the reveal, but subscribers must not
789
780
  * re-derive until the settling transaction's optimistic overrides have
@@ -792,7 +783,7 @@ function setPatchCommitHook(e) {
792
783
  * frame no timeline contains. */ const heldRevealed = [];
793
784
 
794
785
  function commitPendingNodes() {
795
- const e = currentBatch.Lt;
786
+ const e = currentBatch.Mt;
796
787
  for (let t = 0; t < e.length; t++) {
797
788
  const i = e[t];
798
789
  commitPendingNode(i);
@@ -815,7 +806,6 @@ function commitPendingNodes() {
815
806
  }
816
807
  e.length = 0;
817
808
  storeCommitHook?.();
818
- patchCommitHook?.(currentBatch);
819
809
  }
820
810
 
821
811
  function finalizePureQueue(e = null, t = false) {
@@ -823,8 +813,8 @@ function finalizePureQueue(e = null, t = false) {
823
813
  // For completing transitions or no-transition, resolve pending and revert optimistic
824
814
  const i = !t;
825
815
  if (i) commitPendingNodes();
826
- if (!t && globalQueue.Qt.length) checkBoundaryChildren(globalQueue);
827
- const n = dirtyQueue.EE >= dirtyQueue.Qe;
816
+ if (!t && globalQueue.Bt.length) checkBoundaryChildren(globalQueue);
817
+ const n = dirtyQueue.EE >= dirtyQueue.Me;
828
818
  if (n) runHeap(dirtyQueue, GlobalQueue.Fe);
829
819
  if (i) {
830
820
  if (n) commitPendingNodes();
@@ -832,7 +822,7 @@ function finalizePureQueue(e = null, t = false) {
832
822
  const t = e ?? globalQueue.m;
833
823
  // Optimistic reversion: a non-empty batch means _optimisticWrite ran,
834
824
  // which installed the engine's hooks.
835
- if (t.ze.length) GlobalQueue.Tn(t.ze);
825
+ if (t.Xe.length) GlobalQueue.Tn(t.Xe);
836
826
  // Replay entanglement: subs recorded by the read-time gate get rescheduled
837
827
  // so they re-run with the now-committed values visible. The ambient batch
838
828
  // replays too — laneReadsCommitted records readers whose committed-view
@@ -856,13 +846,13 @@ function finalizePureQueue(e = null, t = false) {
856
846
  // (the earlier sweep above ran while the marks were still live).
857
847
  if (t.A.length) {
858
848
  GlobalQueue.G(t.A);
859
- if (globalQueue.Qt.length) checkBoundaryChildren(globalQueue);
849
+ if (globalQueue.Bt.length) checkBoundaryChildren(globalQueue);
860
850
  }
861
851
  // A non-empty set means trackOptimisticStore ran, which installed the
862
852
  // hook; the hook iterates, clears, and schedules (keeping the loop out of
863
853
  // core lets esbuild shake it — rollup already folds the null guard). The
864
854
  // completing transition scopes the clear to its own layer keys (#2899).
865
- if (t.En.size) GlobalQueue.qt(t.En, e);
855
+ if (t.En.size) GlobalQueue.Wt(t.En, e);
866
856
  // Held-truth reveal wake (#3164), post-revert by construction: this
867
857
  // finalize committed confirming truth whose subscribers were masked all
868
858
  // hold — some re-derived against the committed view (the staging, or a
@@ -874,7 +864,7 @@ function finalizePureQueue(e = null, t = false) {
874
864
  // committed but the settling transaction's overrides still display.
875
865
  if (heldRevealed.length !== 0) {
876
866
  while (heldRevealed.length) insertSubs(heldRevealed.pop());
877
- if (dirtyQueue.EE >= dirtyQueue.Qe) {
867
+ if (dirtyQueue.EE >= dirtyQueue.Me) {
878
868
  runHeap(dirtyQueue, GlobalQueue.Fe);
879
869
  commitPendingNodes();
880
870
  }
@@ -886,7 +876,7 @@ function finalizePureQueue(e = null, t = false) {
886
876
  }
887
877
 
888
878
  function checkBoundaryChildren(e) {
889
- for (const t of e.Qt) {
879
+ for (const t of e.Bt) {
890
880
  t.se?.();
891
881
  checkBoundaryChildren(t);
892
882
  }
@@ -956,11 +946,11 @@ function runQueue(e, t) {
956
946
  function reporterBlocksSource(e, t) {
957
947
  if (e.ie & (REACTIVE_ZOMBIE | REACTIVE_DISPOSED)) return false;
958
948
  if (e.o?.le?.has(t)) return true;
959
- for (let i = e.ut; i; i = i.lt) {
960
- let e = i.ot;
949
+ for (let i = e.lt; i; i = i.ot) {
950
+ let e = i.st;
961
951
  while (e) {
962
- if (e === t || e.st === t) return true;
963
- e = e.o?.Tt;
952
+ if (e === t || e.rt === t) return true;
953
+ e = e.o?.St;
964
954
  }
965
955
  }
966
956
  return !!(e.S & STATUS_PENDING && e.o?._ instanceof NotReadyError && e.o?._.source === t);
@@ -968,18 +958,20 @@ function reporterBlocksSource(e, t) {
968
958
 
969
959
  function transitionComplete(e) {
970
960
  if (e.sn) return true;
971
- if (e.ue.length) return false;
961
+ if (e.ue.length) {
962
+ return false;
963
+ }
972
964
  let t = true;
973
965
  for (const [i, n] of e._e) {
974
- let s = false;
966
+ let r = false;
975
967
  for (const e of n) {
976
968
  if (reporterBlocksSource(e, i)) {
977
- s = true;
969
+ r = true;
978
970
  break;
979
971
  }
980
972
  n.delete(e);
981
973
  }
982
- if (!s) e._e.delete(i); else if (i.S & STATUS_PENDING && i.o?._?.source === i) {
974
+ if (!r) e._e.delete(i); else if (i.S & STATUS_PENDING && i.o?._?.source === i) {
983
975
  t = false;
984
976
  break;
985
977
  }
@@ -992,6 +984,13 @@ function transitionComplete(e) {
992
984
  return t;
993
985
  }
994
986
 
987
+ /** A fresh, unentered transaction (#3146): the optimistic store's truth
988
+ * flight DECLARES an owned transaction instead of relying on whatever the
989
+ * ambient adoption machinery stamped on its firewall. Activate it with
990
+ * initTransition; it is a plain batch until then. */ function createTransition() {
991
+ return createBatch();
992
+ }
993
+
995
994
  function currentTransition(e) {
996
995
  while (e.sn && typeof e.sn === "object") e = e.sn;
997
996
  return e;
@@ -1030,4 +1029,4 @@ function runInTransition(e, t) {
1030
1029
  }
1031
1030
  }
1032
1031
 
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 };
1032
+ export { GlobalQueue, Queue, activeAffectsMarks, activeLanes, activeTransition, armReaskClear, assignOrMergeLane, bumpNotifyEpoch, clock, createTransition, currentTransition, dirtyQueue, enforceLoadingBoundary, entangleConfirmingTransitions, finalizePureQueue, findLane, flush, globalQueue, haltReactivity, hasActiveOverride, insertSubs, notifyEpoch, projectionWriteActive, queuePendingNode, reaskArmed, resetErrorHalt, runAsTransitionBatch, runInTransition, schedule, setProjectionWriteActive, setStoreCommitHook, shiftAffectsMarks, storeCommitHook, zombieQueue };