@solidjs/signals 2.0.0-rc.13 → 2.0.0-rc.14

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