@solidjs/signals 2.0.0-rc.17 → 2.0.0-rc.19

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 (71) hide show
  1. package/dist/dev-shared.js +426 -241
  2. package/dist/dev.attribution.js +19 -16
  3. package/dist/dev.js +768 -409
  4. package/dist/observe/boundaries.js +88 -67
  5. package/dist/observe/core/action.js +8 -0
  6. package/dist/observe/core/async.js +183 -138
  7. package/dist/observe/core/attribution.js +30 -28
  8. package/dist/observe/core/constants.js +23 -1
  9. package/dist/observe/core/core.js +185 -157
  10. package/dist/observe/core/effect.js +34 -37
  11. package/dist/observe/core/graph.js +8 -8
  12. package/dist/observe/core/heap.js +5 -5
  13. package/dist/observe/core/lanes.js +104 -84
  14. package/dist/observe/core/owner.js +17 -16
  15. package/dist/observe/core/scheduler.js +61 -47
  16. package/dist/observe/core/truth.js +6 -6
  17. package/dist/observe/core/verdict.js +15 -15
  18. package/dist/observe/deferred.js +40 -38
  19. package/dist/observe/from.js +248 -0
  20. package/dist/observe/index.js +4 -2
  21. package/dist/observe/map.js +22 -22
  22. package/dist/observe/reveal.js +5 -5
  23. package/dist/observe/signals.js +57 -15
  24. package/dist/observe/store/affects.js +1 -1
  25. package/dist/observe/store/hooks.js +3 -0
  26. package/dist/observe/store/optimistic.js +11 -11
  27. package/dist/observe/store/projection.js +34 -21
  28. package/dist/observe/store/reconcile.js +138 -122
  29. package/dist/observe/store/store.js +628 -641
  30. package/dist/observe/store/target.js +1 -5
  31. package/dist/observe/store/utils.js +7 -2
  32. package/dist/prod/boundaries.js +153 -132
  33. package/dist/prod/core/action.js +8 -0
  34. package/dist/prod/core/async.js +242 -197
  35. package/dist/prod/core/constants.js +16 -1
  36. package/dist/prod/core/core.js +166 -138
  37. package/dist/prod/core/effect.js +31 -34
  38. package/dist/prod/core/graph.js +5 -5
  39. package/dist/prod/core/heap.js +12 -12
  40. package/dist/prod/core/lanes.js +98 -78
  41. package/dist/prod/core/owner.js +15 -14
  42. package/dist/prod/core/scheduler.js +64 -50
  43. package/dist/prod/core/truth.js +6 -6
  44. package/dist/prod/core/verdict.js +17 -17
  45. package/dist/prod/deferred.js +40 -38
  46. package/dist/prod/from.js +248 -0
  47. package/dist/prod/index.js +4 -2
  48. package/dist/prod/map.js +14 -14
  49. package/dist/prod/reveal.js +8 -8
  50. package/dist/prod/signals.js +46 -4
  51. package/dist/prod/store/affects.js +1 -1
  52. package/dist/prod/store/hooks.js +3 -0
  53. package/dist/prod/store/optimistic.js +16 -16
  54. package/dist/prod/store/projection.js +22 -9
  55. package/dist/prod/store/reconcile.js +138 -122
  56. package/dist/prod/store/store.js +308 -331
  57. package/dist/prod/store/target.js +13 -17
  58. package/dist/prod/store/utils.js +2 -0
  59. package/dist/types/core/action.d.ts +8 -0
  60. package/dist/types/core/async.d.ts +12 -2
  61. package/dist/types/core/constants.d.ts +25 -0
  62. package/dist/types/core/core.d.ts +33 -4
  63. package/dist/types/core/dev.d.ts +1 -1
  64. package/dist/types/core/index.d.ts +1 -1
  65. package/dist/types/core/lanes.d.ts +5 -1
  66. package/dist/types/core/scheduler.d.ts +17 -3
  67. package/dist/types/from.d.ts +47 -0
  68. package/dist/types/index.d.ts +4 -1
  69. package/dist/types/signals.d.ts +37 -0
  70. package/dist/types/store/target.d.ts +2 -0
  71. package/package.json +1 -1
@@ -18,45 +18,48 @@ import { globalQueue, schedule, heldRunHook, haltReactivity, GlobalQueue } from
18
18
  * Effects are the leaf nodes of our reactive graph. When their sources change, they are
19
19
  * automatically added to the queue of effects to re-execute, which will cause them to fetch their
20
20
  * sources and recompute
21
- */ function effect(t, e, o, u) {
22
- const E = !!u?.user;
23
- const r = createEffectNode(t, e, o, E ? EFFECT_USER : EFFECT_RENDER, u);
24
- recompute(r, true);
21
+ */ function effect(t, e, o, r) {
22
+ const u = !!r?.user;
23
+ const E = createEffectNode(t, e, o, u ? EFFECT_USER : EFFECT_RENDER, r);
24
+ recompute(E, true);
25
25
  // A first pass that read a staged value was staged itself (recompute: born
26
26
  // staged); the flush's commit replays this effect. Its first run is not this
27
27
  // creation's (A29). A lane's node likewise: its run is the lane's, queued
28
28
  // by the pass and released at the reveal (lanes.ts).
29
- !u?.defer && r.ue === NOT_PENDING && !r.T?.Je?.V && (r.i === EFFECT_USER || u?.schedule ? globalQueue.enqueue(r.i, runEffect.bind(null, r)) : runEffect(r, r.i));
29
+ !r?.defer && E.ie === NOT_PENDING && !E.T?.Je?.V && (E.i === EFFECT_USER || r?.schedule ? globalQueue.enqueue(E.i, runEffect.bind(null, E)) : runEffect(E, E.i));
30
30
  }
31
31
 
32
- function notifyEffectStatus(t, e) {
33
- // Use passed values if provided, otherwise read from node
34
- const o = t !== undefined ? t : this.m;
35
- const u = e !== undefined ? e : this.T?.F;
36
- if (o & STATUS_ERROR) {
32
+ function notifyEffectStatus() {
33
+ const t = this.m;
34
+ const e = this.T?.F;
35
+ if (t & STATUS_ERROR) {
37
36
  globalQueue.notify(this, STATUS_PENDING, 0);
38
37
  if (this.i === EFFECT_USER) {
39
38
  // The error handler is the error arm of the effect phase (#2840 ruling):
40
39
  // queue it like the effect function. It runs in the same imperative,
41
40
  // writable scope and throws escalate the same way. No payload is queued — the node already carries `_statusFlags`/`_error`,
42
41
  // and the runner dispatches on them, so a recovery before the effect
43
- // phase takes the success arm instead. Blocked forwards (explicit
44
- // `status` arg without node-state writes) don't queue: the status
45
- // re-propagates unblocked at commit.
42
+ // phase takes the success arm instead. (Re-read: the boundary
43
+ // notify above can run a redraw.)
46
44
  if (this.m & STATUS_ERROR) {
47
45
  this.Qe = true;
48
46
  globalQueue.enqueue(this.i, this.W ??= runEffect.bind(null, this));
49
47
  }
50
48
  return;
51
49
  }
52
- if (!globalQueue.notify(this, STATUS_ERROR, STATUS_ERROR)) {
53
- // Dev locates the halt at the node that threw: the StatusError's
54
- // source, upstream of this effect when a computed failed.
55
- haltReactivity(unwrapStatusError(u));
56
- throw u;
57
- }
50
+ // Dev locates the halt at the node that threw: the StatusError's
51
+ // source, upstream of this effect when a computed failed.
52
+ escalate(this, unwrapStatusError(e), e, this);
58
53
  } else if (this.i === EFFECT_RENDER) {
59
- globalQueue.notify(this, STATUS_PENDING | STATUS_ERROR, o, u);
54
+ globalQueue.notify(this, STATUS_PENDING | STATUS_ERROR, t, e);
55
+ }
56
+ }
57
+
58
+ /** An effect's error no boundary takes halts the system (boundary-or-halt):
59
+ * halted with `cause`, `thrown` rethrown; dev locates it at `subject`. */ function escalate(t, e, o = e, r = t) {
60
+ if (!globalQueue.notify(t, STATUS_ERROR, STATUS_ERROR)) {
61
+ haltReactivity(e);
62
+ throw o;
60
63
  }
61
64
  }
62
65
 
@@ -83,19 +86,16 @@ function runEffect(t, e) {
83
86
  // same flush must not be hijacked by a later-arriving error status.
84
87
  if (t.m & STATUS_ERROR && t.i === EFFECT_USER) {
85
88
  const e = unwrapStatusError(t.T?.F);
86
- t._t = t.Ie;
89
+ t._t = t.le;
87
90
  t.Qe = false;
88
91
  try {
89
- t.ht ? t.ht(e, () => {
92
+ t.Ht ? t.Ht(e, () => {
90
93
  const e = t.kt;
91
94
  t.kt = undefined;
92
95
  e?.();
93
96
  }) : console.error(e);
94
97
  } catch (e) {
95
- if (!globalQueue.notify(t, STATUS_ERROR, STATUS_ERROR)) {
96
- haltReactivity(e);
97
- throw e;
98
- }
98
+ escalate(t, e);
99
99
  }
100
100
  return;
101
101
  }
@@ -107,23 +107,20 @@ function runEffect(t, e) {
107
107
  // cascade an observer reports) and what times the callback (the `effect`
108
108
  // record) — facts a production observer needs, not only a dev console.
109
109
  if (attrHooks !== null) attrHooks.effectRunStart(t);
110
- const u = t.kt;
110
+ const r = t.kt;
111
111
  t.kt = undefined;
112
112
  try {
113
- u?.();
114
- const e = t.Ge(t.Ie, t._t);
113
+ r?.();
114
+ const e = t.Ge(t.le, t._t);
115
115
  if (false && e !== undefined && typeof e !== "function") ;
116
116
  // The final cleanup is invoked by disposeChildren at true disposal.
117
117
  t.kt = e;
118
118
  } catch (e) {
119
119
  ext(t).F = new StatusError(t, e);
120
120
  t.m |= STATUS_ERROR;
121
- if (!globalQueue.notify(t, STATUS_ERROR, STATUS_ERROR)) {
122
- haltReactivity(e);
123
- throw e;
124
- }
121
+ escalate(t, e);
125
122
  } finally {
126
- t._t = t.Ie;
123
+ t._t = t.le;
127
124
  t.Qe = false;
128
125
  // The run applied: this is the frame now, so the dependency tail the
129
126
  // compute pass left linked goes (A30, #3438 — `recompute` defers an
@@ -159,8 +156,8 @@ GlobalQueue.K = runEffect;
159
156
  const e = o.kt;
160
157
  o.kt = undefined;
161
158
  e?.();
162
- const u = staleValues(t);
163
- o.kt = u;
159
+ const r = staleValues(t);
160
+ o.kt = r;
164
161
  }, {
165
162
  ...e,
166
163
  lazy: true
@@ -175,7 +172,7 @@ GlobalQueue.K = runEffect;
175
172
  // Status dispatch rides the SHARED notifier (statusNotifierOf keys off
176
173
  // _type): its error arm is behavior-identical to the closure that used to
177
174
  // live here, without the per-node NodeExtension allocation.
178
- o.M = run;
175
+ o.Z = run;
179
176
  // The first run rides the heap like every wake (GlobalQueue._update), so a
180
177
  // tracked effect created inside a render-effect callback runs after that
181
178
  // pass's staged writes commit, not before.
@@ -1,4 +1,4 @@
1
- import { REACTIVE_RECOMPUTING_DEPS, CONFIG_AUTO_DISPOSE, STATUS_PENDING, REACTIVE_DISPOSED, REACTIVE_ZOMBIE, CONFIG_SLOT_NODE } from "./constants.js";
1
+ import { NewSet, REACTIVE_RECOMPUTING_DEPS, CONFIG_AUTO_DISPOSE, STATUS_PENDING, REACTIVE_DISPOSED, REACTIVE_ZOMBIE, CONFIG_SLOT_NODE } from "./constants.js";
2
2
 
3
3
  import { deleteFromHeap } from "./heap.js";
4
4
 
@@ -21,7 +21,7 @@ function unlinkSubs(e) {
21
21
  const l = e.G;
22
22
  const o = e.t;
23
23
  const s = e.en;
24
- if (o !== null) o.en = s; else n.dt = s;
24
+ if (o !== null) o.en = s; else n.Ct = s;
25
25
  if (s !== null) s.t = o; else {
26
26
  n.o = o;
27
27
  if (o === null) {
@@ -107,7 +107,7 @@ function unobserved(e) {
107
107
  * stays alive and serves its cache, uniform with observed memos) while
108
108
  * reclamation still happens within one microtask — the enqueue site arms
109
109
  * schedule(), so a flush is guaranteed even when no other work is queued.
110
- */ const dormantNodes = new Set;
110
+ */ const dormantNodes = new NewSet;
111
111
 
112
112
  function sweepDormant() {
113
113
  if (dormantNodes.size === 0) return;
@@ -133,7 +133,7 @@ function link(e, n) {
133
133
  if (s) {
134
134
  o = l !== null ? l.G : n.C;
135
135
  if (o !== null && o.S === e) {
136
- o.Te = n.Ne;
136
+ o.Ie = n.Ne;
137
137
  n._ = o;
138
138
  return;
139
139
  }
@@ -143,15 +143,15 @@ function link(e, n) {
143
143
  // [deps.._depsTail] prefix — the O(1) equivalent of scanning the dep list
144
144
  // (the old alien-signals `isValidLink` walk, O(n²) when a computation
145
145
  // re-reads earlier deps non-consecutively, e.g. store leaf reads).
146
- const r = e.dt;
147
- if (r !== null && r.u === n && (!s || r.Te === n.Ne)) return;
148
- const t = n._ = e.dt = {
146
+ const r = e.Ct;
147
+ if (r !== null && r.u === n && (!s || r.Ie === n.Ne)) return;
148
+ const t = n._ = e.Ct = {
149
149
  S: e,
150
150
  u: n,
151
151
  G: o,
152
152
  en: r,
153
153
  t: null,
154
- Te: n.Ne
154
+ Ie: n.Ne
155
155
  };
156
156
  if (l !== null) l.G = t; else n.C = t;
157
157
  if (r !== null) r.t = t; else e.o = t;
@@ -11,12 +11,12 @@ import { dirtyQueue } from "./scheduler.js";
11
11
  * callback to the user queue once the pass has committed (see
12
12
  * GlobalQueue._update, #3291).
13
13
  */ function enqueueSub(e) {
14
- if (dirtyQueue.st > e.Et) dirtyQueue.st = e.Et;
14
+ if (dirtyQueue.lt > e.Et) dirtyQueue.lt = e.Et;
15
15
  insertIntoHeap(e, dirtyQueue);
16
16
  }
17
17
 
18
18
  function actualInsertIntoHeap(e, t) {
19
- const E = (e._parent?.Pt ? e._parent.Ht?.Et : e._parent?.Et) ?? -1;
19
+ const E = (e._parent?.Pt ? e._parent.ht?.Et : e._parent?.Et) ?? -1;
20
20
  if (E >= e.Et) e.Et = E + 1;
21
21
  const n = e.Et;
22
22
  const I = t.gt[n];
@@ -98,11 +98,11 @@ function markNode(e, t = REACTIVE_DIRTY) {
98
98
 
99
99
  function runHeap(e, t) {
100
100
  e.p = false;
101
- for (e.st = 0; e.st <= e.jt; e.st++) {
102
- let E = e.gt[e.st];
101
+ for (e.lt = 0; e.lt <= e.jt; e.lt++) {
102
+ let E = e.gt[e.lt];
103
103
  while (E !== undefined) {
104
104
  if (E.R & REACTIVE_IN_HEAP) t(E); else adjustHeight(E, e);
105
- E = e.gt[e.st];
105
+ E = e.gt[e.lt];
106
106
  }
107
107
  }
108
108
  e.jt = 0;
@@ -1,4 +1,4 @@
1
- import { CONFIG_GUESS, NOT_PENDING, CONFIG_OVERRIDE, REACTIVE_DISPOSED, CONFIG_VERDICT, REACTIVE_PROBE_UNANSWERED, REACTIVE_LANE_DIRTY, CONFIG_HELD, REACTIVE_LANE_READ, STATUS_UNINITIALIZED, REACTIVE_RECOMPUTING_DEPS, CONFIG_AUTHORITATIVE, STATUS_ERROR, CONFIG_CHILDREN_FORBIDDEN, STATUS_PENDING, EFFECT_RENDER, REACTIVE_SCREEN_READ, REACTIVE_FRAME_READ, REACTIVE_IN_HEAP, REACTIVE_DIRTY, REACTIVE_CHECK, CONFIG_INPUTS_PUBLISHED, CONFIG_SLOT_NODE, CONFIG_STAGED, REACTIVE_JOINED, REACTIVE_STAGED_READ } from "./constants.js";
1
+ import { CONFIG_GUESS, NOT_PENDING, CONFIG_OVERRIDE, REACTIVE_DISPOSED, CONFIG_VERDICT, REACTIVE_PROBE_UNANSWERED, REACTIVE_LANE_DIRTY, CONFIG_HELD, REACTIVE_LANE_READ, STATUS_UNINITIALIZED, REACTIVE_RECOMPUTING_DEPS, CONFIG_AUTHORITATIVE, STATUS_ERROR, CONFIG_CHILDREN_FORBIDDEN, EFFECT_RENDER, REACTIVE_SCREEN_READ, REACTIVE_FRAME_READ, REACTIVE_IN_HEAP, REACTIVE_DIRTY, REACTIVE_CHECK, STATUS_PENDING, CONFIG_INPUTS_PUBLISHED, CONFIG_SLOT_NODE, CONFIG_STAGED, REACTIVE_JOINED, REACTIVE_STAGED_READ } from "./constants.js";
2
2
 
3
3
  import { attrHooks } from "./attribution-hooks.js";
4
4
 
@@ -10,7 +10,7 @@ import { laneValueOf } from "./truth.js";
10
10
 
11
11
  import { enqueueSub } from "./heap.js";
12
12
 
13
- import { schedule, question, nextQuestion, installLaneHooks, GlobalQueue, resolveTx, newTransaction, ownFlights, blocked, txOf, clock, insertSubs, laneDirty, queuePendingNode, list, passLane, sameLane, flushTransaction, commitPendingNode, releaseQueues, holdNode, joinFuture, reruns, merge, setPassLane } from "./scheduler.js";
13
+ import { schedule, question, nextQuestion, installLaneHooks, GlobalQueue, resolveTx, newTransaction, ownFlights, blocked, txOf, clock, insertSubs, laneDirty, queuePendingNode, list, passLane, setPassLane, sameLane, flushTransaction, commitPendingNode, releaseQueues, holdNode, joinFuture, reruns, merge } from "./scheduler.js";
14
14
 
15
15
  // Lanes (plan sec. 28 replay, 2026-10-02; maintainer rulings 2026-10-01/02). A lane
16
16
  // is "a new base of a transition": the sub-frame an optimistic write opens
@@ -53,7 +53,7 @@ function newLane(e) {
53
53
  /** What the screen shows for a lane's node: the lane's value once the lane
54
54
  * has revealed, the committed truth before. */ function display(e) {
55
55
  const n = e.T.V;
56
- return e.T.Je.It && n !== NOT_PENDING ? n : e.Ie;
56
+ return e.T.Je.It && n !== NOT_PENDING ? n : e.le;
57
57
  }
58
58
 
59
59
  /** Guesses written since the last seam (`[node, value, question, …]`).
@@ -68,7 +68,7 @@ function newLane(e) {
68
68
  * on (a store setter's draft too, store/optimistic.ts). */ function guessedValueOf(e) {
69
69
  const n = pendingGuessOf(e);
70
70
  if (n !== NOT_PENDING) return n;
71
- return e.l & CONFIG_OVERRIDE ? laneValueOf(e) : e.Ie;
71
+ return e.l & CONFIG_OVERRIDE ? laneValueOf(e) : e.le;
72
72
  }
73
73
 
74
74
  /** The unflushed guess to `n`, else NOT_PENDING. */ function pendingGuessOf(e) {
@@ -84,7 +84,7 @@ function optimisticWrite(e, n) {
84
84
  // the guess's question and entangles its frame (#3347) without notifying.
85
85
  const s = guessedValueOf(e);
86
86
  if (typeof n === "function") n = n(s);
87
- if (e.Ye && e.Ye(s, n) && !(e.l & CONFIG_GUESS)) return n;
87
+ if (e.je && e.je(s, n) && !(e.l & CONFIG_GUESS)) return n;
88
88
  pendingGuesses.push(e, n, question || nextQuestion());
89
89
  schedule();
90
90
  return n;
@@ -114,25 +114,25 @@ function applyGuesses(e) {
114
114
  // The frame already holding the slot: a guess over it is a second
115
115
  // suggestion for the slot, and two cannot finish apart (A34 (1)) — the
116
116
  // frames entangle.
117
- let r = null;
117
+ let i = null;
118
118
  if (t.l & CONFIG_OVERRIDE) {
119
119
  // A lane's node already: a guess re-asked, or a derivation guessed
120
120
  // over (its staging is void — the guess is the value now). The same
121
121
  // value re-asked renews the question; nothing to notify.
122
- u = !t.Ye || !t.Ye(laneValueOf(t), l);
122
+ u = !t.je || !t.je(laneValueOf(t), l);
123
123
  if (!(t.l & CONFIG_GUESS)) {
124
- t.ue = NOT_PENDING;
124
+ t.ie = NOT_PENDING;
125
125
  t.l |= CONFIG_GUESS;
126
126
  }
127
127
  t.T.V = l;
128
- r = txOf(t)._parent;
128
+ i = txOf(t)._parent;
129
129
  } else {
130
130
  let s;
131
131
  // A held node: a truth staged there, or what a corrected guess showed.
132
132
  // The guess composes on it, so it ends with that hold — and with its
133
133
  // own frame's (F-11): it lands only when both have (the board's rapid
134
134
  // adds — a later add's row reverted with an earlier add's landing).
135
- if (t.l & CONFIG_HELD) s = newLane(r = txOf(t)); else {
135
+ if (t.l & CONFIG_HELD) s = newLane(i = txOf(t)); else {
136
136
  if (e === null && inFlight(t)) e = newTransaction(false);
137
137
  if (e === null) continue;
138
138
  s = n ??= newLane(e);
@@ -147,8 +147,8 @@ function applyGuesses(e) {
147
147
  t.l |= CONFIG_OVERRIDE | CONFIG_GUESS;
148
148
  list(t, s);
149
149
  }
150
- if (e !== null && r !== null) merge(e, r);
151
- ext(t).Ee = pendingGuesses[s + 2];
150
+ if (e !== null && i !== null) merge(e, i);
151
+ ext(t).de = pendingGuesses[s + 2];
152
152
  if (u) {
153
153
  // The lane's members re-derive as its work (REACTIVE_LANE_DIRTY): a
154
154
  // blocked lane's re-guess does not re-run its render effects as stale
@@ -266,7 +266,7 @@ function unlink(e) {
266
266
  }
267
267
  if (!e.i) {
268
268
  if (!(e.l & CONFIG_OVERRIDE)) {
269
- e.ue = NOT_PENDING;
269
+ e.ie = NOT_PENDING;
270
270
  e.l = e.l & -1048833 | CONFIG_OVERRIDE;
271
271
  ext(e);
272
272
  }
@@ -327,7 +327,7 @@ function supersede(e, n, s) {
327
327
  dissolveLane(t, l, e);
328
328
  // What the screen showed stays the screen until the parent lands (A18
329
329
  // (c): display and untracked reads keep the override until the commit).
330
- if (t.It) e.Ie = e.T.V;
330
+ if (t.It) e.le = e.T.V;
331
331
  e.l &= -10241;
332
332
  e.T.V = NOT_PENDING;
333
333
  holdNode(e, l);
@@ -337,7 +337,7 @@ function supersede(e, n, s) {
337
337
  // A confirmation is silent (A17); only truth readers are told.
338
338
  GlobalQueue.tn?.(t);
339
339
  }
340
- e.ue = n;
340
+ e.ie = n;
341
341
  joinFuture(l);
342
342
  }
343
343
 
@@ -361,16 +361,16 @@ function supersede(e, n, s) {
361
361
  e.it = null;
362
362
  if (lanes.indexOf(l) !== -1) dissolveLane(l, null);
363
363
  }
364
- for (let t = 0; t < e.je.length; t++) {
365
- const l = e.je[t];
364
+ for (let t = 0; t < e.Ye.length; t++) {
365
+ const l = e.Ye[t];
366
366
  const u = l.T;
367
367
  if (l === s || u.Je !== e) continue;
368
- const r = l.i;
369
- const i = l.l & CONFIG_GUESS;
368
+ const i = l.i;
369
+ const r = l.l & CONFIG_GUESS;
370
370
  const o = u.V;
371
371
  // What the screen shows of it (NOT_PENDING: nothing — a lane pass that
372
372
  // errored or pends staged no value), and the lane's latest.
373
- const E = e.It && !r ? o : l.Ie;
373
+ const E = e.It && !i ? o : l.le;
374
374
  const f = laneValueOf(l);
375
375
  u.V = NOT_PENDING;
376
376
  l.l &= -10241;
@@ -381,9 +381,9 @@ function supersede(e, n, s) {
381
381
  // displayed guess that lifts to something else — the truth staged
382
382
  // beneath it (superseded by a landing held there) or the value it
383
383
  // covered (reverted: nothing came true).
384
- if (i && e.It && attrHooks !== null) {
385
- const e = l.ue !== NOT_PENDING ? l.ue : l.Ie;
386
- if (e !== o) attrHooks.optimisticReverted(l, o, e, l.ue !== NOT_PENDING ? "superseded" : "reverted");
384
+ if (r && e.It && attrHooks !== null) {
385
+ const e = l.ie !== NOT_PENDING ? l.ie : l.le;
386
+ if (e !== o) attrHooks.optimisticReverted(l, o, e, l.ie !== NOT_PENDING ? "superseded" : "reverted");
387
387
  }
388
388
  // A derivation the revert re-derives (dirtied by the guess's
389
389
  // notification — listed before it), or one still in flight, shows the
@@ -391,19 +391,19 @@ function supersede(e, n, s) {
391
391
  // of its flight observes it (#3648, #3651; #3305's commit beneath a
392
392
  // flight).
393
393
  u.Je = null;
394
- if (!r && !i) l.ue = f;
394
+ if (!i && !r) l.ie = f;
395
395
  commitPendingNode(l);
396
- if (!r) {
396
+ if (!i) {
397
397
  if (l.R & (REACTIVE_IN_HEAP | REACTIVE_DIRTY | REACTIVE_CHECK) || l.m & STATUS_PENDING) l.l |= CONFIG_INPUTS_PUBLISHED;
398
- if (E !== NOT_PENDING && (!l.Ye || !l.Ye(E, l.Ie))) insertSubs(l);
398
+ if (E !== NOT_PENDING && (!l.je || !l.je(E, l.le))) insertSubs(l);
399
399
  }
400
- } else if (i) {
400
+ } else if (r) {
401
401
  // A guess of a dissolving lane: its truth — a landing held beneath it,
402
402
  // else the value it covered — is the parent's to land, and its readers
403
403
  // re-derive from it there. What the screen showed stays the screen
404
404
  // until then (A18 (c)).
405
- if (l.ue === NOT_PENDING) l.ue = covered(l);
406
- if (e.It) l.Ie = o;
405
+ if (l.ie === NOT_PENDING) l.ie = covered(l);
406
+ if (e.It) l.le = o;
407
407
  holdNode(l, n);
408
408
  insertSubs(l);
409
409
  } else {
@@ -415,18 +415,18 @@ function supersede(e, n, s) {
415
415
  // answer — it re-asks from the truth (whose notification may not reach
416
416
  // it: an input's truth equal to its committed value is silent).
417
417
  u.Je = null;
418
- if (!r) {
419
- if (E !== NOT_PENDING && e.It) l.ue = E;
418
+ if (!i) {
419
+ if (E !== NOT_PENDING && e.It) l.ie = E;
420
420
  if (l.m & STATUS_PENDING) {
421
- u.Se = null;
421
+ u.Ee = null;
422
422
  enqueueSub(l);
423
423
  }
424
424
  }
425
425
  if (e.It) {
426
426
  commitPendingNode(l);
427
- if (!r) l.l |= CONFIG_INPUTS_PUBLISHED;
427
+ if (!i) l.l |= CONFIG_INPUTS_PUBLISHED;
428
428
  } else {
429
- if (u.Nt !== null || u.ft !== null) GlobalQueue.et(l, false, true);
429
+ if (u.ft !== null || u.Nt !== null) GlobalQueue.et(l, false, true);
430
430
  l.l &= -1048961;
431
431
  }
432
432
  }
@@ -459,7 +459,11 @@ function supersede(e, n, s) {
459
459
  * has shown, or one the seam has not judged yet (its run waits on the
460
460
  * verdict: released if the lane shows, held if it blocks) — and sees the
461
461
  * lane's latest. NOT_PENDING falls through: lane work's read of a pending
462
- * member throws like any. */ function laneRead(e, n) {
462
+ * member throws like any. A verdict reader entering a lane the seam has
463
+ * not judged reads ahead of it like a staging read: if the seam holds the
464
+ * lane, the pass re-derives on the screen (`entered`, #4284) — `latest()`
465
+ * beside the lane's nodes tears ahead of the hold however the reads are
466
+ * grouped into effects. */ function laneRead(e, n) {
463
467
  const s = n.l & CONFIG_GUESS;
464
468
  const t = txOf(n);
465
469
  // A lane a correction folded under (D-34): store/optimistic.ts reads it.
@@ -476,6 +480,7 @@ function supersede(e, n, s) {
476
480
  // An errored member's lane value predates its error; the error is the answer (#4106).
477
481
  if (!s && u & STATUS_ERROR) return NOT_PENDING;
478
482
  if (e === null) return laneValueOf(n);
483
+ const i = !s && u & STATUS_PENDING;
479
484
  if (!tracking || e.l & CONFIG_CHILDREN_FORBIDDEN) {
480
485
  // An untracked read inside lane work derives from the lane like a
481
486
  // tracked one, minus the subscription — `untrack` is about
@@ -484,45 +489,51 @@ function supersede(e, n, s) {
484
489
  // lanes read by one pass link, as for tracked reads). Outside lane
485
490
  // work, the screen.
486
491
  if (passLane === null || e.l & CONFIG_CHILDREN_FORBIDDEN) return display(n);
487
- enterLane(t, e);
488
- if (!s && u & STATUS_PENDING) return NOT_PENDING;
489
- return laneValueOf(n);
490
- }
491
- if (e.i === EFFECT_RENDER && (passLane === null || !sameLane(passLane, t))) {
492
- if (!s) {
493
- // A flight whose value has never shown: nothing to show — the leaf
494
- // waits on it, and its own frame holds (A15, #3334), not the lane's
495
- // parent. The landing lands the frame: its own pass (the flush
496
- // joined its transaction) reads the answer it waited on — the lane's
497
- // staging, revealed at this seam.
498
- if (u & STATUS_PENDING && n.T.V === NOT_PENDING) return NOT_PENDING;
499
- if (n.ue !== NOT_PENDING && e.l & CONFIG_HELD && flushTransaction !== null && resolveTx(flushTransaction) === txOf(e)) return n.ue;
500
- }
501
- if (t.ln || !s && u & STATUS_PENDING) {
502
- if (!s && !t.It && u & STATUS_UNINITIALIZED) throw new NotReadyError(null);
503
- // Re-derived at the reveal; once per pass (the pass may also be a stale
504
- // reader of a transaction, REACTIVE_FRAME_READ — both reruns apply).
505
- if (!(e.R & REACTIVE_SCREEN_READ)) {
506
- e.R |= REACTIVE_FRAME_READ | REACTIVE_SCREEN_READ;
507
- t.En.push(e);
492
+ } else if (e.i === EFFECT_RENDER) {
493
+ // A flight whose value has never shown: nothing to show — the leaf
494
+ // waits on it, and its own frame holds (A15, #3334), not the lane's
495
+ // parent. The landing lands the frame: its own pass (the flush joined
496
+ // its transaction) reads the answer it waited on — the lane's staging,
497
+ // revealed at this seam.
498
+ const l = i && n.T.V === NOT_PENDING;
499
+ // D-28: a leaf a verdict read moved into the lane still throws on such
500
+ // a flight — the pass leaves the lane, and its frame holds as if the
501
+ // read had come first (S-39, #4275). Nor does a verdict lane's seat carry
502
+ // the leaf into another lane (no link): it reads that lane as from the
503
+ // frame's seat (#4284).
504
+ if ((l || passLane !== t) && passLane?._parent.it === passLane) setPassLane(null);
505
+ if (passLane === null || !sameLane(passLane, t)) {
506
+ // (A lane that has never shown: the screen is the committed value.)
507
+ if (l && t.It) return NOT_PENDING;
508
+ if (!s && n.ie !== NOT_PENDING && e.l & CONFIG_HELD && flushTransaction !== null && resolveTx(flushTransaction) === txOf(e)) return n.ie;
509
+ if (t.ln || i) {
510
+ if (!s && !t.It && u & STATUS_UNINITIALIZED) throw new NotReadyError(null);
511
+ // Re-derived at the reveal; once per pass (the pass may also be a stale
512
+ // reader of a transaction, REACTIVE_FRAME_READ — both reruns apply).
513
+ if (!(e.R & REACTIVE_SCREEN_READ)) {
514
+ e.R |= REACTIVE_FRAME_READ | REACTIVE_SCREEN_READ;
515
+ t.En.push(e);
516
+ }
517
+ return display(n);
508
518
  }
509
- return display(n);
519
+ // A verdict reader reads ahead of the seam's verdict: re-derived on the
520
+ // screen if the lane holds (#4284).
521
+ if (e.l & CONFIG_VERDICT && t._parent.it !== t) entered.push(e);
510
522
  }
511
523
  }
512
524
  enterLane(t, e);
513
- if (!s && u & STATUS_PENDING) return NOT_PENDING;
514
- return laneValueOf(n);
525
+ return i ? NOT_PENDING : laneValueOf(n);
515
526
  }
516
527
 
517
528
  /** The value a guess covered: the node's committed value — a store slot's
518
529
  * asked of the store (a chained link follows the inner store's commits). */ function covered(e) {
519
- return e.l & CONFIG_SLOT_NODE && GlobalQueue.fn !== undefined ? GlobalQueue.fn(e) : e.Ie;
530
+ return e.l & CONFIG_SLOT_NODE && GlobalQueue.fn !== undefined ? GlobalQueue.fn(e) : e.le;
520
531
  }
521
532
 
522
533
  /** A guess in `l` whose own truth is in flight (its refetch — authoritative:
523
534
  * the landing supersedes, not the body's end). */ function guessFlights(e) {
524
- for (let n = 0; n < e.je.length; n++) {
525
- const s = e.je[n];
535
+ for (let n = 0; n < e.Ye.length; n++) {
536
+ const s = e.Ye[n];
526
537
  if (s.l & CONFIG_GUESS && s.T.Je === e && inFlight(s)) return true;
527
538
  }
528
539
  return false;
@@ -552,12 +563,12 @@ function supersede(e, n, s) {
552
563
  // Each guess's truth: the one beneath it (a landing held there), else
553
564
  // the one it covered. (A correction dissolves the lane — the others are
554
565
  // re-homed with theirs, `dissolveLane`.)
555
- for (let e = 0; e < t.je.length && lanes.indexOf(t) !== -1; e++) {
556
- const n = t.je[e];
566
+ for (let e = 0; e < t.Ye.length && lanes.indexOf(t) !== -1; e++) {
567
+ const n = t.Ye[e];
557
568
  if (n.l & CONFIG_GUESS && n.T.Je === t) {
558
- const e = n.ue !== NOT_PENDING ? n.ue : covered(n);
559
- reverting = n.ue === NOT_PENDING;
560
- supersede(n, e, !n.Ye || !n.Ye(n.T.V, e));
569
+ const e = n.ie !== NOT_PENDING ? n.ie : covered(n);
570
+ reverting = n.ie === NOT_PENDING;
571
+ supersede(n, e, !n.je || !n.je(n.T.V, e));
561
572
  reverting = false;
562
573
  }
563
574
  }
@@ -575,21 +586,21 @@ function laneSeam(e, n) {
575
586
  e.ln = false;
576
587
  e.It = true;
577
588
  reruns(e);
578
- for (let n = 0; n < e.je.length; n++) {
579
- const s = e.je[n];
589
+ for (let n = 0; n < e.Ye.length; n++) {
590
+ const s = e.Ye[n];
580
591
  if (s.R & LEFT_SHOWN) {
581
592
  s.R ^= LEFT_SHOWN;
582
593
  insertSubs(s);
583
594
  }
584
595
  const t = s.T;
585
596
  if (t.Je !== e) continue;
586
- if (!(s.l & CONFIG_GUESS) && !s.i && s.ue !== NOT_PENDING) {
587
- t.V = s.ue;
588
- s.ue = NOT_PENDING;
597
+ if (!(s.l & CONFIG_GUESS) && !s.i && s.ie !== NOT_PENDING) {
598
+ t.V = s.ie;
599
+ s.ie = NOT_PENDING;
589
600
  }
590
601
  // The frame this pass built is the screen's now; the one it replaced
591
602
  // goes (as `commitPendingNode` retires a committed pass's).
592
- if (t.Nt !== null || t.ft !== null) GlobalQueue.et(s, false, true);
603
+ if (t.ft !== null || t.Nt !== null) GlobalQueue.et(s, false, true);
593
604
  s.l &= ~CONFIG_STAGED;
594
605
  }
595
606
  releaseQueues(e);
@@ -639,17 +650,17 @@ function laneSeam(e, n) {
639
650
  // folds a current one beneath it (D-34): true.
640
651
  const s = stale(e, question);
641
652
  if (GlobalQueue.an?.(e, n, s) || s) {
642
- e.ue = n;
653
+ e.ie = n;
643
654
  e.l |= CONFIG_HELD;
644
655
  schedule();
645
656
  return n;
646
657
  }
647
- supersede(e, n, !e.Ye || !e.Ye(e.T.V, n));
658
+ supersede(e, n, !e.je || !e.je(e.T.V, n));
648
659
  } else {
649
660
  const s = txOf(e);
650
- if (e.Ye && e.Ye(laneValueOf(e), n)) return n;
651
- if (s.It) e.ue = n; else e.T.V = n;
652
- if (e.X !== undefined) e.ie = clock;
661
+ if (e.je && e.je(laneValueOf(e), n)) return n;
662
+ if (s.It) e.ie = n; else e.T.V = n;
663
+ if (e.X !== undefined) e.ae = clock;
653
664
  insertSubs(e);
654
665
  laneDirty(e, s);
655
666
  }
@@ -671,18 +682,18 @@ function laneSeam(e, n) {
671
682
  * A24). The guess's own question answering supersedes as usual. */ function laneOutcome(e, n, s) {
672
683
  if (s) return true;
673
684
  if (stale(e, answered(e))) {
674
- e.ue = n;
685
+ e.ie = n;
675
686
  e.l |= CONFIG_HELD;
676
687
  return true;
677
688
  }
678
- supersede(e, n, !e.Ye || !e.Ye(e.T.V, n));
689
+ supersede(e, n, !e.je || !e.je(e.T.V, n));
679
690
  return true;
680
691
  }
681
692
 
682
693
  /** THE provenance test, for a derivation's pass and a write alike: an
683
694
  * answer to question `q` landing on a guess is stale when `q` is older
684
695
  * than the guess's own; unstamped (0) and mainline are current. */ function stale(e, n) {
685
- return n !== 0 && n < e.T.Ee;
696
+ return n !== 0 && n < e.T.de;
686
697
  }
687
698
 
688
699
  /** The question a derivation's pass answered: the newest among the stamped
@@ -691,7 +702,7 @@ function laneSeam(e, n) {
691
702
  let n = 0;
692
703
  for (let s = e.C; s !== null; s = s.G) {
693
704
  const e = s.S;
694
- if (e.T !== null && e.T.Ee > n && e.ie === clock) n = e.T.Ee;
705
+ if (e.T !== null && e.T.de > n && e.ae === clock) n = e.T.de;
695
706
  }
696
707
  return n;
697
708
  }
@@ -733,16 +744,25 @@ const laneSeams = e => {
733
744
  if (l != null && l.V && n.indexOf(l) === -1) n.push(l);
734
745
  }
735
746
  for (let e = Math.min(judged, lanes.length) - 1; e >= 0; e--) laneSeam(lanes[e], n.indexOf(lanes[e]) !== -1);
747
+ // A verdict reader that entered a lane this seam held: its run waits with
748
+ // the lane, and the verdict beside it would freeze — it re-derives next
749
+ // round from the frame's seat, a stale reader of the lane (#4284).
750
+ for (let e; e = entered.pop(); ) if (e.T.Je?.ln) {
751
+ e.R &= ~REACTIVE_LANE_DIRTY;
752
+ enqueueSub(e);
753
+ }
736
754
  };
737
755
 
756
+ /** Render effects that read a verdict and entered a lane before its seam (#4284). */ const entered = [];
757
+
738
758
  installLaneHooks(laneRead, laneStage, laneOutcome, laneWrite, applyGuessesAt, laneSeams, () => lanes.length !== 0 && laneCorrections(), endLanes, lanesBlocked);
739
759
 
740
- GlobalQueue.Ke = e => {
760
+ GlobalQueue.xe = e => {
741
761
  const n = [];
742
762
  for (let s = 0; s < lanes.length; s++) {
743
763
  const t = lanes[s];
744
764
  if (resolveTx(t._parent) !== e) continue;
745
- for (let e = 0; e < t.je.length; e++) if (t.je[e].l & CONFIG_GUESS && t.je[e].T.Je === t) n.push(t.je[e]);
765
+ for (let e = 0; e < t.Ye.length; e++) if (t.Ye[e].l & CONFIG_GUESS && t.Ye[e].T.Je === t) n.push(t.Ye[e]);
746
766
  }
747
767
  return n;
748
768
  };