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