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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/dev-shared.js +1399 -285
  2. package/dist/dev.attribution.js +459 -307
  3. package/dist/dev.js +1864 -435
  4. package/dist/observe/affects.js +3 -1
  5. package/dist/observe/attribution.js +7 -1
  6. package/dist/observe/boundaries.js +209 -154
  7. package/dist/observe/core/action.js +18 -8
  8. package/dist/observe/core/async.js +220 -110
  9. package/dist/observe/core/attribution-costs.js +66 -0
  10. package/dist/observe/core/attribution-feedback.js +282 -0
  11. package/dist/observe/core/attribution-hooks.js +23 -1
  12. package/dist/observe/core/attribution-queries.js +28 -0
  13. package/dist/observe/core/attribution.js +262 -485
  14. package/dist/observe/core/constants.js +34 -1
  15. package/dist/observe/core/context.js +3 -3
  16. package/dist/observe/core/core.js +867 -367
  17. package/dist/observe/core/dev.js +78 -17
  18. package/dist/observe/core/effect.js +67 -51
  19. package/dist/observe/core/error-hooks.js +71 -0
  20. package/dist/observe/core/external.js +4 -4
  21. package/dist/observe/core/graph.js +37 -37
  22. package/dist/observe/core/heap.js +45 -45
  23. package/dist/observe/core/invariants.js +2 -0
  24. package/dist/observe/core/lanes.js +86 -49
  25. package/dist/observe/core/optimistic.js +242 -95
  26. package/dist/observe/core/owner.js +98 -84
  27. package/dist/observe/core/scheduler.js +543 -305
  28. package/dist/observe/core/verdict.js +247 -129
  29. package/dist/observe/index.js +7 -3
  30. package/dist/observe/map.js +126 -124
  31. package/dist/observe/signals.js +33 -17
  32. package/dist/observe/store/index.js +2 -0
  33. package/dist/observe/store/next/optimistic.js +141 -132
  34. package/dist/observe/store/next/projection.js +34 -21
  35. package/dist/observe/store/next/reconcile.js +58 -56
  36. package/dist/observe/store/next/store.js +260 -146
  37. package/dist/observe/store/store.js +5 -3
  38. package/dist/observe/store/utils.js +948 -135
  39. package/dist/prod/attribution.js +26 -17
  40. package/dist/prod/boundaries.js +128 -76
  41. package/dist/prod/core/action.js +16 -8
  42. package/dist/prod/core/async.js +251 -143
  43. package/dist/prod/core/constants.js +34 -1
  44. package/dist/prod/core/context.js +3 -3
  45. package/dist/prod/core/core.js +843 -347
  46. package/dist/prod/core/dev.js +17 -1
  47. package/dist/prod/core/effect.js +48 -34
  48. package/dist/prod/core/error-hooks.js +71 -0
  49. package/dist/prod/core/external.js +4 -4
  50. package/dist/prod/core/graph.js +37 -37
  51. package/dist/prod/core/heap.js +45 -45
  52. package/dist/prod/core/lanes.js +90 -53
  53. package/dist/prod/core/optimistic.js +247 -100
  54. package/dist/prod/core/owner.js +57 -45
  55. package/dist/prod/core/scheduler.js +543 -305
  56. package/dist/prod/core/verdict.js +245 -127
  57. package/dist/prod/index.js +7 -3
  58. package/dist/prod/map.js +112 -112
  59. package/dist/prod/signals.js +28 -12
  60. package/dist/prod/store/next/optimistic.js +135 -128
  61. package/dist/prod/store/next/projection.js +31 -20
  62. package/dist/prod/store/next/store.js +325 -252
  63. package/dist/prod/store/store.js +2 -2
  64. package/dist/prod/store/utils.js +946 -135
  65. package/dist/types/attribution.d.ts +7 -2
  66. package/dist/types/attribution.prod.d.ts +10 -1
  67. package/dist/types/boundaries.d.ts +10 -1
  68. package/dist/types/core/action.d.ts +12 -5
  69. package/dist/types/core/attribution-costs.d.ts +35 -0
  70. package/dist/types/core/attribution-feedback.d.ts +133 -0
  71. package/dist/types/core/attribution-hooks.d.ts +28 -3
  72. package/dist/types/core/attribution-queries.d.ts +10 -0
  73. package/dist/types/core/attribution.d.ts +51 -172
  74. package/dist/types/core/constants.d.ts +33 -0
  75. package/dist/types/core/core.d.ts +186 -5
  76. package/dist/types/core/dev.d.ts +129 -9
  77. package/dist/types/core/error-hooks.d.ts +71 -0
  78. package/dist/types/core/index.d.ts +3 -1
  79. package/dist/types/core/invariants.d.ts +4 -0
  80. package/dist/types/core/lanes.d.ts +31 -4
  81. package/dist/types/core/scheduler.d.ts +95 -2
  82. package/dist/types/core/types.d.ts +3 -0
  83. package/dist/types/index.d.ts +2 -2
  84. package/dist/types/signals.d.ts +8 -0
  85. package/dist/types/store/index.d.ts +2 -1
  86. package/dist/types/store/next/optimistic.d.ts +1 -1
  87. package/dist/types/store/next/store.d.ts +6 -5
  88. package/dist/types/store/next/target.d.ts +1 -1
  89. package/dist/types/store/utils.d.ts +177 -6
  90. package/package.json +1 -1
@@ -1,16 +1,16 @@
1
- import { NOT_PENDING, unwrapOverride, STATUS_UNINITIALIZED, REACTIVE_DIRTY, REACTIVE_CHECK, CONFIG_HAS_LANE, OVERRIDE_UNDEFINED, STATUS_PENDING, CONFIG_OVERRIDE_SUPERSEDED, EFFECT_RENDER, CONFIG_AUTHORITATIVE_OBSERVED, REACTIVE_MANUAL_WRITE, REACTIVE_OPTIMISTIC_DIRTY, LANE_RUN, EFFECT_USER } from "./constants.js";
1
+ import { NOT_PENDING, unwrapOverride, STATUS_UNINITIALIZED, REACTIVE_DIRTY, REACTIVE_CHECK, CONFIG_HAS_LANE, OVERRIDE_UNDEFINED, STATUS_PENDING, CONFIG_DERIVED_OVERRIDE, CONFIG_OPTIMISTIC, CONFIG_OVERRIDE_SUPERSEDED, EFFECT_RENDER, CONFIG_AUTHORITATIVE_OBSERVED, REACTIVE_MANUAL_WRITE, REACTIVE_OPTIMISTIC_DIRTY, LANE_RUN, EFFECT_USER } from "./constants.js";
2
2
 
3
3
  import { attrHooks } from "./attribution-hooks.js";
4
4
 
5
- import { ext, stale, latestReadActive, currentOptimisticLane } from "./core.js";
5
+ import { ext, hasActiveOverride, stale, enterStagedRead, latestReadActive, currentOptimisticLane } from "./core.js";
6
6
 
7
7
  import { NotReadyError } from "./error.js";
8
8
 
9
9
  import "./invariants.js";
10
10
 
11
- import { resolveTransition, getOrCreateLane, hasActiveOverride, activeLanes, signalLanes, laneHeld, findLane, resolveLane, assignOrMergeLane } from "./lanes.js";
11
+ import { resolveTransition, getOrCreateLane, activeLanes, signalLanes, laneHeld, findLane, resolveLane, readsHeldCommitted, assignOrMergeLane } from "./lanes.js";
12
12
 
13
- import { GlobalQueue, activeTransition, globalQueue, origin, clock, insertSubs, schedule, queuePendingNode } from "./scheduler.js";
13
+ import { GlobalQueue, activeTransition, globalQueue, origin, clock, insertSubs, schedule, sourceObserved, currentTransition, queuePendingNode } from "./scheduler.js";
14
14
 
15
15
  /**
16
16
  * The optimistic write engine, moved out of core.ts/scheduler.ts. Everything
@@ -26,18 +26,18 @@ import { GlobalQueue, activeTransition, globalQueue, origin, clock, insertSubs,
26
26
  * once the gate holds — the same late-binding contract as verdict.ts.
27
27
  */
28
28
  /** The optimistic half of setSignal, fired when `_overrideValue !== undefined`. */ function optimisticWrite(e, n) {
29
- const i = e.o?.De !== NOT_PENDING;
30
- const t = i ? unwrapOverride(e.o?.De) : e.ge;
29
+ const i = e.o?.Ne !== NOT_PENDING;
30
+ const t = i ? unwrapOverride(e.o?.Ne) : e.Ae;
31
31
  if (typeof n === "function") n = n(t);
32
- const u = !!(e.S & STATUS_UNINITIALIZED) ||
32
+ const r = !!(e.S & STATUS_UNINITIALIZED) ||
33
33
  // A dirty node's _value is stale (its queued recompute hasn't run — e.g.
34
34
  // a latest() shadow marked by the previous landing's companion snap), so
35
35
  // equality against it must not swallow the write. Without this, a sync
36
36
  // push returning the shadow to that stale value was dropped, the snap
37
37
  // recompute then committed the parent's old value, and the banner showed
38
38
  // the previous transition's target (#3041 follow-up).
39
- !!((e.it ?? 0) & (REACTIVE_DIRTY | REACTIVE_CHECK)) || !e.Ue || !e.Ue(t, n);
40
- if (!u) {
39
+ !!((e.ft ?? 0) & (REACTIVE_DIRTY | REACTIVE_CHECK)) || !e.Re || !e.Re(t, n);
40
+ if (!r) {
41
41
  // Same-value write with an active override still entangles the current
42
42
  // action's transition — the hold must outlast all overlapping actions —
43
43
  // and renews the override's PROVENANCE: the newer action re-asks the
@@ -47,7 +47,7 @@ import { GlobalQueue, activeTransition, globalQueue, origin, clock, insertSubs,
47
47
  if (i) {
48
48
  const n = resolveTransition(e);
49
49
  if (n && activeTransition !== n) globalQueue.initTransition(n);
50
- if (origin > e.o.Zt) e.o.Zt = origin;
50
+ if (origin > e.o.zt) e.o.zt = origin;
51
51
  }
52
52
  return n;
53
53
  }
@@ -55,42 +55,93 @@ import { GlobalQueue, activeTransition, globalQueue, origin, clock, insertSubs,
55
55
  // No revert target is stashed: while the override is active every reader
56
56
  // sees it (A17), so authoritative arrivals commit silently into _value and
57
57
  // reverting is just dropping the override — _value is already correct.
58
- else globalQueue.m.en.push(e);
58
+ else globalQueue.m.tn.push(e);
59
59
  // Stamp ownership on the node (post-merge, so entangled writers share the
60
60
  // joint root). resolveTransition prefers this over the lane's _transition,
61
61
  // which a shared subscriber can merge across transactions (#2912).
62
- ext(e).Yt = activeTransition;
63
- ext(e).Lt = clock;
62
+ ext(e).jt = activeTransition;
63
+ ext(e).Qt = clock;
64
64
  // Provenance: the action asking. An answer an OLDER action's flight brings
65
65
  // back is a stale question and holds silently to commit (#3331).
66
- ext(e).Zt = origin;
67
- const r = getOrCreateLane(e);
68
- ext(e).Te = r;
66
+ ext(e).zt = origin;
67
+ const u = getOrCreateLane(e);
68
+ ext(e).de = u;
69
69
  // A fresh override re-masks: whatever truth is staged, this write is the
70
70
  // value for the graph again until the source answers it (#3331).
71
- e.T = (e.T | CONFIG_HAS_LANE) & ~CONFIG_OVERRIDE_SUPERSEDED;
71
+ e.C = (e.C | CONFIG_HAS_LANE) & -8912897;
72
72
  // Literal undefined must not land raw: the slot doubles as the optimistic
73
73
  // brand, and erasing it makes the write invisible and routes follow-up
74
74
  // writes off the optimistic path into permanent commits (#2898).
75
- ext(e).De = n === undefined ? OVERRIDE_UNDEFINED : n;
75
+ ext(e).Ne = n === undefined ? OVERRIDE_UNDEFINED : n;
76
76
  // syncCompanions only pokes _pendingSignal/_latestValueComputed — with
77
77
  // neither companion present the call is a guaranteed no-op.
78
- (e.o?.be !== undefined || e.o?.pe !== undefined) && GlobalQueue.Ae !== null && GlobalQueue.Ae(e, n);
79
- if (e.ot !== undefined) e.ae = clock;
78
+ (e.o?.he !== undefined || e.o?.Ge !== undefined) && GlobalQueue.De !== null && GlobalQueue.De(e, n);
79
+ if (e.ht !== undefined) e.fe = clock;
80
80
  // §12e: computed-only slot
81
81
  insertSubs(e, true);
82
82
  schedule();
83
83
  return n;
84
84
  }
85
85
 
86
+ /**
87
+ * Lanes stage (#3479): a lane pass's publish for a memo. An optimistic
88
+ * derivation is an override — the speculative result lives in the override
89
+ * slot, `_value` stays the committed truth. The whole optimistic frame is then
90
+ * in one place: the lane's readers and untracked reads see it (A17), a render
91
+ * effect off the held lane sees the committed frame whole (readsHeldCommitted,
92
+ * #3460) — the source's shadow AND its derivations — where a speculative
93
+ * `_value` beside a committed shadow tore it. The node joins the
94
+ * transaction's optimistic nodes on its first speculative publish; the revert
95
+ * drops the override and re-derives it from the truth (a derived override has
96
+ * no truth of its own — see resolveOptimisticNodes, endOptimism).
97
+ */ function laneOverride(e, n, i) {
98
+ // The wake-only channel (#3009, see recomputeLane): a plain write to a
99
+ // latest()-tracked source rides a companion-sourced lane with no
100
+ // transaction on either side only to wake the verdict companions. Nothing
101
+ // is speculative — the pass commits directly, as any plain write does.
102
+ i = findLane(i);
103
+ if (!i.Te && !activeTransition && i.Ln.o?.nn !== undefined) {
104
+ e.Ae = n;
105
+ return;
106
+ }
107
+ if (!hasActiveOverride(e)) {
108
+ // It reverts with the lane's transaction (a landing runs outside any
109
+ // flush, where the ambient batch would revert it at its own end); an
110
+ // orphan lane's falls to the batch, adopted with it (initTransition) as a
111
+ // write's is. No `_overrideOwner`: a derived override is a plain member,
112
+ // its transaction its lane's (resolveTransition), and it merges lanes
113
+ // through itself as any shared reader does (assignOrMergeLane). No
114
+ // provenance stamp either: not an intent, any truth supersedes it.
115
+ (i.Te ? currentTransition(i.Te) : globalQueue.m).tn.push(e);
116
+ }
117
+ // A lane pass's output is a derivation — also over a WRITTEN guess it
118
+ // corrects (a `createOptimistic(fn)` re-derived from fresh upstream data):
119
+ // the guess is gone, the slot holds fn's answer, and the revert promotes it
120
+ // rather than dropping to a stale `_value` and re-asking downstream (the
121
+ // next user write re-arms the guess: optimisticWrite clears the bit). No
122
+ // `_overrideTime` stamp: that marks a user WRITE unflushed until the flush
123
+ // that carries it (A28) and shields it from same-tick supersession — a pass's
124
+ // result is neither (a pulled ownerless memo publishes outside any flush).
125
+ // A fresh lane frame ends a supersession in force: the pass just dropped
126
+ // the staged truth it pointed at (recompute, INV-11 corollary) — left set,
127
+ // the flag served a `_value` never committed (fuzzer latest-1 #2481).
128
+ e.C = (e.C | CONFIG_DERIVED_OVERRIDE) & ~CONFIG_OVERRIDE_SUPERSEDED;
129
+ e.o.Ne = n === undefined ? OVERRIDE_UNDEFINED : n;
130
+ }
131
+
86
132
  /**
87
133
  * transitionComplete's override blockage: a settling transition stays open
88
134
  * while one of its optimistic nodes holds an active override that is still
89
- * pending on real (non-affects-sentinel) async.
135
+ * pending on real (non-affects-sentinel) async. A derived override's flight is
136
+ * the lane's own work, never authoritative — it does not hold the settle.
137
+ * Neither is a companion's (#3494): the `latest()` shadow backfilled under the
138
+ * owner's transaction (A28 (3)) is an observation of the flight, and a
139
+ * mainline `latest(details)` after the flight's last reader unmounted held the
140
+ * released write until the orphaned request landed.
90
141
  */ function transitionBlocked(e) {
91
- for (let n = 0; n < e.en.length; n++) {
92
- const i = e.en[n];
93
- if (hasActiveOverride(i) && "S" in i && i.S & STATUS_PENDING && i.o?._ instanceof NotReadyError) {
142
+ for (let n = 0; n < e.tn.length; n++) {
143
+ const i = e.tn[n];
144
+ if (!(i.C & CONFIG_DERIVED_OVERRIDE) && i.o?.nn === undefined && hasActiveOverride(i) && "S" in i && i.S & STATUS_PENDING && i.o?._ instanceof NotReadyError) {
94
145
  return true;
95
146
  }
96
147
  }
@@ -104,30 +155,42 @@ function resolveOptimisticNodes(e) {
104
155
  const n = e.length;
105
156
  for (let i = 0; i < n; i++) {
106
157
  const n = e[i];
107
- if (n.o !== null) n.o.Te = undefined;
158
+ if (n.o !== null) n.o.de = undefined;
108
159
  // Revert is a pure drop: there is no revert target to commit —
109
160
  // override-covered authoritative values hold in _pendingValue and
110
161
  // elevate on their OWN transition's schedule (A18 as re-ruled 2026-07-07).
111
162
  if (!(n.S & STATUS_PENDING)) n.S &= ~STATUS_UNINITIALIZED;
112
- const t = n.o?.De;
113
- ext(n).De = NOT_PENDING;
163
+ const t = n.o?.Ne;
164
+ // A derived override (lanes stage, #3479) has no truth of its own: the
165
+ // slot disarms — the memo is plain again — and the override PROMOTES to
166
+ // `_value`. Not superseded, nothing it derives from told it otherwise
167
+ // (a source override that reverts to a differing truth dirties it just
168
+ // above — sources join this list before their derivations — and its
169
+ // recompute then replaces the promotion), so by the graph's invariant
170
+ // the override IS what a recompute from the truth yields. Re-deriving
171
+ // instead re-asked an async member's flight and held the transaction on
172
+ // it — a waterfall after the reveal.
173
+ const r = n.C & CONFIG_DERIVED_OVERRIDE;
174
+ ext(n).Ne = r && !(n.C & CONFIG_OPTIMISTIC) ? undefined : NOT_PENDING;
114
175
  // A superseded override's subscribers already re-derived from the truth
115
176
  // when it arrived (#3331) — the drop changes nothing they read. Everyone
116
177
  // else learns of the correction here: this drop IS their notification.
117
- const u = (n.T & CONFIG_OVERRIDE_SUPERSEDED) !== 0;
118
- n.T &= ~CONFIG_OVERRIDE_SUPERSEDED;
119
- if (!u && t !== NOT_PENDING && n.ge !== unwrapOverride(t)) insertSubs(n, true);
120
- n.Ee = null;
121
- if (n.o !== null) n.o.Yt = null;
178
+ const u = (n.C & CONFIG_OVERRIDE_SUPERSEDED) !== 0;
179
+ n.C &= -8912897;
180
+ if (!u && t !== NOT_PENDING && n.Ae !== unwrapOverride(t)) {
181
+ if (r) n.Ae = unwrapOverride(t); else insertSubs(n, true);
182
+ }
183
+ n.Te = null;
184
+ if (n.o !== null) n.o.jt = null;
122
185
  }
123
186
  // Settlement checkpoint (#2838): companions caught in this batch (or owned
124
187
  // by a node in it) re-derive from committed state, so verdicts survive the
125
188
  // transition that produced them (A19 — pending is a property of the data).
126
189
  for (let i = 0; i < n; i++) {
127
190
  const n = e[i];
128
- if (n.o?.be || n.o?.pe) GlobalQueue.Dn(n);
129
- const t = n.o?.Qe;
130
- if (t && (t.o?.be === n || t.o?.pe === n)) GlobalQueue.Dn(t);
191
+ if (n.o?.he || n.o?.Ge) GlobalQueue.Qn(n);
192
+ const t = n.o?.nn;
193
+ if (t && (t.o?.he === n || t.o?.Ge === n)) GlobalQueue.Qn(t);
131
194
  }
132
195
  e.splice(0, n);
133
196
  }
@@ -162,13 +225,13 @@ function resolveOptimisticNodes(e) {
162
225
  * wake hook is installed by the setters of that bit; optional here because
163
226
  * the bit only implies the optimistic engine was consulted.
164
227
  */ function supersedeOverride(e, n) {
165
- const i = !e.Ue || !e.Ue(n, unwrapOverride(e.o.De));
228
+ const i = !e.Re || !e.Re(n, unwrapOverride(e.o.Ne));
166
229
  if (!i) {
167
- if (!(e.T & CONFIG_OVERRIDE_SUPERSEDED)) {
168
- if (e.T & CONFIG_AUTHORITATIVE_OBSERVED) GlobalQueue.En?.(e);
230
+ if (!(e.C & CONFIG_OVERRIDE_SUPERSEDED)) {
231
+ if (e.C & CONFIG_AUTHORITATIVE_OBSERVED) GlobalQueue.Tn?.(e);
169
232
  return;
170
233
  }
171
- e.T &= ~CONFIG_OVERRIDE_SUPERSEDED;
234
+ e.C &= ~CONFIG_OVERRIDE_SUPERSEDED;
172
235
  } else {
173
236
  // Provenance (#3331): an answer brought back by an OLDER action than the
174
237
  // one that wrote this override answers a question the user has since
@@ -176,19 +239,19 @@ function resolveOptimisticNodes(e) {
176
239
  // still the truth when the transaction commits, but it does not move the
177
240
  // graph now — a slow source must not leak back in over a newer intent.
178
241
  // 0 is mainline (no action): always the current question.
179
- if (origin && origin < e.o.Zt) return;
180
- e.T |= CONFIG_OVERRIDE_SUPERSEDED;
181
- const n = e.o?.Te;
242
+ if (origin && origin < e.o.zt) return;
243
+ e.C |= CONFIG_OVERRIDE_SUPERSEDED;
244
+ const n = e.o?.de;
182
245
  if (n) {
183
246
  const i = findLane(n);
184
247
  const t = [ e ];
185
248
  while (t.length) {
186
249
  const e = t.pop();
187
- const n = e.o?.Te;
250
+ const n = e.o?.de;
188
251
  if (!n || findLane(n) !== i) continue;
189
- e.o.Te = undefined;
190
- i._e.delete(e);
191
- for (let n = e.u; n !== null; n = n.se) t.push(n.le);
252
+ e.o.de = undefined;
253
+ i.Ie.delete(e);
254
+ for (let n = e.u; n !== null; n = n.ie) t.push(n.se);
192
255
  }
193
256
  }
194
257
  }
@@ -196,15 +259,95 @@ function resolveOptimisticNodes(e) {
196
259
  insertSubs(e);
197
260
  }
198
261
 
262
+ /**
263
+ * The flush's pre-verdict step once the action bodies have ended (#3427).
264
+ * The bodies were the optimism's justification; with them over, the
265
+ * overrides still in force revert at the settle — unless the transaction is
266
+ * still waiting on AUTHORITATIVE work: an override node's own source in
267
+ * flight (`transitionBlocked` — that answer supersedes or confirms on
268
+ * arrival), or a held flight that does not derive from an override (a plain
269
+ * write's load the action asked for). Through that window the optimistic
270
+ * world stands: a co-written "saving" flag stays rendered until the page it
271
+ * covers lands (A17 — the optimistic world is one).
272
+ *
273
+ * The flights that DO derive from an override — routed through a live lane —
274
+ * are obsolete: their input is the guess that is about to revert, and nobody
275
+ * will read their answer. With nothing authoritative left, each override's
276
+ * truth is already here (the staged value an A17-silent landing left, else
277
+ * the committed value) and supersedes it now, exactly as an arriving
278
+ * differing truth does (A18, #3331): the graph re-derives from it as this
279
+ * transaction's held work — a lane-derived memo re-asks with the truth, its
280
+ * other changed inputs included — and the transaction settles when THAT
281
+ * lands. Before this the settle first waited for the obsolete flight,
282
+ * revealed the obsolete optimistic frame when it landed, and only then
283
+ * started the correction — a waterfall with a flash in the middle. Returns
284
+ * whether it superseded anything (the caller re-runs the heap).
285
+ *
286
+ * The optimistic world is one, so it ends early only when all of it can. An
287
+ * optimistic STORE edit cannot yet: its truth is the base layer under an
288
+ * overlay that `_clearOptimisticStores` folds off at settlement, with no
289
+ * tracked/displayed split — superseding its tracking signals alone would
290
+ * re-derive readers against a still-displayed overlay (and a memo reading
291
+ * both a signal and the store would ask a mixed question). A transaction
292
+ * holding one keeps the settle-then-revert order throughout. Companions
293
+ * (`_parentSource` set) are optimistic nodes too — a verdict written through
294
+ * the optimistic path so it flushes ahead of the hold — but they answer for
295
+ * their owner and snap at settlement (`_snapCompanions`), not here.
296
+ */ function endOptimism(e) {
297
+ if (!e.Tt || e.dt.length || !e.tn.length || e.rn.size || transitionBlocked(e)) return false;
298
+ for (const n of e.ot.keys()) if (sourceObserved(e, n, e) && n.o?.ut?.has(n) && !resolveLane(n)) return false;
299
+ let n = false;
300
+ for (const i of e.tn) {
301
+ if (!hasActiveOverride(i) || i.o.nn ||
302
+ // A derived override re-derives when its source's is superseded.
303
+ i.C & (CONFIG_OVERRIDE_SUPERSEDED | CONFIG_DERIVED_OVERRIDE) || i.S & STATUS_UNINITIALIZED) continue;
304
+ const e = i._e !== NOT_PENDING ? i._e : i.Ae;
305
+ if (!i.Re || !i.Re(e, unwrapOverride(i.o.Ne))) {
306
+ supersedeOverride(i, e);
307
+ // Judged by the mark, not the call: a provenance refusal (an older
308
+ // action's window is still open) leaves the node for a later pass.
309
+ if (i.C & CONFIG_OVERRIDE_SUPERSEDED) n = true;
310
+ }
311
+ }
312
+ return n;
313
+ }
314
+
199
315
  /** read()'s value for a tracked reader of a superseded node: the truth —
200
316
  * staged, or already committed (a mainline landing commits at the head of
201
317
  * its flush, ahead of the heap run, and the override drops only at the
202
318
  * batch's end; in between the graph must not fall back to the override it
203
319
  * has left) — or the displayed override for a stale (render) reader of some
204
320
  * OTHER transaction, the same visibility a foreign transaction's staged
205
- * write has. */ function supersededRead(e) {
206
- if (stale && e.Ee && activeTransition !== e.Ee) return unwrapOverride(e.o?.De);
207
- return e.Pe !== NOT_PENDING ? e.Pe : e.ge;
321
+ * write has. */
322
+ /**
323
+ * A tracked read of an active override (read()'s override arm). Lanes mirror
324
+ * transitions (#3460): a render effect OFF the override's held lane — re-run
325
+ * by a sync write, or mounted mid-hold — sees the committed value, as a stale
326
+ * reader of a held transaction does, and publishes now; the lane's release
327
+ * re-runs it (readsHeldCommitted). The lane defers the override's own readers'
328
+ * runs, so the committed value is what is on screen — the override is the
329
+ * visible value only once the lane has revealed (or, demoted at body-end,
330
+ * A18). Otherwise the override displays, unless the node's own source
331
+ * answered with a DIFFERENT value (A18 supersession, #3331): the optimism is
332
+ * over for the graph — a tracked reader sees the staged truth — while the
333
+ * override remains the DISPLAYED value for untracked reads (and for a stale
334
+ * reader of some other transaction).
335
+ */ function overrideRead(e, n) {
336
+ if (stale && readsHeldCommitted(e, n)) return e.Ae;
337
+ if (!(e.C & CONFIG_OVERRIDE_SUPERSEDED)) return unwrapOverride(e.o?.Ne);
338
+ // The owning transaction: `_overrideOwner` (#2912), not the stamp — an
339
+ // override written directly inside an action never passes the adoption
340
+ // loop that stamps `_transition`, and a body-end supersession (#3427)
341
+ // stages nothing that would queue it. Without the owner a stale reader of
342
+ // a body-ended node read the committed truth beside a display still
343
+ // showing the override.
344
+ const i = resolveTransition(e);
345
+ if (stale && i && activeTransition !== i) return unwrapOverride(e.o?.Ne);
346
+ // A superseded read is a staged read (A29) whether the truth is staged or
347
+ // already committed: the pass that derives from it derives from the
348
+ // owning transaction's world (the override is still displayed by it).
349
+ enterStagedRead(e, i);
350
+ return e._e !== NOT_PENDING ? e._e : e.Ae;
208
351
  }
209
352
 
210
353
  /**
@@ -219,12 +362,12 @@ function resolveOptimisticNodes(e) {
219
362
  * landing took setSignal's plain path: a differing truth staged silently
220
363
  * under the override and the graph never moved until the commit.
221
364
  */ function landOnOverride(e, n) {
222
- const i = e.Pe === NOT_PENDING ? e.ge : e.Pe;
365
+ const i = e._e === NOT_PENDING ? e.Ae : e._e;
223
366
  if (typeof n === "function") n = n(i);
224
367
  if (attrHooks !== null) attrHooks.write(e, i, n);
225
- if (e.Pe === NOT_PENDING) queuePendingNode(e);
226
- e.Pe = n;
227
- GlobalQueue.Ae?.(e, n);
368
+ if (e._e === NOT_PENDING) queuePendingNode(e);
369
+ e._e = n;
370
+ GlobalQueue.De?.(e, n);
228
371
  supersedeOverride(e, n);
229
372
  schedule();
230
373
  return n;
@@ -239,7 +382,7 @@ function runQueue(e, n) {
239
382
  * laneHeld).
240
383
  */ function runLaneEffects(e) {
241
384
  for (const n of activeLanes) {
242
- if (n.gn || laneHeld(n)) continue;
385
+ if (n.wn || laneHeld(n)) continue;
243
386
  const i = n.vn[e - 1];
244
387
  if (i.length) {
245
388
  n.vn[e - 1] = [];
@@ -248,19 +391,19 @@ function runQueue(e, n) {
248
391
  }
249
392
  // Optimistic patch applications ride the same visibility slot as lane
250
393
  // effects (in-flight DOM updates); no-op unless patches registered.
251
- if (e === EFFECT_RENDER) GlobalQueue.bn?.();
394
+ if (e === EFFECT_RENDER) GlobalQueue.Vn?.();
252
395
  }
253
396
 
254
397
  function cleanupCompletedLanes(e) {
255
398
  for (const n of activeLanes) {
256
- const i = e ? n.Ee === e : !n.Ee;
399
+ const i = e ? n.Te === e : !n.Te;
257
400
  if (!i) continue;
258
- if (!n.gn) {
401
+ if (!n.wn) {
259
402
  if (n.vn[0].length) runQueue(n.vn[0], EFFECT_RENDER);
260
403
  if (n.vn[1].length) runQueue(n.vn[1], EFFECT_USER);
261
404
  }
262
- if (n.Ln.o?.Te === n) if (n.Ln.o !== null) n.Ln.o.Te = undefined;
263
- n._e.clear();
405
+ if (n.Ln.o?.de === n) if (n.Ln.o !== null) n.Ln.o.de = undefined;
406
+ n.Ie.clear();
264
407
  n.vn[0].length = 0;
265
408
  n.vn[1].length = 0;
266
409
  activeLanes.delete(n);
@@ -278,11 +421,13 @@ function cleanupCompletedLanes(e) {
278
421
  // this is only reachable under a lane, which implies the engine.
279
422
  if (e.S & STATUS_UNINITIALIZED) return true;
280
423
  // Per-lane suspension: only throw if in same lane as pending async
281
- // AND the node doesn't have an active override (overrides are the visible value,
282
- // downstream in the lane should read the override, not throw)
283
- const n = e.o?.Te;
424
+ // AND the node doesn't have an active WRITTEN override (overrides are the
425
+ // visible value, downstream in the lane should read the override, not
426
+ // throw). A derived override (#3479) is a previous speculative answer, not
427
+ // an intent: the re-ask pending behind it suspends like any lane async.
428
+ const n = e.o?.de;
284
429
  if (!n) return false;
285
- return findLane(n) === findLane(currentOptimisticLane) && !hasActiveOverride(e);
430
+ return findLane(n) === findLane(currentOptimisticLane) && (!hasActiveOverride(e) || (e.C & CONFIG_DERIVED_OVERRIDE) !== 0);
286
431
  }
287
432
 
288
433
  /**
@@ -302,10 +447,10 @@ function cleanupCompletedLanes(e) {
302
447
  * that reads a pending mid-transition write sees the committed value; the sub
303
448
  * is recorded for replay at commit.
304
449
  */ function gatedRead(e, n, i) {
305
- if (latestReadActive || e.Pe === NOT_PENDING || e.ot || n !== e && !(n.it & REACTIVE_MANUAL_WRITE)) {
450
+ if (latestReadActive || e._e === NOT_PENDING || e.ht || n !== e && !(n.ft & REACTIVE_MANUAL_WRITE)) {
306
451
  return false;
307
452
  }
308
- activeTransition.ht.add(i);
453
+ activeTransition.Ht.add(i);
309
454
  return true;
310
455
  }
311
456
 
@@ -313,7 +458,7 @@ function cleanupCompletedLanes(e) {
313
458
  * read()'s value selection under a lane: return the committed `_value` for
314
459
  * optimistic/lane-assigned signals, stale-mode reads, and pending owners.
315
460
  */ function laneReadsCommitted(e, n, i) {
316
- if (e.o?.De !== undefined || !!e.o?.Te || !!(n.S & STATUS_PENDING)) {
461
+ if (e.o?.Ne !== undefined || !!e.o?.de || !!(n.S & STATUS_PENDING)) {
317
462
  // The committed view hides a staged in-flight value that will promote
318
463
  // silently (commitPendingNode never re-notifies). gatedRead records plain
319
464
  // signals for replay at commit; async memos are excluded from it by the
@@ -328,10 +473,10 @@ function cleanupCompletedLanes(e) {
328
473
  // against an unchanged frame (#3330). An override-covered node's revert
329
474
  // notifies its own subscribers when the truth differs (resolveOptimistic
330
475
  // Nodes), so the reader is recorded only for the staged-vs-committed gap.
331
- if (e.Pe !== NOT_PENDING && e.Pe !== e.ge) (activeTransition ?? globalQueue.m).ht.add(i);
476
+ if (e._e !== NOT_PENDING && e._e !== e.Ae) (activeTransition ?? globalQueue.m).Ht.add(i);
332
477
  return true;
333
478
  }
334
- if (n === e && stale && i.o?.Qe !== e) {
479
+ if (n === e && stale && i.o?.nn !== e) {
335
480
  // The committed view can hide a staged write (a lane member — even just
336
481
  // an isPending companion flip — puts the reader "under a lane"). The
337
482
  // staged value commits with no re-delivery (commitPendingNode never
@@ -343,7 +488,7 @@ function cleanupCompletedLanes(e) {
343
488
  // into the transaction (#3041 follow-up: a pending-gated branch that
344
489
  // first read its async source during the landing flush stayed one value
345
490
  // behind permanently); with none, into the ambient batch.
346
- if (e.Pe !== NOT_PENDING) (activeTransition ?? globalQueue.m).ht.add(i);
491
+ if (e._e !== NOT_PENDING) (activeTransition ?? globalQueue.m).Ht.add(i);
347
492
  return true;
348
493
  }
349
494
  return false;
@@ -366,18 +511,18 @@ function cleanupCompletedLanes(e) {
366
511
  // recompute() runs plain: the value stages and commits with the flush.
367
512
  // (el's own override slot excludes companions themselves; a lane merged
368
513
  // into a real optimistic lane resolves to a non-companion source.)
369
- if (!globalQueue.Qn && !activeTransition && !n.Ee && n.Ln.o?.Qe !== undefined && e.o?.De === undefined) {
370
- if (e.o !== null) e.o.Te = undefined;
514
+ if (!globalQueue.Sn && !activeTransition && !n.Te && n.Ln.o?.nn !== undefined && e.o?.Ne === undefined) {
515
+ if (e.o !== null) e.o.de = undefined;
371
516
  return false;
372
517
  }
373
518
  return n;
374
519
  }
375
- for (let n = e.te; n; n = n.ne) {
376
- const i = n.oe;
377
- if (i.it & REACTIVE_OPTIMISTIC_DIRTY) {
520
+ for (let n = e.ee; n; n = n.te) {
521
+ const i = n.re;
522
+ if (i.ft & REACTIVE_OPTIMISTIC_DIRTY) {
378
523
  const n = resolveLane(i);
379
524
  if (n) {
380
- e.it |= REACTIVE_OPTIMISTIC_DIRTY;
525
+ e.ft |= REACTIVE_OPTIMISTIC_DIRTY;
381
526
  assignOrMergeLane(e, n);
382
527
  return n;
383
528
  }
@@ -392,22 +537,22 @@ function cleanupCompletedLanes(e) {
392
537
  * the source's own write/commit/settlement paths keep it current. */ function laneAsyncPending(e) {
393
538
  const n = findLane(currentOptimisticLane);
394
539
  if (n.Ln !== e) {
395
- n._e.add(e);
396
- ext(e).Te = n;
397
- e.T |= CONFIG_HAS_LANE;
540
+ n.Ie.add(e);
541
+ ext(e).de = n;
542
+ e.C |= CONFIG_HAS_LANE;
398
543
  }
399
544
  }
400
545
 
401
546
  /** recompute()'s success path: the node's async settled, clear it from its lane. */ function laneAsyncSettled(e) {
402
547
  const n = resolveLane(e);
403
548
  if (n) {
404
- n._e.delete(e);
549
+ n.Ie.delete(e);
405
550
  }
406
551
  }
407
552
 
408
553
  function trackOptimisticStore(e) {
409
554
  // After initTransition, globalQueue._batch IS activeTransition (same reference)
410
- globalQueue.m.nn.add(e);
555
+ globalQueue.m.rn.add(e);
411
556
  schedule();
412
557
  }
413
558
 
@@ -416,23 +561,25 @@ function trackOptimisticStore(e) {
416
561
  * create optimistic state (verdict.ts at module top level, createOptimistic
417
562
  * and createOptimisticStore at first call) BEFORE any optimistic node exists.
418
563
  */ function installOptimisticEngine() {
419
- if (GlobalQueue.pn !== null) return;
420
- GlobalQueue.pn = optimisticWrite;
564
+ if (GlobalQueue.mn !== null) return;
565
+ GlobalQueue.mn = optimisticWrite;
421
566
  GlobalQueue.Un = resolveOptimisticNodes;
422
- GlobalQueue.Pn = transitionBlocked;
423
- GlobalQueue.Fn = cleanupCompletedLanes;
424
- GlobalQueue.Vn = runLaneEffects;
567
+ GlobalQueue.kn = transitionBlocked;
568
+ GlobalQueue.xn = cleanupCompletedLanes;
569
+ GlobalQueue.Mn = runLaneEffects;
425
570
  GlobalQueue.Oe = supersedeOverride;
426
- GlobalQueue.Sn = supersededRead;
427
- GlobalQueue.Rn = landOnOverride;
428
- GlobalQueue.An = gatedRead;
429
- GlobalQueue.dn = laneSuspends;
430
- GlobalQueue.Tn = laneLive;
431
- GlobalQueue.On = laneReadsCommitted;
432
- GlobalQueue.Ft = recomputeLane;
433
- GlobalQueue.vt = laneAsyncPending;
434
- GlobalQueue.Ht = laneAsyncSettled;
435
- GlobalQueue.Hn = trackOptimisticStore;
571
+ GlobalQueue.yn = endOptimism;
572
+ GlobalQueue.Dn = overrideRead;
573
+ GlobalQueue.ge = laneOverride;
574
+ GlobalQueue.Fn = landOnOverride;
575
+ GlobalQueue.hn = gatedRead;
576
+ GlobalQueue.Gn = laneSuspends;
577
+ GlobalQueue.Rn = laneLive;
578
+ GlobalQueue.An = laneReadsCommitted;
579
+ GlobalQueue.gt = recomputeLane;
580
+ GlobalQueue.kt = laneAsyncPending;
581
+ GlobalQueue.vt = laneAsyncSettled;
582
+ GlobalQueue.jn = trackOptimisticStore;
436
583
  }
437
584
 
438
585
  export { installOptimisticEngine };