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

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 (74) hide show
  1. package/dist/dev-shared.js +553 -264
  2. package/dist/dev.attribution.js +18 -18
  3. package/dist/dev.js +1030 -439
  4. package/dist/observe/affects.js +12 -2
  5. package/dist/observe/boundaries.js +168 -115
  6. package/dist/observe/core/async.js +57 -37
  7. package/dist/observe/core/attribution.js +6 -6
  8. package/dist/observe/core/constants.js +9 -1
  9. package/dist/observe/core/copies.js +10 -0
  10. package/dist/observe/core/core.js +261 -181
  11. package/dist/observe/core/effect.js +32 -27
  12. package/dist/observe/core/graph.js +7 -7
  13. package/dist/observe/core/heap.js +29 -29
  14. package/dist/observe/core/lanes.js +301 -101
  15. package/dist/observe/core/owner.js +13 -13
  16. package/dist/observe/core/scheduler.js +124 -69
  17. package/dist/observe/core/truth.js +9 -7
  18. package/dist/observe/core/verdict.js +160 -98
  19. package/dist/observe/deferred.js +1 -1
  20. package/dist/observe/index.js +2 -0
  21. package/dist/observe/map.js +120 -116
  22. package/dist/observe/reveal.js +48 -48
  23. package/dist/observe/signals.js +5 -5
  24. package/dist/observe/store/affects.js +2 -2
  25. package/dist/observe/store/optimistic.js +272 -176
  26. package/dist/observe/store/projection.js +21 -10
  27. package/dist/observe/store/reconcile.js +8 -2
  28. package/dist/observe/store/store.js +91 -55
  29. package/dist/observe/store/types.js +11 -1
  30. package/dist/observe/store/utils.js +1 -1
  31. package/dist/prod/affects.js +12 -2
  32. package/dist/prod/boundaries.js +164 -109
  33. package/dist/prod/core/async.js +201 -181
  34. package/dist/prod/core/constants.js +9 -1
  35. package/dist/prod/core/copies.js +10 -0
  36. package/dist/prod/core/core.js +244 -164
  37. package/dist/prod/core/dev.js +108 -3
  38. package/dist/prod/core/effect.js +28 -23
  39. package/dist/prod/core/graph.js +9 -9
  40. package/dist/prod/core/heap.js +16 -16
  41. package/dist/prod/core/identities.js +1 -1
  42. package/dist/prod/core/lanes.js +307 -95
  43. package/dist/prod/core/owner.js +22 -22
  44. package/dist/prod/core/scheduler.js +266 -211
  45. package/dist/prod/core/truth.js +9 -7
  46. package/dist/prod/core/verdict.js +161 -99
  47. package/dist/prod/deferred.js +1 -1
  48. package/dist/prod/index.js +2 -0
  49. package/dist/prod/map.js +78 -74
  50. package/dist/prod/reveal.js +34 -34
  51. package/dist/prod/signals.js +3 -3
  52. package/dist/prod/store/affects.js +15 -15
  53. package/dist/prod/store/optimistic.js +185 -89
  54. package/dist/prod/store/projection.js +49 -38
  55. package/dist/prod/store/reconcile.js +8 -2
  56. package/dist/prod/store/store.js +99 -63
  57. package/dist/prod/store/types.js +11 -1
  58. package/dist/types/affects.d.ts +3 -2
  59. package/dist/types/boundaries.d.ts +14 -2
  60. package/dist/types/core/async.d.ts +2 -1
  61. package/dist/types/core/constants.d.ts +8 -0
  62. package/dist/types/core/copies.d.ts +54 -0
  63. package/dist/types/core/core.d.ts +15 -0
  64. package/dist/types/core/dev.d.ts +1 -1
  65. package/dist/types/core/lanes.d.ts +8 -1
  66. package/dist/types/core/scheduler.d.ts +36 -4
  67. package/dist/types/core/truth.d.ts +3 -1
  68. package/dist/types/core/types.d.ts +6 -0
  69. package/dist/types/index.d.ts +2 -0
  70. package/dist/types/store/hooks.d.ts +15 -1
  71. package/dist/types/store/store.d.ts +2 -1
  72. package/dist/types/store/target.d.ts +5 -0
  73. package/dist/types/store/types.d.ts +7 -0
  74. package/package.json +1 -1
@@ -1,14 +1,16 @@
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";
1
+ import { CONFIG_GUESS, NOT_PENDING, CONFIG_OVERRIDE, REACTIVE_DIRTY, REACTIVE_CHECK, REACTIVE_IN_HEAP, STATUS_PENDING, CONFIG_SLOT_NODE, REACTIVE_STAGED_READ, 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, CONFIG_INPUTS_PUBLISHED, CONFIG_STAGED, REACTIVE_JOINED } from "./constants.js";
2
2
 
3
- import { ext, tracking, stagedRead } from "./core.js";
3
+ import { ext, bornInto, tracking, creatorReads, stagedRead } from "./core.js";
4
4
 
5
5
  import { NotReadyError } from "./error.js";
6
6
 
7
+ import { removePendingSource, releaseFlightTeardown, handleAsync, notifyStatus, isThenable } from "./async.js";
8
+
7
9
  import { laneValueOf } from "./truth.js";
8
10
 
9
11
  import { enqueueSub } from "./heap.js";
10
12
 
11
- import { schedule, question, nextQuestion, installLaneHooks, GlobalQueue, newTransaction, resolveTx, ownFlights, blocked, txOf, clock, insertSubs, laneDirty, queuePendingNode, list, passLane, setPassLane, sameLane, flushTransaction, commitPendingNode, releaseQueues, holdNode, joinFuture, reruns, merge } from "./scheduler.js";
13
+ import { schedule, question, nextQuestion, armGuessing, installLaneHooks, GlobalQueue, newTransaction, list, staleReader, resolveTx, txOf, commitPendingNode, ownFlights, stagedReaders, blocked, clock, insertSubs, laneDirty, flushTransaction, queuePendingNode, passLane, setPassLane, sameLane, releaseQueues, holdNode, reruns, merge } from "./scheduler.js";
12
14
 
13
15
  // Lanes (plan sec. 28 replay, 2026-10-02; maintainer rulings 2026-10-01/02). A lane
14
16
  // is "a new base of a transition": the sub-frame an optimistic write opens
@@ -49,9 +51,10 @@ function newLane(e) {
49
51
  }
50
52
 
51
53
  /** What the screen shows for a lane's node: the lane's value once the lane
52
- * has revealed, the committed truth before. */ function display(e) {
54
+ * has revealed, the committed truth before — NOT_PENDING when there is none
55
+ * (uninitialized, as `laneValueOf`): its read throws NotReady. */ function display(e) {
53
56
  const n = e.T.P;
54
- return e.T.Re.ge && n !== NOT_PENDING ? n : e.le;
57
+ return e.T.Re.Ue && n !== NOT_PENDING ? n : e.m & STATUS_UNINITIALIZED ? NOT_PENDING : e.le;
55
58
  }
56
59
 
57
60
  /** Guesses written since the last seam (`[node, value, question, …]`).
@@ -64,9 +67,8 @@ function newLane(e) {
64
67
  /** What the writer sees of a node: an unflushed guess to it, else the
65
68
  * lane's value or the committed one — what an optimistic updater composes
66
69
  * on (a store setter's draft too, store/optimistic.ts). */ function guessedValueOf(e) {
67
- const n = pendingGuessOf(e);
68
- if (n !== NOT_PENDING) return n;
69
- return e.l & CONFIG_OVERRIDE ? laneValueOf(e) : e.le;
70
+ let n = pendingGuessOf(e);
71
+ return n !== NOT_PENDING || e.l & CONFIG_OVERRIDE && (n = laneValueOf(e)) !== NOT_PENDING ? n : e.le;
70
72
  }
71
73
 
72
74
  /** The unflushed guess to `n`, else NOT_PENDING. */ function pendingGuessOf(e) {
@@ -74,6 +76,32 @@ function newLane(e) {
74
76
  return NOT_PENDING;
75
77
  }
76
78
 
79
+ /** This module's copy of scheduler.ts `guessing`: its readers here stay
80
+ * foldable when a bundler splits lanes.ts from scheduler.ts into separate
81
+ * chunks (a cross-chunk binding is not folded; the compiled live page). */ let guessed = false;
82
+
83
+ /** Guesses another action's lane took over (#4336): the node, its guess,
84
+ * the guess's lane, its question, the lane that took it. */ const kept = [];
85
+
86
+ /** Lanes a revert dissolved: the guesses they took come back at their
87
+ * parent's landing (`endLanes`). */ const reverted = /* @__PURE__ */ new WeakSet;
88
+
89
+ /** Lane `l` takes `n`'s guess (another action's), kept to come back if `l`
90
+ * reverts. Its readers saw the guess: they re-derive at the lane's reveal
91
+ * (and, a flight's, hold the lane while they would show it). `d`: a guess
92
+ * `l` takes without writing it (a store target's other slots) — the node
93
+ * shows its truth in `l`'s world until the guess comes back. */ function take(e, n, s) {
94
+ const t = e.T;
95
+ kept.push(e, t.P, t.Re, t.Ie, n);
96
+ rerun(e, n);
97
+ e.l &= s ? -10241 : ~CONFIG_GUESS;
98
+ if (s) t.Re = null, t.P = NOT_PENDING; else list(e, n);
99
+ }
100
+
101
+ /** `n`'s readers are stale readers of `t` (re-derived at its landing). */ function rerun(e, n) {
102
+ for (let s = e.o; s !== null; s = s.t) staleReader(s.u, n);
103
+ }
104
+
77
105
  function optimisticWrite(e, n) {
78
106
  // What the user sees: an unflushed guess to the same node, else the lane's
79
107
  // value or the committed one. The updater composes on it; guessing it
@@ -82,8 +110,10 @@ function optimisticWrite(e, n) {
82
110
  // the guess's question and entangles its frame (#3347) without notifying.
83
111
  const s = guessedValueOf(e);
84
112
  if (typeof n === "function") n = n(s);
85
- if (e.xe && e.xe(s, n) && !(e.l & CONFIG_GUESS)) return n;
113
+ if (e.ye && e.ye(s, n) && !(e.l & CONFIG_GUESS)) return n;
86
114
  pendingGuesses.push(e, n, question || nextQuestion());
115
+ armGuessing();
116
+ guessed = true;
87
117
  schedule();
88
118
  return n;
89
119
  }
@@ -100,7 +130,7 @@ function optimisticWrite(e, n) {
100
130
  /** A guess's own truth is in flight: the node's own refetch — or, a store
101
131
  * leaf's, its family's derive (the slot's flight; store/optimistic.ts
102
132
  * installs the resolver). A17: the guess stands in for the flight. */ function inFlight(e) {
103
- return (e.m & STATUS_PENDING) !== 0 || (e.l & CONFIG_SLOT_NODE) !== 0 && GlobalQueue.k?.(e) === true;
133
+ return e.m & STATUS_PENDING || e.l & CONFIG_SLOT_NODE && GlobalQueue.k?.(e);
104
134
  }
105
135
 
106
136
  function applyGuesses(e) {
@@ -117,9 +147,9 @@ function applyGuesses(e) {
117
147
  // A lane's node already: a guess re-asked, or a derivation guessed
118
148
  // over (its staging is void — the guess is the value now). The same
119
149
  // value re-asked renews the question; nothing to notify.
120
- u = !t.xe || !t.xe(laneValueOf(t), l);
150
+ u = !t.ye || !t.ye(laneValueOf(t), l);
121
151
  if (!(t.l & CONFIG_GUESS)) {
122
- t.ie = NOT_PENDING;
152
+ t.ue = NOT_PENDING;
123
153
  t.l |= CONFIG_GUESS;
124
154
  }
125
155
  t.T.P = l;
@@ -146,7 +176,7 @@ function applyGuesses(e) {
146
176
  list(t, s);
147
177
  }
148
178
  if (e !== null && i !== null) merge(e, i);
149
- ext(t).Te = pendingGuesses[s + 2];
179
+ ext(t).Ie = pendingGuesses[s + 2];
150
180
  if (u) {
151
181
  // The lane's members re-derive as its work (REACTIVE_LANE_DIRTY): a
152
182
  // blocked lane's re-guess does not re-run its render effects as stale
@@ -166,24 +196,31 @@ function applyGuesses(e) {
166
196
  * action, not with the one it shares a reader with). A nested lane reading
167
197
  * through an ancestor's guess stays its own (the screen plus its own
168
198
  * guesses). */ function enterLane(e, n) {
169
- n.R |= REACTIVE_LANE_READ;
170
- const s = passLane;
171
- if (s === null) {
199
+ const s = n.R |= REACTIVE_LANE_READ, t = passLane;
200
+ if (t === null) {
201
+ const t = e._parent.Pe === e;
202
+ // A verdict lane's value (a memo of `latest` / `isPending`, F-6) read by
203
+ // a derivation that already read the frame's staging or joined its
204
+ // hold, or by any pass a pass that did is creating: the verdict read
205
+ // does not break it out — it stays the frame's, as the same read inline
206
+ // does (verdict.ts `markVerdictReader`, `route`; D-25, D-26). An
207
+ // effect's own reads do not count (its `_type`, 1–3, carries neither bit).
208
+ if (!(t && creatorReads(n, n.i || s))) {
209
+ setPassLane(e);
210
+ // The pass read a held write before its first lane read (it joined the
211
+ // frame's future as mainline would): lane work sees the screen — the
212
+ // seam re-derives it on the committed world next round (`stagedReaders`,
213
+ // the park's repair), as it does a lane pass that read a staging. Not a
214
+ // pass whose held reads were all of nodes born held (no staged read,
215
+ // `stagedRead`): it has no committed world to re-derive on (#3969).
216
+ // (A pass not joined: a held write beside a guess, #4336; a leaf of a
217
+ // verdict lane — its plain reads are the screen's, D-26 `[s(), L()]`.)
218
+ if (s & REACTIVE_STAGED_READ && (guessed || t || s & REACTIVE_JOINED)) stagedRead(n);
219
+ }
220
+ } else if (sameLane(t, e)) setPassLane(e); else if (!under(t, e)) {
221
+ if (!under(e, t)) linkLanes(t, e);
172
222
  setPassLane(e);
173
- // The pass read a held write before its first lane read (it joined the
174
- // frame's future as mainline would): lane work sees the screen — the
175
- // seam re-derives it on the committed world next round (`stagedReaders`,
176
- // the park's repair), as it does a lane pass that read a staging. Not a
177
- // pass whose held reads were all of nodes born held (no staged read,
178
- // `stagedRead`): it has no committed world to re-derive on (#3969).
179
- if (n.R & REACTIVE_JOINED && n.R & REACTIVE_STAGED_READ) stagedRead(n);
180
- return;
181
223
  }
182
- if (sameLane(s, e)) return setPassLane(e);
183
- if (under(s, e)) return;
184
- if (under(e, s)) return setPassLane(e);
185
- linkLanes(s, e);
186
- setPassLane(e);
187
224
  }
188
225
 
189
226
  /** Is `l` nested under `a` (an ancestor lane)? */ function under(e, n) {
@@ -253,10 +290,19 @@ function unlink(e) {
253
290
  const n = e.T;
254
291
  if (n !== null) {
255
292
  const s = n.Re;
256
- // Cleared before equality, so a return to the committed value notifies
257
- // nobody. The seam wakes it unless the frame is still held (#3892).
258
- if (s?.ge && !blocked(resolveTx(s._parent))) e.R |= LEFT_SHOWN;
259
- if (s?.P) n.Re = null;
293
+ if (s?.P) {
294
+ // Cleared before equality, so a return to the committed value
295
+ // notifies nobody. The seam wakes it (#3892) — always for a pass
296
+ // outside any transaction's flush: its value is the screen's now
297
+ // (#4367). While a transaction's flush runs the pass and the lane's
298
+ // frame is still held, what the screen shows stays the screen until
299
+ // the landing, and `_value` takes it (as `dissolveLane` keeps a shown
300
+ // lane's), so this pass's answer — the frame's — is a change against
301
+ // what is seen and reaches the screen with the frame's commit. (Only
302
+ // a lane is ever `_shown`.)
303
+ if (s.Ue) if (!flushTransaction || !blocked(resolveTx(s._parent))) e.R |= LEFT_SHOWN; else e.le = display(e);
304
+ n.Re = null;
305
+ }
260
306
  n.P = NOT_PENDING;
261
307
  }
262
308
  e.l &= -1050881;
@@ -264,7 +310,7 @@ function unlink(e) {
264
310
  }
265
311
  if (!e.i) {
266
312
  if (!(e.l & CONFIG_OVERRIDE)) {
267
- e.ie = NOT_PENDING;
313
+ e.ue = NOT_PENDING;
268
314
  e.l = e.l & -1048833 | CONFIG_OVERRIDE;
269
315
  ext(e);
270
316
  }
@@ -274,7 +320,12 @@ function unlink(e) {
274
320
  // of the lane from here (its value the lane's, its fate the lane's), no
275
321
  // longer a written guess. A pass with no answer (pending, errored)
276
322
  // leaves the guess.
277
- if (!t) e.l &= ~CONFIG_GUESS;
323
+ // Nor a pass of another action's guess: the re-ask is this lane's new
324
+ // question, which the guess (over the old one) does not answer — the
325
+ // lane waits for its own answer (F-1, F-6, #4336). (A guess of the
326
+ // lane's own action, D-33's entangled slot, still stands in.)
327
+ // (Read behind `guessed`: a bundle without optimism drops the reads.)
328
+ if (guessed && e.l & CONFIG_GUESS && e.T.Re?.P && resolveTx(e.T.Re._parent) !== resolveTx(n._parent)) take(e, n); else if (!t) e.l &= ~CONFIG_GUESS;
278
329
  }
279
330
  // A leaf has no lane value to seat it (`recompute`): one left waiting on
280
331
  // the lane's flight runs its next pass as the lane's — the landing that
@@ -297,6 +348,21 @@ function unlink(e) {
297
348
  return true;
298
349
  }
299
350
 
351
+ /** The truth arrived for a written guess (A18): a correction dissolves the
352
+ * lane — the guess and everything derived from it is void, the truth is
353
+ * the parent's held write and the graph re-derives from it there, one frame
354
+ * at the parent's landing — while the display keeps what it shows until
355
+ * then. A confirmation is silent (A17): the truth stays staged beneath the
356
+ * guess for the parent's commit, the guess stays the value; only a reader
357
+ * of the truth (CONFIG_AUTHORITATIVE) is told its view changed. Either way
358
+ * the parent takes this node, not the tick that carried the truth (D-33):
359
+ * one landing answering two actions' guesses (a store re-ask over two
360
+ * rows) must not tie them together; a pass that derives from the held
361
+ * truth joins the parent on its own (`joinPass`). */
362
+ /** The correction round is replacing a guess with the value it covered (no
363
+ * landing beneath it): observe's "reverted"; the lane gives back the
364
+ * guesses it took (#4336, `endLanes`). */ let reverting = false;
365
+
300
366
  function supersede(e, n, s) {
301
367
  const t = txOf(e);
302
368
  // Resolved: the parent may have merged into another transaction since the
@@ -309,9 +375,10 @@ function supersede(e, n, s) {
309
375
  if (s && e.l & CONFIG_SLOT_NODE && GlobalQueue.nn?.(e.T.P, n)) s = false;
310
376
  if (s) {
311
377
  dissolveLane(t, l, e);
378
+ if (guessed && reverting) reverted.add(t);
312
379
  // What the screen showed stays the screen until the parent lands (A18
313
380
  // (c): display and untracked reads keep the override until the commit).
314
- if (t.ge) e.le = e.T.P;
381
+ if (t.Ue) e.le = e.T.P;
315
382
  e.l &= -10241;
316
383
  e.T.P = NOT_PENDING;
317
384
  holdNode(e, l);
@@ -321,8 +388,7 @@ function supersede(e, n, s) {
321
388
  // A confirmation is silent (A17); only truth readers are told.
322
389
  GlobalQueue.sn?.(t);
323
390
  }
324
- e.ie = n;
325
- joinFuture(l);
391
+ e.ue = n;
326
392
  }
327
393
 
328
394
  /** A lane's end, two ways. `into === null`: the parent landed — the lane's
@@ -345,18 +411,20 @@ function supersede(e, n, s) {
345
411
  e.Pe = null;
346
412
  if (lanes.indexOf(l) !== -1) dissolveLane(l, null);
347
413
  }
414
+ // First loads this dissolution leaves loading (below).
415
+ const u = [];
348
416
  for (let t = 0; t < e.tn.length; t++) {
349
417
  const l = e.tn[t];
350
- const u = l.T;
351
- if (l === s || u.Re !== e) continue;
352
- const i = l.i;
353
- const r = l.l & CONFIG_GUESS;
354
- const o = u.P;
418
+ const i = l.T;
419
+ if (l === s || i.Re !== e) continue;
420
+ const r = l.i;
421
+ const o = l.l & CONFIG_GUESS;
422
+ const E = i.P;
355
423
  // What the screen shows of it (NOT_PENDING: nothing — a lane pass that
356
424
  // errored or pends staged no value), and the lane's latest.
357
- const E = e.ge && !i ? o : l.le;
425
+ const a = e.Ue && !r ? E : l.le;
358
426
  const f = laneValueOf(l);
359
- u.P = NOT_PENDING;
427
+ i.P = NOT_PENDING;
360
428
  l.l &= -10241;
361
429
  if (n === null) {
362
430
  // A derivation the revert re-derives (dirtied by the guess's
@@ -364,20 +432,33 @@ function supersede(e, n, s) {
364
432
  // lane's answer beside inputs that are the truth now: a fresh reader
365
433
  // of its flight observes it (#3648, #3651; #3305's commit beneath a
366
434
  // flight).
367
- u.Re = null;
368
- if (!i && !r) l.ie = f;
435
+ // Not a first load the lane produced nothing for whose own flight is
436
+ // up, nor one pending on such a load kept here (an identity memo of
437
+ // it, a derived store's derive): committing its empty `_value` would
438
+ // initialize it as `undefined`, which `latest()` would serve and
439
+ // `isPending()` call pending. It stays loading until its flight
440
+ // answers (D-22, A19 exc. 1; async-gen 3289 #929, 91501 #504, #156).
441
+ i.Re = null;
442
+ if (!r && !o) E === NOT_PENDING && (l.m & (STATUS_UNINITIALIZED | STATUS_PENDING)) === (STATUS_UNINITIALIZED | STATUS_PENDING) && (i.Ee || u.some(e => i.se?.has(e))) ? u.push(l) : l.ue = f;
369
443
  commitPendingNode(l);
370
- if (!i) {
444
+ if (!r) {
371
445
  if (l.R & (REACTIVE_IN_HEAP | REACTIVE_DIRTY | REACTIVE_CHECK) || l.m & STATUS_PENDING) l.l |= CONFIG_INPUTS_PUBLISHED;
372
- if (E !== NOT_PENDING && (!l.xe || !l.xe(E, l.le))) insertSubs(l);
446
+ if (a !== NOT_PENDING && (!l.ye || !l.ye(a, l.le))) insertSubs(l);
373
447
  }
374
- } else if (r) {
448
+ // A stale reader's own error — its pass (a memo's or a render
449
+ // effect's) threw over the committed value of what the parent held
450
+ // (A10), served as the screen — is no answer of the world landing now:
451
+ // it re-derives on it (A15; D-39, the landing is the new answer). The
452
+ // landing's `reruns` skips an errored reader: its error is an errored
453
+ // source's (#3945).
454
+ if (l.R & REACTIVE_FRAME_READ && l.m & STATUS_ERROR && i.U.source === l) enqueueSub(l);
455
+ } else if (o) {
375
456
  // A guess of a dissolving lane: its truth — a landing held beneath it,
376
457
  // else the value it covered — is the parent's to land, and its readers
377
458
  // re-derive from it there. What the screen showed stays the screen
378
459
  // until then (A18 (c)).
379
- if (l.ie === NOT_PENDING) l.ie = covered(l);
380
- if (e.ge) l.le = o;
460
+ if (l.ue === NOT_PENDING) l.ue = covered(l);
461
+ if (e.Ue) l.le = E;
381
462
  holdNode(l, n);
382
463
  insertSubs(l);
383
464
  } else {
@@ -388,19 +469,19 @@ function supersede(e, n, s) {
388
469
  // flight is asking the void world's question: its landing is nobody's
389
470
  // answer — it re-asks from the truth (whose notification may not reach
390
471
  // it: an input's truth equal to its committed value is silent).
391
- u.Re = null;
392
- if (!i) {
393
- if (E !== NOT_PENDING && e.ge) l.ie = E;
472
+ i.Re = null;
473
+ if (!r) {
474
+ if (a !== NOT_PENDING && e.Ue) l.ue = a;
394
475
  if (l.m & STATUS_PENDING) {
395
- u.Ee = null;
476
+ i.Ee = null;
396
477
  enqueueSub(l);
397
478
  }
398
479
  }
399
- if (e.ge) {
480
+ if (e.Ue) {
400
481
  commitPendingNode(l);
401
- if (!i) l.l |= CONFIG_INPUTS_PUBLISHED;
482
+ if (!r) l.l |= CONFIG_INPUTS_PUBLISHED;
402
483
  } else {
403
- if (u.Ue !== null || u.me !== null) GlobalQueue.Ce(l, false, true);
484
+ if (i.me !== null || i.Le !== null) GlobalQueue.Ce(l, false, true);
404
485
  l.l &= -1048961;
405
486
  }
406
487
  }
@@ -416,7 +497,7 @@ function supersede(e, n, s) {
416
497
  // void: nothing of it reached the screen. So are the runs a blocked lane
417
498
  // parked: they show a re-guess the seam never revealed, beside derivations
418
499
  // that keep what the screen showed.
419
- if (n === null || e.ge && !e.ln) releaseQueues(e); else e.un[0].length = e.un[1].length = 0;
500
+ if (n === null || e.Ue && !e.ln) releaseQueues(e); else e.un[0].length = e.un[1].length = 0;
420
501
  }
421
502
 
422
503
  /** Lanes. A lane's node is read by its reader's kind: an authoritative
@@ -478,17 +559,22 @@ function supersede(e, n, s) {
478
559
  if ((l || passLane !== t) && passLane?._parent.Pe === passLane) setPassLane(null);
479
560
  if (passLane === null || !sameLane(passLane, t)) {
480
561
  // (A lane that has never shown: the screen is the committed value.)
481
- if (l && t.ge) return NOT_PENDING;
482
- if (!s && n.ie !== NOT_PENDING && e.l & CONFIG_HELD && flushTransaction !== null && resolveTx(flushTransaction) === txOf(e)) return n.ie;
562
+ if (l && t.Ue) return NOT_PENDING;
563
+ if (!s && n.ue !== NOT_PENDING && e.l & CONFIG_HELD && flushTransaction !== null && resolveTx(flushTransaction) === txOf(e)) return n.ue;
483
564
  if (t.ln || i) {
484
- if (!s && !t.ge && u & STATUS_UNINITIALIZED) throw new NotReadyError(null);
485
565
  // Re-derived at the reveal; once per pass (the pass may also be a stale
486
566
  // reader of a transaction, REACTIVE_FRAME_READ — both reruns apply).
487
- if (!(e.R & REACTIVE_SCREEN_READ)) {
567
+ // Before the throw below: a leaf that waits on a lane that has never
568
+ // shown has no flight to settle it — only the reveal re-derives it,
569
+ // and its pending holds its frame until then (#4338).
570
+ if (!(e.R & REACTIVE_SCREEN_READ)) {
488
571
  e.R |= REACTIVE_FRAME_READ | REACTIVE_SCREEN_READ;
489
572
  t.En.push(e);
490
573
  }
491
- return display(n);
574
+ // Nothing on screen yet (uninitialized: `display` is NOT_PENDING).
575
+ const s = display(n);
576
+ if (s === NOT_PENDING) throw new NotReadyError(null);
577
+ return s;
492
578
  }
493
579
  // A verdict reader reads ahead of the seam's verdict: re-derived on the
494
580
  // screen if the lane holds (#4284).
@@ -501,7 +587,7 @@ function supersede(e, n, s) {
501
587
 
502
588
  /** The value a guess covered: the node's committed value — a store slot's
503
589
  * asked of the store (a chained link follows the inner store's commits). */ function covered(e) {
504
- return e.l & CONFIG_SLOT_NODE && GlobalQueue.fn !== undefined ? GlobalQueue.fn(e) : e.le;
590
+ return e.l & CONFIG_SLOT_NODE && GlobalQueue.an !== undefined ? GlobalQueue.an(e) : e.le;
505
591
  }
506
592
 
507
593
  /** A guess in `l` whose own truth is in flight (its refetch — authoritative:
@@ -540,8 +626,10 @@ function supersede(e, n, s) {
540
626
  for (let e = 0; e < t.tn.length && lanes.indexOf(t) !== -1; e++) {
541
627
  const n = t.tn[e];
542
628
  if (n.l & CONFIG_GUESS && n.T.Re === t) {
543
- const e = n.ie !== NOT_PENDING ? n.ie : covered(n);
544
- supersede(n, e, !n.xe || !n.xe(n.T.P, e));
629
+ const e = n.ue !== NOT_PENDING ? n.ue : covered(n);
630
+ if (guessed) reverting = n.ue === NOT_PENDING;
631
+ supersede(n, e, !n.ye || !n.ye(n.T.P, e));
632
+ if (guessed) reverting = false;
545
633
  }
546
634
  }
547
635
  }
@@ -556,8 +644,8 @@ function laneSeam(e, n) {
556
644
  return;
557
645
  }
558
646
  e.ln = false;
559
- e.ge = true;
560
- reruns(e);
647
+ e.Ue = true;
648
+ if (reruns(e)) revealing.push(resolveTx(e._parent));
561
649
  for (let n = 0; n < e.tn.length; n++) {
562
650
  const s = e.tn[n];
563
651
  if (s.R & LEFT_SHOWN) {
@@ -566,13 +654,13 @@ function laneSeam(e, n) {
566
654
  }
567
655
  const t = s.T;
568
656
  if (t.Re !== e) continue;
569
- if (!(s.l & CONFIG_GUESS) && !s.i && s.ie !== NOT_PENDING) {
570
- t.P = s.ie;
571
- s.ie = NOT_PENDING;
657
+ if (!(s.l & CONFIG_GUESS) && !s.i && s.ue !== NOT_PENDING) {
658
+ t.P = s.ue;
659
+ s.ue = NOT_PENDING;
572
660
  }
573
661
  // The frame this pass built is the screen's now; the one it replaced
574
662
  // goes (as `commitPendingNode` retires a committed pass's).
575
- if (t.Ue !== null || t.me !== null) GlobalQueue.Ce(s, false, true);
663
+ if (t.me !== null || t.Le !== null) GlobalQueue.Ce(s, false, true);
576
664
  s.l &= ~CONFIG_STAGED;
577
665
  }
578
666
  releaseQueues(e);
@@ -580,6 +668,25 @@ function laneSeam(e, n) {
580
668
 
581
669
  /** The parent landed: its lanes end, nested first. (None is blocked: a
582
670
  * blocked lane blocks its parent.) */ function endLanes(e) {
671
+ // A revert landing (#4336): the guesses its lane took from another action
672
+ // that is still out come back in this frame, over the truth it lands —
673
+ // its readers re-derive with the landing (one frame, no bare truth
674
+ // between). Taken by a later lane since, a node stays that lane's.
675
+ if (guessed) for (let n = kept.length - 5; n >= 0; n -= 5) if (resolveTx(kept[n + 4]._parent) === e) {
676
+ let [s, t, l, u, i] = kept.splice(n, 5);
677
+ // (A store slot's node now: the one taken, or a read's since.)
678
+ if (reverted.has(i) && lanes.includes(l) && (!(s.l & CONFIG_SLOT_NODE) || (s = GlobalQueue.fn(s, true)))) {
679
+ const n = ext(s);
680
+ if (n.Re === null || txOf(s) === e) {
681
+ commitPendingNode(s);
682
+ n.P = t;
683
+ n.Ie = u;
684
+ s.l |= CONFIG_OVERRIDE | CONFIG_GUESS;
685
+ list(s, l);
686
+ rerun(s, e);
687
+ }
688
+ }
689
+ }
583
690
  for (let n = lanes.length - 1; n >= 0; n--) {
584
691
  const s = lanes[n];
585
692
  if (resolveTx(s._parent) !== e) continue;
@@ -596,7 +703,13 @@ function laneSeam(e, n) {
596
703
  return e.P && linkBlocked(e) || nestedBlocked(e);
597
704
  }
598
705
 
706
+ /** Parents whose lane revealed this seam with readers to re-derive there
707
+ * (`laneSeam`): the lane shows ahead of its parent (F-6), so the parent lands
708
+ * a round later — after those readers have shown the lane's answer — even
709
+ * when nothing else holds it (#4393). */ const revealing = [];
710
+
599
711
  /** The lanes under `t` (its own; a link group's are each other's). */ function nestedBlocked(e) {
712
+ if (revealing.indexOf(e) !== -1) return true;
600
713
  for (let n = 0; n < lanes.length; n++) {
601
714
  const s = lanes[n];
602
715
  if (resolveTx(s._parent) !== e) continue;
@@ -610,7 +723,9 @@ function laneSeam(e, n) {
610
723
  * landing), judged against the guess. On a derivation it is the lane's
611
724
  * latest: its staging once the lane has shown (promoted at the next
612
725
  * reveal), its slot before; the lane's members re-derive as its work. */ function laneWrite(e, n) {
613
- if (e.l & CONFIG_GUESS) {
726
+ // (A store slot's guess its derive's re-ask in another action's lane
727
+ // writes is that lane's derivation from here — store `_slotTake`, #4336.)
728
+ if (e.l & CONFIG_GUESS && !(guessed && GlobalQueue.fn?.(e))) {
614
729
  // Provenance (A18, #3331; Q-D, plan sec. 39): a write asking an OLDER
615
730
  // question than the guess's — another action's landing beneath it —
616
731
  // is not its answer: held beneath (the truth for the commit; the
@@ -621,18 +736,18 @@ function laneSeam(e, n) {
621
736
  // A store re-bases a keyed arrangement guess over an older truth, and
622
737
  // folds a current one beneath it (D-34): true.
623
738
  const s = stale(e, question);
624
- if (GlobalQueue.an?.(e, n, s) || s) {
625
- e.ie = n;
739
+ if (GlobalQueue.Nn?.(e, n, s) || s) {
740
+ e.ue = n;
626
741
  e.l |= CONFIG_HELD;
627
742
  schedule();
628
743
  return n;
629
744
  }
630
- supersede(e, n, !e.xe || !e.xe(e.T.P, n));
745
+ supersede(e, n, !e.ye || !e.ye(e.T.P, n));
631
746
  } else {
632
747
  const s = txOf(e);
633
- if (e.xe && e.xe(laneValueOf(e), n)) return n;
634
- if (s.ge) e.ie = n; else e.T.P = n;
635
- if (e.X !== undefined) e.Se = clock;
748
+ if (e.ye && e.ye(laneValueOf(e), n)) return n;
749
+ if (s.Ue) e.ue = n; else e.T.P = n;
750
+ if (e.X !== undefined) e.ie = clock;
636
751
  insertSubs(e);
637
752
  laneDirty(e, s);
638
753
  }
@@ -654,18 +769,18 @@ function laneSeam(e, n) {
654
769
  * A24). The guess's own question answering supersedes as usual. */ function laneOutcome(e, n, s) {
655
770
  if (s) return true;
656
771
  if (stale(e, answered(e))) {
657
- e.ie = n;
772
+ e.ue = n;
658
773
  e.l |= CONFIG_HELD;
659
774
  return true;
660
775
  }
661
- supersede(e, n, !e.xe || !e.xe(e.T.P, n));
776
+ supersede(e, n, !e.ye || !e.ye(e.T.P, n));
662
777
  return true;
663
778
  }
664
779
 
665
780
  /** THE provenance test, for a derivation's pass and a write alike: an
666
781
  * answer to question `q` landing on a guess is stale when `q` is older
667
782
  * than the guess's own; unstamped (0) and mainline are current. */ function stale(e, n) {
668
- return n !== 0 && n < e.T.Te;
783
+ return n !== 0 && n < e.T.Ie;
669
784
  }
670
785
 
671
786
  /** The question a derivation's pass answered: the newest among the stamped
@@ -674,7 +789,7 @@ function laneSeam(e, n) {
674
789
  let n = 0;
675
790
  for (let s = e.C; s !== null; s = s.G) {
676
791
  const e = s.S;
677
- if (e.T !== null && e.T.Te > n && e.Se === clock) n = e.T.Te;
792
+ if (e.T !== null && e.T.Ie > n && e.ie === clock) n = e.T.Ie;
678
793
  }
679
794
  return n;
680
795
  }
@@ -689,33 +804,100 @@ const applyGuessesAt = e => {
689
804
  if (pendingGuesses.length !== 0) applyGuesses(e);
690
805
  };
691
806
 
807
+ /** `t` is parked by `r`'s pass alone: each node keeping it — `t`'s own, or
808
+ * a lane's under it — is `r` itself (its flight, in the verdict lane) or a
809
+ * render effect pending on `r` only: the frame observing the lane work that
810
+ * read its staging. Anything else (an open action, a guess's flight,
811
+ * another flight) holds it on its own. */ function heldBy(e, n) {
812
+ if (e.tt !== 0 || e.P && linkBlocked(e)) return false;
813
+ for (let s = -1, t = e; t; t = lanes[++s]) if (t === e || resolveTx(t._parent) === e) {
814
+ if (t !== e && guessFlights(t)) return false;
815
+ for (let e = 0; e < t.tn.length; e++) {
816
+ const s = t.tn[e];
817
+ if (s === n || !(s.m & STATUS_PENDING) || s.T.Re !== null && txOf(s) !== t) continue;
818
+ const l = s.T.se;
819
+ if (s.i !== EFFECT_RENDER || l?.size !== 1 || !l.has(n)) return false;
820
+ }
821
+ }
822
+ return true;
823
+ }
824
+
692
825
  const laneSeams = e => {
693
826
  // Passes that read a write the parked frame holds as the screen
694
827
  // (`stagedReaders`: lane work, a verdict reader before the verdict): they
695
828
  // re-derive on the committed world next round (and register as stale
696
829
  // readers of the transaction there); a lane's runs wait that round — a
697
830
  // lane sees the screen plus its own guesses.
831
+ if (revealing.length) revealing.length = 0;
698
832
  const n = [];
699
- if (e !== null) for (let s = 0; s < e.length; s++) {
700
- const t = e[s];
701
- const l = t.T?.Re;
833
+ if (e !== null) for (let s = 0; s < stagedReaders.length; s++) {
834
+ const t = stagedReaders[s],
835
+ // A pass no transaction holds that read the frame's staging (an
836
+ // effect, a `[s(), latest(s)]` reader watched before the frame had
837
+ // a transaction): judged by the frame's — it re-derives only if the
838
+ // frame stays parked; landing here, what it read is the screen (one
839
+ // run, D-22).
840
+ l = t.T?.Re || (t.R & REACTIVE_STAGED_READ ? e : null);
702
841
  // A verdict reader a later read of the same pass routed into a verdict
703
842
  // lane answered for itself: it re-derives at the holder's landing, not
704
843
  // now (one run — #3322, #3540). Other verdict-lane work re-derives only
705
844
  // if the frame stays parked: a transaction landing at this seam makes
706
- // what it read the screen (one run).
707
- if (t.R & REACTIVE_DISPOSED || l != null && l._parent?.Pe === l && (t.l & CONFIG_VERDICT ? !(t.R & REACTIVE_PROBE_UNANSWERED) : !blocked(l._parent))) continue;
845
+ // what it read the screen (one run). The frame is the transaction that
846
+ // holds what it read (`t`, this flush's — not always the lane's parent:
847
+ // a landing that reached a frame held elsewhere joined that one). Nor
848
+ // when only this pass keeps it parked (`heldBy`): the frame lands with
849
+ // the pass's answer, so what the pass read is the screen when anything
850
+ // shows it — re-derived on the committed world, it would ask a question
851
+ // already asked, over inputs the landing replaces. A reader a
852
+ // transaction holds that read none of its stagings (a verdict reader
853
+ // that probed only a flight, held as the frame's pending observer or
854
+ // its staged derivation) has nothing to re-derive: its re-run
855
+ // re-enters that transaction and parks it again, every round — it
856
+ // runs at the landing (#4349). One that read a staging re-derives only
857
+ // if its frame stays parked: landing at this seam, what it read is the
858
+ // screen (one run, D-22). Not one born into that frame (a held pass's
859
+ // child, ruling A): it has no screen, its reads are the transaction's
860
+ // either way — it runs again at the landing, not now.
861
+ if (t.R & REACTIVE_DISPOSED || (l?.P ? l._parent?.Pe === l && (t.l & CONFIG_VERDICT ? !(t.R & REACTIVE_PROBE_UNANSWERED) : !blocked(e) || heldBy(e, t)) : l && !(t.R & REACTIVE_PROBE_UNANSWERED || t.R & REACTIVE_STAGED_READ && blocked(l) && !bornInto(t, l)))) continue;
708
862
  // A leaked effect re-derives seated in its lane from its first read: it
709
863
  // carries no lane value (`laneStage`), so without this its pass starts
710
864
  // as mainline, joins the parked frame on its first held read and reads
711
865
  // the staging, then re-enters the lane at its guess read and leaks
712
866
  // again — every round (#4014). A computed is already seated by its
713
867
  // override, and a written guess's own pass stays the frame's.
714
- if (l != null && l.P && t.i) t.R |= REACTIVE_LANE_DIRTY;
868
+ if (l?.P && t.i) t.R |= REACTIVE_LANE_DIRTY;
869
+ // R5 for an async reader: its frame pass asked the frame's question
870
+ // (F-6's lane gets its own pass now). That flight is the frame's
871
+ // answer: the landing adopts it rather than asking it again (F-9: a
872
+ // re-ask of the same question is no new question; `reruns`). A frame
873
+ // landing at this seam has nothing to re-derive: the pass was the
874
+ // landing's, its flight the screen's answer. Sync or async, the
875
+ // reader's lane pass is still to come: the verdict lane it joins does
876
+ // not reveal this seam (F-6: the lane's readers show its answer
877
+ // together, D-23: atomic with the value) — revealed now, it showed a
878
+ // verdict beside the frame pass's value, before the pass that answers
879
+ // for the lane, and a reader of the re-derived memo read it outside
880
+ // the shown lane and waited for the frame (#4393).
881
+ else if (!l?.P && t.R & REACTIVE_PROBE_UNANSWERED) {
882
+ if (isThenable(t.T?.Ee)) {
883
+ if (!blocked(e)) continue;
884
+ t.T.He = t.T.Ee;
885
+ } else if (blocked(e))
886
+ // A sync reader's lane pass re-derives the async memos over it (a
887
+ // memo over a verdict memo is a verdict reader, D-25 #4050; D-22:
888
+ // `latest()` extracted into a memo reads as inline). The flight
889
+ // each asked over its frame staging, up and held by `t`, is the
890
+ // frame's answer too: the landing adopts it (F-9).
891
+ for (let n = t.o, s; n; n = n.t) if (isThenable((s = n.u).T?.Ee) && s.T.se?.has(s) && s.l & CONFIG_HELD && txOf(s) === e) s.T.He = s.T.Ee;
892
+ if (e?.Pe && n.indexOf(e.Pe) === -1) n.push(e.Pe);
893
+ }
715
894
  enqueueSub(t);
716
- if (l != null && l.P && n.indexOf(l) === -1) n.push(l);
895
+ if (l?.P && n.indexOf(l) === -1) n.push(l);
717
896
  }
718
- for (let e = Math.min(judged, lanes.length) - 1; e >= 0; e--) laneSeam(lanes[e], n.indexOf(lanes[e]) !== -1);
897
+ // A verdict lane born at this seam is judged with it: it exists only once
898
+ // a pass routed into it — the park seam's zombie verdict readers re-run
899
+ // as its work so that this seam reveals them (#3444).
900
+ for (let e = lanes.length - 1; e >= 0; e--) if (e < judged || lanes[e]._parent?.Pe === lanes[e]) laneSeam(lanes[e], n.indexOf(lanes[e]) !== -1);
719
901
  // A verdict reader that entered a lane this seam held: its run waits with
720
902
  // the lane, and the verdict beside it would freeze — it re-derives next
721
903
  // round from the frame's seat, a stale reader of the lane (#4284).
@@ -725,10 +907,40 @@ const laneSeams = e => {
725
907
  }
726
908
  };
727
909
 
910
+ /** The landing's re-derive of a reader whose frame flight the seam parked
911
+ * (R5): the flight asked the question the landing would — adopted, it is
912
+ * asked once (F-9). Not over a pass already queued: that one asks anew.
913
+ * Registered as if this pass had returned it — the node pending on it, its
914
+ * landing the answer — through a promise of its own: the frame pass's
915
+ * registration still listens to the flight, and must not land it a second
916
+ * time. */ const adoptParked = e => {
917
+ const n = e.T?.He;
918
+ if (!n || e.R & (REACTIVE_DIRTY | REACTIVE_CHECK | REACTIVE_IN_HEAP)) return false;
919
+ e.T.He = undefined;
920
+ releaseFlightTeardown(e);
921
+ try {
922
+ handleAsync(e, n.then());
923
+ } catch {}
924
+ e.T.fe = true;
925
+ notifyStatus(e, STATUS_PENDING, new NotReadyError(e));
926
+ return true;
927
+ };
928
+
929
+ /** A15: lane work or a verdict reader was served `el`'s committed value
930
+ * (core.ts `frameRead`), so its pass does not wait on `el`'s flight: what
931
+ * `el`'s pending propagated onto it before the pass (its origins, `el`
932
+ * included) is not this pass's — the landing re-derives it (`reruns`).
933
+ * Kept, a pass that went pending on another source never woke when that
934
+ * source settled (#4397). Only a reader something made pending has anything
935
+ * to drop. */ const laneServed = (e, n) => {
936
+ const s = n.T?.se;
937
+ if (s && e.T?.se) for (const n of s) removePendingSource(e, n);
938
+ };
939
+
728
940
  /** Render effects that read a verdict and entered a lane before its seam (#4284). */ const entered = [];
729
941
 
730
- installLaneHooks(laneRead, laneStage, laneOutcome, laneWrite, applyGuessesAt, laneSeams, () => lanes.length !== 0 && laneCorrections(), endLanes, lanesBlocked);
942
+ installLaneHooks(laneRead, laneStage, laneOutcome, laneWrite, applyGuessesAt, laneSeams, () => lanes.length !== 0 && laneCorrections(), endLanes, lanesBlocked, adoptParked, laneServed);
731
943
 
732
944
  GlobalQueue.J = verdictLane;
733
945
 
734
- export { display, enterLane, guessedValueOf, laneRead, laneStage, laneValueOf, optimisticWrite, pendingGuessOf, supersede, verdictLane };
946
+ export { display, enterLane, guessedValueOf, laneRead, laneStage, laneValueOf, optimisticWrite, pendingGuessOf, supersede, take, verdictLane };