@solidjs/signals 2.0.0-rc.15 → 2.0.0-rc.16

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 (89) hide show
  1. package/dist/dev-shared.js +785 -367
  2. package/dist/dev.attribution.js +75 -52
  3. package/dist/dev.js +1120 -567
  4. package/dist/observe/affects-marks.js +86 -0
  5. package/dist/observe/affects.js +22 -73
  6. package/dist/observe/boundaries.js +191 -189
  7. package/dist/observe/core/async.js +175 -131
  8. package/dist/observe/core/attribution-queries.js +1 -1
  9. package/dist/observe/core/attribution.js +176 -159
  10. package/dist/observe/core/constants.js +17 -3
  11. package/dist/observe/core/context.js +3 -3
  12. package/dist/observe/core/core.js +503 -363
  13. package/dist/observe/core/dev.js +66 -38
  14. package/dist/observe/core/effect.js +48 -48
  15. package/dist/observe/core/error.js +5 -3
  16. package/dist/observe/core/external.js +5 -6
  17. package/dist/observe/core/graph.js +57 -43
  18. package/dist/observe/core/heap.js +33 -33
  19. package/dist/observe/core/identities.js +62 -0
  20. package/dist/observe/core/lanes.js +226 -259
  21. package/dist/observe/core/owner.js +62 -50
  22. package/dist/observe/core/scheduler.js +319 -171
  23. package/dist/observe/core/truth.js +45 -0
  24. package/dist/observe/core/verdict.js +88 -50
  25. package/dist/observe/deferred.js +184 -0
  26. package/dist/observe/index.js +7 -1
  27. package/dist/observe/map.js +345 -260
  28. package/dist/observe/reveal.js +76 -76
  29. package/dist/observe/signals.js +41 -30
  30. package/dist/observe/store/affects.js +6 -6
  31. package/dist/observe/store/edges.js +39 -0
  32. package/dist/observe/store/optimistic.js +41 -36
  33. package/dist/observe/store/projection.js +68 -55
  34. package/dist/observe/store/store.js +157 -104
  35. package/dist/prod/affects-marks.js +86 -0
  36. package/dist/prod/affects.js +23 -72
  37. package/dist/prod/boundaries.js +155 -153
  38. package/dist/prod/core/async.js +171 -127
  39. package/dist/prod/core/constants.js +17 -3
  40. package/dist/prod/core/context.js +3 -3
  41. package/dist/prod/core/core.js +478 -342
  42. package/dist/prod/core/effect.js +68 -68
  43. package/dist/prod/core/error.js +5 -3
  44. package/dist/prod/core/external.js +5 -6
  45. package/dist/prod/core/graph.js +57 -43
  46. package/dist/prod/core/heap.js +38 -38
  47. package/dist/prod/core/identities.js +62 -0
  48. package/dist/prod/core/lanes.js +217 -250
  49. package/dist/prod/core/owner.js +62 -51
  50. package/dist/prod/core/scheduler.js +310 -162
  51. package/dist/prod/core/truth.js +45 -0
  52. package/dist/prod/core/verdict.js +87 -49
  53. package/dist/prod/deferred.js +182 -0
  54. package/dist/prod/index.js +7 -1
  55. package/dist/prod/map.js +360 -275
  56. package/dist/prod/reveal.js +76 -76
  57. package/dist/prod/signals.js +41 -30
  58. package/dist/prod/store/affects.js +6 -6
  59. package/dist/prod/store/edges.js +39 -0
  60. package/dist/prod/store/optimistic.js +37 -33
  61. package/dist/prod/store/projection.js +25 -19
  62. package/dist/prod/store/store.js +142 -96
  63. package/dist/types/affects-marks.d.ts +14 -0
  64. package/dist/types/boundaries.d.ts +1 -1
  65. package/dist/types/core/async.d.ts +10 -0
  66. package/dist/types/core/constants.d.ts +13 -1
  67. package/dist/types/core/core.d.ts +44 -2
  68. package/dist/types/core/dev.d.ts +43 -13
  69. package/dist/types/core/graph.d.ts +11 -0
  70. package/dist/types/core/identities.d.ts +17 -0
  71. package/dist/types/core/index.d.ts +2 -1
  72. package/dist/types/core/lanes.d.ts +2 -14
  73. package/dist/types/core/owner.d.ts +5 -3
  74. package/dist/types/core/scheduler.d.ts +47 -28
  75. package/dist/types/core/truth.d.ts +16 -0
  76. package/dist/types/core/types.d.ts +21 -0
  77. package/dist/types/core/verdict.d.ts +32 -0
  78. package/dist/types/deferred.d.ts +28 -0
  79. package/dist/types/index.d.ts +6 -0
  80. package/dist/types/map.d.ts +2 -2
  81. package/dist/types/signals.d.ts +28 -7
  82. package/dist/types/store/edges.d.ts +9 -0
  83. package/dist/types/store/index.d.ts +10 -3
  84. package/dist/types/store/optimistic.d.ts +3 -2
  85. package/dist/types/store/projection.d.ts +4 -3
  86. package/dist/types/store/store.d.ts +1 -1
  87. package/dist/types/store/target.d.ts +16 -8
  88. package/dist/types/store/types.d.ts +14 -0
  89. package/package.json +1 -1
@@ -23,18 +23,18 @@
23
23
  * ```
24
24
  */
25
25
  class NotReadyError extends Error {
26
- source;
27
26
  constructor(source) {
28
27
  super();
29
28
  this.source = source;
30
29
  }
31
30
  }
32
31
  class StatusError extends Error {
33
- source;
34
32
  constructor(source, original) {
35
- super(original instanceof Error ? original.message : String(original), {
36
- cause: original
37
- });
33
+ // Best-effort message: formatting must never replace the original (#4112).
34
+ try {
35
+ var message = String(original instanceof Error ? original.message : original);
36
+ } catch {}
37
+ super(message, { cause: original });
38
38
  this.source = source;
39
39
  }
40
40
  }
@@ -191,6 +191,9 @@ const CONFIG_SLOT_NODE = 1 << 12;
191
191
  * leaf twin of REACTIVE_MANUAL_WRITE). Set by the user setter, cleared by
192
192
  * the derive's own write to the key. */
193
193
  const CONFIG_MANUAL_WRITE = 1 << 18;
194
+ /** D-41: the staged value in `_pendingValue` is an error — an
195
+ * answer of the held frame, published at its landing like a value. */
196
+ const CONFIG_STAGED_ERROR = 1 << 21;
194
197
  /** Lanes — the node carries a written guess (`optimisticWrite`): its own
195
198
  * source recomputing it, or a plain write landing on it, is the truth
196
199
  * (`supersede`). A lane's derived staging is not. */
@@ -199,15 +202,21 @@ const CONFIG_GUESS = 1 << 13;
199
202
  * pending is a question for its next pass — the verdict changed — so the
200
203
  * propagation re-derives it instead of marking it pending (as a kept-tail
201
204
  * link does, A30). Set at the window, never cleared: a reader that stopped
202
- * asking re-derives once and goes pending through its own read. */
203
- const CONFIG_VERDICT = 1 << 14;
205
+ * asking re-derives once and goes pending through its own read. The same
206
+ * bit as REACTIVE_PROBED: a pass's probe (or a read of a verdict memo,
207
+ * core.ts `read`) carries into the config at its end without a shift. */
208
+ const CONFIG_VERDICT = 1 << 15;
204
209
  /** A dependency's status is a question for this node's pass, not a fact
205
210
  * about its current one: the propagation re-derives it instead of marking
206
211
  * it, for errors as for pending (as CONFIG_VERDICT). A boundary's output
207
212
  * (show the content, or the fallback) and its `on` node (boundaries.ts); a
208
213
  * promise over an expression (`resolve`/`until`, signals.ts: the pass reads
209
214
  * the error and rejects, or re-asks a pending source). */
210
- const CONFIG_REDERIVE = 1 << 15;
215
+ const CONFIG_REDERIVE = 1 << 14;
216
+ /** An answered `createDeferred` node (deferred.ts): held because a
217
+ * downstream hold staged its landing, its re-pass is a lagging question,
218
+ * not an answer, and joins no transaction (D-40). */
219
+ const CONFIG_LAGS = 1 << 19;
211
220
  /** The reader wants the truth, not the lane's guess (`until`, signals.ts):
212
221
  * a read of displayed optimism serves the base the guess covers, makes the
213
222
  * pass no lane's, and the truth landing wakes it even when it confirms the
@@ -259,9 +268,13 @@ const CONFIG_WIDE = 1 << 26;
259
268
  * creation by dev builds only (the observe artifact mangles the option name,
260
269
  * as it does `_wide`). */
261
270
  const CONFIG_SUPERSEDES = 1 << 27;
271
+ const STATUS_NONE = 0;
262
272
  const STATUS_PENDING = 1 << 0;
263
273
  const STATUS_ERROR = 1 << 1;
264
274
  const STATUS_UNINITIALIZED = 1 << 2;
275
+ /** `catchHook`'s answer when the catcher is a boundary re-armed by
276
+ * `on` (truthy, as any catch): what it collects is its own, not the hold's. */
277
+ const CAUGHT_REARMED = 2;
265
278
  const EFFECT_RENDER = 1;
266
279
  const EFFECT_USER = 2;
267
280
  const EFFECT_TRACKED = 3;
@@ -410,13 +423,21 @@ const diagnostics = {
410
423
  };
411
424
  }
412
425
  };
426
+ // Flight identity → earliest declared start (see `AttributionSlot.markFlight`).
427
+ // Weak: a stamp must not keep a settled promise alive.
428
+ const flightOrigins = new WeakMap();
413
429
  const attributionSlot = {
414
430
  get installed() {
415
431
  return attrHooks;
416
432
  },
417
433
  withInteraction,
418
434
  withOrigin,
419
- currentOrigin
435
+ currentOrigin,
436
+ markFlight(flight, startedAt = performance.now()) {
437
+ // Earliest wins (`undefined <= n` is false: the first mark always lands).
438
+ if (!(flightOrigins.get(flight) <= startedAt)) flightOrigins.set(flight, startedAt);
439
+ },
440
+ flightOrigin: flight => flightOrigins.get(flight)
420
441
  };
421
442
  const RECORDS = Symbol.for("@solidjs/signals/observe/records");
422
443
  function recordsChannel() {
@@ -518,10 +539,11 @@ function isSuppressed(entry) {
518
539
  return suppressedEvents.has(entry);
519
540
  }
520
541
  // The repair guide: the `reactivity-diagnostics` skill `solid-js` ships,
521
- // one section per code, at its stable GitHub path. GitHub heading anchors
522
- // are lowercased with underscores kept (`### SILENT_HOLD` → `#silent_hold`).
523
- const GUIDE_URL =
524
- "https://github.com/solidjs/solid/blob/main/packages/solid/skills/reactivity-diagnostics/SKILL.md";
542
+ // one page per code on the docs site. The slug is the code lowercased with
543
+ // `_` → `-` (`STRICT_READ_UNTRACKED` → `strict-read-untracked`). The host
544
+ // flips to docs.solidjs.com at stable (one search-and-replace, with the JSDoc
545
+ // links).
546
+ const GUIDE_URL = "https://v2.solidjs.com/reference/solid-js/diagnostics/";
525
547
  const DEV = {
526
548
  hooks,
527
549
  getChildren,
@@ -531,7 +553,7 @@ const DEV = {
531
553
  getObservers,
532
554
  report: reportDiagnostic,
533
555
  guideUrl(code) {
534
- return `${GUIDE_URL}#${code.toLowerCase()}`;
556
+ return GUIDE_URL + code.toLowerCase().replace(/_/g, "-");
535
557
  }
536
558
  };
537
559
  /**
@@ -570,15 +592,29 @@ function ownerPath(subject) {
570
592
  }
571
593
  return path.length ? path.reverse() : undefined;
572
594
  }
595
+ /**
596
+ * The innermost source location on the subject's chain: the subject's own
597
+ * `_loc` (a signal or computation created with `{ loc }`), else the first
598
+ * `_loc` up the owners — a signal hops to its registering owner, then
599
+ * `_parent` upward (a component root carries its JSX site). The same walk
600
+ * `ownerPath` makes; `undefined` when nothing on the chain was located.
601
+ */
602
+ function locationOf(subject) {
603
+ // Owners have a `_parent` key (null on a root); a signal has none and hops
604
+ // to its registering `_owner` instead.
605
+ for (let node = subject; node; node = node._parent !== undefined ? node._parent : node._owner)
606
+ if (node._loc) return node._loc;
607
+ return undefined;
608
+ }
573
609
  /**
574
610
  * Records a diagnostic on the structured channel (listeners, captures) and
575
611
  * returns the entry. `subject` locates it: the current reactive `context` by
576
612
  * default (right for the synchronous rule checks — they fire inside the
577
613
  * scope that misbehaved); pass the node for scheduler-time findings whose
578
614
  * ambient context is the flush, or `null` for events that have no location
579
- * by nature. An `ownerPath` already on the event wins over the subject walk
580
- * (hosts whose owners are not signals' owners compute their own). Console
581
- * output is a separate, dev-tier step — see `reportDiagnostic`.
615
+ * by nature. An `ownerPath` (or `loc`) already on the event wins over the
616
+ * subject walk (hosts whose owners are not signals' owners compute their
617
+ * own). Console output is a separate, dev-tier step — see `reportDiagnostic`.
582
618
  */
583
619
  function emitDiagnostic(event, subject = context) {
584
620
  const entry = {
@@ -595,6 +631,10 @@ function emitDiagnostic(event, subject = context) {
595
631
  const path = ownerPath(subject);
596
632
  if (path) entry.ownerPath = path;
597
633
  }
634
+ if (entry.loc === undefined) {
635
+ const loc = locationOf(subject);
636
+ if (loc !== undefined) entry.loc = loc;
637
+ }
598
638
  const live = subject ?? undefined;
599
639
  if (live !== undefined) eventSubjects.set(entry, live);
600
640
  for (const listener of diagnosticListeners) listener(entry, live);
@@ -631,12 +671,31 @@ function takeFooter(entry) {
631
671
  * `reportDiagnostic(emitDiagnostic(…))`. Weak, keyed by the entry.
632
672
  */
633
673
  const eventSubjects = new WeakMap();
674
+ /**
675
+ * The `at` line of a console report: the event's `loc` as the compiler wrote
676
+ * it (`src/App.tsx:18:3`), or — in a browser, where a dev server serves the
677
+ * project root at `location.origin` — `http://localhost:5173/src/App.tsx:18:3`,
678
+ * which Chrome and Firefox link to the Sources panel. A path that leaves the
679
+ * root (`../`) or is absolute is not under the origin and prints as-is.
680
+ */
681
+ function locationLine(loc) {
682
+ if (
683
+ typeof document !== "undefined" &&
684
+ typeof location !== "undefined" &&
685
+ !loc.startsWith("..") &&
686
+ !loc.startsWith("/")
687
+ )
688
+ return `${location.origin}/${loc}`;
689
+ return loc;
690
+ }
634
691
  /**
635
692
  * The console face of a diagnostic — ONE entry per finding: the message, the
636
693
  * owner path (`in <App> › <TodoRow> › effect`) so a human can locate it, the
637
- * once-per-code footer as trailing lines, and — when the subject is a
638
- * binding effect the rendering runtime tagged — the element it writes, as a
639
- * second console argument (hover highlights it, click jumps to Elements).
694
+ * source location (`at src/App.tsx:18:3` — see `locationLine`) when a node on
695
+ * the chain was compiled with one, the once-per-code footer as trailing
696
+ * lines, and — when the subject is a binding effect the rendering runtime
697
+ * tagged — the element it writes, as a second console argument (hover
698
+ * highlights it, click jumps to Elements).
640
699
  * Severity picks the console method. Call sites report the entry
641
700
  * `emitDiagnostic` returned so the structured and console channels never
642
701
  * disagree. Dev-tier: in an observe build this is a no-op, so wiring paths
@@ -647,6 +706,7 @@ function reportDiagnostic(entry) {
647
706
  if (suppressedEvents.has(entry)) return;
648
707
  let text = entry.message;
649
708
  if (entry.ownerPath) text += `\n in ${entry.ownerPath.join(" › ")}`;
709
+ if (entry.loc) text += `\n at ${locationLine(entry.loc)}`;
650
710
  const footer = takeFooter(entry);
651
711
  if (footer) text += `\n${footer}`;
652
712
  const element = eventSubjects.get(entry)?._devElement;
@@ -1052,6 +1112,101 @@ function devCheckQuiescent(isQueuedForCommit) {
1052
1112
  return;
1053
1113
  }
1054
1114
 
1115
+ // Optional-module hooks: module bindings, not `GlobalQueue` statics, so a
1116
+ // bundle that never imports the installing module never assigns them — its
1117
+ // minifier folds every guarded call site (and the branch behind it) away.
1118
+ // Each module installs its own set at evaluation (pay-for-use).
1119
+ //
1120
+ // Lanes (lanes.ts; installed when `createOptimistic` — or verdict.ts — is
1121
+ // imported; every call site is behind a bit or a `passLane` nothing else sets).
1122
+ let laneReadHook = null;
1123
+ let laneStageHook = null;
1124
+ let laneOutcomeHook = null;
1125
+ let laneWriteHook = null;
1126
+ let applyGuessesHook = null;
1127
+ let laneSeamsHook = null;
1128
+ let laneCorrectionsHook = null;
1129
+ let endLanesHook = null;
1130
+ let lanesBlockedHook = null;
1131
+ function installLaneHooks(
1132
+ read,
1133
+ stage,
1134
+ outcome,
1135
+ write,
1136
+ applyGuesses,
1137
+ seams,
1138
+ corrections,
1139
+ end,
1140
+ blocked
1141
+ ) {
1142
+ laneReadHook = read;
1143
+ laneStageHook = stage;
1144
+ laneOutcomeHook = outcome;
1145
+ laneWriteHook = write;
1146
+ applyGuessesHook = applyGuesses;
1147
+ laneSeamsHook = seams;
1148
+ laneCorrectionsHook = corrections;
1149
+ endLanesHook = end;
1150
+ lanesBlockedHook = blocked;
1151
+ }
1152
+ // Boundaries (boundaries.ts): the display consumers between an observer
1153
+ // and the root. `catch` — status from a frame reader, nearest boundary
1154
+ // first (true: caught, the root never hears of it; a clear — flags 0 —
1155
+ // settles the reader there); `fresh` — a pass that read a hold (true: a
1156
+ // first pass a loading boundary that has not shown content caught,
1157
+ // `joinPass`; `held`: the flush's hold owns it, nothing catches it);
1158
+ // `hidden` — a frame reader behind a fallback (the boundary showing it)
1159
+ // is not on screen and holds nothing; `boundarySeam` — the seam's sweep (readers
1160
+ // gone or settled without a pass reveal; an `on` re-arm resolves);
1161
+ // `heldRun` — a queued run under a fallback-showing boundary waits for
1162
+ // the reveal (true: held; the boundary re-queues it by type). The
1163
+ // synchronous first render on creation builds the subtree, attached or
1164
+ // not; its updates and the user effects wait.
1165
+ let catchHook = null;
1166
+ let freshHook = null;
1167
+ let hiddenHook = null;
1168
+ let boundarySeamHook = null;
1169
+ let heldRunHook = null;
1170
+ function installBoundaryHooks(caught, fresh, hidden, seam, heldRun) {
1171
+ catchHook = caught;
1172
+ freshHook = fresh;
1173
+ hiddenHook = hidden;
1174
+ boundarySeamHook = seam;
1175
+ heldRunHook = heldRun;
1176
+ }
1177
+ /** Store (store/store.ts): `park` — the flush parked into `t`: the store
1178
+ * materializes and holds the container node of every staging that had none
1179
+ * (a staging needs the node as its home only when something can read the
1180
+ * frame through it; a hold can); `commit` — fold the pending backings whose
1181
+ * container nodes this flush committed (the owned-raw model's one mutation
1182
+ * point). */
1183
+ let storeParkHook = null;
1184
+ let storeCommitHook = null;
1185
+ function installStoreHooks(park, commit) {
1186
+ storeParkHook = park;
1187
+ storeCommitHook = commit;
1188
+ }
1189
+ /** `affects()` marks (affects.ts): the releases at a landing and at the seam
1190
+ * (ambient marks). */
1191
+ let releaseMarksHook = null;
1192
+ let releaseAmbientMarksHook = null;
1193
+ function installMarkHooks(release, releaseAmbient) {
1194
+ releaseMarksHook = release;
1195
+ releaseAmbientMarksHook = releaseAmbient;
1196
+ }
1197
+ /** Verdicts (verdict.ts). */
1198
+ let observeFlightHook = null;
1199
+ function installVerdictHooks(observeFlight) {
1200
+ observeFlightHook = observeFlight;
1201
+ }
1202
+ /** External-source bridge (wired by enableExternalSource(); undefined while
1203
+ * no config is active — including after _resetExternalSourceConfig()). */
1204
+ let wireExternalSourceHook = undefined;
1205
+ let externalUntrackHook = undefined;
1206
+ function setExternalHooks(wire, untrack) {
1207
+ wireExternalSourceHook = wire;
1208
+ externalUntrackHook = untrack;
1209
+ }
1055
1210
  /** Live transactions — opened, unmerged, not landed. Scanned at every seam
1056
1211
  * while non-empty; nothing on the plain path. */
1057
1212
  const transactions = [];
@@ -1190,10 +1345,7 @@ function blocked(t) {
1190
1345
  judge ??= t;
1191
1346
  // An action still running in it holds it open (action.ts). Its lanes
1192
1347
  // (lanes.ts): a lane under `t` blocks it while blocked itself.
1193
- const r =
1194
- t._open !== 0 ||
1195
- blockedBy(t._nodes, t) ||
1196
- (!!GlobalQueue._lanesBlocked && GlobalQueue._lanesBlocked(t));
1348
+ const r = t._open !== 0 || blockedBy(t._nodes, t) || !!lanesBlockedHook?.(t);
1197
1349
  judge = prev;
1198
1350
  return r;
1199
1351
  }
@@ -1288,10 +1440,7 @@ function blockedBy(nodes, owner, own = false) {
1288
1440
  * everything under it, so a reader there is not on screen and holds
1289
1441
  * nothing — boundaries.ts). */
1290
1442
  function onScreen(r, t) {
1291
- return (
1292
- !(r._flags & REACTIVE_ZOMBIE && removalStagedBy(r, t)) &&
1293
- !(GlobalQueue._hidden && GlobalQueue._hidden(r))
1294
- );
1443
+ return !(r._flags & REACTIVE_ZOMBIE && removalStagedBy(r, t)) && !hiddenHook?.(r);
1295
1444
  }
1296
1445
  /** A15 (#3463): a reader whose removal is staged — a zombie, parked by a
1297
1446
  * pass awaiting its commit — is still on screen and live for every hold,
@@ -1301,13 +1450,8 @@ function onScreen(r, t) {
1301
1450
  * the nearest non-zombie ancestor. */
1302
1451
  function removalStagedBy(r, t) {
1303
1452
  let o = r;
1304
- while (o !== null && o._flags & REACTIVE_ZOMBIE) o = o._parent;
1305
- return (
1306
- o !== null &&
1307
- (o._config & CONFIG_HELD) !== 0 &&
1308
- o._x?._transaction != null &&
1309
- txOf(o) === resolveTx(t)
1310
- );
1453
+ while (o?._flags & REACTIVE_ZOMBIE) o = o._parent;
1454
+ return o?._config & CONFIG_HELD && o._x?._transaction != null && txOf(o) === resolveTx(t);
1311
1455
  }
1312
1456
  /** Unchanged passes this flush whose stale dependency tail awaits the
1313
1457
  * flush's verdict (A30, #3469): trimmed when the flush commits, kept when
@@ -1367,6 +1511,19 @@ function holdFrame(owner, t) {
1367
1511
  holdFrame(c, t);
1368
1512
  }
1369
1513
  }
1514
+ /** The nodes under `owner` a transaction other than `t` holds (boundaries.ts:
1515
+ * what an earlier hold staged under a re-armed boundary). */
1516
+ function heldUnder(owner, t, fn) {
1517
+ for (let c = owner._firstChild; c !== null; c = c._nextSibling) {
1518
+ if ((c._config & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && txOf(c) !== t) fn(c);
1519
+ heldUnder(c, t, fn);
1520
+ }
1521
+ }
1522
+ /** `n` leaves the hold it was staged in: its next pass publishes mainline. */
1523
+ function unhold(n) {
1524
+ n._x._transaction = null;
1525
+ n._config &= ~CONFIG_HELD;
1526
+ }
1370
1527
  const dirtyQueue = {
1371
1528
  _heap: new Array(2000).fill(undefined),
1372
1529
  _marked: false,
@@ -1559,7 +1716,7 @@ class GlobalQueue {
1559
1716
  // Lanes: an ended action body judges its guesses now that their passes
1560
1717
  // have run (lanes.ts); a correction's re-derivations run in one more
1561
1718
  // pure round — the parent's flights before the parent is judged below.
1562
- if (GlobalQueue._laneCorrections?.()) runHeap(dirtyQueue, GlobalQueue._update);
1719
+ if (laneCorrectionsHook?.()) runHeap(dirtyQueue, GlobalQueue._update);
1563
1720
  const joined = flushTransaction;
1564
1721
  flushTransaction = null;
1565
1722
  // The frame's verdict is in: the guesses written since the last seam
@@ -1567,7 +1724,7 @@ class GlobalQueue {
1567
1724
  // parents a nested lane to it), or are dropped — the frame commits, and
1568
1725
  // there is nothing to be optimistic over. Their passes run next round;
1569
1726
  // a parent landing at this very seam ends the lane before they do.
1570
- GlobalQueue._applyGuesses?.(joined);
1727
+ applyGuessesHook?.(joined);
1571
1728
  // A lane's own flush (its flight landing, its reveal) parks nothing: the
1572
1729
  // lane's work reached it per pass; the frame's plain writes commit.
1573
1730
  // (Resolved: a guess over another transaction's guess merged them.)
@@ -1607,10 +1764,12 @@ class GlobalQueue {
1607
1764
  if (
1608
1765
  n._pendingValue !== NOT_PENDING &&
1609
1766
  !(n._statusFlags & (STATUS_PENDING | STATUS_UNINITIALIZED)) &&
1610
- n._equals &&
1611
- n._equals(n._value, n._pendingValue) &&
1612
1767
  n._x?._pendingFirstChild == null &&
1613
- n._x?._pendingDisposal == null
1768
+ n._x?._pendingDisposal == null &&
1769
+ // The comparator last: a pass that created children (an object
1770
+ // owning a fresh async memo, #4111) must not have it read them.
1771
+ // A NotReady read there is a change (`isSame`).
1772
+ isSame(n, n._value, n._pendingValue)
1614
1773
  ) {
1615
1774
  n._pendingValue = NOT_PENDING;
1616
1775
  n._config &= ~CONFIG_STAGED;
@@ -1624,7 +1783,7 @@ class GlobalQueue {
1624
1783
  if (n._config & CONFIG_STAGED) holdFrame(n, t);
1625
1784
  }
1626
1785
  pendingNodes.length = 0;
1627
- GlobalQueue._storePark?.(t);
1786
+ storeParkHook?.(t);
1628
1787
  // (This flush's runs are stashed with `t` below — after the landings,
1629
1788
  // so a `t` that lands at this very seam runs them first, ahead of
1630
1789
  // the runs it held from earlier flushes.)
@@ -1634,13 +1793,17 @@ class GlobalQueue {
1634
1793
  // through like the same read outside): it re-runs now, as the
1635
1794
  // holder's verdict lane's work — before the lane seams, which reveal
1636
1795
  // it (nothing commits this seam that could dispose it).
1637
- const zombies = deferredZombies.splice(0).sort((a, b) => a._height - b._height);
1638
- for (let i = 0; i < zombies.length; i++)
1639
- if (
1640
- (zombies[i]._flags & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE)) === REACTIVE_ZOMBIE &&
1641
- zombies[i]._config & CONFIG_VERDICT
1642
- )
1643
- recompute(zombies[i]);
1796
+ // (Verdict readers exist only with the verdict engine installed.)
1797
+ const zombies = deferredZombies.splice(0);
1798
+ if (observeFlightHook) {
1799
+ zombies.sort((a, b) => a._height - b._height);
1800
+ for (let i = 0; i < zombies.length; i++)
1801
+ if (
1802
+ (zombies[i]._flags & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE)) === REACTIVE_ZOMBIE &&
1803
+ zombies[i]._config & CONFIG_VERDICT
1804
+ )
1805
+ recompute(zombies[i]);
1806
+ }
1644
1807
  // The unchanged passes' tails stay linked (A30, #3469): their inputs
1645
1808
  // are held, and the committed frame still derives from them — no trim.
1646
1809
  } else {
@@ -1671,14 +1834,16 @@ class GlobalQueue {
1671
1834
  // The passes that read this frame's stagings as the screen read held
1672
1835
  // writes if it parked: the lane seam re-derives them on the committed
1673
1836
  // world (a lane's runs wait this round).
1674
- GlobalQueue._laneSeams?.(t !== null ? stagedReaders : null);
1675
- if (stagedReaders.length) stagedReaders.length = 0;
1837
+ if (laneSeamsHook) {
1838
+ laneSeamsHook(t !== null ? stagedReaders : null);
1839
+ if (stagedReaders.length) stagedReaders.length = 0;
1840
+ }
1676
1841
  this._queues = [[], []];
1677
1842
  for (let k = transactions.length - 1; k >= 0; k--) {
1678
1843
  const u = transactions[k];
1679
1844
  if (blocked(u)) continue;
1680
1845
  transactions.splice(k, 1);
1681
- GlobalQueue._endLanes?.(u);
1846
+ endLanesHook?.(u);
1682
1847
  land(u);
1683
1848
  // A landing's commits can dispose a zombie a transaction judged above
1684
1849
  // was blocked on (#3463: live "until the commit that disposes it") —
@@ -1687,13 +1852,13 @@ class GlobalQueue {
1687
1852
  }
1688
1853
  // The store folds the pending backings whose container nodes committed
1689
1854
  // — this flush's, or a landing's (store/store.ts installs it).
1690
- GlobalQueue._storeCommit?.();
1855
+ storeCommitHook?.();
1691
1856
  // Marks declared outside a transaction: the flush that carried them
1692
1857
  // parked (something async below) — the window is the frame's, the marks
1693
1858
  // are its transaction's; else they release at the seam, verdict-only,
1694
1859
  // nothing to show (affects.ts).
1695
1860
  const live = t && liveTx(t);
1696
- GlobalQueue._releaseAmbientMarks?.(live);
1861
+ releaseAmbientMarksHook?.(live);
1697
1862
  // The effect phase: lane work first (displayed ahead of the frame), then
1698
1863
  // this flush's runs, then the runs the landings held from earlier
1699
1864
  // flushes — as one pass would have queued them (#3540: a shell
@@ -1731,7 +1896,7 @@ class GlobalQueue {
1731
1896
  }
1732
1897
  // Boundaries: readers that settled, landed or died this flush are
1733
1898
  // dropped; a fallback with none left reveals next round.
1734
- GlobalQueue._boundarySeam?.();
1899
+ boundarySeamHook?.();
1735
1900
  }
1736
1901
  run(type) {
1737
1902
  const effects = this._queues[type - 1];
@@ -1763,7 +1928,7 @@ class GlobalQueue {
1763
1928
  * first: a fallback catches what is not ready under it, and the root
1764
1929
  * never hears of it. */
1765
1930
  notify(node, mask, flags, error) {
1766
- if (GlobalQueue._catch && GlobalQueue._catch(node, flags & mask, error)) return true;
1931
+ if (catchHook?.(node, flags & mask, error)) return true;
1767
1932
  if (mask & STATUS_PENDING) {
1768
1933
  if (flags & STATUS_PENDING) {
1769
1934
  if (_enforceLoadingBoundary) _hitUnhandledAsync = true;
@@ -1822,7 +1987,7 @@ function land(u) {
1822
1987
  // (`_nodes` still lists them).
1823
1988
  if (attrHooks !== null) attrHooks.transitionSettled(u);
1824
1989
  reruns(u);
1825
- if (u._marks !== null) GlobalQueue._releaseMarks(u._marks);
1990
+ if (u._marks !== null) releaseMarksHook(u._marks);
1826
1991
  // Old children die in the commits (cleanups first), then the stashed
1827
1992
  // effects run ahead of this flush's own. A node a lane took over since
1828
1993
  // (a guess written over the staged truth) is the lane's to land.
@@ -1832,6 +1997,10 @@ function land(u) {
1832
1997
  n._x._transaction = null;
1833
1998
  commitPendingNode(n);
1834
1999
  }
2000
+ // A cleanup those commits ran that wrote a node `u` still held joined
2001
+ // `u`, which has left `transactions`: the write is this landing's, and
2002
+ // the next flush must not park in it (#4089).
2003
+ if (flushTransaction === u) flushTransaction = null;
1835
2004
  releaseQueues(u);
1836
2005
  }
1837
2006
  /** The held runs join this flush's effect phase after its own runs (the
@@ -1951,8 +2120,20 @@ function commitPendingNode(n) {
1951
2120
  // commit beneath a flight publishes its inputs (A15 reveal corollary,
1952
2121
  // #3305): a reveal of it must observe, not show the pre-flight value.
1953
2122
  const inFlight = n._pendingValue === NOT_PENDING && (c._statusFlags & STATUS_PENDING) !== 0;
1954
- n._config = inFlight ? (n._config & -1048833) | CONFIG_INPUTS_PUBLISHED : n._config & -1049985;
1955
- if (n._pendingValue !== NOT_PENDING) {
2123
+ const err = n._config & CONFIG_STAGED_ERROR;
2124
+ // An answer commits: the error it replaces leaves the display (D-41).
2125
+ if (!inFlight && c._x) c._x._shownError = undefined;
2126
+ n._config = inFlight ? (n._config & -1048833) | CONFIG_INPUTS_PUBLISHED : n._config & -3147137;
2127
+ // D-41: the staged answer is an error — published now, when a value would
2128
+ // show, through the ordinary error path (`stageError`). Its readers first
2129
+ // see it in the cycle after this seam: a tracked re-read there is not a
2130
+ // retry (`read`), or a boundary re-derived by it would retry it forever.
2131
+ if (err) {
2132
+ const e = n._pendingValue;
2133
+ n._pendingValue = NOT_PENDING;
2134
+ n._time = clock + 1;
2135
+ notifyStatus(c, STATUS_ERROR, e);
2136
+ } else if (n._pendingValue !== NOT_PENDING) {
1956
2137
  n._value = n._pendingValue;
1957
2138
  n._pendingValue = NOT_PENDING;
1958
2139
  // A node born staged (recompute) initializes at this commit.
@@ -2443,13 +2624,15 @@ function getOwner() {
2443
2624
  /**
2444
2625
  * Low-level: registers `fn` as a disposal callback on the current owner.
2445
2626
  * Most code should use `onCleanup()` from `solid-js`, which adds dev-mode
2446
- * checks. `cleanup()` is the unchecked primitive used by internals.
2627
+ * checks. `cleanup()` is the unchecked primitive used by internals; `owner`
2628
+ * (internal) targets a specific owner's disposal list instead of the ambient
2629
+ * one.
2447
2630
  */
2448
- function cleanup(fn) {
2449
- if (!context) return fn;
2450
- if (!context._disposal) context._disposal = fn;
2451
- else if (Array.isArray(context._disposal)) context._disposal.push(fn);
2452
- else context._disposal = [context._disposal, fn];
2631
+ function cleanup(fn, owner = context) {
2632
+ if (!owner) return fn;
2633
+ if (!owner._disposal) owner._disposal = fn;
2634
+ else if (Array.isArray(owner._disposal)) owner._disposal.push(fn);
2635
+ else owner._disposal = [owner._disposal, fn];
2453
2636
  return fn;
2454
2637
  }
2455
2638
  /**
@@ -2486,12 +2669,18 @@ function createOwner(options) {
2486
2669
  const parent = context;
2487
2670
  const transparent = options?.transparent ?? false;
2488
2671
  // Prod and observe boilerplates (see core.ts computed()). The observe
2489
- // literal carries the `_name` slot the rendering layer fills with the
2490
- // component label (`owner._name = "<App>"`) — a slot, so labelling a root
2491
- // is a plain store rather than a shape fork between labelled and plain roots.
2672
+ // literal carries the `_name` and `_loc` slots the rendering layer fills
2673
+ // with the component label and JSX site (`owner._name = "<App>"`,
2674
+ // `owner._loc = "src/App.tsx:3:5"`) — slots, so labelling a root is a
2675
+ // plain store rather than a shape fork between labelled and plain roots.
2492
2676
  const owner = {
2493
2677
  id: inheritId(options, transparent, parent),
2494
2678
  _config: transparent ? CONFIG_TRANSPARENT : 0,
2679
+ // Present from birth: disposeChildren marks death here on roots too, and
2680
+ // a first store of a missing property is a shape transition plus a
2681
+ // properties-store allocation per root — on a 100×4 table teardown that
2682
+ // was 12 KB and most of disposal's time (2026-10-17).
2683
+ _flags: REACTIVE_NONE,
2495
2684
  _root: true,
2496
2685
  _parentComputed: parent?._root ? parent._parentComputed : parent,
2497
2686
  _firstChild: null,
@@ -2503,7 +2692,8 @@ function createOwner(options) {
2503
2692
  _x: null,
2504
2693
  _parent: parent,
2505
2694
  dispose: disposeRootSelf,
2506
- _name: undefined
2695
+ _name: undefined,
2696
+ _loc: undefined
2507
2697
  };
2508
2698
  if (parent && parent._config & CONFIG_CHILDREN_FORBIDDEN) {
2509
2699
  emitDiagnostic({
@@ -2555,11 +2745,14 @@ function createOwner(options) {
2555
2745
  * const detached = runWithOwner(null, () => createRoot(d => d));
2556
2746
  * ```
2557
2747
  *
2558
- * @description https://docs.solidjs.com/reference/reactive-utilities/create-root
2748
+ * @description https://v2.solidjs.com/reference/solid-js/advanced/owner-introspection/create-root
2559
2749
  */
2560
2750
  function createRoot(init, options) {
2561
2751
  const owner = createOwner(options);
2562
- return runWithOwner(owner, () => init(() => owner.dispose()));
2752
+ // A bound function, not an arrow: an arrow would share this scope, which
2753
+ // holds `init`, so keeping `dispose` would keep `init` and all it closes
2754
+ // over alive (#4095).
2755
+ return runWithOwner(owner, () => init(disposeRootSelf.bind(owner, true)));
2563
2756
  }
2564
2757
 
2565
2758
  /** The store's shared slot-node release handler (store/store.ts installs it
@@ -2592,12 +2785,14 @@ function unlinkSubs(link) {
2592
2785
  // read. The settle path runs this same last-one-out check when that
2593
2786
  // observer releases (the untracked-read dormancy sweep guards on
2594
2787
  // pending identically).
2788
+ // During a pass's teardown of its unfinished frame, the release waits
2789
+ // for the pass to end (`deferredReleases`, #4142).
2595
2790
  const c = dep;
2596
2791
  c._fn &&
2597
2792
  c._config & CONFIG_AUTO_DISPOSE &&
2598
2793
  !(c._flags & REACTIVE_ZOMBIE) &&
2599
2794
  !(c._statusFlags & STATUS_PENDING) &&
2600
- unobserved(c);
2795
+ (deferredReleases ? deferredReleases.push(c) : unobserved(c));
2601
2796
  }
2602
2797
  }
2603
2798
  return nextDep;
@@ -2626,6 +2821,17 @@ function clearDeps(el) {
2626
2821
  el._deps = null;
2627
2822
  el._depsTail = null;
2628
2823
  }
2824
+ /**
2825
+ * Last-one-out releases held back while a pass tears down the frame of an
2826
+ * earlier pass that never committed (#4142). The pass is about to rebuild
2827
+ * the same children, which read the same lazy nodes again. Released on the
2828
+ * spot, a lazy async memo whose only reader was the old frame's child memo
2829
+ * was torn down, re-ran on the new child's read, went pending, and the cycle
2830
+ * repeated forever. `recompute` collects them around that teardown and
2831
+ * releases what is still unobserved when the pass ends.
2832
+ */
2833
+ let deferredReleases = null;
2834
+ const deferReleases = list => (deferredReleases = list);
2629
2835
  function unobserved(el) {
2630
2836
  deleteFromHeap(el, dirtyQueue);
2631
2837
  clearDeps(el);
@@ -2827,6 +3033,43 @@ function releaseSettledDependents(el) {
2827
3033
  // (releaseSettledDependents shape): identity holders can sit below an
2828
3034
  // intermediate whose own error state has since been scrubbed or replaced
2829
3035
  // (e.g. an error boundary's tree node).
3036
+ /** D-41: an error a pass of a held node found is an answer of
3037
+ * that frame — it ends the wait like a value and shows when a value would.
3038
+ * Staged like one: `_pendingValue` carries it (CONFIG_STAGED_ERROR), readers
3039
+ * in the hold derive from it (`serve` throws it), and the commit publishes
3040
+ * it through the ordinary error path (`commitPendingNode` → `notifyStatus`),
3041
+ * dependents included. Not for a lane pass, nor a node with no shown value
3042
+ * (born held — its error has no frame to wait behind): those publish now,
3043
+ * as before. Returns whether the error was staged. */
3044
+ function stageError(el, error, inPass) {
3045
+ // Held, or — for a pass's own throw — derived from a staging of a flush
3046
+ // (REACTIVE_STAGED_READ is this pass's; a landing after the pass cannot
3047
+ // read it, the flush it read a staging of has committed or parked since).
3048
+ // Staged whether or not the flush has joined a hold yet: a pass that runs
3049
+ // before its async sibling joins is the seam's to decide, like a value
3050
+ // (#4109). A flush that parks nothing publishes it at its commit (S-38).
3051
+ if (
3052
+ !(
3053
+ el._config & CONFIG_HELD ||
3054
+ (inPass && el._flags & REACTIVE_STAGED_READ && globalQueue._running)
3055
+ ) ||
3056
+ passLane
3057
+ )
3058
+ return false;
3059
+ // The node answered (an error ends the wait — no longer pending, nor
3060
+ // blocked); a shown error stays its display until the commit.
3061
+ clearPendingSources(el);
3062
+ if (el._x) el._x._blocked = false;
3063
+ if (!((el._statusFlags &= STATUS_ERROR | STATUS_UNINITIALIZED) & STATUS_ERROR))
3064
+ setPendingError(el);
3065
+ el._pendingValue = error instanceof StatusError ? error : new StatusError(el, error);
3066
+ el._config |= CONFIG_STAGED | CONFIG_STAGED_ERROR;
3067
+ // Readers parked on the flight re-run and derive from the staging; the
3068
+ // verdict readers the walk wakes need a flush (#4106).
3069
+ settlePendingSource(el);
3070
+ schedule();
3071
+ return true;
3072
+ }
2830
3073
  function settleErroredDependents(el, error) {
2831
3074
  let scheduled = false;
2832
3075
  const visited = new Set();
@@ -3073,7 +3316,11 @@ function handleAsync(
3073
3316
  el._time = clock;
3074
3317
  return;
3075
3318
  }
3076
- notifyStatus(el, stillPending ? STATUS_PENDING : STATUS_ERROR, error);
3319
+ // D-41: a held node's error is staged (`stageError`). A derived store's
3320
+ // readers learn its error through the family, not the derive's
3321
+ // dependents: it publishes now.
3322
+ if (stillPending || setter || !stageError(el, error))
3323
+ notifyStatus(el, stillPending ? STATUS_PENDING : STATUS_ERROR, error);
3077
3324
  // A NotReady rejection is a landing into another pending source. The
3078
3325
  // rejected flight will never settle its self entry, so transfer ownership
3079
3326
  // after notifyStatus has propagated the replacement source.
@@ -3091,7 +3338,7 @@ function handleAsync(
3091
3338
  // skip this stale async result — the upcoming flush will recompute the node
3092
3339
  // with the new value, creating a fresh Promise that supersedes this one.
3093
3340
  if (el._flags & REACTIVE_DIRTY) return;
3094
- const wasUninitialized = !!(el._statusFlags & STATUS_UNINITIALIZED);
3341
+ const wasUninitialized = el._statusFlags & STATUS_UNINITIALIZED;
3095
3342
  landStatus(el);
3096
3343
  // Attribution hook: lets the engine snapshot state before the landing
3097
3344
  // branches, so it can tell whether the plain path's setSignal committed a
@@ -3104,6 +3351,9 @@ function handleAsync(
3104
3351
  handleError(error);
3105
3352
  return;
3106
3353
  }
3354
+ // A store derive's landing commits through its setter: unheld, the
3355
+ // shown error leaves now; held, at the derive's commit.
3356
+ if (!(el._config & CONFIG_HELD)) el._x._shownError = undefined;
3107
3357
  if (wasUninitialized) landStatus(el, true);
3108
3358
  // Attribution hook: this path landed through the setter (derived
3109
3359
  // stores, projections), which does not go through setSignal, so the
@@ -3119,14 +3369,9 @@ function handleAsync(
3119
3369
  // CARVE 2: the override-covered landing (hold + A18 supersession) and the
3120
3370
  // lane-routed landing (derived override, lane effect queue) went with
3121
3371
  // the optimistic engine; every landing is the plain setSignal. A node
3122
- // already held stages its answer into that hold (`setSignal`).
3123
- try {
3124
- setSignal(el, () => value);
3125
- } catch (e) {
3126
- // Same containment as above: setSignal's comparator throw is the only
3127
- // pre-commit failure here, and there is no user callsite to throw to.
3128
- notifyStatus(el, STATUS_ERROR, e);
3129
- }
3372
+ // already held stages its answer into that hold (`setSignal`). Its
3373
+ // comparator throw is contained there (`isSame`, A3).
3374
+ setSignal(el, () => value);
3130
3375
  // Attribution hook: this path landed through setSignal, whose write
3131
3376
  // hook already saw any committed change — direct=false lets the engine
3132
3377
  // reclassify that write as an async landing iff it actually committed.
@@ -3171,15 +3416,14 @@ function handleAsync(
3171
3416
  // close registration uses the ambient owner; and DEFERRED (the flattening
3172
3417
  // path — a thenable resolved to an iterable in a later microtask), where
3173
3418
  // there is no caller to serve and no ambient owner: sync-settled steps
3174
- // write through asyncWrite, and close registration goes through the slot
3175
- // the thenable branch pre-registered while it still owned the context.
3176
- // Returns whether a sync answer landed (first yield or empty completion) —
3177
- // meaningful only in the live posture.
3178
- const consumeIterator = (source, registerClose) => {
3419
+ // write through asyncWrite, and close registration targets `owner` (el
3420
+ // itself) directly. Returns whether a sync answer landed (first yield or
3421
+ // empty completion) — meaningful only in the live posture.
3422
+ const consumeIterator = (source, owner) => {
3179
3423
  const it = source[Symbol.asyncIterator]();
3180
3424
  let hadValue = false;
3181
3425
  let completed = false;
3182
- let initialRead = !registerClose;
3426
+ let initialRead = !owner;
3183
3427
  const close = () => {
3184
3428
  if (completed) return;
3185
3429
  completed = true;
@@ -3188,7 +3432,7 @@ function handleAsync(
3188
3432
  if (isThenable(returned)) returned.then(undefined, () => {});
3189
3433
  } catch {}
3190
3434
  };
3191
- registerClose ? registerClose(close) : cleanup(close);
3435
+ cleanup(close, owner);
3192
3436
  // Flight-identity cancellation (#3122): the registration above is the
3193
3437
  // owner-death backstop; the teardown slot fires at the _inFlight release
3194
3438
  // sites so supersede stops this stream immediately.
@@ -3196,8 +3440,15 @@ function handleAsync(
3196
3440
  // Release check before each next pull: an unobserved lazy node must tear
3197
3441
  // down (its close above runs via disposal, closing the iterator) instead
3198
3442
  // of pumping the stream forever with zero subscribers (#2935).
3443
+ // Later pulls run from an async continuation with no compute catch above
3444
+ // them: a throw invoking the protocol (next(), the step's then) settles
3445
+ // as this flight's error instead of escaping as a rejection (#4112).
3199
3446
  const iterateOrRelease = () => {
3200
- if (!settleAutodispose()) iterate();
3447
+ try {
3448
+ if (!settleAutodispose()) iterate();
3449
+ } catch (e) {
3450
+ handleError(e);
3451
+ }
3201
3452
  };
3202
3453
  const iterate = () => {
3203
3454
  let syncResult,
@@ -3282,7 +3533,7 @@ function handleAsync(
3282
3533
  // stream itself rather than settling on the iterable object. One level
3283
3534
  // only: A+ `then` already collapses nested thenables, so the resolved
3284
3535
  // value is never itself a thenable.
3285
- const flattenIfIterable = (value, registerClose) => {
3536
+ const flattenIfIterable = (value, owner) => {
3286
3537
  let innerIterator = false;
3287
3538
  if (typeof value === "object" && value !== null) {
3288
3539
  untrack(() => {
@@ -3290,8 +3541,9 @@ function handleAsync(
3290
3541
  });
3291
3542
  }
3292
3543
  if (!innerIterator) return false;
3293
- const landed = consumeIterator(value, registerClose);
3294
- if (!registerClose) liveLanded = landed;
3544
+ syncValue = undefined;
3545
+ const landed = consumeIterator(value, owner);
3546
+ if (!owner) liveLanded = landed;
3295
3547
  return true;
3296
3548
  };
3297
3549
  if (thenable) {
@@ -3299,29 +3551,32 @@ function handleAsync(
3299
3551
  rejected = false,
3300
3552
  syncError,
3301
3553
  isSync = true;
3302
- // Close registration for the flattening path. Consumption starts in a
3303
- // microtask where the ambient owner is gone (or worse, someone else's),
3304
- // so `cleanup()` can't be used — the close targets el's disposal list
3305
- // directly, exactly where a live cleanup() during this recompute would
3306
- // have put it. Only a flight that actually flattens becomes
3307
- // disposal-bearing.
3308
- const registerDeferredClose = fn => {
3309
- if (!el._disposal) el._disposal = fn;
3310
- else if (Array.isArray(el._disposal)) el._disposal.push(fn);
3311
- else el._disposal = [el._disposal, fn];
3312
- };
3313
3554
  watchAsyncTail(el, result).then(
3314
3555
  v => {
3315
3556
  if (isSync) {
3316
3557
  syncValue = v;
3317
3558
  resolved = true;
3318
- } else if (
3319
- el._x?._inFlight === result &&
3320
- !(el._flags & REACTIVE_DISPOSED) &&
3321
- flattenIfIterable(v, registerDeferredClose)
3322
- );
3323
- else {
3324
- asyncWrite(v);
3559
+ } else {
3560
+ // Flattening consumes the stream as the value: each yield lands
3561
+ // through asyncWrite under this flight's identity; the first one
3562
+ // clears pending exactly like a plain promise resolution would.
3563
+ // Disposed nodes never start a pump (their disposal list already
3564
+ // ran, so nothing could close the iterator). Consumption starts in
3565
+ // a microtask where the ambient owner is gone, so the close
3566
+ // registers on el's own disposal list. Iterator setup here has no
3567
+ // compute catch above it: a protocol throw settles as this
3568
+ // flight's error (#4112).
3569
+ try {
3570
+ if (
3571
+ el._x?._inFlight === result &&
3572
+ !(el._flags & REACTIVE_DISPOSED) &&
3573
+ flattenIfIterable(v, el)
3574
+ )
3575
+ return;
3576
+ asyncWrite(v);
3577
+ } catch (e) {
3578
+ handleError(e);
3579
+ }
3325
3580
  settleAutodispose();
3326
3581
  }
3327
3582
  },
@@ -3425,6 +3680,8 @@ function notifyStatus(el, status, error, blockStatus) {
3425
3680
  // (CARVE 2: the lane assignment went with the engine.)
3426
3681
  {
3427
3682
  if (status === STATUS_PENDING && pendingSource) {
3683
+ // D-41: a re-ask of a shown error keeps it the display (`_shownError`).
3684
+ if (el._statusFlags & STATUS_ERROR) el._x._shownError = el._x._error;
3428
3685
  addPendingSource(el, pendingSource);
3429
3686
  el._statusFlags = STATUS_PENDING | (el._statusFlags & STATUS_UNINITIALIZED);
3430
3687
  // Preserve the current source on this propagation so readers park on
@@ -3504,7 +3761,8 @@ function propagateStatus(el, status, error, downstreamBlockStatus) {
3504
3761
  if (
3505
3762
  passLane === null &&
3506
3763
  (sub._config & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD &&
3507
- globalQueue._running
3764
+ globalQueue._running &&
3765
+ !freshHook?.(sub, pendingSource._time === clock)
3508
3766
  )
3509
3767
  joinFuture(txOf(sub));
3510
3768
  return;
@@ -3519,7 +3777,7 @@ function propagateStatus(el, status, error, downstreamBlockStatus) {
3519
3777
  // the dependent's pending is the lane's own flight (`laneStage`).
3520
3778
  {
3521
3779
  // (No answer: a pending propagation leaves a written guess a guess.)
3522
- if (passLane !== null) GlobalQueue._laneStage(sub, passLane, true, el);
3780
+ if (laneStageHook && passLane !== null) laneStageHook(sub, passLane, true, el);
3523
3781
  else {
3524
3782
  queuePendingNode(sub);
3525
3783
  // A15 (#3443): pending propagates onto a held memo without
@@ -3561,8 +3819,29 @@ GlobalQueue._update = el => {
3561
3819
  // (REACTIVE_LANE_DIRTY — a lane shows ahead of any park): it runs now, as
3562
3820
  // the lane's work, for the reveal this seam (#3463).
3563
3821
  else if (el._flags & REACTIVE_ZOMBIE && !(el._flags & REACTIVE_LANE_DIRTY)) deferZombie(el);
3564
- else recompute(el);
3822
+ // A lane frame's member (parked by a lane pass, #3698) follows mainline
3823
+ // writes only: a lane's dirty on it is cancelled — the lane's apply retires
3824
+ // it, and the pass that replaced it already decided what that screen shows
3825
+ // (#4137: a `<Show>` branch withdrawn by its own lane re-ran on a falsy
3826
+ // condition).
3827
+ // (Lanes exist only with the lane engine installed: folded away without.)
3828
+ else if (laneStageHook && el._flags & REACTIVE_ZOMBIE && laneFrameMember(el)) {
3829
+ deleteFromHeap(el, dirtyQueue);
3830
+ el._flags &= -65540;
3831
+ } else recompute(el);
3565
3832
  };
3833
+ /** The zombie's frame was parked by lane work: the nearest non-zombie owner
3834
+ * (the one whose pass parked it) carries a lane's derived value — or, an
3835
+ * effect (no lane value: `laneStage`), is listed in a lane (an `insert()`
3836
+ * hole's render effect). */
3837
+ function laneFrameMember(el) {
3838
+ let o = el._parent;
3839
+ while (o._flags & REACTIVE_ZOMBIE) o = o._parent;
3840
+ return (
3841
+ (o._config & (CONFIG_OVERRIDE | CONFIG_GUESS)) === CONFIG_OVERRIDE ||
3842
+ (o._type && o._x?._transaction?._lane)
3843
+ );
3844
+ }
3566
3845
  GlobalQueue._dispose = disposeChildren;
3567
3846
  const PRIMITIVE_IN_FORBIDDEN_SCOPE_MESSAGE =
3568
3847
  "[PRIMITIVE_IN_FORBIDDEN_SCOPE] Cannot create reactive primitives inside createTrackedEffect or owner-backed onSettled";
@@ -3597,6 +3876,10 @@ const REACTIVE_WRITE_IN_OWNED_SCOPE_REFRESH_MESSAGE =
3597
3876
  let tracking = false;
3598
3877
  let context = null;
3599
3878
  let snapshotCaptureActive = false;
3879
+ /** The lane at the root of the running chain of first passes: the lane whose
3880
+ * pass created the chain's first node (a mount), inherited by every first
3881
+ * pass nested under it, whose seats are mainline (`recompute`, #4166). */
3882
+ let laneRoot;
3600
3883
  let snapshotSources = null;
3601
3884
  function ownerInSnapshotScope(owner) {
3602
3885
  while (owner) {
@@ -3656,6 +3939,7 @@ function recompute(el, create = false) {
3656
3939
  // §12d: any recompute can clean a marked subscriber — invalidate skips.
3657
3940
  bumpNotifyEpoch();
3658
3941
  const isEffect = el._type;
3942
+ let released;
3659
3943
  // Lanes (plan sec. 28): the seat of a pass is its node's. A node carrying a lane's
3660
3944
  // derived value runs as the lane's whoever dirtied it — a sync write, a
3661
3945
  // boundary reset, a frame rerun — so its children and its result are the
@@ -3671,18 +3955,27 @@ function recompute(el, create = false) {
3671
3955
  // derivation's pass into the lane (`read`); a leaf's never moves.
3672
3956
  // Restored at the end, after this pass's staging and runs have been
3673
3957
  // routed.
3958
+ // Lanes exist only with the lane engine installed (`laneStageHook`): a
3959
+ // bundle without it seats every pass on mainline, and folds the seat away.
3674
3960
  const prevLane = passLane;
3961
+ // The mount a first pass belongs to: its creator's lane, or the one its
3962
+ // creator's own mount came from — a render effect a verdict lane's mount's
3963
+ // content creates is that mount's too (#4166). Lanes only, like the seat.
3964
+ const prevMount = laneRoot;
3965
+ const mount = laneStageHook && (laneRoot = create && (prevLane || prevMount));
3675
3966
  setPassLane(
3676
- (el._flags & REACTIVE_LANE_DIRTY ||
3677
- (el._config & (CONFIG_OVERRIDE | CONFIG_GUESS)) === CONFIG_OVERRIDE) &&
3678
- el._x?._transaction?._lane
3679
- ? el._x._transaction
3680
- : // Loose `!=`: false with no creator lane (undefined == null).
3681
- create &&
3682
- prevLane?._parent._verdict != prevLane &&
3683
- (creatorPass(context)?._flags ?? 0) & REACTIVE_RECOMPUTING_DEPS
3684
- ? prevLane
3685
- : null
3967
+ !laneStageHook
3968
+ ? null
3969
+ : (el._flags & REACTIVE_LANE_DIRTY ||
3970
+ (el._config & (CONFIG_OVERRIDE | CONFIG_GUESS)) === CONFIG_OVERRIDE) &&
3971
+ el._x?._transaction?._lane
3972
+ ? el._x._transaction
3973
+ : // Loose `!=`: false with no creator lane (undefined == null).
3974
+ create &&
3975
+ prevLane?._parent._verdict != prevLane &&
3976
+ creatorPass(context)?._flags & REACTIVE_RECOMPUTING_DEPS
3977
+ ? prevLane
3978
+ : null
3686
3979
  );
3687
3980
  // Attribution hook: fired before this run touches the dep list — `_deps`
3688
3981
  // still holds the previous run's links (the subscriptions that could have
@@ -3724,8 +4017,11 @@ function recompute(el, create = false) {
3724
4017
  // Held by a blocked lane: the frame joins nothing — a lane never holds
3725
4018
  // a sync write (#3460). The pass is the lane's if it reads the lane's
3726
4019
  // world (`read`), and has left it otherwise.
3727
- if (tx._lane);
3728
- else if (isEffect !== EFFECT_RENDER && !(el._config & CONFIG_VERDICT)) joinPassTx(tx);
4020
+ // An answered deferred node's re-pass is a lagging question (D-40):
4021
+ // it joins nothing, so the write that reached it commits mainline.
4022
+ if (tx._lane || el._config & CONFIG_LAGS);
4023
+ else if (isEffect !== EFFECT_RENDER && !(el._config & CONFIG_VERDICT))
4024
+ joinPassTx(tx, freshHook?.(el, el._statusFlags & STATUS_UNINITIALIZED));
3729
4025
  else if (
3730
4026
  tx !== flushTransaction &&
3731
4027
  !(el._statusFlags & STATUS_UNINITIALIZED || el._config & CONFIG_HELD_FRAME)
@@ -3745,8 +4041,20 @@ function recompute(el, create = false) {
3745
4041
  // frame parked before it stays parked. Otherwise they ARE the frame:
3746
4042
  // park them until this pass commits. Tracked effects run after the seam
3747
4043
  // and own nothing to park.
3748
- if (isEffect === EFFECT_TRACKED || el._config & CONFIG_STAGED) disposeChildren(el);
3749
- else if (el._firstChild !== null || el._disposal !== null) parkChildren(el);
4044
+ if (isEffect === EFFECT_TRACKED || el._config & CONFIG_STAGED) {
4045
+ // A lazy node only those children read is released when this pass
4046
+ // ends, if the pass doesn't read it again (#4142).
4047
+ const prev = deferredReleases;
4048
+ deferReleases((released = []));
4049
+ disposeChildren(el);
4050
+ deferReleases(prev);
4051
+ } else if (el._firstChild !== null || el._disposal !== null) parkChildren(el);
4052
+ // An error a pass staged (D-41) is replaced by this pass's answer: a
4053
+ // value, a flight, or an error of its own (#4109, #4145).
4054
+ if (el._config & CONFIG_STAGED_ERROR) {
4055
+ el._pendingValue = NOT_PENDING;
4056
+ el._config ^= CONFIG_STAGED_ERROR;
4057
+ }
3750
4058
  clearSignals(el);
3751
4059
  }
3752
4060
  const wasUninitialized = !!(el._statusFlags & STATUS_UNINITIALIZED);
@@ -3763,6 +4071,8 @@ function recompute(el, create = false) {
3763
4071
  // settlePendingSource walk never runs for a landing that was preempted
3764
4072
  // (#3181).
3765
4073
  const wasPendingSource = el._x?._pendingSources?.has(el);
4074
+ // D-41: this pass's error is staged, not published (`stageError`).
4075
+ let stagedErr = false;
3766
4076
  // A19 exc. 2: a `refresh()` re-asks the question already answered — quiet
3767
4077
  // for the verdict — unless the question in flight is a new one, which the
3768
4078
  // re-ask does not launder (a poll during a quiet confirm stays quiet).
@@ -3777,7 +4087,7 @@ function recompute(el, create = false) {
3777
4087
  // frame, and the splice/reawaken paths key off it. A zombie reruns for
3778
4088
  // live writes until the commit that disposes it, so every per-pass wipe
3779
4089
  // — here, the finally below, updateIfNecessary — carries it (#3543).
3780
- el._flags = REACTIVE_RECOMPUTING_DEPS | (el._flags & REACTIVE_ZOMBIE);
4090
+ el._flags = REACTIVE_RECOMPUTING_DEPS | (el._flags & (REACTIVE_ZOMBIE | REACTIVE_REASK));
3781
4091
  el._time = clock;
3782
4092
  // The pass's previous value: its staging, else a lane node's lane value
3783
4093
  // when it has one (lanes.ts), else the committed one.
@@ -3789,7 +4099,8 @@ function recompute(el, create = false) {
3789
4099
  : el._value;
3790
4100
  let oldHeight = el._height;
3791
4101
  let missedWake = false;
3792
- // L2: did this pass read the future (REACTIVE_JOINED, set by read)?
4102
+ // L2: did this pass read the future (REACTIVE_JOINED, set by read) — or,
4103
+ // a lane's mount, a staging of this flush (#3851, #4166)?
3793
4104
  let joined = false;
3794
4105
  let prevTracking = tracking;
3795
4106
  let prevStrictRead = false;
@@ -3834,11 +4145,15 @@ function recompute(el, create = false) {
3834
4145
  // retry can actually run.
3835
4146
  parkLoadingWindow(el, e);
3836
4147
  } else {
3837
- if (notReady) ext(el)._blocked = true;
3838
- notifyStatus(el, notReady ? STATUS_PENDING : STATUS_ERROR, e);
3839
- // The replacement source is fully propagated now. If no new flight
3840
- // re-owned self, retire the superseded flight and its dependent copies.
3841
- if (notReady && wasPendingSource && !el._x?._inFlight) settlePendingSource(el);
4148
+ // D-41: a pass of a held node, or one that read a staging of this
4149
+ // flush, stages its error (`stageError`).
4150
+ if (notReady || !(stagedErr = stageError(el, e, true))) {
4151
+ if (notReady) ext(el)._blocked = true;
4152
+ notifyStatus(el, notReady ? STATUS_PENDING : STATUS_ERROR, e);
4153
+ // The replacement source is fully propagated now. If no new flight
4154
+ // re-owned self, retire the superseded flight and its dependent copies.
4155
+ if (notReady && wasPendingSource && !el._x?._inFlight) settlePendingSource(el);
4156
+ }
3842
4157
  // A re-park drops what the earlier pass carried (#3456): a source this
3843
4158
  // pass no longer reaches — its branch switched, or a fresh flight
3844
4159
  // replaced the inputs' pending with its own — stays copied onto
@@ -3846,14 +4161,16 @@ function recompute(el, create = false) {
3846
4161
  // stops at this node (nothing left to retire) before it finds them. A
3847
4162
  // dependent then waits forever on a flight it has no path to. The
3848
4163
  // re-park twin of the unchanged-value recovery sweep below; dependents
3849
- // with another path keep the source (retryReaches).
3850
- if (notReady && outgoingPendingSources)
4164
+ // with another path keep the source (retryReaches). A staged error
4165
+ // answered what the pass carried: the same.
4166
+ if ((notReady || stagedErr) && outgoingPendingSources)
3851
4167
  for (const source of outgoingPendingSources)
3852
4168
  if (source !== el && !el._x?._pendingSources?.has(source))
3853
4169
  settlePendingSource(el, source);
3854
4170
  }
3855
4171
  } finally {
3856
4172
  tracking = prevTracking;
4173
+ if (laneStageHook) laneRoot = prevMount;
3857
4174
  strictRead = prevStrictRead;
3858
4175
  // Consume the missed-wake latch (#3037, set by insertSubs): a dep write
3859
4176
  // landed beneath this pass on a link it had already validated. The wipe
@@ -3861,10 +4178,11 @@ function recompute(el, create = false) {
3861
4178
  // (markNode(c) in read()) marks the running node as part of ordinary
3862
4179
  // bookkeeping, and those marks are correctly discarded here.
3863
4180
  missedWake = (el._flags & REACTIVE_MISSED_WAKE) !== 0;
3864
- joined = (el._flags & REACTIVE_JOINED) !== 0;
3865
- // A verdict reader is one for as long as it probes: a pass that entered
3866
- // no window (REACTIVE_PROBED) is an ordinary derivation again.
3867
- if (!(el._flags & REACTIVE_PROBED)) el._config &= ~CONFIG_VERDICT;
4181
+ joined = !!(el._flags & (mount ? REACTIVE_JOINED | REACTIVE_STAGED_READ : REACTIVE_JOINED));
4182
+ // A verdict reader is one for as long as it probes, or reads a memo
4183
+ // that does (`read`): a pass that did neither (REACTIVE_PROBED, the same
4184
+ // bit) is an ordinary derivation again.
4185
+ el._config = (el._config & ~CONFIG_VERDICT) | (el._flags & REACTIVE_PROBED);
3868
4186
  // REACTIVE_DISPOSED survives too (#3621): the pass may have disposed its
3869
4187
  // own owner (a memo calling its root's `dispose()`, a cleanup doing so
3870
4188
  // #3601/#3606), and `disposeChildren` set the flag on this node
@@ -3883,6 +4201,7 @@ function recompute(el, create = false) {
3883
4201
  REACTIVE_PROBE_UNANSWERED)) |
3884
4202
  (create ? el._flags & REACTIVE_SNAPSHOT_STALE : 0);
3885
4203
  context = oldcontext;
4204
+ if (released) for (const node of released) releaseIfSettledUnobserved(node);
3886
4205
  // A19 exc. 2: a pass that went pending on a `refresh()` re-asks the
3887
4206
  // question already answered — quiet for the verdict; any other pending
3888
4207
  // pass is a new question. Settled, there is nothing to classify.
@@ -3911,15 +4230,15 @@ function recompute(el, create = false) {
3911
4230
  // seat that read none of the lane's world has left it: its result is the
3912
4231
  // frame's (a derivation whose branch no longer reaches the guess). A
3913
4232
  // guess is written, not derived — it never leaves this way.
3914
- let lane = passLane;
4233
+ let lane = !laneStageHook ? null : passLane;
3915
4234
  // Listed before its staging, a pending pass included (the lane's own
3916
4235
  // flight is the lane's); false: the pass left the lane (lanes.ts).
3917
- const errored = !!el._x?._error;
3918
- if (lane !== null) {
3919
- if (GlobalQueue._laneStage(el, lane, create, errored)) setPassLane(lane);
4236
+ const errored = !!el._x?._error || stagedErr;
4237
+ if (laneStageHook && lane !== null) {
4238
+ if (laneStageHook(el, lane, create, errored)) setPassLane(lane);
3920
4239
  else lane = null;
3921
4240
  }
3922
- if (!el._x?._error) {
4241
+ if (!errored) {
3923
4242
  // Observe-tier fan-in (HUGE_FAN_IN): the validated prefix [_deps.._depsTail]
3924
4243
  // IS this pass's distinct sources — count it here rather than per link. A
3925
4244
  // begin/end bracket around the pass plus a per-link increment measured
@@ -3946,17 +4265,9 @@ function recompute(el, create = false) {
3946
4265
  : el._config & CONFIG_OVERRIDE && el._x._lane !== NOT_PENDING
3947
4266
  ? el._x._lane
3948
4267
  : el._value;
3949
- let valueChanged = false;
3950
- try {
3951
- valueChanged =
3952
- (!isEffect && wasUninitialized) || !el._equals || !el._equals(compareValue, value);
3953
- } catch (e) {
3954
- // A throwing user comparator is an error of this node's computation.
3955
- // Route it through the same status path as a compute-phase throw so
3956
- // error boundaries contain it; otherwise it unwinds the scheduler
3957
- // flush, bypassing every boundary and wedging the queue (#2837).
3958
- notifyStatus(el, STATUS_ERROR, e);
3959
- }
4268
+ // A throwing comparator is contained inside `isSame` (A3, #2837; #4111).
4269
+ const valueChanged =
4270
+ (!isEffect && wasUninitialized) || !!el._x?._shownError || !isSame(el, compareValue, value);
3960
4271
  // A committed derived change becomes a cause for this node's subscribers,
3961
4272
  // chaining their attribution through this node to the root write.
3962
4273
  if (attrHooks !== null) {
@@ -4000,7 +4311,7 @@ function recompute(el, create = false) {
4000
4311
  !create &&
4001
4312
  lane === null &&
4002
4313
  el._config & CONFIG_GUESS &&
4003
- GlobalQueue._laneOutcome(el, value, errored)
4314
+ laneOutcomeHook(el, value, errored)
4004
4315
  );
4005
4316
  else if (errored);
4006
4317
  else if (valueChanged) {
@@ -4034,9 +4345,7 @@ function recompute(el, create = false) {
4034
4345
  create
4035
4346
  ? !(
4036
4347
  (flushTransaction !== null || passTx !== null) &&
4037
- (joined ||
4038
- (prevLane && el._flags & REACTIVE_STAGED_READ) ||
4039
- (creatorPass(oldcontext)?._flags ?? 0) & REACTIVE_JOINED)
4348
+ (joined || creatorPass(oldcontext)?._flags & REACTIVE_JOINED)
4040
4349
  )
4041
4350
  : isEffect && el._pendingValue === NOT_PENDING
4042
4351
  ) {
@@ -4065,8 +4374,12 @@ function recompute(el, create = false) {
4065
4374
  el._statusFlags |= STATUS_UNINITIALIZED;
4066
4375
  // A boundary that has not shown content catches it: its fallback
4067
4376
  // waits for the commit (#3540). (The pass's own status clear above
4068
- // dropped what the read recorded.)
4069
- GlobalQueue._fresh?.(el);
4377
+ // dropped what the read recorded.) Not when the flush has joined
4378
+ // the hold and the pass is its own (not a verdict lane's mount,
4379
+ // #3851): the mount is part of the hold, and the boundary appears
4380
+ // at its commit, its fallback never seen (D-13, U-3) — a `<Show>`
4381
+ // mounting it over a landing included.
4382
+ freshHook?.(el, !mount && flushTransaction);
4070
4383
  }
4071
4384
  }
4072
4385
  // insertSubs only walks _subs (no scheduling of its own), so a
@@ -4101,16 +4414,14 @@ function recompute(el, create = false) {
4101
4414
  // A first pass that went pending where the born-held arm would have held
4102
4415
  // its value (it read a held node, or a verdict lane's mount read a staging,
4103
4416
  // #3851) is held like it: its answer is the hold's, and the hold waits
4104
- // for it (A29, #3800) — unless a boundary that has not shown content
4105
- // catches it: then it is the boundary's, and the hold does not wait (#3540).
4106
- if (
4107
- create &&
4108
- lane === null &&
4109
- (joined || (prevLane && el._flags & REACTIVE_STAGED_READ)) &&
4110
- el._statusFlags & STATUS_PENDING
4111
- ) {
4417
+ // for it (A29, #3800). A boundary that has not shown content catches it
4418
+ // and it is held all the same — its answer reveals no earlier than the
4419
+ // hold's commit (D-12) — but the hold does not wait for it: its only
4420
+ // readers are behind the fallback (`_hidden`; F-4, #3540). A boundary
4421
+ // re-armed by `on` owns what it collects outright (D-14): not held.
4422
+ if (create && lane === null && joined && el._statusFlags & STATUS_PENDING) {
4112
4423
  const t = flushTransaction ?? passTx;
4113
- if (t !== null && !t._lane && !GlobalQueue._fresh?.(el)) holdNode(el, t);
4424
+ if (t && !t._lane && freshHook?.(el) !== CAUGHT_REARMED) holdNode(el, t);
4114
4425
  }
4115
4426
  // Dependencies are the committed frame's until it is replaced (A30, #3410):
4116
4427
  // a pass that staged its value leaves the previous pass's tail linked for
@@ -4222,12 +4533,12 @@ function computed(fn, options) {
4222
4533
  const loading = options !== null && typeof options === "object" && "loadingValue" in options;
4223
4534
  // Two literals, one per tier, selected at build time (the observe flag is
4224
4535
  // a literal after replacement; the untaken branch is dead code). The observe
4225
- // literal is the prod literal plus its `_name` slot — a slot in the
4226
- // boilerplate, because a post-construction `self._name = …` forces a
4536
+ // literal is the prod literal plus its `_name` and `_loc` slots — slots in
4537
+ // the boilerplate, because a post-construction `self._name = …` forces a
4227
4538
  // hidden-class transition and an out-of-object property store on EVERY node
4228
4539
  // (measured: the whole of the observe tier's creation overhead). Keep the
4229
4540
  // two in sync — the dist artifact test pins observe's key set to prod's
4230
- // plus `_name`.
4541
+ // plus the slots.
4231
4542
  const self = {
4232
4543
  id: inheritId(options, transparent, context),
4233
4544
  _config:
@@ -4269,6 +4580,7 @@ function computed(fn, options) {
4269
4580
  // The slot is always present (hidden class); plumbing leaves it
4270
4581
  // unset, which `ownerPath` skips.
4271
4582
  _name: options?._plumbing ? undefined : (options?.name ?? "computed"),
4583
+ _loc: options?.loc,
4272
4584
  // Observe: the verdict windows this pass entered (verdict.ts) — a
4273
4585
  // slot, so the stamp is never a write after construction.
4274
4586
  _devWindows: 0
@@ -4300,7 +4612,8 @@ function ext(el) {
4300
4612
  _flushedAt: -1,
4301
4613
  _q: 0,
4302
4614
  _lane: NOT_PENDING,
4303
- _marks: 0
4615
+ _marks: 0,
4616
+ _shownError: undefined
4304
4617
  });
4305
4618
  }
4306
4619
  /** A tracked read of a held node (the reader derives from its transaction's
@@ -4322,10 +4635,7 @@ function joinPass(c, el) {
4322
4635
  // the reader's position, not where the memo was created). Its catch, if
4323
4636
  // any, is at its staging.
4324
4637
  if (el._statusFlags & STATUS_UNINITIALIZED || c._type !== EFFECT_RENDER)
4325
- joinPassTx(
4326
- txOf(el),
4327
- (!c._type && c._statusFlags & STATUS_UNINITIALIZED) || GlobalQueue._fresh?.(c)
4328
- );
4638
+ joinPassTx(txOf(el), (!c._type && c._statusFlags & STATUS_UNINITIALIZED) || freshHook?.(c));
4329
4639
  }
4330
4640
  /** A15's stale reader (shared-hole and reveal corollaries): a render effect
4331
4641
  * reading a node held by a transaction that is not the flush's. It is served
@@ -4362,10 +4672,11 @@ function frameRead(c, el) {
4362
4672
  // read the screen. Lane work with no committed value yet reads a flight
4363
4673
  // as a mount's memo does: it enters, and the boundary it mounts catches
4364
4674
  // the pending (#3540).
4365
- const verdict = c._config & CONFIG_VERDICT;
4675
+ // (Verdict readers and lanes exist only with their engines installed.)
4676
+ const verdict = observeFlightHook && c._config & CONFIG_VERDICT;
4677
+ const lane = laneStageHook && passLane !== null;
4366
4678
  if (
4367
- (passLane === null ||
4368
- (c._statusFlags & STATUS_UNINITIALIZED && el._statusFlags & STATUS_PENDING)) &&
4679
+ (!lane || (c._statusFlags & STATUS_UNINITIALIZED && el._statusFlags & STATUS_PENDING)) &&
4369
4680
  !verdict &&
4370
4681
  (c._type !== EFFECT_RENDER || el._config & CONFIG_INPUTS_PUBLISHED)
4371
4682
  )
@@ -4374,7 +4685,7 @@ function frameRead(c, el) {
4374
4685
  // Lane work and verdict readers are never the transaction's pass, whichever
4375
4686
  // flush runs it: a node T holds re-enters T when it re-runs (`recompute`'s
4376
4687
  // head), and the pass may then read a guess and become the lane's.
4377
- if ((t === flushTransaction && passLane === null && !verdict) || bornInto(c, t)) return false;
4688
+ if ((t === flushTransaction && !lane && !verdict) || bornInto(c, t)) return false;
4378
4689
  staleReader(c, t);
4379
4690
  return true;
4380
4691
  }
@@ -4461,6 +4772,7 @@ function createEffectNode(fn, effectFn, errorFn, type, options) {
4461
4772
  _type: type,
4462
4773
  _x: null,
4463
4774
  _name: options?.name ?? "effect",
4775
+ _loc: options?.loc,
4464
4776
  _devWindows: 0
4465
4777
  };
4466
4778
  // Effects dispatch status through the SHARED notifier (statusNotifierOf,
@@ -4512,7 +4824,7 @@ function setupComputedNode(self, options) {
4512
4824
  if (context) linkChild(context, self);
4513
4825
  DEV.hooks.onOwner?.(self);
4514
4826
  if (parent) self._height = parent._height + 1;
4515
- GlobalQueue._wireExternalSource?.(self);
4827
+ wireExternalSourceHook?.(self);
4516
4828
  !options?.lazy && recompute(self, true);
4517
4829
  if (snapshotCaptureActive && !options?.lazy) {
4518
4830
  if (!(self._statusFlags & STATUS_PENDING) && !(self._config & CONFIG_NO_SNAPSHOT)) {
@@ -4524,7 +4836,7 @@ function setupComputedNode(self, options) {
4524
4836
  }
4525
4837
  function signal(v, options) {
4526
4838
  // Prod and observe boilerplates — see computed(). The observe literal adds
4527
- // `_name` and `_owner` (the creating owner, stamped by registerGraph for
4839
+ // `_name`, `_loc` and `_owner` (the creating owner, stamped by registerGraph for
4528
4840
  // createSignal nodes so ownerPath can locate signal subjects; null here,
4529
4841
  // and staying null on internal signals — one shape either way).
4530
4842
  const s = {
@@ -4540,6 +4852,7 @@ function signal(v, options) {
4540
4852
  _notifiedAt: -1,
4541
4853
  _x: null,
4542
4854
  _name: options?.name ?? "signal",
4855
+ _loc: options?.loc,
4543
4856
  _owner: null
4544
4857
  };
4545
4858
  s._internal = false;
@@ -4563,7 +4876,8 @@ function isEqual(a, b) {
4563
4876
  * object, no NodeExtension, no post-construction expandos. `ownedWrite` is
4564
4877
  * baked in: the setter carries the owned-scope guard; node-level writes are
4565
4878
  * the store's notification machinery. The observe literal adds the label
4566
- * slot (the store relabels it `store.<key>` when the engine is installed). */
4879
+ * and location slots (the store relabels `store.<key>` and stamps the
4880
+ * store's `loc` when the engine is installed). */
4567
4881
  function slotSignal(v, equals, host, key, acc) {
4568
4882
  const s = {
4569
4883
  _equals: equals,
@@ -4581,6 +4895,7 @@ function slotSignal(v, equals, host, key, acc) {
4581
4895
  px: undefined,
4582
4896
  pxv: undefined,
4583
4897
  _name: "signal",
4898
+ _loc: undefined,
4584
4899
  _owner: null
4585
4900
  };
4586
4901
  s._internal = false;
@@ -4617,7 +4932,11 @@ function exitCallback() {
4617
4932
  }
4618
4933
  /**
4619
4934
  * Dev-only: > 0 while owner teardown runs cleanups (`_disposal` entries and
4620
- * effect-returned cleanups — owner.ts), for the same reason. Kept apart from
4935
+ * effect-returned cleanups — owner.ts), for the same reason. The owned-scope
4936
+ * write guard (setSignal, markRefresh, the store setter) stands down here
4937
+ * too: a cleanup is teardown, not part of whichever computation is disposing
4938
+ * its owner (a list pass removing a row, a branch switching away), so its
4939
+ * writes are imperative like an effect callback's (#4165). Kept apart from
4621
4940
  * `callbackDepth` because its sites cannot use try/finally (the frame would
4622
4941
  * survive into prod): a throwing cleanup leaves it raised, and dev.ts resets
4623
4942
  * it on the next microtask, which teardown — synchronous — never spans.
@@ -4644,6 +4963,16 @@ function setStrictRead(v) {
4644
4963
  * Use `untrack` inside a memo or effect when you need to read a signal once
4645
4964
  * without making the surrounding computation depend on its future changes.
4646
4965
  *
4966
+ * `untrack` stops dependency tracking, not async settlement. Reading an
4967
+ * uninitialized async source still suspends the owning computation; it runs
4968
+ * again when that source first resolves. Afterward, the untracked read is a
4969
+ * snapshot and does not subscribe the computation to later changes.
4970
+ *
4971
+ * If the first read must return immediately, give the async source a
4972
+ * `loadingValue` before reading it with `untrack`. `isPending` and `latest`
4973
+ * inspect updates to an existing value; neither supplies a missing initial
4974
+ * value.
4975
+ *
4647
4976
  * Pass a `strictReadLabel` string to enable a dev-mode warning: any reactive
4648
4977
  * read inside `fn` that isn't inside a nested tracking scope will log a
4649
4978
  * warning naming the label.
@@ -4657,17 +4986,12 @@ function setStrictRead(v) {
4657
4986
  * log(snapshot);
4658
4987
  * }
4659
4988
  * );
4989
+ *
4990
+ * const color = createMemo<string>(() => fetchColor(), { loadingValue: "gray" });
4991
+ * const initialColor = untrack(color); // "gray" while the fetch is unresolved
4660
4992
  * ```
4661
4993
  */
4662
4994
  function untrack(fn, strictReadLabel) {
4663
- if (
4664
- GlobalQueue._externalUntrack === null &&
4665
- !tracking &&
4666
- !strictRead &&
4667
- !strictReadLabel &&
4668
- asyncTailFlights === 0
4669
- )
4670
- return fn();
4671
4995
  const prevTracking = tracking;
4672
4996
  const prevStrictRead = strictRead;
4673
4997
  tracking = false;
@@ -4676,7 +5000,7 @@ function untrack(fn, strictReadLabel) {
4676
5000
  untrackDepth++;
4677
5001
  }
4678
5002
  try {
4679
- if (GlobalQueue._externalUntrack) return GlobalQueue._externalUntrack(fn);
5003
+ if (externalUntrackHook) return externalUntrackHook(fn);
4680
5004
  return fn();
4681
5005
  } finally {
4682
5006
  tracking = prevTracking;
@@ -4748,7 +5072,7 @@ function prepareComputed(comp, refresh) {
4748
5072
  * False means the reader derives from the staged value.
4749
5073
  */
4750
5074
  function readerSeesCommitted(el, c) {
4751
- return !!(!c || el._pendingValue === NOT_PENDING || c._config & CONFIG_CHILDREN_FORBIDDEN);
5075
+ return !c || el._pendingValue === NOT_PENDING || c._config & CONFIG_CHILDREN_FORBIDDEN;
4752
5076
  }
4753
5077
  /** A28 — set when a node is staged (queuePendingNode) OUTSIDE a flush;
4754
5078
  * cleared when the next flush begins. The read sites test this one module
@@ -4827,6 +5151,7 @@ function pullComputed(owner, c) {
4827
5151
  if (height >= c._height && owner._parent !== c) c._height = height + 1;
4828
5152
  }
4829
5153
  function read(el) {
5154
+ DEV.hooks.onRead?.(el);
4830
5155
  if (asyncTailFlights !== 0 && !tracking && untrackDepth === 0)
4831
5156
  checkPostAwaitRead(
4832
5157
  el,
@@ -4877,7 +5202,7 @@ function read(el) {
4877
5202
  if (!c || el._pendingValue === NOT_PENDING || c._config & CONFIG_CHILDREN_FORBIDDEN)
4878
5203
  return el._value;
4879
5204
  // (A plain signal is never uninitialized — the carve-out is `serve`'s.)
4880
- if (c._config & CONFIG_VERDICT && stagedScreen(c)) return el._value;
5205
+ if (observeFlightHook && c._config & CONFIG_VERDICT && stagedScreen(c)) return el._value;
4881
5206
  stagedRead(c);
4882
5207
  return el._pendingValue;
4883
5208
  }
@@ -4893,7 +5218,15 @@ function read(el) {
4893
5218
  if (c) {
4894
5219
  if (tracking) {
4895
5220
  link(el, c);
4896
- if (owner._fn) pullComputed(owner, c);
5221
+ if (owner._fn) {
5222
+ pullComputed(owner, c);
5223
+ // F-6 (#4050): a memo over a verdict reader's value derives from a
5224
+ // display-ahead read — a verdict reader from this pass's end, as if
5225
+ // it probed inline: it reads held values as the screen and shows the
5226
+ // verdict now. Not an effect: a render effect is a frame reader
5227
+ // already, and a user effect's posture is its own reads'.
5228
+ if (!c._type) c._flags |= owner._config & CONFIG_VERDICT;
5229
+ }
4897
5230
  }
4898
5231
  // L2, as in the fast block above (tracked or not). A node born into the
4899
5232
  // future (uninitialized) has no committed value: every reader derives
@@ -4910,8 +5243,9 @@ function read(el) {
4910
5243
  }
4911
5244
  // Lanes: a lane's node (after the pull — the node is current). NOT_PENDING
4912
5245
  // falls through: lane work's read of a pending member throws like any.
4913
- if (el._config & CONFIG_OVERRIDE) {
4914
- const v = GlobalQueue._laneRead(c, el);
5246
+ // (CONFIG_OVERRIDE is the lane engine's: no engine, no lane's node.)
5247
+ if (laneReadHook && el._config & CONFIG_OVERRIDE) {
5248
+ const v = laneReadHook(c, el);
4915
5249
  if (v !== NOT_PENDING) return v;
4916
5250
  }
4917
5251
  // A stale reader of a held flight (rule 3, A15's reveal corollary) is served
@@ -4936,9 +5270,11 @@ function read(el) {
4936
5270
  ) {
4937
5271
  // A reader with no committed value yet sees the flight pending: a
4938
5272
  // boundary a lane mounts shows its fallback (A29's boundary exemption).
4939
- if (passLane !== null && !(c?._statusFlags & STATUS_UNINITIALIZED)) committed = true;
4940
- else if (c !== null && c._config & CONFIG_VERDICT)
4941
- committed = GlobalQueue._observeFlight(c, owner);
5273
+ // (A lane, and a verdict reader, exist only with their engines installed.)
5274
+ if (laneStageHook && passLane !== null && !(c?._statusFlags & STATUS_UNINITIALIZED))
5275
+ committed = true;
5276
+ else if (observeFlightHook && c !== null && c._config & CONFIG_VERDICT)
5277
+ committed = observeFlightHook(c, owner);
4942
5278
  }
4943
5279
  if (owner._statusFlags & STATUS_PENDING && !committed) {
4944
5280
  // A reader landing on a pending node throws; an untracked read of an
@@ -5007,6 +5343,9 @@ function read(el) {
5007
5343
  }
5008
5344
  }
5009
5345
  if (committed) return el._value;
5346
+ // D-41: an untracked read reads the screen, and a shown error is on it
5347
+ // until the answer that replaces it commits.
5348
+ if (!c && el._x?._shownError) throw el._x._shownError;
5010
5349
  const value = serve(el, c);
5011
5350
  if (
5012
5351
  !c &&
@@ -5053,9 +5392,16 @@ function serve(el, c) {
5053
5392
  return u;
5054
5393
  }
5055
5394
  if (readerSeesCommitted(el, c)) return el._value;
5056
- if (c._config & CONFIG_VERDICT && !(el._statusFlags & STATUS_UNINITIALIZED) && stagedScreen(c))
5395
+ if (
5396
+ observeFlightHook &&
5397
+ c._config & CONFIG_VERDICT &&
5398
+ !(el._statusFlags & STATUS_UNINITIALIZED) &&
5399
+ stagedScreen(c)
5400
+ )
5057
5401
  return el._value;
5058
5402
  stagedRead(c, el);
5403
+ // D-41: the staged answer is an error (`stageError`).
5404
+ if (el._config & CONFIG_STAGED_ERROR) throw el._pendingValue;
5059
5405
  return el._pendingValue;
5060
5406
  }
5061
5407
  /** A pass read a staging of this flush: it derives from what the flush may
@@ -5073,7 +5419,7 @@ function serve(el, c) {
5073
5419
  function stagedRead(c, el) {
5074
5420
  if (!(el?._statusFlags & STATUS_UNINITIALIZED)) {
5075
5421
  c._flags |= REACTIVE_STAGED_READ;
5076
- if (passLane !== null) stagedReaders.push(c);
5422
+ if (laneSeamsHook && passLane !== null) stagedReaders.push(c);
5077
5423
  }
5078
5424
  }
5079
5425
  /** A10 for a staged node: a verdict reader (the pass entered a window) that
@@ -5123,11 +5469,46 @@ function captureWriteSnapshot(el, current) {
5123
5469
  el._config |= CONFIG_HAS_SNAPSHOT;
5124
5470
  snapshotSources.add(el);
5125
5471
  }
5472
+ /**
5473
+ * A callback run before every signal or store write lands, while set, with
5474
+ * the written signal or the store proxy whose setter runs. The
5475
+ * server-components client sets it while slot occurrences are left
5476
+ * unmounted after hydration (deferred hydration): a write may be one an
5477
+ * unmounted component would have observed, so the client mounts what is
5478
+ * left before the write applies, and clears the hook. `null` otherwise.
5479
+ */
5480
+ let writeHook = null;
5481
+ /** @internal */
5482
+ function setWriteHook(hook) {
5483
+ writeHook = hook;
5484
+ }
5485
+ /**
5486
+ * `el`'s comparator verdict (`_equals`), the one guarded call for recompute,
5487
+ * setSignal (user setters and async landings) and the seam's
5488
+ * staged-equals-committed check.
5489
+ * - A comparator that read an unresolved async source has not shown the
5490
+ * values equal (#4111): falsy, a change, whose readers suspend in their own
5491
+ * tracked context.
5492
+ * - Any other throw is an error of `el`'s computation (A3, #2837): routed
5493
+ * through the status path so error boundaries contain it, instead of
5494
+ * unwinding a flush or a setter; answered "same" so nothing commits.
5495
+ * @internal
5496
+ */
5497
+ function isSame(el, a, b) {
5498
+ if (el._equals === isEqual) return a === b;
5499
+ try {
5500
+ return el._equals && el._equals(a, b);
5501
+ } catch (e) {
5502
+ if (!(e instanceof NotReadyError)) return (notifyStatus(el, STATUS_ERROR, e), true);
5503
+ }
5504
+ }
5126
5505
  function setSignal(el, v) {
5506
+ if (writeHook !== null) writeHook(el);
5127
5507
  if (
5128
5508
  !(el._config & CONFIG_OWNED_WRITE) &&
5129
5509
  !(context && context._config & CONFIG_CHILDREN_FORBIDDEN) &&
5130
- context
5510
+ context &&
5511
+ !disposalDepth
5131
5512
  ) {
5132
5513
  emitDiagnostic({
5133
5514
  code: "REACTIVE_WRITE_IN_OWNED_SCOPE",
@@ -5146,7 +5527,7 @@ function setSignal(el, v) {
5146
5527
  // Lanes: a write landing on a guess is its truth (A18 — an async guess's
5147
5528
  // own landing), judged against the guess; one landing on a lane's
5148
5529
  // derivation (its flight) is the lane's staging (lanes.ts).
5149
- if (el._config & CONFIG_OVERRIDE) return GlobalQueue._laneWrite(el, v);
5530
+ if (laneWriteHook && el._config & CONFIG_OVERRIDE) return laneWriteHook(el, v);
5150
5531
  // L2 (T4) / A34 (1): a write to a node whose staged state is held is a
5151
5532
  // second proposal for the slot — the same value again or another — and
5152
5533
  // the two cannot finish at different times: the writer's tick joins the
@@ -5154,8 +5535,12 @@ function setSignal(el, v) {
5154
5535
  if (el._config & CONFIG_HELD) joinFuture(txOf(el));
5155
5536
  // Uninitialized check first: the first commit has no previous value, so the
5156
5537
  // user comparator must not run against `undefined` (matches recompute).
5538
+ // D-41: a write over a shown error is a change whatever the last good
5539
+ // value was — the error leaves at its commit (`commitPendingNode`).
5157
5540
  const valueChanged =
5158
- !!(el._statusFlags & STATUS_UNINITIALIZED) || !el._equals || !el._equals(currentValue, v);
5541
+ !!(el._statusFlags & STATUS_UNINITIALIZED) ||
5542
+ !!el._x?._shownError ||
5543
+ !isSame(el, currentValue, v);
5159
5544
  if (!valueChanged) return v;
5160
5545
  // Attribution hook: this committed write is where a re-run chain begins.
5161
5546
  if (attrHooks !== null) attrHooks.write(el, currentValue, v);
@@ -5213,8 +5598,21 @@ function setMemo(el, v) {
5213
5598
  !(el._flags & REACTIVE_MANUAL_WRITE) &&
5214
5599
  txOf(el) !== flushTransaction;
5215
5600
  // The writer read the committed frame; its updater composes on it.
5216
- if (held && typeof v === "function") v = v(el._value);
5217
- const result = setSignal(el, v);
5601
+ if (held && typeof v === "function") {
5602
+ const value = v(el._value);
5603
+ v = () => value;
5604
+ }
5605
+ // #4110: the write replaces a failed derivation's outcome. It lands without
5606
+ // re-running `_fn`, and nothing else clears STATUS_ERROR, so without this
5607
+ // every later read keeps throwing the error the derivation left behind.
5608
+ // The error stays the display until the write commits (D-41).
5609
+ if (el._statusFlags & STATUS_ERROR) {
5610
+ el._x._shownError = el._x._error;
5611
+ notifyStatus(el, STATUS_NONE, undefined);
5612
+ }
5613
+ v = setSignal(el, v);
5614
+ // A write replaces an error a pass staged (D-41): it is a value again.
5615
+ el._config &= ~CONFIG_STAGED_ERROR;
5218
5616
  if (held) {
5219
5617
  // The one write that re-runs: the staging it replaced was derived from
5220
5618
  // inputs the writer never saw, so the hold re-derives over the write.
@@ -5222,7 +5620,7 @@ function setMemo(el, v) {
5222
5620
  insertIntoHeap(el, dirtyQueue);
5223
5621
  schedule();
5224
5622
  } else if (el._pendingValue !== NOT_PENDING) el._flags |= REACTIVE_MANUAL_WRITE;
5225
- return result;
5623
+ return v;
5226
5624
  }
5227
5625
  /**
5228
5626
  * Executes `fn` with the given `owner` set as the current owner. Any reactive
@@ -5289,7 +5687,8 @@ function markRefresh(node) {
5289
5687
  if (
5290
5688
  context &&
5291
5689
  !((node._config ?? 0) & CONFIG_OWNED_WRITE) &&
5292
- !(context._config & CONFIG_CHILDREN_FORBIDDEN)
5690
+ !(context._config & CONFIG_CHILDREN_FORBIDDEN) &&
5691
+ !disposalDepth
5293
5692
  ) {
5294
5693
  emitDiagnostic({
5295
5694
  code: "REACTIVE_WRITE_IN_OWNED_SCOPE",
@@ -5323,175 +5722,194 @@ function markRefresh(node) {
5323
5722
  }
5324
5723
 
5325
5724
  export {
5326
- REACTIVE_CHECK as $,
5327
- list as A,
5328
- clock as B,
5725
+ setPassLane as $,
5726
+ tracking as A,
5727
+ CONFIG_CHILDREN_FORBIDDEN as B,
5329
5728
  CONFIG_GUESS as C,
5330
- insertSubs as D,
5331
- EFFECT_RENDER as E,
5332
- laneDirty as F,
5729
+ passLane as D,
5730
+ STATUS_PENDING as E,
5731
+ EFFECT_RENDER as F,
5333
5732
  GlobalQueue as G,
5334
- merge as H,
5335
- newTransaction as I,
5336
- reruns as J,
5337
- CONFIG_STAGED as K,
5338
- releaseQueues as L,
5339
- ownFlights as M,
5733
+ sameLane as H,
5734
+ flushTransaction as I,
5735
+ REACTIVE_SCREEN_READ as J,
5736
+ REACTIVE_FRAME_READ as K,
5737
+ attrHooks as L,
5738
+ commitPendingNode as M,
5340
5739
  NOT_PENDING as N,
5341
- setPassLane as O,
5342
- REACTIVE_JOINED as P,
5343
- REACTIVE_STAGED_READ as Q,
5740
+ REACTIVE_IN_HEAP as O,
5741
+ REACTIVE_DIRTY as P,
5742
+ REACTIVE_CHECK as Q,
5344
5743
  REACTIVE_DISPOSED as R,
5345
- STATUS_PENDING as S,
5346
- stagedRead as T,
5347
- CONFIG_SLOT_NODE as U,
5348
- attrHooks as V,
5744
+ STATUS_UNINITIALIZED as S,
5745
+ CONFIG_INPUTS_PUBLISHED as T,
5746
+ releaseQueues as U,
5747
+ CONFIG_SLOT_NODE as V,
5349
5748
  holdNode as W,
5350
5749
  joinFuture as X,
5351
- commitPendingNode as Y,
5352
- REACTIVE_IN_HEAP as Z,
5353
- REACTIVE_DIRTY as _,
5354
- CONFIG_OVERRIDE as a,
5355
- untrack as a$,
5356
- CONFIG_INPUTS_PUBLISHED as a0,
5357
- setVerdict as a1,
5358
- context as a2,
5359
- REACTIVE_PROBED as a3,
5360
- bornInto as a4,
5361
- globalQueue as a5,
5362
- staleReader as a6,
5750
+ reruns as Y,
5751
+ CONFIG_STAGED as Z,
5752
+ merge as _,
5753
+ NotReadyError as a,
5754
+ setMemo as a$,
5755
+ REACTIVE_JOINED as a0,
5756
+ REACTIVE_STAGED_READ as a1,
5757
+ stagedRead as a2,
5758
+ setVerdict as a3,
5759
+ context as a4,
5760
+ installVerdictHooks as a5,
5761
+ REACTIVE_PROBED as a6,
5363
5762
  verdict as a7,
5364
5763
  link as a8,
5365
5764
  pullComputed as a9,
5366
- createEffectNode as aA,
5367
- recompute as aB,
5368
- EFFECT_USER as aC,
5369
- reportDiagnostic as aD,
5370
- computed as aE,
5371
- staleValues as aF,
5372
- CONFIG_AUTO_DISPOSE as aG,
5373
- EFFECT_TRACKED as aH,
5374
- unwrapStatusError as aI,
5375
- haltReactivity as aJ,
5376
- setStrictRead as aK,
5377
- StatusError as aL,
5378
- trimStaleDeps as aM,
5379
- setTrackedQueueCallback as aN,
5380
- setEffectStatusNotify as aO,
5381
- _hitUnhandledAsync as aP,
5382
- resetUnhandledAsync as aQ,
5383
- ROOT_ERROR_HOOK as aR,
5384
- registerGraph as aS,
5385
- runWithOwner as aT,
5386
- setMemo as aU,
5387
- $REFRESH as aV,
5388
- markRefresh as aW,
5389
- createRoot as aX,
5390
- getObserver as aY,
5391
- dispose as aZ,
5392
- TimeoutError as a_,
5765
+ cleanup as aA,
5766
+ read as aB,
5767
+ DEV as aC,
5768
+ CONFIG_TRANSPARENT as aD,
5769
+ NoOwnerError as aE,
5770
+ ContextNotFoundError as aF,
5771
+ createEffectNode as aG,
5772
+ recompute as aH,
5773
+ EFFECT_USER as aI,
5774
+ reportDiagnostic as aJ,
5775
+ computed as aK,
5776
+ staleValues as aL,
5777
+ CONFIG_AUTO_DISPOSE as aM,
5778
+ EFFECT_TRACKED as aN,
5779
+ heldRunHook as aO,
5780
+ unwrapStatusError as aP,
5781
+ haltReactivity as aQ,
5782
+ setStrictRead as aR,
5783
+ StatusError as aS,
5784
+ trimStaleDeps as aT,
5785
+ setTrackedQueueCallback as aU,
5786
+ setEffectStatusNotify as aV,
5787
+ _hitUnhandledAsync as aW,
5788
+ resetUnhandledAsync as aX,
5789
+ ROOT_ERROR_HOOK as aY,
5790
+ registerGraph as aZ,
5791
+ runWithOwner as a_,
5393
5792
  strictRead as aa,
5394
5793
  warnStrictReadUntracked as ab,
5395
- joinPass as ac,
5396
- markLateLinker as ad,
5397
- spectating as ae,
5398
- STATUS_ERROR as af,
5399
- unflushedValue as ag,
5400
- stagedReaders as ah,
5401
- enterAction as ai,
5402
- setEffectCallback as aj,
5403
- exitAction as ak,
5404
- isThenable as al,
5405
- actionDepth as am,
5406
- flush as an,
5407
- setQuestion as ao,
5408
- emitDiagnostic as ap,
5409
- enterCallback as aq,
5410
- exitCallback as ar,
5411
- getOwner as as,
5412
- inEffectCallback as at,
5413
- signal as au,
5414
- setSignal as av,
5415
- cleanup as aw,
5416
- read as ax,
5417
- NoOwnerError as ay,
5418
- ContextNotFoundError as az,
5419
- CONFIG_VERDICT as b,
5420
- dirtyQueue as b0,
5421
- CONFIG_REDERIVE as b1,
5422
- passTx as b2,
5423
- CONFIG_FRESH_READ as b3,
5424
- createOwner as b4,
5425
- REACTIVE_WRITE_IN_OWNED_SCOPE_SIGNAL_MESSAGE as b5,
5426
- ownedScopeWriteMessage as b6,
5427
- DEV as b7,
5428
- isEqual as b8,
5429
- CONFIG_MANUAL_WRITE as b9,
5430
- peekNextChildId as bA,
5431
- releaseSnapshotScope as bB,
5432
- resetErrorHalt as bC,
5433
- setConsoleFooter as bD,
5434
- setSnapshotCapture as bE,
5435
- setAttributionHooks as bF,
5436
- liveRootOwners as bG,
5437
- ownerPath as bH,
5438
- isSuppressed as bI,
5439
- blockersOf as bJ,
5440
- CONFIG_PLUMBING as bK,
5441
- anyExcluded as bL,
5442
- isExcluded as bM,
5443
- CONFIG_WIDE as bN,
5444
- GRAPH_SIZE_WARN_AT as bO,
5445
- noteFanOut as bP,
5446
- CONFIG_SUPERSEDES as bQ,
5447
- snapshotCaptureActive as ba,
5448
- captureWriteSnapshot as bb,
5449
- notePromotedWrite as bc,
5450
- slotSignal as bd,
5451
- stagedScreen as be,
5452
- CONFIG_PROMOTED as bf,
5453
- setSlotUnobserved as bg,
5454
- joinPassTx as bh,
5455
- asyncTailFlights as bi,
5456
- untrackDepth as bj,
5457
- checkPostAwaitRead as bk,
5458
- insertIntoHeap as bl,
5459
- isDisposed as bm,
5460
- handleAsync as bn,
5461
- addPendingSource as bo,
5462
- propagateStatus as bp,
5463
- forEachDependent as bq,
5464
- settlePendingSource as br,
5465
- notifyEpoch as bs,
5466
- SUPPORTS_PROXY as bt,
5467
- REACTIVE_ZOMBIE as bu,
5468
- OBSERVE as bv,
5469
- clearSnapshots as bw,
5470
- enforceLoadingBoundary as bx,
5471
- getNextChildId as by,
5472
- markSnapshotScope as bz,
5473
- REACTIVE_PROBE_UNANSWERED as c,
5474
- blocked as d,
5475
- REACTIVE_LANE_DIRTY as e,
5476
- enqueueSub as f,
5477
- CONFIG_AUTHORITATIVE as g,
5478
- tracking as h,
5479
- CONFIG_CHILDREN_FORBIDDEN as i,
5480
- sameLane as j,
5794
+ staleReader as ac,
5795
+ globalQueue as ad,
5796
+ joinPass as ae,
5797
+ markLateLinker as af,
5798
+ spectating as ag,
5799
+ unflushedValue as ah,
5800
+ CONFIG_STAGED_ERROR as ai,
5801
+ stagedReaders as aj,
5802
+ bornInto as ak,
5803
+ enterAction as al,
5804
+ setEffectCallback as am,
5805
+ exitAction as an,
5806
+ isThenable as ao,
5807
+ actionDepth as ap,
5808
+ flush as aq,
5809
+ setQuestion as ar,
5810
+ emitDiagnostic as as,
5811
+ enterCallback as at,
5812
+ exitCallback as au,
5813
+ getOwner as av,
5814
+ inEffectCallback as aw,
5815
+ setExternalHooks as ax,
5816
+ signal as ay,
5817
+ setSignal as az,
5818
+ CONFIG_AUTHORITATIVE as b,
5819
+ isSuppressed as b$,
5820
+ $REFRESH as b0,
5821
+ markRefresh as b1,
5822
+ createRoot as b2,
5823
+ getObserver as b3,
5824
+ dispose as b4,
5825
+ TimeoutError as b5,
5826
+ untrack as b6,
5827
+ dirtyQueue as b7,
5828
+ CONFIG_REDERIVE as b8,
5829
+ passTx as b9,
5830
+ settlePendingSource as bA,
5831
+ notifyEpoch as bB,
5832
+ SUPPORTS_PROXY as bC,
5833
+ releaseMarksHook as bD,
5834
+ installMarkHooks as bE,
5835
+ CONFIG_LAGS as bF,
5836
+ REACTIVE_REASK as bG,
5837
+ heldUnder as bH,
5838
+ unhold as bI,
5839
+ laneStageHook as bJ,
5840
+ REACTIVE_ZOMBIE as bK,
5841
+ installBoundaryHooks as bL,
5842
+ CAUGHT_REARMED as bM,
5843
+ OBSERVE as bN,
5844
+ clearSnapshots as bO,
5845
+ enforceLoadingBoundary as bP,
5846
+ getNextChildId as bQ,
5847
+ markSnapshotScope as bR,
5848
+ peekNextChildId as bS,
5849
+ releaseSnapshotScope as bT,
5850
+ resetErrorHalt as bU,
5851
+ setConsoleFooter as bV,
5852
+ setSnapshotCapture as bW,
5853
+ setWriteHook as bX,
5854
+ setAttributionHooks as bY,
5855
+ liveRootOwners as bZ,
5856
+ ownerPath as b_,
5857
+ CONFIG_FRESH_READ as ba,
5858
+ createOwner as bb,
5859
+ writeHook as bc,
5860
+ REACTIVE_WRITE_IN_OWNED_SCOPE_SIGNAL_MESSAGE as bd,
5861
+ ownedScopeWriteMessage as be,
5862
+ isEqual as bf,
5863
+ CONFIG_MANUAL_WRITE as bg,
5864
+ snapshotCaptureActive as bh,
5865
+ captureWriteSnapshot as bi,
5866
+ notePromotedWrite as bj,
5867
+ slotSignal as bk,
5868
+ stagedScreen as bl,
5869
+ CONFIG_PROMOTED as bm,
5870
+ setSlotUnobserved as bn,
5871
+ installStoreHooks as bo,
5872
+ joinPassTx as bp,
5873
+ asyncTailFlights as bq,
5874
+ untrackDepth as br,
5875
+ checkPostAwaitRead as bs,
5876
+ disposalDepth as bt,
5877
+ insertIntoHeap as bu,
5878
+ isDisposed as bv,
5879
+ handleAsync as bw,
5880
+ addPendingSource as bx,
5881
+ propagateStatus as by,
5882
+ forEachDependent as bz,
5883
+ CONFIG_OVERRIDE as c,
5884
+ blockersOf as c0,
5885
+ CONFIG_PLUMBING as c1,
5886
+ anyExcluded as c2,
5887
+ isExcluded as c3,
5888
+ CONFIG_WIDE as c4,
5889
+ GRAPH_SIZE_WARN_AT as c5,
5890
+ noteFanOut as c6,
5891
+ CONFIG_SUPERSEDES as c7,
5892
+ newTransaction as d,
5893
+ enqueueSub as e,
5894
+ CONFIG_VERDICT as f,
5895
+ REACTIVE_PROBE_UNANSWERED as g,
5896
+ blocked as h,
5897
+ installLaneHooks as i,
5898
+ REACTIVE_LANE_DIRTY as j,
5481
5899
  CONFIG_HELD as k,
5482
- flushTransaction as l,
5483
- STATUS_UNINITIALIZED as m,
5900
+ clock as l,
5901
+ insertSubs as m,
5484
5902
  nextQuestion as n,
5485
- NotReadyError as o,
5486
- passLane as p,
5903
+ ownFlights as o,
5904
+ laneDirty as p,
5487
5905
  question as q,
5488
5906
  resolveTx as r,
5489
5907
  schedule as s,
5490
5908
  txOf as t,
5491
- REACTIVE_SCREEN_READ as u,
5492
- REACTIVE_FRAME_READ as v,
5493
- REACTIVE_LANE_READ as w,
5494
- ext as x,
5495
- REACTIVE_RECOMPUTING_DEPS as y,
5496
- queuePendingNode as z
5909
+ REACTIVE_LANE_READ as u,
5910
+ ext as v,
5911
+ REACTIVE_RECOMPUTING_DEPS as w,
5912
+ queuePendingNode as x,
5913
+ list as y,
5914
+ STATUS_ERROR as z
5497
5915
  };