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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/dist/dev-shared.js +6357 -0
  2. package/dist/dev.attribution.js +2485 -0
  3. package/dist/dev.js +2851 -7027
  4. package/dist/observe/affects.js +129 -0
  5. package/dist/observe/attribution.js +7 -0
  6. package/dist/observe/boundaries.js +641 -0
  7. package/dist/observe/core/action.js +177 -0
  8. package/dist/observe/core/async.js +846 -0
  9. package/dist/observe/core/attribution-costs.js +66 -0
  10. package/dist/observe/core/attribution-feedback.js +282 -0
  11. package/dist/observe/core/attribution-hooks.js +82 -0
  12. package/dist/observe/core/attribution-queries.js +28 -0
  13. package/dist/observe/core/attribution.js +2095 -0
  14. package/dist/observe/core/constants.js +248 -0
  15. package/dist/{types-cjs/core/context.d.cts → observe/core/context.js} +37 -12
  16. package/dist/observe/core/core.js +1789 -0
  17. package/dist/observe/core/dev.js +293 -0
  18. package/dist/observe/core/effect.js +195 -0
  19. package/dist/observe/core/error-hooks.js +71 -0
  20. package/dist/observe/core/error.js +80 -0
  21. package/dist/observe/core/external.js +98 -0
  22. package/dist/observe/core/graph.js +155 -0
  23. package/dist/observe/core/heap.js +147 -0
  24. package/dist/observe/core/invariants.js +45 -0
  25. package/dist/observe/core/lanes.js +205 -0
  26. package/dist/observe/core/optimistic.js +585 -0
  27. package/dist/observe/core/owner.js +322 -0
  28. package/dist/observe/core/scheduler.js +1428 -0
  29. package/dist/observe/core/verdict.js +669 -0
  30. package/dist/observe/index.js +59 -0
  31. package/dist/observe/map.js +484 -0
  32. package/dist/{types-cjs/signals.d.cts → observe/signals.js} +311 -317
  33. package/dist/observe/store/index.js +40 -0
  34. package/dist/observe/store/next/optimistic.js +660 -0
  35. package/dist/observe/store/next/projection.js +255 -0
  36. package/dist/observe/store/next/reconcile.js +375 -0
  37. package/dist/observe/store/next/store.js +2327 -0
  38. package/dist/observe/store/next/target.js +51 -0
  39. package/dist/observe/store/store.js +325 -0
  40. package/dist/observe/store/storePath.js +103 -0
  41. package/dist/observe/store/utils.js +1023 -0
  42. package/dist/prod/attribution.js +41 -0
  43. package/dist/prod/boundaries.js +128 -76
  44. package/dist/prod/core/action.js +65 -37
  45. package/dist/prod/core/async.js +324 -172
  46. package/dist/prod/core/constants.js +57 -1
  47. package/dist/prod/core/context.js +3 -3
  48. package/dist/prod/core/core.js +1003 -364
  49. package/dist/prod/core/dev.js +17 -1
  50. package/dist/prod/core/effect.js +76 -42
  51. package/dist/prod/core/error-hooks.js +71 -0
  52. package/dist/prod/core/external.js +4 -4
  53. package/dist/prod/core/graph.js +37 -37
  54. package/dist/prod/core/heap.js +61 -56
  55. package/dist/prod/core/lanes.js +107 -63
  56. package/dist/prod/core/optimistic.js +374 -93
  57. package/dist/prod/core/owner.js +62 -46
  58. package/dist/prod/core/scheduler.js +653 -286
  59. package/dist/prod/core/verdict.js +245 -127
  60. package/dist/prod/index.js +15 -5
  61. package/dist/prod/map.js +292 -141
  62. package/dist/prod/signals.js +29 -16
  63. package/dist/prod/store/next/optimistic.js +77 -55
  64. package/dist/prod/store/next/projection.js +31 -20
  65. package/dist/prod/store/next/reconcile.js +87 -84
  66. package/dist/prod/store/next/store.js +718 -401
  67. package/dist/prod/store/next/target.js +32 -10
  68. package/dist/prod/store/store.js +12 -14
  69. package/dist/prod/store/utils.js +950 -130
  70. package/dist/types/attribution.d.ts +20 -0
  71. package/dist/types/attribution.prod.d.ts +22 -0
  72. package/dist/types/boundaries.d.ts +10 -1
  73. package/dist/types/core/action.d.ts +12 -5
  74. package/dist/types/core/async.d.ts +1 -1
  75. package/dist/types/core/attribution-costs.d.ts +35 -0
  76. package/dist/types/core/attribution-feedback.d.ts +133 -0
  77. package/dist/types/core/attribution-hooks.d.ts +147 -9
  78. package/dist/types/core/attribution-queries.d.ts +10 -0
  79. package/dist/types/core/attribution.d.ts +216 -182
  80. package/dist/types/core/constants.d.ts +56 -0
  81. package/dist/types/core/core.d.ts +190 -2
  82. package/dist/types/core/dev.d.ts +246 -28
  83. package/dist/types/core/effect.d.ts +8 -1
  84. package/dist/types/core/error-hooks.d.ts +71 -0
  85. package/dist/types/core/index.d.ts +4 -1
  86. package/dist/types/core/invariants.d.ts +4 -0
  87. package/dist/types/core/lanes.d.ts +39 -5
  88. package/dist/types/core/scheduler.d.ts +153 -0
  89. package/dist/types/core/types.d.ts +32 -9
  90. package/dist/types/index.d.ts +10 -3
  91. package/dist/types/map.d.ts +2 -0
  92. package/dist/types/signals.d.ts +8 -0
  93. package/dist/types/store/index.d.ts +2 -0
  94. package/dist/types/store/next/optimistic.d.ts +1 -1
  95. package/dist/types/store/next/store.d.ts +11 -5
  96. package/dist/types/store/next/target.d.ts +37 -10
  97. package/dist/types/store/utils.d.ts +178 -0
  98. package/package.json +21 -17
  99. package/dist/node.cjs +0 -10541
  100. package/dist/node.dev.cjs +0 -13724
  101. package/dist/types-cjs/affects.d.cts +0 -47
  102. package/dist/types-cjs/boundaries.d.cts +0 -175
  103. package/dist/types-cjs/core/action.d.cts +0 -64
  104. package/dist/types-cjs/core/async.d.cts +0 -23
  105. package/dist/types-cjs/core/attribution-hooks.d.cts +0 -127
  106. package/dist/types-cjs/core/attribution.d.cts +0 -495
  107. package/dist/types-cjs/core/constants.d.cts +0 -159
  108. package/dist/types-cjs/core/core.d.cts +0 -185
  109. package/dist/types-cjs/core/dev.d.cts +0 -136
  110. package/dist/types-cjs/core/effect.d.cts +0 -30
  111. package/dist/types-cjs/core/error.d.cts +0 -56
  112. package/dist/types-cjs/core/external.d.cts +0 -15
  113. package/dist/types-cjs/core/graph.d.cts +0 -28
  114. package/dist/types-cjs/core/heap.d.cts +0 -24
  115. package/dist/types-cjs/core/index.d.cts +0 -13
  116. package/dist/types-cjs/core/invariants.d.cts +0 -59
  117. package/dist/types-cjs/core/lanes.d.cts +0 -54
  118. package/dist/types-cjs/core/optimistic.d.cts +0 -6
  119. package/dist/types-cjs/core/owner.d.cts +0 -124
  120. package/dist/types-cjs/core/scheduler.d.cts +0 -236
  121. package/dist/types-cjs/core/types.d.cts +0 -204
  122. package/dist/types-cjs/core/verdict.d.cts +0 -2
  123. package/dist/types-cjs/index.d.cts +0 -10
  124. package/dist/types-cjs/map.d.cts +0 -69
  125. package/dist/types-cjs/package.json +0 -3
  126. package/dist/types-cjs/store/index.d.cts +0 -18
  127. package/dist/types-cjs/store/next/optimistic.d.cts +0 -25
  128. package/dist/types-cjs/store/next/projection.d.cts +0 -8
  129. package/dist/types-cjs/store/next/reconcile.d.cts +0 -10
  130. package/dist/types-cjs/store/next/store.d.cts +0 -121
  131. package/dist/types-cjs/store/next/target.d.cts +0 -166
  132. package/dist/types-cjs/store/store.d.cts +0 -143
  133. package/dist/types-cjs/store/storePath.d.cts +0 -58
  134. package/dist/types-cjs/store/utils.d.cts +0 -74
@@ -0,0 +1,846 @@
1
+ import { STATUS_UNINITIALIZED, CONFIG_CHILD_COMPANIONS, STATUS_ERROR, STATUS_PENDING, CONFIG_DERIVED_OVERRIDE, CONFIG_INPUTS_PUBLISHED, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, unwrapOverride, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_DISPOSED } from "./constants.js";
2
+
3
+ import { attrHooks } from "./attribution-hooks.js";
4
+
5
+ import { untrack, ext, statusNotifierOf, hasActiveOverride, setSignal, context } from "./core.js";
6
+
7
+ import "./invariants.js";
8
+
9
+ import "./dev.js";
10
+
11
+ import { StatusError, NotReadyError } from "./error.js";
12
+
13
+ import { trimStaleDeps, unobserved } from "./graph.js";
14
+
15
+ import { enqueueSub } from "./heap.js";
16
+
17
+ import { assignOrMergeLane, resolveLane, resolveTransition } from "./lanes.js";
18
+
19
+ import { cleanup } from "./owner.js";
20
+
21
+ import { GlobalQueue, schedule, flush, queuePendingNode, insertSubs, clock, waitingTransition, currentTransition, globalQueue, enterWaiting, setOrigin, origin } from "./scheduler.js";
22
+
23
+ // The lazily-created Set is the ONE container for pending sources. Its
24
+ // predecessor — a singular slot promoted to a Set on the second source —
25
+ // created dual state whose migration invariant was easy to break: a third
26
+ // overlapping source landed beside the Set and removePendingSource refused
27
+ // to clear it, stranding the Set members' pending forever (#2893).
28
+ function addPendingSource(e, t) {
29
+ if (e.o?.ut?.has(t)) return false;
30
+ (ext(e).ut ??= new Set).add(t);
31
+ return true;
32
+ }
33
+
34
+ function removePendingSource(e, t) {
35
+ const n = e.o?.ut;
36
+ if (!n?.delete(t)) return false;
37
+ if (!n.size) e.o.ut = undefined;
38
+ return true;
39
+ }
40
+
41
+ function clearPendingSources(e) {
42
+ // This set is node-owned and never shared; dropping the sole reference
43
+ // releases the set and every entry without a redundant clear() walk.
44
+ if (e.o !== null) e.o.ut = undefined;
45
+ }
46
+
47
+ // A rejection-pending only resolves through the settle sweep over the
48
+ // SOURCE's subscribers, so it is retryable iff a tracked read created that
49
+ // edge: a dep that IS the source, or one whose own pending chain carries it
50
+ // (pending sources propagate the origin node, so this covers any depth).
51
+ // Also guards branch-local recovery: another dependency may still need the source.
52
+ function retryReaches(e, t) {
53
+ for (let n = e.ee; n; n = n.te) {
54
+ const e = n.re.ne || n.re;
55
+ if (e === t || e.o?.ut?.has(t)) return true;
56
+ }
57
+ return false;
58
+ }
59
+
60
+ /**
61
+ * A loading-window node hit an unready source (sync throw in recompute, or a
62
+ * NotReadyError-rejected flight): register for the source's settle — the
63
+ * settlePendingSource walk runs off `_pendingSources` + `_blocked` alone —
64
+ * with NO read-visible pending status, no downstream propagation, no
65
+ * transition, no lane registration. Commit #0 keeps serving.
66
+ */ function parkLoadingWindow(e, t) {
67
+ ext(e).oe = true;
68
+ if (t.source) addPendingSource(e, t.source);
69
+ // A settled error is the node's answer ("the error stays the answer until
70
+ // this retry can actually run") — the park must not replace it: reads
71
+ // throw `_error` while STATUS_ERROR is set, and overwriting it here leaks
72
+ // a pending-class NotReadyError from a read-invisible park (#2989).
73
+ if (!(e.S & STATUS_ERROR)) setPendingError(e, t.source, t);
74
+ }
75
+
76
+ function setPendingError(e, t, n) {
77
+ if (!t) {
78
+ if (e.o !== null) e.o._ = null;
79
+ return;
80
+ }
81
+ if (n instanceof NotReadyError && n.source === t) {
82
+ ext(e)._ = n;
83
+ return;
84
+ }
85
+ const r = e.o?._;
86
+ if (!(r instanceof NotReadyError) || r.source !== t) {
87
+ ext(e)._ = new NotReadyError(t);
88
+ }
89
+ }
90
+
91
+ function forEachDependent(e, t) {
92
+ for (let n = e.u; n !== null; n = n.ie) t(n.se, n);
93
+ // `?? null`: affects() marks route plain signals (no `_child` slot) through here.
94
+ for (let n = e.o?.i ?? null; n !== null; n = n.ue) {
95
+ for (let e = n.u; e !== null; e = e.ie) t(e.se, e);
96
+ }
97
+ }
98
+
99
+ // Queue a node to re-run on the next flush (used both when a pending source
100
+ // settles and when an `isPending` observer must re-evaluate after a real error):
101
+ // shared scheduling helper in heap.ts (tracked effects bypass the heap).
102
+ // Settle-time counterpart of unlinkSubs' last-one-out check. A lazy node that
103
+ // loses its last subscriber while STATUS_PENDING is exempt from autodispose
104
+ // (the in-flight work is an observer), so whatever CLEARS that pending state
105
+ // must run the release — otherwise the node stays linked and recomputes
106
+ // forever with zero subscribers (#2934). The node's own promise/iterator
107
+ // callbacks handle their own release (settleAutodispose in handleAsync); this
108
+ // covers derivatively-pending dependents, which have no callbacks of their own.
109
+ function releaseIfSettledUnobserved(e) {
110
+ e.ht && e.C & CONFIG_AUTO_DISPOSE && !e.u && !(e.ft & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
111
+ }
112
+
113
+ // Error-path sweep: notifyStatus(STATUS_ERROR) clears dependents' pending
114
+ // sources through its own recursion (no per-node settle callback), so after
115
+ // the propagation completes, walk the same graph for stranded lazy nodes.
116
+ // Collect-then-release so unobserved() never unlinks under the walk.
117
+ function releaseSettledDependents(e) {
118
+ let t;
119
+ const n = new Set;
120
+ const visit = e => {
121
+ if (n.has(e)) return;
122
+ n.add(e);
123
+ if (!e.u && e.C & CONFIG_AUTO_DISPOSE) (t ??= []).push(e);
124
+ forEachDependent(e, visit);
125
+ };
126
+ forEachDependent(e, visit);
127
+ if (t) for (const e of t) releaseIfSettledUnobserved(e);
128
+ }
129
+
130
+ // Error-dimension twin of settlePendingSource's blocked re-enqueue (#2949):
131
+ // a node in STATUS_ERROR that recovers by recomputing to an UNCHANGED value
132
+ // fires no value notification — the recovery is completely silent. But a
133
+ // dependent that re-ran during the error window consumed its dirty flag and
134
+ // committed nothing (the fresh sibling values it read were absorbed into an
135
+ // errored run), so its committed value is stale. The propagated error is one
136
+ // object identity down the whole dependent tree, and holding it is exactly
137
+ // the "blocked on this error" marker — re-enqueue those holders so they
138
+ // re-run: fresh values commit and flow, and a dependent with another
139
+ // still-broken source simply re-errors. Pending recovery uses
140
+ // settlePendingSource to clear inherited status and retry blocked readers.
141
+ // Walks the full dependent graph
142
+ // (releaseSettledDependents shape): identity holders can sit below an
143
+ // intermediate whose own error state has since been scrubbed or replaced
144
+ // (e.g. an error boundary's tree node).
145
+ function settleErroredDependents(e, t) {
146
+ let n = false;
147
+ const r = new Set;
148
+ const visit = e => {
149
+ if (r.has(e)) return;
150
+ r.add(e);
151
+ if (e.o?._ === t) {
152
+ enqueueSub(e);
153
+ n = true;
154
+ }
155
+ forEachDependent(e, visit);
156
+ };
157
+ forEachDependent(e, visit);
158
+ if (n) schedule();
159
+ }
160
+
161
+ // Retire `source` from pending state along the dependent graph rooted at `el`.
162
+ // By default, `el` is the source whose flight settled or was superseded.
163
+ // With a distinct `source`, `el` is a recovered computation that dropped it:
164
+ // the source may still be pending, so dependents with another path to it stay pending.
165
+ function settlePendingSource(e, t = e) {
166
+ // Landing and branch recovery already cleared el's own set. Superseded
167
+ // re-parks can retain an abandoned self entry (source === el), which must
168
+ // retire in the same walk as its propagated copies.
169
+ removePendingSource(e, t);
170
+ let n = false;
171
+ let r;
172
+ const o = new Set;
173
+ // Companion updates no-op without the verdict layer (null hook).
174
+ const i = GlobalQueue.le;
175
+ const settle = s => {
176
+ if (o.has(s)) return;
177
+ // A conditional dropped this source, but another dependency can still
178
+ // carry it. Only retire pending state inherited through the recovered
179
+ // branch. Deliberately NOT marked visited on this early return: the
180
+ // carrying dependency may itself be a later branch of this same walk
181
+ // (two unchanged memos converging), and its visit must be free to
182
+ // re-examine this node once that branch has retired the source.
183
+ if (t !== e && retryReaches(s, t)) return;
184
+ if (!removePendingSource(s, t)) return;
185
+ o.add(s);
186
+ s.fe = clock;
187
+ const u = s.o?.ut?.values().next().value;
188
+ // STATUS_ERROR + pending sources only coexist via an errored loading
189
+ // window's park (notifyStatus(STATUS_ERROR) clears pending sources
190
+ // otherwise): the settled error stays the answer through the settle —
191
+ // nulling it here would have reads throw `null` until the re-enqueued
192
+ // retry lands, or lose it entirely if that retry parks again (#2989).
193
+ const l = s.S & STATUS_ERROR;
194
+ if (u) {
195
+ if (!l) setPendingError(s, u);
196
+ i?.(s);
197
+ } else {
198
+ s.S &= ~STATUS_PENDING;
199
+ if (!l) setPendingError(s);
200
+ i?.(s);
201
+ if (s.o?.oe) {
202
+ enqueueSub(s);
203
+ n = true;
204
+ }
205
+ if (s.o !== null) s.o.oe = false;
206
+ // Fully settled with nobody watching: release candidate (#2934). Checked
207
+ // again at release time — deferred so unobserved() can't unlink subs
208
+ // lists this walk is still iterating.
209
+ if (!s.u && s.C & CONFIG_AUTO_DISPOSE) (r ??= []).push(s);
210
+ }
211
+ forEachDependent(s, settle);
212
+ };
213
+ forEachDependent(e, settle);
214
+ // Release before the flush schedule below: unobserved() pulls the node back
215
+ // out of the heap, so the enqueueSub above never recomputes a released node.
216
+ if (r) for (const e of r) releaseIfSettledUnobserved(e);
217
+ if (n) schedule();
218
+ }
219
+
220
+ // Object-thenable detection (Promises/A+ shape).
221
+ function isThenable(e) {
222
+ return e != null && typeof e === "object" && typeof e.then === "function";
223
+ }
224
+
225
+ /** Fire and clear a node's iterator-flight cancellation hook (#3122). */ function releaseFlightTeardown(e) {
226
+ const t = e.o?.ae;
227
+ if (t != null) {
228
+ e.o.ae = null;
229
+ t();
230
+ }
231
+ }
232
+
233
+ function handleAsync(e, t, n) {
234
+ let r = false;
235
+ let o = false;
236
+ if (typeof t === "object" && t !== null) {
237
+ untrack(() => {
238
+ r = t[Symbol.asyncIterator];
239
+ o = !r && isThenable(t);
240
+ });
241
+ }
242
+ if (!o && !r) {
243
+ if (e.o !== null) e.o.ce = null;
244
+ // A sync landing is the first real answer for a loadingValue node.
245
+ e.Se = false;
246
+ return t;
247
+ }
248
+ // Flight replacement relies on recompute's supersede release for iterator
249
+ // teardown (#3122): every handleAsync call — including the projection
250
+ // self-registration — runs during a recompute of `el`, which has already
251
+ // fired _flightTeardown. A future non-recompute registration path must
252
+ // release it here before overwriting _inFlight.
253
+ ext(e).ce = t;
254
+ // The run that asked this flight read every input without throwing: an
255
+ // input still in flight was masked for it (an active override, A17), so
256
+ // pending state those inputs propagated onto the node earlier does not
257
+ // describe this answer. Drop it — the flight is the node's pending now.
258
+ // The landing retires only the flight's own entry (landStatus, #3373), so
259
+ // an entry that survived here would hold the node past its own answer.
260
+ e.o.ut = undefined;
261
+ // Provenance of the question this flight asks (#3331): the action whose
262
+ // window is registering it, or the flight whose landing is. Its landings
263
+ // propagate under it (asyncWrite) so an override downstream can tell a
264
+ // stale answer from its own.
265
+ const i = origin;
266
+ // Attribution hook: a new flight is registered. Fired here (not in the
267
+ // branches below) so every flight shape — plain thenable, iterator, the
268
+ // flattened combinations — is announced exactly once, while the recompute
269
+ // frame that caused it is still on the engine's stack. Not inside a try
270
+ // (#2883 — see attribution-hooks.ts).
271
+ if (attrHooks !== null) attrHooks.flightStart(e, t);
272
+ let s;
273
+ // Settle-time transition re-entry. The loading rail is invisible to
274
+ // transactions (#2933): a boundary-caught first load never registers as an
275
+ // async reporter, so its settle — the boundary's fallback -> content
276
+ // reveal — must flow ambiently. The node can still carry a `_transition`
277
+ // stamp (pending-node bookkeeping rides through the stamping sites), and
278
+ // blindly re-entering that stamped, still-incomplete transaction stashed
279
+ // the reveal with it — a deadlock when the transaction's completion
280
+ // depended on the reveal (#2937). An ESCAPED first load did register and
281
+ // keeps transition scheduling; initialized (value-holding) pending settles
282
+ // are the transaction's reveal machinery and always re-enter.
283
+ const settleTransition = () => {
284
+ let t = resolveTransition(e);
285
+ // A lane-routed node's landing is revealed by its lane, ahead of the
286
+ // transaction that owns the lane (whose own commit is only the override's
287
+ // confirm/revert). Entering that owner here would fold every transaction
288
+ // waiting on this flight into it at the landing — a reveal that
289
+ // discovered the flight (#3305) would then wait on the owner's action
290
+ // instead of on the flight (#3334). Enter the waiter: the transaction
291
+ // whose blocker this landing clears. Then every transaction waiting on
292
+ // the flight folds in (enterWaiting): a reveal that discovered it
293
+ // completes at its landing (A15) — a stampless node's fresh batch
294
+ // included (its flight started under a batch that committed beneath it,
295
+ // #3305). The fold used to happen as each stamped reader recomputed,
296
+ // which effects no longer do (#3407).
297
+ if (e.o?.de) t = waitingTransition(e) ?? t;
298
+ if (t && e.S & STATUS_UNINITIALIZED && !currentTransition(t).ot.has(e)) {
299
+ // Drop the stale stamp too: the plain settle write (setSignal) and the
300
+ // stash-path restamp both re-enter the transaction through it.
301
+ e.Te = null;
302
+ return;
303
+ }
304
+ globalQueue.initTransition(t);
305
+ enterWaiting(e);
306
+ };
307
+ const handleError = n => {
308
+ if (e.o?.ce !== t) return;
309
+ // NotReadyError from rejected promises should be treated as pending, not error
310
+ let r = n instanceof NotReadyError;
311
+ if (r && e.Se) {
312
+ // Loading window: the flight died waiting on an unready source. Keep
313
+ // serving commit #0 — same parking as recompute's catch for sync
314
+ // dependency throws. The dead flight is released so the clock-gated
315
+ // error-retry pull (updateIfNecessary) can also re-ask.
316
+ if (e.o !== null) e.o.ce = null;
317
+ parkLoadingWindow(e, n);
318
+ e.fe = clock;
319
+ return;
320
+ }
321
+ settleTransition();
322
+ notifyStatus(e, r ? STATUS_PENDING : STATUS_ERROR, n);
323
+ // A NotReady rejection is a landing into another pending source. The
324
+ // rejected flight will never settle its self entry, so transfer ownership
325
+ // after notifyStatus has propagated the replacement source.
326
+ if (r) settlePendingSource(e);
327
+ e.fe = clock;
328
+ // A real error settles derivatively-pending dependents (notifyStatus
329
+ // cleared their pending sources), so stranded lazy ones release here —
330
+ // the error twin of settlePendingSource's release (#2934).
331
+ if (!r) releaseSettledDependents(e);
332
+ };
333
+ const asyncWrite = (r, o) => {
334
+ if (e.o?.ce !== t) return;
335
+ // If the node was dirtied by a newer write (optimistic override or regular),
336
+ // skip this stale async result — the upcoming flush will recompute the node
337
+ // with the new value, creating a fresh Promise that supersedes this one.
338
+ if (e.ft & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
339
+ // The landing propagates under the flight's provenance (#3331) — through
340
+ // the flush below, which clears it.
341
+ setOrigin(i);
342
+ settleTransition();
343
+ const s = !!(e.S & STATUS_UNINITIALIZED);
344
+ // Captured before clearStatus wipes it: a quiet re-ask's landing may be
345
+ // transition-held below, and the displayed value keeps answering the same
346
+ // question until the hold commits — the classification must survive to
347
+ // that reveal or companion synchronization briefly classifies the held
348
+ // old value as pending, a one-frame pulse to direct observers (#3178).
349
+ // A truthy capture implies `_x` exists, so the restore writes it directly.
350
+ const u = e.o?.Ee;
351
+ landStatus(e);
352
+ if (u) e.o.Ee = true;
353
+ const l = resolveLane(e);
354
+ if (l) l.Ie.delete(e);
355
+ // Attribution hook: lets the engine snapshot state before the landing
356
+ // branches, so it can tell whether the plain path's setSignal committed a
357
+ // change (and only then classify it as an async landing).
358
+ if (attrHooks !== null) attrHooks.asyncStart(e);
359
+ if (n) {
360
+ try {
361
+ n(r);
362
+ } catch (e) {
363
+ handleError(e);
364
+ return;
365
+ }
366
+ if (s) landStatus(e, true);
367
+ } else if (e.o?.Ne !== undefined && !(l && e.C & CONFIG_DERIVED_OVERRIDE)) {
368
+ // A derived override's landing UNDER its lane is the lane's own work
369
+ // (the branch below); demoted — its source superseded (A18) — the
370
+ // landing is the truth the correction asked for, and holds and
371
+ // supersedes here like the sync twin (recompute). Otherwise:
372
+ // Optimistic node — resting OR covered by an active override — holds
373
+ // through the shared pending-node path, exactly like a plain async memo,
374
+ // so the commit clears STATUS_UNINITIALIZED (#2806) and elevation to
375
+ // _value happens on this value's OWN transition schedule (A18 as
376
+ // re-ruled 2026-07-07: _value only changes at commit points). With an
377
+ // override active the hold and its eventual commit are unobservable
378
+ // (A17 — every reader sees the override); the revert reveals whatever
379
+ // has committed by then, so corrections reveal atomically with their
380
+ // transition rather than escaping it.
381
+ if (e._e === NOT_PENDING) queuePendingNode(e);
382
+ e._e = r;
383
+ // The hold is a companion-visible write like any other (A13/A19): the
384
+ // clearStatus() above computed its verdict before the hold existed, so
385
+ // isPending must re-derive (the value is not final until commit — V1)
386
+ // and latest() must see the fresh in-flight value (V2). Subscribers are
387
+ // only notified when the hold is visible to them: under an active
388
+ // override every reader sees the override (A17), so waking subs would
389
+ // re-show an unchanged view — the revert is the notification point.
390
+ // Under an override the landing is handed to the engine's
391
+ // supersedeOverride (A18 supersession, #3331): own-source truth that
392
+ // differs from the override ends the optimism for the graph now (plain
393
+ // channel, lane demoted); a matching arrival is silent except to an
394
+ // authoritative-view reader (until()'s predicate) waiting on exactly
395
+ // this staged truth (#3164 — without the wake the hold deadlocks: the
396
+ // landing waits on the transaction, the transaction on the action, the
397
+ // action on an until() never re-notified). The hook is installed with
398
+ // the engine, which an active override implies. The propagation runs
399
+ // under this flight's provenance (setOrigin above).
400
+ GlobalQueue.De?.(e, r);
401
+ if (!hasActiveOverride(e)) {
402
+ if (attrHooks !== null) attrHooks.asyncEnd(e, undefined, r, true);
403
+ insertSubs(e);
404
+ } else GlobalQueue.Oe(e, r);
405
+ e.fe = clock;
406
+ } else if (l) {
407
+ // Route through lane's effect queue for independent flushing
408
+ const t = e.Pe;
409
+ const n = hasActiveOverride(e) ? unwrapOverride(e.o.Ne) : e.Ae;
410
+ const o = e.Re;
411
+ try {
412
+ if (!t && s || !o || !o(r, n)) {
413
+ // Lanes stage (#3479): a memo's landing under its lane is a derived
414
+ // override, as its sync pass's result is (recompute) — `_value`
415
+ // stays the committed truth for readers off the lane.
416
+ if (t) e.Ae = r; else GlobalQueue.ge(e, r, l);
417
+ e.fe = clock;
418
+ // The latest() shadow write gives latest() effects independent lanes; the
419
+ // _pendingSignal update is a no-op repeat of the clearStatus() call above
420
+ // (computePendingState doesn't read _value).
421
+ GlobalQueue.De?.(e, r);
422
+ insertSubs(e, true);
423
+ }
424
+ } catch (t) {
425
+ // A user comparator throwing during async resolution has no caller to
426
+ // surface to (we're in promise machinery) — route it through the node's
427
+ // error status so boundaries contain it instead of an unhandled
428
+ // rejection (#2837).
429
+ notifyStatus(e, STATUS_ERROR, t);
430
+ }
431
+ // Attribution hook — unconditional, and OUTSIDE the try: rollup's
432
+ // tryCatchDeoptimization retains anything referenced inside a try even
433
+ // behind a folded false guard, so even a dev-only flag smuggled out of
434
+ // the commit branch leaves prod residue (#2883). The engine instead
435
+ // detects whether this landing committed by comparing the node against
436
+ // its asyncStart snapshot (see attribution.ts).
437
+ if (attrHooks !== null) attrHooks.asyncEnd(e, n, r, true);
438
+ } else {
439
+ try {
440
+ setSignal(e, () => r);
441
+ } catch (t) {
442
+ // Same containment as above: setSignal's comparator throw is the only
443
+ // pre-commit failure here, and there is no user callsite to throw to.
444
+ notifyStatus(e, STATUS_ERROR, t);
445
+ }
446
+ // Attribution hook: this path landed through setSignal, whose write
447
+ // hook already saw any committed change — direct=false lets the engine
448
+ // reclassify that write as an async landing iff it actually committed.
449
+ // Outside the try (#2883 — see attribution-hooks.ts).
450
+ if (attrHooks !== null) attrHooks.asyncEnd(e, undefined, r, false);
451
+ }
452
+ // First real answer landing: the window closes when the answer becomes
453
+ // OBSERVABLE. A direct commit is observable now; a transition-held write
454
+ // (`_pendingValue` set above or inside setSignal) is not — the verdict's
455
+ // held-value branch is window-gated, and commitPendingNode closes the
456
+ // window when the hold commits, so no one-frame isPending pulse can leak
457
+ // to live observers between the landing and its commit (#2990). The
458
+ // quiet re-ask classification follows the same schedule (#3178).
459
+ if (e._e === NOT_PENDING) {
460
+ e.Se = false;
461
+ if (u) e.o.Ee = false;
462
+ // The landing published: the dependency tail the flight's pass left
463
+ // linked goes now (A30, #3410). A transition-held landing has not
464
+ // replaced the committed frame — the committed value still derives
465
+ // from the previous pass's inputs, and a mainline write to one of them
466
+ // must reach this node and join its hold (its stamp) instead of
467
+ // publishing beside the stale derivation (#3461: `b() ? b() : a()`
468
+ // held on `b` dropped `a` at its landing, and `A: 1` then committed
469
+ // beside `Selected: 0`). `commitPendingNode` trims a held landing.
470
+ trimStaleDeps(e);
471
+ }
472
+ settlePendingSource(e);
473
+ schedule();
474
+ flush();
475
+ o?.();
476
+ };
477
+ // A pending node's in-flight promise is an observer: `unlinkSubs` skips
478
+ // autodispose while STATUS_PENDING so subscriber churn can't orphan the
479
+ // work (a lazy async memo would otherwise tear down and re-execute — one
480
+ // fetch per suspended re-read). Settling is that observer's release, so
481
+ // it runs the same last-one-out check the other release sites run.
482
+ // Returns whether the node released, so the iterator branch can stop
483
+ // pulling values instead of pumping an unobserved stream forever (#2935).
484
+ const settleAutodispose = () => {
485
+ if (e.C & CONFIG_AUTO_DISPOSE && !e.u && !(e.S & STATUS_PENDING)) {
486
+ unobserved(e);
487
+ return true;
488
+ }
489
+ return false;
490
+ };
491
+ // Consumes an AsyncIterable as this flight's value stream. Two postures:
492
+ // LIVE (called synchronously from this read — the compute returned an
493
+ // iterable directly, or a sync-settled thenable held one), where the
494
+ // initial drain may stash a sync first yield for the caller to return and
495
+ // close registration uses the ambient owner; and DEFERRED (the flattening
496
+ // path — a thenable resolved to an iterable in a later microtask), where
497
+ // there is no caller to serve and no ambient owner: sync-settled steps
498
+ // write through asyncWrite, and close registration goes through the slot
499
+ // the thenable branch pre-registered while it still owned the context.
500
+ // Returns whether a sync answer landed (first yield or empty completion) —
501
+ // meaningful only in the live posture.
502
+ const consumeIterator = (n, r) => {
503
+ const o = n[Symbol.asyncIterator]();
504
+ let i = false;
505
+ let u = false;
506
+ let l = !r;
507
+ const close = () => {
508
+ if (u) return;
509
+ u = true;
510
+ try {
511
+ const e = o.return?.();
512
+ if (isThenable(e)) e.then(undefined, () => {});
513
+ } catch {}
514
+ };
515
+ r ? r(close) : cleanup(close);
516
+ // Flight-identity cancellation (#3122): the registration above is the
517
+ // owner-death backstop, but its disposal list can be zombie-deferred
518
+ // until the SUPERSEDING flight settles. The teardown slot fires at the
519
+ // _inFlight release sites so supersede stops this stream immediately.
520
+ ext(e).ae = close;
521
+ // Release check before each next pull: an unobserved lazy node must tear
522
+ // down (its close above runs via disposal, closing the iterator) instead
523
+ // of pumping the stream forever with zero subscribers (#2935).
524
+ const iterateOrRelease = () => {
525
+ if (!settleAutodispose()) iterate();
526
+ };
527
+ const iterate = () => {
528
+ let n, r, f = false, a = false, c = true;
529
+ // Protocol tolerance, matching `for await`: `await` unwraps whatever
530
+ // next() returns — a thenable OR a bare IteratorResult. Real producers
531
+ // use the bare form as a promise-free fast path when a value is already
532
+ // buffered (seroval's deserialized streams do), so a bare result is
533
+ // assimilated as an already-settled step instead of crashing on `.then`.
534
+ const S = o.next();
535
+ const d = isThenable(S) ? S : {
536
+ then: e => void e(S)
537
+ };
538
+ d.then(r => {
539
+ // The sync stash only serves the INITIAL drain (handleAsync's caller
540
+ // consumes syncValue / throws NotReady from it). A sync-settled step
541
+ // after an async gap — seroval buffering values between pulls, a
542
+ // sync-thenable producer mid-stream — has no caller reading the
543
+ // stash: it must write through the async path or the value is
544
+ // silently dropped. (The deferred posture never has a caller, so
545
+ // initialRead starts false there and everything writes through.)
546
+ if (c && l) {
547
+ n = r;
548
+ f = true;
549
+ if (r.done) u = true;
550
+ } else if (e.o?.ce !== t) {
551
+ return;
552
+ } else if (!r.done) {
553
+ i = true;
554
+ asyncWrite(r.value, iterateOrRelease);
555
+ } else {
556
+ u = true;
557
+ if (i) {
558
+ schedule();
559
+ flush();
560
+ } else {
561
+ // Empty completion settles like the immediately-done sync path.
562
+ asyncWrite(undefined);
563
+ }
564
+ settleAutodispose();
565
+ }
566
+ }, n => {
567
+ if (c && l) {
568
+ r = n;
569
+ a = true;
570
+ } else if (e.o?.ce === t) {
571
+ u = true;
572
+ handleError(n);
573
+ settleAutodispose();
574
+ }
575
+ });
576
+ c = false;
577
+ if (a) {
578
+ // Match the promise branch, but only rethrow during the initial read.
579
+ u = true;
580
+ handleError(r);
581
+ if (l) throw r;
582
+ return true;
583
+ }
584
+ if (f && !n.done) {
585
+ s = n.value;
586
+ i = true;
587
+ return iterate();
588
+ }
589
+ return f && n.done;
590
+ };
591
+ const f = iterate();
592
+ // Later iterate() calls run from asyncWrite, where rethrowing would be unhandled.
593
+ l = false;
594
+ return i || f;
595
+ };
596
+ // Landed-synchronously verdict for a LIVE iterator drain; null when no live
597
+ // drain ran (plain promise flight, or a deferred flatten). Drives the
598
+ // shared NotReady/loading tail below.
599
+ let u = null;
600
+ // Flatten one async level: a thenable that RESOLVES to an AsyncIterable —
601
+ // the shape every async stub returning a stream produces — consumes as the
602
+ // stream itself rather than settling on the iterable object. One level
603
+ // only: A+ `then` already collapses nested thenables, so the resolved
604
+ // value is never itself a thenable.
605
+ const flattenIfIterable = (e, t) => {
606
+ let n = false;
607
+ if (typeof e === "object" && e !== null) {
608
+ untrack(() => {
609
+ n = e[Symbol.asyncIterator];
610
+ });
611
+ }
612
+ if (!n) return false;
613
+ const r = consumeIterator(e, t);
614
+ if (!t) u = r;
615
+ return true;
616
+ };
617
+ if (o) {
618
+ let n = false, r = false, o, i = true;
619
+ // Close registration for the flattening path. Consumption starts in a
620
+ // microtask where the ambient owner is gone (or worse, someone else's),
621
+ // so `cleanup()` can't be used — the close targets el's disposal list
622
+ // directly, exactly where a live cleanup() during this recompute would
623
+ // have put it. Deliberately NOT pre-registered at flight start: a
624
+ // non-null `_disposal` reclassifies the node into recompute's deferred
625
+ // (zombie) disposal path, and plain promise flights — the overwhelming
626
+ // majority — must not pay that. Only a flight that actually flattens
627
+ // becomes disposal-bearing, which is exactly the class a directly
628
+ // returned iterable already occupies.
629
+ const registerDeferredClose = t => {
630
+ if (!e.Ue) e.Ue = t; else if (Array.isArray(e.Ue)) e.Ue.push(t); else e.Ue = [ e.Ue, t ];
631
+ };
632
+ t.then(r => {
633
+ if (i) {
634
+ s = r;
635
+ n = true;
636
+ } else if (e.o?.ce === t && !(e.ft & REACTIVE_DISPOSED) && flattenIfIterable(r, registerDeferredClose)) ; else {
637
+ asyncWrite(r);
638
+ settleAutodispose();
639
+ }
640
+ }, e => {
641
+ if (i) {
642
+ o = e;
643
+ r = true;
644
+ } else {
645
+ handleError(e);
646
+ settleAutodispose();
647
+ }
648
+ });
649
+ i = false;
650
+ if (r) {
651
+ // Settle through the same status path an async rejection uses, then
652
+ // unwind the in-progress synchronous read so the errored node isn't
653
+ // momentarily read as `undefined`.
654
+ handleError(o);
655
+ throw o;
656
+ } else if (!n) {
657
+ // Loading window: serve commit #0 instead of suspending. No transition
658
+ // is opened — first-flight work on a loadingValue node is loading-class
659
+ // (invisible to boundaries and transitions); the flight itself is
660
+ // already registered in _inFlight and lands through asyncWrite.
661
+ if (e.Se) return e.Ae;
662
+ globalQueue.initTransition(resolveTransition(e));
663
+ throw new NotReadyError(context);
664
+ } else if (!flattenIfIterable(s)) {
665
+ // Synchronously-resolved promise: the first real answer landed.
666
+ e.Se = false;
667
+ }
668
+ // A sync-resolved promise holding an AsyncIterable flattened LIVE (we
669
+ // are still inside the synchronous read): full initial-drain semantics
670
+ // apply and the shared tail below settles the verdict.
671
+ }
672
+ if (r) flattenIfIterable(t);
673
+ if (u !== null) {
674
+ if (!u) {
675
+ // Loading window: serve commit #0 (see the promise branch above).
676
+ if (e.Se) return e.Ae;
677
+ globalQueue.initTransition(resolveTransition(e));
678
+ throw new NotReadyError(context);
679
+ }
680
+ // A sync first yield (or immediate empty completion) is the first real
681
+ // answer; async yields clear inside asyncWrite.
682
+ e.Se = false;
683
+ }
684
+ return s;
685
+ }
686
+
687
+ function clearStatus(e, t = false) {
688
+ if (e.o?.ut) clearPendingSources(e);
689
+ if (e.o?.oe) if (e.o !== null) e.o.oe = false;
690
+ // The pending window is over; its quiet classification dies with it.
691
+ // (Unconditional: _reask is baked into the node literals, so this is a
692
+ // plain store to an existing slot — no shape change.)
693
+ if (e.o !== null) e.o.Ee = false;
694
+ e.S = t ? 0 : e.S & STATUS_UNINITIALIZED;
695
+ if (e.o?._) setPendingError(e);
696
+ // Update pending signal for isPending() reactivity (companions only exist
697
+ // once the verdict layer created them, which installs the hooks).
698
+ if (e.o?.he || e.o?.Ge) GlobalQueue.le(e);
699
+ if (e.o?.i && e.C & CONFIG_CHILD_COMPANIONS && GlobalQueue.be !== null) GlobalQueue.be(e);
700
+ const n = statusNotifierOf(e);
701
+ if (n) n.call(e);
702
+ }
703
+
704
+ /**
705
+ * Status clear for a flight LANDING (asyncWrite). A landing answers the
706
+ * node's OWN question — it retires the node's self entry, not the pending
707
+ * state its sources propagated onto it. An input re-asked while this flight
708
+ * was up (a second write to the signal feeding `a` while `b`'s first flight
709
+ * is in the air, #3373) marks `b` pending on `a` by propagation, with `b`'s
710
+ * flight still current: nothing superseded it (the re-ask only changed `a`'s
711
+ * status, not yet its value), so the landing arrives, and a full clear made
712
+ * `b` answer with the stale value — the transaction's reporter for `a` found
713
+ * nothing pending below it and committed the newer signal beside the older
714
+ * derived value (`2 / 1`); `isPending(b)` read false for the gap (#3376).
715
+ * With another source still pending the node stays derivatively pending on
716
+ * it; the landed value is written below (the staged answer is still the
717
+ * answer for the inputs it was asked with) and the input's own settle
718
+ * releases it, or its value change recomputes the node into a fresh flight.
719
+ * `_blocked` clears like a full clear: a landing that passed the `_inFlight`
720
+ * guard was not superseded by a re-run (recompute nulls `_inFlight` first),
721
+ * so the flag is the flight's own registration throw — the input settling
722
+ * unchanged must not re-run the node (an extra flight for the same inputs).
723
+ * The node is already STATUS_PENDING in that branch (only notifyStatus fills
724
+ * the set, with status; a loading-window park cannot coexist with a live
725
+ * flight since registration drops the set), so the flags only change when
726
+ * the first landing retires UNINITIALIZED. `_error` must move off self: a
727
+ * reader thrown NotReady(self) would park on a retired entry. Companions
728
+ * keep their verdict (pending before and after; the write re-syncs them).
729
+ */ function landStatus(e, t = false) {
730
+ const n = e.o?.ut;
731
+ // (The full clear below drops the set whether or not self was retired first.)
732
+ if (n && (n.delete(e), n.size)) {
733
+ e.o.oe = false;
734
+ if (t) e.S = STATUS_PENDING;
735
+ setPendingError(e, n.values().next().value);
736
+ } else clearStatus(e, t);
737
+ }
738
+
739
+ function notifyStatus(e, t, n, r, o) {
740
+ // Wrap regular errors to track source node
741
+ if (t === STATUS_ERROR && !(n instanceof StatusError) && !(n instanceof NotReadyError)) n = new StatusError(e, n);
742
+ const i = t === STATUS_PENDING && n instanceof NotReadyError ? n.source : undefined;
743
+ const s = i === e;
744
+ // An optimistic node (a WRITTEN override slot) pending derivatively is a
745
+ // boundary: its override is the answer, pending stops here (A17). A
746
+ // derived override (#3479) is a previous speculative answer on a plain
747
+ // member — pending flows through it as through any memo.
748
+ const u = t === STATUS_PENDING && e.o?.Ne !== undefined && !(e.C & CONFIG_DERIVED_OVERRIDE) && !s;
749
+ const l = u && hasActiveOverride(e);
750
+ if (!r) {
751
+ // Lane before companions: the companion pokes below may create the
752
+ // node's pending-signal lane, whose parent is read from the node's lane
753
+ // at creation. Assigned after them (as it was), a node made pending by
754
+ // propagation before it rode the lane got a parentless companion lane,
755
+ // and the isPending reader that also depends on the node merged it into
756
+ // the held lane — the verdict then waited on the async it reports (#3379).
757
+ if (o) assignOrMergeLane(e, o);
758
+ if (t === STATUS_PENDING && i) {
759
+ addPendingSource(e, i);
760
+ // A fresh flight from a settled state starts with its inputs unpublished
761
+ // (a replacement flight while still pending keeps the mark: the first
762
+ // flight's committed inputs are still the frame).
763
+ if (!(e.S & STATUS_PENDING)) e.C &= ~CONFIG_INPUTS_PUBLISHED;
764
+ e.S = STATUS_PENDING | e.S & STATUS_UNINITIALIZED;
765
+ // Preserve the current source on this propagation so render-effect notification
766
+ // can register every distinct pending source with the transition.
767
+ setPendingError(e, i, n);
768
+ } else {
769
+ clearPendingSources(e);
770
+ e.S = t | (t !== STATUS_ERROR ? e.S & STATUS_UNINITIALIZED : 0);
771
+ ext(e)._ = n;
772
+ }
773
+ GlobalQueue.le?.(e);
774
+ if (e.o?.i && e.C & CONFIG_CHILD_COMPANIONS && GlobalQueue.be !== null) GlobalQueue.be(e);
775
+ }
776
+ const f = r || l;
777
+ const a = r || u ? undefined : o;
778
+ const c = statusNotifierOf(e);
779
+ if (c) {
780
+ if (r && t === STATUS_PENDING) {
781
+ return;
782
+ }
783
+ if (f) {
784
+ c.call(e, t, n);
785
+ } else {
786
+ c.call(e);
787
+ }
788
+ return;
789
+ }
790
+ forEachDependent(e, (e, r) => {
791
+ e.fe = clock;
792
+ // A pending mark on a kept-tail link re-derives the subscriber instead of
793
+ // marking it (A30, #3494 review; fuzzer latest-1 #2141; #3519 review).
794
+ // Past `_depsTail` lie the committed frame's deps, kept by A30 because
795
+ // that frame still derives from them while the pass that dropped them is
796
+ // held (staged, or unchanged and parked). A source going pending there is
797
+ // a question for the node's NEXT pass, not a fact about its current one:
798
+ // marked, the node was registered as the flight's reporter and its holder
799
+ // entangled with the flight (the A15 arm below) on a dep the held frame
800
+ // never reads — an orphaned fetch held the truth (a hide joined to a
801
+ // parked action, A34), and a manual flight nobody awaited held a gated
802
+ // reader hidden forever (fuzzer branches-1 #1105). Skipped, the committed
803
+ // frame published stale beside its new inputs (`query=1` beside a
804
+ // `selected` derived from `remote(0)`). Re-derived, the pass decides: it
805
+ // reads the dep and registers through its own read, or reads a held input
806
+ // and enters that transaction (A29), or reads neither and is done. Clears
807
+ // and errors still ride every link. A link inside the prefix carries the
808
+ // pass's generation (`link()`), so the test is O(1); mid-pass the prefix
809
+ // is what the pass has read so far, and the heap refuses a recomputing
810
+ // node — a dep it has yet to reach registers through its own read.
811
+ if (t === STATUS_PENDING && r.pe !== e.ye) {
812
+ enqueueSub(e);
813
+ schedule();
814
+ return;
815
+ }
816
+ if (t === STATUS_PENDING && i && !e.o?.ut?.has(i) || t !== STATUS_PENDING && (e.o?._ !== n || e.o?.ut)) {
817
+ // A pending-observer link is the subscription an `isPending` read created.
818
+ // It exists so the observer re-runs when the source settles, but it must
819
+ // not carry a real (non-NotReadyError) error — the synchronous `isPending`
820
+ // read swallows those, and the async path must match. Re-run the observer
821
+ // so `isPending` re-evaluates (to not-pending) instead of forwarding.
822
+ if (r.Ce && t !== STATUS_PENDING && !(n instanceof NotReadyError)) {
823
+ enqueueSub(e);
824
+ schedule();
825
+ return;
826
+ }
827
+ // A memo another live transaction HOLDS — pending on its work, or
828
+ // staged by it — made pending by THIS flight cannot reveal before the
829
+ // flight lands: the two settle as one unit (A15, a shared derivation of
830
+ // both — #3443). Propagation marks the held memo without recomputing it
831
+ // (its inputs' values are unchanged), so this is the one moment the
832
+ // entanglement is known; the memo's stamped re-entry at its next pass
833
+ // came too late — the holder's own flight landed first and revealed the
834
+ // inputs beside the stale sum. The stamp alone decides nothing (#3334):
835
+ // a node the transaction once queued but holds nothing of — a switch's
836
+ // shared output whose first flight the second write superseded — must
837
+ // not drag the older flight into the newer reveal. Effects entangle
838
+ // nothing (A15 shared-hole corollary): their reader registers with the
839
+ // flight's transaction at queue notification.
840
+ if (!f) e.Te ? i && !e.Pe && (e.S & STATUS_PENDING || e._e !== NOT_PENDING) && globalQueue.initTransition(e.Te) : queuePendingNode(e);
841
+ notifyStatus(e, t, n, f, a);
842
+ }
843
+ });
844
+ }
845
+
846
+ export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, parkLoadingWindow, releaseFlightTeardown, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource };