@solidjs/signals 2.0.0-rc.4 → 2.0.0-rc.6

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 (57) hide show
  1. package/dist/dev.js +1618 -232
  2. package/dist/node.cjs +2262 -1199
  3. package/dist/prod/boundaries.js +4 -1
  4. package/dist/prod/core/async.js +152 -101
  5. package/dist/prod/core/constants.js +55 -1
  6. package/dist/prod/core/core.js +305 -234
  7. package/dist/prod/core/effect.js +28 -28
  8. package/dist/prod/core/error.js +13 -1
  9. package/dist/prod/core/external.js +2 -2
  10. package/dist/prod/core/graph.js +27 -27
  11. package/dist/prod/core/heap.js +30 -30
  12. package/dist/prod/core/lanes.js +32 -32
  13. package/dist/prod/core/optimistic.js +57 -57
  14. package/dist/prod/core/owner.js +34 -34
  15. package/dist/prod/core/scheduler.js +346 -152
  16. package/dist/prod/core/verdict.js +122 -65
  17. package/dist/prod/index.js +3 -3
  18. package/dist/prod/map.js +106 -106
  19. package/dist/prod/signals.js +321 -26
  20. package/dist/prod/store/next/optimistic.js +363 -151
  21. package/dist/prod/store/next/patch.js +6 -6
  22. package/dist/prod/store/next/projection.js +25 -21
  23. package/dist/prod/store/next/store.js +223 -113
  24. package/dist/prod/store/store.js +2 -2
  25. package/dist/types/core/async.d.ts +2 -0
  26. package/dist/types/core/attribution-hooks.d.ts +11 -0
  27. package/dist/types/core/attribution.d.ts +57 -4
  28. package/dist/types/core/constants.d.ts +54 -0
  29. package/dist/types/core/core.d.ts +19 -20
  30. package/dist/types/core/dev.d.ts +8 -2
  31. package/dist/types/core/error.d.ts +9 -0
  32. package/dist/types/core/index.d.ts +2 -2
  33. package/dist/types/core/scheduler.d.ts +39 -0
  34. package/dist/types/core/types.d.ts +12 -0
  35. package/dist/types/index.d.ts +3 -3
  36. package/dist/types/signals.d.ts +108 -11
  37. package/dist/types/store/next/optimistic.d.ts +13 -10
  38. package/dist/types/store/next/projection.d.ts +1 -1
  39. package/dist/types/store/next/store.d.ts +22 -0
  40. package/dist/types/store/next/target.d.ts +25 -0
  41. package/dist/types-cjs/core/async.d.cts +2 -0
  42. package/dist/types-cjs/core/attribution-hooks.d.cts +11 -0
  43. package/dist/types-cjs/core/attribution.d.cts +57 -4
  44. package/dist/types-cjs/core/constants.d.cts +54 -0
  45. package/dist/types-cjs/core/core.d.cts +19 -20
  46. package/dist/types-cjs/core/dev.d.cts +8 -2
  47. package/dist/types-cjs/core/error.d.cts +9 -0
  48. package/dist/types-cjs/core/index.d.cts +2 -2
  49. package/dist/types-cjs/core/scheduler.d.cts +39 -0
  50. package/dist/types-cjs/core/types.d.cts +12 -0
  51. package/dist/types-cjs/index.d.cts +3 -3
  52. package/dist/types-cjs/signals.d.cts +108 -11
  53. package/dist/types-cjs/store/next/optimistic.d.cts +13 -10
  54. package/dist/types-cjs/store/next/projection.d.cts +1 -1
  55. package/dist/types-cjs/store/next/store.d.cts +22 -0
  56. package/dist/types-cjs/store/next/target.d.cts +25 -0
  57. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import { NOT_PENDING, REACTIVE_DISPOSED, REACTIVE_DIRTY, REACTIVE_CHECK, unwrapOverride, REACTIVE_ZOMBIE, STATUS_UNINITIALIZED, STATUS_PENDING, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_MANUAL_WRITE, CONFIG_HAS_COMPANIONS, CONFIG_CHILD_COMPANIONS, STATUS_ERROR, REACTIVE_RECOMPUTING_DEPS } from "./constants.js";
2
2
 
3
- import { setSignal, prepareComputed, read, context, stale, currentOptimisticLane, tracking, ext, setLatestReadActive, setContextInternal, optimisticComputed, setPendingCheckActive, latestReadActive, optimisticSignal, pendingCheckActive } from "./core.js";
3
+ import { setSignal, pendingCheckActive, setPendingCheckActive, prepareComputed, read, context, stale, currentOptimisticLane, tracking, ext, setLatestReadActive, setContextInternal, optimisticComputed, latestReadActive, optimisticSignal } from "./core.js";
4
4
 
5
5
  import { NotReadyError } from "./error.js";
6
6
 
@@ -47,23 +47,23 @@ let pendingProbe = null;
47
47
  * O(all-leaves-ever-read)-per-update pathology). Entries are permanent like
48
48
  * the companions themselves; a store with no leaf-level isPending()/latest()
49
49
  * reads never allocates the set or pays the walk. */ function markFirewallChildCompanions(e) {
50
- const t = e.lt;
50
+ const t = e.st;
51
51
  if (!t) return;
52
52
  t.T |= CONFIG_CHILD_COMPANIONS;
53
- (ext(t).It ??= new Set).add(e);
53
+ (ext(t).St ??= new Set).add(e);
54
54
  }
55
55
 
56
56
  function getPendingSignal(e) {
57
- let t = e.o?.Ge;
57
+ let t = e.o?.ye;
58
58
  if (!t) {
59
59
  // Start false, write true if pending - ensures reversion returns to false
60
60
  t = optimisticSignal(false, {
61
61
  ownedWrite: true
62
62
  });
63
- ext(e).Ge = t;
63
+ ext(e).ye = t;
64
64
  e.T |= CONFIG_HAS_COMPANIONS;
65
65
  markFirewallChildCompanions(e);
66
- ext(t).Et = e;
66
+ ext(t).Tt = e;
67
67
  if (computePendingState(e)) setSignal(t, true);
68
68
  }
69
69
  return t;
@@ -72,7 +72,7 @@ function getPendingSignal(e) {
72
72
  function collectPendingSources(e) {
73
73
  if (!pendingProbe) return;
74
74
  pendingProbe.sources.add(e);
75
- const t = e.lt || e;
75
+ const t = e.st || e;
76
76
  if (t !== e) pendingProbe.sources.add(t);
77
77
  }
78
78
 
@@ -105,17 +105,17 @@ function collectPendingSources(e) {
105
105
  if (e.S & STATUS_ERROR) return false;
106
106
  if (t.has(e)) return false;
107
107
  t.add(e);
108
- const n = e.lt;
108
+ const n = e.st;
109
109
  if (n && markWalk(n, t)) return true;
110
110
  // Mid-recompute (the clearStatus companion poke runs before
111
111
  // trimStaleDeps), only the validated prefix [_deps.._depsTail] is this
112
112
  // pass's dependency set — walking past it would read dropped deps and
113
113
  // latch a stale verdict on the companion.
114
114
  const i = e;
115
- const r = i.ie & REACTIVE_RECOMPUTING_DEPS ? i.Ye : undefined;
115
+ const r = i.ie & REACTIVE_RECOMPUTING_DEPS ? i.je : undefined;
116
116
  if (r !== null) {
117
- for (let e = i.nt ?? null; e !== null; e = e.it) {
118
- if (!e.me && markWalk(e.ut, t)) return true;
117
+ for (let e = i.ut ?? null; e !== null; e = e.lt) {
118
+ if (!e.ve && markWalk(e.ot, t)) return true;
119
119
  if (e === r) break;
120
120
  }
121
121
  }
@@ -128,10 +128,10 @@ function collectPendingSources(e) {
128
128
 
129
129
  function quietPending(e) {
130
130
  if (e.o?.le) {
131
- for (const t of e.o.le) if (!t.o?.pe) return false;
131
+ for (const t of e.o.le) if (!t.o?.De) return false;
132
132
  return true;
133
133
  }
134
- return e.o?.pe ?? false;
134
+ return e.o?.De ?? false;
135
135
  }
136
136
 
137
137
  // NOTE: a loadingValue node's open loading window (_loading) is verdict-quiet
@@ -152,40 +152,44 @@ function computePendingState(e) {
152
152
  // reaches its owner (and a store leaf its firewall) through its own deps,
153
153
  // so the one walk covers direct marks, derivation, and companion chains.
154
154
  if (markCovered(e)) return true;
155
- const n = e.lt;
156
- if (e.o?.Et) {
157
- const t = e.o?.Et;
158
- const n = t.lt || t;
155
+ const n = e.st;
156
+ if (e.o?.Tt) {
157
+ const t = e.o?.Tt;
158
+ const n = t.st || t;
159
159
  return newQuestionInFlight(n);
160
160
  }
161
- if (n && e.Pe !== NOT_PENDING && !hasActiveOverride(e)) {
162
- return !!(n.ie & REACTIVE_MANUAL_WRITE) || !n.o?.Ee && !(n.S & STATUS_PENDING) || !!(n.S & STATUS_PENDING) && quietPending(n);
161
+ if (n && e.Re !== NOT_PENDING && !hasActiveOverride(e)) {
162
+ return !!(n.ie & REACTIVE_MANUAL_WRITE) || !n.o?.Ie && !(n.S & STATUS_PENDING) || !!(n.S & STATUS_PENDING) && quietPending(n);
163
163
  }
164
164
  // `!comp._loading`: a hold created while the loading window is still open is
165
165
  // the window's own landing in flight to its commit — verdict-quiet like the
166
166
  // rest of the window (the UNINITIALIZED check suppresses exactly this frame
167
167
  // for windowless first loads; born-committed nodes need their own gate, #2990).
168
- if (e.Pe !== NOT_PENDING && !(t.S & STATUS_UNINITIALIZED) && !t.Ie) {
169
- if (hasActiveOverride(e)) return !e.Ue || !e.Ue(e.Pe, unwrapOverride(e.o?.De));
170
- return true;
168
+ if (e.Re !== NOT_PENDING && !(t.S & STATUS_UNINITIALIZED) && !t.Ne) {
169
+ if (hasActiveOverride(e)) return !e.pe || !e.pe(e.Re, unwrapOverride(e.o?.Pe));
170
+ // A quiet re-ask's held landing still answers the same question: the
171
+ // classification survives the landing (asyncWrite) and dies with the
172
+ // commit (commitPendingNode) — verdict-quiet through the reveal, like
173
+ // the loading window above (#3178).
174
+ if (!t.o?.De) return true;
171
175
  }
172
176
  return newQuestionInFlight(t);
173
177
  }
174
178
 
175
179
  function syncCompanions(e, t) {
176
- if (e.o?.Ge) updatePendingSignal(e);
177
- if (e.o?.ge) setSignal(e.o?.ge, t);
180
+ if (e.o?.ye) updatePendingSignal(e);
181
+ if (e.o?.Ce) setSignal(e.o?.Ce, t);
178
182
  }
179
183
 
180
184
  function updatePendingSignal(e) {
181
- if (e.o?.Ge) {
182
- setSignal(e.o?.Ge, computePendingState(e));
185
+ if (e.o?.ye) {
186
+ setSignal(e.o?.ye, computePendingState(e));
183
187
  }
184
- if (e.o?.ge) updatePendingSignal(e.o?.ge);
188
+ if (e.o?.Ce) updatePendingSignal(e.o?.Ce);
185
189
  }
186
190
 
187
191
  function updateChildCompanions(e) {
188
- const t = e.o?.It;
192
+ const t = e.o?.St;
189
193
  if (t === undefined) return;
190
194
  for (const e of t) updatePendingSignal(e);
191
195
  }
@@ -204,7 +208,7 @@ function updateChildCompanions(e) {
204
208
  const visit = e => {
205
209
  if (i.has(e)) return;
206
210
  i.add(e);
207
- if (e.o?.Ge || e.o?.ge) n(e);
211
+ if (e.o?.ye || e.o?.Ce) n(e);
208
212
  for (let t = e.u; t !== null; t = t.ae) visit(t.ce);
209
213
  for (let t = e.o?.i ?? null; t !== null; t = t.Se) {
210
214
  visit(t);
@@ -227,7 +231,7 @@ function updateChildCompanions(e) {
227
231
  if (suppressedProbes.size === 0) return;
228
232
  let t = false;
229
233
  for (const [n, i] of suppressedProbes) {
230
- const r = n._e;
234
+ const r = n.Ae;
231
235
  const s = r ? currentTransition(r) : null;
232
236
  if (!s) {
233
237
  suppressedProbes.delete(n);
@@ -235,11 +239,11 @@ function updateChildCompanions(e) {
235
239
  }
236
240
  if (s !== e) continue;
237
241
  suppressedProbes.delete(n);
238
- const o = n.o?.Ge?.o?.Be;
242
+ const o = n.o?.ye?.o?.Je;
239
243
  for (const e of i) {
240
244
  if (e.ie & REACTIVE_DISPOSED) continue;
241
245
  e.ie |= REACTIVE_OPTIMISTIC_DIRTY;
242
- if (o) assignOrMergeLane(e, o); else if (e.o !== null) e.o.Be = undefined;
246
+ if (o) assignOrMergeLane(e, o); else if (e.o !== null) e.o.Je = undefined;
243
247
  enqueueSub(e);
244
248
  t = true;
245
249
  }
@@ -249,19 +253,19 @@ function updateChildCompanions(e) {
249
253
 
250
254
  function snapCompanionsToState(e) {
251
255
  suppressedProbes.size !== 0 && suppressedProbes.delete(e);
252
- const t = e.o?.Ge;
253
- if (t && (t.o?.De === undefined || t.o?.De === NOT_PENDING)) {
256
+ const t = e.o?.ye;
257
+ if (t && (t.o?.Pe === undefined || t.o?.Pe === NOT_PENDING)) {
254
258
  const n = computePendingState(e);
255
- if (t.be !== n || t.Pe !== NOT_PENDING) {
259
+ if (t.be !== n || t.Re !== NOT_PENDING) {
256
260
  t.be = n;
257
- t.Pe = NOT_PENDING;
261
+ t.Re = NOT_PENDING;
258
262
  insertSubs(t);
259
263
  schedule();
260
264
  }
261
265
  }
262
- const n = e.o?.ge;
266
+ const n = e.o?.Ce;
263
267
  if (n && !(n.ie & REACTIVE_DISPOSED)) {
264
- if ((n.o?.De === undefined || n.o?.De === NOT_PENDING) && n.Pe === NOT_PENDING && !Object.is(n.be, e.be) && !(n.ie & (REACTIVE_DIRTY | REACTIVE_CHECK))) {
268
+ if ((n.o?.Pe === undefined || n.o?.Pe === NOT_PENDING) && n.Re === NOT_PENDING && !Object.is(n.be, e.be) && !(n.ie & (REACTIVE_DIRTY | REACTIVE_CHECK))) {
265
269
  n.ie |= REACTIVE_DIRTY;
266
270
  insertIntoHeap(n, queueFor(n));
267
271
  insertSubs(n);
@@ -272,7 +276,7 @@ function snapCompanionsToState(e) {
272
276
  }
273
277
 
274
278
  function getLatestValueComputed(e) {
275
- let t = e.o?.ge;
279
+ let t = e.o?.Ce;
276
280
  // A shadow disposed while unobserved (its gated reader unmounted at a
277
281
  // landing) is a corpse: sync writes into it equality-swallow against its
278
282
  // frozen _value, and a later read revives it via recompute — clearing
@@ -289,16 +293,16 @@ function getLatestValueComputed(e) {
289
293
  setContextInternal(null);
290
294
  // Detach from owner so it isn't disposed with effects
291
295
  t = optimisticComputed(() => read(e));
292
- ext(e).ge = t;
296
+ ext(e).Ce = t;
293
297
  e.T |= CONFIG_HAS_COMPANIONS;
294
298
  markFirewallChildCompanions(e);
295
- ext(t).Et = e;
299
+ ext(t).Tt = e;
296
300
  // Parent-child lane relationship
297
301
  // Backfill an in-flight write (mirrors getPendingSignal): the companion is
298
302
  // created lazily, possibly after the write was processed — syncCompanions
299
303
  // only pushes into companions that already exist, so the first latest()
300
304
  // read inside a held transition showed the committed value (#3041).
301
- if (e.Pe !== NOT_PENDING && !hasActiveOverride(e)) setSignal(t, e.Pe);
305
+ if (e.Re !== NOT_PENDING && !hasActiveOverride(e)) setSignal(t, e.Re);
302
306
  setContextInternal(r);
303
307
  setPendingCheckActive(i);
304
308
  setLatestReadActive(n);
@@ -310,7 +314,7 @@ function getLatestValueComputed(e) {
310
314
  const t = getLatestValueComputed(e);
311
315
  const n = latestReadActive;
312
316
  setLatestReadActive(false);
313
- const i = e.o?.De !== undefined && e.o?.De !== NOT_PENDING ? unwrapOverride(e.o?.De) : e.be;
317
+ const i = e.o?.Pe !== undefined && e.o?.Pe !== NOT_PENDING ? unwrapOverride(e.o?.Pe) : e.be;
314
318
  let r;
315
319
  try {
316
320
  // An untracked latest() read has no reading context, so read() never
@@ -319,9 +323,24 @@ function getLatestValueComputed(e) {
319
323
  // until the flush (#2922). Mirror the tracked-read pull here: mark the
320
324
  // queued staleness through the graph, then bring the shadow up to date.
321
325
  const e = queueFor(t);
322
- if (t.Le >= e.xe && !(t.ie & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
326
+ if (t.Me >= e.Qe && !(t.ie & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
323
327
  markHeap(e);
324
- prepareComputed(t, true);
328
+ // Suspend probe collection during the pull (mirrors pendingCheckRead's
329
+ // prepare): a probe through latest() answers for the SHADOW — the
330
+ // read() dispatch collects it deliberately, so the verdict reflects
331
+ // async still in flight for the latest view, not the parent's held
332
+ // write. A stale shadow recomputing HERE ran its `read(parent)` with
333
+ // the probe still live and collected the parent too, so the verdict
334
+ // depended on whether anything had pulled the shadow current earlier
335
+ // in the tick (#3104: reading latest(m) flipped a later
336
+ // latest(() => isPending(x)) from true to false).
337
+ const n = pendingCheckActive;
338
+ setPendingCheckActive(false);
339
+ try {
340
+ prepareComputed(t, true);
341
+ } finally {
342
+ setPendingCheckActive(n);
343
+ }
325
344
  }
326
345
  r = read(t);
327
346
  } catch (t) {
@@ -331,10 +350,10 @@ function getLatestValueComputed(e) {
331
350
  setLatestReadActive(n);
332
351
  }
333
352
  if (t.S & STATUS_PENDING) return i;
334
- if (stale && currentOptimisticLane && t.o?.Be) {
335
- const e = findLane(t.o?.Be);
353
+ if (stale && currentOptimisticLane && t.o?.Je) {
354
+ const e = findLane(t.o?.Je);
336
355
  const n = findLane(currentOptimisticLane);
337
- if (e !== n && e.Ae.size > 0) {
356
+ if (e !== n && e.Oe.size > 0) {
338
357
  return i;
339
358
  }
340
359
  }
@@ -342,15 +361,41 @@ function getLatestValueComputed(e) {
342
361
  // speculative value in _pendingValue; a contextless read() only surfaces
343
362
  // _value. Overrides stay authoritative (A17), and stale readers keep the
344
363
  // other transition's committed view, matching read()'s own selection.
345
- if (t.Pe !== NOT_PENDING && !hasActiveOverride(t) && !(stale && t._e && activeTransition !== t._e)) return t.Pe;
364
+ if (t.Re !== NOT_PENDING && !hasActiveOverride(t) && !(stale && t.Ae && activeTransition !== t.Ae)) return t.Re;
346
365
  return r;
347
366
  }
348
367
 
368
+ /**
369
+ * A latest() shadow that is uninitialized only because it was CREATED during
370
+ * an active flight — its parent source already has a committed value, so
371
+ * latest() serves that as the visible value and a tracked reader has
372
+ * something to pair a verdict with (#3166). Same parent resolution as
373
+ * computePendingState. The pending signal companion also carries
374
+ * `_parentSource` but is a plain signal (no `_fn`) that never goes pending,
375
+ * so the `_fn` check is belt-and-braces for this call site.
376
+ */ function latestShadowWithInitializedParent(e) {
377
+ if (typeof e.oe !== "function") return false;
378
+ const t = e.o?.Tt;
379
+ if (t === undefined) return false;
380
+ const n = t.st || t;
381
+ return !(n.S & STATUS_UNINITIALIZED);
382
+ }
383
+
349
384
  /** The isPending()-probe read path, installed as GlobalQueue._pendingCheck. */ function pendingCheckRead(e, t, n, i) {
350
385
  setPendingCheckActive(false);
351
386
  if (typeof e.oe === "function") prepareComputed(e, true);
352
387
  const r = n.S;
353
- if (t && r & STATUS_PENDING && r & STATUS_UNINITIALIZED) {
388
+ if (t && r & STATUS_PENDING && r & STATUS_UNINITIALIZED &&
389
+ // The suspend-throw is for a genuinely-first-load source: the tracked
390
+ // reader has nothing to pair a verdict with, so it parks on the source.
391
+ // A latest() SHADOW created lazily mid-flight is born uninitialized even
392
+ // though its parent has a committed value latest() will serve — throwing
393
+ // here (swallowed by latestRead's fallback) dropped the shadow from the
394
+ // probe, so a tracked latest(isPending()) probe created during a
395
+ // new-question flight cached `false` for that whole flight (#3166).
396
+ // Defer to the PARENT's initialization state and fall through to normal
397
+ // collection; the plain pending throw downstream still links the reader.
398
+ !latestShadowWithInitializedParent(n)) {
354
399
  if (tracking && e !== t) link(e, t);
355
400
  setPendingCheckActive(true);
356
401
  throw n.o?._;
@@ -368,9 +413,9 @@ function getLatestValueComputed(e) {
368
413
  * reader saw is an input, and pending remains the truth for every reader
369
414
  * (#3028).
370
415
  */ function heldAwaitingAsync(e) {
371
- const t = e._e;
416
+ const t = e.Ae;
372
417
  const n = t ? currentTransition(t) : activeTransition;
373
- if (!n || n.fn) return false;
418
+ if (!n || n.sn) return false;
374
419
  // A plain staged write (a signal/store leaf — no _fn) held while an action
375
420
  // is still running is an INPUT to a computation still in flight (#3078):
376
421
  // the pairing rule must not suppress the verdict, or a memo recomputing
@@ -385,14 +430,14 @@ function getLatestValueComputed(e) {
385
430
  // action check above; the reporter scan below is for transition-held
386
431
  // writes whose source async is still computing.
387
432
  if (!t) return false;
388
- for (const [e, t] of n.Ne) {
433
+ for (const [e, t] of n._e) {
389
434
  if (t.size && e.S & STATUS_PENDING && e.o?._?.source === e) return true;
390
435
  }
391
436
  return false;
392
437
  }
393
438
 
394
439
  function recordFreshRead(e, t) {
395
- if (pendingProbe !== null && e.Pe !== NOT_PENDING && t === e.Pe) {
440
+ if (pendingProbe !== null && e.Re !== NOT_PENDING && t === e.Re) {
396
441
  if (heldAwaitingAsync(e)) return;
397
442
  pendingProbe.freshReads.add(e);
398
443
  }
@@ -400,10 +445,10 @@ function recordFreshRead(e, t) {
400
445
 
401
446
  function applyReask(e, t) {
402
447
  const n = !!(e.S & STATUS_PENDING);
403
- const i = t && !(n && !e.o?.pe);
404
- const r = n && (e.o?.pe ?? false) !== i;
448
+ const i = t && !(n && !e.o?.De);
449
+ const r = n && (e.o?.De ?? false) !== i;
405
450
  // Allocation-free for the quiet case: false is the extension default.
406
- if (i) ext(e).pe = true; else if (e.o !== null) e.o.pe = false;
451
+ if (i) ext(e).De = true; else if (e.o !== null) e.o.De = false;
407
452
  return r;
408
453
  }
409
454
 
@@ -429,6 +474,17 @@ function isPending(e) {
429
474
  };
430
475
  const collectPending = () => {
431
476
  setPendingCheckActive(false);
477
+ // Companion reads are mode-neutral plumbing: under an outer latest()
478
+ // (isPending inside a latest window — #3104's memo shape) leaving latest
479
+ // mode active dispatched these reads through latestRead, which built a
480
+ // SHADOW OF THE PENDING SIGNAL itself. The next updatePendingSignal then
481
+ // wrote that companion-on-companion from inside a recompute
482
+ // (syncCompanions → setSignal on a shadow created without ownedWrite)
483
+ // and halted dev with the owned-scope write guard. The creation paths
484
+ // (getLatestValueComputed / getPendingSignal) already suspend both
485
+ // modes; this read site must too.
486
+ const e = latestReadActive;
487
+ setLatestReadActive(false);
432
488
  try {
433
489
  i.sources.forEach(e => {
434
490
  if (read(getPendingSignal(e))) {
@@ -436,6 +492,7 @@ function isPending(e) {
436
492
  }
437
493
  });
438
494
  } finally {
495
+ setLatestReadActive(e);
439
496
  setPendingCheckActive(true);
440
497
  }
441
498
  // A "not pending" verdict that exists only because this reader saw the
@@ -472,26 +529,26 @@ function isPending(e) {
472
529
  // Hook installation (same late-binding pattern as GlobalQueue._update /
473
530
  // _propagateAffects): core call sites fire these behind the same guards the
474
531
  // direct calls used, so behavior is identical once this module loads.
475
- GlobalQueue.Oe = syncCompanions;
532
+ GlobalQueue.Ue = syncCompanions;
476
533
 
477
534
  GlobalQueue.de = updatePendingSignal;
478
535
 
479
- GlobalQueue.ye = updateChildCompanions;
536
+ GlobalQueue.me = updateChildCompanions;
480
537
 
481
538
  GlobalQueue.un = snapCompanionsToState;
482
539
 
483
- GlobalQueue.Pt = latestRead;
540
+ GlobalQueue.gt = latestRead;
484
541
 
485
- GlobalQueue.Dt = pendingCheckRead;
542
+ GlobalQueue.Ht = pendingCheckRead;
486
543
 
487
- GlobalQueue.Ht = recordFreshRead;
544
+ GlobalQueue.kt = recordFreshRead;
488
545
 
489
- GlobalQueue.Je = applyReask;
546
+ GlobalQueue.et = applyReask;
490
547
 
491
548
  GlobalQueue.k = repollDownstreamVerdicts;
492
549
 
493
- GlobalQueue.wt = witnessAffects;
550
+ GlobalQueue.Wt = witnessAffects;
494
551
 
495
- GlobalQueue.jt = wakeSuppressedProbes;
552
+ GlobalQueue.zt = wakeSuppressedProbes;
496
553
 
497
554
  export { isPending, latest };
@@ -1,6 +1,6 @@
1
- export { ContextNotFoundError, NoOwnerError, NotReadyError } from "./core/error.js";
1
+ export { ContextNotFoundError, NoOwnerError, NotReadyError, TimeoutError } from "./core/error.js";
2
2
 
3
- export { clearSnapshots, isEqual, markSnapshotScope, refresh, releaseSnapshotScope, runWithOwner, setSnapshotCapture, untrack } from "./core/core.js";
3
+ export { clearSnapshots, isEqual, markSnapshotScope, releaseSnapshotScope, runWithOwner, setSnapshotCapture, untrack } from "./core/core.js";
4
4
 
5
5
  export { enableExternalSource } from "./core/external.js";
6
6
 
@@ -20,7 +20,7 @@ import "./core/effect.js";
20
20
 
21
21
  export { action } from "./core/action.js";
22
22
 
23
- export { createEffect, createMemo, createOptimistic, createReaction, createRenderEffect, createSignal, createTrackedEffect, onCleanup, onSettled, resolve } from "./signals.js";
23
+ export { createEffect, createMemo, createOptimistic, createReaction, createRenderEffect, createSignal, createTrackedEffect, onCleanup, onSettled, refresh, resolve, until } from "./signals.js";
24
24
 
25
25
  export { affects } from "./affects.js";
26
26