@solidjs/signals 2.0.0-beta.30 → 2.0.0-beta.32

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.
@@ -1,4 +1,4 @@
1
- import { STATUS_UNINITIALIZED, STATUS_ERROR, STATUS_PENDING, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING } from "./constants.js";
1
+ import { STATUS_UNINITIALIZED, STATUS_ERROR, STATUS_PENDING, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE } from "./constants.js";
2
2
 
3
3
  import { untrack, context, setSignal } from "./core.js";
4
4
 
@@ -6,7 +6,7 @@ import "./invariants.js";
6
6
 
7
7
  import { NotReadyError, StatusError } from "./error.js";
8
8
 
9
- import { unobserved, trimStaleDeps } from "./graph.js";
9
+ import { trimStaleDeps, unobserved } from "./graph.js";
10
10
 
11
11
  import { enqueueSub } from "./heap.js";
12
12
 
@@ -14,7 +14,7 @@ import { resolveTransition, hasActiveOverride, assignOrMergeLane, resolveLane }
14
14
 
15
15
  import { cleanup } from "./owner.js";
16
16
 
17
- import { globalQueue, GlobalQueue, clock, currentTransition, schedule, flush, queuePendingNode, insertSubs } from "./scheduler.js";
17
+ import { globalQueue, GlobalQueue, schedule, flush, queuePendingNode, insertSubs, clock, currentTransition } from "./scheduler.js";
18
18
 
19
19
  // The lazily-created Set is the ONE container for pending sources. Its
20
20
  // predecessor — a singular slot promoted to a Set on the second source —
@@ -133,9 +133,9 @@ function settlePendingSource(e) {
133
133
  if (r.has(u) || !removePendingSource(u, e)) return;
134
134
  r.add(u);
135
135
  u.Se = clock;
136
- const l = u.oe?.values().next().value;
137
- if (l) {
138
- setPendingError(u, l);
136
+ const s = u.oe?.values().next().value;
137
+ if (s) {
138
+ setPendingError(u, s);
139
139
  o !== null && o(u);
140
140
  } else {
141
141
  u.S &= ~STATUS_PENDING;
@@ -222,8 +222,8 @@ function handleAsync(e, n, t) {
222
222
  const u = !!(e.S & STATUS_UNINITIALIZED);
223
223
  trimStaleDeps(e);
224
224
  clearStatus(e);
225
- const l = resolveLane(e);
226
- if (l) l.Ne.delete(e);
225
+ const s = resolveLane(e);
226
+ if (s) s.Ne.delete(e);
227
227
  if (t) {
228
228
  t(r);
229
229
  if (u) clearStatus(e, true);
@@ -249,14 +249,14 @@ function handleAsync(e, n, t) {
249
249
  GlobalQueue._e !== null && GlobalQueue._e(e, r);
250
250
  if (!hasActiveOverride(e)) insertSubs(e);
251
251
  e.Se = clock;
252
- } else if (l) {
252
+ } else if (s) {
253
253
  // Route through lane's effect queue for independent flushing
254
254
  const n = e.Pe;
255
- const t = e.Ue;
256
- const o = e.be;
255
+ const t = e.be;
256
+ const o = e.he;
257
257
  try {
258
258
  if (!n && u || !o || !o(r, t)) {
259
- e.Ue = r;
259
+ e.be = r;
260
260
  e.Se = clock;
261
261
  // The latest() shadow write gives latest() effects independent lanes; the
262
262
  // _pendingSignal update is a no-op repeat of the clearStatus() call above
@@ -300,9 +300,9 @@ function handleAsync(e, n, t) {
300
300
  return false;
301
301
  };
302
302
  if (o) {
303
- let t = false, r = false, o, l = true;
303
+ let t = false, r = false, o, s = true;
304
304
  n.then(e => {
305
- if (l) {
305
+ if (s) {
306
306
  u = e;
307
307
  t = true;
308
308
  } else {
@@ -310,7 +310,7 @@ function handleAsync(e, n, t) {
310
310
  settleAutodispose();
311
311
  }
312
312
  }, e => {
313
- if (l) {
313
+ if (s) {
314
314
  o = e;
315
315
  r = true;
316
316
  } else {
@@ -318,7 +318,7 @@ function handleAsync(e, n, t) {
318
318
  settleAutodispose();
319
319
  }
320
320
  });
321
- l = false;
321
+ s = false;
322
322
  if (r) {
323
323
  // Settle through the same status path an async rejection uses, then
324
324
  // unwind the in-progress synchronous read so the errored node isn't
@@ -334,7 +334,7 @@ function handleAsync(e, n, t) {
334
334
  const t = n[Symbol.asyncIterator]();
335
335
  let r = false;
336
336
  let o = false;
337
- let l = true;
337
+ let s = true;
338
338
  cleanup(() => {
339
339
  if (o) return;
340
340
  o = true;
@@ -350,10 +350,25 @@ function handleAsync(e, n, t) {
350
350
  if (!settleAutodispose()) iterate();
351
351
  };
352
352
  const iterate = () => {
353
- let s, i, f = false, a = false, c = true;
354
- t.next().then(t => {
355
- if (c) {
356
- s = t;
353
+ let l, i, f = false, a = false, c = true;
354
+ // Protocol tolerance, matching `for await`: `await` unwraps whatever
355
+ // next() returns — a thenable OR a bare IteratorResult. Real producers
356
+ // use the bare form as a promise-free fast path when a value is already
357
+ // buffered (seroval's deserialized streams do), so a bare result is
358
+ // assimilated as an already-settled step instead of crashing on `.then`.
359
+ const S = t.next();
360
+ const d = isThenable(S) ? S : {
361
+ then: e => void e(S)
362
+ };
363
+ d.then(t => {
364
+ // The sync stash only serves the INITIAL drain (handleAsync's caller
365
+ // consumes syncValue / throws NotReady from it). A sync-settled step
366
+ // after an async gap — seroval buffering values between pulls, a
367
+ // sync-thenable producer mid-stream — has no caller reading the
368
+ // stash: it must write through the async path or the value is
369
+ // silently dropped.
370
+ if (c && s) {
371
+ l = t;
357
372
  f = true;
358
373
  if (t.done) o = true;
359
374
  } else if (e.Te !== n) {
@@ -387,20 +402,20 @@ function handleAsync(e, n, t) {
387
402
  // Match the promise branch, but only rethrow during the initial read.
388
403
  o = true;
389
404
  handleError(i);
390
- if (l) throw i;
405
+ if (s) throw i;
391
406
  return true;
392
407
  }
393
- if (f && !s.done) {
394
- u = s.value;
408
+ if (f && !l.done) {
409
+ u = l.value;
395
410
  r = true;
396
411
  return iterate();
397
412
  }
398
- return f && s.done;
413
+ return f && l.done;
399
414
  };
400
- const s = iterate();
415
+ const l = iterate();
401
416
  // Later iterate() calls run from asyncWrite, where rethrowing would be unhandled.
402
- l = false;
403
- if (!r && !s) {
417
+ s = false;
418
+ if (!r && !l) {
404
419
  globalQueue.initTransition(resolveTransition(e));
405
420
  throw new NotReadyError(context);
406
421
  }
@@ -414,12 +429,12 @@ function clearStatus(e, n = false) {
414
429
  // The pending window is over; its quiet classification dies with it.
415
430
  // (Unconditional: _reask is baked into the node literals, so this is a
416
431
  // plain store to an existing slot — no shape change.)
417
- e.Re = false;
432
+ e.Ue = false;
418
433
  e.S = n ? 0 : e.S & STATUS_UNINITIALIZED;
419
434
  if (e._) setPendingError(e);
420
435
  // Update pending signal for isPending() reactivity (companions only exist
421
436
  // once the verdict layer created them, which installs the hooks).
422
- if (e.he || e.pe) GlobalQueue.ce(e);
437
+ if (e.Re || e.pe) GlobalQueue.ce(e);
423
438
  if (e.u && GlobalQueue.Ge !== null) GlobalQueue.Ge(e);
424
439
  if (e.i) e.i();
425
440
  }
@@ -428,9 +443,9 @@ function notifyStatus(e, n, t, r, o) {
428
443
  // Wrap regular errors to track source node
429
444
  if (n === STATUS_ERROR && !(t instanceof StatusError) && !(t instanceof NotReadyError)) t = new StatusError(e, t);
430
445
  const u = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
431
- const l = u === e;
432
- const s = n === STATUS_PENDING && e.Ae !== undefined && !l;
433
- const i = s && hasActiveOverride(e);
446
+ const s = u === e;
447
+ const l = n === STATUS_PENDING && e.Ae !== undefined && !s;
448
+ const i = l && hasActiveOverride(e);
434
449
  if (!r) {
435
450
  if (n === STATUS_PENDING && u) {
436
451
  addPendingSource(e, u);
@@ -450,7 +465,7 @@ function notifyStatus(e, n, t, r, o) {
450
465
  assignOrMergeLane(e, o);
451
466
  }
452
467
  const f = r || i;
453
- const a = r || s ? undefined : o;
468
+ const a = r || l ? undefined : o;
454
469
  if (e.i) {
455
470
  if (r && n === STATUS_PENDING) {
456
471
  return;
@@ -137,7 +137,7 @@ function recompute(e, t = false) {
137
137
  e.Ye++;
138
138
  e.se = REACTIVE_RECOMPUTING_DEPS;
139
139
  e.Se = clock;
140
- let r = e.De === NOT_PENDING ? e.Ue : e.De;
140
+ let r = e.De === NOT_PENDING ? e.be : e.De;
141
141
  let c = e.Ve;
142
142
  let _ = tracking;
143
143
  let f = currentOptimisticLane;
@@ -189,7 +189,7 @@ function recompute(e, t = false) {
189
189
  // On a status-free node clearStatus is a guaranteed no-op: every branch
190
190
  // in its body is gated on one of these fields, and with _statusFlags === 0
191
191
  // the flags write (create or not) stores the 0 already there.
192
- if (e.S !== 0 || e.i !== undefined || e._ || e.Re || e.de || e.oe !== undefined || e.he !== undefined || e.pe !== undefined || e.u !== null) clearStatus(e, t);
192
+ if (e.S !== 0 || e.i !== undefined || e._ || e.Ue || e.de || e.oe !== undefined || e.Re !== undefined || e.pe !== undefined || e.u !== null) clearStatus(e, t);
193
193
  // _optimisticLane is only ever assigned by engine paths.
194
194
  if (e.Me) GlobalQueue.Ke(e);
195
195
  } catch (t) {
@@ -212,10 +212,10 @@ function recompute(e, t = false) {
212
212
  }
213
213
  if (!e._) {
214
214
  trimStaleDeps(e);
215
- const s = u ? unwrapOverride(e.Ae) : e.De === NOT_PENDING ? e.Ue : e.De;
215
+ const s = u ? unwrapOverride(e.Ae) : e.De === NOT_PENDING ? e.be : e.De;
216
216
  let a = false;
217
217
  try {
218
- a = !n && l || !e.be || !e.be(s, r);
218
+ a = !n && l || !e.he || !e.he(s, r);
219
219
  } catch (t) {
220
220
  // A throwing user comparator is an error of this node's computation.
221
221
  // Route it through the same status path as a compute-phase throw so
@@ -242,7 +242,7 @@ function recompute(e, t = false) {
242
242
  // commitPendingNodes) exists to sequence transition reveals, and
243
243
  // paying it per effect on the plain path is pure overhead.
244
244
  n && (activeTransition !== e.Ie || activeTransition === null) || i) {
245
- e.Ue = r;
245
+ e.be = r;
246
246
  // Lane-propagated correction: upstream data is fresh, correct the
247
247
  // override unconditionally. The direct _value commit is the lane's
248
248
  // own reveal schedule; drop any superseded older hold so its queued
@@ -316,14 +316,14 @@ function computed(e, t) {
316
316
  const i = {
317
317
  id: inheritId(t, n, context),
318
318
  T: (n ? CONFIG_TRANSPARENT : 0) | (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (!context || t?.lazy ? CONFIG_AUTO_DISPOSE : 0) | (t?.sync ? CONFIG_SYNC : 0) | (t?.V ? CONFIG_NO_SNAPSHOT : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
319
- be: t?.equals != null ? t.equals : isEqual,
319
+ he: t?.equals != null ? t.equals : isEqual,
320
320
  ut: t?.unobserved,
321
321
  Le: null,
322
322
  C: context?.C ?? globalQueue,
323
323
  we: context?.we ?? defaultContext,
324
324
  qe: 0,
325
325
  ae: e,
326
- Ue: undefined,
326
+ be: undefined,
327
327
  Ve: 0,
328
328
  u: null,
329
329
  lt: undefined,
@@ -345,7 +345,7 @@ function computed(e, t) {
345
345
  Qe: null,
346
346
  Te: null,
347
347
  Ie: null,
348
- Re: false
348
+ Ue: false
349
349
  };
350
350
  setupComputedNode(i, t);
351
351
  return i;
@@ -361,14 +361,14 @@ function computed(e, t) {
361
361
  const s = {
362
362
  id: inheritId(l, o, context),
363
363
  T: (o ? CONFIG_TRANSPARENT : 0) | (l?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (l?.sync ? CONFIG_SYNC : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
364
- be: false,
364
+ he: false,
365
365
  ut: l?.unobserved,
366
366
  Le: null,
367
367
  C: context?.C ?? globalQueue,
368
368
  we: context?.we ?? defaultContext,
369
369
  qe: 0,
370
370
  ae: e,
371
- Ue: undefined,
371
+ be: undefined,
372
372
  Ve: 0,
373
373
  u: null,
374
374
  lt: undefined,
@@ -390,7 +390,7 @@ function computed(e, t) {
390
390
  Qe: null,
391
391
  Te: null,
392
392
  Ie: null,
393
- Re: false,
393
+ Ue: false,
394
394
  Be: false,
395
395
  ct: undefined,
396
396
  _t: t,
@@ -425,7 +425,7 @@ function setupComputedNode(e, t) {
425
425
  !t?.lazy && recompute(e, true);
426
426
  if (snapshotCaptureActive && !t?.lazy) {
427
427
  if (!(e.S & STATUS_PENDING) && !(e.T & CONFIG_NO_SNAPSHOT)) {
428
- e.xe = e.Ue === undefined ? NO_SNAPSHOT : e.Ue;
428
+ e.xe = e.be === undefined ? NO_SNAPSHOT : e.be;
429
429
  snapshotSources.add(e);
430
430
  }
431
431
  }
@@ -433,10 +433,10 @@ function setupComputedNode(e, t) {
433
433
 
434
434
  function signal(e, t, n = null) {
435
435
  const i = {
436
- be: t?.equals != null ? t.equals : isEqual,
436
+ he: t?.equals != null ? t.equals : isEqual,
437
437
  T: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.V ? CONFIG_NO_SNAPSHOT : 0),
438
438
  ut: t?.unobserved,
439
- Ue: e,
439
+ be: e,
440
440
  o: null,
441
441
  st: null,
442
442
  Se: clock,
@@ -534,7 +534,7 @@ function isEqual(e, t) {
534
534
  let t = context;
535
535
  if (t?.Nt) t = t.Tt;
536
536
  if (t && tracking) link(e, t);
537
- return !t || e.De === NOT_PENDING ? e.Ue : e.De;
537
+ return !t || e.De === NOT_PENDING ? e.be : e.De;
538
538
  }
539
539
 
540
540
  function read(e) {
@@ -558,7 +558,7 @@ function read(e) {
558
558
  }
559
559
  if (!n.ae && u === e && e.Ae === undefined && e.xe === undefined && activeTransition === null && currentOptimisticLane === null && !snapshotCaptureActive && true) {
560
560
  if (t && tracking) link(e, t);
561
- return !t || e.De === NOT_PENDING ? e.Ue : e.De;
561
+ return !t || e.De === NOT_PENDING ? e.be : e.De;
562
562
  }
563
563
  if (t && tracking) {
564
564
  link(e, t, pendingCheckActive);
@@ -610,7 +610,7 @@ function read(e) {
610
610
  const n = e.xe;
611
611
  if (n !== undefined) {
612
612
  const i = n === NO_SNAPSHOT ? undefined : n;
613
- const u = e.De !== NOT_PENDING ? e.De : e.Ue;
613
+ const u = e.De !== NOT_PENDING ? e.De : e.be;
614
614
  if (u !== i) t.se |= REACTIVE_SNAPSHOT_STALE;
615
615
  return i;
616
616
  }
@@ -627,13 +627,13 @@ function read(e) {
627
627
  // so it re-runs with the new committed view. (Gate details live with the
628
628
  // engine — a non-null lane implies it is installed.)
629
629
  if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.Ot(e, u, t)) {
630
- return e.Ue;
630
+ return e.be;
631
631
  }
632
632
  // In optimistic lane context, return _value for optimistic/lane-assigned signals
633
633
  // and for regular signals in stale mode (render effects). Non-stale readers (user
634
634
  // effects) see _pendingValue so that latest() and direct reads stay consistent.
635
635
  // (The lane-context clause lives with the engine.)
636
- const l = !t || currentOptimisticLane !== null && GlobalQueue.Rt(e, u, t) || e.De === NOT_PENDING || stale && e.Ie && activeTransition !== e.Ie ? e.Ue : e.De;
636
+ const l = !t || currentOptimisticLane !== null && GlobalQueue.Rt(e, u, t) || e.De === NOT_PENDING || stale && e.Ie && activeTransition !== e.Ie ? e.be : e.De;
637
637
  // Record that this isPending() probe observed the fresh pending value, so
638
638
  // the probe doesn't pair "pending" with the new value (#2831).
639
639
  if (pendingCheckActive) GlobalQueue.Pt(e, l);
@@ -650,18 +650,18 @@ function setSignal(e, t) {
650
650
  // _overrideValue slot, and every module that creates one installs the
651
651
  // engine first.
652
652
  if (e.Ae !== undefined && !projectionWriteActive) return GlobalQueue.ht(e, t);
653
- const n = e.De === NOT_PENDING ? e.Ue : e.De;
653
+ const n = e.De === NOT_PENDING ? e.be : e.De;
654
654
  if (typeof t === "function") t = t(n);
655
655
  // Uninitialized check first: the first commit has no previous value, so the
656
656
  // user comparator must not run against `undefined` (matches recompute).
657
- const i = !!(e.S & STATUS_UNINITIALIZED) || !e.be || !e.be(n, t);
657
+ const i = !!(e.S & STATUS_UNINITIALIZED) || !e.he || !e.he(n, t);
658
658
  if (!i) return t;
659
659
  if (e.De === NOT_PENDING) queuePendingNode(e);
660
660
  e.De = t;
661
661
  // syncCompanions only pokes _pendingSignal/_latestValueComputed — with
662
662
  // neither companion present the call is a guaranteed no-op (companions are
663
663
  // only ever created, never removed, and creating one installs the hook).
664
- (e.he !== undefined || e.pe !== undefined) && GlobalQueue._e !== null && GlobalQueue._e(e, t);
664
+ (e.Re !== undefined || e.pe !== undefined) && GlobalQueue._e !== null && GlobalQueue._e(e, t);
665
665
  e.Se = clock;
666
666
  insertSubs(e);
667
667
  schedule();
@@ -62,7 +62,7 @@ function runEffect(t) {
62
62
  // same flush must not be hijacked by a later-arriving error status.
63
63
  if (t.S & STATUS_ERROR && t.Pe === EFFECT_USER) {
64
64
  const E = unwrapStatusError(t._);
65
- t.ct = t.Ue;
65
+ t.ct = t.be;
66
66
  t.Be = false;
67
67
  try {
68
68
  t.ft ? t.ft(E, () => {
@@ -82,7 +82,7 @@ function runEffect(t) {
82
82
  t.Et = undefined;
83
83
  try {
84
84
  E?.();
85
- const e = t._t(t.Ue, t.ct);
85
+ const e = t._t(t.be, t.ct);
86
86
  if (false && e !== undefined && typeof e !== "function") ;
87
87
  // The final cleanup is invoked by disposeChildren at true disposal.
88
88
  t.Et = e;
@@ -94,7 +94,7 @@ function runEffect(t) {
94
94
  throw E;
95
95
  }
96
96
  } finally {
97
- t.ct = t.Ue;
97
+ t.ct = t.be;
98
98
  t.Be = false;
99
99
  }
100
100
  }
@@ -36,7 +36,7 @@ const activeLanes = new Set;
36
36
  // parent-child is a property of the nodes, not of write order — otherwise
37
37
  // the owner's write merges the companion's subscribers into this lane and
38
38
  // their effects wait on its async instead of flushing immediately.
39
- adoptCompanionLane(n.he, e);
39
+ adoptCompanionLane(n.Re, e);
40
40
  adoptCompanionLane(n.pe, e);
41
41
  return e;
42
42
  }
@@ -25,9 +25,9 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
25
25
  */
26
26
  /** The optimistic half of setSignal, fired when `_overrideValue !== undefined`. */ function optimisticWrite(e, n) {
27
27
  const t = e.Ae !== NOT_PENDING;
28
- const i = t ? unwrapOverride(e.Ae) : e.Ue;
28
+ const i = t ? unwrapOverride(e.Ae) : e.be;
29
29
  if (typeof n === "function") n = n(i);
30
- const u = !!(e.S & STATUS_UNINITIALIZED) || !e.be || !e.be(i, n);
30
+ const u = !!(e.S & STATUS_UNINITIALIZED) || !e.he || !e.he(i, n);
31
31
  if (!u) {
32
32
  // Same-value write with an active override still entangles the current
33
33
  // action's transition — the hold must outlast all overlapping actions.
@@ -46,15 +46,15 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
46
46
  // joint root). resolveTransition prefers this over the lane's _transition,
47
47
  // which a shared subscriber can merge across transactions (#2912).
48
48
  e.sn = activeTransition;
49
- const o = getOrCreateLane(e);
50
- e.Me = o;
49
+ const r = getOrCreateLane(e);
50
+ e.Me = r;
51
51
  // Literal undefined must not land raw: the slot doubles as the optimistic
52
52
  // brand, and erasing it makes the write invisible and routes follow-up
53
53
  // writes off the optimistic path into permanent commits (#2898).
54
54
  e.Ae = n === undefined ? OVERRIDE_UNDEFINED : n;
55
55
  // syncCompanions only pokes _pendingSignal/_latestValueComputed — with
56
56
  // neither companion present the call is a guaranteed no-op.
57
- (e.he !== undefined || e.pe !== undefined) && GlobalQueue._e !== null && GlobalQueue._e(e, n);
57
+ (e.Re !== undefined || e.pe !== undefined) && GlobalQueue._e !== null && GlobalQueue._e(e, n);
58
58
  e.Se = clock;
59
59
  insertSubs(e, true);
60
60
  schedule();
@@ -89,7 +89,7 @@ function resolveOptimisticNodes(e) {
89
89
  if (!(n.S & STATUS_PENDING)) n.S &= ~STATUS_UNINITIALIZED;
90
90
  const i = n.Ae;
91
91
  n.Ae = NOT_PENDING;
92
- if (i !== NOT_PENDING && n.Ue !== unwrapOverride(i)) insertSubs(n, true);
92
+ if (i !== NOT_PENDING && n.be !== unwrapOverride(i)) insertSubs(n, true);
93
93
  n.Ie = null;
94
94
  n.sn = null;
95
95
  }
@@ -98,9 +98,9 @@ function resolveOptimisticNodes(e) {
98
98
  // transition that produced them (A19 — pending is a property of the data).
99
99
  for (let t = 0; t < n; t++) {
100
100
  const n = e[t];
101
- if (n.he || n.pe) GlobalQueue.un(n);
101
+ if (n.Re || n.pe) GlobalQueue.un(n);
102
102
  const i = n.nn;
103
- if (i && (i.he === n || i.pe === n)) GlobalQueue.un(i);
103
+ if (i && (i.Re === n || i.pe === n)) GlobalQueue.un(i);
104
104
  }
105
105
  e.splice(0, n);
106
106
  }
@@ -164,7 +164,19 @@ function cleanupCompletedLanes(e) {
164
164
  * read()'s value selection under a lane: return the committed `_value` for
165
165
  * optimistic/lane-assigned signals, stale-mode reads, and pending owners.
166
166
  */ function laneReadsCommitted(e, n, t) {
167
- return e.Ae !== undefined || !!e.Me || n === e && stale && t.nn !== e || !!(n.S & STATUS_PENDING);
167
+ if (e.Ae !== undefined || !!e.Me || !!(n.S & STATUS_PENDING)) return true;
168
+ if (n === e && stale && t.nn !== e) {
169
+ // The committed view can hide a same-tick ambient write (a lane member —
170
+ // even just an isPending companion flip — puts the reader "under a lane"
171
+ // against unrelated plain writes). With no transaction the write commits
172
+ // at THIS flush's end with no re-delivery, so record the reader for
173
+ // replay at commit — the same contract gatedRead provides when a
174
+ // transaction is active (#2963). If a transaction forms mid-flush,
175
+ // initTransition carries the recording over to it.
176
+ if (e.De !== NOT_PENDING && activeTransition === null) globalQueue.m.ln.add(t);
177
+ return true;
178
+ }
179
+ return false;
168
180
  }
169
181
 
170
182
  /**
@@ -218,8 +230,8 @@ function trackOptimisticStore(e) {
218
230
  if (GlobalQueue.ht !== null) return;
219
231
  GlobalQueue.ht = optimisticWrite;
220
232
  GlobalQueue.En = resolveOptimisticNodes;
221
- GlobalQueue.dn = transitionBlocked;
222
- GlobalQueue.Tn = cleanupCompletedLanes;
233
+ GlobalQueue.Tn = transitionBlocked;
234
+ GlobalQueue.dn = cleanupCompletedLanes;
223
235
  GlobalQueue.In = runLaneEffects;
224
236
  GlobalQueue.Ot = gatedRead;
225
237
  GlobalQueue.Ct = laneSuspends;
@@ -51,7 +51,7 @@ function disposeChildren(e, n = false, t) {
51
51
  // edge). Snap runs after the DISPOSED flag is set so the oracle reads
52
52
  // false, and notifies subscribers still watching the companion.
53
53
  const n = e;
54
- if (n.he || n.pe) GlobalQueue.un(n);
54
+ if (n.Re || n.pe) GlobalQueue.un(n);
55
55
  }
56
56
  if (n && e.ae) e.Te = null;
57
57
  let l = t ? e.Qe : e.ke;
@@ -330,8 +330,8 @@ class GlobalQueue extends Queue {
330
330
  // once the gate holds.
331
331
  static ht=null;
332
332
  static En=null;
333
- static dn=null;
334
333
  static Tn=null;
334
+ static dn=null;
335
335
  static In=null;
336
336
  static Ot=null;
337
337
  static Ct=null;
@@ -486,6 +486,12 @@ class GlobalQueue extends Queue {
486
486
  }
487
487
  if (t.A.length) activeTransition.A.push(...t.A);
488
488
  for (const e of t.cn) activeTransition.cn.add(e);
489
+ // Gated readers recorded against the ambient batch move with it: their
490
+ // replay-at-commit now happens at the transaction's completion.
491
+ if (t.ln.size) {
492
+ for (const e of t.ln) activeTransition.ln.add(e);
493
+ t.ln.clear();
494
+ }
489
495
  currentBatch = this.m = activeTransition;
490
496
  }
491
497
  for (const e of activeLanes) {
@@ -537,14 +543,14 @@ function commitPendingNode(e) {
537
543
  const t = e;
538
544
  if (!t.ae) {
539
545
  if (e.De !== NOT_PENDING) {
540
- e.Ue = e.De;
546
+ e.be = e.De;
541
547
  e.De = NOT_PENDING;
542
548
  }
543
- if (e.he || e.pe) GlobalQueue.un(e);
549
+ if (e.Re || e.pe) GlobalQueue.un(e);
544
550
  return;
545
551
  }
546
552
  if (e.De !== NOT_PENDING) {
547
- e.Ue = e.De;
553
+ e.be = e.De;
548
554
  e.De = NOT_PENDING;
549
555
  // Set _modified for effects, but not for tracked effects (they handle their own scheduling)
550
556
  if (e.Pe && e.Pe !== EFFECT_TRACKED) e.Be = true;
@@ -552,7 +558,7 @@ function commitPendingNode(e) {
552
558
  t.se &= ~REACTIVE_MANUAL_WRITE;
553
559
  if (!(t.S & STATUS_PENDING)) t.S &= ~STATUS_UNINITIALIZED;
554
560
  if (t.Qe !== null || t.ye !== null) GlobalQueue.me(t, false, true);
555
- if (e.he || e.pe) GlobalQueue.un(e);
561
+ if (e.Re || e.pe) GlobalQueue.un(e);
556
562
  }
557
563
 
558
564
  function commitPendingNodes() {
@@ -579,13 +585,19 @@ function finalizePureQueue(e = null, t = false) {
579
585
  // which installed the engine's hooks.
580
586
  if (t.je.length) GlobalQueue.En(t.je);
581
587
  // Replay entanglement: subs recorded by the read-time gate get rescheduled
582
- // so they re-run with the now-committed values visible.
583
- if (e && e.ln.size) {
584
- for (const t of e.ln) {
585
- if (t.se & REACTIVE_DISPOSED) continue;
586
- enqueueSub(t);
588
+ // so they re-run with the now-committed values visible. The ambient batch
589
+ // replays too laneReadsCommitted records readers whose committed-view
590
+ // read hid a same-tick plain write that just committed above (#2963).
591
+ if (t.ln.size) {
592
+ for (const e of t.ln) {
593
+ if (e.se & REACTIVE_DISPOSED) continue;
594
+ enqueueSub(e);
587
595
  }
588
- e.ln.clear();
596
+ t.ln.clear();
597
+ // A completing transition keeps the outer flush loop alive by itself;
598
+ // the ambient batch needs the re-arm or the replay sits in the heap
599
+ // until the next unrelated write.
600
+ schedule();
589
601
  }
590
602
  // Declared motion ends with the transaction: settle (or plain flush end
591
603
  // for ambient marks) releases each registration's refcount. A non-empty
@@ -604,7 +616,7 @@ function finalizePureQueue(e = null, t = false) {
604
616
  if (t.cn.size) GlobalQueue.Dt(t.cn, e);
605
617
  sweepTransientStoreNodes();
606
618
  // Lanes only enter activeLanes through the engine's getOrCreateLane.
607
- if (activeLanes.size) GlobalQueue.Tn(e);
619
+ if (activeLanes.size) GlobalQueue.dn(e);
608
620
  }
609
621
  }
610
622
 
@@ -710,7 +722,7 @@ function transitionComplete(e) {
710
722
  // Override blockage lives with the engine (absent hook = "no optimistic
711
723
  // blockage"); the hook's loops over _optimisticNodes/_optimisticStores are
712
724
  // no-ops when the transition holds neither, so no pre-check is needed.
713
- if (t && GlobalQueue.dn?.(e)) t = false;
725
+ if (t && GlobalQueue.Tn?.(e)) t = false;
714
726
  t && (e.fn = true);
715
727
  return t;
716
728
  }