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

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 (66) hide show
  1. package/dist/dev.js +2467 -1322
  2. package/dist/node.cjs +2037 -2428
  3. package/dist/node.dev.cjs +13724 -0
  4. package/dist/prod/boundaries.js +3 -1
  5. package/dist/prod/core/async.js +47 -20
  6. package/dist/prod/core/attribution-hooks.js +3 -0
  7. package/dist/prod/core/constants.js +9 -1
  8. package/dist/prod/core/context.js +10 -16
  9. package/dist/prod/core/core.js +260 -188
  10. package/dist/prod/core/dev.js +2 -0
  11. package/dist/prod/core/effect.js +41 -32
  12. package/dist/prod/core/external.js +2 -2
  13. package/dist/prod/core/graph.js +35 -31
  14. package/dist/prod/core/heap.js +34 -40
  15. package/dist/prod/core/lanes.js +44 -30
  16. package/dist/prod/core/optimistic.js +55 -45
  17. package/dist/prod/core/owner.js +35 -35
  18. package/dist/prod/core/scheduler.js +176 -177
  19. package/dist/prod/core/verdict.js +53 -52
  20. package/dist/prod/index.js +0 -2
  21. package/dist/prod/map.js +104 -94
  22. package/dist/prod/signals.js +119 -35
  23. package/dist/prod/store/next/optimistic.js +217 -163
  24. package/dist/prod/store/next/projection.js +2 -2
  25. package/dist/prod/store/next/reconcile.js +140 -288
  26. package/dist/prod/store/next/store.js +338 -252
  27. package/dist/prod/store/store.js +2 -2
  28. package/dist/types/core/attribution-hooks.d.ts +75 -0
  29. package/dist/types/core/attribution.d.ts +313 -9
  30. package/dist/types/core/constants.d.ts +8 -0
  31. package/dist/types/core/core.d.ts +12 -3
  32. package/dist/types/core/dev.d.ts +56 -9
  33. package/dist/types/core/heap.d.ts +5 -3
  34. package/dist/types/core/invariants.d.ts +1 -1
  35. package/dist/types/core/lanes.d.ts +10 -0
  36. package/dist/types/core/scheduler.d.ts +14 -4
  37. package/dist/types/signals.d.ts +10 -11
  38. package/dist/types/store/index.d.ts +3 -6
  39. package/dist/types/store/next/optimistic.d.ts +4 -2
  40. package/dist/types/store/next/reconcile.d.ts +5 -12
  41. package/dist/types/store/next/store.d.ts +3 -9
  42. package/dist/types/store/next/target.d.ts +30 -46
  43. package/dist/types/store/store.d.ts +14 -9
  44. package/dist/types-cjs/core/attribution-hooks.d.cts +75 -0
  45. package/dist/types-cjs/core/attribution.d.cts +313 -9
  46. package/dist/types-cjs/core/constants.d.cts +8 -0
  47. package/dist/types-cjs/core/core.d.cts +12 -3
  48. package/dist/types-cjs/core/dev.d.cts +56 -9
  49. package/dist/types-cjs/core/heap.d.cts +5 -3
  50. package/dist/types-cjs/core/invariants.d.cts +1 -1
  51. package/dist/types-cjs/core/lanes.d.cts +10 -0
  52. package/dist/types-cjs/core/scheduler.d.cts +14 -4
  53. package/dist/types-cjs/signals.d.cts +10 -11
  54. package/dist/types-cjs/store/index.d.cts +3 -6
  55. package/dist/types-cjs/store/next/optimistic.d.cts +4 -2
  56. package/dist/types-cjs/store/next/reconcile.d.cts +5 -12
  57. package/dist/types-cjs/store/next/store.d.cts +3 -9
  58. package/dist/types-cjs/store/next/target.d.cts +30 -46
  59. package/dist/types-cjs/store/store.d.cts +14 -9
  60. package/package.json +3 -2
  61. package/dist/prod/store/next/patch-hooks.js +0 -13
  62. package/dist/prod/store/next/patch.js +0 -614
  63. package/dist/types/store/next/patch-hooks.d.ts +0 -41
  64. package/dist/types/store/next/patch.d.ts +0 -91
  65. package/dist/types-cjs/store/next/patch-hooks.d.cts +0 -41
  66. package/dist/types-cjs/store/next/patch.d.cts +0 -91
@@ -291,7 +291,9 @@ class CollectionQueue extends Queue {
291
291
  if (t) {
292
292
  const e = this.v.size === 0;
293
293
  this.v.add(t);
294
- if (e) setSignal(this.D, true);
294
+ if (e) {
295
+ setSignal(this.D, true);
296
+ }
295
297
  if (this.ee & STATUS_ERROR) {
296
298
  setSignal(this._, unwrapStatusError(t.o?._));
297
299
  }
@@ -28,13 +28,15 @@ function addPendingSource(e, n) {
28
28
  }
29
29
 
30
30
  function removePendingSource(e, n) {
31
- if (!e.o?.le?.delete(n)) return false;
32
- if (e.o?.le.size === 0) if (e.o !== null) e.o.le = undefined;
31
+ const t = e.o?.le;
32
+ if (!t?.delete(n)) return false;
33
+ if (!t.size) e.o.le = undefined;
33
34
  return true;
34
35
  }
35
36
 
36
37
  function clearPendingSources(e) {
37
- e.o?.le?.clear();
38
+ // This set is node-owned and never shared; dropping the sole reference
39
+ // releases the set and every entry without a redundant clear() walk.
38
40
  if (e.o !== null) e.o.le = undefined;
39
41
  }
40
42
 
@@ -140,6 +142,10 @@ function settleErroredDependents(e, n) {
140
142
  }
141
143
 
142
144
  function settlePendingSource(e) {
145
+ // The normal landing path already cleared the source's own set. Superseded
146
+ // re-parks arrive here with an abandoned self entry, which must retire in
147
+ // the same walk as its propagated copies.
148
+ removePendingSource(e, e);
143
149
  let n = false;
144
150
  let t;
145
151
  const r = new Set;
@@ -253,6 +259,10 @@ function handleAsync(e, n, t) {
253
259
  }
254
260
  settleTransition();
255
261
  notifyStatus(e, r ? STATUS_PENDING : STATUS_ERROR, t);
262
+ // A NotReady rejection is a landing into another pending source. The
263
+ // rejected flight will never settle its self entry, so transfer ownership
264
+ // after notifyStatus has propagated the replacement source.
265
+ if (r) settlePendingSource(e);
256
266
  e.Te = clock;
257
267
  // A real error settles derivatively-pending dependents (notifyStatus
258
268
  // cleared their pending sources), so stranded lazy ones release here —
@@ -267,14 +277,27 @@ function handleAsync(e, n, t) {
267
277
  if (e.ie & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
268
278
  settleTransition();
269
279
  const i = !!(e.S & STATUS_UNINITIALIZED);
280
+ // Captured before clearStatus wipes it: a quiet re-ask's landing may be
281
+ // transition-held below, and the displayed value keeps answering the same
282
+ // question until the hold commits — the classification must survive to
283
+ // that reveal or companion synchronization briefly classifies the held
284
+ // old value as pending, a one-frame pulse to direct observers (#3178).
285
+ // A truthy capture implies `_x` exists, so the restore writes it directly.
286
+ const l = e.o?.De;
270
287
  trimStaleDeps(e);
271
288
  clearStatus(e);
272
- const l = resolveLane(e);
273
- if (l) l.De.delete(e);
289
+ if (l) e.o.De = true;
290
+ const s = resolveLane(e);
291
+ if (s) s.Oe.delete(e);
274
292
  if (t) {
275
- t(r);
293
+ try {
294
+ t(r);
295
+ } catch (e) {
296
+ handleError(e);
297
+ return;
298
+ }
276
299
  if (i) clearStatus(e, true);
277
- } else if (e.o?.Oe !== undefined) {
300
+ } else if (e.o?.Pe !== undefined) {
278
301
  // Optimistic node — resting OR covered by an active override — holds
279
302
  // through the shared pending-node path, exactly like a plain async memo,
280
303
  // so the commit clears STATUS_UNINITIALIZED (#2806) and elevation to
@@ -284,8 +307,8 @@ function handleAsync(e, n, t) {
284
307
  // (A17 — every reader sees the override); the revert reveals whatever
285
308
  // has committed by then, so corrections reveal atomically with their
286
309
  // transition rather than escaping it.
287
- if (e.Pe === NOT_PENDING) queuePendingNode(e);
288
- e.Pe = r;
310
+ if (e.Re === NOT_PENDING) queuePendingNode(e);
311
+ e.Re = r;
289
312
  // The hold is a companion-visible write like any other (A13/A19): the
290
313
  // clearStatus() above computed its verdict before the hold existed, so
291
314
  // isPending must re-derive (the value is not final until commit — V1)
@@ -293,7 +316,7 @@ function handleAsync(e, n, t) {
293
316
  // only notified when the hold is visible to them: under an active
294
317
  // override every reader sees the override (A17), so waking subs would
295
318
  // re-show an unchanged view — the revert is the notification point.
296
- GlobalQueue.Re?.(e, r);
319
+ GlobalQueue.Ue?.(e, r);
297
320
  if (!hasActiveOverride(e)) {
298
321
  insertSubs(e);
299
322
  } else if (e.T & CONFIG_AUTHORITATIVE_OBSERVED) {
@@ -306,14 +329,14 @@ function handleAsync(e, n, t) {
306
329
  // branch in recompute(). Optional call: the bit implies the
307
330
  // optimistic engine WAS consulted, but the hook only installs with
308
331
  // it — a bare-core build must not crash here.
309
- GlobalQueue.Ue?.(e);
332
+ GlobalQueue.he?.(e);
310
333
  }
311
334
  e.Te = clock;
312
- } else if (l) {
335
+ } else if (s) {
313
336
  // Route through lane's effect queue for independent flushing
314
- const n = e.he;
337
+ const n = e.ge;
315
338
  const t = e.be;
316
- const o = e.ge;
339
+ const o = e.pe;
317
340
  try {
318
341
  if (!n && i || !o || !o(r, t)) {
319
342
  e.be = r;
@@ -321,7 +344,7 @@ function handleAsync(e, n, t) {
321
344
  // The latest() shadow write gives latest() effects independent lanes; the
322
345
  // _pendingSignal update is a no-op repeat of the clearStatus() call above
323
346
  // (computePendingState doesn't read _value).
324
- GlobalQueue.Re?.(e, r);
347
+ GlobalQueue.Ue?.(e, r);
325
348
  insertSubs(e, true);
326
349
  }
327
350
  } catch (n) {
@@ -345,8 +368,12 @@ function handleAsync(e, n, t) {
345
368
  // (`_pendingValue` set above or inside setSignal) is not — the verdict's
346
369
  // held-value branch is window-gated, and commitPendingNode closes the
347
370
  // window when the hold commits, so no one-frame isPending pulse can leak
348
- // to live observers between the landing and its commit (#2990).
349
- if (e.Pe === NOT_PENDING) e.Ne = false;
371
+ // to live observers between the landing and its commit (#2990). The
372
+ // quiet re-ask classification follows the same schedule (#3178).
373
+ if (e.Re === NOT_PENDING) {
374
+ e.Ne = false;
375
+ if (l) e.o.De = false;
376
+ }
350
377
  settlePendingSource(e);
351
378
  schedule();
352
379
  flush();
@@ -505,7 +532,7 @@ function handleAsync(e, n, t) {
505
532
  // becomes disposal-bearing, which is exactly the class a directly
506
533
  // returned iterable already occupies.
507
534
  const registerDeferredClose = n => {
508
- if (!e.pe) e.pe = n; else if (Array.isArray(e.pe)) e.pe.push(n); else e.pe = [ e.pe, n ];
535
+ if (!e.Ge) e.Ge = n; else if (Array.isArray(e.Ge)) e.Ge.push(n); else e.Ge = [ e.Ge, n ];
509
536
  };
510
537
  n.then(r => {
511
538
  if (l) {
@@ -568,7 +595,7 @@ function clearStatus(e, n = false) {
568
595
  // The pending window is over; its quiet classification dies with it.
569
596
  // (Unconditional: _reask is baked into the node literals, so this is a
570
597
  // plain store to an existing slot — no shape change.)
571
- if (e.o !== null) e.o.Ge = false;
598
+ if (e.o !== null) e.o.De = false;
572
599
  e.S = n ? 0 : e.S & STATUS_UNINITIALIZED;
573
600
  if (e.o?._) setPendingError(e);
574
601
  // Update pending signal for isPending() reactivity (companions only exist
@@ -584,7 +611,7 @@ function notifyStatus(e, n, t, r, o) {
584
611
  if (n === STATUS_ERROR && !(t instanceof StatusError) && !(t instanceof NotReadyError)) t = new StatusError(e, t);
585
612
  const i = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
586
613
  const l = i === e;
587
- const s = n === STATUS_PENDING && e.o?.Oe !== undefined && !l;
614
+ const s = n === STATUS_PENDING && e.o?.Pe !== undefined && !l;
588
615
  const u = s && hasActiveOverride(e);
589
616
  if (!r) {
590
617
  if (n === STATUS_PENDING && i) {
@@ -0,0 +1,3 @@
1
+ let attrHooks = null;
2
+
3
+ export { attrHooks };
@@ -137,6 +137,14 @@ const CONFIG_HAS_LANE = 1 << 10;
137
137
  * (A17). Cleared at commit (the commit IS the reveal); subscribers masked
138
138
  * during the hold are woken by finalizePureQueue's post-revert pass. */ const CONFIG_HELD_TRUTH = 1 << 17;
139
139
 
140
+ /** SLOT node (store leaf): created through `slotSignal` with `_host`/`_key`
141
+ * backrefs baked into the literal. The unobserved sweep dispatches these to
142
+ * the ONE shared hook (`setSlotUnobserved`) instead of a per-node closure
143
+ * held in a per-node extension — store mounts materialize one signal per
144
+ * touched leaf, so per-node allocations (options object, equals closure,
145
+ * unobserved closure, NodeExtension) were the measured create-floor bytes
146
+ * (warm dbmon profile: store node machinery ~36% + GC ~29%). */ const CONFIG_SLOT_NODE = 1 << 18;
147
+
140
148
  const STATUS_PENDING = 1 << 0;
141
149
 
142
150
  const STATUS_ERROR = 1 << 1;
@@ -181,4 +189,4 @@ const defaultContext = {};
181
189
  * @internal
182
190
  */ const $REFRESH = Symbol("refresh");
183
191
 
184
- export { $REFRESH, CONFIG_AUTHORITATIVE_OBSERVED, CONFIG_AUTHORITATIVE_READ, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_CHILD_COMPANIONS, CONFIG_DIRECT_COMMIT, CONFIG_FRESH_READ, CONFIG_FW_CHILDREN, CONFIG_HAS_COMPANIONS, CONFIG_HAS_LANE, CONFIG_HAS_SNAPSHOT, CONFIG_HELD_TRUTH, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_NO_SNAPSHOT, CONFIG_OPTIMISTIC, CONFIG_OWNED_WRITE, CONFIG_SYNC, CONFIG_TRANSPARENT, EFFECT_RENDER, EFFECT_TRACKED, EFFECT_USER, NOT_PENDING, NO_SNAPSHOT, OVERRIDE_UNDEFINED, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_DISPOSED, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_LAZY, REACTIVE_MANUAL_WRITE, REACTIVE_MISSED_WAKE, REACTIVE_NONE, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, REACTIVE_SNAPSHOT_STALE, REACTIVE_ZOMBIE, STATUS_ERROR, STATUS_PENDING, STATUS_UNINITIALIZED, STORE_SNAPSHOT_PROPS, SUPPORTS_PROXY, defaultContext, unwrapOverride };
192
+ export { $REFRESH, CONFIG_AUTHORITATIVE_OBSERVED, CONFIG_AUTHORITATIVE_READ, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_CHILD_COMPANIONS, CONFIG_DIRECT_COMMIT, CONFIG_FRESH_READ, CONFIG_FW_CHILDREN, CONFIG_HAS_COMPANIONS, CONFIG_HAS_LANE, CONFIG_HAS_SNAPSHOT, CONFIG_HELD_TRUTH, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_NO_SNAPSHOT, CONFIG_OPTIMISTIC, CONFIG_OWNED_WRITE, CONFIG_SLOT_NODE, CONFIG_SYNC, CONFIG_TRANSPARENT, EFFECT_RENDER, EFFECT_TRACKED, EFFECT_USER, NOT_PENDING, NO_SNAPSHOT, OVERRIDE_UNDEFINED, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_DISPOSED, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_LAZY, REACTIVE_MANUAL_WRITE, REACTIVE_MISSED_WAKE, REACTIVE_NONE, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, REACTIVE_SNAPSHOT_STALE, REACTIVE_ZOMBIE, STATUS_ERROR, STATUS_PENDING, STATUS_UNINITIALIZED, STORE_SNAPSHOT_PROPS, SUPPORTS_PROXY, defaultContext, unwrapOverride };
@@ -29,11 +29,13 @@ import { getOwner } from "./owner.js";
29
29
  if (!t) {
30
30
  throw new NoOwnerError;
31
31
  }
32
- const n = hasContext(e, t) ? t.we[e.id] : e.defaultValue;
33
- if (isUndefined(n)) {
32
+ // `undefined` alone means unset a provided `null` is a value (no `??`).
33
+ let r = t.we[e.id];
34
+ if (r === undefined) r = e.defaultValue;
35
+ if (r === undefined) {
34
36
  throw new ContextNotFoundError;
35
37
  }
36
- return n;
38
+ return r;
37
39
  }
38
40
 
39
41
  /**
@@ -44,24 +46,16 @@ import { getOwner } from "./owner.js";
44
46
  * @throws `NoOwnerError` if there's no owner at the time of call.
45
47
  *
46
48
  * @internal
47
- */ function setContext(e, t, n = getOwner()) {
48
- if (!n) {
49
+ */ function setContext(e, t, r = getOwner()) {
50
+ if (!r) {
49
51
  throw new NoOwnerError;
50
52
  }
51
53
  // We're creating a new object to avoid child context values being exposed to parent owners. If
52
54
  // we don't do this, everything will be a singleton and all hell will break lose.
53
- n.we = {
54
- ...n.we,
55
- [e.id]: isUndefined(t) ? e.defaultValue : t
55
+ r.we = {
56
+ ...r.we,
57
+ [e.id]: t === undefined ? e.defaultValue : t
56
58
  };
57
59
  }
58
60
 
59
- function hasContext(e, t) {
60
- return !isUndefined(t?.we[e.id]);
61
- }
62
-
63
- function isUndefined(e) {
64
- return typeof e === "undefined";
65
- }
66
-
67
61
  export { createContext, getContext, setContext };