@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,16 +1,18 @@
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
3
  import { attrHooks } from "./attribution-hooks.js";
4
4
 
5
- import { ext, tracking, stagedRead } from "./core.js";
5
+ import { ext, bornInto, tracking, creatorReads, stagedRead } from "./core.js";
6
6
 
7
7
  import { NotReadyError } from "./error.js";
8
8
 
9
+ import { removePendingSource, releaseFlightTeardown, handleAsync, notifyStatus, isThenable } from "./async.js";
10
+
9
11
  import { laneValueOf } from "./truth.js";
10
12
 
11
13
  import { enqueueSub } from "./heap.js";
12
14
 
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";
15
+ import { schedule, question, nextQuestion, armGuessing, installLaneHooks, GlobalQueue, resolveTx, newTransaction, list, staleReader, txOf, commitPendingNode, ownFlights, stagedReaders, blocked, clock, insertSubs, laneDirty, flushTransaction, queuePendingNode, passLane, setPassLane, sameLane, releaseQueues, holdNode, reruns, merge } from "./scheduler.js";
14
16
 
15
17
  // Lanes (plan sec. 28 replay, 2026-10-02; maintainer rulings 2026-10-01/02). A lane
16
18
  // is "a new base of a transition": the sub-frame an optimistic write opens
@@ -51,9 +53,10 @@ function newLane(e) {
51
53
  }
52
54
 
53
55
  /** What the screen shows for a lane's node: the lane's value once the lane
54
- * has revealed, the committed truth before. */ function display(e) {
56
+ * has revealed, the committed truth before — NOT_PENDING when there is none
57
+ * (uninitialized, as `laneValueOf`): its read throws NotReady. */ function display(e) {
55
58
  const n = e.T.V;
56
- return e.T.Je.It && n !== NOT_PENDING ? n : e.le;
59
+ return e.T.Je.Nt && n !== NOT_PENDING ? n : e.m & STATUS_UNINITIALIZED ? NOT_PENDING : e.le;
57
60
  }
58
61
 
59
62
  /** Guesses written since the last seam (`[node, value, question, …]`).
@@ -66,9 +69,8 @@ function newLane(e) {
66
69
  /** What the writer sees of a node: an unflushed guess to it, else the
67
70
  * lane's value or the committed one — what an optimistic updater composes
68
71
  * on (a store setter's draft too, store/optimistic.ts). */ function guessedValueOf(e) {
69
- const n = pendingGuessOf(e);
70
- if (n !== NOT_PENDING) return n;
71
- return e.l & CONFIG_OVERRIDE ? laneValueOf(e) : e.le;
72
+ let n = pendingGuessOf(e);
73
+ return n !== NOT_PENDING || e.l & CONFIG_OVERRIDE && (n = laneValueOf(e)) !== NOT_PENDING ? n : e.le;
72
74
  }
73
75
 
74
76
  /** The unflushed guess to `n`, else NOT_PENDING. */ function pendingGuessOf(e) {
@@ -76,6 +78,32 @@ function newLane(e) {
76
78
  return NOT_PENDING;
77
79
  }
78
80
 
81
+ /** This module's copy of scheduler.ts `guessing`: its readers here stay
82
+ * foldable when a bundler splits lanes.ts from scheduler.ts into separate
83
+ * chunks (a cross-chunk binding is not folded; the compiled live page). */ let guessed = false;
84
+
85
+ /** Guesses another action's lane took over (#4336): the node, its guess,
86
+ * the guess's lane, its question, the lane that took it. */ const kept = [];
87
+
88
+ /** Lanes a revert dissolved: the guesses they took come back at their
89
+ * parent's landing (`endLanes`). */ const reverted = /* @__PURE__ */ new WeakSet;
90
+
91
+ /** Lane `l` takes `n`'s guess (another action's), kept to come back if `l`
92
+ * reverts. Its readers saw the guess: they re-derive at the lane's reveal
93
+ * (and, a flight's, hold the lane while they would show it). `d`: a guess
94
+ * `l` takes without writing it (a store target's other slots) — the node
95
+ * shows its truth in `l`'s world until the guess comes back. */ function take(e, n, s) {
96
+ const t = e.T;
97
+ kept.push(e, t.V, t.Je, t.Ie, n);
98
+ rerun(e, n);
99
+ e.l &= s ? -10241 : ~CONFIG_GUESS;
100
+ if (s) t.Je = null, t.V = NOT_PENDING; else list(e, n);
101
+ }
102
+
103
+ /** `n`'s readers are stale readers of `t` (re-derived at its landing). */ function rerun(e, n) {
104
+ for (let s = e.o; s !== null; s = s.t) staleReader(s.u, n);
105
+ }
106
+
79
107
  function optimisticWrite(e, n) {
80
108
  // What the user sees: an unflushed guess to the same node, else the lane's
81
109
  // value or the committed one. The updater composes on it; guessing it
@@ -86,6 +114,8 @@ function optimisticWrite(e, n) {
86
114
  if (typeof n === "function") n = n(s);
87
115
  if (e.je && e.je(s, n) && !(e.l & CONFIG_GUESS)) return n;
88
116
  pendingGuesses.push(e, n, question || nextQuestion());
117
+ armGuessing();
118
+ guessed = true;
89
119
  schedule();
90
120
  return n;
91
121
  }
@@ -102,7 +132,7 @@ function optimisticWrite(e, n) {
102
132
  /** A guess's own truth is in flight: the node's own refetch — or, a store
103
133
  * leaf's, its family's derive (the slot's flight; store/optimistic.ts
104
134
  * installs the resolver). A17: the guess stands in for the flight. */ function inFlight(e) {
105
- return (e.m & STATUS_PENDING) !== 0 || (e.l & CONFIG_SLOT_NODE) !== 0 && GlobalQueue.k?.(e) === true;
135
+ return e.m & STATUS_PENDING || e.l & CONFIG_SLOT_NODE && GlobalQueue.k?.(e);
106
136
  }
107
137
 
108
138
  function applyGuesses(e) {
@@ -110,29 +140,29 @@ function applyGuesses(e) {
110
140
  for (let s = 0; s < pendingGuesses.length; s += 3) {
111
141
  const t = pendingGuesses[s];
112
142
  const l = pendingGuesses[s + 1];
113
- let u = true;
143
+ let r = true;
114
144
  // The frame already holding the slot: a guess over it is a second
115
145
  // suggestion for the slot, and two cannot finish apart (A34 (1)) — the
116
146
  // frames entangle.
117
- let i = null;
147
+ let u = null;
118
148
  if (t.l & CONFIG_OVERRIDE) {
119
149
  // A lane's node already: a guess re-asked, or a derivation guessed
120
150
  // over (its staging is void — the guess is the value now). The same
121
151
  // value re-asked renews the question; nothing to notify.
122
- u = !t.je || !t.je(laneValueOf(t), l);
152
+ r = !t.je || !t.je(laneValueOf(t), l);
123
153
  if (!(t.l & CONFIG_GUESS)) {
124
- t.ie = NOT_PENDING;
154
+ t.ue = NOT_PENDING;
125
155
  t.l |= CONFIG_GUESS;
126
156
  }
127
157
  t.T.V = l;
128
- i = txOf(t)._parent;
158
+ u = txOf(t)._parent;
129
159
  } else {
130
160
  let s;
131
161
  // A held node: a truth staged there, or what a corrected guess showed.
132
162
  // The guess composes on it, so it ends with that hold — and with its
133
163
  // own frame's (F-11): it lands only when both have (the board's rapid
134
164
  // adds — a later add's row reverted with an earlier add's landing).
135
- if (t.l & CONFIG_HELD) s = newLane(i = txOf(t)); else {
165
+ if (t.l & CONFIG_HELD) s = newLane(u = txOf(t)); else {
136
166
  if (e === null && inFlight(t)) e = newTransaction(false);
137
167
  if (e === null) continue;
138
168
  s = n ??= newLane(e);
@@ -147,9 +177,9 @@ function applyGuesses(e) {
147
177
  t.l |= CONFIG_OVERRIDE | CONFIG_GUESS;
148
178
  list(t, s);
149
179
  }
150
- if (e !== null && i !== null) merge(e, i);
151
- ext(t).de = pendingGuesses[s + 2];
152
- if (u) {
180
+ if (e !== null && u !== null) merge(e, u);
181
+ ext(t).Ie = pendingGuesses[s + 2];
182
+ if (r) {
153
183
  // The lane's members re-derive as its work (REACTIVE_LANE_DIRTY): a
154
184
  // blocked lane's re-guess does not re-run its render effects as stale
155
185
  // readers republishing the committed view (#3460).
@@ -168,24 +198,31 @@ function applyGuesses(e) {
168
198
  * action, not with the one it shares a reader with). A nested lane reading
169
199
  * through an ancestor's guess stays its own (the screen plus its own
170
200
  * guesses). */ function enterLane(e, n) {
171
- n.R |= REACTIVE_LANE_READ;
172
- const s = passLane;
173
- if (s === null) {
201
+ const s = n.R |= REACTIVE_LANE_READ, t = passLane;
202
+ if (t === null) {
203
+ const t = e._parent.it === e;
204
+ // A verdict lane's value (a memo of `latest` / `isPending`, F-6) read by
205
+ // a derivation that already read the frame's staging or joined its
206
+ // hold, or by any pass a pass that did is creating: the verdict read
207
+ // does not break it out — it stays the frame's, as the same read inline
208
+ // does (verdict.ts `markVerdictReader`, `route`; D-25, D-26). An
209
+ // effect's own reads do not count (its `_type`, 1–3, carries neither bit).
210
+ if (!(t && creatorReads(n, n.i || s))) {
211
+ setPassLane(e);
212
+ // The pass read a held write before its first lane read (it joined the
213
+ // frame's future as mainline would): lane work sees the screen — the
214
+ // seam re-derives it on the committed world next round (`stagedReaders`,
215
+ // the park's repair), as it does a lane pass that read a staging. Not a
216
+ // pass whose held reads were all of nodes born held (no staged read,
217
+ // `stagedRead`): it has no committed world to re-derive on (#3969).
218
+ // (A pass not joined: a held write beside a guess, #4336; a leaf of a
219
+ // verdict lane — its plain reads are the screen's, D-26 `[s(), L()]`.)
220
+ if (s & REACTIVE_STAGED_READ && (guessed || t || s & REACTIVE_JOINED)) stagedRead(n);
221
+ }
222
+ } else if (sameLane(t, e)) setPassLane(e); else if (!under(t, e)) {
223
+ if (!under(e, t)) linkLanes(t, e);
174
224
  setPassLane(e);
175
- // The pass read a held write before its first lane read (it joined the
176
- // frame's future as mainline would): lane work sees the screen — the
177
- // seam re-derives it on the committed world next round (`stagedReaders`,
178
- // the park's repair), as it does a lane pass that read a staging. Not a
179
- // pass whose held reads were all of nodes born held (no staged read,
180
- // `stagedRead`): it has no committed world to re-derive on (#3969).
181
- if (n.R & REACTIVE_JOINED && n.R & REACTIVE_STAGED_READ) stagedRead(n);
182
- return;
183
225
  }
184
- if (sameLane(s, e)) return setPassLane(e);
185
- if (under(s, e)) return;
186
- if (under(e, s)) return setPassLane(e);
187
- linkLanes(s, e);
188
- setPassLane(e);
189
226
  }
190
227
 
191
228
  /** Is `l` nested under `a` (an ancestor lane)? */ function under(e, n) {
@@ -255,10 +292,19 @@ function unlink(e) {
255
292
  const n = e.T;
256
293
  if (n !== null) {
257
294
  const s = n.Je;
258
- // Cleared before equality, so a return to the committed value notifies
259
- // nobody. The seam wakes it unless the frame is still held (#3892).
260
- if (s?.It && !blocked(resolveTx(s._parent))) e.R |= LEFT_SHOWN;
261
- if (s?.V) n.Je = null;
295
+ if (s?.V) {
296
+ // Cleared before equality, so a return to the committed value
297
+ // notifies nobody. The seam wakes it (#3892) — always for a pass
298
+ // outside any transaction's flush: its value is the screen's now
299
+ // (#4367). While a transaction's flush runs the pass and the lane's
300
+ // frame is still held, what the screen shows stays the screen until
301
+ // the landing, and `_value` takes it (as `dissolveLane` keeps a shown
302
+ // lane's), so this pass's answer — the frame's — is a change against
303
+ // what is seen and reaches the screen with the frame's commit. (Only
304
+ // a lane is ever `_shown`.)
305
+ if (s.Nt) if (!flushTransaction || !blocked(resolveTx(s._parent))) e.R |= LEFT_SHOWN; else e.le = display(e);
306
+ n.Je = null;
307
+ }
262
308
  n.V = NOT_PENDING;
263
309
  }
264
310
  e.l &= -1050881;
@@ -266,7 +312,7 @@ function unlink(e) {
266
312
  }
267
313
  if (!e.i) {
268
314
  if (!(e.l & CONFIG_OVERRIDE)) {
269
- e.ie = NOT_PENDING;
315
+ e.ue = NOT_PENDING;
270
316
  e.l = e.l & -1048833 | CONFIG_OVERRIDE;
271
317
  ext(e);
272
318
  }
@@ -276,7 +322,12 @@ function unlink(e) {
276
322
  // of the lane from here (its value the lane's, its fate the lane's), no
277
323
  // longer a written guess. A pass with no answer (pending, errored)
278
324
  // leaves the guess.
279
- if (!t) e.l &= ~CONFIG_GUESS;
325
+ // Nor a pass of another action's guess: the re-ask is this lane's new
326
+ // question, which the guess (over the old one) does not answer — the
327
+ // lane waits for its own answer (F-1, F-6, #4336). (A guess of the
328
+ // lane's own action, D-33's entangled slot, still stands in.)
329
+ // (Read behind `guessed`: a bundle without optimism drops the reads.)
330
+ if (guessed && e.l & CONFIG_GUESS && e.T.Je?.V && resolveTx(e.T.Je._parent) !== resolveTx(n._parent)) take(e, n); else if (!t) e.l &= ~CONFIG_GUESS;
280
331
  }
281
332
  // A leaf has no lane value to seat it (`recompute`): one left waiting on
282
333
  // the lane's flight runs its next pass as the lane's — the landing that
@@ -305,9 +356,14 @@ function unlink(e) {
305
356
  * at the parent's landing — while the display keeps what it shows until
306
357
  * then. A confirmation is silent (A17): the truth stays staged beneath the
307
358
  * guess for the parent's commit, the guess stays the value; only a reader
308
- * of the truth (CONFIG_AUTHORITATIVE) is told its view changed. */
309
- /** Observe: the correction round is replacing a guess with the value it
310
- * covered (no landing beneath it) — `optimisticReverted`'s "reverted". */ let reverting = false;
359
+ * of the truth (CONFIG_AUTHORITATIVE) is told its view changed. Either way
360
+ * the parent takes this node, not the tick that carried the truth (D-33):
361
+ * one landing answering two actions' guesses (a store re-ask over two
362
+ * rows) must not tie them together; a pass that derives from the held
363
+ * truth joins the parent on its own (`joinPass`). */
364
+ /** The correction round is replacing a guess with the value it covered (no
365
+ * landing beneath it): observe's "reverted"; the lane gives back the
366
+ * guesses it took (#4336, `endLanes`). */ let reverting = false;
311
367
 
312
368
  function supersede(e, n, s) {
313
369
  const t = txOf(e);
@@ -323,11 +379,12 @@ function supersede(e, n, s) {
323
379
  // Observe: a displayed guess is being replaced by a differing truth — a
324
380
  // landing's (superseded), or the value it covered (reverted: the body
325
381
  // ended with nothing coming true).
326
- if (t.It && attrHooks !== null) attrHooks.optimisticReverted(e, e.T.V, n, reverting ? "reverted" : "superseded");
382
+ if (t.Nt && attrHooks !== null) attrHooks.optimisticReverted(e, e.T.V, n, reverting ? "reverted" : "superseded");
327
383
  dissolveLane(t, l, e);
384
+ if (guessed && reverting) reverted.add(t);
328
385
  // What the screen showed stays the screen until the parent lands (A18
329
386
  // (c): display and untracked reads keep the override until the commit).
330
- if (t.It) e.le = e.T.V;
387
+ if (t.Nt) e.le = e.T.V;
331
388
  e.l &= -10241;
332
389
  e.T.V = NOT_PENDING;
333
390
  holdNode(e, l);
@@ -337,8 +394,7 @@ function supersede(e, n, s) {
337
394
  // A confirmation is silent (A17); only truth readers are told.
338
395
  GlobalQueue.tn?.(t);
339
396
  }
340
- e.ie = n;
341
- joinFuture(l);
397
+ e.ue = n;
342
398
  }
343
399
 
344
400
  /** A lane's end, two ways. `into === null`: the parent landed — the lane's
@@ -361,16 +417,18 @@ function supersede(e, n, s) {
361
417
  e.it = null;
362
418
  if (lanes.indexOf(l) !== -1) dissolveLane(l, null);
363
419
  }
420
+ // First loads this dissolution leaves loading (below).
421
+ const r = [];
364
422
  for (let t = 0; t < e.Ye.length; t++) {
365
423
  const l = e.Ye[t];
366
424
  const u = l.T;
367
425
  if (l === s || u.Je !== e) continue;
368
426
  const i = l.i;
369
- const r = l.l & CONFIG_GUESS;
370
- const o = u.V;
427
+ const o = l.l & CONFIG_GUESS;
428
+ const E = u.V;
371
429
  // What the screen shows of it (NOT_PENDING: nothing — a lane pass that
372
430
  // errored or pends staged no value), and the lane's latest.
373
- const E = e.It && !i ? o : l.le;
431
+ const a = e.Nt && !i ? E : l.le;
374
432
  const f = laneValueOf(l);
375
433
  u.V = NOT_PENDING;
376
434
  l.l &= -10241;
@@ -381,29 +439,42 @@ function supersede(e, n, s) {
381
439
  // displayed guess that lifts to something else — the truth staged
382
440
  // beneath it (superseded by a landing held there) or the value it
383
441
  // covered (reverted: nothing came true).
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");
442
+ if (o && e.Nt && attrHooks !== null) {
443
+ const e = l.ue !== NOT_PENDING ? l.ue : l.le;
444
+ if (e !== E) attrHooks.optimisticReverted(l, E, e, l.ue !== NOT_PENDING ? "superseded" : "reverted");
387
445
  }
388
446
  // A derivation the revert re-derives (dirtied by the guess's
389
447
  // notification — listed before it), or one still in flight, shows the
390
448
  // lane's answer beside inputs that are the truth now: a fresh reader
391
449
  // of its flight observes it (#3648, #3651; #3305's commit beneath a
392
450
  // flight).
451
+ // Not a first load the lane produced nothing for whose own flight is
452
+ // up, nor one pending on such a load kept here (an identity memo of
453
+ // it, a derived store's derive): committing its empty `_value` would
454
+ // initialize it as `undefined`, which `latest()` would serve and
455
+ // `isPending()` call pending. It stays loading until its flight
456
+ // answers (D-22, A19 exc. 1; async-gen 3289 #929, 91501 #504, #156).
393
457
  u.Je = null;
394
- if (!i && !r) l.ie = f;
458
+ if (!i && !o) E === NOT_PENDING && (l.m & (STATUS_UNINITIALIZED | STATUS_PENDING)) === (STATUS_UNINITIALIZED | STATUS_PENDING) && (u.Ee || r.some(e => u.se?.has(e))) ? r.push(l) : l.ue = f;
395
459
  commitPendingNode(l);
396
460
  if (!i) {
397
461
  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.je || !l.je(E, l.le))) insertSubs(l);
462
+ if (a !== NOT_PENDING && (!l.je || !l.je(a, l.le))) insertSubs(l);
399
463
  }
400
- } else if (r) {
464
+ // A stale reader's own error — its pass (a memo's or a render
465
+ // effect's) threw over the committed value of what the parent held
466
+ // (A10), served as the screen — is no answer of the world landing now:
467
+ // it re-derives on it (A15; D-39, the landing is the new answer). The
468
+ // landing's `reruns` skips an errored reader: its error is an errored
469
+ // source's (#3945).
470
+ if (l.R & REACTIVE_FRAME_READ && l.m & STATUS_ERROR && u.U.source === l) enqueueSub(l);
471
+ } else if (o) {
401
472
  // A guess of a dissolving lane: its truth — a landing held beneath it,
402
473
  // else the value it covered — is the parent's to land, and its readers
403
474
  // re-derive from it there. What the screen showed stays the screen
404
475
  // until then (A18 (c)).
405
- if (l.ie === NOT_PENDING) l.ie = covered(l);
406
- if (e.It) l.le = o;
476
+ if (l.ue === NOT_PENDING) l.ue = covered(l);
477
+ if (e.Nt) l.le = E;
407
478
  holdNode(l, n);
408
479
  insertSubs(l);
409
480
  } else {
@@ -416,17 +487,17 @@ function supersede(e, n, s) {
416
487
  // it: an input's truth equal to its committed value is silent).
417
488
  u.Je = null;
418
489
  if (!i) {
419
- if (E !== NOT_PENDING && e.It) l.ie = E;
490
+ if (a !== NOT_PENDING && e.Nt) l.ue = a;
420
491
  if (l.m & STATUS_PENDING) {
421
492
  u.Ee = null;
422
493
  enqueueSub(l);
423
494
  }
424
495
  }
425
- if (e.It) {
496
+ if (e.Nt) {
426
497
  commitPendingNode(l);
427
498
  if (!i) l.l |= CONFIG_INPUTS_PUBLISHED;
428
499
  } else {
429
- if (u.ft !== null || u.Nt !== null) GlobalQueue.et(l, false, true);
500
+ if (u.ft !== null || u.Tt !== null) GlobalQueue.et(l, false, true);
430
501
  l.l &= -1048961;
431
502
  }
432
503
  }
@@ -442,7 +513,7 @@ function supersede(e, n, s) {
442
513
  // void: nothing of it reached the screen. So are the runs a blocked lane
443
514
  // parked: they show a re-guess the seam never revealed, beside derivations
444
515
  // that keep what the screen showed.
445
- if (n === null || e.It && !e.ln) releaseQueues(e); else e.un[0].length = e.un[1].length = 0;
516
+ if (n === null || e.Nt && !e.ln) releaseQueues(e); else e.rn[0].length = e.rn[1].length = 0;
446
517
  }
447
518
 
448
519
  /** Lanes. A lane's node is read by its reader's kind: an authoritative
@@ -475,12 +546,12 @@ function supersede(e, n, s) {
475
546
  const s = l(e, n);
476
547
  if (s !== NOT_PENDING) return s;
477
548
  }
478
- if (e !== null && e.l & CONFIG_AUTHORITATIVE) return GlobalQueue.rn(n);
479
- const u = n.m;
549
+ if (e !== null && e.l & CONFIG_AUTHORITATIVE) return GlobalQueue.un(n);
550
+ const r = n.m;
480
551
  // An errored member's lane value predates its error; the error is the answer (#4106).
481
- if (!s && u & STATUS_ERROR) return NOT_PENDING;
552
+ if (!s && r & STATUS_ERROR) return NOT_PENDING;
482
553
  if (e === null) return laneValueOf(n);
483
- const i = !s && u & STATUS_PENDING;
554
+ const u = !s && r & STATUS_PENDING;
484
555
  if (!tracking || e.l & CONFIG_CHILDREN_FORBIDDEN) {
485
556
  // An untracked read inside lane work derives from the lane like a
486
557
  // tracked one, minus the subscription — `untrack` is about
@@ -495,7 +566,7 @@ function supersede(e, n, s) {
495
566
  // parent. The landing lands the frame: its own pass (the flush joined
496
567
  // its transaction) reads the answer it waited on — the lane's staging,
497
568
  // revealed at this seam.
498
- const l = i && n.T.V === NOT_PENDING;
569
+ const l = u && n.T.V === NOT_PENDING;
499
570
  // D-28: a leaf a verdict read moved into the lane still throws on such
500
571
  // a flight — the pass leaves the lane, and its frame holds as if the
501
572
  // read had come first (S-39, #4275). Nor does a verdict lane's seat carry
@@ -504,17 +575,22 @@ function supersede(e, n, s) {
504
575
  if ((l || passLane !== t) && passLane?._parent.it === passLane) setPassLane(null);
505
576
  if (passLane === null || !sameLane(passLane, t)) {
506
577
  // (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);
578
+ if (l && t.Nt) return NOT_PENDING;
579
+ if (!s && n.ue !== NOT_PENDING && e.l & CONFIG_HELD && flushTransaction !== null && resolveTx(flushTransaction) === txOf(e)) return n.ue;
580
+ if (t.ln || u) {
511
581
  // Re-derived at the reveal; once per pass (the pass may also be a stale
512
582
  // reader of a transaction, REACTIVE_FRAME_READ — both reruns apply).
513
- if (!(e.R & REACTIVE_SCREEN_READ)) {
583
+ // Before the throw below: a leaf that waits on a lane that has never
584
+ // shown has no flight to settle it — only the reveal re-derives it,
585
+ // and its pending holds its frame until then (#4338).
586
+ if (!(e.R & REACTIVE_SCREEN_READ)) {
514
587
  e.R |= REACTIVE_FRAME_READ | REACTIVE_SCREEN_READ;
515
588
  t.En.push(e);
516
589
  }
517
- return display(n);
590
+ // Nothing on screen yet (uninitialized: `display` is NOT_PENDING).
591
+ const s = display(n);
592
+ if (s === NOT_PENDING) throw new NotReadyError(null);
593
+ return s;
518
594
  }
519
595
  // A verdict reader reads ahead of the seam's verdict: re-derived on the
520
596
  // screen if the lane holds (#4284).
@@ -522,12 +598,12 @@ function supersede(e, n, s) {
522
598
  }
523
599
  }
524
600
  enterLane(t, e);
525
- return i ? NOT_PENDING : laneValueOf(n);
601
+ return u ? NOT_PENDING : laneValueOf(n);
526
602
  }
527
603
 
528
604
  /** The value a guess covered: the node's committed value — a store slot's
529
605
  * asked of the store (a chained link follows the inner store's commits). */ function covered(e) {
530
- return e.l & CONFIG_SLOT_NODE && GlobalQueue.fn !== undefined ? GlobalQueue.fn(e) : e.le;
606
+ return e.l & CONFIG_SLOT_NODE && GlobalQueue.an !== undefined ? GlobalQueue.an(e) : e.le;
531
607
  }
532
608
 
533
609
  /** A guess in `l` whose own truth is in flight (its refetch — authoritative:
@@ -566,8 +642,8 @@ function supersede(e, n, s) {
566
642
  for (let e = 0; e < t.Ye.length && lanes.indexOf(t) !== -1; e++) {
567
643
  const n = t.Ye[e];
568
644
  if (n.l & CONFIG_GUESS && n.T.Je === t) {
569
- const e = n.ie !== NOT_PENDING ? n.ie : covered(n);
570
- reverting = n.ie === NOT_PENDING;
645
+ const e = n.ue !== NOT_PENDING ? n.ue : covered(n);
646
+ reverting = n.ue === NOT_PENDING;
571
647
  supersede(n, e, !n.je || !n.je(n.T.V, e));
572
648
  reverting = false;
573
649
  }
@@ -584,8 +660,8 @@ function laneSeam(e, n) {
584
660
  return;
585
661
  }
586
662
  e.ln = false;
587
- e.It = true;
588
- reruns(e);
663
+ e.Nt = true;
664
+ if (reruns(e)) revealing.push(resolveTx(e._parent));
589
665
  for (let n = 0; n < e.Ye.length; n++) {
590
666
  const s = e.Ye[n];
591
667
  if (s.R & LEFT_SHOWN) {
@@ -594,13 +670,13 @@ function laneSeam(e, n) {
594
670
  }
595
671
  const t = s.T;
596
672
  if (t.Je !== e) continue;
597
- if (!(s.l & CONFIG_GUESS) && !s.i && s.ie !== NOT_PENDING) {
598
- t.V = s.ie;
599
- s.ie = NOT_PENDING;
673
+ if (!(s.l & CONFIG_GUESS) && !s.i && s.ue !== NOT_PENDING) {
674
+ t.V = s.ue;
675
+ s.ue = NOT_PENDING;
600
676
  }
601
677
  // The frame this pass built is the screen's now; the one it replaced
602
678
  // goes (as `commitPendingNode` retires a committed pass's).
603
- if (t.ft !== null || t.Nt !== null) GlobalQueue.et(s, false, true);
679
+ if (t.ft !== null || t.Tt !== null) GlobalQueue.et(s, false, true);
604
680
  s.l &= ~CONFIG_STAGED;
605
681
  }
606
682
  releaseQueues(e);
@@ -608,6 +684,25 @@ function laneSeam(e, n) {
608
684
 
609
685
  /** The parent landed: its lanes end, nested first. (None is blocked: a
610
686
  * blocked lane blocks its parent.) */ function endLanes(e) {
687
+ // A revert landing (#4336): the guesses its lane took from another action
688
+ // that is still out come back in this frame, over the truth it lands —
689
+ // its readers re-derive with the landing (one frame, no bare truth
690
+ // between). Taken by a later lane since, a node stays that lane's.
691
+ if (guessed) for (let n = kept.length - 5; n >= 0; n -= 5) if (resolveTx(kept[n + 4]._parent) === e) {
692
+ let [s, t, l, r, u] = kept.splice(n, 5);
693
+ // (A store slot's node now: the one taken, or a read's since.)
694
+ if (reverted.has(u) && lanes.includes(l) && (!(s.l & CONFIG_SLOT_NODE) || (s = GlobalQueue.fn(s, true)))) {
695
+ const n = ext(s);
696
+ if (n.Je === null || txOf(s) === e) {
697
+ commitPendingNode(s);
698
+ n.V = t;
699
+ n.Ie = r;
700
+ s.l |= CONFIG_OVERRIDE | CONFIG_GUESS;
701
+ list(s, l);
702
+ rerun(s, e);
703
+ }
704
+ }
705
+ }
611
706
  for (let n = lanes.length - 1; n >= 0; n--) {
612
707
  const s = lanes[n];
613
708
  if (resolveTx(s._parent) !== e) continue;
@@ -624,7 +719,13 @@ function laneSeam(e, n) {
624
719
  return e.V && linkBlocked(e) || nestedBlocked(e);
625
720
  }
626
721
 
722
+ /** Parents whose lane revealed this seam with readers to re-derive there
723
+ * (`laneSeam`): the lane shows ahead of its parent (F-6), so the parent lands
724
+ * a round later — after those readers have shown the lane's answer — even
725
+ * when nothing else holds it (#4393). */ const revealing = [];
726
+
627
727
  /** The lanes under `t` (its own; a link group's are each other's). */ function nestedBlocked(e) {
728
+ if (revealing.indexOf(e) !== -1) return true;
628
729
  for (let n = 0; n < lanes.length; n++) {
629
730
  const s = lanes[n];
630
731
  if (resolveTx(s._parent) !== e) continue;
@@ -638,7 +739,9 @@ function laneSeam(e, n) {
638
739
  * landing), judged against the guess. On a derivation it is the lane's
639
740
  * latest: its staging once the lane has shown (promoted at the next
640
741
  * reveal), its slot before; the lane's members re-derive as its work. */ function laneWrite(e, n) {
641
- if (e.l & CONFIG_GUESS) {
742
+ // (A store slot's guess its derive's re-ask in another action's lane
743
+ // writes is that lane's derivation from here — store `_slotTake`, #4336.)
744
+ if (e.l & CONFIG_GUESS && !(guessed && GlobalQueue.fn?.(e))) {
642
745
  // Provenance (A18, #3331; Q-D, plan sec. 39): a write asking an OLDER
643
746
  // question than the guess's — another action's landing beneath it —
644
747
  // is not its answer: held beneath (the truth for the commit; the
@@ -649,8 +752,8 @@ function laneSeam(e, n) {
649
752
  // A store re-bases a keyed arrangement guess over an older truth, and
650
753
  // folds a current one beneath it (D-34): true.
651
754
  const s = stale(e, question);
652
- if (GlobalQueue.an?.(e, n, s) || s) {
653
- e.ie = n;
755
+ if (GlobalQueue.Nn?.(e, n, s) || s) {
756
+ e.ue = n;
654
757
  e.l |= CONFIG_HELD;
655
758
  schedule();
656
759
  return n;
@@ -659,8 +762,8 @@ function laneSeam(e, n) {
659
762
  } else {
660
763
  const s = txOf(e);
661
764
  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;
765
+ if (s.Nt) e.ue = n; else e.T.V = n;
766
+ if (e.X !== undefined) e.ie = clock;
664
767
  insertSubs(e);
665
768
  laneDirty(e, s);
666
769
  }
@@ -682,7 +785,7 @@ function laneSeam(e, n) {
682
785
  * A24). The guess's own question answering supersedes as usual. */ function laneOutcome(e, n, s) {
683
786
  if (s) return true;
684
787
  if (stale(e, answered(e))) {
685
- e.ie = n;
788
+ e.ue = n;
686
789
  e.l |= CONFIG_HELD;
687
790
  return true;
688
791
  }
@@ -693,7 +796,7 @@ function laneSeam(e, n) {
693
796
  /** THE provenance test, for a derivation's pass and a write alike: an
694
797
  * answer to question `q` landing on a guess is stale when `q` is older
695
798
  * than the guess's own; unstamped (0) and mainline are current. */ function stale(e, n) {
696
- return n !== 0 && n < e.T.de;
799
+ return n !== 0 && n < e.T.Ie;
697
800
  }
698
801
 
699
802
  /** The question a derivation's pass answered: the newest among the stamped
@@ -702,7 +805,7 @@ function laneSeam(e, n) {
702
805
  let n = 0;
703
806
  for (let s = e.C; s !== null; s = s.G) {
704
807
  const e = s.S;
705
- if (e.T !== null && e.T.de > n && e.ae === clock) n = e.T.de;
808
+ if (e.T !== null && e.T.Ie > n && e.ie === clock) n = e.T.Ie;
706
809
  }
707
810
  return n;
708
811
  }
@@ -717,33 +820,100 @@ const applyGuessesAt = e => {
717
820
  if (pendingGuesses.length !== 0) applyGuesses(e);
718
821
  };
719
822
 
823
+ /** `t` is parked by `r`'s pass alone: each node keeping it — `t`'s own, or
824
+ * a lane's under it — is `r` itself (its flight, in the verdict lane) or a
825
+ * render effect pending on `r` only: the frame observing the lane work that
826
+ * read its staging. Anything else (an open action, a guess's flight,
827
+ * another flight) holds it on its own. */ function heldBy(e, n) {
828
+ if (e.tt !== 0 || e.V && linkBlocked(e)) return false;
829
+ for (let s = -1, t = e; t; t = lanes[++s]) if (t === e || resolveTx(t._parent) === e) {
830
+ if (t !== e && guessFlights(t)) return false;
831
+ for (let e = 0; e < t.Ye.length; e++) {
832
+ const s = t.Ye[e];
833
+ if (s === n || !(s.m & STATUS_PENDING) || s.T.Je !== null && txOf(s) !== t) continue;
834
+ const l = s.T.se;
835
+ if (s.i !== EFFECT_RENDER || l?.size !== 1 || !l.has(n)) return false;
836
+ }
837
+ }
838
+ return true;
839
+ }
840
+
720
841
  const laneSeams = e => {
721
842
  // Passes that read a write the parked frame holds as the screen
722
843
  // (`stagedReaders`: lane work, a verdict reader before the verdict): they
723
844
  // re-derive on the committed world next round (and register as stale
724
845
  // readers of the transaction there); a lane's runs wait that round — a
725
846
  // lane sees the screen plus its own guesses.
847
+ if (revealing.length) revealing.length = 0;
726
848
  const n = [];
727
- if (e !== null) for (let s = 0; s < e.length; s++) {
728
- const t = e[s];
729
- const l = t.T?.Je;
849
+ if (e !== null) for (let s = 0; s < stagedReaders.length; s++) {
850
+ const t = stagedReaders[s],
851
+ // A pass no transaction holds that read the frame's staging (an
852
+ // effect, a `[s(), latest(s)]` reader watched before the frame had
853
+ // a transaction): judged by the frame's — it re-derives only if the
854
+ // frame stays parked; landing here, what it read is the screen (one
855
+ // run, D-22).
856
+ l = t.T?.Je || (t.R & REACTIVE_STAGED_READ ? e : null);
730
857
  // A verdict reader a later read of the same pass routed into a verdict
731
858
  // lane answered for itself: it re-derives at the holder's landing, not
732
859
  // now (one run — #3322, #3540). Other verdict-lane work re-derives only
733
860
  // if the frame stays parked: a transaction landing at this seam makes
734
- // what it read the screen (one run).
735
- if (t.R & REACTIVE_DISPOSED || l != null && l._parent?.it === l && (t.l & CONFIG_VERDICT ? !(t.R & REACTIVE_PROBE_UNANSWERED) : !blocked(l._parent))) continue;
861
+ // what it read the screen (one run). The frame is the transaction that
862
+ // holds what it read (`t`, this flush's — not always the lane's parent:
863
+ // a landing that reached a frame held elsewhere joined that one). Nor
864
+ // when only this pass keeps it parked (`heldBy`): the frame lands with
865
+ // the pass's answer, so what the pass read is the screen when anything
866
+ // shows it — re-derived on the committed world, it would ask a question
867
+ // already asked, over inputs the landing replaces. A reader a
868
+ // transaction holds that read none of its stagings (a verdict reader
869
+ // that probed only a flight, held as the frame's pending observer or
870
+ // its staged derivation) has nothing to re-derive: its re-run
871
+ // re-enters that transaction and parks it again, every round — it
872
+ // runs at the landing (#4349). One that read a staging re-derives only
873
+ // if its frame stays parked: landing at this seam, what it read is the
874
+ // screen (one run, D-22). Not one born into that frame (a held pass's
875
+ // child, ruling A): it has no screen, its reads are the transaction's
876
+ // either way — it runs again at the landing, not now.
877
+ if (t.R & REACTIVE_DISPOSED || (l?.V ? l._parent?.it === 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;
736
878
  // A leaked effect re-derives seated in its lane from its first read: it
737
879
  // carries no lane value (`laneStage`), so without this its pass starts
738
880
  // as mainline, joins the parked frame on its first held read and reads
739
881
  // the staging, then re-enters the lane at its guess read and leaks
740
882
  // again — every round (#4014). A computed is already seated by its
741
883
  // override, and a written guess's own pass stays the frame's.
742
- if (l != null && l.V && t.i) t.R |= REACTIVE_LANE_DIRTY;
884
+ if (l?.V && t.i) t.R |= REACTIVE_LANE_DIRTY;
885
+ // R5 for an async reader: its frame pass asked the frame's question
886
+ // (F-6's lane gets its own pass now). That flight is the frame's
887
+ // answer: the landing adopts it rather than asking it again (F-9: a
888
+ // re-ask of the same question is no new question; `reruns`). A frame
889
+ // landing at this seam has nothing to re-derive: the pass was the
890
+ // landing's, its flight the screen's answer. Sync or async, the
891
+ // reader's lane pass is still to come: the verdict lane it joins does
892
+ // not reveal this seam (F-6: the lane's readers show its answer
893
+ // together, D-23: atomic with the value) — revealed now, it showed a
894
+ // verdict beside the frame pass's value, before the pass that answers
895
+ // for the lane, and a reader of the re-derived memo read it outside
896
+ // the shown lane and waited for the frame (#4393).
897
+ else if (!l?.V && t.R & REACTIVE_PROBE_UNANSWERED) {
898
+ if (isThenable(t.T?.Ee)) {
899
+ if (!blocked(e)) continue;
900
+ t.T.rt = t.T.Ee;
901
+ } else if (blocked(e))
902
+ // A sync reader's lane pass re-derives the async memos over it (a
903
+ // memo over a verdict memo is a verdict reader, D-25 #4050; D-22:
904
+ // `latest()` extracted into a memo reads as inline). The flight
905
+ // each asked over its frame staging, up and held by `t`, is the
906
+ // frame's answer too: the landing adopts it (F-9).
907
+ 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.rt = s.T.Ee;
908
+ if (e?.it && n.indexOf(e.it) === -1) n.push(e.it);
909
+ }
743
910
  enqueueSub(t);
744
- if (l != null && l.V && n.indexOf(l) === -1) n.push(l);
911
+ if (l?.V && n.indexOf(l) === -1) n.push(l);
745
912
  }
746
- for (let e = Math.min(judged, lanes.length) - 1; e >= 0; e--) laneSeam(lanes[e], n.indexOf(lanes[e]) !== -1);
913
+ // A verdict lane born at this seam is judged with it: it exists only once
914
+ // a pass routed into it — the park seam's zombie verdict readers re-run
915
+ // as its work so that this seam reveals them (#3444).
916
+ for (let e = lanes.length - 1; e >= 0; e--) if (e < judged || lanes[e]._parent?.it === lanes[e]) laneSeam(lanes[e], n.indexOf(lanes[e]) !== -1);
747
917
  // A verdict reader that entered a lane this seam held: its run waits with
748
918
  // the lane, and the verdict beside it would freeze — it re-derives next
749
919
  // round from the frame's seat, a stale reader of the lane (#4284).
@@ -753,9 +923,39 @@ const laneSeams = e => {
753
923
  }
754
924
  };
755
925
 
926
+ /** The landing's re-derive of a reader whose frame flight the seam parked
927
+ * (R5): the flight asked the question the landing would — adopted, it is
928
+ * asked once (F-9). Not over a pass already queued: that one asks anew.
929
+ * Registered as if this pass had returned it — the node pending on it, its
930
+ * landing the answer — through a promise of its own: the frame pass's
931
+ * registration still listens to the flight, and must not land it a second
932
+ * time. */ const adoptParked = e => {
933
+ const n = e.T?.rt;
934
+ if (!n || e.R & (REACTIVE_DIRTY | REACTIVE_CHECK | REACTIVE_IN_HEAP)) return false;
935
+ e.T.rt = undefined;
936
+ releaseFlightTeardown(e);
937
+ try {
938
+ handleAsync(e, n.then());
939
+ } catch {}
940
+ e.T.fe = true;
941
+ notifyStatus(e, STATUS_PENDING, new NotReadyError(e));
942
+ return true;
943
+ };
944
+
945
+ /** A15: lane work or a verdict reader was served `el`'s committed value
946
+ * (core.ts `frameRead`), so its pass does not wait on `el`'s flight: what
947
+ * `el`'s pending propagated onto it before the pass (its origins, `el`
948
+ * included) is not this pass's — the landing re-derives it (`reruns`).
949
+ * Kept, a pass that went pending on another source never woke when that
950
+ * source settled (#4397). Only a reader something made pending has anything
951
+ * to drop. */ const laneServed = (e, n) => {
952
+ const s = n.T?.se;
953
+ if (s && e.T?.se) for (const n of s) removePendingSource(e, n);
954
+ };
955
+
756
956
  /** Render effects that read a verdict and entered a lane before its seam (#4284). */ const entered = [];
757
957
 
758
- installLaneHooks(laneRead, laneStage, laneOutcome, laneWrite, applyGuessesAt, laneSeams, () => lanes.length !== 0 && laneCorrections(), endLanes, lanesBlocked);
958
+ installLaneHooks(laneRead, laneStage, laneOutcome, laneWrite, applyGuessesAt, laneSeams, () => lanes.length !== 0 && laneCorrections(), endLanes, lanesBlocked, adoptParked, laneServed);
759
959
 
760
960
  GlobalQueue.xe = e => {
761
961
  const n = [];
@@ -769,4 +969,4 @@ GlobalQueue.xe = e => {
769
969
 
770
970
  GlobalQueue.J = verdictLane;
771
971
 
772
- export { display, enterLane, guessedValueOf, laneRead, laneStage, laneValueOf, optimisticWrite, pendingGuessOf, supersede, verdictLane };
972
+ export { display, enterLane, guessedValueOf, laneRead, laneStage, laneValueOf, optimisticWrite, pendingGuessOf, supersede, take, verdictLane };