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

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