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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/dev-shared.js +553 -264
  2. package/dist/dev.attribution.js +18 -18
  3. package/dist/dev.js +1030 -439
  4. package/dist/observe/affects.js +12 -2
  5. package/dist/observe/boundaries.js +168 -115
  6. package/dist/observe/core/async.js +57 -37
  7. package/dist/observe/core/attribution.js +6 -6
  8. package/dist/observe/core/constants.js +9 -1
  9. package/dist/observe/core/copies.js +10 -0
  10. package/dist/observe/core/core.js +261 -181
  11. package/dist/observe/core/effect.js +32 -27
  12. package/dist/observe/core/graph.js +7 -7
  13. package/dist/observe/core/heap.js +29 -29
  14. package/dist/observe/core/lanes.js +301 -101
  15. package/dist/observe/core/owner.js +13 -13
  16. package/dist/observe/core/scheduler.js +124 -69
  17. package/dist/observe/core/truth.js +9 -7
  18. package/dist/observe/core/verdict.js +160 -98
  19. package/dist/observe/deferred.js +1 -1
  20. package/dist/observe/index.js +2 -0
  21. package/dist/observe/map.js +120 -116
  22. package/dist/observe/reveal.js +48 -48
  23. package/dist/observe/signals.js +5 -5
  24. package/dist/observe/store/affects.js +2 -2
  25. package/dist/observe/store/optimistic.js +272 -176
  26. package/dist/observe/store/projection.js +21 -10
  27. package/dist/observe/store/reconcile.js +8 -2
  28. package/dist/observe/store/store.js +91 -55
  29. package/dist/observe/store/types.js +11 -1
  30. package/dist/observe/store/utils.js +1 -1
  31. package/dist/prod/affects.js +12 -2
  32. package/dist/prod/boundaries.js +164 -109
  33. package/dist/prod/core/async.js +201 -181
  34. package/dist/prod/core/constants.js +9 -1
  35. package/dist/prod/core/copies.js +10 -0
  36. package/dist/prod/core/core.js +244 -164
  37. package/dist/prod/core/dev.js +108 -3
  38. package/dist/prod/core/effect.js +28 -23
  39. package/dist/prod/core/graph.js +9 -9
  40. package/dist/prod/core/heap.js +16 -16
  41. package/dist/prod/core/identities.js +1 -1
  42. package/dist/prod/core/lanes.js +307 -95
  43. package/dist/prod/core/owner.js +22 -22
  44. package/dist/prod/core/scheduler.js +266 -211
  45. package/dist/prod/core/truth.js +9 -7
  46. package/dist/prod/core/verdict.js +161 -99
  47. package/dist/prod/deferred.js +1 -1
  48. package/dist/prod/index.js +2 -0
  49. package/dist/prod/map.js +78 -74
  50. package/dist/prod/reveal.js +34 -34
  51. package/dist/prod/signals.js +3 -3
  52. package/dist/prod/store/affects.js +15 -15
  53. package/dist/prod/store/optimistic.js +185 -89
  54. package/dist/prod/store/projection.js +49 -38
  55. package/dist/prod/store/reconcile.js +8 -2
  56. package/dist/prod/store/store.js +99 -63
  57. package/dist/prod/store/types.js +11 -1
  58. package/dist/types/affects.d.ts +3 -2
  59. package/dist/types/boundaries.d.ts +14 -2
  60. package/dist/types/core/async.d.ts +2 -1
  61. package/dist/types/core/constants.d.ts +8 -0
  62. package/dist/types/core/copies.d.ts +54 -0
  63. package/dist/types/core/core.d.ts +15 -0
  64. package/dist/types/core/dev.d.ts +1 -1
  65. package/dist/types/core/lanes.d.ts +8 -1
  66. package/dist/types/core/scheduler.d.ts +36 -4
  67. package/dist/types/core/truth.d.ts +3 -1
  68. package/dist/types/core/types.d.ts +6 -0
  69. package/dist/types/index.d.ts +2 -0
  70. package/dist/types/store/hooks.d.ts +15 -1
  71. package/dist/types/store/store.d.ts +2 -1
  72. package/dist/types/store/target.d.ts +5 -0
  73. package/dist/types/store/types.d.ts +7 -0
  74. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
- import { CONFIG_VERDICT, REACTIVE_PROBED, CONFIG_CHILDREN_FORBIDDEN, CONFIG_OVERRIDE, STATUS_ERROR, CONFIG_GUESS, STATUS_PENDING, NOT_PENDING, STATUS_UNINITIALIZED, CONFIG_HELD, EFFECT_RENDER, CONFIG_STAGED_ERROR, REACTIVE_STAGED_READ, REACTIVE_PROBE_UNANSWERED, CONFIG_INPUTS_PUBLISHED, REACTIVE_JOINED, REACTIVE_LANE_READ } from "./constants.js";
1
+ import { REACTIVE_PROBED, REACTIVE_WINDOW, CONFIG_VERDICT, CONFIG_ASYNC, CONFIG_CHILDREN_FORBIDDEN, CONFIG_OVERRIDE, STATUS_ERROR, CONFIG_GUESS, STATUS_PENDING, NOT_PENDING, STATUS_UNINITIALIZED, CONFIG_HELD, EFFECT_RENDER, CONFIG_STAGED_ERROR, REACTIVE_STAGED_READ, REACTIVE_PROBE_UNANSWERED, REACTIVE_REASK, CONFIG_INPUTS_PUBLISHED, REACTIVE_JOINED, REACTIVE_LANE_READ } from "./constants.js";
2
2
 
3
- import { context, tracking, setVerdict, verdict, pullComputed, joinPass, markLateLinker, spectating, unflushedValue, bornInto } from "./core.js";
3
+ import { context, creatorPass, tracking, creatorReads, setVerdict, verdict, prepareComputed, pullComputed, joinPass, markLateLinker, spectating, unflushedValue, bornInto } from "./core.js";
4
4
 
5
5
  import "./dev.js";
6
6
 
@@ -40,7 +40,7 @@ function watchVerdict(e, t = false) {
40
40
  * node's, or the staging of a write this flush may hold — and the verdict
41
41
  * does not rescue it: its result shows at the landing, re-derived). A lane
42
42
  * read, for `recompute`'s tail (REACTIVE_LANE_READ). */ function route(e, t) {
43
- if (passLane === null && !(e.R & (REACTIVE_JOINED | REACTIVE_STAGED_READ))) setPassLane(verdictLane(t));
43
+ if (!passLane && !(e.R & (REACTIVE_JOINED | REACTIVE_STAGED_READ))) setPassLane(verdictLane(t));
44
44
  e.R |= REACTIVE_LANE_READ;
45
45
  }
46
46
 
@@ -67,7 +67,7 @@ function watchVerdict(e, t = false) {
67
67
  * transaction (`bornInto`, #4010): it has no screen — it waits on the flight
68
68
  * with the rest of its frame. */ function observeFlight(e, t) {
69
69
  if (t.l & CONFIG_INPUTS_PUBLISHED || t.l & CONFIG_HELD && bornInto(e, txOf(t))) return false;
70
- if (globalQueue.q) {
70
+ if (globalQueue.Y) {
71
71
  joinFuture(null);
72
72
  if (!(t.l & CONFIG_HELD)) queuePendingNode(t);
73
73
  verdictRead(e, flushTransaction, true);
@@ -92,9 +92,9 @@ let probeFound = false;
92
92
  * value notifying anyone (a held write's commit; a held landing's): its
93
93
  * reader re-derives there. `latest` shows the proposal that lands — a
94
94
  * rewrite or a landing reaches its reader through the link it holds — so a
95
- * re-run would only repeat the frame. */ function verdictRead(e, t, n) {
95
+ * re-run would only repeat the frame. */ function verdictRead(e, t, r) {
96
96
  route(e, t);
97
- if (n) staleReader(e, t); else if (probing && t.En[t.En.length - 1] !== e) t.En.push(e);
97
+ if (r) staleReader(e, t); else if (probing && t.En[t.En.length - 1] !== e) t.En.push(e);
98
98
  }
99
99
 
100
100
  /** A verdict read of a node staged or in flight but not held yet: whether it
@@ -107,39 +107,40 @@ let probeFound = false;
107
107
  * watches the seam and runs again if the flush parks (a plain sync write
108
108
  * never glitches it true).
109
109
  *
110
- * `latest` has its answer either way — the proposal — and only the reader's
111
- * membership was open, so it is settled now: the frame gets its transaction
112
- * (`joinFuture(null)`; nothing holds it at the seam and it lands there, the
113
- * lane dissolving into it — a plain commit) and the reader is its verdict
114
- * lane's. One pass: a provisional pass re-derived at the seam ran everything
115
- * downstream of it twice (#3540 web). A pass that also read the frame's own
116
- * stagings plainly (REACTIVE_STAGED_READ) stays the frame's (`route` does
117
- * not move it); a verdict reader's plain read AFTER the window sees the
118
- * screen (`read`: `stagedScreen`). */ function provisionalVerdict(e, t) {
119
- if (!probing) {
120
- joinFuture(null);
121
- verdictRead(e, flushTransaction);
122
- return;
123
- }
124
- if (flushTransaction !== null) {
125
- if (t) probeFound = true;
110
+ * `latest` has its answer either way — the proposal — but its reader's
111
+ * membership is the seam's too: the frame opens no transaction for it
112
+ * (#4365 — one opened for the read alone made a plain read after the window
113
+ * take it for a hold, `stagedScreen`, and serve the screen beside the
114
+ * staging in a frame nothing held). The reader watches the seam like a
115
+ * probe before anything joined: its pass is the frame's, and runs again in
116
+ * the verdict lane if the flush parks. Once the flush has joined a
117
+ * transaction, the reader is its verdict lane's at once; a pass that also
118
+ * read the frame's own stagings plainly (REACTIVE_STAGED_READ) stays the
119
+ * frame's (`route` does not move it). */ function provisionalVerdict(e, t) {
120
+ if (flushTransaction) {
121
+ if (probing && t) probeFound = true;
126
122
  verdictRead(e, flushTransaction);
127
- if (e.R & REACTIVE_STAGED_READ) watchVerdict(e);
123
+ if (probing && e.R & REACTIVE_STAGED_READ) watchVerdict(e);
128
124
  return;
129
125
  }
130
126
  // Before anything joined, a staging that would not be final held has no
131
127
  // answer yet: whatever the seam decides, the pass runs again — a later
132
128
  // plain read of the same pass may route it into the verdict lane
133
129
  // (`observeFlight`), which the seam must not take as this probe's answer.
134
- watchVerdict(e, t);
130
+ // `latest` opens no transaction for its answer either (#4365): the plain
131
+ // read of a staging after it took one opened for it for a hold and paired
132
+ // the staging with the screen, in a frame nothing held. Its pass is the
133
+ // frame's — whole — and re-derives once if the flush parks (unanswered:
134
+ // its membership is the seam's, not the transaction it joins).
135
+ watchVerdict(e, !probing || t);
135
136
  }
136
137
 
137
138
  /** A19 exc. 2 / A24: a flight that re-asks the question already answered
138
139
  * (a `refresh()`, a poll) is quiet; a node pending through others is quiet
139
140
  * when every flight it waits on is. */ function quietPending(e) {
140
141
  const t = e.T;
141
- if (t === null) return false;
142
- if (t.se !== undefined) {
142
+ if (!t) return false;
143
+ if (t.se) {
143
144
  for (const e of t.se) if (!e.T?.ut) return false;
144
145
  return true;
145
146
  }
@@ -159,16 +160,24 @@ let probeFound = false;
159
160
  * flight — a staging equal to what is displayed changes nothing when it
160
161
  * lands: final now (maintainer, 2026-10-02). */ function heldNotFinal(e) {
161
162
  if ((e.m & STATUS_PENDING) !== 0 && !quietPending(e)) return true;
162
- return e.ie !== NOT_PENDING && !latestActive && !e.T.ut && !(e.m & STATUS_UNINITIALIZED) && (!!e.T.Te || !e.je || !e.je(e.le, e.ie));
163
+ return e.ue !== NOT_PENDING && !latestActive && !e.T.ut && !(e.m & STATUS_UNINITIALIZED) && (!!e.T.Te || !e.je || !e.je(e.le, e.ue));
163
164
  }
164
165
 
165
166
  /** `isPending`'s answer for an unheld node whose flight is up (cause ii). */ function pendingVerdict(e, t) {
166
167
  if (t.m & STATUS_UNINITIALIZED) return;
167
168
  if (probing && !quietPending(t)) probeFound = true;
168
- if (e !== null && tracking) {
169
- if (t.l & CONFIG_HELD) verdictRead(e, txOf(t)); else if (globalQueue.q) provisionalVerdict(e, false);
169
+ if (e && tracking) {
170
+ if (t.l & CONFIG_HELD) verdictRead(e, txOf(t)); else if (globalQueue.Y) {
171
+ // `latest` of a flight serves the committed value, the frame's and the
172
+ // lane's alike (D-22): nothing is the seam's, so the reader is the
173
+ // verdict lane's now, as a plain read of a flight makes it
174
+ // (`observeFlight`). Its frame pass re-derived in the lane (R5) asked
175
+ // its question again (F-9, #4397). A staging's is the seam's (#4365).
176
+ if (!probing) joinFuture(null);
177
+ provisionalVerdict(e, false);
170
178
  // decided above
171
- }
179
+ }
180
+ }
172
181
  }
173
182
 
174
183
  /** The pass entering a verdict window is a verdict reader (CONFIG_VERDICT):
@@ -176,11 +185,19 @@ let probeFound = false;
176
185
  * an untracked window: that is a snapshot, like any untracked read (#3946 —
177
186
  * a router's link claim probing inside the computation that creates the
178
187
  * anchor). A throw still reaches the pass through `untrack`. */ function markVerdictReader(e) {
179
- let t = context;
180
- if (t?.Pt) t = t.ht;
181
- if (t !== null && tracking) {
182
- t.l |= CONFIG_VERDICT;
183
- t.R |= REACTIVE_PROBED;
188
+ const t = creatorPass(context);
189
+ if (t && tracking) {
190
+ // A pass run inside passes still running — its creator's, theirs — one
191
+ // of which read the future or a staging (a branch a held or staged
192
+ // condition opened, and whatever that branch creates) has no screen: the
193
+ // branch exists only in that world. It reads that world as any
194
+ // derivation of it does (`bornInto` once held) and stays its pass
195
+ // (`route`); a verdict reader from its next pass. #4341: a memo born in
196
+ // a newly opened <Match> read the screen, where its branch was closed.
197
+ // Not such a pass: a verdict reader now.
198
+ t.l &= ~CONFIG_ASYNC;
199
+ t.R |= REACTIVE_PROBED | REACTIVE_WINDOW | (creatorReads(t, 0) || (t.l |= CONFIG_VERDICT,
200
+ 0));
184
201
  // Observe tier: which windows this pass entered (1 `isPending`, 2
185
202
  // `latest`) — the hold census names the affordance (attribution.ts).
186
203
  t.Ve = (t.Ve ?? 0) | e;
@@ -191,14 +208,14 @@ let probeFound = false;
191
208
  * restores the dispatch it found — none inside a memo's pass a window
192
209
  * pulled (`verdictValue`). */ function latest(e) {
193
210
  markVerdictReader(2);
194
- const t = latestActive, n = verdict;
211
+ const t = latestActive, r = verdict;
195
212
  latestActive = true;
196
213
  setVerdict(verdictValue);
197
214
  try {
198
215
  return e();
199
216
  } finally {
200
217
  latestActive = t;
201
- setVerdict(n);
218
+ setVerdict(r);
202
219
  }
203
220
  }
204
221
 
@@ -236,8 +253,8 @@ let probeFound = false;
236
253
  */ function isPending(e) {
237
254
  markVerdictReader(1);
238
255
  const t = probing;
239
- const n = probeFound;
240
- const r = verdict;
256
+ const r = probeFound;
257
+ const i = verdict;
241
258
  probing = true;
242
259
  probeFound = false;
243
260
  setVerdict(verdictValue);
@@ -255,23 +272,23 @@ let probeFound = false;
255
272
  return probeFound;
256
273
  } finally {
257
274
  probing = t;
258
- probeFound = n;
259
- setVerdict(r);
275
+ probeFound = r;
276
+ setVerdict(i);
260
277
  }
261
278
  }
262
279
 
263
280
  /** The staging, or the staged error thrown (D-41). */ function staged(e) {
264
- if (e.l & CONFIG_STAGED_ERROR) throw e.ie;
265
- return e.ie;
281
+ if (e.l & CONFIG_STAGED_ERROR) throw e.ue;
282
+ return e.ue;
266
283
  }
267
284
 
268
285
  /** `latest()` of a held node: the flushed staging — the proposal — and, for
269
286
  * a tracked reader of a rewrite not yet flushed, the late-linker re-run in
270
287
  * the flush that carries it. */ function heldLatest(e, t) {
271
- const n = unflushedValue(e);
272
- if (n === NOT_PENDING) return staged(e);
273
- if (t !== null) markLateLinker(t);
274
- return n;
288
+ const r = unflushedValue(e);
289
+ if (r === NOT_PENDING) return staged(e);
290
+ if (t) markLateLinker(t);
291
+ return r;
275
292
  }
276
293
 
277
294
  /** The verdict windows' read — `latest(fn)` and `isPending(fn)` — in one
@@ -284,7 +301,9 @@ let probeFound = false;
284
301
  * differs (A24, A18 provenance). A derivation: `latest` sees the lane's
285
302
  * latest (a landing a blocked lane holds has answered its question — the
286
303
  * checkout's shipping text updates independently of the tax in flight),
287
- * `isPending` the screen; not final while its own flight is up;
304
+ * `isPending` the screen; not final while its own flight is up. A
305
+ * derivation's first load in flight (nothing produced yet — an async memo
306
+ * over a verdict memo, F-6) reads as the flight below: loading (D-22);
288
307
  * - a held node: the reader breaks out as the holder's verdict lane's and
289
308
  * re-derives at the landing; `latest` serves the proposal (`heldLatest`),
290
309
  * `isPending` answers `heldNotFinal`;
@@ -295,63 +314,76 @@ let probeFound = false;
295
314
  * serves the committed value — below the async nothing newer exists; an
296
315
  * uninitialized one throws NotReady: loading, not pending (A19 exc. 1);
297
316
  * - an errored derivation throws for every reader (#2897). */ function verdictValue(e, t) {
298
- const n = e;
299
- const r = t !== null && !(t.l & CONFIG_CHILDREN_FORBIDDEN);
300
- const i = r && tracking;
301
- if (t !== null && tracking) {
317
+ const r = e;
318
+ const i = !!t && !(t.l & CONFIG_CHILDREN_FORBIDDEN);
319
+ const n = i && tracking;
320
+ // A31: the pulled memo's pass is its own, not the probe's — it reads
321
+ // outside the window, and the probe's read below answers for what the pass
322
+ // produced. So is a lazy or dormant memo's wake (an unowned memo with no
323
+ // reader left, #4366): woken inside the window, it read its sources as the
324
+ // probe does, the committed value under a landing. (A signal has no
325
+ // `_flags`: the wake is a no-op for it.)
326
+ setVerdict(null);
327
+ prepareComputed(r);
328
+ if (t && tracking) {
302
329
  link(e, t);
303
- if (n.X !== undefined) {
304
- // A31: the pulled memo's pass is its own, not the probe's — it reads
305
- // outside the window, and the probe's read below answers for what
306
- // the pass produced.
307
- setVerdict(null);
308
- pullComputed(n, t);
309
- setVerdict(verdictValue);
310
- }
330
+ if (e.X) pullComputed(r, t);
311
331
  }
332
+ setVerdict(verdictValue);
312
333
  // A live `affects()` mark on the node or a dependency (affects.ts): not
313
334
  // final by declaration — the value itself is read as below. The verdict is
314
335
  // display-ahead like any: in a transaction's flush (the declaring action's
315
336
  // body) the reader is its verdict lane's, not a staging the frame parks.
316
- if (probing && GlobalQueue.p !== undefined && GlobalQueue.p(n)) {
337
+ if (probing && GlobalQueue.p?.(r)) {
317
338
  probeFound = true;
318
- if (i && flushTransaction !== null) verdictRead(t, flushTransaction);
339
+ if (n && flushTransaction) verdictRead(t, flushTransaction);
319
340
  }
320
- if (e.l & CONFIG_OVERRIDE && !(n.m & STATUS_ERROR && !(e.l & CONFIG_GUESS))) {
321
- const r = txOf(e);
322
- if (i) verdictRead(t, r._parent ?? r);
323
- const u = (n.m & STATUS_PENDING) !== 0 && !quietPending(n);
324
- if (e.l & CONFIG_GUESS) {
325
- if (probing && (u || e.ie !== NOT_PENDING && (!e.je || !e.je(e.T.V, e.ie)))) probeFound = true;
326
- // A lane a correction folded under (D-34): `latest()` shows the hold's
327
- // world whole — the truth beneath each guess, else what it covered.
328
- if (latestActive && r.folded) return r.folded(null, e);
329
- return e.T.V;
341
+ if (e.l & CONFIG_OVERRIDE && !(r.m & STATUS_ERROR && !(e.l & CONFIG_GUESS))) {
342
+ const i = txOf(e), o = (r.m & STATUS_PENDING) !== 0 && !quietPending(r), a = e.l & CONFIG_GUESS,
343
+ // NOT_PENDING: a node born in the lane with nothing in it yet has no
344
+ // value in either view — loading, not pending, and no verdict to read
345
+ // ahead: it is read as any uninitialized node, NotReady below (A7, A19
346
+ // exc. 1; #4363), its reader not moved into the verdict lane.
347
+ u = a ? 0 : latestActive && !o ? laneValueOf(e) : display(e);
348
+ if (u !== NOT_PENDING) {
349
+ if (n) verdictRead(t, i._parent ?? i);
350
+ if (a) {
351
+ if (probing && (o || e.ue !== NOT_PENDING && (!e.je || !e.je(e.T.V, e.ue)))) probeFound = true;
352
+ // A lane a correction folded under (D-34): `latest()` shows the hold's
353
+ // world whole — the truth beneath each guess, else what it covered.
354
+ if (latestActive && i.folded) return i.folded(null, e);
355
+ return e.T.V;
356
+ }
357
+ // A first load in flight with nothing produced yet: loading, for
358
+ // `latest` (D-22) as for `isPending` (A19 exc. 1) — unless an error
359
+ // answered it first: its re-ask is pending, the error its display
360
+ // (D-41, #4375).
361
+ if (o && u === e.le && r.m & STATUS_UNINITIALIZED && (latestActive || !r.T.Te)) throw r.T.U;
362
+ if (probing && o) probeFound = true;
363
+ return u;
330
364
  }
331
- if (probing && u) probeFound = true;
332
- return latestActive && !u ? laneValueOf(e) : display(e);
333
365
  }
334
- const u = (n.m & STATUS_UNINITIALIZED) !== 0;
366
+ const o = (r.m & STATUS_UNINITIALIZED) !== 0;
335
367
  if (e.l & CONFIG_HELD) {
336
- if (!u) {
337
- const r = txOf(e);
368
+ if (!o) {
369
+ const i = txOf(e);
338
370
  // A render effect re-run (or mounted) outside the verdict lane's flush
339
371
  // while the lane is blocked — its display held on its own derivation
340
372
  // in flight — is a stale reader of the lane (#3460, as `laneRead`): the
341
373
  // committed value now, re-derived at the reveal. Not inside a probe:
342
374
  // `isPending` is answered below either way.
343
- if (i && !probing && t.i === EFFECT_RENDER && passLane === null && r.it !== null && r.it.ln && flushTransaction !== r) {
344
- staleReader(t, r.it);
375
+ if (n && !probing && t.i === EFFECT_RENDER && !passLane && i.it?.ln && flushTransaction !== i) {
376
+ staleReader(t, i.it);
345
377
  return e.le;
346
378
  }
347
- if (i) verdictRead(t, r);
348
- if (probing && heldNotFinal(n)) probeFound = true;
349
- if (latestActive && e.ie !== NOT_PENDING) {
379
+ if (n) verdictRead(t, i);
380
+ if (probing && heldNotFinal(r)) probeFound = true;
381
+ if (latestActive && e.ue !== NOT_PENDING) {
350
382
  // An action body reading another transaction's proposal derives
351
383
  // from it: the two settle as one (A15; posture C, 2026-09-15). An
352
384
  // untracked read elsewhere has no transaction to entangle.
353
- if (!i && flushTransaction !== null && !globalQueue.q) joinFuture(txOf(e));
354
- return heldLatest(e, i ? t : null);
385
+ if (!n && flushTransaction && !globalQueue.Y) joinFuture(txOf(e));
386
+ return heldLatest(e, n ? t : null);
355
387
  }
356
388
  return e.le;
357
389
  }
@@ -359,46 +391,76 @@ let probeFound = false;
359
391
  // committed value, so no verdict: `isPending` reads it as its read does —
360
392
  // the owner joins the hold (held with it, or caught by a boundary that
361
393
  // has not shown content) and answers for the commit, where it is final.
362
- if (e.ie !== NOT_PENDING && !(n.m & STATUS_PENDING)) {
363
- if (probing && r) joinPass(t, e);
394
+ if (e.ue !== NOT_PENDING && !(r.m & STATUS_PENDING)) {
395
+ if (probing && i) joinPass(t, e);
364
396
  return staged(e);
365
397
  }
366
- } else if (e.ie !== NOT_PENDING) {
367
- if (n.m & STATUS_PENDING && !u) pendingVerdict(i ? t : null, n); else if (i && globalQueue.q) provisionalVerdict(t, !latestActive && !u);
398
+ } else if (e.ue !== NOT_PENDING) {
399
+ if (r.m & STATUS_PENDING && !o) pendingVerdict(n ? t : null, r); else if (n && globalQueue.Y) provisionalVerdict(t, !latestActive && !o);
368
400
  // Outside a flush, the staging is an unflushed write (A28): the probe
369
401
  // answers for the flushed world, and watches the seam of the flush that
370
402
  // carries the write — a memo of `isPending(x)` created after the write
371
403
  // flips if that flush parks (#3078). (The write's own fan-out ran before
372
404
  // the reader existed; nothing else would re-derive it.)
373
- else if (i && probing) watchVerdict(t);
405
+ else if (n && probing) watchVerdict(t);
374
406
  if (latestActive) {
375
407
  // A28: an unflushed write is not what `latest` serves; a tracked
376
408
  // reader derived from the flushed world runs again in the carrying
377
409
  // flush.
378
- if (globalQueue.q) return staged(e);
379
- if (t !== null) markLateLinker(t);
410
+ if (globalQueue.Y) return staged(e);
411
+ if (t) markLateLinker(t);
380
412
  }
381
413
  return e.le;
382
414
  }
383
- if (n.m & STATUS_PENDING) {
384
- if (!u) {
385
- pendingVerdict(i ? t : null, n);
415
+ if (r.m & STATUS_PENDING) {
416
+ if (!o) {
417
+ pendingVerdict(n ? t : null, r);
386
418
  return e.le;
387
419
  }
388
420
  // As `read()`: an untracked read of a pending node still re-runs its
389
421
  // reader when the node settles (a projection's pull reads its firewall
390
422
  // untracked, store/store.ts).
391
- if (t !== null && !tracking && !spectating && e !== t) link(e, t);
392
- throw n.T?.F;
423
+ if (t && !tracking && !spectating && e !== t) link(e, t);
424
+ throw r.T?.U;
393
425
  }
394
- if (n.X !== undefined && n.m & STATUS_ERROR) throw n.T.F;
426
+ if (e.X && r.m & STATUS_ERROR) throw r.T.U;
395
427
  return e.le;
396
428
  }
397
429
 
430
+ /** D-25: a node whose fetch a hold is waiting on is that hold's work, not a
431
+ * verdict reader — a pass that re-runs it reads plainly — and a memo's
432
+ * posture is what it reads: one that reads a verdict reader's value is a
433
+ * verdict reader itself (#4050). An async memo with no verdict window of its
434
+ * own (REACTIVE_WINDOW: it read a verdict memo, it called neither `latest`
435
+ * nor `isPending`) is a verdict reader between its flights, and the hold's
436
+ * work while one is up or its hold re-runs it.
437
+ *
438
+ * At a pass's head (core.ts `recompute`): a pending node's pass — its
439
+ * flight is the transition's own work (#3884) — and a held one's re-run of
440
+ * such a memo read plainly: the write that re-ran it is its hold's, and
441
+ * served the screen it re-asked a question it had answered (A10, #4397).
442
+ * Only a memo that never asked: D-25's "a verdict posture outlives the pass
443
+ * that asked" is a pass that called `latest`/`isPending`, which clears
444
+ * CONFIG_ASYNC and stays a verdict reader whoever dirties it.
445
+ *
446
+ * On `source` going pending (`propagateStatus`): such a memo waits on it
447
+ * like any derivation (F-9) instead of re-deriving — what it read changed
448
+ * no value, so its re-run re-asked the question it was answering. Unless
449
+ * its own flight is up outside a lane on a new question of the source's:
450
+ * its re-run supersedes a flight asked of a value the source has moved past
451
+ * (re-asked by a `refresh()`, the source's question is the same one). */ function holdWork(e, t) {
452
+ // CONFIG_ASYNC records an async memo with no window of its own: its last
453
+ // answering pass registered a flight (`handleAsync`) and called neither
454
+ // `latest` nor `isPending`. A window (`markVerdictReader`), a sync answer or
455
+ // an error clears it; a pass left pending keeps the last answer's.
456
+ const r = e.l & CONFIG_ASYNC;
457
+ return r && (!(e.m & STATUS_PENDING) || e.T.Je?.V || t.R & REACTIVE_REASK);
458
+ }
459
+
398
460
  // Installed at module evaluation — present exactly when something imports
399
461
  // `latest` or `isPending`. (`read` dispatches to `verdictValue` through
400
462
  // `verdict`, set while a window is open.)
401
- installVerdictHooks(observeFlight);
463
+ installVerdictHooks(observeFlight, holdWork);
402
464
 
403
465
  /** A store read inside an untracked `isPending` probe found a live mark on
404
466
  * a record with no node to carry it (store/affects.ts): not final. */ GlobalQueue.bt = () => {
@@ -123,7 +123,7 @@ GlobalQueue.er = e => lagging.size !== 0 ? lagOf(e, new Set) : undefined;
123
123
  * does not replace (D-40) — restaged as the committed value, the hold's
124
124
  * readers would re-derive against the old input. */ const serve = e => {
125
125
  const r = handleAsync(t, e);
126
- return t.l & CONFIG_HELD && t.ie !== NOT_PENDING ? t.ie : r;
126
+ return t.l & CONFIG_HELD && t.ue !== NOT_PENDING ? t.ue : r;
127
127
  };
128
128
  const end = () => t.R & REACTIVE_DISPOSED && land(o);
129
129
  return accessor(computed(r => {
@@ -26,6 +26,8 @@ import "./core/effect.js";
26
26
 
27
27
  export { configureClientErrors } from "./core/error-hooks.js";
28
28
 
29
+ export { checkForeignOwnerless, runAsSolidCopy } from "./core/copies.js";
30
+
29
31
  export { storeName } from "./store/edges.js";
30
32
 
31
33
  export { createEffect, createMemo, createOptimistic, createReaction, createRenderEffect, createSignal, createTrackedEffect, onCleanup, onFlush, onSettled, refresh, resolve, until } from "./signals.js";