@solidjs/signals 2.0.0-rc.3 → 2.0.0-rc.5

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 (71) hide show
  1. package/dist/dev.js +2494 -271
  2. package/dist/node.cjs +3691 -1595
  3. package/dist/prod/affects.js +13 -12
  4. package/dist/prod/boundaries.js +43 -35
  5. package/dist/prod/core/action.js +3 -3
  6. package/dist/prod/core/async.js +137 -106
  7. package/dist/prod/core/constants.js +55 -1
  8. package/dist/prod/core/core.js +354 -247
  9. package/dist/prod/core/effect.js +47 -50
  10. package/dist/prod/core/error.js +13 -1
  11. package/dist/prod/core/external.js +4 -4
  12. package/dist/prod/core/graph.js +88 -52
  13. package/dist/prod/core/heap.js +38 -38
  14. package/dist/prod/core/lanes.js +34 -34
  15. package/dist/prod/core/optimistic.js +61 -58
  16. package/dist/prod/core/owner.js +38 -38
  17. package/dist/prod/core/scheduler.js +401 -174
  18. package/dist/prod/core/verdict.js +132 -65
  19. package/dist/prod/index.js +7 -3
  20. package/dist/prod/map.js +106 -106
  21. package/dist/prod/signals.js +253 -25
  22. package/dist/prod/store/index.js +2 -0
  23. package/dist/prod/store/next/optimistic.js +314 -121
  24. package/dist/prod/store/next/patch-hooks.js +13 -0
  25. package/dist/prod/store/next/patch.js +614 -0
  26. package/dist/prod/store/next/projection.js +23 -19
  27. package/dist/prod/store/next/reconcile.js +307 -120
  28. package/dist/prod/store/next/store.js +440 -117
  29. package/dist/prod/store/next/target.js +13 -4
  30. package/dist/prod/store/store.js +5 -5
  31. package/dist/types/core/async.d.ts +2 -0
  32. package/dist/types/core/attribution.d.ts +9 -4
  33. package/dist/types/core/constants.d.ts +54 -0
  34. package/dist/types/core/core.d.ts +34 -21
  35. package/dist/types/core/dev.d.ts +8 -0
  36. package/dist/types/core/error.d.ts +9 -0
  37. package/dist/types/core/graph.d.ts +22 -0
  38. package/dist/types/core/index.d.ts +2 -2
  39. package/dist/types/core/scheduler.d.ts +46 -0
  40. package/dist/types/core/types.d.ts +12 -0
  41. package/dist/types/index.d.ts +3 -3
  42. package/dist/types/signals.d.ts +108 -0
  43. package/dist/types/store/index.d.ts +2 -0
  44. package/dist/types/store/next/optimistic.d.ts +13 -10
  45. package/dist/types/store/next/patch-hooks.d.ts +41 -0
  46. package/dist/types/store/next/patch.d.ts +91 -0
  47. package/dist/types/store/next/projection.d.ts +1 -1
  48. package/dist/types/store/next/reconcile.d.ts +14 -0
  49. package/dist/types/store/next/store.d.ts +52 -2
  50. package/dist/types/store/next/target.d.ts +73 -8
  51. package/dist/types-cjs/core/async.d.cts +2 -0
  52. package/dist/types-cjs/core/attribution.d.cts +9 -4
  53. package/dist/types-cjs/core/constants.d.cts +54 -0
  54. package/dist/types-cjs/core/core.d.cts +34 -21
  55. package/dist/types-cjs/core/dev.d.cts +8 -0
  56. package/dist/types-cjs/core/error.d.cts +9 -0
  57. package/dist/types-cjs/core/graph.d.cts +22 -0
  58. package/dist/types-cjs/core/index.d.cts +2 -2
  59. package/dist/types-cjs/core/scheduler.d.cts +46 -0
  60. package/dist/types-cjs/core/types.d.cts +12 -0
  61. package/dist/types-cjs/index.d.cts +3 -3
  62. package/dist/types-cjs/signals.d.cts +108 -0
  63. package/dist/types-cjs/store/index.d.cts +2 -0
  64. package/dist/types-cjs/store/next/optimistic.d.cts +13 -10
  65. package/dist/types-cjs/store/next/patch-hooks.d.cts +41 -0
  66. package/dist/types-cjs/store/next/patch.d.cts +91 -0
  67. package/dist/types-cjs/store/next/projection.d.cts +1 -1
  68. package/dist/types-cjs/store/next/reconcile.d.cts +14 -0
  69. package/dist/types-cjs/store/next/store.d.cts +52 -2
  70. package/dist/types-cjs/store/next/target.d.cts +73 -8
  71. package/package.json +2 -2
@@ -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).Nt ??= 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).It = 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?.Ge) return false;
132
132
  return true;
133
133
  }
134
- return e.o?.pe ?? false;
134
+ return e.o?.Ge ?? false;
135
135
  }
136
136
 
137
137
  // NOTE: a loadingValue node's open loading window (_loading) is verdict-quiet
@@ -152,40 +152,40 @@ 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?.It) {
157
- const t = e.o?.It;
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
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);
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));
168
+ if (e.Pe !== NOT_PENDING && !(t.S & STATUS_UNINITIALIZED) && !t.Ne) {
169
+ if (hasActiveOverride(e)) return !e.ge || !e.ge(e.Pe, unwrapOverride(e.o?.Oe));
170
170
  return true;
171
171
  }
172
172
  return newQuestionInFlight(t);
173
173
  }
174
174
 
175
175
  function syncCompanions(e, t) {
176
- if (e.o?.Ge) updatePendingSignal(e);
177
- if (e.o?.ge) setSignal(e.o?.ge, t);
176
+ if (e.o?.ye) updatePendingSignal(e);
177
+ if (e.o?.Ce) setSignal(e.o?.Ce, t);
178
178
  }
179
179
 
180
180
  function updatePendingSignal(e) {
181
- if (e.o?.Ge) {
182
- setSignal(e.o?.Ge, computePendingState(e));
181
+ if (e.o?.ye) {
182
+ setSignal(e.o?.ye, computePendingState(e));
183
183
  }
184
- if (e.o?.ge) updatePendingSignal(e.o?.ge);
184
+ if (e.o?.Ce) updatePendingSignal(e.o?.Ce);
185
185
  }
186
186
 
187
187
  function updateChildCompanions(e) {
188
- const t = e.o?.Nt;
188
+ const t = e.o?.St;
189
189
  if (t === undefined) return;
190
190
  for (const e of t) updatePendingSignal(e);
191
191
  }
@@ -204,9 +204,9 @@ function updateChildCompanions(e) {
204
204
  const visit = e => {
205
205
  if (i.has(e)) return;
206
206
  i.add(e);
207
- if (e.o?.Ge || e.o?.ge) n(e);
208
- for (let t = e.u; t !== null; t = t.fe) visit(t.ae);
209
- for (let t = e.o?.l ?? null; t !== null; t = t.ce) {
207
+ if (e.o?.ye || e.o?.Ce) n(e);
208
+ for (let t = e.u; t !== null; t = t.ae) visit(t.ce);
209
+ for (let t = e.o?.i ?? null; t !== null; t = t.Se) {
210
210
  visit(t);
211
211
  }
212
212
  };
@@ -227,7 +227,7 @@ function updateChildCompanions(e) {
227
227
  if (suppressedProbes.size === 0) return;
228
228
  let t = false;
229
229
  for (const [n, i] of suppressedProbes) {
230
- const r = n._e;
230
+ const r = n.Ae;
231
231
  const s = r ? currentTransition(r) : null;
232
232
  if (!s) {
233
233
  suppressedProbes.delete(n);
@@ -235,11 +235,11 @@ function updateChildCompanions(e) {
235
235
  }
236
236
  if (s !== e) continue;
237
237
  suppressedProbes.delete(n);
238
- const o = n.o?.Ge?.o?.Be;
238
+ const o = n.o?.ye?.o?.Je;
239
239
  for (const e of i) {
240
240
  if (e.ie & REACTIVE_DISPOSED) continue;
241
241
  e.ie |= REACTIVE_OPTIMISTIC_DIRTY;
242
- if (o) assignOrMergeLane(e, o); else if (e.o !== null) e.o.Be = undefined;
242
+ if (o) assignOrMergeLane(e, o); else if (e.o !== null) e.o.Je = undefined;
243
243
  enqueueSub(e);
244
244
  t = true;
245
245
  }
@@ -249,8 +249,8 @@ function updateChildCompanions(e) {
249
249
 
250
250
  function snapCompanionsToState(e) {
251
251
  suppressedProbes.size !== 0 && suppressedProbes.delete(e);
252
- const t = e.o?.Ge;
253
- if (t && (t.o?.De === undefined || t.o?.De === NOT_PENDING)) {
252
+ const t = e.o?.ye;
253
+ if (t && (t.o?.Oe === undefined || t.o?.Oe === NOT_PENDING)) {
254
254
  const n = computePendingState(e);
255
255
  if (t.be !== n || t.Pe !== NOT_PENDING) {
256
256
  t.be = n;
@@ -259,9 +259,9 @@ function snapCompanionsToState(e) {
259
259
  schedule();
260
260
  }
261
261
  }
262
- const n = e.o?.ge;
262
+ const n = e.o?.Ce;
263
263
  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))) {
264
+ if ((n.o?.Oe === undefined || n.o?.Oe === NOT_PENDING) && n.Pe === NOT_PENDING && !Object.is(n.be, e.be) && !(n.ie & (REACTIVE_DIRTY | REACTIVE_CHECK))) {
265
265
  n.ie |= REACTIVE_DIRTY;
266
266
  insertIntoHeap(n, queueFor(n));
267
267
  insertSubs(n);
@@ -272,7 +272,7 @@ function snapCompanionsToState(e) {
272
272
  }
273
273
 
274
274
  function getLatestValueComputed(e) {
275
- let t = e.o?.ge;
275
+ let t = e.o?.Ce;
276
276
  // A shadow disposed while unobserved (its gated reader unmounted at a
277
277
  // landing) is a corpse: sync writes into it equality-swallow against its
278
278
  // frozen _value, and a later read revives it via recompute — clearing
@@ -289,10 +289,10 @@ function getLatestValueComputed(e) {
289
289
  setContextInternal(null);
290
290
  // Detach from owner so it isn't disposed with effects
291
291
  t = optimisticComputed(() => read(e));
292
- ext(e).ge = t;
292
+ ext(e).Ce = t;
293
293
  e.T |= CONFIG_HAS_COMPANIONS;
294
294
  markFirewallChildCompanions(e);
295
- ext(t).It = e;
295
+ ext(t).Tt = e;
296
296
  // Parent-child lane relationship
297
297
  // Backfill an in-flight write (mirrors getPendingSignal): the companion is
298
298
  // created lazily, possibly after the write was processed — syncCompanions
@@ -310,7 +310,7 @@ function getLatestValueComputed(e) {
310
310
  const t = getLatestValueComputed(e);
311
311
  const n = latestReadActive;
312
312
  setLatestReadActive(false);
313
- const i = e.o?.De !== undefined && e.o?.De !== NOT_PENDING ? unwrapOverride(e.o?.De) : e.be;
313
+ const i = e.o?.Oe !== undefined && e.o?.Oe !== NOT_PENDING ? unwrapOverride(e.o?.Oe) : e.be;
314
314
  let r;
315
315
  try {
316
316
  // An untracked latest() read has no reading context, so read() never
@@ -319,9 +319,24 @@ function getLatestValueComputed(e) {
319
319
  // until the flush (#2922). Mirror the tracked-read pull here: mark the
320
320
  // queued staleness through the graph, then bring the shadow up to date.
321
321
  const e = queueFor(t);
322
- if (t.Le >= e.xe && !(t.ie & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
322
+ if (t.Me >= e.Qe && !(t.ie & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
323
323
  markHeap(e);
324
- prepareComputed(t, true);
324
+ // Suspend probe collection during the pull (mirrors pendingCheckRead's
325
+ // prepare): a probe through latest() answers for the SHADOW — the
326
+ // read() dispatch collects it deliberately, so the verdict reflects
327
+ // async still in flight for the latest view, not the parent's held
328
+ // write. A stale shadow recomputing HERE ran its `read(parent)` with
329
+ // the probe still live and collected the parent too, so the verdict
330
+ // depended on whether anything had pulled the shadow current earlier
331
+ // in the tick (#3104: reading latest(m) flipped a later
332
+ // latest(() => isPending(x)) from true to false).
333
+ const n = pendingCheckActive;
334
+ setPendingCheckActive(false);
335
+ try {
336
+ prepareComputed(t, true);
337
+ } finally {
338
+ setPendingCheckActive(n);
339
+ }
325
340
  }
326
341
  r = read(t);
327
342
  } catch (t) {
@@ -331,10 +346,10 @@ function getLatestValueComputed(e) {
331
346
  setLatestReadActive(n);
332
347
  }
333
348
  if (t.S & STATUS_PENDING) return i;
334
- if (stale && currentOptimisticLane && t.o?.Be) {
335
- const e = findLane(t.o?.Be);
349
+ if (stale && currentOptimisticLane && t.o?.Je) {
350
+ const e = findLane(t.o?.Je);
336
351
  const n = findLane(currentOptimisticLane);
337
- if (e !== n && e.Ae.size > 0) {
352
+ if (e !== n && e.De.size > 0) {
338
353
  return i;
339
354
  }
340
355
  }
@@ -342,15 +357,41 @@ function getLatestValueComputed(e) {
342
357
  // speculative value in _pendingValue; a contextless read() only surfaces
343
358
  // _value. Overrides stay authoritative (A17), and stale readers keep the
344
359
  // 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;
360
+ if (t.Pe !== NOT_PENDING && !hasActiveOverride(t) && !(stale && t.Ae && activeTransition !== t.Ae)) return t.Pe;
346
361
  return r;
347
362
  }
348
363
 
364
+ /**
365
+ * A latest() shadow that is uninitialized only because it was CREATED during
366
+ * an active flight — its parent source already has a committed value, so
367
+ * latest() serves that as the visible value and a tracked reader has
368
+ * something to pair a verdict with (#3166). Same parent resolution as
369
+ * computePendingState. The pending signal companion also carries
370
+ * `_parentSource` but is a plain signal (no `_fn`) that never goes pending,
371
+ * so the `_fn` check is belt-and-braces for this call site.
372
+ */ function latestShadowWithInitializedParent(e) {
373
+ if (typeof e.oe !== "function") return false;
374
+ const t = e.o?.Tt;
375
+ if (t === undefined) return false;
376
+ const n = t.st || t;
377
+ return !(n.S & STATUS_UNINITIALIZED);
378
+ }
379
+
349
380
  /** The isPending()-probe read path, installed as GlobalQueue._pendingCheck. */ function pendingCheckRead(e, t, n, i) {
350
381
  setPendingCheckActive(false);
351
- if (typeof e.Se === "function") prepareComputed(e, true);
382
+ if (typeof e.oe === "function") prepareComputed(e, true);
352
383
  const r = n.S;
353
- if (t && r & STATUS_PENDING && r & STATUS_UNINITIALIZED) {
384
+ if (t && r & STATUS_PENDING && r & STATUS_UNINITIALIZED &&
385
+ // The suspend-throw is for a genuinely-first-load source: the tracked
386
+ // reader has nothing to pair a verdict with, so it parks on the source.
387
+ // A latest() SHADOW created lazily mid-flight is born uninitialized even
388
+ // though its parent has a committed value latest() will serve — throwing
389
+ // here (swallowed by latestRead's fallback) dropped the shadow from the
390
+ // probe, so a tracked latest(isPending()) probe created during a
391
+ // new-question flight cached `false` for that whole flight (#3166).
392
+ // Defer to the PARENT's initialization state and fall through to normal
393
+ // collection; the plain pending throw downstream still links the reader.
394
+ !latestShadowWithInitializedParent(n)) {
354
395
  if (tracking && e !== t) link(e, t);
355
396
  setPendingCheckActive(true);
356
397
  throw n.o?._;
@@ -368,10 +409,24 @@ function getLatestValueComputed(e) {
368
409
  * reader saw is an input, and pending remains the truth for every reader
369
410
  * (#3028).
370
411
  */ function heldAwaitingAsync(e) {
371
- const t = e._e;
372
- const n = t ? currentTransition(t) : null;
373
- if (!n || n.an) return false;
374
- for (const [e, t] of n.Ne) {
412
+ const t = e.Ae;
413
+ const n = t ? currentTransition(t) : activeTransition;
414
+ if (!n || n.sn) return false;
415
+ // A plain staged write (a signal/store leaf no _fn) held while an action
416
+ // is still running is an INPUT to a computation still in flight (#3078):
417
+ // the pairing rule must not suppress the verdict, or a memo recomputing
418
+ // mid-action reads the staged value, gets told "not pending", and
419
+ // disagrees with a direct isPending() probe for the whole action window.
420
+ // A computed's staged value is the opposite case — a LANDED answer
421
+ // awaiting reveal — where the pairing rule stands even inside an open
422
+ // action (#2831: a reader that saw the new value must not also see
423
+ // pending); still-computing answers are covered by the reporter scan.
424
+ if (n.ue.length && !e.oe) return true;
425
+ // A node not yet stamped with a transition only qualifies through the
426
+ // action check above; the reporter scan below is for transition-held
427
+ // writes whose source async is still computing.
428
+ if (!t) return false;
429
+ for (const [e, t] of n._e) {
375
430
  if (t.size && e.S & STATUS_PENDING && e.o?._?.source === e) return true;
376
431
  }
377
432
  return false;
@@ -386,10 +441,10 @@ function recordFreshRead(e, t) {
386
441
 
387
442
  function applyReask(e, t) {
388
443
  const n = !!(e.S & STATUS_PENDING);
389
- const i = t && !(n && !e.o?.pe);
390
- const r = n && (e.o?.pe ?? false) !== i;
444
+ const i = t && !(n && !e.o?.Ge);
445
+ const r = n && (e.o?.Ge ?? false) !== i;
391
446
  // Allocation-free for the quiet case: false is the extension default.
392
- if (i) ext(e).pe = true; else if (e.o !== null) e.o.pe = false;
447
+ if (i) ext(e).Ge = true; else if (e.o !== null) e.o.Ge = false;
393
448
  return r;
394
449
  }
395
450
 
@@ -415,6 +470,17 @@ function isPending(e) {
415
470
  };
416
471
  const collectPending = () => {
417
472
  setPendingCheckActive(false);
473
+ // Companion reads are mode-neutral plumbing: under an outer latest()
474
+ // (isPending inside a latest window — #3104's memo shape) leaving latest
475
+ // mode active dispatched these reads through latestRead, which built a
476
+ // SHADOW OF THE PENDING SIGNAL itself. The next updatePendingSignal then
477
+ // wrote that companion-on-companion from inside a recompute
478
+ // (syncCompanions → setSignal on a shadow created without ownedWrite)
479
+ // and halted dev with the owned-scope write guard. The creation paths
480
+ // (getLatestValueComputed / getPendingSignal) already suspend both
481
+ // modes; this read site must too.
482
+ const e = latestReadActive;
483
+ setLatestReadActive(false);
418
484
  try {
419
485
  i.sources.forEach(e => {
420
486
  if (read(getPendingSignal(e))) {
@@ -422,6 +488,7 @@ function isPending(e) {
422
488
  }
423
489
  });
424
490
  } finally {
491
+ setLatestReadActive(e);
425
492
  setPendingCheckActive(true);
426
493
  }
427
494
  // A "not pending" verdict that exists only because this reader saw the
@@ -429,7 +496,7 @@ function isPending(e) {
429
496
  // this flush (a downstream async pends and holds it), the suppression was
430
497
  // wrong and the wrapper must re-ask (#3028). Remember who to wake — the
431
498
  // async registration (GlobalQueue.notify) triggers wakeSuppressedProbes.
432
- if (!i.found && i.suppressed.length && context && typeof context.Se === "function") {
499
+ if (!i.found && i.suppressed.length && context && typeof context.oe === "function") {
433
500
  for (const e of i.suppressed) {
434
501
  let t = suppressedProbes.get(e);
435
502
  if (!t) suppressedProbes.set(e, t = new Set);
@@ -458,26 +525,26 @@ function isPending(e) {
458
525
  // Hook installation (same late-binding pattern as GlobalQueue._update /
459
526
  // _propagateAffects): core call sites fire these behind the same guards the
460
527
  // direct calls used, so behavior is identical once this module loads.
461
- GlobalQueue.Oe = syncCompanions;
528
+ GlobalQueue.Re = syncCompanions;
462
529
 
463
530
  GlobalQueue.de = updatePendingSignal;
464
531
 
465
- GlobalQueue.ye = updateChildCompanions;
532
+ GlobalQueue.me = updateChildCompanions;
466
533
 
467
534
  GlobalQueue.un = snapCompanionsToState;
468
535
 
469
- GlobalQueue.Pt = latestRead;
536
+ GlobalQueue.gt = latestRead;
470
537
 
471
- GlobalQueue.Dt = pendingCheckRead;
538
+ GlobalQueue.Ht = pendingCheckRead;
472
539
 
473
- GlobalQueue.Ht = recordFreshRead;
540
+ GlobalQueue.kt = recordFreshRead;
474
541
 
475
- GlobalQueue.Je = applyReask;
542
+ GlobalQueue.et = applyReask;
476
543
 
477
- GlobalQueue.p = repollDownstreamVerdicts;
544
+ GlobalQueue.k = repollDownstreamVerdicts;
478
545
 
479
- GlobalQueue.Bt = witnessAffects;
546
+ GlobalQueue.Wt = witnessAffects;
480
547
 
481
- GlobalQueue.wt = wakeSuppressedProbes;
548
+ GlobalQueue.zt = wakeSuppressedProbes;
482
549
 
483
550
  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
 
@@ -38,6 +38,10 @@ export { createProjectionNext as createProjection } from "./store/next/projectio
38
38
 
39
39
  export { merge, omit } from "./store/utils.js";
40
40
 
41
+ export { patchableRaw, registerPatch, registerRowOps, registerSlotPatchNext as registerSlotPatch } from "./store/next/patch.js";
42
+
43
+ export { storeHasFamily, storeHasOptimisticFamily, storeIsShallow } from "./store/next/store.js";
44
+
41
45
  export { storePath } from "./store/storePath.js";
42
46
 
43
47
  const DEV = undefined;