@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
package/dist/dev.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  C as CONFIG_GUESS,
3
3
  N as NOT_PENDING,
4
+ S as STATUS_UNINITIALIZED,
4
5
  a as NotReadyError,
5
6
  b as CONFIG_AUTHORITATIVE,
6
7
  e as enqueueSub,
@@ -8,186 +9,198 @@ import {
8
9
  s as schedule,
9
10
  q as question,
10
11
  n as nextQuestion,
12
+ d as armGuessing,
11
13
  i as installLaneHooks,
12
14
  G as GlobalQueue,
13
15
  r as resolveTx,
14
- d as newTransaction,
15
- o as ownFlights,
16
- R as REACTIVE_DISPOSED,
17
- f as CONFIG_VERDICT,
18
- g as REACTIVE_PROBE_UNANSWERED,
19
- h as blocked,
20
- j as REACTIVE_LANE_DIRTY,
21
- k as CONFIG_HELD,
22
- t as txOf,
23
- l as clock,
24
- m as insertSubs,
25
- p as laneDirty,
26
- u as REACTIVE_LANE_READ,
16
+ f as newTransaction,
17
+ l as list,
18
+ g as staleReader,
19
+ h as removePendingSource,
20
+ R as REACTIVE_DIRTY,
21
+ j as REACTIVE_CHECK,
22
+ k as REACTIVE_IN_HEAP,
23
+ m as releaseFlightTeardown,
24
+ o as handleAsync,
25
+ p as notifyStatus,
26
+ t as STATUS_PENDING,
27
+ u as CONFIG_SLOT_NODE,
27
28
  v as ext,
28
- S as STATUS_UNINITIALIZED,
29
- w as REACTIVE_RECOMPUTING_DEPS,
30
- x as queuePendingNode,
31
- y as list,
32
- z as STATUS_ERROR,
33
- A as tracking,
34
- B as CONFIG_CHILDREN_FORBIDDEN,
35
- D as passLane,
36
- E as EFFECT_RENDER,
37
- F as setPassLane,
38
- H as sameLane,
39
- I as flushTransaction,
40
- J as REACTIVE_SCREEN_READ,
41
- K as REACTIVE_FRAME_READ,
42
- L as attrHooks,
43
- M as commitPendingNode,
44
- O as REACTIVE_IN_HEAP,
45
- P as REACTIVE_DIRTY,
46
- Q as REACTIVE_CHECK,
47
- T as STATUS_PENDING,
48
- U as CONFIG_INPUTS_PUBLISHED,
49
- V as releaseQueues,
50
- W as CONFIG_SLOT_NODE,
51
- X as holdNode,
52
- Y as joinFuture,
53
- Z as reruns,
54
- _ as CONFIG_STAGED,
55
- $ as merge$1,
56
- a0 as REACTIVE_JOINED,
57
- a1 as REACTIVE_STAGED_READ,
58
- a2 as stagedRead,
59
- a3 as setVerdict,
60
- a4 as context,
61
- a5 as installVerdictHooks,
62
- a6 as REACTIVE_PROBED,
63
- a7 as verdict,
64
- a8 as link,
65
- a9 as pullComputed,
66
- aa as strictRead,
67
- ab as strictReadActive,
68
- ac as warnStrictReadUntracked,
69
- ad as staleReader,
70
- ae as globalQueue,
71
- af as joinPass,
72
- ag as markLateLinker,
73
- ah as spectating,
74
- ai as unflushedValue,
75
- aj as CONFIG_STAGED_ERROR,
76
- ak as stagedReaders,
77
- al as bornInto,
78
- am as enterAction,
79
- an as setEffectCallback,
80
- ao as exitAction,
81
- ap as isThenable,
82
- aq as actionDepth,
83
- ar as flush,
84
- as as setQuestion,
85
- at as emitDiagnostic,
86
- au as enterCallback,
87
- av as exitCallback,
88
- aw as getOwner,
89
- ax as inEffectCallback,
90
- ay as effectCallbackEvent,
91
- az as setExternalHooks,
92
- aA as signal,
93
- aB as setSignal,
94
- aC as cleanup,
95
- aD as read,
96
- aE as DEV$1,
97
- aF as CONFIG_TRANSPARENT,
98
- aG as NoOwnerError,
99
- aH as ContextNotFoundError,
100
- aI as createEffectNode,
101
- aJ as recompute,
102
- aK as EFFECT_USER,
103
- aL as reportDiagnostic,
104
- aM as computed,
105
- aN as staleValues,
106
- aO as CONFIG_AUTO_DISPOSE,
107
- aP as EFFECT_TRACKED,
108
- aQ as heldRunHook,
109
- aR as unwrapStatusError,
110
- aS as setStrictRead,
111
- aT as queueEvent,
112
- aU as StatusError,
113
- aV as trimStaleDeps,
114
- aW as haltReactivityAt,
115
- aX as strictReadEvent,
116
- aY as setTrackedQueueCallback,
117
- aZ as setEffectStatusNotify,
118
- a_ as _hitUnhandledAsync,
119
- a$ as resetUnhandledAsync,
120
- b0 as ROOT_ERROR_HOOK,
121
- b1 as registerGraph,
122
- b2 as runWithOwner,
123
- b3 as setMemo,
124
- b4 as $REFRESH,
125
- b5 as markRefresh,
126
- b6 as createRoot,
127
- b7 as getObserver,
128
- b8 as dispose,
129
- b9 as TimeoutError,
130
- ba as untrack,
131
- bb as dirtyQueue,
132
- bc as CONFIG_REDERIVE,
133
- bd as passTx,
134
- be as CONFIG_FRESH_READ,
135
- bf as createOwner,
136
- bg as writeHook,
137
- bh as REACTIVE_WRITE_IN_OWNED_SCOPE_SIGNAL_MESSAGE,
138
- bi as ownedScopeWriteMessage,
139
- bj as isEqual,
140
- bk as CONFIG_MANUAL_WRITE,
141
- bl as snapshotCaptureActive,
142
- bm as captureWriteSnapshot,
143
- bn as notePromotedWrite,
144
- bo as slotSignal,
145
- bp as stagedScreen,
146
- bq as CONFIG_PROMOTED,
147
- br as setSlotUnobserved,
148
- bs as installStoreHooks,
149
- bt as joinPassTx,
150
- bu as asyncTailFlights,
151
- bv as untrackDepth,
152
- bw as checkPostAwaitRead,
153
- bx as disposalDepth,
154
- by as insertIntoHeap,
155
- bz as isDisposed,
156
- bA as handleAsync,
157
- bB as addPendingSource,
158
- bC as propagateStatus,
159
- bD as forEachDependent,
160
- bE as settlePendingSource,
161
- bF as joinPassAt,
162
- bG as notifyEpoch,
163
- bH as SUPPORTS_PROXY,
164
- bI as CONFIG_KEY_BAG,
165
- bJ as releaseMarksHook,
166
- bK as installMarkHooks,
167
- bL as CONFIG_LAGS,
168
- bM as REACTIVE_REASK,
169
- bN as onSupersede,
170
- bO as haltReactivity,
171
- bP as CONFIG_FRAME,
172
- bQ as heldUnder,
173
- bR as unhold,
174
- bS as laneStageHook,
175
- bT as REACTIVE_ZOMBIE,
176
- bU as installBoundaryHooks,
177
- bV as CAUGHT_REARMED,
178
- bW as OBSERVE$1
29
+ w as txOf,
30
+ x as commitPendingNode,
31
+ y as ownFlights,
32
+ z as stagedReaders,
33
+ A as REACTIVE_STAGED_READ,
34
+ B as REACTIVE_DISPOSED,
35
+ D as CONFIG_VERDICT,
36
+ E as REACTIVE_PROBE_UNANSWERED,
37
+ F as blocked,
38
+ H as bornInto,
39
+ I as REACTIVE_LANE_DIRTY,
40
+ J as isThenable,
41
+ K as CONFIG_HELD,
42
+ L as clock,
43
+ M as insertSubs,
44
+ O as laneDirty,
45
+ P as REACTIVE_LANE_READ,
46
+ Q as flushTransaction,
47
+ T as REACTIVE_RECOMPUTING_DEPS,
48
+ U as queuePendingNode,
49
+ V as STATUS_ERROR,
50
+ W as tracking,
51
+ X as CONFIG_CHILDREN_FORBIDDEN,
52
+ Y as passLane,
53
+ Z as EFFECT_RENDER,
54
+ _ as setPassLane,
55
+ $ as sameLane,
56
+ a0 as REACTIVE_SCREEN_READ,
57
+ a1 as REACTIVE_FRAME_READ,
58
+ a2 as attrHooks,
59
+ a3 as CONFIG_INPUTS_PUBLISHED,
60
+ a4 as releaseQueues,
61
+ a5 as holdNode,
62
+ a6 as reruns,
63
+ a7 as CONFIG_STAGED,
64
+ a8 as merge$1,
65
+ a9 as creatorReads,
66
+ aa as REACTIVE_JOINED,
67
+ ab as stagedRead,
68
+ ac as setVerdict,
69
+ ad as context,
70
+ ae as installVerdictHooks,
71
+ af as creatorPass,
72
+ ag as CONFIG_ASYNC,
73
+ ah as REACTIVE_PROBED,
74
+ ai as REACTIVE_WINDOW,
75
+ aj as verdict,
76
+ ak as prepareComputed,
77
+ al as link,
78
+ am as pullComputed,
79
+ an as strictRead,
80
+ ao as strictReadActive,
81
+ ap as warnStrictReadUntracked,
82
+ aq as globalQueue,
83
+ ar as joinFuture,
84
+ as as joinPass,
85
+ at as markLateLinker,
86
+ au as spectating,
87
+ av as unflushedValue,
88
+ aw as CONFIG_STAGED_ERROR,
89
+ ax as REACTIVE_REASK,
90
+ ay as enterAction,
91
+ az as setEffectCallback,
92
+ aA as exitAction,
93
+ aB as actionDepth,
94
+ aC as flush,
95
+ aD as setQuestion,
96
+ aE as emitDiagnostic,
97
+ aF as enterCallback,
98
+ aG as exitCallback,
99
+ aH as getOwner,
100
+ aI as inEffectCallback,
101
+ aJ as effectCallbackEvent,
102
+ aK as setExternalHooks,
103
+ aL as signal,
104
+ aM as setSignal,
105
+ aN as cleanup,
106
+ aO as read,
107
+ aP as DEV$1,
108
+ aQ as CONFIG_TRANSPARENT,
109
+ aR as NoOwnerError,
110
+ aS as ContextNotFoundError,
111
+ aT as createEffectNode,
112
+ aU as recompute,
113
+ aV as EFFECT_USER,
114
+ aW as reportDiagnostic,
115
+ aX as computed,
116
+ aY as staleValues,
117
+ aZ as CONFIG_AUTO_DISPOSE,
118
+ a_ as EFFECT_TRACKED,
119
+ a$ as heldRunHook,
120
+ b0 as unwrapStatusError,
121
+ b1 as setStrictRead,
122
+ b2 as queueEvent,
123
+ b3 as StatusError,
124
+ b4 as trimStaleDeps,
125
+ b5 as haltReactivityAt,
126
+ b6 as strictReadEvent,
127
+ b7 as setTrackedQueueCallback,
128
+ b8 as setEffectStatusNotify,
129
+ b9 as _hitUnhandledAsync,
130
+ ba as resetUnhandledAsync,
131
+ bb as ROOT_ERROR_HOOK,
132
+ bc as registerGraph,
133
+ bd as runWithOwner,
134
+ be as setMemo,
135
+ bf as $REFRESH,
136
+ bg as markRefresh,
137
+ bh as createRoot,
138
+ bi as getObserver,
139
+ bj as dispose,
140
+ bk as TimeoutError,
141
+ bl as untrack,
142
+ bm as dirtyQueue,
143
+ bn as CONFIG_REDERIVE,
144
+ bo as passTx,
145
+ bp as CONFIG_FRESH_READ,
146
+ bq as createOwner,
147
+ br as writeHook,
148
+ bs as REACTIVE_WRITE_IN_OWNED_SCOPE_SIGNAL_MESSAGE,
149
+ bt as ownedScopeWriteMessage,
150
+ bu as isEqual,
151
+ bv as CONFIG_MANUAL_WRITE,
152
+ bw as checkForeignRead,
153
+ bx as snapshotCaptureActive,
154
+ by as captureWriteSnapshot,
155
+ bz as notePromotedWrite,
156
+ bA as slotSignal,
157
+ bB as stagedScreen,
158
+ bC as devRead,
159
+ bD as CONFIG_PROMOTED,
160
+ bE as setSlotUnobserved,
161
+ bF as installStoreHooks,
162
+ bG as joinPassTx,
163
+ bH as asyncTailFlights,
164
+ bI as untrackDepth,
165
+ bJ as checkPostAwaitRead,
166
+ bK as disposalDepth,
167
+ bL as insertIntoHeap,
168
+ bM as isDisposed,
169
+ bN as addPendingSource,
170
+ bO as propagateStatus,
171
+ bP as forEachDependent,
172
+ bQ as settlePendingSource,
173
+ bR as joinPassAt,
174
+ bS as notifyEpoch,
175
+ bT as SUPPORTS_PROXY,
176
+ bU as CONFIG_KEY_BAG,
177
+ bV as releaseMarksHook,
178
+ bW as installMarkHooks,
179
+ bX as CONFIG_LAGS,
180
+ bY as onSupersede,
181
+ bZ as haltReactivity,
182
+ b_ as CONFIG_FRAME,
183
+ b$ as heldUnder,
184
+ c0 as unhold,
185
+ c1 as laneStageHook,
186
+ c2 as REACTIVE_ZOMBIE,
187
+ c3 as installBoundaryHooks,
188
+ c4 as CAUGHT_REARMED,
189
+ c5 as OBSERVE$1
179
190
  } from "./dev-shared.js";
180
191
  export {
181
- bX as clearSnapshots,
182
- bY as enforceLoadingBoundary,
183
- bZ as getNextChildId,
184
- b_ as markSnapshotScope,
185
- b$ as peekNextChildId,
186
- c0 as releaseSnapshotScope,
187
- c1 as resetErrorHalt,
188
- c2 as setConsoleFooter,
189
- c3 as setSnapshotCapture,
190
- c4 as setWriteHook
192
+ c6 as checkForeignOwnerless,
193
+ c7 as clearSnapshots,
194
+ c8 as enforceLoadingBoundary,
195
+ c9 as getNextChildId,
196
+ ca as markSnapshotScope,
197
+ cb as peekNextChildId,
198
+ cc as releaseSnapshotScope,
199
+ cd as resetErrorHalt,
200
+ ce as runAsSolidCopy,
201
+ cf as setConsoleFooter,
202
+ cg as setSnapshotCapture,
203
+ ch as setWriteHook
191
204
  } from "./dev-shared.js";
192
205
 
193
206
  // Truth reads (`until`, `refresh`; CONFIG_AUTHORITATIVE) over lane nodes.
@@ -196,11 +209,13 @@ export {
196
209
  // which exists once something installs lanes (`createOptimistic`, verdicts).
197
210
  /** A lane node's current value: its guess, or the latest it derived — the
198
211
  * staging a shown lane holds for its next reveal, else its slot; the
199
- * committed value while it has none (its first lane pass in flight). */
212
+ * committed value while it has none (its first lane pass in flight) —
213
+ * NOT_PENDING for a node with no committed value either (born in the lane,
214
+ * uninitialized: its reads throw NotReady, A7; #4363). */
200
215
  function laneValueOf(el) {
201
216
  if (!(el._config & CONFIG_GUESS) && el._pendingValue !== NOT_PENDING) return el._pendingValue;
202
217
  const v = el._x._lane;
203
- return v !== NOT_PENDING ? v : el._value;
218
+ return v !== NOT_PENDING || el._statusFlags & STATUS_UNINITIALIZED ? v : el._value;
204
219
  }
205
220
  /** Truth readers (`until`, `refresh`; CONFIG_AUTHORITATIVE) — pay-for-use:
206
221
  * the first one installs these on GlobalQueue (signals.ts `watch`).
@@ -272,10 +287,15 @@ function newLane(parent) {
272
287
  return l;
273
288
  }
274
289
  /** What the screen shows for a lane's node: the lane's value once the lane
275
- * has revealed, the committed truth before. */
290
+ * has revealed, the committed truth before — NOT_PENDING when there is none
291
+ * (uninitialized, as `laneValueOf`): its read throws NotReady. */
276
292
  function display(el) {
277
293
  const v = el._x._lane;
278
- return el._x._transaction._shown && v !== NOT_PENDING ? v : el._value;
294
+ return el._x._transaction._shown && v !== NOT_PENDING
295
+ ? v
296
+ : el._statusFlags & STATUS_UNINITIALIZED
297
+ ? NOT_PENDING
298
+ : el._value;
279
299
  }
280
300
  /** Guesses written since the last seam (`[node, value, question, …]`).
281
301
  * Applied at the seam, once the frame's verdict is known — the parent is
@@ -288,9 +308,10 @@ const pendingGuesses = [];
288
308
  * lane's value or the committed one — what an optimistic updater composes
289
309
  * on (a store setter's draft too, store/optimistic.ts). */
290
310
  function guessedValueOf(n) {
291
- const g = pendingGuessOf(n);
292
- if (g !== NOT_PENDING) return g;
293
- return n._config & CONFIG_OVERRIDE ? laneValueOf(n) : n._value;
311
+ let g = pendingGuessOf(n);
312
+ return g !== NOT_PENDING || (n._config & CONFIG_OVERRIDE && (g = laneValueOf(n)) !== NOT_PENDING)
313
+ ? g
314
+ : n._value;
294
315
  }
295
316
  /** The unflushed guess to `n`, else NOT_PENDING. */
296
317
  function pendingGuessOf(n) {
@@ -298,6 +319,33 @@ function pendingGuessOf(n) {
298
319
  if (pendingGuesses[i] === n) return pendingGuesses[i + 1];
299
320
  return NOT_PENDING;
300
321
  }
322
+ /** This module's copy of scheduler.ts `guessing`: its readers here stay
323
+ * foldable when a bundler splits lanes.ts from scheduler.ts into separate
324
+ * chunks (a cross-chunk binding is not folded; the compiled live page). */
325
+ let guessed = false;
326
+ /** Guesses another action's lane took over (#4336): the node, its guess,
327
+ * the guess's lane, its question, the lane that took it. */
328
+ const kept = [];
329
+ /** Lanes a revert dissolved: the guesses they took come back at their
330
+ * parent's landing (`endLanes`). */
331
+ const reverted = /* @__PURE__ */ new WeakSet();
332
+ /** Lane `l` takes `n`'s guess (another action's), kept to come back if `l`
333
+ * reverts. Its readers saw the guess: they re-derive at the lane's reveal
334
+ * (and, a flight's, hold the lane while they would show it). `d`: a guess
335
+ * `l` takes without writing it (a store target's other slots) — the node
336
+ * shows its truth in `l`'s world until the guess comes back. */
337
+ function take(n, l, d) {
338
+ const x = n._x;
339
+ kept.push(n, x._lane, x._transaction, x._q, l);
340
+ rerun(n, l);
341
+ n._config &= d ? -10241 : ~CONFIG_GUESS;
342
+ if (d) ((x._transaction = null), (x._lane = NOT_PENDING));
343
+ else list(n, l);
344
+ }
345
+ /** `n`'s readers are stale readers of `t` (re-derived at its landing). */
346
+ function rerun(n, t) {
347
+ for (let s = n._subs; s !== null; s = s._nextSub) staleReader(s._sub, t);
348
+ }
301
349
  function optimisticWrite(n, v) {
302
350
  // What the user sees: an unflushed guess to the same node, else the lane's
303
351
  // value or the committed one. The updater composes on it; guessing it
@@ -308,6 +356,8 @@ function optimisticWrite(n, v) {
308
356
  if (typeof v === "function") v = v(prev);
309
357
  if (n._equals && n._equals(prev, v) && !(n._config & CONFIG_GUESS)) return v;
310
358
  pendingGuesses.push(n, v, question || nextQuestion());
359
+ armGuessing();
360
+ guessed = true;
311
361
  schedule();
312
362
  return v;
313
363
  }
@@ -325,8 +375,8 @@ function optimisticWrite(n, v) {
325
375
  * installs the resolver). A17: the guess stands in for the flight. */
326
376
  function inFlight$1(n) {
327
377
  return (
328
- (n._statusFlags & STATUS_PENDING) !== 0 ||
329
- ((n._config & CONFIG_SLOT_NODE) !== 0 && GlobalQueue._slotFlight?.(n) === true)
378
+ n._statusFlags & STATUS_PENDING ||
379
+ (n._config & CONFIG_SLOT_NODE && GlobalQueue._slotFlight?.(n))
330
380
  );
331
381
  }
332
382
  function applyGuesses(parent) {
@@ -393,24 +443,33 @@ function applyGuesses(parent) {
393
443
  * through an ancestor's guess stays its own (the screen plus its own
394
444
  * guesses). */
395
445
  function enterLane(l, c) {
396
- c._flags |= REACTIVE_LANE_READ;
397
- const p = passLane;
446
+ const f = (c._flags |= REACTIVE_LANE_READ),
447
+ p = passLane;
398
448
  if (p === null) {
449
+ const verdict = l._parent._verdict === l;
450
+ // A verdict lane's value (a memo of `latest` / `isPending`, F-6) read by
451
+ // a derivation that already read the frame's staging or joined its
452
+ // hold, or by any pass a pass that did is creating: the verdict read
453
+ // does not break it out — it stays the frame's, as the same read inline
454
+ // does (verdict.ts `markVerdictReader`, `route`; D-25, D-26). An
455
+ // effect's own reads do not count (its `_type`, 1–3, carries neither bit).
456
+ if (!(verdict && creatorReads(c, c._type || f))) {
457
+ setPassLane(l);
458
+ // The pass read a held write before its first lane read (it joined the
459
+ // frame's future as mainline would): lane work sees the screen — the
460
+ // seam re-derives it on the committed world next round (`stagedReaders`,
461
+ // the park's repair), as it does a lane pass that read a staging. Not a
462
+ // pass whose held reads were all of nodes born held (no staged read,
463
+ // `stagedRead`): it has no committed world to re-derive on (#3969).
464
+ // (A pass not joined: a held write beside a guess, #4336; a leaf of a
465
+ // verdict lane — its plain reads are the screen's, D-26 `[s(), L()]`.)
466
+ if (f & REACTIVE_STAGED_READ && (guessed || verdict || f & REACTIVE_JOINED)) stagedRead(c);
467
+ }
468
+ } else if (sameLane(p, l)) setPassLane(l);
469
+ else if (!under(p, l)) {
470
+ if (!under(l, p)) linkLanes(p, l);
399
471
  setPassLane(l);
400
- // The pass read a held write before its first lane read (it joined the
401
- // frame's future as mainline would): lane work sees the screen — the
402
- // seam re-derives it on the committed world next round (`stagedReaders`,
403
- // the park's repair), as it does a lane pass that read a staging. Not a
404
- // pass whose held reads were all of nodes born held (no staged read,
405
- // `stagedRead`): it has no committed world to re-derive on (#3969).
406
- if (c._flags & REACTIVE_JOINED && c._flags & REACTIVE_STAGED_READ) stagedRead(c);
407
- return;
408
472
  }
409
- if (sameLane(p, l)) return setPassLane(l);
410
- if (under(p, l)) return;
411
- if (under(l, p)) return setPassLane(l);
412
- linkLanes(p, l);
413
- setPassLane(l);
414
473
  }
415
474
  /** Is `l` nested under `a` (an ancestor lane)? */
416
475
  function under(l, a) {
@@ -481,10 +540,21 @@ function laneStage(el, l, create, errored) {
481
540
  const x = el._x;
482
541
  if (x !== null) {
483
542
  const tx = x._transaction;
484
- // Cleared before equality, so a return to the committed value notifies
485
- // nobody. The seam wakes it unless the frame is still held (#3892).
486
- if (tx?._shown && !blocked(resolveTx(tx._parent))) el._flags |= LEFT_SHOWN;
487
- if (tx?._lane) x._transaction = null;
543
+ if (tx?._lane) {
544
+ // Cleared before equality, so a return to the committed value
545
+ // notifies nobody. The seam wakes it (#3892) — always for a pass
546
+ // outside any transaction's flush: its value is the screen's now
547
+ // (#4367). While a transaction's flush runs the pass and the lane's
548
+ // frame is still held, what the screen shows stays the screen until
549
+ // the landing, and `_value` takes it (as `dissolveLane` keeps a shown
550
+ // lane's), so this pass's answer — the frame's — is a change against
551
+ // what is seen and reaches the screen with the frame's commit. (Only
552
+ // a lane is ever `_shown`.)
553
+ if (tx._shown)
554
+ if (!flushTransaction || !blocked(resolveTx(tx._parent))) el._flags |= LEFT_SHOWN;
555
+ else el._value = display(el);
556
+ x._transaction = null;
557
+ }
488
558
  x._lane = NOT_PENDING;
489
559
  }
490
560
  el._config &= -1050881;
@@ -502,7 +572,19 @@ function laneStage(el, l, create, errored) {
502
572
  // of the lane from here (its value the lane's, its fate the lane's), no
503
573
  // longer a written guess. A pass with no answer (pending, errored)
504
574
  // leaves the guess.
505
- if (!errored) el._config &= ~CONFIG_GUESS;
575
+ // Nor a pass of another action's guess: the re-ask is this lane's new
576
+ // question, which the guess (over the old one) does not answer — the
577
+ // lane waits for its own answer (F-1, F-6, #4336). (A guess of the
578
+ // lane's own action, D-33's entangled slot, still stands in.)
579
+ // (Read behind `guessed`: a bundle without optimism drops the reads.)
580
+ if (
581
+ guessed &&
582
+ el._config & CONFIG_GUESS &&
583
+ el._x._transaction?._lane &&
584
+ resolveTx(el._x._transaction._parent) !== resolveTx(l._parent)
585
+ )
586
+ take(el, l);
587
+ else if (!errored) el._config &= ~CONFIG_GUESS;
506
588
  }
507
589
  // A leaf has no lane value to seat it (`recompute`): one left waiting on
508
590
  // the lane's flight runs its next pass as the lane's — the landing that
@@ -535,9 +617,14 @@ function laneStage(el, l, create, errored) {
535
617
  * at the parent's landing — while the display keeps what it shows until
536
618
  * then. A confirmation is silent (A17): the truth stays staged beneath the
537
619
  * guess for the parent's commit, the guess stays the value; only a reader
538
- * of the truth (CONFIG_AUTHORITATIVE) is told its view changed. */
539
- /** Observe: the correction round is replacing a guess with the value it
540
- * covered (no landing beneath it) — `optimisticReverted`'s "reverted". */
620
+ * of the truth (CONFIG_AUTHORITATIVE) is told its view changed. Either way
621
+ * the parent takes this node, not the tick that carried the truth (D-33):
622
+ * one landing answering two actions' guesses (a store re-ask over two
623
+ * rows) must not tie them together; a pass that derives from the held
624
+ * truth joins the parent on its own (`joinPass`). */
625
+ /** The correction round is replacing a guess with the value it covered (no
626
+ * landing beneath it): observe's "reverted"; the lane gives back the
627
+ * guesses it took (#4336, `endLanes`). */
541
628
  let reverting = false;
542
629
  function supersede(n, value, changed) {
543
630
  const l = txOf(n);
@@ -557,6 +644,7 @@ function supersede(n, value, changed) {
557
644
  if (l._shown && attrHooks !== null)
558
645
  attrHooks.optimisticReverted(n, n._x._lane, value, reverting ? "reverted" : "superseded");
559
646
  dissolveLane(l, parent, n);
647
+ if (guessed && reverting) reverted.add(l);
560
648
  // What the screen showed stays the screen until the parent lands (A18
561
649
  // (c): display and untracked reads keep the override until the commit).
562
650
  if (l._shown) n._value = n._x._lane;
@@ -570,7 +658,6 @@ function supersede(n, value, changed) {
570
658
  GlobalQueue._confirmed?.(l);
571
659
  }
572
660
  n._pendingValue = value;
573
- joinFuture(parent);
574
661
  }
575
662
  /** A lane's end, two ways. `into === null`: the parent landed — the lane's
576
663
  * values are the truth now: a guess lands the truth staged beneath it or
@@ -593,6 +680,8 @@ function dissolveLane(l, into, except) {
593
680
  l._verdict = null;
594
681
  if (lanes.indexOf(v) !== -1) dissolveLane(v, null);
595
682
  }
683
+ // First loads this dissolution leaves loading (below).
684
+ const loading = [];
596
685
  for (let i = 0; i < l._nodes.length; i++) {
597
686
  const n = l._nodes[i];
598
687
  const x = n._x;
@@ -628,8 +717,20 @@ function dissolveLane(l, into, except) {
628
717
  // lane's answer beside inputs that are the truth now: a fresh reader
629
718
  // of its flight observes it (#3648, #3651; #3305's commit beneath a
630
719
  // flight).
720
+ // Not a first load the lane produced nothing for whose own flight is
721
+ // up, nor one pending on such a load kept here (an identity memo of
722
+ // it, a derived store's derive): committing its empty `_value` would
723
+ // initialize it as `undefined`, which `latest()` would serve and
724
+ // `isPending()` call pending. It stays loading until its flight
725
+ // answers (D-22, A19 exc. 1; async-gen 3289 #929, 91501 #504, #156).
631
726
  x._transaction = null;
632
- if (!effect && !guess) n._pendingValue = latest;
727
+ if (!effect && !guess)
728
+ slot === NOT_PENDING &&
729
+ (n._statusFlags & (STATUS_UNINITIALIZED | STATUS_PENDING)) ===
730
+ (STATUS_UNINITIALIZED | STATUS_PENDING) &&
731
+ (x._inFlight || loading.some(q => x._pendingSources?.has(q)))
732
+ ? loading.push(n)
733
+ : (n._pendingValue = latest);
633
734
  commitPendingNode(n);
634
735
  if (!effect) {
635
736
  if (
@@ -639,6 +740,14 @@ function dissolveLane(l, into, except) {
639
740
  n._config |= CONFIG_INPUTS_PUBLISHED;
640
741
  if (shown !== NOT_PENDING && (!n._equals || !n._equals(shown, n._value))) insertSubs(n);
641
742
  }
743
+ // A stale reader's own error — its pass (a memo's or a render
744
+ // effect's) threw over the committed value of what the parent held
745
+ // (A10), served as the screen — is no answer of the world landing now:
746
+ // it re-derives on it (A15; D-39, the landing is the new answer). The
747
+ // landing's `reruns` skips an errored reader: its error is an errored
748
+ // source's (#3945).
749
+ if (n._flags & REACTIVE_FRAME_READ && n._statusFlags & STATUS_ERROR && x._error.source === n)
750
+ enqueueSub(n);
642
751
  } else if (guess) {
643
752
  // A guess of a dissolving lane: its truth — a landing held beneath it,
644
753
  // else the value it covered — is the parent's to land, and its readers
@@ -758,14 +867,19 @@ function laneRead(c, el) {
758
867
  )
759
868
  return el._pendingValue;
760
869
  if (l._held || pending) {
761
- if (!guess && !l._shown && status & STATUS_UNINITIALIZED) throw new NotReadyError(null);
762
870
  // Re-derived at the reveal; once per pass (the pass may also be a stale
763
871
  // reader of a transaction, REACTIVE_FRAME_READ — both reruns apply).
872
+ // Before the throw below: a leaf that waits on a lane that has never
873
+ // shown has no flight to settle it — only the reveal re-derives it,
874
+ // and its pending holds its frame until then (#4338).
764
875
  if (!(c._flags & REACTIVE_SCREEN_READ)) {
765
876
  c._flags |= REACTIVE_FRAME_READ | REACTIVE_SCREEN_READ;
766
877
  l._reruns.push(c);
767
878
  }
768
- return display(el);
879
+ // Nothing on screen yet (uninitialized: `display` is NOT_PENDING).
880
+ const v = display(el);
881
+ if (v === NOT_PENDING) throw new NotReadyError(null);
882
+ return v;
769
883
  }
770
884
  // A verdict reader reads ahead of the seam's verdict: re-derived on the
771
885
  // screen if the lane holds (#4284).
@@ -837,7 +951,7 @@ function laneSeam(l, leaked) {
837
951
  }
838
952
  l._held = false;
839
953
  l._shown = true;
840
- reruns(l);
954
+ if (reruns(l)) revealing.push(resolveTx(l._parent));
841
955
  for (let i = 0; i < l._nodes.length; i++) {
842
956
  const n = l._nodes[i];
843
957
  if (n._flags & LEFT_SHOWN) {
@@ -861,6 +975,31 @@ function laneSeam(l, leaked) {
861
975
  /** The parent landed: its lanes end, nested first. (None is blocked: a
862
976
  * blocked lane blocks its parent.) */
863
977
  function endLanes(u) {
978
+ // A revert landing (#4336): the guesses its lane took from another action
979
+ // that is still out come back in this frame, over the truth it lands —
980
+ // its readers re-derive with the landing (one frame, no bare truth
981
+ // between). Taken by a later lane since, a node stays that lane's.
982
+ if (guessed)
983
+ for (let i = kept.length - 5; i >= 0; i -= 5)
984
+ if (resolveTx(kept[i + 4]._parent) === u) {
985
+ let [n, v, g, q, t] = kept.splice(i, 5);
986
+ // (A store slot's node now: the one taken, or a read's since.)
987
+ if (
988
+ reverted.has(t) &&
989
+ lanes.includes(g) &&
990
+ (!(n._config & CONFIG_SLOT_NODE) || (n = GlobalQueue._slotTake(n, true)))
991
+ ) {
992
+ const x = ext(n);
993
+ if (x._transaction === null || txOf(n) === u) {
994
+ commitPendingNode(n);
995
+ x._lane = v;
996
+ x._q = q;
997
+ n._config |= CONFIG_OVERRIDE | CONFIG_GUESS;
998
+ list(n, g);
999
+ rerun(n, u);
1000
+ }
1001
+ }
1002
+ }
864
1003
  for (let k = lanes.length - 1; k >= 0; k--) {
865
1004
  const l = lanes[k];
866
1005
  if (resolveTx(l._parent) !== u) continue;
@@ -876,8 +1015,14 @@ function endLanes(u) {
876
1015
  function lanesBlocked(t) {
877
1016
  return (t._lane && linkBlocked(t)) || nestedBlocked(t);
878
1017
  }
1018
+ /** Parents whose lane revealed this seam with readers to re-derive there
1019
+ * (`laneSeam`): the lane shows ahead of its parent (F-6), so the parent lands
1020
+ * a round later — after those readers have shown the lane's answer — even
1021
+ * when nothing else holds it (#4393). */
1022
+ const revealing = [];
879
1023
  /** The lanes under `t` (its own; a link group's are each other's). */
880
1024
  function nestedBlocked(t) {
1025
+ if (revealing.indexOf(t) !== -1) return true;
881
1026
  for (let i = 0; i < lanes.length; i++) {
882
1027
  const l = lanes[i];
883
1028
  if (resolveTx(l._parent) !== t) continue;
@@ -891,7 +1036,9 @@ function nestedBlocked(t) {
891
1036
  * latest: its staging once the lane has shown (promoted at the next
892
1037
  * reveal), its slot before; the lane's members re-derive as its work. */
893
1038
  function laneWrite(el, v) {
894
- if (el._config & CONFIG_GUESS) {
1039
+ // (A store slot's guess its derive's re-ask in another action's lane
1040
+ // writes is that lane's derivation from here — store `_slotTake`, #4336.)
1041
+ if (el._config & CONFIG_GUESS && !(guessed && GlobalQueue._slotTake?.(el))) {
895
1042
  // Provenance (A18, #3331; Q-D, plan sec. 39): a write asking an OLDER
896
1043
  // question than the guess's — another action's landing beneath it —
897
1044
  // is not its answer: held beneath (the truth for the commit; the
@@ -969,29 +1116,79 @@ const applyGuessesAt = parent => {
969
1116
  judged = lanes.length;
970
1117
  if (pendingGuesses.length !== 0) applyGuesses(parent);
971
1118
  };
972
- const laneSeams = leaks => {
1119
+ /** `t` is parked by `r`'s pass alone: each node keeping it — `t`'s own, or
1120
+ * a lane's under it — is `r` itself (its flight, in the verdict lane) or a
1121
+ * render effect pending on `r` only: the frame observing the lane work that
1122
+ * read its staging. Anything else (an open action, a guess's flight,
1123
+ * another flight) holds it on its own. */
1124
+ function heldBy(t, r) {
1125
+ if (t._open !== 0 || (t._lane && linkBlocked(t))) return false;
1126
+ for (let k = -1, u = t; u; u = lanes[++k])
1127
+ if (u === t || resolveTx(u._parent) === t) {
1128
+ if (u !== t && guessFlights(u)) return false;
1129
+ for (let i = 0; i < u._nodes.length; i++) {
1130
+ const n = u._nodes[i];
1131
+ if (
1132
+ n === r ||
1133
+ !(n._statusFlags & STATUS_PENDING) ||
1134
+ (n._x._transaction !== null && txOf(n) !== u)
1135
+ )
1136
+ continue;
1137
+ const s = n._x._pendingSources;
1138
+ if (n._type !== EFFECT_RENDER || s?.size !== 1 || !s.has(r)) return false;
1139
+ }
1140
+ }
1141
+ return true;
1142
+ }
1143
+ const laneSeams = t => {
973
1144
  // Passes that read a write the parked frame holds as the screen
974
1145
  // (`stagedReaders`: lane work, a verdict reader before the verdict): they
975
1146
  // re-derive on the committed world next round (and register as stale
976
1147
  // readers of the transaction there); a lane's runs wait that round — a
977
1148
  // lane sees the screen plus its own guesses.
1149
+ if (revealing.length) revealing.length = 0;
978
1150
  const leaked = [];
979
- if (leaks !== null)
980
- for (let i = 0; i < leaks.length; i++) {
981
- const r = leaks[i];
982
- const l = r._x?._transaction;
1151
+ if (t !== null)
1152
+ for (let i = 0; i < stagedReaders.length; i++) {
1153
+ const r = stagedReaders[i],
1154
+ // A pass no transaction holds that read the frame's staging (an
1155
+ // effect, a `[s(), latest(s)]` reader watched before the frame had
1156
+ // a transaction): judged by the frame's — it re-derives only if the
1157
+ // frame stays parked; landing here, what it read is the screen (one
1158
+ // run, D-22).
1159
+ l = r._x?._transaction || (r._flags & REACTIVE_STAGED_READ ? t : null);
983
1160
  // A verdict reader a later read of the same pass routed into a verdict
984
1161
  // lane answered for itself: it re-derives at the holder's landing, not
985
1162
  // now (one run — #3322, #3540). Other verdict-lane work re-derives only
986
1163
  // if the frame stays parked: a transaction landing at this seam makes
987
- // what it read the screen (one run).
1164
+ // what it read the screen (one run). The frame is the transaction that
1165
+ // holds what it read (`t`, this flush's — not always the lane's parent:
1166
+ // a landing that reached a frame held elsewhere joined that one). Nor
1167
+ // when only this pass keeps it parked (`heldBy`): the frame lands with
1168
+ // the pass's answer, so what the pass read is the screen when anything
1169
+ // shows it — re-derived on the committed world, it would ask a question
1170
+ // already asked, over inputs the landing replaces. A reader a
1171
+ // transaction holds that read none of its stagings (a verdict reader
1172
+ // that probed only a flight, held as the frame's pending observer or
1173
+ // its staged derivation) has nothing to re-derive: its re-run
1174
+ // re-enters that transaction and parks it again, every round — it
1175
+ // runs at the landing (#4349). One that read a staging re-derives only
1176
+ // if its frame stays parked: landing at this seam, what it read is the
1177
+ // screen (one run, D-22). Not one born into that frame (a held pass's
1178
+ // child, ruling A): it has no screen, its reads are the transaction's
1179
+ // either way — it runs again at the landing, not now.
988
1180
  if (
989
1181
  r._flags & REACTIVE_DISPOSED ||
990
- (l != null &&
991
- l._parent?._verdict === l &&
992
- (r._config & CONFIG_VERDICT
993
- ? !(r._flags & REACTIVE_PROBE_UNANSWERED)
994
- : !blocked(l._parent)))
1182
+ (l?._lane
1183
+ ? l._parent?._verdict === l &&
1184
+ (r._config & CONFIG_VERDICT
1185
+ ? !(r._flags & REACTIVE_PROBE_UNANSWERED)
1186
+ : !blocked(t) || heldBy(t, r))
1187
+ : l &&
1188
+ !(
1189
+ r._flags & REACTIVE_PROBE_UNANSWERED ||
1190
+ (r._flags & REACTIVE_STAGED_READ && blocked(l) && !bornInto(r, l))
1191
+ ))
995
1192
  )
996
1193
  continue;
997
1194
  // A leaked effect re-derives seated in its lane from its first read: it
@@ -1000,12 +1197,48 @@ const laneSeams = leaks => {
1000
1197
  // the staging, then re-enters the lane at its guess read and leaks
1001
1198
  // again — every round (#4014). A computed is already seated by its
1002
1199
  // override, and a written guess's own pass stays the frame's.
1003
- if (l != null && l._lane && r._type) r._flags |= REACTIVE_LANE_DIRTY;
1200
+ if (l?._lane && r._type) r._flags |= REACTIVE_LANE_DIRTY;
1201
+ // R5 for an async reader: its frame pass asked the frame's question
1202
+ // (F-6's lane gets its own pass now). That flight is the frame's
1203
+ // answer: the landing adopts it rather than asking it again (F-9: a
1204
+ // re-ask of the same question is no new question; `reruns`). A frame
1205
+ // landing at this seam has nothing to re-derive: the pass was the
1206
+ // landing's, its flight the screen's answer. Sync or async, the
1207
+ // reader's lane pass is still to come: the verdict lane it joins does
1208
+ // not reveal this seam (F-6: the lane's readers show its answer
1209
+ // together, D-23: atomic with the value) — revealed now, it showed a
1210
+ // verdict beside the frame pass's value, before the pass that answers
1211
+ // for the lane, and a reader of the re-derived memo read it outside
1212
+ // the shown lane and waited for the frame (#4393).
1213
+ else if (!l?._lane && r._flags & REACTIVE_PROBE_UNANSWERED) {
1214
+ if (isThenable(r._x?._inFlight)) {
1215
+ if (!blocked(t)) continue;
1216
+ r._x._parked = r._x._inFlight;
1217
+ } else if (blocked(t))
1218
+ // A sync reader's lane pass re-derives the async memos over it (a
1219
+ // memo over a verdict memo is a verdict reader, D-25 #4050; D-22:
1220
+ // `latest()` extracted into a memo reads as inline). The flight
1221
+ // each asked over its frame staging, up and held by `t`, is the
1222
+ // frame's answer too: the landing adopts it (F-9).
1223
+ for (let s = r._subs, n; s; s = s._nextSub)
1224
+ if (
1225
+ isThenable((n = s._sub)._x?._inFlight) &&
1226
+ n._x._pendingSources?.has(n) &&
1227
+ n._config & CONFIG_HELD &&
1228
+ txOf(n) === t
1229
+ )
1230
+ n._x._parked = n._x._inFlight;
1231
+ if (t?._verdict && leaked.indexOf(t._verdict) === -1) leaked.push(t._verdict);
1232
+ }
1004
1233
  enqueueSub(r);
1005
- if (l != null && l._lane && leaked.indexOf(l) === -1) leaked.push(l);
1006
- }
1007
- for (let k = Math.min(judged, lanes.length) - 1; k >= 0; k--)
1008
- laneSeam(lanes[k], leaked.indexOf(lanes[k]) !== -1);
1234
+ if (l?._lane && leaked.indexOf(l) === -1) leaked.push(l);
1235
+ }
1236
+ // A verdict lane born at this seam is judged with it: it exists only once
1237
+ // a pass routed into it — the park seam's zombie verdict readers re-run
1238
+ // as its work so that this seam reveals them (#3444).
1239
+ for (let k = lanes.length - 1; k >= 0; k--)
1240
+ if (k < judged || lanes[k]._parent?._verdict === lanes[k])
1241
+ laneSeam(lanes[k], leaked.indexOf(lanes[k]) !== -1);
1009
1242
  // A verdict reader that entered a lane this seam held: its run waits with
1010
1243
  // the lane, and the verdict beside it would freeze — it re-derives next
1011
1244
  // round from the frame's seat, a stale reader of the lane (#4284).
@@ -1015,6 +1248,36 @@ const laneSeams = leaks => {
1015
1248
  enqueueSub(r);
1016
1249
  }
1017
1250
  };
1251
+ /** The landing's re-derive of a reader whose frame flight the seam parked
1252
+ * (R5): the flight asked the question the landing would — adopted, it is
1253
+ * asked once (F-9). Not over a pass already queued: that one asks anew.
1254
+ * Registered as if this pass had returned it — the node pending on it, its
1255
+ * landing the answer — through a promise of its own: the frame pass's
1256
+ * registration still listens to the flight, and must not land it a second
1257
+ * time. */
1258
+ const adoptParked = r => {
1259
+ const parked = r._x?._parked;
1260
+ if (!parked || r._flags & (REACTIVE_DIRTY | REACTIVE_CHECK | REACTIVE_IN_HEAP)) return false;
1261
+ r._x._parked = undefined;
1262
+ releaseFlightTeardown(r);
1263
+ try {
1264
+ handleAsync(r, parked.then());
1265
+ } catch {}
1266
+ r._x._blocked = true;
1267
+ notifyStatus(r, STATUS_PENDING, new NotReadyError(r));
1268
+ return true;
1269
+ };
1270
+ /** A15: lane work or a verdict reader was served `el`'s committed value
1271
+ * (core.ts `frameRead`), so its pass does not wait on `el`'s flight: what
1272
+ * `el`'s pending propagated onto it before the pass (its origins, `el`
1273
+ * included) is not this pass's — the landing re-derives it (`reruns`).
1274
+ * Kept, a pass that went pending on another source never woke when that
1275
+ * source settled (#4397). Only a reader something made pending has anything
1276
+ * to drop. */
1277
+ const laneServed = (c, el) => {
1278
+ const from = el._x?._pendingSources;
1279
+ if (from && c._x?._pendingSources) for (const s of from) removePendingSource(c, s);
1280
+ };
1018
1281
  /** Render effects that read a verdict and entered a lane before its seam (#4284). */
1019
1282
  const entered = [];
1020
1283
  installLaneHooks(
@@ -1026,7 +1289,9 @@ installLaneHooks(
1026
1289
  laneSeams,
1027
1290
  () => lanes.length !== 0 && laneCorrections(),
1028
1291
  endLanes,
1029
- lanesBlocked
1292
+ lanesBlocked,
1293
+ adoptParked,
1294
+ laneServed
1030
1295
  );
1031
1296
  GlobalQueue._laneGuesses = t => {
1032
1297
  const out = [];
@@ -1067,7 +1332,7 @@ function watchVerdict(c, unanswered = false) {
1067
1332
  * does not rescue it: its result shows at the landing, re-derived). A lane
1068
1333
  * read, for `recompute`'s tail (REACTIVE_LANE_READ). */
1069
1334
  function route(c, t) {
1070
- if (passLane === null && !(c._flags & (REACTIVE_JOINED | REACTIVE_STAGED_READ)))
1335
+ if (!passLane && !(c._flags & (REACTIVE_JOINED | REACTIVE_STAGED_READ)))
1071
1336
  setPassLane(verdictLane(t));
1072
1337
  c._flags |= REACTIVE_LANE_READ;
1073
1338
  }
@@ -1134,40 +1399,41 @@ function verdictRead(c, t, plain) {
1134
1399
  * watches the seam and runs again if the flush parks (a plain sync write
1135
1400
  * never glitches it true).
1136
1401
  *
1137
- * `latest` has its answer either way — the proposal — and only the reader's
1138
- * membership was open, so it is settled now: the frame gets its transaction
1139
- * (`joinFuture(null)`; nothing holds it at the seam and it lands there, the
1140
- * lane dissolving into it — a plain commit) and the reader is its verdict
1141
- * lane's. One pass: a provisional pass re-derived at the seam ran everything
1142
- * downstream of it twice (#3540 web). A pass that also read the frame's own
1143
- * stagings plainly (REACTIVE_STAGED_READ) stays the frame's (`route` does
1144
- * not move it); a verdict reader's plain read AFTER the window sees the
1145
- * screen (`read`: `stagedScreen`). */
1402
+ * `latest` has its answer either way — the proposal — but its reader's
1403
+ * membership is the seam's too: the frame opens no transaction for it
1404
+ * (#4365 — one opened for the read alone made a plain read after the window
1405
+ * take it for a hold, `stagedScreen`, and serve the screen beside the
1406
+ * staging in a frame nothing held). The reader watches the seam like a
1407
+ * probe before anything joined: its pass is the frame's, and runs again in
1408
+ * the verdict lane if the flush parks. Once the flush has joined a
1409
+ * transaction, the reader is its verdict lane's at once; a pass that also
1410
+ * read the frame's own stagings plainly (REACTIVE_STAGED_READ) stays the
1411
+ * frame's (`route` does not move it). */
1146
1412
  function provisionalVerdict(c, notFinal) {
1147
- if (!probing) {
1148
- joinFuture(null);
1413
+ if (flushTransaction) {
1414
+ if (probing && notFinal) probeFound = true;
1149
1415
  verdictRead(c, flushTransaction);
1150
- return;
1151
- }
1152
- if (flushTransaction !== null) {
1153
- if (notFinal) probeFound = true;
1154
- verdictRead(c, flushTransaction);
1155
- if (c._flags & REACTIVE_STAGED_READ) watchVerdict(c);
1416
+ if (probing && c._flags & REACTIVE_STAGED_READ) watchVerdict(c);
1156
1417
  return;
1157
1418
  }
1158
1419
  // Before anything joined, a staging that would not be final held has no
1159
1420
  // answer yet: whatever the seam decides, the pass runs again — a later
1160
1421
  // plain read of the same pass may route it into the verdict lane
1161
1422
  // (`observeFlight`), which the seam must not take as this probe's answer.
1162
- watchVerdict(c, notFinal);
1423
+ // `latest` opens no transaction for its answer either (#4365): the plain
1424
+ // read of a staging after it took one opened for it for a hold and paired
1425
+ // the staging with the screen, in a frame nothing held. Its pass is the
1426
+ // frame's — whole — and re-derives once if the flush parks (unanswered:
1427
+ // its membership is the seam's, not the transaction it joins).
1428
+ watchVerdict(c, !probing || notFinal);
1163
1429
  }
1164
1430
  /** A19 exc. 2 / A24: a flight that re-asks the question already answered
1165
1431
  * (a `refresh()`, a poll) is quiet; a node pending through others is quiet
1166
1432
  * when every flight it waits on is. */
1167
1433
  function quietPending(el) {
1168
1434
  const x = el._x;
1169
- if (x === null) return false;
1170
- if (x._pendingSources !== undefined) {
1435
+ if (!x) return false;
1436
+ if (x._pendingSources) {
1171
1437
  for (const source of x._pendingSources) if (!source._x?._reask) return false;
1172
1438
  return true;
1173
1439
  }
@@ -1199,9 +1465,17 @@ function heldNotFinal(owner) {
1199
1465
  function pendingVerdict(c, el) {
1200
1466
  if (el._statusFlags & STATUS_UNINITIALIZED) return;
1201
1467
  if (probing && !quietPending(el)) probeFound = true;
1202
- if (c !== null && tracking) {
1468
+ if (c && tracking) {
1203
1469
  if (el._config & CONFIG_HELD) verdictRead(c, txOf(el));
1204
- else if (globalQueue._running) provisionalVerdict(c, false); // decided above
1470
+ else if (globalQueue._running) {
1471
+ // `latest` of a flight serves the committed value, the frame's and the
1472
+ // lane's alike (D-22): nothing is the seam's, so the reader is the
1473
+ // verdict lane's now, as a plain read of a flight makes it
1474
+ // (`observeFlight`). Its frame pass re-derived in the lane (R5) asked
1475
+ // its question again (F-9, #4397). A staging's is the seam's (#4365).
1476
+ if (!probing) joinFuture(null);
1477
+ provisionalVerdict(c, false); // decided above
1478
+ }
1205
1479
  }
1206
1480
  }
1207
1481
  /** The pass entering a verdict window is a verdict reader (CONFIG_VERDICT):
@@ -1210,11 +1484,21 @@ function pendingVerdict(c, el) {
1210
1484
  * a router's link claim probing inside the computation that creates the
1211
1485
  * anchor). A throw still reaches the pass through `untrack`. */
1212
1486
  function markVerdictReader(window) {
1213
- let c = context;
1214
- if (c?._root) c = c._parentComputed;
1215
- if (c !== null && tracking) {
1216
- c._config |= CONFIG_VERDICT;
1217
- c._flags |= REACTIVE_PROBED;
1487
+ const c = creatorPass(context);
1488
+ if (c && tracking) {
1489
+ // A pass run inside passes still running — its creator's, theirs — one
1490
+ // of which read the future or a staging (a branch a held or staged
1491
+ // condition opened, and whatever that branch creates) has no screen: the
1492
+ // branch exists only in that world. It reads that world as any
1493
+ // derivation of it does (`bornInto` once held) and stays its pass
1494
+ // (`route`); a verdict reader from its next pass. #4341: a memo born in
1495
+ // a newly opened <Match> read the screen, where its branch was closed.
1496
+ // Not such a pass: a verdict reader now.
1497
+ c._config &= ~CONFIG_ASYNC;
1498
+ c._flags |=
1499
+ REACTIVE_PROBED |
1500
+ REACTIVE_WINDOW |
1501
+ (creatorReads(c, 0) || ((c._config |= CONFIG_VERDICT), 0));
1218
1502
  // Observe tier: which windows this pass entered (1 `isPending`, 2
1219
1503
  // `latest`) — the hold census names the affordance (attribution.ts).
1220
1504
  c._devWindows = (c._devWindows ?? 0) | window;
@@ -1305,7 +1589,7 @@ function staged(el) {
1305
1589
  function heldLatest(el, c) {
1306
1590
  const u = unflushedValue(el);
1307
1591
  if (u === NOT_PENDING) return staged(el);
1308
- if (c !== null) markLateLinker(c);
1592
+ if (c) markLateLinker(c);
1309
1593
  return u;
1310
1594
  }
1311
1595
  /** The verdict windows' read — `latest(fn)` and `isPending(fn)` — in one
@@ -1318,7 +1602,9 @@ function heldLatest(el, c) {
1318
1602
  * differs (A24, A18 provenance). A derivation: `latest` sees the lane's
1319
1603
  * latest (a landing a blocked lane holds has answered its question — the
1320
1604
  * checkout's shipping text updates independently of the tax in flight),
1321
- * `isPending` the screen; not final while its own flight is up;
1605
+ * `isPending` the screen; not final while its own flight is up. A
1606
+ * derivation's first load in flight (nothing produced yet — an async memo
1607
+ * over a verdict memo, F-6) reads as the flight below: loading (D-22);
1322
1608
  * - a held node: the reader breaks out as the holder's verdict lane's and
1323
1609
  * re-derives at the landing; `latest` serves the proposal (`heldLatest`),
1324
1610
  * `isPending` answers `heldNotFinal`;
@@ -1331,49 +1617,70 @@ function heldLatest(el, c) {
1331
1617
  * - an errored derivation throws for every reader (#2897). */
1332
1618
  function verdictValue(el, c) {
1333
1619
  const owner = el;
1334
- const owned = c !== null && !(c._config & CONFIG_CHILDREN_FORBIDDEN);
1620
+ const owned = !!c && !(c._config & CONFIG_CHILDREN_FORBIDDEN);
1335
1621
  const tracked = owned && tracking;
1336
- if (c !== null && tracking) {
1622
+ // A31: the pulled memo's pass is its own, not the probe's — it reads
1623
+ // outside the window, and the probe's read below answers for what the pass
1624
+ // produced. So is a lazy or dormant memo's wake (an unowned memo with no
1625
+ // reader left, #4366): woken inside the window, it read its sources as the
1626
+ // probe does, the committed value under a landing. (A signal has no
1627
+ // `_flags`: the wake is a no-op for it.)
1628
+ setVerdict(null);
1629
+ prepareComputed(owner);
1630
+ if (c && tracking) {
1337
1631
  link(el, c);
1338
- if (owner._fn !== undefined) {
1339
- // A31: the pulled memo's pass is its own, not the probe's — it reads
1340
- // outside the window, and the probe's read below answers for what
1341
- // the pass produced.
1342
- setVerdict(null);
1343
- pullComputed(owner, c);
1344
- setVerdict(verdictValue);
1345
- }
1632
+ if (el._fn) pullComputed(owner, c);
1346
1633
  }
1634
+ setVerdict(verdictValue);
1347
1635
  // A live `affects()` mark on the node or a dependency (affects.ts): not
1348
1636
  // final by declaration — the value itself is read as below. The verdict is
1349
1637
  // display-ahead like any: in a transaction's flush (the declaring action's
1350
1638
  // body) the reader is its verdict lane's, not a staging the frame parks.
1351
- if (probing && GlobalQueue._marked !== undefined && GlobalQueue._marked(owner)) {
1639
+ if (probing && GlobalQueue._marked?.(owner)) {
1352
1640
  probeFound = true;
1353
- if (tracked && flushTransaction !== null) verdictRead(c, flushTransaction);
1641
+ if (tracked && flushTransaction) verdictRead(c, flushTransaction);
1354
1642
  }
1355
1643
  if (
1356
1644
  el._config & CONFIG_OVERRIDE &&
1357
1645
  !(owner._statusFlags & STATUS_ERROR && !(el._config & CONFIG_GUESS))
1358
1646
  ) {
1359
- const l = txOf(el);
1360
- if (tracked) verdictRead(c, l._parent ?? l);
1361
- const flying = (owner._statusFlags & STATUS_PENDING) !== 0 && !quietPending(owner);
1362
- if (el._config & CONFIG_GUESS) {
1647
+ const l = txOf(el),
1648
+ flying = (owner._statusFlags & STATUS_PENDING) !== 0 && !quietPending(owner),
1649
+ guess = el._config & CONFIG_GUESS,
1650
+ // NOT_PENDING: a node born in the lane with nothing in it yet has no
1651
+ // value in either view — loading, not pending, and no verdict to read
1652
+ // ahead: it is read as any uninitialized node, NotReady below (A7, A19
1653
+ // exc. 1; #4363), its reader not moved into the verdict lane.
1654
+ v = guess ? 0 : latestActive && !flying ? laneValueOf(el) : display(el);
1655
+ if (v !== NOT_PENDING) {
1656
+ if (tracked) verdictRead(c, l._parent ?? l);
1657
+ if (guess) {
1658
+ if (
1659
+ probing &&
1660
+ (flying ||
1661
+ (el._pendingValue !== NOT_PENDING &&
1662
+ (!el._equals || !el._equals(el._x._lane, el._pendingValue))))
1663
+ )
1664
+ probeFound = true;
1665
+ // A lane a correction folded under (D-34): `latest()` shows the hold's
1666
+ // world whole — the truth beneath each guess, else what it covered.
1667
+ if (latestActive && l.folded) return l.folded(null, el);
1668
+ return el._x._lane;
1669
+ }
1670
+ // A first load in flight with nothing produced yet: loading, for
1671
+ // `latest` (D-22) as for `isPending` (A19 exc. 1) — unless an error
1672
+ // answered it first: its re-ask is pending, the error its display
1673
+ // (D-41, #4375).
1363
1674
  if (
1364
- probing &&
1365
- (flying ||
1366
- (el._pendingValue !== NOT_PENDING &&
1367
- (!el._equals || !el._equals(el._x._lane, el._pendingValue))))
1675
+ flying &&
1676
+ v === el._value &&
1677
+ owner._statusFlags & STATUS_UNINITIALIZED &&
1678
+ (latestActive || !owner._x._shownError)
1368
1679
  )
1369
- probeFound = true;
1370
- // A lane a correction folded under (D-34): `latest()` shows the hold's
1371
- // world whole — the truth beneath each guess, else what it covered.
1372
- if (latestActive && l.folded) return l.folded(null, el);
1373
- return el._x._lane;
1680
+ throw owner._x._error;
1681
+ if (probing && flying) probeFound = true;
1682
+ return v;
1374
1683
  }
1375
- if (probing && flying) probeFound = true;
1376
- return latestActive && !flying ? laneValueOf(el) : display(el);
1377
1684
  }
1378
1685
  // Dev strict-read scopes (a component body, an effect callback) warn on a
1379
1686
  // verdict read as on any untracked read; the pending throw they add for a
@@ -1398,9 +1705,8 @@ function verdictValue(el, c) {
1398
1705
  tracked &&
1399
1706
  !probing &&
1400
1707
  c._type === EFFECT_RENDER &&
1401
- passLane === null &&
1402
- t._verdict !== null &&
1403
- t._verdict._held &&
1708
+ !passLane &&
1709
+ t._verdict?._held &&
1404
1710
  flushTransaction !== t
1405
1711
  ) {
1406
1712
  staleReader(c, t._verdict);
@@ -1412,7 +1718,7 @@ function verdictValue(el, c) {
1412
1718
  // An action body reading another transaction's proposal derives
1413
1719
  // from it: the two settle as one (A15; posture C, 2026-09-15). An
1414
1720
  // untracked read elsewhere has no transaction to entangle.
1415
- if (!tracked && flushTransaction !== null && !globalQueue._running) joinFuture(txOf(el));
1721
+ if (!tracked && flushTransaction && !globalQueue._running) joinFuture(txOf(el));
1416
1722
  return heldLatest(el, tracked ? c : null);
1417
1723
  }
1418
1724
  return el._value;
@@ -1441,7 +1747,7 @@ function verdictValue(el, c) {
1441
1747
  // reader derived from the flushed world runs again in the carrying
1442
1748
  // flush.
1443
1749
  if (globalQueue._running) return staged(el);
1444
- if (c !== null) markLateLinker(c);
1750
+ if (c) markLateLinker(c);
1445
1751
  }
1446
1752
  return el._value;
1447
1753
  }
@@ -1453,16 +1759,51 @@ function verdictValue(el, c) {
1453
1759
  // As `read()`: an untracked read of a pending node still re-runs its
1454
1760
  // reader when the node settles (a projection's pull reads its firewall
1455
1761
  // untracked, store/store.ts).
1456
- if (c !== null && !tracking && !spectating && el !== c) link(el, c);
1762
+ if (c && !tracking && !spectating && el !== c) link(el, c);
1457
1763
  throw owner._x?._error;
1458
1764
  }
1459
- if (owner._fn !== undefined && owner._statusFlags & STATUS_ERROR) throw owner._x._error;
1765
+ if (el._fn && owner._statusFlags & STATUS_ERROR) throw owner._x._error;
1460
1766
  return el._value;
1461
1767
  }
1768
+ /** D-25: a node whose fetch a hold is waiting on is that hold's work, not a
1769
+ * verdict reader — a pass that re-runs it reads plainly — and a memo's
1770
+ * posture is what it reads: one that reads a verdict reader's value is a
1771
+ * verdict reader itself (#4050). An async memo with no verdict window of its
1772
+ * own (REACTIVE_WINDOW: it read a verdict memo, it called neither `latest`
1773
+ * nor `isPending`) is a verdict reader between its flights, and the hold's
1774
+ * work while one is up or its hold re-runs it.
1775
+ *
1776
+ * At a pass's head (core.ts `recompute`): a pending node's pass — its
1777
+ * flight is the transition's own work (#3884) — and a held one's re-run of
1778
+ * such a memo read plainly: the write that re-ran it is its hold's, and
1779
+ * served the screen it re-asked a question it had answered (A10, #4397).
1780
+ * Only a memo that never asked: D-25's "a verdict posture outlives the pass
1781
+ * that asked" is a pass that called `latest`/`isPending`, which clears
1782
+ * CONFIG_ASYNC and stays a verdict reader whoever dirties it.
1783
+ *
1784
+ * On `source` going pending (`propagateStatus`): such a memo waits on it
1785
+ * like any derivation (F-9) instead of re-deriving — what it read changed
1786
+ * no value, so its re-run re-asked the question it was answering. Unless
1787
+ * its own flight is up outside a lane on a new question of the source's:
1788
+ * its re-run supersedes a flight asked of a value the source has moved past
1789
+ * (re-asked by a `refresh()`, the source's question is the same one). */
1790
+ function holdWork(n, source) {
1791
+ // CONFIG_ASYNC records an async memo with no window of its own: its last
1792
+ // answering pass registered a flight (`handleAsync`) and called neither
1793
+ // `latest` nor `isPending`. A window (`markVerdictReader`), a sync answer or
1794
+ // an error clears it; a pass left pending keeps the last answer's.
1795
+ const own = n._config & CONFIG_ASYNC;
1796
+ return (
1797
+ own &&
1798
+ (!(n._statusFlags & STATUS_PENDING) ||
1799
+ n._x._transaction?._lane ||
1800
+ source._flags & REACTIVE_REASK)
1801
+ );
1802
+ }
1462
1803
  // Installed at module evaluation — present exactly when something imports
1463
1804
  // `latest` or `isPending`. (`read` dispatches to `verdictValue` through
1464
1805
  // `verdict`, set while a window is open.)
1465
- installVerdictHooks(observeFlight);
1806
+ installVerdictHooks(observeFlight, holdWork);
1466
1807
  /** A store read inside an untracked `isPending` probe found a live mark on
1467
1808
  * a record with no node to carry it (store/affects.ts): not final. */
1468
1809
  GlobalQueue._witnessMark = () => {
@@ -1906,7 +2247,8 @@ function effect(compute, effect, error, options) {
1906
2247
  !node._x?._transaction?._lane &&
1907
2248
  (node._type === EFFECT_USER || options?.schedule
1908
2249
  ? globalQueue.enqueue(node._type, runEffect.bind(null, node))
1909
- : runEffect(node, node._type));
2250
+ : // Type 0: the synchronous first render, not a queued run (D-19).
2251
+ runEffect(node, 0));
1910
2252
  if (!node._parent) {
1911
2253
  const message =
1912
2254
  "[NO_OWNER_EFFECT] Effects created outside a reactive context will never be disposed";
@@ -1945,11 +2287,12 @@ function notifyEffectStatus() {
1945
2287
  return;
1946
2288
  }
1947
2289
  // Dev locates the halt at the node that threw: the StatusError's
1948
- // source, upstream of this effect when a computed failed.
2290
+ // source, upstream of this effect when a computed failed. The rethrow is
2291
+ // the user's error, not the wrapper: the wrapper's `source` is a node, and
2292
+ // a reporter that serializes the throw walks the reactive graph.
1949
2293
  escalate(
1950
2294
  this,
1951
2295
  unwrapStatusError(actualError),
1952
- actualError,
1953
2296
  actualError instanceof StatusError ? actualError.source : this
1954
2297
  );
1955
2298
  } else if (this._type === EFFECT_RENDER) {
@@ -1981,11 +2324,11 @@ function notifyEffectStatus() {
1981
2324
  }
1982
2325
  }
1983
2326
  /** An effect's error no boundary takes halts the system (boundary-or-halt):
1984
- * halted with `cause`, `thrown` rethrown; dev locates it at `subject`. */
1985
- function escalate(node, cause, thrown = cause, subject = node) {
2327
+ * halted with `cause` and rethrown; dev locates it at `subject`. */
2328
+ function escalate(node, cause, subject = node) {
1986
2329
  if (!globalQueue.notify(node, STATUS_ERROR, STATUS_ERROR)) {
1987
2330
  haltReactivityAt(cause, subject);
1988
- throw thrown;
2331
+ throw cause;
1989
2332
  }
1990
2333
  }
1991
2334
  function runEffect(node, type) {
@@ -1995,10 +2338,12 @@ function runEffect(node, type) {
1995
2338
  // effect's update too (maintainer, 2026-10-02: "we held the queue but let
1996
2339
  // the sync renders through… we definitely shouldn't be showing portals
1997
2340
  // early") — a Portal's or head-tag's render effect writes outside the
1998
- // hidden subtree. The synchronous first render on creation is not a queued
1999
- // run and goes through. `_modified` stays set; the boundary re-queues the
2000
- // run (`release`).
2001
- if (heldRunHook?.(node)) return;
2341
+ // hidden subtree. The synchronous first render on creation (`type` 0) is
2342
+ // not a queued run and goes through, whichever boundary above is on its
2343
+ // fallback then — an outer one about to reveal in this flush included
2344
+ // (#4343). `_modified` stays set; the boundary re-queues the run
2345
+ // (`release`).
2346
+ if (type && heldRunHook?.(node)) return;
2002
2347
  // Error arm (#2840), user effects only: a compute-phase error that is still
2003
2348
  // the node's settled state at effect time runs the bundle's error handler in
2004
2349
  // this same imperative, writable scope. Unwrap the StatusError used for
@@ -2334,6 +2679,15 @@ let writeOverride = false;
2334
2679
  function setWriteOverride(value) {
2335
2680
  writeOverride = value;
2336
2681
  }
2682
+ /** The list driver's view of a shallow store (map.ts): the backing a read of
2683
+ * every slot serves this reader, or `undefined` when the list is no shallow
2684
+ * store or a slot read could differ from it. Installed by store.ts's top
2685
+ * level, so a `<For>` in an app without stores carries none of it; it lives
2686
+ * beside `$TRACK` so map.ts reaches no other store module. */
2687
+ let shallowView = null;
2688
+ function installShallowView(view) {
2689
+ shallowView = view;
2690
+ }
2337
2691
 
2338
2692
  /**
2339
2693
  * Deferred hydration, dependency edges for stores. Only the frames client
@@ -3439,9 +3793,12 @@ function updateKeyedMap() {
3439
3793
  // vanishing. Set per pass: the first pass runs inside `computed()`, before
3440
3794
  // the node exists to the caller.
3441
3795
  this._owner._parentComputed = getOwner();
3442
- const newItems = this._list() || [],
3443
- newLen = newItems.length;
3796
+ let newItems = this._list() || [];
3444
3797
  newItems[$TRACK]; // top level tracking (a store array's container node)
3798
+ // A shallow store array diffs its backing: its slots are raw records, and
3799
+ // a trap per slot read was most of a one-row write on a 1k-row list.
3800
+ newItems = shallowView?.(newItems[$TARGET]) ?? newItems;
3801
+ const newLen = newItems.length;
3445
3802
  runWithOwner(this._owner, () => {
3446
3803
  let i,
3447
3804
  j,
@@ -4199,6 +4556,7 @@ const storePark = t => {
4199
4556
  const target = foldList[i];
4200
4557
  if (target.k === null && target.pb !== null) holdNode(getContainerNode(target), t);
4201
4558
  }
4559
+ optHooks?.park(t);
4202
4560
  };
4203
4561
  function getDeepNode(target) {
4204
4562
  let dk = target.dk;
@@ -4274,17 +4632,26 @@ function cloneRaw(source, t) {
4274
4632
  clone[$OWNER] = t;
4275
4633
  return clone;
4276
4634
  }
4277
- const descs = Object.getOwnPropertyDescriptors(source);
4278
- for (const key of Reflect.ownKeys(descs)) {
4279
- const d = descs[key];
4280
- if (key === "length" && Array.isArray(source)) continue;
4281
- d.configurable = true;
4282
- if (!d.get && !d.set) d.writable = true;
4283
- else t.a = true;
4284
- }
4285
- const clone = Array.isArray(source)
4286
- ? Object.defineProperties([], descs)
4287
- : Object.create(Object.getPrototypeOf(source), descs);
4635
+ // A plain array with no accessors and no named keys beyond `length`
4636
+ // (`kc` counts own keys) clones by `slice()`: the descriptor path
4637
+ // allocates one descriptor per element, O(n) per first draft write (10k
4638
+ // rows: ~10 ms per push).
4639
+ let clone;
4640
+ if (!t.a && t.kc <= source.length + 1 && Object.getPrototypeOf(source) === Array.prototype)
4641
+ clone = source.slice();
4642
+ else {
4643
+ const descs = Object.getOwnPropertyDescriptors(source);
4644
+ for (const key of Reflect.ownKeys(descs)) {
4645
+ const d = descs[key];
4646
+ if (key === "length" && Array.isArray(source)) continue;
4647
+ d.configurable = true;
4648
+ if (!d.get && !d.set) d.writable = true;
4649
+ else t.a = true;
4650
+ }
4651
+ clone = Array.isArray(source)
4652
+ ? Object.defineProperties([], descs)
4653
+ : Object.create(Object.getPrototypeOf(source), descs);
4654
+ }
4288
4655
  clone[$OWNER] = t;
4289
4656
  return clone;
4290
4657
  }
@@ -4783,13 +5150,18 @@ function notifyWrites(t) {
4783
5150
  // Optimistic channel: a user's writes on an optimistic family become
4784
5151
  // guesses on the nodes (lanes); the draft is discarded, the committed
4785
5152
  // backing never touched.
4786
- if (optHooks && userWrite && t.fam?.opt === true) {
4787
- // The draft is discarded first: the diff reads the target (its nodes
4788
- // are born from the committed frame, not the draft).
4789
- t.pb = null;
4790
- t.wk = null;
4791
- t.pb = optHooks.writes(t, pb);
4792
- return;
5153
+ if (optHooks && t.fam?.opt === true) {
5154
+ if (userWrite) {
5155
+ // The draft is discarded first: the diff reads the target (its nodes
5156
+ // are born from the committed frame, not the draft).
5157
+ t.pb = null;
5158
+ t.wk = null;
5159
+ t.pb = optHooks.writes(t, pb);
5160
+ return;
5161
+ }
5162
+ // The derive's draft: new rows under guessed rows are paired with them
5163
+ // first (D-34, #4329).
5164
+ optHooks.pair(t, pb);
4793
5165
  }
4794
5166
  const old = t.v;
4795
5167
  // Devtools mutation hook: full-key diff (dev-only cost) so unobserved
@@ -4966,7 +5338,10 @@ function notifyKeyDiff(node, key, old, neu, probe = true) {
4966
5338
  } else {
4967
5339
  const ov = old[key];
4968
5340
  const nv = neu[key];
4969
- if (!isEqual(ov, nv) && !targetsEqual(ov, nv))
5341
+ // A live chained `old` (a store proxy, §7b) already reads the inner
5342
+ // store's next frame, not what the node was last told: written, the
5343
+ // node's own equality judging it (#4361).
5344
+ if (old[$TARGET] || (!isEqual(ov, nv) && !targetsEqual(ov, nv)))
4970
5345
  setSignal(node, typeof nv === "function" ? () => nv : nv);
4971
5346
  }
4972
5347
  }
@@ -4992,7 +5367,8 @@ function notifyKeyValue(node, key, ov, nv, old, neu) {
4992
5367
  * unrelated `a.value` stayed stale until the action settled). A real
4993
5368
  * change still writes, and on a held node still proposes. A live chained
4994
5369
  * `old` (a store proxy, §7b) reflects the inner store, not what the node
4995
- * was last told — written unconditionally, as before.
5370
+ * was last told — written unconditionally, as before; its container too
5371
+ * (#4361).
4996
5372
  *
4997
5373
  * The container is told of an arrangement change when it has structural
4998
5374
  * subscribers OR carries an arrangement guess (S4): for a guessed
@@ -5004,17 +5380,18 @@ function notifyKeyValue(node, key, ov, nv, old, neu) {
5004
5380
  * lane, by accident of being unconditional. */
5005
5381
  function notifyFoldTail(t, old, neu) {
5006
5382
  const has = t.h;
5383
+ const live = old[$TARGET];
5007
5384
  if (has !== null) {
5008
- const live = old[$TARGET];
5009
5385
  for (const key of Reflect.ownKeys(has))
5010
5386
  if (live || key in old !== key in neu) setSignal(has[key], key in neu);
5011
5387
  }
5012
5388
  const k = t.k;
5013
5389
  if (k !== null && (k._subs !== null || k._config & CONFIG_OVERRIDE)) {
5014
5390
  const changed =
5015
- Array.isArray(neu) && Array.isArray(old)
5391
+ live ||
5392
+ (Array.isArray(neu) && Array.isArray(old)
5016
5393
  ? arrayStructureChanged(old, neu)
5017
- : membershipChanged(old, neu);
5394
+ : membershipChanged(old, neu));
5018
5395
  if (changed) notifyContainer(k, neu);
5019
5396
  }
5020
5397
  }
@@ -5302,16 +5679,27 @@ function changedBetween(target, v, pb, key, shape) {
5302
5679
  }
5303
5680
  /** Value of a key with a leaf: core's — `read()` links a tracked reader and,
5304
5681
  * tracked or not, applies the hold and A28 rules a signal's read does. A
5305
- * projection's unheld staging is ahead for a handler (`familyAhead`). */
5682
+ * projection's unheld staging is ahead for a handler (`familyAhead`), and is
5683
+ * the only value while its derive has never committed: the committed side
5684
+ * is the seed, never observable (A25) — a reader core serves the screen (a
5685
+ * verdict reader, #4355) is served the staging, as for a memo born staged
5686
+ * (core `serve`). */
5306
5687
  function nodeValue(node, backing, target) {
5307
- let v = read(node);
5688
+ let v;
5308
5689
  if (
5309
5690
  node._pendingValue !== NOT_PENDING &&
5310
5691
  !(node._config & CONFIG_HELD) &&
5311
- context === null &&
5312
- target.fam !== null
5313
- )
5692
+ target.fam !== null &&
5693
+ (context === null || target.fam.node?._statusFlags & STATUS_UNINITIALIZED)
5694
+ ) {
5695
+ // A pass over a derive that has never committed: the staging is its
5696
+ // first answer, not a change — linked and served with no verdict (a
5697
+ // first landing is loading, not pending: A19 exc. 1, F-9, as for a memo
5698
+ // born staged; core's verdict would call the leaf's staging pending).
5699
+ devRead(node); // read()'s dev diagnostics, which this read skips
5700
+ if (context !== null && tracking) link(node, context);
5314
5701
  v = node._pendingValue;
5702
+ } else v = read(node);
5315
5703
  return v === FORCE ? backing : v;
5316
5704
  }
5317
5705
  /** Serve a present data key: link the leaf for a tracked reader (creating it
@@ -5398,15 +5786,9 @@ function pullFamily(target) {
5398
5786
  // (committed plus the guesses) and does not suspend on the derive; a
5399
5787
  // verdict window still asks it (`isPending` is true while the flight is
5400
5788
  // up). The family's, not the target's: the edit stands in for the one
5401
- // flight the family has.
5402
- const overlaid = target.fam.overlaid;
5403
- if (
5404
- verdict === null &&
5405
- fw._statusFlags & STATUS_PENDING &&
5406
- overlaid !== undefined &&
5407
- overlaid.size !== 0
5408
- )
5409
- return;
5789
+ // flight the family has — not over a new question they do not answer
5790
+ // (optimistic.ts `stands`, #4336).
5791
+ if (verdict === null && fw._statusFlags & STATUS_PENDING && optHooks?.stands(target)) return;
5410
5792
  read(fw);
5411
5793
  } else untrack(() => read(fw));
5412
5794
  }
@@ -5531,6 +5913,7 @@ function warnWriteOutsideSetter(target, key, operation) {
5531
5913
  }
5532
5914
  const traps = {
5533
5915
  get(target, key, receiver) {
5916
+ if (getObserver() === null) checkForeignRead();
5534
5917
  // One typeof gates every brand-symbol compare off the hot string path.
5535
5918
  if (typeof key !== "string") {
5536
5919
  if (key === $TARGET) return target;
@@ -5679,7 +6062,8 @@ const traps = {
5679
6062
  // Reading a currently-absent own key subscribes to it (R12) — for any
5680
6063
  // target OUTSIDE its own draft scope, even mid-setter (#3037, above).
5681
6064
  if (v === undefined && !inDraft(target)) {
5682
- if (getObserver() !== null) read(getNode(target, key, accProbe));
6065
+ // (`nodeValue` reads — and links — the node it creates.)
6066
+ if (getObserver() !== null) getNode(target, key, accProbe);
5683
6067
  const node = target.n?.[key];
5684
6068
  if (node) {
5685
6069
  const nv = nodeValue(node, undefined, target);
@@ -6009,6 +6393,17 @@ function createStore$1(initialValue, options) {
6009
6393
  const setter = fn => storeSetter(proxy, fn);
6010
6394
  return [proxy, setter];
6011
6395
  }
6396
+ /** The list driver's slot view of a shallow store (types.ts `shallowView`):
6397
+ * one `readSource` for the container instead of a trap per slot. A shallow
6398
+ * store's slots hold raw records, so the backing the container read serves
6399
+ * is what each slot read would — except where a slot's own leaf can answer
6400
+ * differently: an optimistic family's guesses, a chained backing, accessor
6401
+ * keys, or a read inside a draft or write scope. Those keep the trap. */
6402
+ installShallowView(t => {
6403
+ // Any open write scope keeps the trap (it covers `inDraft`).
6404
+ if (t?.s && !(t.ch || t.a || writeScopes || writeOverride || t.fam?.opt))
6405
+ return (t.fam && pullFamily(t), readSource(t));
6406
+ });
6012
6407
  /** True when `proxy` is a SHALLOW store (children served verbatim, slots
6013
6408
  * replaced by reference — #2932). The list driver uses this to choose the
6014
6409
  * slot-patch channel (collected row bodies) over per-record registration. */
@@ -6358,7 +6753,10 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
6358
6753
  if (shallow) markRawIngest(incoming);
6359
6754
  // The deep witness is bumped once per adoption (none to bump: done).
6360
6755
  let bumped = t.dk === null;
6361
- if (Array.isArray(prevView) !== nextArr) {
6756
+ // A live chained base (a store proxy, §7b) reads the inner store's
6757
+ // current frame, not what the nodes were last told — nothing to diff
6758
+ // against: the fold writes every node (#4361).
6759
+ if (Array.isArray(prevView) !== nextArr || old[$TARGET]) {
6362
6760
  notifyFold(t, old, incoming);
6363
6761
  return;
6364
6762
  }
@@ -6476,7 +6874,10 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
6476
6874
  let nodesHit = 0;
6477
6875
  for (const k in incoming) {
6478
6876
  const nv = incoming[k];
6479
- const ov = old[k];
6877
+ let ov = old[k];
6878
+ // A key under a guess is judged against the guess too (D-34): a truth
6879
+ // equal to the value the guess covered still answers it.
6880
+ if (optHooks && ov === nv && prevView !== old) ov = prevView[k];
6480
6881
  const isObj = nv !== null && typeof nv === "object";
6481
6882
  if (
6482
6883
  ov === nv &&
@@ -6676,6 +7077,7 @@ function wakeFamily(fam) {
6676
7077
  // reader the landing leaves unchanged does not re-run to clear it (#4305).
6677
7078
  const error = new NotReadyError(fw);
6678
7079
  fam.woke = true;
7080
+ optHooks?.ask(fam);
6679
7081
  forEachFamilyNode(fam, n => {
6680
7082
  if (n._subs === null) return;
6681
7083
  // The leaf carries the derive's pending as a propagated copy, as a memo
@@ -6711,6 +7113,10 @@ function settleFamily(fam) {
6711
7113
  forEachFamilyNode(fam, n => {
6712
7114
  if (n._subs !== null) settlePendingSource(n, fw);
6713
7115
  });
7116
+ // The landing is the derive's change for a reader parked on the derive
7117
+ // itself, whatever status the landing pass began from (an errored derive
7118
+ // re-asked: a reader that met the error has no leaf to learn it from).
7119
+ insertSubs(fw);
6714
7120
  }
6715
7121
  /** A source's status reached the derive by propagation (core
6716
7122
  * `propagateStatus`), its pass not re-run: the family learns it as from the
@@ -6771,7 +7177,7 @@ function runProjectionComputed(wrappedStore, fn, key, wrapCommit, aroundDraftWri
6771
7177
  draft,
6772
7178
  s => {
6773
7179
  result = fn(shadow ?? s);
6774
- const commit = v => {
7180
+ const commit = (v, wake = 1) => {
6775
7181
  // Shadow run: commit a detached snapshot, never the shadow itself
6776
7182
  // (adoption takes the value by identity — handing it the live
6777
7183
  // shadow would fuse the draft to the observable store).
@@ -6794,8 +7200,11 @@ function runProjectionComputed(wrappedStore, fn, key, wrapCommit, aroundDraftWri
6794
7200
  // A commit is the derive's change for the readers that observed its
6795
7201
  // flight (linked by `pullFamily`; a settled pull does not link), as
6796
7202
  // a memo's landing is for its readers: one that caught the NotReady
6797
- // (a fallback value, not pending) re-runs too (#4070).
6798
- insertSubs(owner);
7203
+ // (a fallback value, not pending) re-runs too (#4070). A pass of a
7204
+ // settled derive has none (`wake` 0): a link left is a stale one a
7205
+ // held frame keeps (A30), from a reader that re-ran since. Woken by
7206
+ // every pass, a reader feeding a key the pass peeks at looped (#4342).
7207
+ if (wake) insertSubs(owner);
6799
7208
  };
6800
7209
  const sync = handleAsync(owner, result, commit, (error, pending) => {
6801
7210
  // The flight rejected: the leaves' readers learn it from here —
@@ -6808,7 +7217,8 @@ function runProjectionComputed(wrappedStore, fn, key, wrapCommit, aroundDraftWri
6808
7217
  if (pending) wakeFamily(fam);
6809
7218
  else errorFamily(fam);
6810
7219
  });
6811
- if (!owner._loading) commit(sync);
7220
+ // (The status is the one before this pass: `recompute` sets it after.)
7221
+ if (!owner._loading) commit(sync, owner._statusFlags & (STATUS_PENDING | STATUS_ERROR));
6812
7222
  },
6813
7223
  false
6814
7224
  );
@@ -7027,21 +7437,23 @@ function arrangement(t, a, b) {
7027
7437
  const av = a instanceof LaneView;
7028
7438
  const bv = b instanceof LaneView;
7029
7439
  if (!av && !bv) return false;
7030
- return arrangementEqual(av ? a.rows : a, bv ? b.rows : b, t.fam?.key ?? null);
7440
+ return arrangementEqual(av ? a.rows : a, bv ? b.rows : b, t.fam);
7031
7441
  }
7032
- function rowSame(a, b, keyFn) {
7442
+ function rowSame(a, b, fam) {
7033
7443
  if (isEqual(a, b) || targetsEqual(a, b)) return true;
7034
- if (keyFn === null || !isWrappable(a) || !isWrappable(b)) return false;
7035
- const ka = keyFn(unwrapValue(a));
7036
- const kb = keyFn(unwrapValue(b));
7037
- // Keyless rows are identified by position — reconcile's fallback when the
7038
- // key is missing. The caller already paired these by index, so two keyless
7039
- // rows are the same row and the landing confirms the arrangement instead of
7040
- // dissolving it. Dissolving held an external echo under the action, and a
7041
- // mainline `until` then timed out on a source that already contained it
7042
- // (#3898). Keyed rows still match only on the key.
7043
- if (ka === undefined && kb === undefined) return true;
7044
- return ka !== undefined && sameKey(ka, kb);
7444
+ const keyFn = fam.key;
7445
+ if (!keyFn || !isWrappable(a) || !isWrappable(b)) return false;
7446
+ // A row the store already serves is its proxy's row: a reader (a list)
7447
+ // holds that proxy, so another object at the index is another row, by key
7448
+ // or contents alike — a guess's fresh object wraps a new proxy. A landing's
7449
+ // object has been adopted by the row it pairs with, so it serves that
7450
+ // row's proxy. Only rows no proxy serves yet pair by key, keyless rows by
7451
+ // position — reconcile's fallback (#3898).
7452
+ const ta = lookupTarget((a = unwrapValue(a)), fam);
7453
+ const tb = lookupTarget((b = unwrapValue(b)), fam);
7454
+ if (ta || tb) return ta === tb;
7455
+ // (Two keyless rows: `undefined` is the same key.)
7456
+ return sameKey(keyFn(a), keyFn(b));
7045
7457
  }
7046
7458
  /** Whether reconcile would write anything into `a` from `b`: wrappable
7047
7459
  * pairs descend, leaves compare as its writes do. A key mismatch is a
@@ -7066,11 +7478,11 @@ GlobalQueue._echoes = (a, b) => {
7066
7478
  return false;
7067
7479
  }
7068
7480
  };
7069
- function arrangementEqual(a, b, keyFn) {
7481
+ function arrangementEqual(a, b, fam) {
7070
7482
  if (Array.isArray(a) !== Array.isArray(b)) return false;
7071
7483
  if (Array.isArray(a)) {
7072
7484
  if (a.length !== b.length) return false;
7073
- for (let i = 0; i < a.length; i++) if (!rowSame(a[i], b[i], keyFn)) return false;
7485
+ for (let i = 0; i < a.length; i++) if (!rowSame(a[i], b[i], fam)) return false;
7074
7486
  return true;
7075
7487
  }
7076
7488
  const ak = Reflect.ownKeys(a).filter(k => k !== $OWNER);
@@ -7130,6 +7542,33 @@ function reconcileDraft(draft, incoming, keyFn) {
7130
7542
  }
7131
7543
  for (const k of Object.keys(draft)) if (!(k in incoming)) delete draft[k];
7132
7544
  }
7545
+ /** Core asks (lanes `laneWrite`): the family's derive, re-asking in a lane of
7546
+ * another action than the one that guessed this slot, writes it — that
7547
+ * lane's new question, which the guess (over the old one) does not answer:
7548
+ * the slot is the lane's derivation from here (F-6, #4336; lanes
7549
+ * `laneStage` for a node's own pass). */
7550
+ GlobalQueue._slotTake = (n, back) => {
7551
+ const t = n._host;
7552
+ if (back) {
7553
+ // The guess comes back (lanes `endLanes`): on the slot's node now — the
7554
+ // node itself, or the one a read made after it was released.
7555
+ const k = n._key;
7556
+ markOverlaid(t);
7557
+ return k === $TRACK
7558
+ ? getContainerNode(t)
7559
+ : n._equals === isEqual
7560
+ ? getHasNode(t, k)
7561
+ : getNode(t, k);
7562
+ }
7563
+ const d = t?.fam?.node?._x?._transaction;
7564
+ const g = txOf(n);
7565
+ if (!d?._lane || sameAction(d, g)) return false;
7566
+ // The re-ask takes the target's guess whole: the slot is the lane's
7567
+ // derivation; its other guessed slots show their truth in the lane's
7568
+ // world (lanes `take`).
7569
+ for (const m of nodesOf(t)) if (m?._config & CONFIG_GUESS && txOf(m) === g) take(m, d, m !== n);
7570
+ return true;
7571
+ };
7133
7572
  /** Q-D (plan sec. 39): an OLDER question's truth landed beneath an arrangement
7134
7573
  * guess (`laneWrite` held it — another action's landing is not this guess's
7135
7574
  * answer). The guess is re-based over it: order from the guess, rows from
@@ -7272,7 +7711,30 @@ function laneSet(node, value, lane) {
7272
7711
  laneDirty(node, lane);
7273
7712
  }
7274
7713
  }
7714
+ /** D-34 for a per-key guess (#4329): the derive's draft wrote new objects
7715
+ * over a target holding guessed rows (`d.length = 0; d.push(...rows)`, or
7716
+ * `d.issues = rows`). The container on the way down to each guessed row
7717
+ * reconciles the new object at its slot — in a keyed list, the one with
7718
+ * the row's key — exactly as a returned landing would: rows match by key,
7719
+ * a guessed row keeps its target, and its leaves are judged against their
7720
+ * guesses. Rows nothing guessed are left as the draft wrote them. */
7721
+ function pairRows(t, pb) {
7722
+ const keyFn = t.fam.key;
7723
+ for (let c of t.fam.overlaid ?? []) {
7724
+ // The guessed row, or its ancestor directly under `t` (none: skip).
7725
+ while (c.u !== t) if ((c = c.u) === null) break;
7726
+ if (c === null) continue;
7727
+ const k = keyFn?.(c.v);
7728
+ const nv = Array.isArray(pb)
7729
+ ? k !== undefined && pb.find(r => isWrappable(r) && sameKey(keyFn(r), k))
7730
+ : pb[c.pk];
7731
+ // A new object only: not `c`'s own, nor one another row serves
7732
+ // (pairing it would alias the two).
7733
+ if (isWrappable(nv) && !lookupTarget(nv, t.fam)) reconcileState(nv, c.px, keyFn, true);
7734
+ }
7735
+ }
7275
7736
  installOptHooks({
7737
+ pair: pairRows,
7276
7738
  draft: optimisticDraft,
7277
7739
  writes: notifyOptimisticWrites,
7278
7740
  arrangement,
@@ -7281,8 +7743,55 @@ installOptHooks({
7281
7743
  writer ? optimisticView(t, src, laneValueOf, true) : optimisticView(t, src),
7282
7744
  has: optimisticHas,
7283
7745
  descriptor: optimisticOwnDescriptor,
7284
- sweep: sweepOverlaid
7746
+ sweep: sweepOverlaid,
7747
+ // A new question (not a `refresh()`) asked over guesses an earlier frame
7748
+ // opened, which answer the old one (D-34, #4336). Stamped on `fam.asked`
7749
+ // with this flush's `clock`, so it lapses when the flush ends: `clock + 1`
7750
+ // from mainline over a live guess — a frame still reading a guess at the
7751
+ // seam shows it beside the new input, so the flush holds with the guess's
7752
+ // action (`park`); `~clock` from a lane with no guess of its own, or with
7753
+ // no live guess left — the lane waits for its answer (F-6), as for a
7754
+ // guessed `createOptimistic` (lanes `laneStage`). The guess's own lane
7755
+ // asks nothing new and stamps nothing.
7756
+ ask: fam => {
7757
+ if (fam.node._flags & REACTIVE_REASK || !fam.overlaid?.size) return;
7758
+ // Stamped with the flush (`clock`): it lapses when the flush ends.
7759
+ let a = ~clock;
7760
+ for (const g of fam.overlaid) {
7761
+ const n = laneNodeOf(g);
7762
+ if (n) {
7763
+ if (passLane === null) a = clock + 1;
7764
+ else if (sameAction(txOf(n), passLane)) return;
7765
+ }
7766
+ }
7767
+ if ((fam.asked = a) > 0) joinFuture(null);
7768
+ },
7769
+ // Over a new question the guesses do not answer (`asked`): from mainline
7770
+ // only a target carrying a guess stands in — a read of one with none is an
7771
+ // ordinary pending read (F-2), so a keyed store's other keys wait for their
7772
+ // answer, not for the guess's action; in a lane with no guess of its own,
7773
+ // or with no live guess left, none does (F-6).
7774
+ stands: t => {
7775
+ const o = t.fam.overlaid;
7776
+ const a = t.fam.asked;
7777
+ return !!o?.size && (a !== clock + 1 || passLane !== null || o.has(t)) && a !== ~clock;
7778
+ },
7779
+ // Mainline hold (`clock + 1`): the flush joins the action of each guess a
7780
+ // reader still reads at the seam; a reader that moved off it (a keyed
7781
+ // store's other key) holds nothing.
7782
+ park: t => {
7783
+ for (const fam of overlaidFamilies)
7784
+ if (fam.asked === clock + 1)
7785
+ for (const g of fam.overlaid) {
7786
+ const n = laneNodeOf(g, true);
7787
+ if (n) merge$1(txOf(n)._parent, t);
7788
+ }
7789
+ }
7285
7790
  });
7791
+ /** Two lanes of one action (their parents resolved through merges). */
7792
+ function sameAction(a, b) {
7793
+ return resolveTx(a._parent) === resolveTx(b._parent);
7794
+ }
7286
7795
  function shallowKeys(o) {
7287
7796
  const out = {};
7288
7797
  for (const k of Reflect.ownKeys(o)) if (k !== $OWNER) out[k] = unwrapValue(o[k]);
@@ -7304,7 +7813,7 @@ function sweepOverlaid() {
7304
7813
  for (const fam of overlaidFamilies) {
7305
7814
  const set = fam.overlaid;
7306
7815
  for (const t of set)
7307
- if (!carriesLane(t)) {
7816
+ if (!laneNodeOf(t)) {
7308
7817
  set.delete(t);
7309
7818
  if (t.ch) rebaseChained(t);
7310
7819
  // The container shows its backing again (a shown arrangement guess
@@ -7359,23 +7868,27 @@ function rebaseChained(t) {
7359
7868
  k._notifiedAt !== notifyEpoch
7360
7869
  ) {
7361
7870
  const rows = Array.isArray(inner) ? untrack(() => inner.map(unwrapValue)) : inner;
7362
- if (!arrangementEqual(k._value.rows, rows, t.fam?.key ?? null)) {
7871
+ if (!arrangementEqual(k._value.rows, rows, t.fam)) {
7363
7872
  insertSubs(k);
7364
7873
  changed = true;
7365
7874
  }
7366
7875
  }
7367
7876
  if (changed) schedule();
7368
7877
  }
7369
- function carriesLane(t) {
7370
- if (t.k !== null && t.k._config & CONFIG_OVERRIDE) return true;
7371
- if (t.dk !== null && t.dk._config & CONFIG_OVERRIDE) return true;
7372
- const nodes = t.n;
7373
- if (nodes !== null)
7374
- for (const key of Reflect.ownKeys(nodes)) if (nodes[key]._config & CONFIG_OVERRIDE) return true;
7375
- const has = t.h;
7376
- if (has !== null)
7377
- for (const key of Reflect.ownKeys(has)) if (has[key]._config & CONFIG_OVERRIDE) return true;
7378
- return false;
7878
+ /** `t`'s nodes (a missing container or deep node: null). */
7879
+ function nodesOf(t) {
7880
+ const all = [t.k, t.dk];
7881
+ for (const r of [t.n, t.h]) if (r !== null) for (const k of Reflect.ownKeys(r)) all.push(r[k]);
7882
+ return all;
7883
+ }
7884
+ /** A node of `t` carrying a lane value, if any (`read`: one a reader's
7885
+ * last pass read). */
7886
+ function laneNodeOf(t, read) {
7887
+ for (const n of nodesOf(t))
7888
+ if (n?._config & CONFIG_OVERRIDE) {
7889
+ if (!read) return n;
7890
+ for (let s = n._subs; s !== null; s = s._nextSub) if (s._gen === s._sub._depGen) return n;
7891
+ }
7379
7892
  }
7380
7893
  function overlaid(t) {
7381
7894
  const fam = t.fam;
@@ -8770,7 +9283,8 @@ function releaseMarkScope(carrier) {
8770
9283
  // A mark is a declared in-flight change: the marked node — and everything
8771
9284
  // derived from it — reads pending (`isPending` → true) from the declaration
8772
9285
  // until the surrounding transaction lands (an ambient mark, declared outside
8773
- // one, releases at the next seam). The marked values stay readable: no reader
9286
+ // one, releases at the next seam; either, on a node whose own flight is
9287
+ // still up then, at the seam after that flight lands). The marked values stay readable: no reader
8774
9288
  // suspends on a mark, a mark holds nothing and entangles nothing — it is not
8775
9289
  // a node of its transaction (`blocked` never sees it), and a reader of marked
8776
9290
  // data joins nothing through it. A mark is never a re-ask, so a declared
@@ -8845,30 +9359,42 @@ function invalid(detail) {
8845
9359
  emitDiagnostic({ code: "INVALID_AFFECTS_TARGET", kind: "write", severity: "error", message });
8846
9360
  throw new Error(message);
8847
9361
  }
8848
- installMarkHooks(release$1, parked => {
8849
- if (ambient.length === 0) return;
8850
- // The flush parked: the declaration's window is the frame's — the marks
8851
- // are its transaction's ("nothing async below ⇒ no window").
8852
- if (parked !== null) {
8853
- const list = (parked._marks ??= []);
8854
- for (let i = 0; i < ambient.length; i++) list.push(ambient[i]);
8855
- ambient.length = 0;
8856
- return;
9362
+ installMarkHooks(
9363
+ nodes => {
9364
+ // A landing's mark on a node whose own flight is up (a declared reload's
9365
+ // quiet re-ask the action did not wait for) stays to the seam after
9366
+ // that flight lands, as an ambient one does: the reload's window is the
9367
+ // flight's (#4349). The rest release with the transaction.
9368
+ const done = [];
9369
+ for (let i = 0; i < nodes.length; i++) (inFlight(nodes[i]) ? ambient : done).push(nodes[i]);
9370
+ nodes.length = 0;
9371
+ release$1(done);
9372
+ },
9373
+ parked => {
9374
+ if (ambient.length === 0) return;
9375
+ // The flush parked: the declaration's window is the frame's — the marks
9376
+ // are its transaction's ("nothing async below ⇒ no window").
9377
+ if (parked !== null) {
9378
+ const list = (parked._marks ??= []);
9379
+ for (let i = 0; i < ambient.length; i++) list.push(ambient[i]);
9380
+ ambient.length = 0;
9381
+ return;
9382
+ }
9383
+ // Nothing parked, but a marked node's own flight is up (a declared
9384
+ // reload's quiet re-ask — the node's, or a store leaf's family derive):
9385
+ // the window is the flight's; the mark stays to the seam after the
9386
+ // landing. The rest release: verdict-only, nothing to show.
9387
+ let kept = 0;
9388
+ const done = [];
9389
+ for (let i = 0; i < ambient.length; i++) {
9390
+ const n = ambient[i];
9391
+ if (inFlight(n)) ambient[kept++] = n;
9392
+ else done.push(n);
9393
+ }
9394
+ ambient.length = kept;
9395
+ if (done.length !== 0) release$1(done);
8857
9396
  }
8858
- // Nothing parked, but a marked node's own flight is up (a declared
8859
- // reload's quiet re-ask — the node's, or a store leaf's family derive):
8860
- // the window is the flight's; the mark stays to the seam after the
8861
- // landing. The rest release: verdict-only, nothing to show.
8862
- let kept = 0;
8863
- const done = [];
8864
- for (let i = 0; i < ambient.length; i++) {
8865
- const n = ambient[i];
8866
- if (inFlight(n)) ambient[kept++] = n;
8867
- else done.push(n);
8868
- }
8869
- ambient.length = kept;
8870
- if (done.length !== 0) release$1(done);
8871
- });
9397
+ );
8872
9398
  function inFlight(n) {
8873
9399
  return (
8874
9400
  (n._statusFlags & STATUS_PENDING) !== 0 ||
@@ -9410,27 +9936,37 @@ const boundaryOf = node => node._context[BOUNDARY];
9410
9936
  * has shown content, or while re-armed. */
9411
9937
  const isCollecting = b => b._type === STATUS_ERROR || !b._initialized || b._armed;
9412
9938
  /** A reader `b` waits on: recorded for its `on`, and `b` visited at the seam. */
9413
- function record(b, node) {
9414
- b._readers.add(node);
9939
+ function record(b, node, source = node) {
9940
+ b._readers.set(node, source);
9415
9941
  collecting.add(b);
9416
9942
  }
9417
9943
  /** The reader goes to the fallback: recorded, and the output re-derives
9418
9944
  * (a no-op while the output's own pass is running — its post-check
9419
9945
  * decides). */
9420
- function caught(b, node, error) {
9946
+ function caught(b, node, error, source = node) {
9421
9947
  if (b._type === STATUS_ERROR) {
9422
9948
  // An error boundary waits on what THREW (`StatusError.source`), not on
9423
- // the reader that saw it: the reader recovers the moment a refetch
9424
- // starts (its boundary catches the pending), while the error is settled
9425
- // only when its source is neither errored nor in flight. `reset` re-runs
9426
- // the same node.
9949
+ // the reader that saw it, while the error is settled only when its
9950
+ // source is neither errored nor in flight and the reader is not pending
9951
+ // on the answer that replaces it (`prune`) — or when the
9952
+ // reader is disposed: a source no live reader holds holds nothing
9953
+ // (#4373). A source that died (a memo its reader owned, kept past the
9954
+ // pass that disposed it) settles nothing: the reader is waited on.
9955
+ // `reset` re-runs the source.
9427
9956
  error ??= node._x?._error;
9428
- node = error?.source ?? node;
9957
+ source = error?.source ?? node;
9958
+ if (source._flags & REACTIVE_DISPOSED) source = node;
9429
9959
  const caught = unwrapStatusError(error);
9430
9960
  setSignal(b._error, caught);
9431
- reportClientError(caught, b._owner, node);
9432
- }
9433
- record(b, node);
9961
+ reportClientError(caught, b._owner, source);
9962
+ }
9963
+ record(b, node, source);
9964
+ // A loading fallback behind an error fallback is hidden: the error stays
9965
+ // the display until what the fallback waits on answers (D-39) — the
9966
+ // error boundary waits on it too.
9967
+ // (`_type`: STATUS_PENDING 1 < STATUS_ERROR 2 — an error boundary above a
9968
+ // loading one.)
9969
+ for (let p = b; (p = p._parent); ) if (p._fallback && p._type > b._type) record(p, node);
9434
9970
  if (b._armed) flip(b);
9435
9971
  // Already showing the fallback: one more reader to wait on changes
9436
9972
  // nothing on screen. The fallback is instantiated once per show — a later
@@ -9463,16 +9999,20 @@ function redraw(b) {
9463
9999
  function catchStatus(node, flags, error) {
9464
10000
  if (flags === 0) {
9465
10001
  // A status cleared. Judged by the node's status now, against each
9466
- // boundary's own rule (`unsettled`) — the error path clears pending
10002
+ // boundary's own rule (`prune`'s mask) — the error path clears pending
9467
10003
  // first and the node is still errored then. The last reader a fallback
9468
10004
  // waited on settling re-derives the output now, in the same heap (a
9469
10005
  // held reader's landing has joined its transaction, so the content
9470
10006
  // lands with its run, not a round after). Others still waiting: nothing
9471
- // on screen changes, the fallback stays as it is.
10007
+ // on screen changes, the fallback stays as it is. `node` here is an
10008
+ // effect reporting its own status. An error boundary records an effect
10009
+ // only while it is errored, and a staged error keeps STATUS_ERROR on a
10010
+ // node that had it (`stageError`), so `prune`'s CONFIG_STAGED_ERROR case
10011
+ // cannot reach this path (#4364).
9472
10012
  if (collecting.size !== 0)
9473
10013
  for (let b = boundaryOf(node); b; b = b._parent)
9474
10014
  if (
9475
- !(node._statusFlags & unsettled(b)) &&
10015
+ !(b._readers.get(node)?._statusFlags & (b._type | STATUS_PENDING)) &&
9476
10016
  b._readers.delete(node) &&
9477
10017
  b._readers.size === 0
9478
10018
  ) {
@@ -9541,16 +10081,28 @@ function release(b) {
9541
10081
  * that landed but is held (its value staged in a transaction) is not: the
9542
10082
  * content reveals at the commit, with its run. The tree is: the output reads
9543
10083
  * it and enters the transaction itself (its committed value shows until the
9544
- * landing). */
9545
- const unsettled = b => (b._type === STATUS_ERROR ? STATUS_ERROR | STATUS_PENDING : STATUS_PENDING);
10084
+ * landing). The statuses that keep a reader: pending — and its error, for an
10085
+ * error boundary (`_type` is the status it catches) — on the reader or its
10086
+ * source. A reader pending again after the source that threw answered (it
10087
+ * re-asks through a memo that carried the error) has no new answer yet: the
10088
+ * error stays the display (D-39: a pending is not a value; async-gen 91501
10089
+ * #1831). */
9546
10090
  function prune(b, pass) {
9547
- const mask = unsettled(b);
9548
- for (const r of b._readers) {
9549
- if (r._flags & REACTIVE_DISPOSED) b._readers.delete(r);
9550
- else if (!(r._statusFlags & mask)) {
10091
+ const mask = b._type | STATUS_PENDING;
10092
+ for (const [r, s] of b._readers) {
10093
+ if (!((r._flags | s._flags) & REACTIVE_DISPOSED)) {
10094
+ // An error staged on its source (D-41, `stageError`) is not settled:
10095
+ // it publishes with the commit. A reader that threw it now (a lane
10096
+ // pass, a reader of a node born staged) reached the boundary first
10097
+ // (#4364).
9551
10098
  if (
9552
- (r !== b._tree || b._armed) &&
9553
- (r._config & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD
10099
+ (r._statusFlags | s._statusFlags) & mask ||
10100
+ (mask & STATUS_ERROR && s._config & CONFIG_STAGED_ERROR)
10101
+ )
10102
+ continue;
10103
+ if (
10104
+ (s !== b._tree || b._armed) &&
10105
+ (s._config & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD
9554
10106
  ) {
9555
10107
  // Landed, held: the content is that transaction's — the output's
9556
10108
  // pass enters it and the reveal lands with the reader's run. The
@@ -9560,10 +10112,10 @@ function prune(b, pass) {
9560
10112
  // the reader is committed (#3540: what a fresh boundary catches holds
9561
10113
  // nothing outside it).
9562
10114
  if (!pass || !b._initialized) continue;
9563
- joinFuture(txOf(r));
10115
+ joinFuture(txOf(s));
9564
10116
  }
9565
- b._readers.delete(r);
9566
10117
  }
10118
+ b._readers.delete(r);
9567
10119
  }
9568
10120
  return b._readers.size;
9569
10121
  }
@@ -9595,12 +10147,17 @@ function boundarySeam() {
9595
10147
  if (flags & REACTIVE_ZOMBIE) continue;
9596
10148
  prune(b, false);
9597
10149
  const o = b._output;
10150
+ // A fallback staged into the very hold its readers landed in folds back
10151
+ // (D-16, U-9): the content is ready when that hold commits (D-15). Not
10152
+ // only at the re-arm's own seam: readers pending then land, held, a
10153
+ // flush later (#4379 — the staged fallback otherwise commits with the
10154
+ // hold, and a quiet refresh then caught keeps it up, F-9).
9598
10155
  if (
9599
- b._armed &&
9600
10156
  !b._initialized &&
9601
10157
  o._config & CONFIG_HELD &&
9602
10158
  [...b._readers].every(
9603
- r => !(r._statusFlags & STATUS_PENDING) && r._config & CONFIG_HELD && txOf(r) === txOf(o)
10159
+ ([, s]) =>
10160
+ !(s._statusFlags & STATUS_PENDING) && s._config & CONFIG_HELD && txOf(s) === txOf(o)
9604
10161
  )
9605
10162
  ) {
9606
10163
  b._readers.clear();
@@ -9636,7 +10193,7 @@ function reportOutsideHold(b) {
9636
10193
  for (let o = boundaryOf(reader); o; o = o._parent) if (o === b) return false;
9637
10194
  return true;
9638
10195
  };
9639
- for (const r of b._readers) {
10196
+ for (const r of b._readers.keys()) {
9640
10197
  const sources = r._x?._pendingSources;
9641
10198
  if (sources === undefined) continue;
9642
10199
  for (const source of sources) {
@@ -9707,7 +10264,7 @@ function arm(b) {
9707
10264
  collecting.add(b);
9708
10265
  const n = b._readers.size;
9709
10266
  heldUnder(b._owner, flushTransaction, r => record(b, r));
9710
- for (const r of b._readers) {
10267
+ for (const [r] of b._readers) {
9711
10268
  if (
9712
10269
  b._readers.size !== n ||
9713
10270
  (r._statusFlags & STATUS_PENDING && !(r._flags & REACTIVE_DISPOSED))
@@ -9717,7 +10274,9 @@ function arm(b) {
9717
10274
  // It is this boundary's alone now (B5, the nearest catcher): an outer
9718
10275
  // re-armed by the same tick does not wait for it (D-14, U-4).
9719
10276
  for (let a = b._parent; a !== null; a = a._parent)
9720
- for (const o of b._readers) a._readers.delete(o);
10277
+ // An outer error boundary keys its readers too, but waits on their
10278
+ // sources: a re-armed Loading taking the pending does not settle it.
10279
+ if (a._type & STATUS_PENDING) for (const [o] of b._readers) a._readers.delete(o);
9721
10280
  flip(b);
9722
10281
  redraw(b);
9723
10282
  return;
@@ -9728,10 +10287,32 @@ function arm(b) {
9728
10287
  * names a signal — nothing to re-run). */
9729
10288
  function reset(b) {
9730
10289
  // A snapshot: a source failing again is caught — and re-added — mid-walk.
9731
- for (const r of [...b._readers])
10290
+ for (const r of new Set(b._readers.values()))
9732
10291
  if (r._fn !== undefined && !(r._flags & REACTIVE_DISPOSED)) recompute(r);
9733
10292
  schedule();
9734
10293
  }
10294
+ /**
10295
+ * Dev CHECK: the `on` dependencies evaluated to a function — `<Loading
10296
+ * on={route}>` or `on={() => route()}` passes the accessor itself, a stable
10297
+ * value that is never called, so nothing is read and the boundary never
10298
+ * re-arms. Checked on the first run only: the shape is static.
10299
+ */
10300
+ function reportOnAccessor(b) {
10301
+ reportDiagnostic(
10302
+ emitDiagnostic(
10303
+ {
10304
+ code: "LOADING_ON_ACCESSOR",
10305
+ kind: "async",
10306
+ severity: "warn",
10307
+ message:
10308
+ "[LOADING_ON_ACCESSOR] A Loading boundary's `on` received a function. `on` takes the " +
10309
+ "dependency's value — `on={route()}`, or `on={[query(), page()]}` for several — and an " +
10310
+ "accessor passed instead is never called, so nothing is read and the boundary never re-arms."
10311
+ },
10312
+ b._owner
10313
+ )
10314
+ );
10315
+ }
9735
10316
  function createBoundary(type, fn, show, onFn) {
9736
10317
  if (!getOwner()) {
9737
10318
  const message =
@@ -9753,7 +10334,7 @@ function createBoundary(type, fn, show, onFn) {
9753
10334
  _owner: owner,
9754
10335
  _tree: null,
9755
10336
  _output: null,
9756
- _readers: new Set(),
10337
+ _readers: new Map(),
9757
10338
  _initialized: false,
9758
10339
  _armed: false,
9759
10340
  _armLane: null,
@@ -9783,7 +10364,8 @@ function createBoundary(type, fn, show, onFn) {
9783
10364
  runWithOwner(owner, () =>
9784
10365
  computed(() => {
9785
10366
  try {
9786
- onFn();
10367
+ const deps = onFn();
10368
+ if (true && !mounted && typeof deps === "function") reportOnAccessor(b);
9787
10369
  } catch (e) {
9788
10370
  if (!(e instanceof NotReadyError)) {
9789
10371
  if (!globalQueue.notify(getOwner(), STATUS_ERROR, STATUS_ERROR, e)) {
@@ -9954,12 +10536,14 @@ function createBoundary(type, fn, show, onFn) {
9954
10536
  return accessor(output);
9955
10537
  }
9956
10538
  function fallback(b) {
10539
+ // (The output's pass.)
10540
+ const out = getOwner();
9957
10541
  // The swap a display-ahead re-arm asked for: this pass is the lane's — a
9958
10542
  // lane read by construction (REACTIVE_LANE_READ: `recompute`'s tail keeps
9959
10543
  // the pass in the lane).
9960
10544
  if (b._lane !== null) {
9961
10545
  setPassLane(b._lane);
9962
- b._output._flags |= REACTIVE_LANE_READ;
10546
+ out._flags |= REACTIVE_LANE_READ;
9963
10547
  b._lane = null;
9964
10548
  }
9965
10549
  // Observe: a loading boundary starts showing its fallback — the swap lands
@@ -9987,11 +10571,18 @@ function fallback(b) {
9987
10571
  // outside the boundary, as the output is: its errors and runs are the
9988
10572
  // enclosing boundary's — with the output's id, so what it builds gets the
9989
10573
  // ids a build under the output would; disposed once content is on screen.
9990
- if (b._fb === null) {
9991
- const out = getOwner();
10574
+ if (b._fb === null)
9992
10575
  b._fb = runWithOwner(out._parent, () => computed(() => b._show(b), { id: out.id }));
9993
- }
9994
- return read(b._fb);
10576
+ // The swap is structure (#3540): what the fallback reads ahead — a
10577
+ // verdict (`latest()`), a lane — is the fallback's display-ahead, not the
10578
+ // swap's: the output's pass stays where it was (F-6 stays inside the
10579
+ // fallback, #4337).
10580
+ const p = passLane;
10581
+ const f = out._flags;
10582
+ const v = read(b._fb);
10583
+ setPassLane(p);
10584
+ out._flags ^= (out._flags ^ f) & (REACTIVE_PROBED | REACTIVE_LANE_READ);
10585
+ return v;
9995
10586
  }
9996
10587
  const ERROR_SIGNAL = { ownedWrite: true, _noSnapshot: true };
9997
10588
  /**