@solidjs/signals 2.0.0-rc.2 → 2.0.0-rc.20

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 (198) hide show
  1. package/dist/dev-shared.js +6505 -0
  2. package/dist/dev.attribution.js +3484 -0
  3. package/dist/dev.js +10227 -9508
  4. package/dist/observe/affects-marks.js +86 -0
  5. package/dist/observe/affects.js +112 -0
  6. package/dist/observe/attribution.js +7 -0
  7. package/dist/observe/boundaries.js +696 -0
  8. package/dist/observe/core/action.js +182 -0
  9. package/dist/observe/core/async.js +824 -0
  10. package/dist/observe/core/attribution-costs.js +66 -0
  11. package/dist/observe/core/attribution-feedback.js +280 -0
  12. package/dist/observe/core/attribution-hooks.js +83 -0
  13. package/dist/observe/core/attribution-queries.js +43 -0
  14. package/dist/observe/core/attribution.js +3079 -0
  15. package/dist/observe/core/constants.js +269 -0
  16. package/dist/observe/core/context.js +63 -0
  17. package/dist/observe/core/copies.js +10 -0
  18. package/dist/observe/core/core.js +1707 -0
  19. package/dist/observe/core/dev.js +394 -0
  20. package/dist/observe/core/effect.js +193 -0
  21. package/dist/observe/core/error-hooks.js +71 -0
  22. package/dist/observe/core/error.js +82 -0
  23. package/dist/observe/core/external.js +97 -0
  24. package/dist/observe/core/graph.js +162 -0
  25. package/dist/observe/core/heap.js +124 -0
  26. package/dist/observe/core/identities.js +62 -0
  27. package/dist/observe/core/invariants.js +12 -0
  28. package/dist/observe/core/lanes.js +972 -0
  29. package/dist/observe/core/owner.js +371 -0
  30. package/dist/observe/core/scheduler.js +1157 -0
  31. package/dist/observe/core/truth.js +47 -0
  32. package/dist/observe/core/verdict.js +470 -0
  33. package/dist/observe/deferred.js +186 -0
  34. package/dist/observe/flatten.js +79 -0
  35. package/dist/observe/from.js +248 -0
  36. package/dist/observe/index.js +71 -0
  37. package/dist/observe/map.js +593 -0
  38. package/dist/observe/reveal.js +212 -0
  39. package/dist/observe/signals.js +717 -0
  40. package/dist/observe/store/affects.js +142 -0
  41. package/dist/observe/store/edges.js +39 -0
  42. package/dist/observe/store/hooks.js +16 -0
  43. package/dist/observe/store/index.js +47 -0
  44. package/dist/observe/store/optimistic.js +713 -0
  45. package/dist/observe/store/projection.js +345 -0
  46. package/dist/observe/store/reconcile.js +281 -0
  47. package/dist/observe/store/store.js +2127 -0
  48. package/dist/observe/store/storePath.js +103 -0
  49. package/dist/observe/store/target.js +46 -0
  50. package/dist/observe/store/types.js +83 -0
  51. package/dist/observe/store/utils.js +1063 -0
  52. package/dist/prod/affects-marks.js +86 -0
  53. package/dist/prod/affects.js +98 -112
  54. package/dist/prod/attribution.js +45 -0
  55. package/dist/prod/boundaries.js +578 -468
  56. package/dist/prod/core/action.js +92 -59
  57. package/dist/prod/core/async.js +522 -336
  58. package/dist/prod/core/attribution-hooks.js +3 -0
  59. package/dist/prod/core/constants.js +168 -44
  60. package/dist/prod/core/context.js +12 -16
  61. package/dist/prod/core/copies.js +10 -0
  62. package/dist/prod/core/core.js +1345 -627
  63. package/dist/prod/core/dev.js +123 -1
  64. package/dist/prod/core/effect.js +113 -80
  65. package/dist/prod/core/error-hooks.js +71 -0
  66. package/dist/prod/core/error.js +18 -4
  67. package/dist/prod/core/external.js +6 -7
  68. package/dist/prod/core/graph.js +119 -72
  69. package/dist/prod/core/heap.js +85 -109
  70. package/dist/prod/core/identities.js +62 -0
  71. package/dist/prod/core/invariants.js +1 -32
  72. package/dist/prod/core/lanes.js +931 -132
  73. package/dist/prod/core/owner.js +192 -129
  74. package/dist/prod/core/scheduler.js +948 -642
  75. package/dist/prod/core/truth.js +47 -0
  76. package/dist/prod/core/verdict.js +409 -420
  77. package/dist/prod/deferred.js +184 -0
  78. package/dist/prod/flatten.js +77 -0
  79. package/dist/prod/from.js +248 -0
  80. package/dist/prod/index.js +40 -16
  81. package/dist/prod/map.js +481 -211
  82. package/dist/prod/reveal.js +212 -0
  83. package/dist/prod/signals.js +401 -84
  84. package/dist/prod/store/affects.js +142 -0
  85. package/dist/prod/store/edges.js +39 -0
  86. package/dist/prod/store/hooks.js +16 -0
  87. package/dist/prod/store/index.js +27 -18
  88. package/dist/prod/store/optimistic.js +707 -0
  89. package/dist/prod/store/projection.js +331 -0
  90. package/dist/prod/store/reconcile.js +281 -0
  91. package/dist/prod/store/store.js +1952 -267
  92. package/dist/prod/store/storePath.js +1 -1
  93. package/dist/prod/store/target.js +43 -0
  94. package/dist/prod/store/types.js +83 -0
  95. package/dist/prod/store/utils.js +997 -138
  96. package/dist/types/affects-marks.d.ts +14 -0
  97. package/dist/types/affects.d.ts +12 -30
  98. package/dist/types/attribution.d.ts +19 -0
  99. package/dist/types/attribution.prod.d.ts +22 -0
  100. package/dist/types/boundaries.d.ts +99 -124
  101. package/dist/types/carved.d.ts +1 -0
  102. package/dist/types/core/action.d.ts +23 -9
  103. package/dist/types/core/async.d.ts +34 -6
  104. package/dist/types/core/attribution-costs.d.ts +35 -0
  105. package/dist/types/core/attribution-feedback.d.ts +133 -0
  106. package/dist/types/core/attribution-hooks.d.ts +307 -8
  107. package/dist/types/core/attribution-queries.d.ts +24 -0
  108. package/dist/types/core/attribution.d.ts +802 -72
  109. package/dist/types/core/constants.d.ts +195 -38
  110. package/dist/types/core/copies.d.ts +54 -0
  111. package/dist/types/core/core.d.ts +295 -65
  112. package/dist/types/core/dev.d.ts +509 -22
  113. package/dist/types/core/effect.d.ts +1 -1
  114. package/dist/types/core/error-hooks.d.ts +71 -0
  115. package/dist/types/core/error.d.ts +18 -1
  116. package/dist/types/core/graph.d.ts +40 -1
  117. package/dist/types/core/heap.d.ts +5 -5
  118. package/dist/types/core/identities.d.ts +17 -0
  119. package/dist/types/core/index.d.ts +11 -7
  120. package/dist/types/core/invariants.d.ts +1 -26
  121. package/dist/types/core/lanes.d.ts +74 -43
  122. package/dist/types/core/owner.d.ts +27 -9
  123. package/dist/types/core/scheduler.d.ts +353 -102
  124. package/dist/types/core/truth.d.ts +18 -0
  125. package/dist/types/core/types.d.ts +97 -71
  126. package/dist/types/core/verdict.d.ts +35 -0
  127. package/dist/types/deferred.d.ts +28 -0
  128. package/dist/types/flatten.d.ts +8 -0
  129. package/dist/types/from.d.ts +47 -0
  130. package/dist/types/index.d.ts +33 -7
  131. package/dist/types/map.d.ts +4 -2
  132. package/dist/types/reveal.d.ts +19 -0
  133. package/dist/types/signals.d.ts +196 -45
  134. package/dist/types/store/affects.d.ts +12 -0
  135. package/dist/types/store/edges.d.ts +9 -0
  136. package/dist/types/store/hooks.d.ts +62 -0
  137. package/dist/types/store/index.d.ts +42 -15
  138. package/dist/types/store/optimistic.d.ts +24 -45
  139. package/dist/types/store/projection.d.ts +17 -68
  140. package/dist/types/store/reconcile.d.ts +3 -46
  141. package/dist/types/store/store.d.ts +133 -136
  142. package/dist/types/store/storePath.d.ts +1 -1
  143. package/dist/types/store/target.d.ts +166 -0
  144. package/dist/types/store/types.d.ts +105 -0
  145. package/dist/types/store/utils.d.ts +188 -0
  146. package/package.json +31 -26
  147. package/dist/node.cjs +0 -8745
  148. package/dist/prod/core/optimistic.js +0 -265
  149. package/dist/prod/store/next/optimistic.js +0 -385
  150. package/dist/prod/store/next/projection.js +0 -172
  151. package/dist/prod/store/next/reconcile.js +0 -331
  152. package/dist/prod/store/next/store.js +0 -1499
  153. package/dist/prod/store/next/target.js +0 -20
  154. package/dist/types/core/optimistic.d.ts +0 -6
  155. package/dist/types/store/next/optimistic.d.ts +0 -20
  156. package/dist/types/store/next/projection.d.ts +0 -8
  157. package/dist/types/store/next/reconcile.d.ts +0 -3
  158. package/dist/types/store/next/store.d.ts +0 -77
  159. package/dist/types/store/next/target.d.ts +0 -117
  160. package/dist/types-cjs/affects.d.cts +0 -47
  161. package/dist/types-cjs/boundaries.d.cts +0 -175
  162. package/dist/types-cjs/core/action.d.cts +0 -64
  163. package/dist/types-cjs/core/async.d.cts +0 -21
  164. package/dist/types-cjs/core/attribution-hooks.d.cts +0 -52
  165. package/dist/types-cjs/core/attribution.d.cts +0 -186
  166. package/dist/types-cjs/core/constants.d.cts +0 -97
  167. package/dist/types-cjs/core/context.d.cts +0 -36
  168. package/dist/types-cjs/core/core.d.cts +0 -163
  169. package/dist/types-cjs/core/dev.d.cts +0 -81
  170. package/dist/types-cjs/core/effect.d.cts +0 -30
  171. package/dist/types-cjs/core/error.d.cts +0 -47
  172. package/dist/types-cjs/core/external.d.cts +0 -15
  173. package/dist/types-cjs/core/graph.d.cts +0 -6
  174. package/dist/types-cjs/core/heap.d.cts +0 -22
  175. package/dist/types-cjs/core/index.d.cts +0 -13
  176. package/dist/types-cjs/core/invariants.d.cts +0 -59
  177. package/dist/types-cjs/core/lanes.d.cts +0 -44
  178. package/dist/types-cjs/core/optimistic.d.cts +0 -6
  179. package/dist/types-cjs/core/owner.d.cts +0 -124
  180. package/dist/types-cjs/core/scheduler.d.cts +0 -180
  181. package/dist/types-cjs/core/types.d.cts +0 -192
  182. package/dist/types-cjs/core/verdict.d.cts +0 -2
  183. package/dist/types-cjs/index.d.cts +0 -10
  184. package/dist/types-cjs/map.d.cts +0 -69
  185. package/dist/types-cjs/package.json +0 -3
  186. package/dist/types-cjs/signals.d.cts +0 -624
  187. package/dist/types-cjs/store/index.d.cts +0 -19
  188. package/dist/types-cjs/store/next/optimistic.d.cts +0 -20
  189. package/dist/types-cjs/store/next/projection.d.cts +0 -8
  190. package/dist/types-cjs/store/next/reconcile.d.cts +0 -3
  191. package/dist/types-cjs/store/next/store.d.cts +0 -77
  192. package/dist/types-cjs/store/next/target.d.cts +0 -117
  193. package/dist/types-cjs/store/optimistic.d.cts +0 -45
  194. package/dist/types-cjs/store/projection.d.cts +0 -70
  195. package/dist/types-cjs/store/reconcile.d.cts +0 -46
  196. package/dist/types-cjs/store/store.d.cts +0 -138
  197. package/dist/types-cjs/store/storePath.d.cts +0 -58
  198. package/dist/types-cjs/store/utils.d.cts +0 -74
@@ -1,80 +1,87 @@
1
- import { STATUS_UNINITIALIZED, CONFIG_CHILD_COMPANIONS, STATUS_ERROR, STATUS_PENDING, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_DISPOSED } from "./constants.js";
1
+ import { STATUS_ERROR, STATUS_PENDING, STATUS_NO_VALUE, STATUS_UNINITIALIZED, CONFIG_GUESS, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, CONFIG_ASYNC, REACTIVE_WINDOW, CONFIG_HELD, REACTIVE_STAGED_READ, CONFIG_STAGED, CONFIG_STAGED_ERROR, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, NewSet, REACTIVE_DIRTY, CONFIG_INPUTS_PUBLISHED, NOT_PENDING, REACTIVE_DISPOSED, CONFIG_REDERIVE, CONFIG_VERDICT, CONFIG_OVERRIDE } from "./constants.js";
2
2
 
3
- import { untrack, ext, setSignal, context } from "./core.js";
4
-
5
- import "./invariants.js";
3
+ import { ext, statusNotifierOf, context, untrack, setSignal } from "./core.js";
6
4
 
7
5
  import { StatusError, NotReadyError } from "./error.js";
8
6
 
9
- import { trimStaleDeps, unobserved } from "./graph.js";
7
+ import { unobserved, trimStaleDeps } from "./graph.js";
10
8
 
11
9
  import { enqueueSub } from "./heap.js";
12
10
 
13
- import { hasActiveOverride, assignOrMergeLane, resolveLane, resolveTransition } from "./lanes.js";
14
-
15
- import { cleanup } from "./owner.js";
11
+ import { disposeChildren, cleanup } from "./owner.js";
16
12
 
17
- import { GlobalQueue, schedule, flush, queuePendingNode, insertSubs, clock, currentTransition, globalQueue } from "./scheduler.js";
13
+ import { guessing, passLane, joinFuture, txOf, holdWorkHook, question, MAINLINE_QUESTION, globalQueue, schedule, clock, flush, freshHook, laneStageHook, queuePendingNode } from "./scheduler.js";
18
14
 
19
15
  // The lazily-created Set is the ONE container for pending sources. Its
20
16
  // predecessor — a singular slot promoted to a Set on the second source —
21
17
  // created dual state whose migration invariant was easy to break: a third
22
18
  // overlapping source landed beside the Set and removePendingSource refused
23
19
  // to clear it, stranding the Set members' pending forever (#2893).
24
- function addPendingSource(e, n) {
25
- if (e.o?.le?.has(n)) return false;
26
- (ext(e).le ??= new Set).add(n);
20
+ function addPendingSource(e, t) {
21
+ if (e.T?.se?.has(t)) return false;
22
+ (ext(e).se ??= new NewSet).add(t);
27
23
  return true;
28
24
  }
29
25
 
30
- function removePendingSource(e, n) {
31
- if (!e.o?.le?.delete(n)) return false;
32
- if (e.o?.le.size === 0) if (e.o !== null) e.o.le = undefined;
26
+ function removePendingSource(e, t) {
27
+ const n = e.T?.se;
28
+ if (!n?.delete(t)) return false;
29
+ if (!n.size) e.T.se = undefined;
33
30
  return true;
34
31
  }
35
32
 
36
33
  function clearPendingSources(e) {
37
- e.o?.le?.clear();
38
- if (e.o !== null) e.o.le = undefined;
34
+ // This set is node-owned and never shared; dropping the sole reference
35
+ // releases the set and every entry without a redundant clear() walk.
36
+ if (e.T !== null) e.T.se = undefined;
37
+ }
38
+
39
+ // A rejection-pending only resolves through the settle sweep over the
40
+ // SOURCE's subscribers, so it is retryable iff a tracked read created that
41
+ // edge: a dep that IS the source, or one whose own pending chain carries it
42
+ // (pending sources propagate the origin node, so this covers any depth).
43
+ // Also guards branch-local recovery: another dependency may still need the source.
44
+ function retryReaches(e, t) {
45
+ for (let n = e.C; n; n = n.G) {
46
+ const e = n.S;
47
+ if (e === t || e.T?.se?.has(t)) return true;
48
+ }
49
+ return false;
39
50
  }
40
51
 
41
52
  /**
42
53
  * A loading-window node hit an unready source (sync throw in recompute, or a
43
54
  * NotReadyError-rejected flight): register for the source's settle — the
44
55
  * settlePendingSource walk runs off `_pendingSources` + `_blocked` alone —
45
- * with NO read-visible pending status, no downstream propagation, no
46
- * transition, no lane registration. Commit #0 keeps serving.
47
- */ function parkLoadingWindow(e, n) {
48
- ext(e).ue = true;
49
- if (n.source) addPendingSource(e, n.source);
56
+ * with NO read-visible pending status and no downstream propagation. Commit
57
+ * #0 keeps serving.
58
+ */ function parkLoadingWindow(e, t) {
59
+ ext(e).fe = true;
60
+ if (t.source) addPendingSource(e, t.source);
50
61
  // A settled error is the node's answer ("the error stays the answer until
51
62
  // this retry can actually run") — the park must not replace it: reads
52
63
  // throw `_error` while STATUS_ERROR is set, and overwriting it here leaks
53
64
  // a pending-class NotReadyError from a read-invisible park (#2989).
54
- if (!(e.S & STATUS_ERROR)) setPendingError(e, n.source, n);
65
+ if (!(e.m & STATUS_ERROR)) setPendingError(e, t.source, t);
55
66
  }
56
67
 
57
- function setPendingError(e, n, t) {
58
- if (!n) {
59
- if (e.o !== null) e.o._ = null;
68
+ function setPendingError(e, t, n) {
69
+ if (!t) {
70
+ if (e.T !== null) e.T.U = null;
60
71
  return;
61
72
  }
62
- if (t instanceof NotReadyError && t.source === n) {
63
- ext(e)._ = t;
73
+ if (n instanceof NotReadyError && n.source === t) {
74
+ ext(e).U = n;
64
75
  return;
65
76
  }
66
- const r = e.o?._;
67
- if (!(r instanceof NotReadyError) || r.source !== n) {
68
- ext(e)._ = new NotReadyError(n);
77
+ const r = e.T?.U;
78
+ if (!(r instanceof NotReadyError) || r.source !== t) {
79
+ ext(e).U = new NotReadyError(t);
69
80
  }
70
81
  }
71
82
 
72
- function forEachDependent(e, n) {
73
- for (let t = e.u; t !== null; t = t.fe) n(t.ae, t);
74
- // `?? null`: affects() marks route plain signals (no `_child` slot) through here.
75
- for (let t = e.o?.l ?? null; t !== null; t = t.ce) {
76
- for (let e = t.u; e !== null; e = e.fe) n(e.ae, e);
77
- }
83
+ function forEachDependent(e, t) {
84
+ for (let n = e.o; n !== null; n = n.t) t(n.u, n);
78
85
  }
79
86
 
80
87
  // Queue a node to re-run on the next flush (used both when a pending source
@@ -88,7 +95,7 @@ function forEachDependent(e, n) {
88
95
  // callbacks handle their own release (settleAutodispose in handleAsync); this
89
96
  // covers derivatively-pending dependents, which have no callbacks of their own.
90
97
  function releaseIfSettledUnobserved(e) {
91
- e.Se && e.T & CONFIG_AUTO_DISPOSE && !e.u && !(e.ie & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
98
+ e.X && e.l & CONFIG_AUTO_DISPOSE && !e.o && !(e.R & REACTIVE_ZOMBIE) && !(e.m & STATUS_PENDING) && unobserved(e);
92
99
  }
93
100
 
94
101
  // Error-path sweep: notifyStatus(STATUS_ERROR) clears dependents' pending
@@ -96,16 +103,16 @@ function releaseIfSettledUnobserved(e) {
96
103
  // the propagation completes, walk the same graph for stranded lazy nodes.
97
104
  // Collect-then-release so unobserved() never unlinks under the walk.
98
105
  function releaseSettledDependents(e) {
99
- let n;
100
- const t = new Set;
106
+ let t;
107
+ const n = new NewSet;
101
108
  const visit = e => {
102
- if (t.has(e)) return;
103
- t.add(e);
104
- if (!e.u && e.T & CONFIG_AUTO_DISPOSE) (n ??= []).push(e);
109
+ if (n.has(e)) return;
110
+ n.add(e);
111
+ if (!e.o && e.l & CONFIG_AUTO_DISPOSE) (t ??= []).push(e);
105
112
  forEachDependent(e, visit);
106
113
  };
107
114
  forEachDependent(e, visit);
108
- if (n) for (const e of n) releaseIfSettledUnobserved(e);
115
+ if (t) for (const e of t) releaseIfSettledUnobserved(e);
109
116
  }
110
117
 
111
118
  // Error-dimension twin of settlePendingSource's blocked re-enqueue (#2949):
@@ -117,68 +124,121 @@ function releaseSettledDependents(e) {
117
124
  // object identity down the whole dependent tree, and holding it is exactly
118
125
  // the "blocked on this error" marker — re-enqueue those holders so they
119
126
  // re-run: fresh values commit and flow, and a dependent with another
120
- // still-broken source simply re-errors. The async dimension needs no twin of
121
- // its own: recovery there passes through a pending window whose re-runners
122
- // set _blocked and ride settlePendingSource. Walks the full dependent graph
127
+ // still-broken source simply re-errors. Pending recovery uses
128
+ // settlePendingSource to clear inherited status and retry blocked readers.
129
+ // Walks the full dependent graph
123
130
  // (releaseSettledDependents shape): identity holders can sit below an
124
131
  // intermediate whose own error state has since been scrubbed or replaced
125
132
  // (e.g. an error boundary's tree node).
126
- function settleErroredDependents(e, n) {
127
- let t = false;
128
- const r = new Set;
133
+ /** D-41: an error a pass of a held node found is an answer of
134
+ * that frame — it ends the wait like a value and shows when a value would.
135
+ * Staged like one: `_pendingValue` carries it (CONFIG_STAGED_ERROR), readers
136
+ * in the hold derive from it (`serve` throws it), and the commit publishes
137
+ * it through the ordinary error path (`commitPendingNode` → `notifyStatus`),
138
+ * dependents included. Not for a lane pass, nor a node with no shown value
139
+ * (born held — its error has no frame to wait behind): those publish now,
140
+ * as before. Returns whether the error was staged. */ function stageError(e, t, n) {
141
+ // Held, or — for a pass's own throw — derived from a staging of a flush
142
+ // (REACTIVE_STAGED_READ is this pass's; a landing after the pass cannot
143
+ // read it, the flush it read a staging of has committed or parked since).
144
+ // Staged whether or not the flush has joined a hold yet: a pass that runs
145
+ // before its async sibling joins is the seam's to decide, like a value
146
+ // (#4109). A flush that parks nothing publishes it at its commit (S-38).
147
+ // Not a projection's derive: its readers learn its error through the
148
+ // family, which the commit's publish never reaches (#4189).
149
+ if (!(e.l & CONFIG_HELD || n && e.R & REACTIVE_STAGED_READ && globalQueue.Y && !e.fam) || passLane) return false;
150
+ // The node answered (an error ends the wait — no longer pending, nor
151
+ // blocked); a shown error stays its display until the commit.
152
+ clearPendingSources(e);
153
+ if (e.T) e.T.fe = false;
154
+ if (!((e.m &= STATUS_ERROR | STATUS_UNINITIALIZED) & STATUS_ERROR)) setPendingError(e);
155
+ e.ue = t instanceof StatusError ? t : new StatusError(e, t);
156
+ e.l |= CONFIG_STAGED | CONFIG_STAGED_ERROR;
157
+ // Readers parked on the flight re-run and derive from the staging; the
158
+ // verdict readers the walk wakes need a flush (#4106).
159
+ settlePendingSource(e);
160
+ schedule();
161
+ return true;
162
+ }
163
+
164
+ function settleErroredDependents(e, t) {
165
+ let n = false;
166
+ const r = new NewSet;
129
167
  const visit = e => {
130
168
  if (r.has(e)) return;
131
169
  r.add(e);
132
- if (e.o?._ === n) {
170
+ if (e.T?.U === t) {
133
171
  enqueueSub(e);
134
- t = true;
172
+ n = true;
135
173
  }
136
174
  forEachDependent(e, visit);
137
175
  };
138
176
  forEachDependent(e, visit);
139
- if (t) schedule();
177
+ if (n) schedule();
140
178
  }
141
179
 
142
- function settlePendingSource(e) {
180
+ // Retire `source` from pending state along the dependent graph rooted at `el`.
181
+ // By default, `el` is the source whose flight settled or was superseded.
182
+ // With a distinct `source`, `el` is a recovered computation that dropped it:
183
+ // the source may still be pending, so dependents with another path to it stay pending.
184
+ function settlePendingSource(e, t = e) {
185
+ // Landing and branch recovery already cleared el's own set. Superseded
186
+ // re-parks can retain an abandoned self entry (source === el), which must
187
+ // retire in the same walk as its propagated copies.
188
+ removePendingSource(e, t);
143
189
  let n = false;
144
- let t;
145
- const r = new Set;
146
- // Companion updates no-op without the verdict layer (null hook).
147
- const o = GlobalQueue.de;
148
- const settle = l => {
149
- if (r.has(l) || !removePendingSource(l, e)) return;
150
- r.add(l);
151
- l.Te = clock;
152
- const u = l.o?.le?.values().next().value;
190
+ let r;
191
+ const o = new NewSet;
192
+ const settle = s => {
193
+ if (o.has(s) ||
194
+ // A conditional dropped this source, but another dependency can still
195
+ // carry it. Only retire pending state inherited through the recovered
196
+ // branch. Deliberately NOT marked visited on this early return: the
197
+ // carrying dependency may itself be a later branch of this same walk
198
+ // (two unchanged memos converging), and its visit must be free to
199
+ // re-examine this node once that branch has retired the source.
200
+ t !== e && retryReaches(s, t) || !removePendingSource(s, t))
201
+ // A19: a verdict reader holds no pending of its own (it was re-derived
202
+ // when the source went pending, `propagateStatus`), and a landing
203
+ // equal to the committed value notifies nobody — the source settling
204
+ // is its verdict changing: it runs again. (Verdict readers exist only
205
+ // with the verdict engine installed.)
206
+ return holdWorkHook && s.l & CONFIG_VERDICT && enqueueSub(s);
207
+ o.add(s);
208
+ s.ie = clock;
209
+ const f = s.T?.se?.values().next().value;
153
210
  // STATUS_ERROR + pending sources only coexist via an errored loading
154
211
  // window's park (notifyStatus(STATUS_ERROR) clears pending sources
155
212
  // otherwise): the settled error stays the answer through the settle —
156
213
  // nulling it here would have reads throw `null` until the re-enqueued
157
214
  // retry lands, or lose it entirely if that retry parks again (#2989).
158
- const i = l.S & STATUS_ERROR;
159
- if (u) {
160
- if (!i) setPendingError(l, u);
161
- o !== null && o(l);
215
+ const u = s.m & STATUS_ERROR;
216
+ if (f) {
217
+ if (!u) setPendingError(s, f);
162
218
  } else {
163
- l.S &= ~STATUS_PENDING;
164
- if (!i) setPendingError(l);
165
- o !== null && o(l);
166
- if (l.o?.ue) {
167
- enqueueSub(l);
219
+ s.m &= ~STATUS_PENDING;
220
+ if (!u) setPendingError(s);
221
+ // L2: a landing resumes the transaction waiting on it (maintainer: "it
222
+ // resumes the sync frame") — a held reader's re-pass over the landed
223
+ // value is that transaction's work, and the flush that carries it
224
+ // parks into it.
225
+ if (s.l & CONFIG_HELD) joinFuture(txOf(s));
226
+ if (s.T?.fe) {
227
+ enqueueSub(s);
168
228
  n = true;
169
229
  }
170
- if (l.o !== null) l.o.ue = false;
230
+ if (s.T !== null) s.T.fe = false;
171
231
  // Fully settled with nobody watching: release candidate (#2934). Checked
172
232
  // again at release time — deferred so unobserved() can't unlink subs
173
233
  // lists this walk is still iterating.
174
- if (!l.u && l.T & CONFIG_AUTO_DISPOSE) (t ??= []).push(l);
234
+ if (!s.o && s.l & CONFIG_AUTO_DISPOSE) (r ??= []).push(s);
175
235
  }
176
- forEachDependent(l, settle);
236
+ forEachDependent(s, settle);
177
237
  };
178
238
  forEachDependent(e, settle);
179
239
  // Release before the flush schedule below: unobserved() pulls the node back
180
240
  // out of the heap, so the enqueueSub above never recomputes a released node.
181
- if (t) for (const e of t) releaseIfSettledUnobserved(e);
241
+ if (r) for (const e of r) releaseIfSettledUnobserved(e);
182
242
  if (n) schedule();
183
243
  }
184
244
 
@@ -187,142 +247,168 @@ function isThenable(e) {
187
247
  return e != null && typeof e === "object" && typeof e.then === "function";
188
248
  }
189
249
 
190
- function handleAsync(e, n, t) {
191
- let r = false;
250
+ /** Fire and clear a node's iterator-flight cancellation hook (#3122). */ function releaseFlightTeardown(e) {
251
+ const t = e.T?.Se;
252
+ if (t != null) {
253
+ e.T.Se = null;
254
+ t();
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Internal (not a package export): run `fn` when the current pass's flight
260
+ * is superseded, at the same instant as the iterator close above, instead of
261
+ * when the replacing pass commits (owner cleanups wait for that). Chains onto
262
+ * the node's teardown slot; fires once with it. A no-op unless the ambient
263
+ * owner is a computed in its own pass. `consumeIterator` fills the slot
264
+ * before its first pull, so a source opened in an async generator's body
265
+ * (`for await`) composes after the generator's close.
266
+ */ function onSupersede(e) {
267
+ const t = context;
268
+ if (t === null || t.X === undefined || !(t.R & REACTIVE_RECOMPUTING_DEPS)) return;
269
+ const n = t.T === null;
270
+ const r = ext(t);
271
+ // Allocating the extension mid-run turns `_x?._inFlight` from undefined into
272
+ // null, which recompute would read as a flight this body registered itself.
273
+ // Restore undefined here, so the fix costs nothing for code that never
274
+ // calls onSupersede.
275
+ if (n) r.Ee = undefined;
276
+ const o = r.Se;
277
+ r.Se = o === null ? e : () => {
278
+ try {
279
+ o();
280
+ } finally {
281
+ e();
282
+ }
283
+ };
284
+ }
285
+
286
+ function handleAsync(e, t, n,
287
+ // The flight rejected (after the node's status is set): a projection
288
+ // tells its leaves' readers, who are not the node's dependents
289
+ // (store/projection.ts). `pending`: a NotReady rejection — the node is
290
+ // pending on another source, not errored.
291
+ r) {
192
292
  let o = false;
193
- if (typeof n === "object" && n !== null) {
293
+ let s = false;
294
+ if (typeof t === "object" && t !== null) {
194
295
  untrack(() => {
195
- r = n[Symbol.asyncIterator];
196
- o = !r && isThenable(n);
296
+ o = t[Symbol.asyncIterator];
297
+ s = !o && isThenable(t);
197
298
  });
198
299
  }
199
- if (!o && !r) {
200
- if (e.o !== null) e.o.Ee = null;
300
+ if (!s && !o) {
301
+ if (e.T !== null) {
302
+ e.T.Ee = null;
303
+ if (holdWorkHook) e.l &= ~CONFIG_ASYNC;
304
+ }
201
305
  // A sync landing is the first real answer for a loadingValue node.
202
- e.Ie = false;
203
- return n;
306
+ e.ce = false;
307
+ return t;
204
308
  }
205
- ext(e).Ee = n;
206
- let l;
207
- // Settle-time transition re-entry. The loading rail is invisible to
208
- // transactions (#2933): a boundary-caught first load never registers as an
209
- // async reporter, so its settle — the boundary's fallback -> content
210
- // reveal — must flow ambiently. The node can still carry a `_transition`
211
- // stamp (pending-node bookkeeping rides through the stamping sites), and
212
- // blindly re-entering that stamped, still-incomplete transaction stashed
213
- // the reveal with it — a deadlock when the transaction's completion
214
- // depended on the reveal (#2937). An ESCAPED first load did register and
215
- // keeps transition scheduling; initialized (value-holding) pending settles
216
- // are the transaction's reveal machinery and always re-enter.
217
- const settleTransition = () => {
218
- const n = resolveTransition(e);
219
- if (n && e.S & STATUS_UNINITIALIZED && !currentTransition(n).Ne.has(e)) {
220
- // Drop the stale stamp too: the plain settle write (setSignal) and the
221
- // stash-path restamp both re-enter the transaction through it.
222
- e._e = null;
223
- return;
224
- }
225
- globalQueue.initTransition(n);
226
- };
227
- const handleError = t => {
228
- if (e.o?.Ee !== n) return;
309
+ // Flight replacement relies on recompute's supersede release for iterator
310
+ // teardown (#3122): every handleAsync call — including the projection
311
+ // self-registration — runs during a recompute of `el`, which has already
312
+ // fired _flightTeardown. A future non-recompute registration path must
313
+ // release it here before overwriting _inFlight.
314
+ ext(e).Ee = t;
315
+ // D-25 (verdict.ts `holdWork`): an async memo whose answering pass opened
316
+ // no verdict window of its own (`markVerdictReader` clears the bit).
317
+ // Kept by the verdict engine alone (`holdWorkHook`): a build without it
318
+ // folds the bit's upkeep away.
319
+ if (holdWorkHook && !(e.R & REACTIVE_WINDOW)) e.l |= CONFIG_ASYNC;
320
+ // The question this flight answers (A18 provenance, lanes.ts): the action
321
+ // whose slice asked it; mainline is always the current question.
322
+ e.T.Ie = question || MAINLINE_QUESTION;
323
+ // The run that asked this flight read every input without throwing: an
324
+ // input still in flight was masked for it (an active override, A17), so
325
+ // pending state those inputs propagated onto the node earlier does not
326
+ // describe this answer. Drop it — the flight is the node's pending now.
327
+ // The landing retires only the flight's own entry (landStatus, #3373), so
328
+ // an entry that survived here would hold the node past its own answer.
329
+ e.T.se = undefined;
330
+ let f;
331
+ const handleError = o => {
332
+ if (e.T?.Ee !== t) return;
229
333
  // NotReadyError from rejected promises should be treated as pending, not error
230
- let r = t instanceof NotReadyError;
231
- if (r && e.Ie) {
334
+ let s = o instanceof NotReadyError;
335
+ if (s && e.ce) {
232
336
  // Loading window: the flight died waiting on an unready source. Keep
233
337
  // serving commit #0 — same parking as recompute's catch for sync
234
338
  // dependency throws. The dead flight is released so the clock-gated
235
339
  // error-retry pull (updateIfNecessary) can also re-ask.
236
- if (e.o !== null) e.o.Ee = null;
237
- parkLoadingWindow(e, t);
238
- e.Te = clock;
340
+ if (e.T !== null) e.T.Ee = null;
341
+ parkLoadingWindow(e, o);
342
+ e.ie = clock;
239
343
  return;
240
344
  }
241
- settleTransition();
242
- notifyStatus(e, r ? STATUS_PENDING : STATUS_ERROR, t);
243
- e.Te = clock;
345
+ // D-41: a held node's error is staged (`stageError`). A derived store's
346
+ // readers learn its error through the family, not the derive's
347
+ // dependents: it publishes now.
348
+ if (s || n || !stageError(e, o)) {
349
+ notifyStatus(e, s ? STATUS_PENDING : STATUS_ERROR, o);
350
+ // The error is this pass's answer, published now: it replaces the shown
351
+ // frame as a value's commit would (`commitPendingNode`), so the frame
352
+ // the pass parked goes instead of waiting for a later success. A no-op
353
+ // when nothing is parked.
354
+ if (!s) disposeChildren(e, false, true);
355
+ }
356
+ // A NotReady rejection is a landing into another pending source. The
357
+ // rejected flight will never settle its self entry, so transfer ownership
358
+ // after notifyStatus has propagated the replacement source.
359
+ if (s) settlePendingSource(e);
360
+ e.ie = clock;
244
361
  // A real error settles derivatively-pending dependents (notifyStatus
245
362
  // cleared their pending sources), so stranded lazy ones release here —
246
363
  // the error twin of settlePendingSource's release (#2934).
247
- if (!r) releaseSettledDependents(e);
364
+ if (!s) releaseSettledDependents(e);
365
+ r?.(o, s);
248
366
  };
249
367
  const asyncWrite = (r, o) => {
250
- if (e.o?.Ee !== n) return;
368
+ if (e.T?.Ee !== t) return;
251
369
  // If the node was dirtied by a newer write (optimistic override or regular),
252
370
  // skip this stale async result — the upcoming flush will recompute the node
253
371
  // with the new value, creating a fresh Promise that supersedes this one.
254
- if (e.ie & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
255
- settleTransition();
256
- const l = !!(e.S & STATUS_UNINITIALIZED);
257
- trimStaleDeps(e);
258
- clearStatus(e);
259
- const u = resolveLane(e);
260
- if (u) u.Ae.delete(e);
261
- if (t) {
262
- t(r);
263
- if (l) clearStatus(e, true);
264
- } else if (e.o?.De !== undefined) {
265
- // Optimistic node — resting OR covered by an active override — holds
266
- // through the shared pending-node path, exactly like a plain async memo,
267
- // so the commit clears STATUS_UNINITIALIZED (#2806) and elevation to
268
- // _value happens on this value's OWN transition schedule (A18 as
269
- // re-ruled 2026-07-07: _value only changes at commit points). With an
270
- // override active the hold and its eventual commit are unobservable
271
- // (A17 — every reader sees the override); the revert reveals whatever
272
- // has committed by then, so corrections reveal atomically with their
273
- // transition rather than escaping it.
274
- if (e.Pe === NOT_PENDING) queuePendingNode(e);
275
- e.Pe = r;
276
- // The hold is a companion-visible write like any other (A13/A19): the
277
- // clearStatus() above computed its verdict before the hold existed, so
278
- // isPending must re-derive (the value is not final until commit — V1)
279
- // and latest() must see the fresh in-flight value (V2). Subscribers are
280
- // only notified when the hold is visible to them: under an active
281
- // override every reader sees the override (A17), so waking subs would
282
- // re-show an unchanged view — the revert is the notification point.
283
- GlobalQueue.Oe !== null && GlobalQueue.Oe(e, r);
284
- if (!hasActiveOverride(e)) {
285
- insertSubs(e);
286
- }
287
- e.Te = clock;
288
- } else if (u) {
289
- // Route through lane's effect queue for independent flushing
290
- const n = e.Re;
291
- const t = e.be;
292
- const o = e.Ue;
372
+ if (e.R & REACTIVE_DIRTY) return;
373
+ const s = e.m & STATUS_UNINITIALIZED;
374
+ landStatus(e);
375
+ if (n) {
293
376
  try {
294
- if (!n && l || !o || !o(r, t)) {
295
- e.be = r;
296
- e.Te = clock;
297
- // The latest() shadow write gives latest() effects independent lanes; the
298
- // _pendingSignal update is a no-op repeat of the clearStatus() call above
299
- // (computePendingState doesn't read _value).
300
- GlobalQueue.Oe !== null && GlobalQueue.Oe(e, r);
301
- insertSubs(e, true);
302
- }
303
- } catch (n) {
304
- // A user comparator throwing during async resolution has no caller to
305
- // surface to (we're in promise machinery) — route it through the node's
306
- // error status so boundaries contain it instead of an unhandled
307
- // rejection (#2837).
308
- notifyStatus(e, STATUS_ERROR, n);
377
+ n(r);
378
+ } catch (e) {
379
+ handleError(e);
380
+ return;
309
381
  }
310
- } else {
311
- try {
312
- setSignal(e, () => r);
313
- } catch (n) {
314
- // Same containment as above: setSignal's comparator throw is the only
315
- // pre-commit failure here, and there is no user callsite to throw to.
316
- notifyStatus(e, STATUS_ERROR, n);
382
+ // A store derive's landing commits through its setter: unheld, the
383
+ // shown error leaves now, and so does a commit-beneath-a-flight's
384
+ // CONFIG_INPUTS_PUBLISHED (its first flight's, #4269) — left standing,
385
+ // a verdict reader of the next flight would suspend instead of being
386
+ // served the screen; held, both at the derive's commit.
387
+ if (!(e.l & CONFIG_HELD)) {
388
+ e.T.Te = null;
389
+ e.l &= ~CONFIG_INPUTS_PUBLISHED;
317
390
  }
391
+ if (s) landStatus(e, true);
392
+ } else {
393
+ // CARVE 2: the override-covered landing (hold + A18 supersession) and the
394
+ // lane-routed landing (derived override, lane effect queue) went with
395
+ // the optimistic engine; every landing is the plain setSignal. A node
396
+ // already held stages its answer into that hold (`setSignal`). Its
397
+ // comparator throw is contained there (`isSame`, A3).
398
+ setSignal(e, () => r);
318
399
  }
319
400
  // First real answer landing: the window closes when the answer becomes
320
- // OBSERVABLE. A direct commit is observable now; a transition-held write
321
- // (`_pendingValue` set above or inside setSignal) is not — the verdict's
322
- // held-value branch is window-gated, and commitPendingNode closes the
323
- // window when the hold commits, so no one-frame isPending pulse can leak
324
- // to live observers between the landing and its commit (#2990).
325
- if (e.Pe === NOT_PENDING) e.Ie = false;
401
+ // OBSERVABLE. A direct commit is observable now; a staged write
402
+ // (`_pendingValue` set inside setSignal) is not — commitPendingNode closes
403
+ // the window when the flush commits it, so no one-frame pulse can leak to
404
+ // observers between the landing and its commit (#2990).
405
+ if (e.ue === NOT_PENDING) {
406
+ e.ce = false;
407
+ // The landing published: the dependency tail the flight's pass left
408
+ // linked goes now (A30, #3410). A staged landing has not replaced the
409
+ // committed frame yet; `commitPendingNode` trims it.
410
+ trimStaleDeps(e);
411
+ }
326
412
  settlePendingSource(e);
327
413
  schedule();
328
414
  flush();
@@ -336,7 +422,7 @@ function handleAsync(e, n, t) {
336
422
  // Returns whether the node released, so the iterator branch can stop
337
423
  // pulling values instead of pumping an unobserved stream forever (#2935).
338
424
  const settleAutodispose = () => {
339
- if (e.T & CONFIG_AUTO_DISPOSE && !e.u && !(e.S & STATUS_PENDING)) {
425
+ if (e.l & CONFIG_AUTO_DISPOSE && !e.o && !(e.m & STATUS_PENDING)) {
340
426
  unobserved(e);
341
427
  return true;
342
428
  }
@@ -349,42 +435,56 @@ function handleAsync(e, n, t) {
349
435
  // close registration uses the ambient owner; and DEFERRED (the flattening
350
436
  // path — a thenable resolved to an iterable in a later microtask), where
351
437
  // there is no caller to serve and no ambient owner: sync-settled steps
352
- // write through asyncWrite, and close registration goes through the slot
353
- // the thenable branch pre-registered while it still owned the context.
354
- // Returns whether a sync answer landed (first yield or empty completion) —
355
- // meaningful only in the live posture.
356
- const consumeIterator = (t, r) => {
357
- const o = t[Symbol.asyncIterator]();
438
+ // write through asyncWrite, and close registration targets `owner` (el
439
+ // itself) directly. Returns whether a sync answer landed (first yield or
440
+ // empty completion) — meaningful only in the live posture.
441
+ const consumeIterator = (n, r) => {
442
+ const o = n[Symbol.asyncIterator]();
443
+ let s = false;
358
444
  let u = false;
359
- let i = false;
360
- let s = !r;
445
+ let i = !r;
361
446
  const close = () => {
362
- if (i) return;
363
- i = true;
447
+ if (u) return;
448
+ u = true;
364
449
  try {
365
450
  const e = o.return?.();
366
451
  if (isThenable(e)) e.then(undefined, () => {});
367
452
  } catch {}
368
453
  };
369
- r ? r(close) : cleanup(close);
454
+ cleanup(close, r);
455
+ // Flight-identity cancellation (#3122): the registration above is the
456
+ // owner-death backstop; the teardown slot fires at the _inFlight release
457
+ // sites so supersede stops this stream immediately. Assigned, not
458
+ // chained (chaining here costs every app bytes): it replaces any
459
+ // `onSupersede` teardown this run registered earlier, which then falls
460
+ // back to the owner-cleanup channel. A returned `from()` is unaffected:
461
+ // `close` calls its `return()`, which runs its own teardown.
462
+ ext(e).Se = close;
370
463
  // Release check before each next pull: an unobserved lazy node must tear
371
464
  // down (its close above runs via disposal, closing the iterator) instead
372
465
  // of pumping the stream forever with zero subscribers (#2935).
466
+ // Later pulls run from an async continuation with no compute catch above
467
+ // them: a throw invoking the protocol (next(), the step's then) settles
468
+ // as this flight's error instead of escaping as a rejection (#4112).
373
469
  const iterateOrRelease = () => {
374
- if (!settleAutodispose()) iterate();
470
+ try {
471
+ if (!settleAutodispose()) iterate();
472
+ } catch (e) {
473
+ handleError(e);
474
+ }
375
475
  };
376
476
  const iterate = () => {
377
- let t, r, f = false, a = false, c = true;
477
+ let n, r, l = false, S = false, a = true;
378
478
  // Protocol tolerance, matching `for await`: `await` unwraps whatever
379
479
  // next() returns — a thenable OR a bare IteratorResult. Real producers
380
480
  // use the bare form as a promise-free fast path when a value is already
381
481
  // buffered (seroval's deserialized streams do), so a bare result is
382
482
  // assimilated as an already-settled step instead of crashing on `.then`.
383
- const S = o.next();
384
- const d = isThenable(S) ? S : {
385
- then: e => void e(S)
483
+ const E = o.next();
484
+ const c = isThenable(E) ? E : {
485
+ then: e => void e(E)
386
486
  };
387
- d.then(r => {
487
+ c.then(r => {
388
488
  // The sync stash only serves the INITIAL drain (handleAsync's caller
389
489
  // consumes syncValue / throws NotReady from it). A sync-settled step
390
490
  // after an async gap — seroval buffering values between pulls, a
@@ -392,18 +492,18 @@ function handleAsync(e, n, t) {
392
492
  // stash: it must write through the async path or the value is
393
493
  // silently dropped. (The deferred posture never has a caller, so
394
494
  // initialRead starts false there and everything writes through.)
395
- if (c && s) {
396
- t = r;
397
- f = true;
398
- if (r.done) i = true;
399
- } else if (e.o?.Ee !== n) {
495
+ if (a && i) {
496
+ n = r;
497
+ l = true;
498
+ if (r.done) u = true;
499
+ } else if (e.T?.Ee !== t) {
400
500
  return;
401
501
  } else if (!r.done) {
402
- u = true;
502
+ s = true;
403
503
  asyncWrite(r.value, iterateOrRelease);
404
504
  } else {
405
- i = true;
406
- if (u) {
505
+ u = true;
506
+ if (s) {
407
507
  schedule();
408
508
  flush();
409
509
  } else {
@@ -412,35 +512,35 @@ function handleAsync(e, n, t) {
412
512
  }
413
513
  settleAutodispose();
414
514
  }
415
- }, t => {
416
- if (c && s) {
417
- r = t;
418
- a = true;
419
- } else if (e.o?.Ee === n) {
420
- i = true;
421
- handleError(t);
515
+ }, n => {
516
+ if (a && i) {
517
+ r = n;
518
+ S = true;
519
+ } else if (e.T?.Ee === t) {
520
+ u = true;
521
+ handleError(n);
422
522
  settleAutodispose();
423
523
  }
424
524
  });
425
- c = false;
426
- if (a) {
525
+ a = false;
526
+ if (S) {
427
527
  // Match the promise branch, but only rethrow during the initial read.
428
- i = true;
528
+ u = true;
429
529
  handleError(r);
430
- if (s) throw r;
530
+ if (i) throw r;
431
531
  return true;
432
532
  }
433
- if (f && !t.done) {
434
- l = t.value;
435
- u = true;
533
+ if (l && !n.done) {
534
+ f = n.value;
535
+ s = true;
436
536
  return iterate();
437
537
  }
438
- return f && t.done;
538
+ return l && n.done;
439
539
  };
440
- const f = iterate();
540
+ const l = iterate();
441
541
  // Later iterate() calls run from asyncWrite, where rethrowing would be unhandled.
442
- s = false;
443
- return u || f;
542
+ i = false;
543
+ return s || l;
444
544
  };
445
545
  // Landed-synchronously verdict for a LIVE iterator drain; null when no live
446
546
  // drain ran (plain promise flight, or a deferred flatten). Drives the
@@ -451,43 +551,45 @@ function handleAsync(e, n, t) {
451
551
  // stream itself rather than settling on the iterable object. One level
452
552
  // only: A+ `then` already collapses nested thenables, so the resolved
453
553
  // value is never itself a thenable.
454
- const flattenIfIterable = (e, n) => {
455
- let t = false;
554
+ const flattenIfIterable = (e, t) => {
555
+ let n = false;
456
556
  if (typeof e === "object" && e !== null) {
457
557
  untrack(() => {
458
- t = e[Symbol.asyncIterator];
558
+ n = e[Symbol.asyncIterator];
459
559
  });
460
560
  }
461
- if (!t) return false;
462
- const r = consumeIterator(e, n);
463
- if (!n) u = r;
561
+ if (!n) return false;
562
+ f = undefined;
563
+ const r = consumeIterator(e, t);
564
+ if (!t) u = r;
464
565
  return true;
465
566
  };
466
- if (o) {
467
- let t = false, r = false, o, u = true;
468
- // Close registration for the flattening path. Consumption starts in a
469
- // microtask where the ambient owner is gone (or worse, someone else's),
470
- // so `cleanup()` can't be used — the close targets el's disposal list
471
- // directly, exactly where a live cleanup() during this recompute would
472
- // have put it. Deliberately NOT pre-registered at flight start: a
473
- // non-null `_disposal` reclassifies the node into recompute's deferred
474
- // (zombie) disposal path, and plain promise flights — the overwhelming
475
- // majority — must not pay that. Only a flight that actually flattens
476
- // becomes disposal-bearing, which is exactly the class a directly
477
- // returned iterable already occupies.
478
- const registerDeferredClose = n => {
479
- if (!e.he) e.he = n; else if (Array.isArray(e.he)) e.he.push(n); else e.he = [ e.he, n ];
480
- };
481
- n.then(r => {
482
- if (u) {
483
- l = r;
484
- t = true;
485
- } else if (e.o?.Ee === n && !(e.ie & REACTIVE_DISPOSED) && flattenIfIterable(r, registerDeferredClose)) ; else {
486
- asyncWrite(r);
567
+ if (s) {
568
+ let n = false, r = false, o, s = true;
569
+ t.then(r => {
570
+ if (s) {
571
+ f = r;
572
+ n = true;
573
+ } else {
574
+ // Flattening consumes the stream as the value: each yield lands
575
+ // through asyncWrite under this flight's identity; the first one
576
+ // clears pending exactly like a plain promise resolution would.
577
+ // Disposed nodes never start a pump (their disposal list already
578
+ // ran, so nothing could close the iterator). Consumption starts in
579
+ // a microtask where the ambient owner is gone, so the close
580
+ // registers on el's own disposal list. Iterator setup here has no
581
+ // compute catch above it: a protocol throw settles as this
582
+ // flight's error (#4112).
583
+ try {
584
+ if (e.T?.Ee === t && !(e.R & REACTIVE_DISPOSED) && flattenIfIterable(r, e)) return;
585
+ asyncWrite(r);
586
+ } catch (e) {
587
+ handleError(e);
588
+ }
487
589
  settleAutodispose();
488
590
  }
489
591
  }, e => {
490
- if (u) {
592
+ if (s) {
491
593
  o = e;
492
594
  r = true;
493
595
  } else {
@@ -495,115 +597,199 @@ function handleAsync(e, n, t) {
495
597
  settleAutodispose();
496
598
  }
497
599
  });
498
- u = false;
600
+ s = false;
499
601
  if (r) {
500
602
  // Settle through the same status path an async rejection uses, then
501
603
  // unwind the in-progress synchronous read so the errored node isn't
502
604
  // momentarily read as `undefined`.
503
605
  handleError(o);
504
606
  throw o;
505
- } else if (!t) {
506
- // Loading window: serve commit #0 instead of suspending. No transition
507
- // is opened — first-flight work on a loadingValue node is loading-class
508
- // (invisible to boundaries and transitions); the flight itself is
509
- // already registered in _inFlight and lands through asyncWrite.
510
- if (e.Ie) return e.be;
511
- globalQueue.initTransition(resolveTransition(e));
512
- throw new NotReadyError(context);
513
- } else if (!flattenIfIterable(l)) {
607
+ } else if (!n) {
608
+ // Not landed: the shared tail below suspends, or — loading window —
609
+ // serves commit #0 (first-flight work on a loadingValue node is
610
+ // loading-class, invisible to boundaries; the flight itself is already
611
+ // registered in _inFlight and lands through asyncWrite). A promise is
612
+ // never an iterator, so nothing between here and there runs.
613
+ u = false;
614
+ } else if (!flattenIfIterable(f)) {
514
615
  // Synchronously-resolved promise: the first real answer landed.
515
- e.Ie = false;
616
+ e.ce = false;
516
617
  }
517
618
  // A sync-resolved promise holding an AsyncIterable flattened LIVE (we
518
619
  // are still inside the synchronous read): full initial-drain semantics
519
620
  // apply and the shared tail below settles the verdict.
520
621
  }
521
- if (r) flattenIfIterable(n);
622
+ if (o) flattenIfIterable(t);
522
623
  if (u !== null) {
523
624
  if (!u) {
524
- // Loading window: serve commit #0 (see the promise branch above).
525
- if (e.Ie) return e.be;
526
- globalQueue.initTransition(resolveTransition(e));
625
+ // Loading window: serve commit #0 instead of suspending.
626
+ if (e.ce) return e.le;
527
627
  throw new NotReadyError(context);
528
628
  }
529
629
  // A sync first yield (or immediate empty completion) is the first real
530
630
  // answer; async yields clear inside asyncWrite.
531
- e.Ie = false;
631
+ e.ce = false;
532
632
  }
533
- return l;
633
+ return f;
534
634
  }
535
635
 
536
- function clearStatus(e, n = false) {
537
- if (e.o?.le) clearPendingSources(e);
538
- if (e.o?.ue) if (e.o !== null) e.o.ue = false;
539
- // The pending window is over; its quiet classification dies with it.
540
- // (Unconditional: _reask is baked into the node literals, so this is a
541
- // plain store to an existing slot — no shape change.)
542
- if (e.o !== null) e.o.pe = false;
543
- e.S = n ? 0 : e.S & STATUS_UNINITIALIZED;
544
- if (e.o?._) setPendingError(e);
545
- // Update pending signal for isPending() reactivity (companions only exist
546
- // once the verdict layer created them, which installs the hooks).
547
- if (e.o?.Ge || e.o?.ge) GlobalQueue.de(e);
548
- if (e.o?.l && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.ye !== null) GlobalQueue.ye(e);
549
- if (e.o?.A) e.o.A.call(e);
636
+ function clearStatus(e, t = false) {
637
+ if (e.T?.se) clearPendingSources(e);
638
+ if (e.T?.fe) e.T.fe = false;
639
+ e.m = t ? 0 : e.m & STATUS_UNINITIALIZED;
640
+ if (e.T?.U) setPendingError(e);
641
+ const n = statusNotifierOf(e);
642
+ if (n) n.call(e);
550
643
  }
551
644
 
552
- function notifyStatus(e, n, t, r, o) {
645
+ /**
646
+ * Status clear for a flight LANDING (asyncWrite). A landing answers the
647
+ * node's OWN question — it retires the node's self entry, not the pending
648
+ * state its sources propagated onto it. An input re-asked while this flight
649
+ * was up (a second write to the signal feeding `a` while `b`'s first flight
650
+ * is in the air, #3373) marks `b` pending on `a` by propagation, with `b`'s
651
+ * flight still current: nothing superseded it (the re-ask only changed `a`'s
652
+ * status, not yet its value), so the landing arrives, and a full clear made
653
+ * `b` answer with the stale value — the newer signal committed beside the
654
+ * older derived value (`2 / 1`) for the gap (#3376).
655
+ * With another source still pending the node stays derivatively pending on
656
+ * it; the landed value is written below (the staged answer is still the
657
+ * answer for the inputs it was asked with) and the input's own settle
658
+ * releases it, or its value change recomputes the node into a fresh flight.
659
+ * `_blocked` clears like a full clear: a landing that passed the `_inFlight`
660
+ * guard was not superseded by a re-run (recompute nulls `_inFlight` first),
661
+ * so the flag is the flight's own registration throw — the input settling
662
+ * unchanged must not re-run the node (an extra flight for the same inputs).
663
+ * The node is already STATUS_PENDING in that branch (only notifyStatus fills
664
+ * the set, with status; a loading-window park cannot coexist with a live
665
+ * flight since registration drops the set), so the flags only change when
666
+ * the first landing retires UNINITIALIZED. `_error` must move off self: a
667
+ * reader thrown NotReady(self) would park on a retired entry. Companions
668
+ * keep their verdict (pending before and after; the write re-syncs them).
669
+ */ function landStatus(e, t = false) {
670
+ const n = e.T?.se;
671
+ // (The full clear below drops the set whether or not self was retired first.)
672
+ if (n && (n.delete(e), n.size)) {
673
+ e.T.fe = false;
674
+ if (t) e.m = STATUS_PENDING;
675
+ setPendingError(e, n.values().next().value);
676
+ } else clearStatus(e, t);
677
+ }
678
+
679
+ function notifyStatus(e, t, n) {
553
680
  // Wrap regular errors to track source node
554
- if (n === STATUS_ERROR && !(t instanceof StatusError) && !(t instanceof NotReadyError)) t = new StatusError(e, t);
555
- const l = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
556
- const u = l === e;
557
- const i = n === STATUS_PENDING && e.o?.De !== undefined && !u;
558
- const s = i && hasActiveOverride(e);
559
- if (!r) {
560
- if (n === STATUS_PENDING && l) {
561
- addPendingSource(e, l);
562
- e.S = STATUS_PENDING | e.S & STATUS_UNINITIALIZED;
563
- // Preserve the current source on this propagation so render-effect notification
564
- // can register every distinct pending source with the transition.
565
- setPendingError(e, l, t);
566
- } else {
567
- clearPendingSources(e);
568
- e.S = n | (n !== STATUS_ERROR ? e.S & STATUS_UNINITIALIZED : 0);
569
- ext(e)._ = t;
570
- }
571
- GlobalQueue.de !== null && GlobalQueue.de(e);
572
- if (e.o?.l && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.ye !== null) GlobalQueue.ye(e);
681
+ if (t === STATUS_ERROR && !(n instanceof StatusError) && !(n instanceof NotReadyError)) n = new StatusError(e, n);
682
+ const r = t === STATUS_PENDING && n instanceof NotReadyError && n.source;
683
+ // (CARVE 2: the lane assignment went with the engine.)
684
+ if (t === STATUS_PENDING && r) {
685
+ // D-41: a re-ask of a shown error keeps it the display (`_shownError`).
686
+ if (e.m & STATUS_ERROR) e.T.Te = e.T.U;
687
+ addPendingSource(e, r);
688
+ // A re-ask of an error that answered the first read: still no value.
689
+ e.m = STATUS_PENDING | (e.m & STATUS_NO_VALUE ? STATUS_UNINITIALIZED : e.m & STATUS_UNINITIALIZED);
690
+ // Preserve the current source on this propagation so readers park on
691
+ // the distinct pending source.
692
+ setPendingError(e, r, n);
693
+ } else {
694
+ clearPendingSources(e);
695
+ // An error answering a node with no value marks it so; a re-ask of it
696
+ // (pending) is uninitialized again; a write (STATUS_NONE) gives it one.
697
+ e.m = t | (t === STATUS_ERROR ? e.m & (STATUS_UNINITIALIZED | STATUS_NO_VALUE) ? STATUS_NO_VALUE : 0 : t === STATUS_PENDING && e.m & STATUS_NO_VALUE ? STATUS_UNINITIALIZED : e.m & STATUS_UNINITIALIZED);
698
+ ext(e).U = n;
573
699
  }
574
- if (o && !r) {
575
- assignOrMergeLane(e, o);
700
+ // Lanes: a written guess is an optimistic boundary (A17 — "the optimistic
701
+ // future value until we know otherwise"). Its own source in flight is its
702
+ // status, not its readers': they read the guess. The flight is the parent
703
+ // transition's to wait on — the frame it was asked in holds (A17: "a
704
+ // transition whose optimistic node is still pending on its own fetch is
705
+ // not complete"), through the node itself (`blocked`). An input changed
706
+ // from outside the guess's hold asks a new question: the frame that
707
+ // changed it is held with that hold (D-34, #4336) — it shows beside the
708
+ // guess's truth, which reveals at that hold's commit (F-1). A `refresh()`
709
+ // re-asks the same question: its frame is its own.
710
+ // (No guess written, no arm.)
711
+ if (guessing && t === STATUS_PENDING && e.l & CONFIG_GUESS) {
712
+ if (passLane === null) joinFuture(e.R & REACTIVE_REASK ? null : txOf(e)._parent);
713
+ return;
714
+ }
715
+ const o = statusNotifierOf(e);
716
+ if (o) {
717
+ o.call(e);
718
+ return;
576
719
  }
577
- const f = r || s;
578
- const a = r || i ? undefined : o;
579
- if (e.o?.A) {
580
- if (r && n === STATUS_PENDING) {
720
+ propagateStatus(e, t, n);
721
+ }
722
+
723
+ /** The dependents' half of `notifyStatus`: `el`'s status reaches each
724
+ * subscriber as a propagated mark (or a re-derive). On its own for a store
725
+ * family's leaves, which are not the derive's dependents — the family
726
+ * propagates the derive's status from each leaf to the leaf's readers
727
+ * (store/projection.ts `wakeFamily`), exactly as a memo's would reach its. */ function propagateStatus(e, t, n) {
728
+ const r = t === STATUS_PENDING && n instanceof NotReadyError && n.source;
729
+ forEachDependent(e, (o, s) => {
730
+ o.ie = clock;
731
+ // A pending mark on a kept-tail link re-derives the subscriber instead of
732
+ // marking it (A30, #3494 review; fuzzer latest-1 #2141; #3519 review).
733
+ // Past `_depsTail` lie the committed frame's deps, kept by A30 because
734
+ // that frame still derives from them while the pass that dropped them is
735
+ // staged. A source going pending there is a question for the node's NEXT
736
+ // pass, not a fact about its current one. Re-derived, the pass decides:
737
+ // it reads the dep and registers through its own read, or reads neither
738
+ // and is done. Clears and errors still ride every link. A link inside the prefix carries the
739
+ // pass's generation (`link()`), so the test is O(1); mid-pass the prefix
740
+ // is what the pass has read so far, and the heap refuses a recomputing
741
+ // node — a dep it has yet to reach registers through its own read.
742
+ // A verdict reader likewise (CONFIG_VERDICT), for either status: the
743
+ // dependency going pending or erroring changed what `isPending`/`latest`
744
+ // answer, so its pass runs again — it decides, breaking out as the
745
+ // verdict lane's, going pending through a plain read of its own, or
746
+ // answering an errored source (`isPending` false; `latest` throws it).
747
+ // Inheriting the error would make a probe's reader errored. A boundary's
748
+ // output (CONFIG_REDERIVE, boundaries.ts) the same: content or fallback
749
+ // is its pass's call.
750
+ // Not one D-25 makes the hold's work (verdict.ts `holdWork`): an async
751
+ // memo with no verdict window of its own reads a verdict memo's value,
752
+ // and that source going pending changes none of its reads — re-derived,
753
+ // it is served the committed value it already derived from (A10) and
754
+ // asks that question again. It waits on the source like any derivation
755
+ // (F-9), and asks its next question once, at the landing (#4397).
756
+ if (o.l & CONFIG_REDERIVE || holdWorkHook && o.l & CONFIG_VERDICT && !(r && holdWorkHook(o, e)) || t === STATUS_PENDING && s.Ne !== o.de) {
757
+ enqueueSub(o);
758
+ schedule();
581
759
  return;
582
760
  }
583
- if (f) {
584
- e.o.A.call(e, n, t);
585
- } else {
586
- e.o.A.call(e);
761
+ // Already pending on this source (a re-ask of its flight): the status is
762
+ // unchanged and nothing re-notifies. The re-asking write is held with the
763
+ // flight the observer waits on (fuzzer #3446 P1): the propagation joins
764
+ // the observer's transaction. An independent render effect in the same
765
+ // frame still passes through — its pass is the frame's (#3372, compiled
766
+ // shape: the unmount it stages makes the observer a zombie whose say is
767
+ // moot for this transaction, A15 #3463).
768
+ if (t === STATUS_PENDING && r && o.T?.se?.has(r)) {
769
+ if (passLane === null && (o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && globalQueue.Y && !freshHook?.(o, r.ie === clock)) joinFuture(txOf(o));
770
+ return;
587
771
  }
588
- return;
589
- }
590
- forEachDependent(e, (e, r) => {
591
- e.Te = clock;
592
- if (n === STATUS_PENDING && l && !e.o?.le?.has(l) || n !== STATUS_PENDING && (e.o?._ !== t || e.o?.le)) {
593
- // A pending-observer link is the subscription an `isPending` read created.
594
- // It exists so the observer re-runs when the source settles, but it must
595
- // not carry a real (non-NotReadyError) error — the synchronous `isPending`
596
- // read swallows those, and the async path must match. Re-run the observer
597
- // so `isPending` re-evaluates (to not-pending) instead of forwarding.
598
- if (r.me && n !== STATUS_PENDING && !(t instanceof NotReadyError)) {
599
- enqueueSub(e);
600
- schedule();
601
- return;
772
+ if (t === STATUS_PENDING && r || t !== STATUS_PENDING && (o.T?.U !== n || o.T?.se)) {
773
+ // The marked subscriber is queued for the commit sweep so its status
774
+ // bookkeeping (loading window, uninitialized clear) runs with the flush
775
+ // — with the lane, when the pass propagating is the lane's work and
776
+ // the dependent's pending is the lane's own flight (`laneStage`).
777
+ // (No answer: a pending propagation leaves a written guess a guess.)
778
+ if (laneStageHook && passLane !== null) laneStageHook(o, passLane, true, e); else {
779
+ queuePendingNode(o);
780
+ // A15 (#3443): pending propagates onto a held memo without
781
+ // recomputing it, and the propagation itself enters the memo's
782
+ // transaction — the flight flows into a memo that transaction
783
+ // holds, so the write that started it is held with it. A render
784
+ // effect's membership is its pass's (`notify`), never sticky.
785
+ if (t === STATUS_PENDING && (o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && !o.i) joinFuture(txOf(o));
602
786
  }
603
- if (!f && !e._e) queuePendingNode(e);
604
- notifyStatus(e, n, t, f, a);
787
+ notifyStatus(o, t, n);
788
+ // A derived store's derive (store/projection.ts): marked, not re-run —
789
+ // its leaves' readers are not its dependents, so the family walks them.
790
+ o.fam?.st(t);
605
791
  }
606
792
  });
607
793
  }
608
794
 
609
- export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, parkLoadingWindow, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource };
795
+ export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, onSupersede, parkLoadingWindow, propagateStatus, releaseFlightTeardown, releaseIfSettledUnobserved, releaseSettledDependents, removePendingSource, setPendingError, settleErroredDependents, settlePendingSource, stageError };