@solidjs/signals 2.0.0-rc.0 → 2.0.0-rc.10

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