@solidjs/signals 2.0.0-rc.8 → 2.0.0-rc.9

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 (90) hide show
  1. package/dist/dev-shared.js +1399 -285
  2. package/dist/dev.attribution.js +459 -307
  3. package/dist/dev.js +1864 -435
  4. package/dist/observe/affects.js +3 -1
  5. package/dist/observe/attribution.js +7 -1
  6. package/dist/observe/boundaries.js +209 -154
  7. package/dist/observe/core/action.js +18 -8
  8. package/dist/observe/core/async.js +220 -110
  9. package/dist/observe/core/attribution-costs.js +66 -0
  10. package/dist/observe/core/attribution-feedback.js +282 -0
  11. package/dist/observe/core/attribution-hooks.js +23 -1
  12. package/dist/observe/core/attribution-queries.js +28 -0
  13. package/dist/observe/core/attribution.js +262 -485
  14. package/dist/observe/core/constants.js +34 -1
  15. package/dist/observe/core/context.js +3 -3
  16. package/dist/observe/core/core.js +867 -367
  17. package/dist/observe/core/dev.js +78 -17
  18. package/dist/observe/core/effect.js +67 -51
  19. package/dist/observe/core/error-hooks.js +71 -0
  20. package/dist/observe/core/external.js +4 -4
  21. package/dist/observe/core/graph.js +37 -37
  22. package/dist/observe/core/heap.js +45 -45
  23. package/dist/observe/core/invariants.js +2 -0
  24. package/dist/observe/core/lanes.js +86 -49
  25. package/dist/observe/core/optimistic.js +242 -95
  26. package/dist/observe/core/owner.js +98 -84
  27. package/dist/observe/core/scheduler.js +543 -305
  28. package/dist/observe/core/verdict.js +247 -129
  29. package/dist/observe/index.js +7 -3
  30. package/dist/observe/map.js +126 -124
  31. package/dist/observe/signals.js +33 -17
  32. package/dist/observe/store/index.js +2 -0
  33. package/dist/observe/store/next/optimistic.js +141 -132
  34. package/dist/observe/store/next/projection.js +34 -21
  35. package/dist/observe/store/next/reconcile.js +58 -56
  36. package/dist/observe/store/next/store.js +260 -146
  37. package/dist/observe/store/store.js +5 -3
  38. package/dist/observe/store/utils.js +948 -135
  39. package/dist/prod/attribution.js +26 -17
  40. package/dist/prod/boundaries.js +128 -76
  41. package/dist/prod/core/action.js +16 -8
  42. package/dist/prod/core/async.js +251 -143
  43. package/dist/prod/core/constants.js +34 -1
  44. package/dist/prod/core/context.js +3 -3
  45. package/dist/prod/core/core.js +843 -347
  46. package/dist/prod/core/dev.js +17 -1
  47. package/dist/prod/core/effect.js +48 -34
  48. package/dist/prod/core/error-hooks.js +71 -0
  49. package/dist/prod/core/external.js +4 -4
  50. package/dist/prod/core/graph.js +37 -37
  51. package/dist/prod/core/heap.js +45 -45
  52. package/dist/prod/core/lanes.js +90 -53
  53. package/dist/prod/core/optimistic.js +247 -100
  54. package/dist/prod/core/owner.js +57 -45
  55. package/dist/prod/core/scheduler.js +543 -305
  56. package/dist/prod/core/verdict.js +245 -127
  57. package/dist/prod/index.js +7 -3
  58. package/dist/prod/map.js +112 -112
  59. package/dist/prod/signals.js +28 -12
  60. package/dist/prod/store/next/optimistic.js +135 -128
  61. package/dist/prod/store/next/projection.js +31 -20
  62. package/dist/prod/store/next/store.js +325 -252
  63. package/dist/prod/store/store.js +2 -2
  64. package/dist/prod/store/utils.js +946 -135
  65. package/dist/types/attribution.d.ts +7 -2
  66. package/dist/types/attribution.prod.d.ts +10 -1
  67. package/dist/types/boundaries.d.ts +10 -1
  68. package/dist/types/core/action.d.ts +12 -5
  69. package/dist/types/core/attribution-costs.d.ts +35 -0
  70. package/dist/types/core/attribution-feedback.d.ts +133 -0
  71. package/dist/types/core/attribution-hooks.d.ts +28 -3
  72. package/dist/types/core/attribution-queries.d.ts +10 -0
  73. package/dist/types/core/attribution.d.ts +51 -172
  74. package/dist/types/core/constants.d.ts +33 -0
  75. package/dist/types/core/core.d.ts +186 -5
  76. package/dist/types/core/dev.d.ts +129 -9
  77. package/dist/types/core/error-hooks.d.ts +71 -0
  78. package/dist/types/core/index.d.ts +3 -1
  79. package/dist/types/core/invariants.d.ts +4 -0
  80. package/dist/types/core/lanes.d.ts +31 -4
  81. package/dist/types/core/scheduler.d.ts +95 -2
  82. package/dist/types/core/types.d.ts +3 -0
  83. package/dist/types/index.d.ts +2 -2
  84. package/dist/types/signals.d.ts +8 -0
  85. package/dist/types/store/index.d.ts +2 -1
  86. package/dist/types/store/next/optimistic.d.ts +1 -1
  87. package/dist/types/store/next/store.d.ts +6 -5
  88. package/dist/types/store/next/target.d.ts +1 -1
  89. package/dist/types/store/utils.d.ts +177 -6
  90. package/package.json +1 -1
@@ -1,22 +1,24 @@
1
- import { STATUS_UNINITIALIZED, CONFIG_CHILD_COMPANIONS, STATUS_ERROR, STATUS_PENDING, CONFIG_INPUTS_PUBLISHED, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_DISPOSED } from "./constants.js";
1
+ import { STATUS_UNINITIALIZED, CONFIG_CHILD_COMPANIONS, STATUS_ERROR, STATUS_PENDING, CONFIG_DERIVED_OVERRIDE, CONFIG_INPUTS_PUBLISHED, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, unwrapOverride, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_DISPOSED } from "./constants.js";
2
2
 
3
3
  import { attrHooks } from "./attribution-hooks.js";
4
4
 
5
- import { untrack, ext, statusNotifierOf, setSignal, context } from "./core.js";
5
+ import { untrack, ext, statusNotifierOf, hasActiveOverride, setSignal, context } from "./core.js";
6
6
 
7
7
  import "./invariants.js";
8
8
 
9
+ import "./dev.js";
10
+
9
11
  import { StatusError, NotReadyError } from "./error.js";
10
12
 
11
13
  import { trimStaleDeps, unobserved } from "./graph.js";
12
14
 
13
15
  import { enqueueSub } from "./heap.js";
14
16
 
15
- import { hasActiveOverride, assignOrMergeLane, resolveLane, resolveTransition } from "./lanes.js";
17
+ import { assignOrMergeLane, resolveLane, resolveTransition } from "./lanes.js";
16
18
 
17
19
  import { cleanup } from "./owner.js";
18
20
 
19
- import { GlobalQueue, schedule, flush, queuePendingNode, insertSubs, clock, waitingTransition, currentTransition, globalQueue, setOrigin, origin } from "./scheduler.js";
21
+ import { GlobalQueue, schedule, flush, queuePendingNode, insertSubs, clock, waitingTransition, currentTransition, globalQueue, enterWaiting, setOrigin, origin } from "./scheduler.js";
20
22
 
21
23
  // The lazily-created Set is the ONE container for pending sources. Its
22
24
  // predecessor — a singular slot promoted to a Set on the second source —
@@ -24,22 +26,22 @@ import { GlobalQueue, schedule, flush, queuePendingNode, insertSubs, clock, wait
24
26
  // overlapping source landed beside the Set and removePendingSource refused
25
27
  // to clear it, stranding the Set members' pending forever (#2893).
26
28
  function addPendingSource(e, t) {
27
- if (e.o?.ee?.has(t)) return false;
28
- (ext(e).ee ??= new Set).add(t);
29
+ if (e.o?.ut?.has(t)) return false;
30
+ (ext(e).ut ??= new Set).add(t);
29
31
  return true;
30
32
  }
31
33
 
32
34
  function removePendingSource(e, t) {
33
- const n = e.o?.ee;
35
+ const n = e.o?.ut;
34
36
  if (!n?.delete(t)) return false;
35
- if (!n.size) e.o.ee = undefined;
37
+ if (!n.size) e.o.ut = undefined;
36
38
  return true;
37
39
  }
38
40
 
39
41
  function clearPendingSources(e) {
40
42
  // This set is node-owned and never shared; dropping the sole reference
41
43
  // releases the set and every entry without a redundant clear() walk.
42
- if (e.o !== null) e.o.ee = undefined;
44
+ if (e.o !== null) e.o.ut = undefined;
43
45
  }
44
46
 
45
47
  // A rejection-pending only resolves through the settle sweep over the
@@ -48,9 +50,9 @@ function clearPendingSources(e) {
48
50
  // (pending sources propagate the origin node, so this covers any depth).
49
51
  // Also guards branch-local recovery: another dependency may still need the source.
50
52
  function retryReaches(e, t) {
51
- for (let n = e.te; n; n = n.ne) {
52
- const e = n.oe.re || n.oe;
53
- if (e === t || e.o?.ee?.has(t)) return true;
53
+ for (let n = e.ee; n; n = n.te) {
54
+ const e = n.re.ne || n.re;
55
+ if (e === t || e.o?.ut?.has(t)) return true;
54
56
  }
55
57
  return false;
56
58
  }
@@ -62,7 +64,7 @@ function retryReaches(e, t) {
62
64
  * with NO read-visible pending status, no downstream propagation, no
63
65
  * transition, no lane registration. Commit #0 keeps serving.
64
66
  */ function parkLoadingWindow(e, t) {
65
- ext(e).ie = true;
67
+ ext(e).oe = true;
66
68
  if (t.source) addPendingSource(e, t.source);
67
69
  // A settled error is the node's answer ("the error stays the answer until
68
70
  // this retry can actually run") — the park must not replace it: reads
@@ -87,10 +89,10 @@ function setPendingError(e, t, n) {
87
89
  }
88
90
 
89
91
  function forEachDependent(e, t) {
90
- for (let n = e.u; n !== null; n = n.se) t(n.le, n);
92
+ for (let n = e.u; n !== null; n = n.ie) t(n.se, n);
91
93
  // `?? null`: affects() marks route plain signals (no `_child` slot) through here.
92
94
  for (let n = e.o?.i ?? null; n !== null; n = n.ue) {
93
- for (let e = n.u; e !== null; e = e.se) t(e.le, e);
95
+ for (let e = n.u; e !== null; e = e.ie) t(e.se, e);
94
96
  }
95
97
  }
96
98
 
@@ -105,7 +107,7 @@ function forEachDependent(e, t) {
105
107
  // callbacks handle their own release (settleAutodispose in handleAsync); this
106
108
  // covers derivatively-pending dependents, which have no callbacks of their own.
107
109
  function releaseIfSettledUnobserved(e) {
108
- e.ot && e.T & CONFIG_AUTO_DISPOSE && !e.u && !(e.it & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
110
+ e.ht && e.C & CONFIG_AUTO_DISPOSE && !e.u && !(e.ft & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
109
111
  }
110
112
 
111
113
  // Error-path sweep: notifyStatus(STATUS_ERROR) clears dependents' pending
@@ -118,7 +120,7 @@ function releaseSettledDependents(e) {
118
120
  const visit = e => {
119
121
  if (n.has(e)) return;
120
122
  n.add(e);
121
- if (!e.u && e.T & CONFIG_AUTO_DISPOSE) (t ??= []).push(e);
123
+ if (!e.u && e.C & CONFIG_AUTO_DISPOSE) (t ??= []).push(e);
122
124
  forEachDependent(e, visit);
123
125
  };
124
126
  forEachDependent(e, visit);
@@ -169,7 +171,7 @@ function settlePendingSource(e, t = e) {
169
171
  let r;
170
172
  const o = new Set;
171
173
  // Companion updates no-op without the verdict layer (null hook).
172
- const i = GlobalQueue.fe;
174
+ const i = GlobalQueue.le;
173
175
  const settle = s => {
174
176
  if (o.has(s)) return;
175
177
  // A conditional dropped this source, but another dependency can still
@@ -181,30 +183,30 @@ function settlePendingSource(e, t = e) {
181
183
  if (t !== e && retryReaches(s, t)) return;
182
184
  if (!removePendingSource(s, t)) return;
183
185
  o.add(s);
184
- s.ae = clock;
185
- const l = s.o?.ee?.values().next().value;
186
+ s.fe = clock;
187
+ const u = s.o?.ut?.values().next().value;
186
188
  // STATUS_ERROR + pending sources only coexist via an errored loading
187
189
  // window's park (notifyStatus(STATUS_ERROR) clears pending sources
188
190
  // otherwise): the settled error stays the answer through the settle —
189
191
  // nulling it here would have reads throw `null` until the re-enqueued
190
192
  // retry lands, or lose it entirely if that retry parks again (#2989).
191
- const u = s.S & STATUS_ERROR;
192
- if (l) {
193
- if (!u) setPendingError(s, l);
193
+ const l = s.S & STATUS_ERROR;
194
+ if (u) {
195
+ if (!l) setPendingError(s, u);
194
196
  i?.(s);
195
197
  } else {
196
198
  s.S &= ~STATUS_PENDING;
197
- if (!u) setPendingError(s);
199
+ if (!l) setPendingError(s);
198
200
  i?.(s);
199
- if (s.o?.ie) {
201
+ if (s.o?.oe) {
200
202
  enqueueSub(s);
201
203
  n = true;
202
204
  }
203
- if (s.o !== null) s.o.ie = false;
205
+ if (s.o !== null) s.o.oe = false;
204
206
  // Fully settled with nobody watching: release candidate (#2934). Checked
205
207
  // again at release time — deferred so unobserved() can't unlink subs
206
208
  // lists this walk is still iterating.
207
- if (!s.u && s.T & CONFIG_AUTO_DISPOSE) (r ??= []).push(s);
209
+ if (!s.u && s.C & CONFIG_AUTO_DISPOSE) (r ??= []).push(s);
208
210
  }
209
211
  forEachDependent(s, settle);
210
212
  };
@@ -221,9 +223,9 @@ function isThenable(e) {
221
223
  }
222
224
 
223
225
  /** Fire and clear a node's iterator-flight cancellation hook (#3122). */ function releaseFlightTeardown(e) {
224
- const t = e.o?.ce;
226
+ const t = e.o?.ae;
225
227
  if (t != null) {
226
- e.o.ce = null;
228
+ e.o.ae = null;
227
229
  t();
228
230
  }
229
231
  }
@@ -238,9 +240,9 @@ function handleAsync(e, t, n) {
238
240
  });
239
241
  }
240
242
  if (!o && !r) {
241
- if (e.o !== null) e.o.Se = null;
243
+ if (e.o !== null) e.o.ce = null;
242
244
  // A sync landing is the first real answer for a loadingValue node.
243
- e.de = false;
245
+ e.Se = false;
244
246
  return t;
245
247
  }
246
248
  // Flight replacement relies on recompute's supersede release for iterator
@@ -248,7 +250,14 @@ function handleAsync(e, t, n) {
248
250
  // self-registration — runs during a recompute of `el`, which has already
249
251
  // fired _flightTeardown. A future non-recompute registration path must
250
252
  // release it here before overwriting _inFlight.
251
- ext(e).Se = t;
253
+ ext(e).ce = t;
254
+ // The run that asked this flight read every input without throwing: an
255
+ // input still in flight was masked for it (an active override, A17), so
256
+ // pending state those inputs propagated onto the node earlier does not
257
+ // describe this answer. Drop it — the flight is the node's pending now.
258
+ // The landing retires only the flight's own entry (landStatus, #3373), so
259
+ // an entry that survived here would hold the node past its own answer.
260
+ e.o.ut = undefined;
252
261
  // Provenance of the question this flight asks (#3331): the action whose
253
262
  // window is registering it, or the flight whose landing is. Its landings
254
263
  // propagate under it (asyncWrite) so an override downstream can tell a
@@ -279,28 +288,34 @@ function handleAsync(e, t, n) {
279
288
  // waiting on this flight into it at the landing — a reveal that
280
289
  // discovered the flight (#3305) would then wait on the owner's action
281
290
  // instead of on the flight (#3334). Enter the waiter: the transaction
282
- // whose blocker this landing clears.
283
- if (e.o?.Te) t = waitingTransition(e) ?? t;
284
- if (t && e.S & STATUS_UNINITIALIZED && !currentTransition(t).Ie.has(e)) {
291
+ // whose blocker this landing clears. Then every transaction waiting on
292
+ // the flight folds in (enterWaiting): a reveal that discovered it
293
+ // completes at its landing (A15) a stampless node's fresh batch
294
+ // included (its flight started under a batch that committed beneath it,
295
+ // #3305). The fold used to happen as each stamped reader recomputed,
296
+ // which effects no longer do (#3407).
297
+ if (e.o?.de) t = waitingTransition(e) ?? t;
298
+ if (t && e.S & STATUS_UNINITIALIZED && !currentTransition(t).ot.has(e)) {
285
299
  // Drop the stale stamp too: the plain settle write (setSignal) and the
286
300
  // stash-path restamp both re-enter the transaction through it.
287
- e.Ee = null;
301
+ e.Te = null;
288
302
  return;
289
303
  }
290
304
  globalQueue.initTransition(t);
305
+ enterWaiting(e);
291
306
  };
292
307
  const handleError = n => {
293
- if (e.o?.Se !== t) return;
308
+ if (e.o?.ce !== t) return;
294
309
  // NotReadyError from rejected promises should be treated as pending, not error
295
310
  let r = n instanceof NotReadyError;
296
- if (r && e.de) {
311
+ if (r && e.Se) {
297
312
  // Loading window: the flight died waiting on an unready source. Keep
298
313
  // serving commit #0 — same parking as recompute's catch for sync
299
314
  // dependency throws. The dead flight is released so the clock-gated
300
315
  // error-retry pull (updateIfNecessary) can also re-ask.
301
- if (e.o !== null) e.o.Se = null;
316
+ if (e.o !== null) e.o.ce = null;
302
317
  parkLoadingWindow(e, n);
303
- e.ae = clock;
318
+ e.fe = clock;
304
319
  return;
305
320
  }
306
321
  settleTransition();
@@ -309,18 +324,18 @@ function handleAsync(e, t, n) {
309
324
  // rejected flight will never settle its self entry, so transfer ownership
310
325
  // after notifyStatus has propagated the replacement source.
311
326
  if (r) settlePendingSource(e);
312
- e.ae = clock;
327
+ e.fe = clock;
313
328
  // A real error settles derivatively-pending dependents (notifyStatus
314
329
  // cleared their pending sources), so stranded lazy ones release here —
315
330
  // the error twin of settlePendingSource's release (#2934).
316
331
  if (!r) releaseSettledDependents(e);
317
332
  };
318
333
  const asyncWrite = (r, o) => {
319
- if (e.o?.Se !== t) return;
334
+ if (e.o?.ce !== t) return;
320
335
  // If the node was dirtied by a newer write (optimistic override or regular),
321
336
  // skip this stale async result — the upcoming flush will recompute the node
322
337
  // with the new value, creating a fresh Promise that supersedes this one.
323
- if (e.it & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
338
+ if (e.ft & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
324
339
  // The landing propagates under the flight's provenance (#3331) — through
325
340
  // the flush below, which clears it.
326
341
  setOrigin(i);
@@ -332,12 +347,11 @@ function handleAsync(e, t, n) {
332
347
  // that reveal or companion synchronization briefly classifies the held
333
348
  // old value as pending, a one-frame pulse to direct observers (#3178).
334
349
  // A truthy capture implies `_x` exists, so the restore writes it directly.
335
- const l = e.o?.Ne;
336
- trimStaleDeps(e);
337
- clearStatus(e);
338
- if (l) e.o.Ne = true;
339
- const u = resolveLane(e);
340
- if (u) u._e.delete(e);
350
+ const u = e.o?.Ee;
351
+ landStatus(e);
352
+ if (u) e.o.Ee = true;
353
+ const l = resolveLane(e);
354
+ if (l) l.Ie.delete(e);
341
355
  // Attribution hook: lets the engine snapshot state before the landing
342
356
  // branches, so it can tell whether the plain path's setSignal committed a
343
357
  // change (and only then classify it as an async landing).
@@ -349,8 +363,12 @@ function handleAsync(e, t, n) {
349
363
  handleError(e);
350
364
  return;
351
365
  }
352
- if (s) clearStatus(e, true);
353
- } else if (e.o?.De !== undefined) {
366
+ if (s) landStatus(e, true);
367
+ } else if (e.o?.Ne !== undefined && !(l && e.C & CONFIG_DERIVED_OVERRIDE)) {
368
+ // A derived override's landing UNDER its lane is the lane's own work
369
+ // (the branch below); demoted — its source superseded (A18) — the
370
+ // landing is the truth the correction asked for, and holds and
371
+ // supersedes here like the sync twin (recompute). Otherwise:
354
372
  // Optimistic node — resting OR covered by an active override — holds
355
373
  // through the shared pending-node path, exactly like a plain async memo,
356
374
  // so the commit clears STATUS_UNINITIALIZED (#2806) and elevation to
@@ -360,8 +378,8 @@ function handleAsync(e, t, n) {
360
378
  // (A17 — every reader sees the override); the revert reveals whatever
361
379
  // has committed by then, so corrections reveal atomically with their
362
380
  // transition rather than escaping it.
363
- if (e.Pe === NOT_PENDING) queuePendingNode(e);
364
- e.Pe = r;
381
+ if (e._e === NOT_PENDING) queuePendingNode(e);
382
+ e._e = r;
365
383
  // The hold is a companion-visible write like any other (A13/A19): the
366
384
  // clearStatus() above computed its verdict before the hold existed, so
367
385
  // isPending must re-derive (the value is not final until commit — V1)
@@ -379,25 +397,28 @@ function handleAsync(e, t, n) {
379
397
  // action on an until() never re-notified). The hook is installed with
380
398
  // the engine, which an active override implies. The propagation runs
381
399
  // under this flight's provenance (setOrigin above).
382
- GlobalQueue.Ae?.(e, r);
400
+ GlobalQueue.De?.(e, r);
383
401
  if (!hasActiveOverride(e)) {
384
402
  if (attrHooks !== null) attrHooks.asyncEnd(e, undefined, r, true);
385
403
  insertSubs(e);
386
404
  } else GlobalQueue.Oe(e, r);
387
- e.ae = clock;
388
- } else if (u) {
405
+ e.fe = clock;
406
+ } else if (l) {
389
407
  // Route through lane's effect queue for independent flushing
390
- const t = e.Re;
391
- const n = e.ge;
392
- const o = e.Ue;
408
+ const t = e.Pe;
409
+ const n = hasActiveOverride(e) ? unwrapOverride(e.o.Ne) : e.Ae;
410
+ const o = e.Re;
393
411
  try {
394
412
  if (!t && s || !o || !o(r, n)) {
395
- e.ge = r;
396
- e.ae = clock;
413
+ // Lanes stage (#3479): a memo's landing under its lane is a derived
414
+ // override, as its sync pass's result is (recompute) — `_value`
415
+ // stays the committed truth for readers off the lane.
416
+ if (t) e.Ae = r; else GlobalQueue.ge(e, r, l);
417
+ e.fe = clock;
397
418
  // The latest() shadow write gives latest() effects independent lanes; the
398
419
  // _pendingSignal update is a no-op repeat of the clearStatus() call above
399
420
  // (computePendingState doesn't read _value).
400
- GlobalQueue.Ae?.(e, r);
421
+ GlobalQueue.De?.(e, r);
401
422
  insertSubs(e, true);
402
423
  }
403
424
  } catch (t) {
@@ -435,9 +456,18 @@ function handleAsync(e, t, n) {
435
456
  // window when the hold commits, so no one-frame isPending pulse can leak
436
457
  // to live observers between the landing and its commit (#2990). The
437
458
  // quiet re-ask classification follows the same schedule (#3178).
438
- if (e.Pe === NOT_PENDING) {
439
- e.de = false;
440
- if (l) e.o.Ne = false;
459
+ if (e._e === NOT_PENDING) {
460
+ e.Se = false;
461
+ if (u) e.o.Ee = false;
462
+ // The landing published: the dependency tail the flight's pass left
463
+ // linked goes now (A30, #3410). A transition-held landing has not
464
+ // replaced the committed frame — the committed value still derives
465
+ // from the previous pass's inputs, and a mainline write to one of them
466
+ // must reach this node and join its hold (its stamp) instead of
467
+ // publishing beside the stale derivation (#3461: `b() ? b() : a()`
468
+ // held on `b` dropped `a` at its landing, and `A: 1` then committed
469
+ // beside `Selected: 0`). `commitPendingNode` trims a held landing.
470
+ trimStaleDeps(e);
441
471
  }
442
472
  settlePendingSource(e);
443
473
  schedule();
@@ -452,7 +482,7 @@ function handleAsync(e, t, n) {
452
482
  // Returns whether the node released, so the iterator branch can stop
453
483
  // pulling values instead of pumping an unobserved stream forever (#2935).
454
484
  const settleAutodispose = () => {
455
- if (e.T & CONFIG_AUTO_DISPOSE && !e.u && !(e.S & STATUS_PENDING)) {
485
+ if (e.C & CONFIG_AUTO_DISPOSE && !e.u && !(e.S & STATUS_PENDING)) {
456
486
  unobserved(e);
457
487
  return true;
458
488
  }
@@ -472,11 +502,11 @@ function handleAsync(e, t, n) {
472
502
  const consumeIterator = (n, r) => {
473
503
  const o = n[Symbol.asyncIterator]();
474
504
  let i = false;
475
- let l = false;
476
- let u = !r;
505
+ let u = false;
506
+ let l = !r;
477
507
  const close = () => {
478
- if (l) return;
479
- l = true;
508
+ if (u) return;
509
+ u = true;
480
510
  try {
481
511
  const e = o.return?.();
482
512
  if (isThenable(e)) e.then(undefined, () => {});
@@ -487,7 +517,7 @@ function handleAsync(e, t, n) {
487
517
  // owner-death backstop, but its disposal list can be zombie-deferred
488
518
  // until the SUPERSEDING flight settles. The teardown slot fires at the
489
519
  // _inFlight release sites so supersede stops this stream immediately.
490
- ext(e).ce = close;
520
+ ext(e).ae = close;
491
521
  // Release check before each next pull: an unobserved lazy node must tear
492
522
  // down (its close above runs via disposal, closing the iterator) instead
493
523
  // of pumping the stream forever with zero subscribers (#2935).
@@ -513,17 +543,17 @@ function handleAsync(e, t, n) {
513
543
  // stash: it must write through the async path or the value is
514
544
  // silently dropped. (The deferred posture never has a caller, so
515
545
  // initialRead starts false there and everything writes through.)
516
- if (c && u) {
546
+ if (c && l) {
517
547
  n = r;
518
548
  f = true;
519
- if (r.done) l = true;
520
- } else if (e.o?.Se !== t) {
549
+ if (r.done) u = true;
550
+ } else if (e.o?.ce !== t) {
521
551
  return;
522
552
  } else if (!r.done) {
523
553
  i = true;
524
554
  asyncWrite(r.value, iterateOrRelease);
525
555
  } else {
526
- l = true;
556
+ u = true;
527
557
  if (i) {
528
558
  schedule();
529
559
  flush();
@@ -534,11 +564,11 @@ function handleAsync(e, t, n) {
534
564
  settleAutodispose();
535
565
  }
536
566
  }, n => {
537
- if (c && u) {
567
+ if (c && l) {
538
568
  r = n;
539
569
  a = true;
540
- } else if (e.o?.Se === t) {
541
- l = true;
570
+ } else if (e.o?.ce === t) {
571
+ u = true;
542
572
  handleError(n);
543
573
  settleAutodispose();
544
574
  }
@@ -546,9 +576,9 @@ function handleAsync(e, t, n) {
546
576
  c = false;
547
577
  if (a) {
548
578
  // Match the promise branch, but only rethrow during the initial read.
549
- l = true;
579
+ u = true;
550
580
  handleError(r);
551
- if (u) throw r;
581
+ if (l) throw r;
552
582
  return true;
553
583
  }
554
584
  if (f && !n.done) {
@@ -560,13 +590,13 @@ function handleAsync(e, t, n) {
560
590
  };
561
591
  const f = iterate();
562
592
  // Later iterate() calls run from asyncWrite, where rethrowing would be unhandled.
563
- u = false;
593
+ l = false;
564
594
  return i || f;
565
595
  };
566
596
  // Landed-synchronously verdict for a LIVE iterator drain; null when no live
567
597
  // drain ran (plain promise flight, or a deferred flatten). Drives the
568
598
  // shared NotReady/loading tail below.
569
- let l = null;
599
+ let u = null;
570
600
  // Flatten one async level: a thenable that RESOLVES to an AsyncIterable —
571
601
  // the shape every async stub returning a stream produces — consumes as the
572
602
  // stream itself rather than settling on the iterable object. One level
@@ -581,7 +611,7 @@ function handleAsync(e, t, n) {
581
611
  }
582
612
  if (!n) return false;
583
613
  const r = consumeIterator(e, t);
584
- if (!t) l = r;
614
+ if (!t) u = r;
585
615
  return true;
586
616
  };
587
617
  if (o) {
@@ -597,13 +627,13 @@ function handleAsync(e, t, n) {
597
627
  // becomes disposal-bearing, which is exactly the class a directly
598
628
  // returned iterable already occupies.
599
629
  const registerDeferredClose = t => {
600
- if (!e.he) e.he = t; else if (Array.isArray(e.he)) e.he.push(t); else e.he = [ e.he, t ];
630
+ if (!e.Ue) e.Ue = t; else if (Array.isArray(e.Ue)) e.Ue.push(t); else e.Ue = [ e.Ue, t ];
601
631
  };
602
632
  t.then(r => {
603
633
  if (i) {
604
634
  s = r;
605
635
  n = true;
606
- } else if (e.o?.Se === t && !(e.it & REACTIVE_DISPOSED) && flattenIfIterable(r, registerDeferredClose)) ; else {
636
+ } else if (e.o?.ce === t && !(e.ft & REACTIVE_DISPOSED) && flattenIfIterable(r, registerDeferredClose)) ; else {
607
637
  asyncWrite(r);
608
638
  settleAutodispose();
609
639
  }
@@ -628,63 +658,109 @@ function handleAsync(e, t, n) {
628
658
  // is opened — first-flight work on a loadingValue node is loading-class
629
659
  // (invisible to boundaries and transitions); the flight itself is
630
660
  // already registered in _inFlight and lands through asyncWrite.
631
- if (e.de) return e.ge;
661
+ if (e.Se) return e.Ae;
632
662
  globalQueue.initTransition(resolveTransition(e));
633
663
  throw new NotReadyError(context);
634
664
  } else if (!flattenIfIterable(s)) {
635
665
  // Synchronously-resolved promise: the first real answer landed.
636
- e.de = false;
666
+ e.Se = false;
637
667
  }
638
668
  // A sync-resolved promise holding an AsyncIterable flattened LIVE (we
639
669
  // are still inside the synchronous read): full initial-drain semantics
640
670
  // apply and the shared tail below settles the verdict.
641
671
  }
642
672
  if (r) flattenIfIterable(t);
643
- if (l !== null) {
644
- if (!l) {
673
+ if (u !== null) {
674
+ if (!u) {
645
675
  // Loading window: serve commit #0 (see the promise branch above).
646
- if (e.de) return e.ge;
676
+ if (e.Se) return e.Ae;
647
677
  globalQueue.initTransition(resolveTransition(e));
648
678
  throw new NotReadyError(context);
649
679
  }
650
680
  // A sync first yield (or immediate empty completion) is the first real
651
681
  // answer; async yields clear inside asyncWrite.
652
- e.de = false;
682
+ e.Se = false;
653
683
  }
654
684
  return s;
655
685
  }
656
686
 
657
687
  function clearStatus(e, t = false) {
658
- if (e.o?.ee) clearPendingSources(e);
659
- if (e.o?.ie) if (e.o !== null) e.o.ie = false;
688
+ if (e.o?.ut) clearPendingSources(e);
689
+ if (e.o?.oe) if (e.o !== null) e.o.oe = false;
660
690
  // The pending window is over; its quiet classification dies with it.
661
691
  // (Unconditional: _reask is baked into the node literals, so this is a
662
692
  // plain store to an existing slot — no shape change.)
663
- if (e.o !== null) e.o.Ne = false;
693
+ if (e.o !== null) e.o.Ee = false;
664
694
  e.S = t ? 0 : e.S & STATUS_UNINITIALIZED;
665
695
  if (e.o?._) setPendingError(e);
666
696
  // Update pending signal for isPending() reactivity (companions only exist
667
697
  // once the verdict layer created them, which installs the hooks).
668
- if (e.o?.be || e.o?.pe) GlobalQueue.fe(e);
669
- if (e.o?.i && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.Ge !== null) GlobalQueue.Ge(e);
698
+ if (e.o?.he || e.o?.Ge) GlobalQueue.le(e);
699
+ if (e.o?.i && e.C & CONFIG_CHILD_COMPANIONS && GlobalQueue.be !== null) GlobalQueue.be(e);
670
700
  const n = statusNotifierOf(e);
671
701
  if (n) n.call(e);
672
702
  }
673
703
 
704
+ /**
705
+ * Status clear for a flight LANDING (asyncWrite). A landing answers the
706
+ * node's OWN question — it retires the node's self entry, not the pending
707
+ * state its sources propagated onto it. An input re-asked while this flight
708
+ * was up (a second write to the signal feeding `a` while `b`'s first flight
709
+ * is in the air, #3373) marks `b` pending on `a` by propagation, with `b`'s
710
+ * flight still current: nothing superseded it (the re-ask only changed `a`'s
711
+ * status, not yet its value), so the landing arrives, and a full clear made
712
+ * `b` answer with the stale value — the transaction's reporter for `a` found
713
+ * nothing pending below it and committed the newer signal beside the older
714
+ * derived value (`2 / 1`); `isPending(b)` read false for the gap (#3376).
715
+ * With another source still pending the node stays derivatively pending on
716
+ * it; the landed value is written below (the staged answer is still the
717
+ * answer for the inputs it was asked with) and the input's own settle
718
+ * releases it, or its value change recomputes the node into a fresh flight.
719
+ * `_blocked` clears like a full clear: a landing that passed the `_inFlight`
720
+ * guard was not superseded by a re-run (recompute nulls `_inFlight` first),
721
+ * so the flag is the flight's own registration throw — the input settling
722
+ * unchanged must not re-run the node (an extra flight for the same inputs).
723
+ * The node is already STATUS_PENDING in that branch (only notifyStatus fills
724
+ * the set, with status; a loading-window park cannot coexist with a live
725
+ * flight since registration drops the set), so the flags only change when
726
+ * the first landing retires UNINITIALIZED. `_error` must move off self: a
727
+ * reader thrown NotReady(self) would park on a retired entry. Companions
728
+ * keep their verdict (pending before and after; the write re-syncs them).
729
+ */ function landStatus(e, t = false) {
730
+ const n = e.o?.ut;
731
+ // (The full clear below drops the set whether or not self was retired first.)
732
+ if (n && (n.delete(e), n.size)) {
733
+ e.o.oe = false;
734
+ if (t) e.S = STATUS_PENDING;
735
+ setPendingError(e, n.values().next().value);
736
+ } else clearStatus(e, t);
737
+ }
738
+
674
739
  function notifyStatus(e, t, n, r, o) {
675
740
  // Wrap regular errors to track source node
676
741
  if (t === STATUS_ERROR && !(n instanceof StatusError) && !(n instanceof NotReadyError)) n = new StatusError(e, n);
677
742
  const i = t === STATUS_PENDING && n instanceof NotReadyError ? n.source : undefined;
678
743
  const s = i === e;
679
- const l = t === STATUS_PENDING && e.o?.De !== undefined && !s;
680
- const u = l && hasActiveOverride(e);
744
+ // An optimistic node (a WRITTEN override slot) pending derivatively is a
745
+ // boundary: its override is the answer, pending stops here (A17). A
746
+ // derived override (#3479) is a previous speculative answer on a plain
747
+ // member — pending flows through it as through any memo.
748
+ const u = t === STATUS_PENDING && e.o?.Ne !== undefined && !(e.C & CONFIG_DERIVED_OVERRIDE) && !s;
749
+ const l = u && hasActiveOverride(e);
681
750
  if (!r) {
751
+ // Lane before companions: the companion pokes below may create the
752
+ // node's pending-signal lane, whose parent is read from the node's lane
753
+ // at creation. Assigned after them (as it was), a node made pending by
754
+ // propagation before it rode the lane got a parentless companion lane,
755
+ // and the isPending reader that also depends on the node merged it into
756
+ // the held lane — the verdict then waited on the async it reports (#3379).
757
+ if (o) assignOrMergeLane(e, o);
682
758
  if (t === STATUS_PENDING && i) {
683
759
  addPendingSource(e, i);
684
760
  // A fresh flight from a settled state starts with its inputs unpublished
685
761
  // (a replacement flight while still pending keeps the mark: the first
686
762
  // flight's committed inputs are still the frame).
687
- if (!(e.S & STATUS_PENDING)) e.T &= ~CONFIG_INPUTS_PUBLISHED;
763
+ if (!(e.S & STATUS_PENDING)) e.C &= ~CONFIG_INPUTS_PUBLISHED;
688
764
  e.S = STATUS_PENDING | e.S & STATUS_UNINITIALIZED;
689
765
  // Preserve the current source on this propagation so render-effect notification
690
766
  // can register every distinct pending source with the transition.
@@ -694,14 +770,11 @@ function notifyStatus(e, t, n, r, o) {
694
770
  e.S = t | (t !== STATUS_ERROR ? e.S & STATUS_UNINITIALIZED : 0);
695
771
  ext(e)._ = n;
696
772
  }
697
- GlobalQueue.fe?.(e);
698
- if (e.o?.i && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.Ge !== null) GlobalQueue.Ge(e);
773
+ GlobalQueue.le?.(e);
774
+ if (e.o?.i && e.C & CONFIG_CHILD_COMPANIONS && GlobalQueue.be !== null) GlobalQueue.be(e);
699
775
  }
700
- if (o && !r) {
701
- assignOrMergeLane(e, o);
702
- }
703
- const f = r || u;
704
- const a = r || l ? undefined : o;
776
+ const f = r || l;
777
+ const a = r || u ? undefined : o;
705
778
  const c = statusNotifierOf(e);
706
779
  if (c) {
707
780
  if (r && t === STATUS_PENDING) {
@@ -715,19 +788,56 @@ function notifyStatus(e, t, n, r, o) {
715
788
  return;
716
789
  }
717
790
  forEachDependent(e, (e, r) => {
718
- e.ae = clock;
719
- if (t === STATUS_PENDING && i && !e.o?.ee?.has(i) || t !== STATUS_PENDING && (e.o?._ !== n || e.o?.ee)) {
791
+ e.fe = clock;
792
+ // A pending mark on a kept-tail link re-derives the subscriber instead of
793
+ // marking it (A30, #3494 review; fuzzer latest-1 #2141; #3519 review).
794
+ // Past `_depsTail` lie the committed frame's deps, kept by A30 because
795
+ // that frame still derives from them while the pass that dropped them is
796
+ // held (staged, or unchanged and parked). A source going pending there is
797
+ // a question for the node's NEXT pass, not a fact about its current one:
798
+ // marked, the node was registered as the flight's reporter and its holder
799
+ // entangled with the flight (the A15 arm below) on a dep the held frame
800
+ // never reads — an orphaned fetch held the truth (a hide joined to a
801
+ // parked action, A34), and a manual flight nobody awaited held a gated
802
+ // reader hidden forever (fuzzer branches-1 #1105). Skipped, the committed
803
+ // frame published stale beside its new inputs (`query=1` beside a
804
+ // `selected` derived from `remote(0)`). Re-derived, the pass decides: it
805
+ // reads the dep and registers through its own read, or reads a held input
806
+ // and enters that transaction (A29), or reads neither and is done. Clears
807
+ // and errors still ride every link. A link inside the prefix carries the
808
+ // pass's generation (`link()`), so the test is O(1); mid-pass the prefix
809
+ // is what the pass has read so far, and the heap refuses a recomputing
810
+ // node — a dep it has yet to reach registers through its own read.
811
+ if (t === STATUS_PENDING && r.pe !== e.ye) {
812
+ enqueueSub(e);
813
+ schedule();
814
+ return;
815
+ }
816
+ if (t === STATUS_PENDING && i && !e.o?.ut?.has(i) || t !== STATUS_PENDING && (e.o?._ !== n || e.o?.ut)) {
720
817
  // A pending-observer link is the subscription an `isPending` read created.
721
818
  // It exists so the observer re-runs when the source settles, but it must
722
819
  // not carry a real (non-NotReadyError) error — the synchronous `isPending`
723
820
  // read swallows those, and the async path must match. Re-run the observer
724
821
  // so `isPending` re-evaluates (to not-pending) instead of forwarding.
725
- if (r.ye && t !== STATUS_PENDING && !(n instanceof NotReadyError)) {
822
+ if (r.Ce && t !== STATUS_PENDING && !(n instanceof NotReadyError)) {
726
823
  enqueueSub(e);
727
824
  schedule();
728
825
  return;
729
826
  }
730
- if (!f && !e.Ee) queuePendingNode(e);
827
+ // A memo another live transaction HOLDS — pending on its work, or
828
+ // staged by it — made pending by THIS flight cannot reveal before the
829
+ // flight lands: the two settle as one unit (A15, a shared derivation of
830
+ // both — #3443). Propagation marks the held memo without recomputing it
831
+ // (its inputs' values are unchanged), so this is the one moment the
832
+ // entanglement is known; the memo's stamped re-entry at its next pass
833
+ // came too late — the holder's own flight landed first and revealed the
834
+ // inputs beside the stale sum. The stamp alone decides nothing (#3334):
835
+ // a node the transaction once queued but holds nothing of — a switch's
836
+ // shared output whose first flight the second write superseded — must
837
+ // not drag the older flight into the newer reveal. Effects entangle
838
+ // nothing (A15 shared-hole corollary): their reader registers with the
839
+ // flight's transaction at queue notification.
840
+ if (!f) e.Te ? i && !e.Pe && (e.S & STATUS_PENDING || e._e !== NOT_PENDING) && globalQueue.initTransition(e.Te) : queuePendingNode(e);
731
841
  notifyStatus(e, t, n, f, a);
732
842
  }
733
843
  });