@solidjs/signals 0.13.13 → 2.0.0-beta.10

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 (55) hide show
  1. package/README.md +8 -7
  2. package/dist/dev.js +502 -244
  3. package/dist/node.cjs +1501 -1308
  4. package/dist/prod.js +1134 -929
  5. package/dist/types/boundaries.d.ts +124 -3
  6. package/dist/types/core/action.d.ts +34 -0
  7. package/dist/types/core/constants.d.ts +24 -0
  8. package/dist/types/core/context.d.ts +10 -2
  9. package/dist/types/core/core.d.ts +108 -9
  10. package/dist/types/core/dev.d.ts +1 -1
  11. package/dist/types/core/effect.d.ts +3 -2
  12. package/dist/types/core/error.d.ts +24 -0
  13. package/dist/types/core/external.d.ts +19 -0
  14. package/dist/types/core/graph.d.ts +1 -0
  15. package/dist/types/core/owner.d.ts +93 -3
  16. package/dist/types/core/scheduler.d.ts +27 -2
  17. package/dist/types/core/types.d.ts +3 -6
  18. package/dist/types/index.d.ts +2 -2
  19. package/dist/types/map.d.ts +32 -3
  20. package/dist/types/signals.d.ts +338 -39
  21. package/dist/types/store/optimistic.d.ts +38 -12
  22. package/dist/types/store/projection.d.ts +55 -15
  23. package/dist/types/store/reconcile.d.ts +22 -0
  24. package/dist/types/store/store.d.ts +72 -15
  25. package/dist/types/store/storePath.d.ts +28 -0
  26. package/dist/types/store/utils.d.ts +78 -7
  27. package/dist/types-cjs/boundaries.d.cts +173 -0
  28. package/dist/types-cjs/core/action.d.cts +35 -0
  29. package/dist/types-cjs/core/async.d.cts +6 -0
  30. package/dist/types-cjs/core/constants.d.cts +49 -0
  31. package/dist/types-cjs/core/context.d.cts +36 -0
  32. package/dist/types-cjs/core/core.d.cts +153 -0
  33. package/dist/types-cjs/core/dev.d.cts +49 -0
  34. package/dist/types-cjs/core/effect.d.cts +31 -0
  35. package/dist/types-cjs/core/error.d.cts +38 -0
  36. package/dist/types-cjs/core/external.d.cts +45 -0
  37. package/dist/types-cjs/core/graph.d.cts +4 -0
  38. package/dist/types-cjs/core/heap.d.cts +14 -0
  39. package/dist/types-cjs/core/index.d.cts +12 -0
  40. package/dist/types-cjs/core/lanes.d.cts +54 -0
  41. package/dist/types-cjs/core/owner.d.cts +116 -0
  42. package/dist/types-cjs/core/scheduler.d.cts +106 -0
  43. package/dist/types-cjs/core/types.d.cts +83 -0
  44. package/dist/types-cjs/index.d.cts +9 -0
  45. package/dist/types-cjs/map.d.cts +53 -0
  46. package/dist/types-cjs/package.json +3 -0
  47. package/dist/types-cjs/signals.d.cts +491 -0
  48. package/dist/types-cjs/store/index.d.cts +9 -0
  49. package/dist/types-cjs/store/optimistic.d.cts +45 -0
  50. package/dist/types-cjs/store/projection.d.cts +66 -0
  51. package/dist/types-cjs/store/reconcile.d.cts +23 -0
  52. package/dist/types-cjs/store/store.d.cts +125 -0
  53. package/dist/types-cjs/store/storePath.d.cts +58 -0
  54. package/dist/types-cjs/store/utils.d.cts +114 -0
  55. package/package.json +32 -24
package/dist/dev.js CHANGED
@@ -35,6 +35,12 @@ const REACTIVE_DISPOSED = 1 << 6;
35
35
  const REACTIVE_OPTIMISTIC_DIRTY = 1 << 7;
36
36
  const REACTIVE_SNAPSHOT_STALE = 1 << 8;
37
37
  const REACTIVE_LAZY = 1 << 9;
38
+ const CONFIG_OWNED_WRITE = 1 << 0;
39
+ const CONFIG_NO_SNAPSHOT = 1 << 1;
40
+ const CONFIG_TRANSPARENT = 1 << 2;
41
+ const CONFIG_IN_SNAPSHOT_SCOPE = 1 << 3;
42
+ const CONFIG_CHILDREN_FORBIDDEN = 1 << 4;
43
+ const CONFIG_AUTO_DISPOSE = 1 << 5;
38
44
  const STATUS_PENDING = 1 << 0;
39
45
  const STATUS_ERROR = 1 << 1;
40
46
  const STATUS_UNINITIALIZED = 1 << 2;
@@ -240,6 +246,23 @@ let inTrackedQueueCallback = false;
240
246
  let _enforceLoadingBoundary = false;
241
247
  let _hitUnhandledAsync = false;
242
248
  let stashedOptimisticReads = null;
249
+ const transientStoreNodes = new Set();
250
+ function registerTransientStoreNode(node) {
251
+ transientStoreNodes.add(node);
252
+ }
253
+ function sweepTransientStoreNodes() {
254
+ if (transientStoreNodes.size === 0) return;
255
+ for (const node of transientStoreNodes) {
256
+ if (node._subs !== null) {
257
+ transientStoreNodes.delete(node);
258
+ continue;
259
+ }
260
+ if (node._pendingValue !== NOT_PENDING) continue;
261
+ if (node._overrideValue !== undefined && node._overrideValue !== NOT_PENDING) continue;
262
+ transientStoreNodes.delete(node);
263
+ node._unobserved?.();
264
+ }
265
+ }
243
266
  function resetUnhandledAsync() {
244
267
  _hitUnhandledAsync = false;
245
268
  }
@@ -292,6 +315,7 @@ function mergeTransitionState(target, outgoing) {
292
315
  if (!targetReporters) target._asyncReporters.set(source, (targetReporters = new Set()));
293
316
  for (const reporter of reporters) targetReporters.add(reporter);
294
317
  }
318
+ for (const sub of outgoing._gatedSubs) target._gatedSubs.add(sub);
295
319
  }
296
320
  function resolveOptimisticNodes(nodes) {
297
321
  for (let i = 0; i < nodes.length; i++) {
@@ -422,11 +446,15 @@ class GlobalQueue extends Queue {
422
446
  scheduled = dirtyQueue._max >= dirtyQueue._min;
423
447
  reassignPendingTransition(stashedTransition._pendingNodes);
424
448
  activeTransition = null;
425
- if (!stashedTransition._actions.length && stashedTransition._optimisticNodes.length) {
449
+ if (
450
+ !stashedTransition._actions.length &&
451
+ !stashedTransition._asyncReporters.size &&
452
+ stashedTransition._optimisticNodes.length
453
+ ) {
426
454
  stashedOptimisticReads = new Set();
427
455
  for (let i = 0; i < stashedTransition._optimisticNodes.length; i++) {
428
456
  const node = stashedTransition._optimisticNodes[i];
429
- if (node._fn || node._pureWrite) continue;
457
+ if (node._fn || node._config & CONFIG_OWNED_WRITE) continue;
430
458
  stashedOptimisticReads.add(node);
431
459
  queueStashedOptimisticEffects(node);
432
460
  }
@@ -492,7 +520,8 @@ class GlobalQueue extends Queue {
492
520
  _optimisticStores: new Set(),
493
521
  _actions: [],
494
522
  _queueStash: { _queues: [[], []], _children: [] },
495
- _done: false
523
+ _done: false,
524
+ _gatedSubs: new Set()
496
525
  };
497
526
  } else if (transition) {
498
527
  const outgoing = activeTransition;
@@ -531,7 +560,7 @@ function insertSubs(node, optimistic = false) {
531
560
  const sourceLane = node._optimisticLane || currentOptimisticLane;
532
561
  const hasSnapshot = node._snapshotValue !== undefined;
533
562
  for (let s = node._subs; s !== null; s = s._nextSub) {
534
- if (hasSnapshot && s._sub._inSnapshotScope) {
563
+ if (hasSnapshot && s._sub._config & CONFIG_IN_SNAPSHOT_SCOPE) {
535
564
  s._sub._flags |= REACTIVE_SNAPSHOT_STALE;
536
565
  continue;
537
566
  }
@@ -579,6 +608,22 @@ function finalizePureQueue(completingTransition = null, incomplete = false) {
579
608
  resolveOptimisticNodes(
580
609
  completingTransition ? completingTransition._optimisticNodes : globalQueue._optimisticNodes
581
610
  );
611
+ if (completingTransition && completingTransition._gatedSubs.size) {
612
+ for (const sub of completingTransition._gatedSubs) {
613
+ if (sub._flags & REACTIVE_DISPOSED) continue;
614
+ if (sub._type === EFFECT_TRACKED) {
615
+ if (!sub._modified) {
616
+ sub._modified = true;
617
+ sub._queue.enqueue(EFFECT_USER, sub._run);
618
+ }
619
+ continue;
620
+ }
621
+ const queue = sub._flags & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue;
622
+ if (queue._min > sub._height) queue._min = sub._height;
623
+ insertIntoHeap(sub, queue);
624
+ }
625
+ completingTransition._gatedSubs.clear();
626
+ }
582
627
  const optimisticStores = completingTransition
583
628
  ? completingTransition._optimisticStores
584
629
  : globalQueue._optimisticStores;
@@ -589,6 +634,7 @@ function finalizePureQueue(completingTransition = null, incomplete = false) {
589
634
  optimisticStores.clear();
590
635
  schedule();
591
636
  }
637
+ sweepTransientStoreNodes();
592
638
  cleanupCompletedLanes(completingTransition);
593
639
  }
594
640
  }
@@ -778,7 +824,8 @@ function unlinkSubs(link) {
778
824
  dep._subs = nextSub;
779
825
  if (nextSub === null) {
780
826
  dep._unobserved?.();
781
- dep._fn && !dep._preventAutoDisposal && !(dep._flags & REACTIVE_ZOMBIE) && unobserved(dep);
827
+ const c = dep;
828
+ c._fn && c._config & CONFIG_AUTO_DISPOSE && !(c._flags & REACTIVE_ZOMBIE) && unobserved(c);
782
829
  }
783
830
  }
784
831
  return nextDep;
@@ -790,6 +837,7 @@ function unobserved(el) {
790
837
  dep = unlinkSubs(dep);
791
838
  }
792
839
  el._deps = null;
840
+ el._depsTail = null;
793
841
  disposeChildren(el, true);
794
842
  }
795
843
  function link(dep, sub) {
@@ -894,7 +942,7 @@ function runDisposal(node, zombie) {
894
942
  }
895
943
  function childId(owner, consume) {
896
944
  let counter = owner;
897
- while (counter._transparent && counter._parent) counter = counter._parent;
945
+ while (counter._config & CONFIG_TRANSPARENT && counter._parent) counter = counter._parent;
898
946
  if (counter.id != null)
899
947
  return formatId(counter.id, consume ? counter._childCount++ : counter._childCount);
900
948
  throw new Error("Cannot get child id from owner without an id");
@@ -934,7 +982,7 @@ function createOwner(options) {
934
982
  id:
935
983
  options?.id ??
936
984
  (transparent ? parent?.id : parent?.id != null ? getNextChildId(parent) : undefined),
937
- _transparent: transparent || undefined,
985
+ _config: transparent ? CONFIG_TRANSPARENT : 0,
938
986
  _root: true,
939
987
  _parentComputed: parent?._root ? parent._parentComputed : parent,
940
988
  _firstChild: null,
@@ -950,10 +998,16 @@ function createOwner(options) {
950
998
  disposeChildren(owner, self);
951
999
  }
952
1000
  };
953
- if (parent?._childrenForbidden) {
954
- throw new Error(
955
- "Cannot create reactive primitives inside createTrackedEffect or owner-backed onSettled"
956
- );
1001
+ if (parent && parent._config & CONFIG_CHILDREN_FORBIDDEN) {
1002
+ emitDiagnostic({
1003
+ code: "PRIMITIVE_IN_FORBIDDEN_SCOPE",
1004
+ kind: "lifecycle",
1005
+ severity: "error",
1006
+ message: PRIMITIVE_IN_FORBIDDEN_SCOPE_MESSAGE,
1007
+ ownerId: parent.id,
1008
+ ownerName: parent._name
1009
+ });
1010
+ throw new Error(PRIMITIVE_IN_FORBIDDEN_SCOPE_MESSAGE);
957
1011
  }
958
1012
  if (parent) {
959
1013
  const lastChild = parent._firstChild;
@@ -1280,6 +1334,8 @@ function enableExternalSource(config) {
1280
1334
  }
1281
1335
  GlobalQueue._update = recompute;
1282
1336
  GlobalQueue._dispose = disposeChildren;
1337
+ const PRIMITIVE_IN_FORBIDDEN_SCOPE_MESSAGE =
1338
+ "[PRIMITIVE_IN_FORBIDDEN_SCOPE] Cannot create reactive primitives inside createTrackedEffect or owner-backed onSettled";
1283
1339
  let tracking = false;
1284
1340
  let stale = false;
1285
1341
  let refreshing = false;
@@ -1318,7 +1374,7 @@ function releaseSubtree(owner) {
1318
1374
  }
1319
1375
  if (child._fn) {
1320
1376
  const comp = child;
1321
- comp._inSnapshotScope = false;
1377
+ comp._config &= ~CONFIG_IN_SNAPSHOT_SCOPE;
1322
1378
  if (comp._flags & REACTIVE_SNAPSHOT_STALE) {
1323
1379
  comp._flags &= ~REACTIVE_SNAPSHOT_STALE;
1324
1380
  comp._flags |= REACTIVE_DIRTY;
@@ -1358,7 +1414,7 @@ function recompute(el, create = false) {
1358
1414
  clearSignals(el);
1359
1415
  }
1360
1416
  }
1361
- const isOptimisticDirty = !!(el._flags & REACTIVE_OPTIMISTIC_DIRTY);
1417
+ let isOptimisticDirty = !!(el._flags & REACTIVE_OPTIMISTIC_DIRTY);
1362
1418
  const hasOverride = el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING;
1363
1419
  const wasPending = !!(el._statusFlags & STATUS_PENDING);
1364
1420
  const wasUninitialized = !!(el._statusFlags & STATUS_UNINITIALIZED);
@@ -1380,9 +1436,25 @@ function recompute(el, create = false) {
1380
1436
  if (isOptimisticDirty) {
1381
1437
  const lane = resolveLane(el);
1382
1438
  if (lane) currentOptimisticLane = lane;
1439
+ } else if (activeTransition && !create && activeTransition._optimisticNodes.length) {
1440
+ for (let d = el._deps; d; d = d._nextDep) {
1441
+ const dep = d._dep;
1442
+ if (dep._flags & REACTIVE_OPTIMISTIC_DIRTY) {
1443
+ const depLane = resolveLane(dep);
1444
+ if (depLane) {
1445
+ isOptimisticDirty = true;
1446
+ currentOptimisticLane = depLane;
1447
+ el._flags |= REACTIVE_OPTIMISTIC_DIRTY;
1448
+ assignOrMergeLane(el, depLane);
1449
+ break;
1450
+ }
1451
+ }
1452
+ }
1383
1453
  }
1384
1454
  try {
1385
- value = handleAsync(el, el._fn(value));
1455
+ const prevInFlight = el._inFlight;
1456
+ const fnResult = el._fn(value);
1457
+ value = el._inFlight !== prevInFlight ? fnResult : handleAsync(el, fnResult);
1386
1458
  clearStatus(el, create);
1387
1459
  const resolvedLane = resolveLane(el);
1388
1460
  if (resolvedLane) {
@@ -1481,22 +1553,25 @@ function updateIfNecessary(el) {
1481
1553
  }
1482
1554
  el._flags = el._flags & (REACTIVE_SNAPSHOT_STALE | REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT);
1483
1555
  }
1484
- function computed(fn, initialValue, options) {
1556
+ function computed(fn, options) {
1485
1557
  const transparent = options?.transparent ?? false;
1486
1558
  const self = {
1487
1559
  id:
1488
1560
  options?.id ??
1489
1561
  (transparent ? context?.id : context?.id != null ? getNextChildId(context) : undefined),
1490
- _transparent: transparent || undefined,
1562
+ _config:
1563
+ (transparent ? CONFIG_TRANSPARENT : 0) |
1564
+ (options?.ownedWrite ? CONFIG_OWNED_WRITE : 0) |
1565
+ (!context || options?.lazy ? CONFIG_AUTO_DISPOSE : 0) |
1566
+ (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
1491
1567
  _equals: options?.equals != null ? options.equals : isEqual,
1492
- _pureWrite: !!options?.pureWrite,
1493
1568
  _unobserved: options?.unobserved,
1494
1569
  _disposal: null,
1495
1570
  _queue: context?._queue ?? globalQueue,
1496
1571
  _context: context?._context ?? defaultContext,
1497
1572
  _childCount: 0,
1498
1573
  _fn: fn,
1499
- _value: initialValue,
1574
+ _value: undefined,
1500
1575
  _height: 0,
1501
1576
  _child: null,
1502
1577
  _nextHeap: undefined,
@@ -1520,10 +1595,16 @@ function computed(fn, initialValue, options) {
1520
1595
  self._name = options?.name ?? "computed";
1521
1596
  self._prevHeap = self;
1522
1597
  const parent = context?._root ? context._parentComputed : context;
1523
- if (context?._childrenForbidden) {
1524
- throw new Error(
1525
- "Cannot create reactive primitives inside createTrackedEffect or owner-backed onSettled"
1526
- );
1598
+ if (context && context._config & CONFIG_CHILDREN_FORBIDDEN) {
1599
+ emitDiagnostic({
1600
+ code: "PRIMITIVE_IN_FORBIDDEN_SCOPE",
1601
+ kind: "lifecycle",
1602
+ severity: "error",
1603
+ message: PRIMITIVE_IN_FORBIDDEN_SCOPE_MESSAGE,
1604
+ ownerId: context.id,
1605
+ ownerName: context._name
1606
+ });
1607
+ throw new Error(PRIMITIVE_IN_FORBIDDEN_SCOPE_MESSAGE);
1527
1608
  }
1528
1609
  if (context) {
1529
1610
  const lastChild = context._firstChild;
@@ -1536,9 +1617,8 @@ function computed(fn, initialValue, options) {
1536
1617
  }
1537
1618
  DEV$1.hooks.onOwner?.(self);
1538
1619
  if (parent) self._height = parent._height + 1;
1539
- if (snapshotCaptureActive && ownerInSnapshotScope(context)) self._inSnapshotScope = true;
1540
1620
  if (externalSourceConfig) {
1541
- const bridgeSignal = signal(undefined, { equals: false, pureWrite: true });
1621
+ const bridgeSignal = signal(undefined, { equals: false, ownedWrite: true });
1542
1622
  const source = externalSourceConfig.factory(self._fn, () => {
1543
1623
  setSignal(bridgeSignal, undefined);
1544
1624
  });
@@ -1560,8 +1640,9 @@ function computed(fn, initialValue, options) {
1560
1640
  function signal(v, options, firewall = null) {
1561
1641
  const s = {
1562
1642
  _equals: options?.equals != null ? options.equals : isEqual,
1563
- _pureWrite: !!options?.pureWrite,
1564
- _noSnapshot: !!options?._noSnapshot,
1643
+ _config:
1644
+ (options?.ownedWrite ? CONFIG_OWNED_WRITE : 0) |
1645
+ (options?._noSnapshot ? CONFIG_NO_SNAPSHOT : 0),
1565
1646
  _unobserved: options?.unobserved,
1566
1647
  _value: v,
1567
1648
  _subs: null,
@@ -1578,7 +1659,7 @@ function signal(v, options, firewall = null) {
1578
1659
  firewall && (firewall._child = s);
1579
1660
  if (
1580
1661
  snapshotCaptureActive &&
1581
- !s._noSnapshot &&
1662
+ !(s._config & CONFIG_NO_SNAPSHOT) &&
1582
1663
  !((firewall?._statusFlags ?? 0) & STATUS_PENDING)
1583
1664
  ) {
1584
1665
  s._snapshotValue = v === undefined ? NO_SNAPSHOT : v;
@@ -1591,8 +1672,8 @@ function optimisticSignal(v, options) {
1591
1672
  s._overrideValue = NOT_PENDING;
1592
1673
  return s;
1593
1674
  }
1594
- function optimisticComputed(fn, initialValue, options) {
1595
- const c = computed(fn, initialValue, options);
1675
+ function optimisticComputed(fn, options) {
1676
+ const c = computed(fn, options);
1596
1677
  c._overrideValue = NOT_PENDING;
1597
1678
  return c;
1598
1679
  }
@@ -1672,15 +1753,21 @@ function read(el) {
1672
1753
  }
1673
1754
  let c = context;
1674
1755
  if (c?._root) c = c._parentComputed;
1675
- if (refreshing && el._fn) recompute(el);
1676
- if (el._flags & REACTIVE_LAZY) {
1677
- el._flags &= ~REACTIVE_LAZY;
1678
- recompute(el, true);
1756
+ const computed = el;
1757
+ if (typeof computed._fn === "function") {
1758
+ const comp = el;
1759
+ if (refreshing && !(comp._flags & REACTIVE_DISPOSED)) recompute(comp);
1760
+ if (comp._flags & REACTIVE_LAZY) {
1761
+ comp._flags &= ~REACTIVE_LAZY;
1762
+ recompute(comp, true);
1763
+ } else if (comp._flags & REACTIVE_DISPOSED) {
1764
+ recompute(comp, true);
1765
+ }
1679
1766
  }
1680
1767
  const owner = el._firewall || el;
1681
1768
  if (strictRead && owner._statusFlags & STATUS_PENDING) {
1682
1769
  const message =
1683
- `Reading a pending async value directly in ${strictRead}. ` +
1770
+ `[PENDING_ASYNC_UNTRACKED_READ] Reading a pending async value directly in ${strictRead}. ` +
1684
1771
  `Async values must be read within a tracking scope (JSX, a memo, or an effect's compute function).`;
1685
1772
  emitDiagnostic({
1686
1773
  code: "PENDING_ASYNC_UNTRACKED_READ",
@@ -1695,7 +1782,6 @@ function read(el) {
1695
1782
  throw new Error(message);
1696
1783
  }
1697
1784
  if (c && tracking) {
1698
- if (el._fn && el._flags & REACTIVE_DISPOSED) recompute(el);
1699
1785
  link(el, c);
1700
1786
  if (owner._fn) {
1701
1787
  const isZombie = el._flags & REACTIVE_ZOMBIE;
@@ -1712,9 +1798,9 @@ function read(el) {
1712
1798
  }
1713
1799
  if (owner._statusFlags & STATUS_PENDING) {
1714
1800
  if (c && !(stale && owner._transition && activeTransition !== owner._transition)) {
1715
- if (c?._childrenForbidden) {
1801
+ if (c && c._config & CONFIG_CHILDREN_FORBIDDEN) {
1716
1802
  const message =
1717
- "Reading a pending async value inside createTrackedEffect or onSettled will throw. " +
1803
+ "[PENDING_ASYNC_FORBIDDEN_SCOPE] Reading a pending async value inside createTrackedEffect or onSettled will throw. " +
1718
1804
  "Use createEffect instead which supports async-aware reactivity.";
1719
1805
  emitDiagnostic({
1720
1806
  code: "PENDING_ASYNC_FORBIDDEN_SCOPE",
@@ -1751,7 +1837,7 @@ function read(el) {
1751
1837
  return read(el);
1752
1838
  } else throw el._error;
1753
1839
  }
1754
- if (snapshotCaptureActive && c && c._inSnapshotScope) {
1840
+ if (snapshotCaptureActive && c && c._config & CONFIG_IN_SNAPSHOT_SCOPE) {
1755
1841
  const sv = el._snapshotValue;
1756
1842
  if (sv !== undefined) {
1757
1843
  const snapshot = sv === NO_SNAPSHOT ? undefined : sv;
@@ -1762,7 +1848,7 @@ function read(el) {
1762
1848
  }
1763
1849
  if (strictRead) {
1764
1850
  const message =
1765
- `Reactive value read directly in ${strictRead} will not update. ` +
1851
+ `[STRICT_READ_UNTRACKED] Reactive value read directly in ${strictRead} will not update. ` +
1766
1852
  `Move it into a tracking scope (JSX, a memo, or an effect's compute function).`;
1767
1853
  emitDiagnostic({
1768
1854
  code: "STRICT_READ_UNTRACKED",
@@ -1780,7 +1866,20 @@ function read(el) {
1780
1866
  if (c && stale && shouldReadStashedOptimisticValue(el)) return el._value;
1781
1867
  return el._overrideValue;
1782
1868
  }
1783
- return !c ||
1869
+ if (
1870
+ activeTransition !== null &&
1871
+ currentOptimisticLane !== null &&
1872
+ !latestReadActive &&
1873
+ el._pendingValue !== NOT_PENDING &&
1874
+ owner === el &&
1875
+ !el._fn &&
1876
+ c
1877
+ ) {
1878
+ activeTransition._gatedSubs.add(c);
1879
+ return el._value;
1880
+ }
1881
+ const value =
1882
+ !c ||
1784
1883
  (currentOptimisticLane !== null &&
1785
1884
  (el._overrideValue !== undefined ||
1786
1885
  el._optimisticLane ||
@@ -1788,14 +1887,30 @@ function read(el) {
1788
1887
  !!(owner._statusFlags & STATUS_PENDING))) ||
1789
1888
  el._pendingValue === NOT_PENDING ||
1790
1889
  (stale && el._transition && activeTransition !== el._transition)
1791
- ? el._value
1792
- : el._pendingValue;
1890
+ ? el._value
1891
+ : el._pendingValue;
1892
+ if (
1893
+ !c &&
1894
+ owner === el &&
1895
+ typeof computed._fn === "function" &&
1896
+ el._config & CONFIG_AUTO_DISPOSE &&
1897
+ !(owner._statusFlags & STATUS_PENDING) &&
1898
+ !el._subs
1899
+ ) {
1900
+ unobserved(el);
1901
+ }
1902
+ return value;
1793
1903
  }
1794
1904
  function setSignal(el, v) {
1795
- if (!el._pureWrite && !context?._childrenForbidden && context && el._firewall !== context) {
1905
+ if (
1906
+ !(el._config & CONFIG_OWNED_WRITE) &&
1907
+ !(context && context._config & CONFIG_CHILDREN_FORBIDDEN) &&
1908
+ context &&
1909
+ el._firewall !== context
1910
+ ) {
1796
1911
  const message =
1797
- "Writing to a Signal inside an owned scope (component, computation) is not allowed. " +
1798
- "Move the write outside or set the `pureWrite` option if this is intentional.";
1912
+ "[SIGNAL_WRITE_IN_OWNED_SCOPE] Writing to a Signal inside an owned scope (component, computation) is not allowed. " +
1913
+ "Move the write outside or set the `ownedWrite` option if this is intentional.";
1799
1914
  emitDiagnostic({
1800
1915
  code: "SIGNAL_WRITE_IN_OWNED_SCOPE",
1801
1916
  kind: "write",
@@ -1855,7 +1970,7 @@ function setSignal(el, v) {
1855
1970
  function runWithOwner(owner, fn) {
1856
1971
  if (owner && owner._flags & REACTIVE_DISPOSED) {
1857
1972
  const message =
1858
- "runWithOwner called with a disposed owner. Children created inside will never be disposed.";
1973
+ "[RUN_WITH_DISPOSED_OWNER] runWithOwner called with a disposed owner. Children created inside will never be disposed.";
1859
1974
  emitDiagnostic({
1860
1975
  code: "RUN_WITH_DISPOSED_OWNER",
1861
1976
  kind: "owner",
@@ -1879,7 +1994,7 @@ function runWithOwner(owner, fn) {
1879
1994
  }
1880
1995
  function getPendingSignal(el) {
1881
1996
  if (!el._pendingSignal) {
1882
- el._pendingSignal = optimisticSignal(false, { pureWrite: true });
1997
+ el._pendingSignal = optimisticSignal(false, { ownedWrite: true });
1883
1998
  if (el._parentSource) {
1884
1999
  el._pendingSignal._parentSource = el;
1885
2000
  }
@@ -2022,26 +2137,24 @@ function hasContext(context, owner) {
2022
2137
  function isUndefined(value) {
2023
2138
  return typeof value === "undefined";
2024
2139
  }
2025
- function effect(compute, effect, error, initialValue, options) {
2140
+ function effect(compute, effect, error, options) {
2026
2141
  let initialized = false;
2027
- const node = computed(
2028
- options?.render ? p => staleValues(() => compute(p)) : compute,
2029
- initialValue,
2030
- {
2031
- ...options,
2032
- equals: () => {
2033
- node._modified = !node._error;
2034
- if (initialized) node._queue.enqueue(node._type, runEffect.bind(node));
2035
- return false;
2036
- },
2037
- lazy: true
2038
- }
2039
- );
2040
- node._prevValue = initialValue;
2142
+ const isUser = !!options?.user;
2143
+ const node = computed(isUser ? compute : p => staleValues(() => compute(p)), {
2144
+ ...options,
2145
+ equals: () => {
2146
+ node._modified = !node._error;
2147
+ if (initialized) node._queue.enqueue(node._type, runEffect.bind(node));
2148
+ return false;
2149
+ },
2150
+ lazy: true
2151
+ });
2152
+ node._config &= ~CONFIG_AUTO_DISPOSE;
2153
+ node._prevValue = undefined;
2041
2154
  node._effectFn = effect;
2042
2155
  node._errorFn = error;
2043
2156
  node._cleanup = undefined;
2044
- node._type = options?.render ? EFFECT_RENDER : EFFECT_USER;
2157
+ node._type = isUser ? EFFECT_USER : EFFECT_RENDER;
2045
2158
  node._notifyStatus = (status, error) => {
2046
2159
  const actualStatus = status !== undefined ? status : node._statusFlags;
2047
2160
  const actualError = error !== undefined ? error : node._error;
@@ -2065,28 +2178,32 @@ function effect(compute, effect, error, initialValue, options) {
2065
2178
  node._queue.notify(node, STATUS_PENDING | STATUS_ERROR, actualStatus, actualError);
2066
2179
  if (_hitUnhandledAsync) {
2067
2180
  resetUnhandledAsync();
2068
- const err = new Error("An async value must be rendered inside a Loading boundary.");
2069
- emitDiagnostic({
2070
- code: "ASYNC_OUTSIDE_LOADING_BOUNDARY",
2071
- kind: "async",
2072
- severity: "error",
2073
- message: err.message,
2074
- ownerId: node.id,
2075
- ownerName: node._name
2076
- });
2077
- if (!node._queue.notify(node, STATUS_ERROR, STATUS_ERROR)) throw err;
2181
+ if (!node._queue.notify(node, STATUS_ERROR, STATUS_ERROR)) {
2182
+ const message =
2183
+ "[ASYNC_OUTSIDE_LOADING_BOUNDARY] An async value was read outside a Loading boundary. The root mount will be deferred until all pending async settles.";
2184
+ emitDiagnostic({
2185
+ code: "ASYNC_OUTSIDE_LOADING_BOUNDARY",
2186
+ kind: "async",
2187
+ severity: "warn",
2188
+ message: message,
2189
+ ownerId: node.id,
2190
+ ownerName: node._name
2191
+ });
2192
+ console.warn(message);
2193
+ }
2078
2194
  }
2079
2195
  }
2080
2196
  };
2081
2197
  recompute(node, true);
2082
2198
  !options?.defer &&
2083
- (node._type === EFFECT_USER
2199
+ (node._type === EFFECT_USER || options?.schedule
2084
2200
  ? node._queue.enqueue(node._type, runEffect.bind(node))
2085
2201
  : runEffect.call(node));
2086
2202
  initialized = true;
2087
2203
  cleanup(() => node._cleanup?.());
2088
2204
  if (!node._parent) {
2089
- const message = "Effects created outside a reactive context will never be disposed";
2205
+ const message =
2206
+ "[NO_OWNER_EFFECT] Effects created outside a reactive context will never be disposed";
2090
2207
  emitDiagnostic({
2091
2208
  code: "NO_OWNER_EFFECT",
2092
2209
  kind: "lifecycle",
@@ -2148,11 +2265,10 @@ function trackedEffect(fn, options) {
2148
2265
  }
2149
2266
  node._cleanup = cleanup;
2150
2267
  },
2151
- undefined,
2152
2268
  { ...options, lazy: true }
2153
2269
  );
2154
2270
  node._cleanup = undefined;
2155
- node._childrenForbidden = true;
2271
+ node._config = (node._config & ~CONFIG_AUTO_DISPOSE) | CONFIG_CHILDREN_FORBIDDEN;
2156
2272
  node._modified = true;
2157
2273
  node._type = EFFECT_TRACKED;
2158
2274
  node._notifyStatus = (status, error) => {
@@ -2167,7 +2283,8 @@ function trackedEffect(fn, options) {
2167
2283
  node._queue.enqueue(EFFECT_USER, run);
2168
2284
  cleanup(() => node._cleanup?.());
2169
2285
  if (!node._parent) {
2170
- const message = "Effects created outside a reactive context will never be disposed";
2286
+ const message =
2287
+ "[NO_OWNER_EFFECT] Effects created outside a reactive context will never be disposed";
2171
2288
  emitDiagnostic({
2172
2289
  code: "NO_OWNER_EFFECT",
2173
2290
  kind: "lifecycle",
@@ -2228,7 +2345,8 @@ function onCleanup(fn) {
2228
2345
  {
2229
2346
  const owner = getOwner();
2230
2347
  if (!owner) {
2231
- const message = "onCleanup called outside a reactive context will never be run";
2348
+ const message =
2349
+ "[NO_OWNER_CLEANUP] onCleanup called outside a reactive context will never be run";
2232
2350
  emitDiagnostic({
2233
2351
  code: "NO_OWNER_CLEANUP",
2234
2352
  kind: "lifecycle",
@@ -2236,9 +2354,9 @@ function onCleanup(fn) {
2236
2354
  message: message
2237
2355
  });
2238
2356
  console.warn(message);
2239
- } else if (owner._childrenForbidden) {
2357
+ } else if (owner._config & CONFIG_CHILDREN_FORBIDDEN) {
2240
2358
  const message =
2241
- "Cannot use onCleanup inside createTrackedEffect or onSettled; return a cleanup function instead";
2359
+ "[CLEANUP_IN_FORBIDDEN_SCOPE] Cannot use onCleanup inside createTrackedEffect or onSettled; return a cleanup function instead";
2242
2360
  emitDiagnostic({
2243
2361
  code: "CLEANUP_IN_FORBIDDEN_SCOPE",
2244
2362
  kind: "lifecycle",
@@ -2253,35 +2371,44 @@ function onCleanup(fn) {
2253
2371
  return cleanup(fn);
2254
2372
  }
2255
2373
  function accessor(node) {
2256
- const fn = read.bind(null, node);
2257
- fn.$r = true;
2258
- return fn;
2374
+ return read.bind(null, node);
2259
2375
  }
2260
- function createSignal(first, second, third) {
2376
+ function createSignal(first, second) {
2261
2377
  if (typeof first === "function") {
2262
- const node = computed(first, second, third);
2378
+ const node = computed(first, second);
2379
+ node._config &= ~CONFIG_AUTO_DISPOSE;
2263
2380
  return [accessor(node), setSignal.bind(null, node)];
2264
2381
  }
2265
2382
  const node = signal(first, second);
2266
2383
  registerGraph(node, getOwner());
2267
2384
  return [accessor(node), setSignal.bind(null, node)];
2268
2385
  }
2269
- function createMemo(compute, value, options) {
2270
- let node = computed(compute, value, options);
2271
- return accessor(node);
2272
- }
2273
- function createEffect(compute, effectFn, value, options) {
2274
- effect(compute, effectFn.effect || effectFn, effectFn.error, value, {
2275
- ...options,
2276
- name: options?.name ?? "effect"
2277
- });
2386
+ function createMemo(compute, options) {
2387
+ return accessor(computed(compute, options));
2278
2388
  }
2279
- function createRenderEffect(compute, effectFn, value, options) {
2280
- effect(compute, effectFn, undefined, value, {
2281
- render: true,
2389
+ function createEffect(compute, effectFn, options) {
2390
+ if (effectFn === undefined) {
2391
+ const message =
2392
+ "[MISSING_EFFECT_FN] createEffect requires both a compute function and an effect function. " +
2393
+ "Use `createEffect(() => signal(), value => doWork(value))`. " +
2394
+ "If you want a derived value, use `createMemo`. " +
2395
+ "If you want a one-shot side effect, just call the function directly.";
2396
+ emitDiagnostic({
2397
+ code: "MISSING_EFFECT_FN",
2398
+ kind: "lifecycle",
2399
+ severity: "error",
2400
+ message: message
2401
+ });
2402
+ throw new Error(message);
2403
+ }
2404
+ effect(compute, effectFn.effect || effectFn, effectFn.error, {
2405
+ user: true,
2282
2406
  ...{ ...options, name: options?.name ?? "effect" }
2283
2407
  });
2284
2408
  }
2409
+ function createRenderEffect(compute, effectFn, options) {
2410
+ effect(compute, effectFn, undefined, { ...options, name: options?.name ?? "effect" });
2411
+ }
2285
2412
  function createTrackedEffect(compute, options) {
2286
2413
  trackedEffect(compute, { ...options, name: options?.name ?? "trackedEffect" });
2287
2414
  }
@@ -2305,8 +2432,11 @@ function createReaction(effectFn, options) {
2305
2432
  dispose(node);
2306
2433
  },
2307
2434
  effectFn.error,
2308
- undefined,
2309
- { defer: true, ...(true ? { ...options, name: options?.name ?? "effect" } : options) }
2435
+ {
2436
+ ...(true ? { ...options, name: options?.name ?? "effect" } : options),
2437
+ user: true,
2438
+ defer: true
2439
+ }
2310
2440
  );
2311
2441
  });
2312
2442
  };
@@ -2331,9 +2461,10 @@ function resolve(fn) {
2331
2461
  });
2332
2462
  });
2333
2463
  }
2334
- function createOptimistic(first, second, third) {
2464
+ function createOptimistic(first, second) {
2335
2465
  if (typeof first === "function") {
2336
- const node = optimisticComputed(first, second, third);
2466
+ const node = optimisticComputed(first, second);
2467
+ node._config &= ~CONFIG_AUTO_DISPOSE;
2337
2468
  return [accessor(node), setSignal.bind(null, node)];
2338
2469
  }
2339
2470
  const node = optimisticSignal(first, second);
@@ -2342,7 +2473,7 @@ function createOptimistic(first, second, third) {
2342
2473
  }
2343
2474
  function onSettled(callback) {
2344
2475
  const owner = getOwner();
2345
- owner && !owner._childrenForbidden
2476
+ owner && !(owner._config & CONFIG_CHILDREN_FORBIDDEN)
2346
2477
  ? createTrackedEffect(() => untrack(callback), { name: "onSettled" })
2347
2478
  : globalQueue.enqueue(EFFECT_USER, () => {
2348
2479
  const cleanup = callback();
@@ -2499,7 +2630,7 @@ function reconcile(value, key) {
2499
2630
  applyState(value, state, keyFn);
2500
2631
  };
2501
2632
  }
2502
- function createProjectionInternal(fn, initialValue = {}, options) {
2633
+ function createProjectionInternal(fn, seed, options) {
2503
2634
  let node;
2504
2635
  const wrappedMap = new WeakMap();
2505
2636
  const wrapper = s => {
@@ -2519,31 +2650,39 @@ function createProjectionInternal(fn, initialValue = {}, options) {
2519
2650
  wrappedMap.set(source, wrapped);
2520
2651
  return wrapped;
2521
2652
  };
2522
- const wrappedStore = wrapProjection(initialValue);
2523
- node = computed(() => {
2524
- const owner = getOwner();
2525
- let settled = false;
2526
- let result;
2527
- const draft = new Proxy(
2528
- wrappedStore,
2529
- createWriteTraps(() => !settled || owner._inFlight === result)
2530
- );
2531
- storeSetter(draft, s => {
2532
- result = fn(s);
2533
- settled = true;
2534
- const value = handleAsync(owner, result, value => {
2535
- value !== s &&
2536
- value !== undefined &&
2537
- storeSetter(wrappedStore, reconcile(value, options?.key || "id"));
2538
- });
2539
- value !== s && value !== undefined && reconcile(value, options?.key || "id")(wrappedStore);
2540
- });
2541
- });
2542
- node._preventAutoDisposal = true;
2653
+ const wrappedStore = wrapProjection(seed);
2654
+ node = computed(
2655
+ () => {
2656
+ if (!node) node = getOwner();
2657
+ runProjectionComputed(wrappedStore, fn, options?.key || "id");
2658
+ },
2659
+ options?.name ? { name: options.name } : undefined
2660
+ );
2661
+ node._config &= ~CONFIG_AUTO_DISPOSE;
2543
2662
  return { store: wrappedStore, node: node };
2544
2663
  }
2545
- function createProjection(fn, initialValue = {}, options) {
2546
- return createProjectionInternal(fn, initialValue, options).store;
2664
+ function createProjection(fn, seed, options) {
2665
+ return createProjectionInternal(fn, seed, options).store;
2666
+ }
2667
+ function runProjectionComputed(wrappedStore, fn, key, wrapCommit) {
2668
+ const owner = getOwner();
2669
+ let settled = false;
2670
+ let result;
2671
+ const draft = new Proxy(
2672
+ wrappedStore,
2673
+ createWriteTraps(() => !settled || owner._inFlight === result)
2674
+ );
2675
+ storeSetter(draft, s => {
2676
+ result = fn(s);
2677
+ settled = true;
2678
+ const commit = v => {
2679
+ if (v === s || v === undefined) return;
2680
+ const write = () => storeSetter(wrappedStore, reconcile(v, key));
2681
+ wrapCommit ? wrapCommit(write) : write();
2682
+ };
2683
+ commit(handleAsync(owner, result, commit));
2684
+ });
2685
+ return owner;
2547
2686
  }
2548
2687
  function createWriteTraps(isActive) {
2549
2688
  const traps = {
@@ -2607,6 +2746,7 @@ const STORE_VALUE = "v",
2607
2746
  STORE_OPTIMISTIC_OVERRIDE = "x",
2608
2747
  STORE_NODE = "n",
2609
2748
  STORE_HAS = "h",
2749
+ STORE_CUSTOM_PROTO = "c",
2610
2750
  STORE_WRAP = "w",
2611
2751
  STORE_LOOKUP = "l",
2612
2752
  STORE_FIREWALL = "f",
@@ -2617,6 +2757,7 @@ function createStoreProxy(value, traps = storeTraps, extend) {
2617
2757
  newTarget = [];
2618
2758
  newTarget.v = value;
2619
2759
  } else newTarget = { v: value };
2760
+ newTarget[STORE_CUSTOM_PROTO] = hasCustomPrototype(unwrapStoreValue(value));
2620
2761
  extend && extend(newTarget);
2621
2762
  return (newTarget[$PROXY] = new Proxy(newTarget, traps));
2622
2763
  }
@@ -2642,6 +2783,23 @@ function setWriteOverride(value) {
2642
2783
  function writeOnly(proxy) {
2643
2784
  return writeOverride || !!Writing?.has(proxy);
2644
2785
  }
2786
+ function unwrapStoreValue(value) {
2787
+ return value?.[$TARGET]?.[STORE_VALUE] ?? value;
2788
+ }
2789
+ function hasCustomPrototype(value) {
2790
+ if (Array.isArray(value)) return false;
2791
+ const proto = Object.getPrototypeOf(value);
2792
+ return proto !== null && proto !== Object.prototype;
2793
+ }
2794
+ function hasInheritedAccessor(source, property) {
2795
+ let current = Object.getPrototypeOf(source);
2796
+ while (current && current !== Object.prototype) {
2797
+ const desc = Reflect.getOwnPropertyDescriptor(current, property);
2798
+ if (desc) return !!desc.get;
2799
+ current = Object.getPrototypeOf(current);
2800
+ }
2801
+ return false;
2802
+ }
2645
2803
  function getNodes(target, type) {
2646
2804
  let nodes = target[type];
2647
2805
  if (!nodes) target[type] = nodes = Object.create(null);
@@ -2654,7 +2812,7 @@ function getNode(nodes, property, value, firewall, equals = isEqual, optimistic,
2654
2812
  {
2655
2813
  equals: equals,
2656
2814
  unobserved() {
2657
- delete nodes[property];
2815
+ if (nodes[property] === s) delete nodes[property];
2658
2816
  }
2659
2817
  },
2660
2818
  firewall
@@ -2728,19 +2886,51 @@ function prepareStoreWrite(target, store, property) {
2728
2886
  if (useOptimistic) trackOptimisticStore(store);
2729
2887
  return { base: base, overrideKey: overrideKey, state: state };
2730
2888
  }
2731
- function notifyStoreProperty(target, property, mode, value) {
2732
- if (target[STORE_HAS]?.[property]) setSignal(target[STORE_HAS][property], mode !== "delete");
2889
+ function upsertStoreNode(target, nodes, property, prev, snapshotProps) {
2890
+ if (nodes[property]) return nodes[property];
2891
+ const initial = isWrappable(prev) ? wrap(prev, target) : prev;
2892
+ const node = getNode(
2893
+ nodes,
2894
+ property,
2895
+ initial,
2896
+ target[STORE_FIREWALL],
2897
+ isEqual,
2898
+ target[STORE_OPTIMISTIC],
2899
+ snapshotProps
2900
+ );
2901
+ registerTransientStoreNode(node);
2902
+ return node;
2903
+ }
2904
+ function notifyStoreProperty(target, property, mode, value, prev, prevHas) {
2905
+ const skipUpsert = projectionWriteActive || target[STORE_OPTIMISTIC];
2906
+ const newHas = mode !== "delete";
2907
+ const existingHas = target[STORE_HAS]?.[property];
2908
+ if (existingHas) {
2909
+ setSignal(existingHas, newHas);
2910
+ } else if (!skipUpsert && mode !== "invalidate" && prevHas !== newHas) {
2911
+ const hasNode = upsertStoreNode(target, getNodes(target, STORE_HAS), property, prevHas);
2912
+ setSignal(hasNode, newHas);
2913
+ }
2733
2914
  const nodes = getNodes(target, STORE_NODE);
2734
2915
  if (mode === "set") {
2735
- nodes[property] &&
2916
+ if (nodes[property]) {
2736
2917
  setSignal(nodes[property], () => (isWrappable(value) ? wrap(value, target) : value));
2918
+ } else if (!skipUpsert) {
2919
+ const node = upsertStoreNode(target, nodes, property, prev, target[STORE_SNAPSHOT_PROPS]);
2920
+ setSignal(node, () => (isWrappable(value) ? wrap(value, target) : value));
2921
+ }
2737
2922
  } else if (mode === "invalidate") {
2738
2923
  if (nodes[property]) {
2739
2924
  setSignal(nodes[property], {});
2740
2925
  delete nodes[property];
2741
2926
  }
2742
2927
  } else {
2743
- nodes[property] && setSignal(nodes[property], undefined);
2928
+ if (nodes[property]) {
2929
+ setSignal(nodes[property], undefined);
2930
+ } else if (!skipUpsert) {
2931
+ const node = upsertStoreNode(target, nodes, property, prev, target[STORE_SNAPSHOT_PROPS]);
2932
+ setSignal(node, undefined);
2933
+ }
2744
2934
  }
2745
2935
  nodes[$TRACK] && setSignal(nodes[$TRACK], undefined);
2746
2936
  }
@@ -2769,6 +2959,12 @@ const storeTraps = {
2769
2959
  if (!tracked) {
2770
2960
  const desc = Object.getOwnPropertyDescriptor(storeValue, property);
2771
2961
  if (desc && desc.get) return desc.get.call(receiver);
2962
+ if (!desc && !overridden && target[STORE_CUSTOM_PROTO]) {
2963
+ const source = unwrapStoreValue(storeValue);
2964
+ if (hasInheritedAccessor(source, property)) {
2965
+ return Reflect.get(storeValue, property, receiver);
2966
+ }
2967
+ }
2772
2968
  }
2773
2969
  if (writeOnly(receiver)) {
2774
2970
  let value =
@@ -2815,7 +3011,7 @@ const storeTraps = {
2815
3011
  }
2816
3012
  if (strictRead && typeof property === "string") {
2817
3013
  const message =
2818
- `Reactive value read directly in ${strictRead} will not update. ` +
3014
+ `[STRICT_READ_UNTRACKED] Reactive value read directly in ${strictRead} will not update. ` +
2819
3015
  `Move it into a tracking scope (JSX, a memo, or an effect's compute function).`;
2820
3016
  emitDiagnostic({
2821
3017
  code: "STRICT_READ_UNTRACKED",
@@ -2837,18 +3033,13 @@ const storeTraps = {
2837
3033
  : target[STORE_OVERRIDE] && property in target[STORE_OVERRIDE]
2838
3034
  ? target[STORE_OVERRIDE][property] !== $DELETED
2839
3035
  : property in target[STORE_VALUE];
2840
- if (!writeOnly(target[$PROXY])) {
2841
- getObserver() &&
2842
- read(
2843
- getNode(
2844
- getNodes(target, STORE_HAS),
2845
- property,
2846
- has,
2847
- target[STORE_FIREWALL],
2848
- isEqual,
2849
- target[STORE_OPTIMISTIC]
2850
- )
2851
- );
3036
+ if (writeOnly(target[$PROXY])) return has;
3037
+ const nodes = getNodes(target, STORE_HAS);
3038
+ if (nodes[property]) return read(nodes[property]);
3039
+ if (getObserver()) {
3040
+ return read(
3041
+ getNode(nodes, property, has, target[STORE_FIREWALL], isEqual, target[STORE_OPTIMISTIC])
3042
+ );
2852
3043
  }
2853
3044
  return has;
2854
3045
  },
@@ -2867,7 +3058,13 @@ const storeTraps = {
2867
3058
  : target[STORE_OVERRIDE] && property in target[STORE_OVERRIDE]
2868
3059
  ? target[STORE_OVERRIDE][property]
2869
3060
  : base;
2870
- const value = rawValue?.[$TARGET]?.[STORE_VALUE] ?? rawValue;
3061
+ const prevHas =
3062
+ target[STORE_OPTIMISTIC_OVERRIDE] && property in target[STORE_OPTIMISTIC_OVERRIDE]
3063
+ ? target[STORE_OPTIMISTIC_OVERRIDE][property] !== $DELETED
3064
+ : target[STORE_OVERRIDE] && property in target[STORE_OVERRIDE]
3065
+ ? target[STORE_OVERRIDE][property] !== $DELETED
3066
+ : property in target[STORE_VALUE];
3067
+ const value = unwrapStoreValue(rawValue);
2871
3068
  const isArrayIndexWrite = Array.isArray(state) && property !== "length";
2872
3069
  const nextIndex = isArrayIndexWrite ? parseInt(property) + 1 : 0;
2873
3070
  const len =
@@ -2886,11 +3083,21 @@ const storeTraps = {
2886
3083
  override[property] = value;
2887
3084
  if (nextLength !== undefined) override.length = nextLength;
2888
3085
  }
2889
- notifyStoreProperty(target, property, "set", value);
2890
- if (Array.isArray(state)) {
3086
+ notifyStoreProperty(target, property, "set", value, prev, prevHas);
3087
+ if (Array.isArray(state) && property !== "length" && nextLength !== undefined) {
2891
3088
  const nodes = getNodes(target, STORE_NODE);
2892
- const lengthValue = property === "length" ? value : nextLength;
2893
- lengthValue !== undefined && nodes.length && setSignal(nodes.length, lengthValue);
3089
+ if (nodes.length) {
3090
+ setSignal(nodes.length, nextLength);
3091
+ } else if (!projectionWriteActive && !target[STORE_OPTIMISTIC]) {
3092
+ const node = upsertStoreNode(
3093
+ target,
3094
+ nodes,
3095
+ "length",
3096
+ len,
3097
+ target[STORE_SNAPSHOT_PROPS]
3098
+ );
3099
+ setSignal(node, nextLength);
3100
+ }
2894
3101
  }
2895
3102
  if (true) DEV$1.hooks.onStoreNodeUpdate?.(target[$PROXY], property, value, prev);
2896
3103
  });
@@ -2904,10 +3111,7 @@ const storeTraps = {
2904
3111
  const { base: base, overrideKey: overrideKey } = prepareStoreWrite(target, store, property);
2905
3112
  const normalizedDescriptor =
2906
3113
  "value" in descriptor
2907
- ? {
2908
- ...descriptor,
2909
- value: descriptor.value?.[$TARGET]?.[STORE_VALUE] ?? descriptor.value
2910
- }
3114
+ ? { ...descriptor, value: unwrapStoreValue(descriptor.value) }
2911
3115
  : descriptor;
2912
3116
  Object.defineProperty(
2913
3117
  target[overrideKey] || (target[overrideKey] = Object.create(null)),
@@ -2948,7 +3152,7 @@ const storeTraps = {
2948
3152
  } else if (target[overrideKey] && property in target[overrideKey]) {
2949
3153
  delete target[overrideKey][property];
2950
3154
  } else return true;
2951
- notifyStoreProperty(target, property, "delete");
3155
+ notifyStoreProperty(target, property, "delete", undefined, prev, true);
2952
3156
  });
2953
3157
  }
2954
3158
  return true;
@@ -3021,7 +3225,7 @@ function createStore(first, second, options) {
3021
3225
  function createOptimisticStore(first, second, options) {
3022
3226
  GlobalQueue._clearOptimisticStore ||= clearOptimisticStore;
3023
3227
  const derived = typeof first === "function";
3024
- const initialValue = (derived ? second : first) ?? {};
3228
+ const initialValue = derived ? second : first;
3025
3229
  const fn = derived ? first : undefined;
3026
3230
  const { store: wrappedStore } = createOptimisticProjectionInternal(fn, initialValue, options);
3027
3231
  return [wrappedStore, fn => storeSetter(wrappedStore, fn)];
@@ -3055,7 +3259,7 @@ function clearOptimisticStore(store) {
3055
3259
  }
3056
3260
  delete target[STORE_OPTIMISTIC_OVERRIDE];
3057
3261
  }
3058
- function createOptimisticProjectionInternal(fn, initialValue = {}, options) {
3262
+ function createOptimisticProjectionInternal(fn, initialValue, options) {
3059
3263
  let node;
3060
3264
  const wrappedMap = new WeakMap();
3061
3265
  const wrapper = s => {
@@ -3078,42 +3282,33 @@ function createOptimisticProjectionInternal(fn, initialValue = {}, options) {
3078
3282
  };
3079
3283
  const wrappedStore = wrapProjection(initialValue);
3080
3284
  if (fn) {
3081
- node = computed(() => {
3082
- const owner = getOwner();
3083
- let settled = false;
3084
- let result;
3085
- const draft = new Proxy(
3086
- wrappedStore,
3087
- createWriteTraps(() => !settled || owner._inFlight === result)
3088
- );
3285
+ const wrapCommit = write => {
3089
3286
  setProjectionWriteActive(true);
3090
3287
  try {
3091
- storeSetter(draft, s => {
3092
- result = fn(s);
3093
- settled = true;
3094
- const value = handleAsync(owner, result, value => {
3095
- setProjectionWriteActive(true);
3096
- try {
3097
- value !== s &&
3098
- value !== undefined &&
3099
- storeSetter(wrappedStore, reconcile(value, options?.key || "id"));
3100
- } finally {
3101
- setProjectionWriteActive(false);
3102
- }
3103
- });
3104
- value !== s &&
3105
- value !== undefined &&
3106
- reconcile(value, options?.key || "id")(wrappedStore);
3107
- });
3288
+ write();
3108
3289
  } finally {
3109
3290
  setProjectionWriteActive(false);
3110
3291
  }
3111
- });
3112
- node._preventAutoDisposal = true;
3292
+ };
3293
+ node = computed(
3294
+ () => {
3295
+ setProjectionWriteActive(true);
3296
+ try {
3297
+ runProjectionComputed(wrappedStore, fn, options?.key || "id", wrapCommit);
3298
+ } finally {
3299
+ setProjectionWriteActive(false);
3300
+ }
3301
+ },
3302
+ options?.name ? { name: options.name } : undefined
3303
+ );
3304
+ node._config &= ~CONFIG_AUTO_DISPOSE;
3113
3305
  }
3114
3306
  return { store: wrappedStore, node: node };
3115
3307
  }
3116
3308
  const DELETE = Symbol("STORE_PATH_DELETE");
3309
+ function isPrototypePollutionKey(part) {
3310
+ return part === "__proto__" || part === "constructor" || part === "prototype";
3311
+ }
3117
3312
  function updatePath(current, args, i = 0) {
3118
3313
  let part,
3119
3314
  prev = current;
@@ -3121,6 +3316,7 @@ function updatePath(current, args, i = 0) {
3121
3316
  part = args[i];
3122
3317
  const partType = typeof part;
3123
3318
  const isArray = Array.isArray(current);
3319
+ if (partType === "string" && isPrototypePollutionKey(part)) return;
3124
3320
  if (Array.isArray(part)) {
3125
3321
  for (let j = 0; j < part.length; j++) {
3126
3322
  args[i] = part[j];
@@ -3167,6 +3363,7 @@ function updatePath(current, args, i = 0) {
3167
3363
  const keys = Object.keys(value);
3168
3364
  for (let i = 0; i < keys.length; i++) {
3169
3365
  const key = keys[i];
3366
+ if (isPrototypePollutionKey(key)) continue;
3170
3367
  const desc = Object.getOwnPropertyDescriptor(value, key);
3171
3368
  if (desc.get || desc.set) Object.defineProperty(target, key, desc);
3172
3369
  else target[key] = desc.value;
@@ -3386,23 +3583,25 @@ function mapArray(list, map, options) {
3386
3583
  }
3387
3584
  }
3388
3585
  : map;
3389
- return createMemo(
3390
- updateKeyedMap.bind({
3391
- _owner: createOwner(),
3392
- _len: 0,
3393
- _list: list,
3394
- _items: [],
3395
- _map: wrappedMap,
3396
- _mappings: [],
3397
- _nodes: [],
3398
- _key: keyFn,
3399
- _rows: keyFn || options?.keyed === false ? [] : undefined,
3400
- _indexes: indexes ? [] : undefined,
3401
- _fallback: options?.fallback
3402
- })
3403
- );
3586
+ const data = {
3587
+ _owner: createOwner(),
3588
+ _len: 0,
3589
+ _list: list,
3590
+ _items: [],
3591
+ _map: wrappedMap,
3592
+ _mappings: [],
3593
+ _nodes: [],
3594
+ _key: keyFn,
3595
+ _rows: keyFn || options?.keyed === false ? [] : undefined,
3596
+ _indexes: indexes ? [] : undefined,
3597
+ _fallback: options?.fallback
3598
+ };
3599
+ const node = computed(updateKeyedMap.bind(data));
3600
+ data._owner._parentComputed = node;
3601
+ node._config &= ~CONFIG_AUTO_DISPOSE;
3602
+ return accessor(node);
3404
3603
  }
3405
- const pureOptions = { pureWrite: true };
3604
+ const pureOptions = { ownedWrite: true };
3406
3605
  function updateKeyedMap() {
3407
3606
  const newItems = this._list() || [],
3408
3607
  newLen = newItems.length;
@@ -3539,17 +3738,21 @@ function repeat(count, map, options) {
3539
3738
  }
3540
3739
  }
3541
3740
  : map;
3542
- return updateRepeat.bind({
3543
- _owner: createOwner(),
3544
- _len: 0,
3545
- _offset: 0,
3546
- _count: count,
3547
- _map: wrappedMap,
3548
- _nodes: [],
3549
- _mappings: [],
3550
- _from: options?.from,
3551
- _fallback: options?.fallback
3552
- });
3741
+ const node = computed(
3742
+ updateRepeat.bind({
3743
+ _owner: createOwner(),
3744
+ _len: 0,
3745
+ _offset: 0,
3746
+ _count: count,
3747
+ _map: wrappedMap,
3748
+ _nodes: [],
3749
+ _mappings: [],
3750
+ _from: options?.from,
3751
+ _fallback: options?.fallback
3752
+ })
3753
+ );
3754
+ node._config &= ~CONFIG_AUTO_DISPOSE;
3755
+ return accessor(node);
3553
3756
  }
3554
3757
  function updateRepeat() {
3555
3758
  const newLen = this._count();
@@ -3606,7 +3809,7 @@ function compare(key, a, b) {
3606
3809
  return key ? key(a) === key(b) : true;
3607
3810
  }
3608
3811
  function boundaryComputed(fn, propagationMask) {
3609
- const node = computed(fn, undefined, { lazy: true });
3812
+ const node = computed(fn, { lazy: true });
3610
3813
  node._notifyStatus = (status, error) => {
3611
3814
  const flags = status !== undefined ? status : node._statusFlags;
3612
3815
  const actualError = error !== undefined ? error : node._error;
@@ -3614,7 +3817,7 @@ function boundaryComputed(fn, propagationMask) {
3614
3817
  node._queue.notify(node, node._propagationMask, flags, actualError);
3615
3818
  };
3616
3819
  node._propagationMask = propagationMask;
3617
- node._preventAutoDisposal = true;
3820
+ node._config &= ~CONFIG_AUTO_DISPOSE;
3618
3821
  recompute(node, true);
3619
3822
  return node;
3620
3823
  }
@@ -3631,12 +3834,16 @@ const ON_INIT = Symbol();
3631
3834
  const RevealControllerContext = createContext(null);
3632
3835
  let _revealUsed = false;
3633
3836
  const FALSE_ACCESSOR = () => false;
3837
+ const SEQUENTIAL_ACCESSOR = () => "sequential";
3634
3838
  function isRevealController(slot) {
3635
3839
  return slot instanceof RevealController;
3636
3840
  }
3637
3841
  function isSlotReady(slot) {
3638
3842
  return isRevealController(slot) ? slot.isReady() : slot._sources.size === 0 && !slot._pending;
3639
3843
  }
3844
+ function isSlotMinimallyReady(slot) {
3845
+ return isRevealController(slot) ? slot.isMinimallyReady() : isSlotReady(slot);
3846
+ }
3640
3847
  function setSlotState(slot, controller, disabled, collapsed) {
3641
3848
  setSignal(slot._disabled, disabled);
3642
3849
  setSignal(slot._collapsed, collapsed);
@@ -3648,16 +3855,17 @@ function setSlotState(slot, controller, disabled, collapsed) {
3648
3855
  slot._revealController = undefined;
3649
3856
  }
3650
3857
  class RevealController {
3651
- _togetherAccessor;
3858
+ _orderAccessor;
3652
3859
  _collapsedAccessor;
3653
3860
  _slots = [];
3654
3861
  _parentController;
3655
- _disabled = signal(false, { pureWrite: true, _noSnapshot: true });
3656
- _collapsed = signal(false, { pureWrite: true, _noSnapshot: true });
3862
+ _disabled = signal(false, { ownedWrite: true, _noSnapshot: true });
3863
+ _collapsed = signal(false, { ownedWrite: true, _noSnapshot: true });
3657
3864
  _ready = true;
3865
+ _minimallyReady = true;
3658
3866
  _evaluating = false;
3659
- constructor(together, collapsed) {
3660
- this._togetherAccessor = together;
3867
+ constructor(order, collapsed) {
3868
+ this._orderAccessor = order;
3661
3869
  this._collapsedAccessor = collapsed;
3662
3870
  }
3663
3871
  _forEachOwnedSlot(fn) {
@@ -3672,12 +3880,37 @@ class RevealController {
3672
3880
  isReady() {
3673
3881
  return this._forEachOwnedSlot(isSlotReady);
3674
3882
  }
3883
+ isMinimallyReady() {
3884
+ const order = untrack(this._orderAccessor);
3885
+ if (order === "together") return this.isReady();
3886
+ if (order === "natural") {
3887
+ let hasSlot = false;
3888
+ let anyReady = false;
3889
+ this._forEachOwnedSlot(slot => {
3890
+ hasSlot = true;
3891
+ if (isSlotMinimallyReady(slot)) {
3892
+ anyReady = true;
3893
+ return false;
3894
+ }
3895
+ });
3896
+ return !hasSlot || anyReady;
3897
+ }
3898
+ let firstReady = true;
3899
+ this._forEachOwnedSlot(slot => {
3900
+ firstReady = isSlotMinimallyReady(slot);
3901
+ return false;
3902
+ });
3903
+ return firstReady;
3904
+ }
3675
3905
  register(slot) {
3676
3906
  if (this._slots.includes(slot)) return;
3677
3907
  this._slots.push(slot);
3678
- const together = !!untrack(this._togetherAccessor);
3679
- setSignal(slot._disabled, true),
3680
- setSignal(slot._collapsed, together ? false : !!untrack(this._collapsedAccessor));
3908
+ const order = untrack(this._orderAccessor);
3909
+ (setSignal(slot._disabled, true),
3910
+ setSignal(
3911
+ slot._collapsed,
3912
+ order === "sequential" ? !!untrack(this._collapsedAccessor) : false
3913
+ ));
3681
3914
  untrack(() => this.evaluate());
3682
3915
  }
3683
3916
  unregister(slot) {
@@ -3689,29 +3922,52 @@ class RevealController {
3689
3922
  if (this._evaluating) return;
3690
3923
  this._evaluating = true;
3691
3924
  const wasReady = this._ready;
3925
+ const wasMinReady = this._minimallyReady;
3692
3926
  try {
3693
3927
  const disabled = disabledOverride ?? read(this._disabled),
3694
- collapseTail = !!untrack(this._collapsedAccessor),
3928
+ order = untrack(this._orderAccessor),
3929
+ collapseTail = order === "sequential" && !!untrack(this._collapsedAccessor),
3695
3930
  collapsed = collapsedOverride ?? collapseTail;
3696
- if (disabled && collapsed)
3697
- this._forEachOwnedSlot(slot => setSlotState(slot, this, true, true));
3698
- else if (!!untrack(this._togetherAccessor)) {
3699
- const ready = this.isReady();
3700
- this._forEachOwnedSlot(slot => setSlotState(slot, this, !ready, false));
3931
+ if (disabled) {
3932
+ this._forEachOwnedSlot(slot => setSlotState(slot, this, true, collapsed));
3933
+ } else if (order === "natural") {
3934
+ this._forEachOwnedSlot(slot => {
3935
+ if (isRevealController(slot)) {
3936
+ setSignal(slot._collapsed, false);
3937
+ setSignal(slot._disabled, false);
3938
+ slot.evaluate(false, false);
3939
+ } else {
3940
+ setSlotState(slot, this, !isSlotReady(slot), false);
3941
+ }
3942
+ });
3943
+ } else if (order === "together") {
3944
+ const minReady = this._forEachOwnedSlot(isSlotMinimallyReady);
3945
+ this._forEachOwnedSlot(slot => setSlotState(slot, this, !minReady, false));
3701
3946
  } else {
3702
3947
  let pendingSeen = false;
3703
3948
  this._forEachOwnedSlot(slot => {
3704
3949
  if (pendingSeen) return setSlotState(slot, this, true, collapseTail);
3705
3950
  if (isSlotReady(slot)) return setSlotState(slot, this, false, false);
3706
3951
  pendingSeen = true;
3707
- setSlotState(slot, this, true, false);
3952
+ if (isRevealController(slot)) {
3953
+ setSignal(slot._collapsed, false);
3954
+ setSignal(slot._disabled, false);
3955
+ slot.evaluate(false, false);
3956
+ } else {
3957
+ setSlotState(slot, this, true, false);
3958
+ }
3708
3959
  });
3709
3960
  }
3710
3961
  } finally {
3711
3962
  this._ready = this.isReady();
3963
+ this._minimallyReady = this.isMinimallyReady();
3712
3964
  this._evaluating = false;
3713
3965
  }
3714
- if (this._parentController && wasReady !== this._ready) this._parentController.evaluate();
3966
+ if (
3967
+ this._parentController &&
3968
+ (wasReady !== this._ready || wasMinReady !== this._minimallyReady)
3969
+ )
3970
+ this._parentController.evaluate();
3715
3971
  }
3716
3972
  }
3717
3973
  class CollectionQueue extends Queue {
@@ -3719,8 +3975,8 @@ class CollectionQueue extends Queue {
3719
3975
  _sources = new Set();
3720
3976
  _tree;
3721
3977
  _pending = true;
3722
- _disabled = signal(false, { pureWrite: true, _noSnapshot: true });
3723
- _collapsed = signal(false, { pureWrite: true, _noSnapshot: true });
3978
+ _disabled = signal(false, { ownedWrite: true, _noSnapshot: true });
3979
+ _collapsed = signal(false, { ownedWrite: true, _noSnapshot: true });
3724
3980
  _revealController;
3725
3981
  _initialized = false;
3726
3982
  _onFn;
@@ -3800,7 +4056,8 @@ class CollectionQueue extends Queue {
3800
4056
  }
3801
4057
  function createCollectionBoundary(type, fn, fallback, onFn) {
3802
4058
  if (!getOwner()) {
3803
- const message = "Boundaries created outside a reactive context will never be disposed.";
4059
+ const message =
4060
+ "[NO_OWNER_BOUNDARY] Boundaries created outside a reactive context will never be disposed.";
3804
4061
  emitDiagnostic({
3805
4062
  code: "NO_OWNER_BOUNDARY",
3806
4063
  kind: "lifecycle",
@@ -3833,15 +4090,16 @@ function createCollectionBoundary(type, fn, fallback, onFn) {
3833
4090
  controller.register(queue);
3834
4091
  cleanup(() => controller.unregister(queue));
3835
4092
  }
3836
- const decision = computed(() => {
3837
- if (!read(queue._disabled)) {
3838
- const resolved = read(tree);
3839
- if (!untrack(() => read(queue._disabled))) return (queue._initialized = true), resolved;
3840
- }
3841
- if (_revealUsed && read(queue._collapsed)) return undefined;
3842
- return fallback(queue);
3843
- });
3844
- return accessor(decision);
4093
+ return accessor(
4094
+ computed(() => {
4095
+ if (!read(queue._disabled)) {
4096
+ const resolved = read(tree);
4097
+ if (!untrack(() => read(queue._disabled))) return ((queue._initialized = true), resolved);
4098
+ }
4099
+ if (_revealUsed && read(queue._collapsed)) return undefined;
4100
+ return fallback(queue);
4101
+ })
4102
+ );
3845
4103
  }
3846
4104
  function createLoadingBoundary(fn, fallback, options) {
3847
4105
  return createCollectionBoundary(STATUS_PENDING, fn, () => fallback(), options?.on);
@@ -3860,14 +4118,14 @@ function createRevealOrder(fn, options) {
3860
4118
  _revealUsed = true;
3861
4119
  const owner = createOwner();
3862
4120
  const parentController = getContext(RevealControllerContext);
3863
- const together = options?.together || FALSE_ACCESSOR,
4121
+ const order = options?.order || SEQUENTIAL_ACCESSOR,
3864
4122
  collapsed = options?.collapsed || FALSE_ACCESSOR;
3865
- const controller = new RevealController(together, collapsed);
4123
+ const controller = new RevealController(order, collapsed);
3866
4124
  setContext(RevealControllerContext, controller, owner);
3867
4125
  return runWithOwner(owner, () => {
3868
4126
  const value = fn();
3869
4127
  computed(() => {
3870
- together();
4128
+ order();
3871
4129
  collapsed();
3872
4130
  controller.evaluate();
3873
4131
  });