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