@solidjs/signals 0.12.0 → 0.13.0

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.
package/dist/dev.js CHANGED
@@ -151,9 +151,13 @@ let clock = 0;
151
151
  let activeTransition = null;
152
152
  let scheduled = false;
153
153
  let projectionWriteActive = false;
154
- let _onUnhandledAsync = null;
155
- function setOnUnhandledAsync(fn) {
156
- _onUnhandledAsync = fn;
154
+ let _enforceLoadingBoundary = false;
155
+ let _hitUnhandledAsync = false;
156
+ function resetUnhandledAsync() {
157
+ _hitUnhandledAsync = false;
158
+ }
159
+ function enforceLoadingBoundary(enabled) {
160
+ _enforceLoadingBoundary = enabled;
157
161
  }
158
162
  function runLaneEffects(type) {
159
163
  for (const lane of activeLanes) {
@@ -291,9 +295,6 @@ class GlobalQueue extends Queue {
291
295
  }
292
296
  notify(node, mask, flags, error) {
293
297
  if (mask & STATUS_PENDING) {
294
- if (_onUnhandledAsync && flags & STATUS_PENDING) {
295
- _onUnhandledAsync();
296
- }
297
298
  if (flags & STATUS_PENDING) {
298
299
  const actualError = error !== undefined ? error : node._error;
299
300
  if (
@@ -304,6 +305,7 @@ class GlobalQueue extends Queue {
304
305
  activeTransition._asyncNodes.push(actualError.source);
305
306
  schedule();
306
307
  }
308
+ if (_enforceLoadingBoundary) _hitUnhandledAsync = true;
307
309
  }
308
310
  return true;
309
311
  }
@@ -387,35 +389,40 @@ function insertSubs(node, optimistic = false) {
387
389
  insertIntoHeap(s._sub, queue);
388
390
  }
389
391
  }
392
+ function commitPendingNodes() {
393
+ const pendingNodes = globalQueue._pendingNodes;
394
+ for (let i = 0; i < pendingNodes.length; i++) {
395
+ const n = pendingNodes[i];
396
+ if (n._pendingValue !== NOT_PENDING) {
397
+ n._value = n._pendingValue;
398
+ n._pendingValue = NOT_PENDING;
399
+ if (n._type && n._type !== EFFECT_TRACKED) n._modified = true;
400
+ }
401
+ if (!(n._statusFlags & STATUS_PENDING)) n._statusFlags &= ~STATUS_UNINITIALIZED;
402
+ if (n._fn) GlobalQueue._dispose(n, false, true);
403
+ }
404
+ pendingNodes.length = 0;
405
+ }
390
406
  function finalizePureQueue(completingTransition = null, incomplete = false) {
391
407
  let resolvePending = !incomplete;
408
+ if (resolvePending) commitPendingNodes();
392
409
  if (!incomplete) checkBoundaryChildren(globalQueue);
393
410
  if (dirtyQueue._max >= dirtyQueue._min) runHeap(dirtyQueue, GlobalQueue._update);
394
411
  if (resolvePending) {
395
- const pendingNodes = globalQueue._pendingNodes;
396
- for (let i = 0; i < pendingNodes.length; i++) {
397
- const n = pendingNodes[i];
398
- if (n._pendingValue !== NOT_PENDING) {
399
- n._value = n._pendingValue;
400
- n._pendingValue = NOT_PENDING;
401
- if (n._type && n._type !== EFFECT_TRACKED) n._modified = true;
402
- }
403
- if (!(n._statusFlags & STATUS_PENDING)) n._statusFlags &= ~STATUS_UNINITIALIZED;
404
- if (n._fn) GlobalQueue._dispose(n, false, true);
405
- }
406
- pendingNodes.length = 0;
412
+ commitPendingNodes();
407
413
  const optimisticNodes = completingTransition
408
414
  ? completingTransition._optimisticNodes
409
415
  : globalQueue._optimisticNodes;
410
416
  for (let i = 0; i < optimisticNodes.length; i++) {
411
417
  const n = optimisticNodes[i];
412
- const original = n._pendingValue;
413
418
  n._optimisticLane = undefined;
414
- if (original !== NOT_PENDING && n._value !== original) {
415
- n._value = original;
416
- insertSubs(n, true);
419
+ if (n._pendingValue !== NOT_PENDING) {
420
+ n._value = n._pendingValue;
421
+ n._pendingValue = NOT_PENDING;
417
422
  }
418
- n._pendingValue = NOT_PENDING;
423
+ const prevOverride = n._overrideValue;
424
+ n._overrideValue = NOT_PENDING;
425
+ if (prevOverride !== NOT_PENDING && n._value !== prevOverride) insertSubs(n, true);
419
426
  n._transition = null;
420
427
  }
421
428
  optimisticNodes.length = 0;
@@ -522,7 +529,7 @@ function getOrCreateLane(signal) {
522
529
  };
523
530
  signalLanes.set(signal, lane);
524
531
  activeLanes.add(lane);
525
- signal._laneVersion = signal._overrideVersion || 0;
532
+ signal._overrideSinceLane = false;
526
533
  return lane;
527
534
  }
528
535
  function findLane(lane) {
@@ -548,7 +555,7 @@ function resolveLane(el) {
548
555
  return undefined;
549
556
  }
550
557
  function hasActiveOverride(el) {
551
- return !!(el._optimistic && el._pendingValue !== NOT_PENDING);
558
+ return !!(el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING);
552
559
  }
553
560
  function assignOrMergeLane(el, sourceLane) {
554
561
  const sourceRoot = findLane(sourceLane);
@@ -595,10 +602,10 @@ function handleAsync(el, result, setter) {
595
602
  const lane = resolveLane(el);
596
603
  if (lane) lane._pendingAsync.delete(el);
597
604
  if (setter) setter(value);
598
- else if (el._optimistic) {
599
- const hadOverride = el._pendingValue !== NOT_PENDING;
600
- if (el._fn) el._pendingValue = value;
601
- if (!hadOverride) {
605
+ else if (el._overrideValue !== undefined) {
606
+ if (el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING)
607
+ el._pendingValue = value;
608
+ else {
602
609
  el._value = value;
603
610
  insertSubs(el);
604
611
  }
@@ -693,7 +700,8 @@ function notifyStatus(el, status, error, blockStatus, lane) {
693
700
  )
694
701
  error = new StatusError(el, error);
695
702
  const isSource = error instanceof NotReadyError && error.source === el;
696
- const isOptimisticBoundary = status === STATUS_PENDING && el._optimistic && !isSource;
703
+ const isOptimisticBoundary =
704
+ status === STATUS_PENDING && el._overrideValue !== undefined && !isSource;
697
705
  const startsBlocking = isOptimisticBoundary && hasActiveOverride(el);
698
706
  if (!blockStatus) {
699
707
  el._statusFlags =
@@ -844,7 +852,6 @@ function disposeChildren(node, self = false, zombie) {
844
852
  node._pendingFirstChild = null;
845
853
  } else {
846
854
  node._firstChild = null;
847
- node._nextSibling = null;
848
855
  node._childCount = 0;
849
856
  }
850
857
  runDisposal(node, zombie);
@@ -978,8 +985,14 @@ function effect(compute, effect, error, initialValue, options) {
978
985
  }
979
986
  }
980
987
  if (!node._queue.notify(node, STATUS_ERROR, STATUS_ERROR)) throw err;
981
- } else if (node._type === EFFECT_RENDER)
988
+ } else if (node._type === EFFECT_RENDER) {
982
989
  node._queue.notify(node, STATUS_PENDING | STATUS_ERROR, actualStatus, actualError);
990
+ if (_hitUnhandledAsync) {
991
+ resetUnhandledAsync();
992
+ const err = new Error("An async value must be rendered inside a Loading boundary.");
993
+ if (!node._queue.notify(node, STATUS_ERROR, STATUS_ERROR)) throw err;
994
+ }
995
+ }
983
996
  };
984
997
  recompute(node, true);
985
998
  !options?.defer &&
@@ -993,6 +1006,10 @@ function effect(compute, effect, error, initialValue, options) {
993
1006
  }
994
1007
  function runEffect() {
995
1008
  if (!this._modified || this._flags & REACTIVE_DISPOSED) return;
1009
+ let prevStrictRead = false;
1010
+ {
1011
+ prevStrictRead = setStrictRead("an effect callback");
1012
+ }
996
1013
  this._cleanup?.();
997
1014
  this._cleanup = undefined;
998
1015
  try {
@@ -1000,6 +1017,7 @@ function runEffect() {
1000
1017
  } catch (error) {
1001
1018
  if (!this._queue.notify(this, STATUS_ERROR, STATUS_ERROR)) throw error;
1002
1019
  } finally {
1020
+ setStrictRead(prevStrictRead);
1003
1021
  this._prevValue = this._value;
1004
1022
  this._modified = false;
1005
1023
  }
@@ -1033,6 +1051,29 @@ function trackedEffect(fn, options) {
1033
1051
  if (!node._parent)
1034
1052
  console.warn("Effects created outside a reactive context will never be disposed");
1035
1053
  }
1054
+ let externalSourceConfig = null;
1055
+ function enableExternalSource(config) {
1056
+ const { factory: factory, untrack: untrackFn = fn => fn() } = config;
1057
+ if (externalSourceConfig) {
1058
+ const { factory: oldFactory, untrack: oldUntrack } = externalSourceConfig;
1059
+ externalSourceConfig = {
1060
+ factory: (fn, trigger) => {
1061
+ const oldSource = oldFactory(fn, trigger);
1062
+ const source = factory(x => oldSource.track(x), trigger);
1063
+ return {
1064
+ track: x => source.track(x),
1065
+ dispose() {
1066
+ source.dispose();
1067
+ oldSource.dispose();
1068
+ }
1069
+ };
1070
+ },
1071
+ untrack: fn => oldUntrack(() => untrackFn(fn))
1072
+ };
1073
+ } else {
1074
+ externalSourceConfig = { factory: factory, untrack: untrackFn };
1075
+ }
1076
+ }
1036
1077
  GlobalQueue._update = recompute;
1037
1078
  GlobalQueue._dispose = disposeChildren;
1038
1079
  let tracking = false;
@@ -1112,7 +1153,7 @@ function recompute(el, create = false) {
1112
1153
  }
1113
1154
  }
1114
1155
  const isOptimisticDirty = !!(el._flags & REACTIVE_OPTIMISTIC_DIRTY);
1115
- const hasOverride = hasActiveOverride(el);
1156
+ const hasOverride = el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING;
1116
1157
  const wasPending = !!(el._statusFlags & STATUS_PENDING);
1117
1158
  const oldcontext = context;
1118
1159
  context = el;
@@ -1174,24 +1215,24 @@ function recompute(el, create = false) {
1174
1215
  else el._deps = null;
1175
1216
  }
1176
1217
  const compareValue = hasOverride
1177
- ? el._value
1218
+ ? el._overrideValue
1178
1219
  : el._pendingValue === NOT_PENDING
1179
1220
  ? el._value
1180
1221
  : el._pendingValue;
1181
1222
  const valueChanged = !el._equals || !el._equals(compareValue, value);
1182
1223
  if (valueChanged) {
1183
- const prevVisible = hasOverride ? el._value : undefined;
1184
- if (create || (isEffect && activeTransition !== el._transition) || isOptimisticDirty)
1224
+ const prevVisible = hasOverride ? el._overrideValue : undefined;
1225
+ if (create || (isEffect && activeTransition !== el._transition) || isOptimisticDirty) {
1185
1226
  el._value = value;
1186
- else el._pendingValue = value;
1187
- if (hasOverride && !isOptimisticDirty && wasPending) {
1188
- const ov = el._overrideVersion || 0;
1189
- const lv = el._laneVersion || 0;
1190
- if (ov <= lv) el._value = value;
1191
- }
1192
- if (!hasOverride || isOptimisticDirty || el._value !== prevVisible) {
1227
+ if (hasOverride && isOptimisticDirty) {
1228
+ el._overrideValue = value;
1229
+ el._pendingValue = value;
1230
+ }
1231
+ } else el._pendingValue = value;
1232
+ if (hasOverride && !isOptimisticDirty && wasPending && !el._overrideSinceLane)
1233
+ el._overrideValue = value;
1234
+ if (!hasOverride || isOptimisticDirty || el._overrideValue !== prevVisible)
1193
1235
  insertSubs(el, isOptimisticDirty || hasOverride);
1194
- }
1195
1236
  } else if (hasOverride) {
1196
1237
  el._pendingValue = value;
1197
1238
  } else if (el._height != oldHeight) {
@@ -1203,7 +1244,7 @@ function recompute(el, create = false) {
1203
1244
  currentOptimisticLane = prevLane;
1204
1245
  (!create || el._statusFlags & STATUS_PENDING) &&
1205
1246
  !el._transition &&
1206
- !(activeTransition && el._optimistic) &&
1247
+ !(activeTransition && hasOverride) &&
1207
1248
  globalQueue._pendingNodes.push(el);
1208
1249
  el._transition &&
1209
1250
  isEffect &&
@@ -1284,6 +1325,17 @@ function computed(fn, initialValue, options) {
1284
1325
  }
1285
1326
  if (parent) self._height = parent._height + 1;
1286
1327
  if (snapshotCaptureActive && ownerInSnapshotScope(context)) self._inSnapshotScope = true;
1328
+ if (externalSourceConfig) {
1329
+ const bridgeSignal = signal(undefined, { equals: false, pureWrite: true });
1330
+ const source = externalSourceConfig.factory(self._fn, () => {
1331
+ setSignal(bridgeSignal, undefined);
1332
+ });
1333
+ onCleanup(() => source.dispose());
1334
+ self._fn = prev => {
1335
+ read(bridgeSignal);
1336
+ return source.track(prev);
1337
+ };
1338
+ }
1287
1339
  !options?.lazy && recompute(self, true);
1288
1340
  if (snapshotCaptureActive && !options?.lazy) {
1289
1341
  if (!(self._statusFlags & STATUS_PENDING)) {
@@ -1308,7 +1360,7 @@ function signal(v, options, firewall = null) {
1308
1360
  };
1309
1361
  s._name = options?.name ?? "signal";
1310
1362
  firewall && (firewall._child = s);
1311
- if (snapshotCaptureActive && !s._pureWrite) {
1363
+ if (snapshotCaptureActive && !s._pureWrite && !((firewall?._statusFlags ?? 0) & STATUS_PENDING)) {
1312
1364
  s._snapshotValue = v === undefined ? NO_SNAPSHOT : v;
1313
1365
  snapshotSources.add(s);
1314
1366
  }
@@ -1316,12 +1368,12 @@ function signal(v, options, firewall = null) {
1316
1368
  }
1317
1369
  function optimisticSignal(v, options) {
1318
1370
  const s = signal(v, options);
1319
- s._optimistic = true;
1371
+ s._overrideValue = NOT_PENDING;
1320
1372
  return s;
1321
1373
  }
1322
1374
  function optimisticComputed(fn, initialValue, options) {
1323
1375
  const c = computed(fn, initialValue, options);
1324
- c._optimistic = true;
1376
+ c._overrideValue = NOT_PENDING;
1325
1377
  return c;
1326
1378
  }
1327
1379
  function isEqual(a, b) {
@@ -1334,12 +1386,13 @@ function setStrictRead(v) {
1334
1386
  return prev;
1335
1387
  }
1336
1388
  function untrack(fn, strictReadLabel) {
1337
- if (!tracking && !strictRead && !strictReadLabel) return fn();
1389
+ if (!externalSourceConfig && !tracking && !strictRead && !strictReadLabel) return fn();
1338
1390
  const prevTracking = tracking;
1339
1391
  const prevStrictRead = strictRead;
1340
1392
  tracking = false;
1341
1393
  strictRead = strictReadLabel || false;
1342
1394
  try {
1395
+ if (externalSourceConfig) return externalSourceConfig.untrack(fn);
1343
1396
  return fn();
1344
1397
  } finally {
1345
1398
  tracking = prevTracking;
@@ -1351,21 +1404,25 @@ function read(el) {
1351
1404
  const pendingComputed = getLatestValueComputed(el);
1352
1405
  const prevPending = latestReadActive;
1353
1406
  latestReadActive = false;
1407
+ const visibleValue =
1408
+ el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING
1409
+ ? el._overrideValue
1410
+ : el._value;
1354
1411
  let value;
1355
1412
  try {
1356
1413
  value = read(pendingComputed);
1357
1414
  } catch (e) {
1358
- if (!context && e instanceof NotReadyError) return el._value;
1415
+ if (!context && e instanceof NotReadyError) return visibleValue;
1359
1416
  throw e;
1360
1417
  } finally {
1361
1418
  latestReadActive = prevPending;
1362
1419
  }
1363
- if (pendingComputed._statusFlags & STATUS_PENDING) return el._value;
1420
+ if (pendingComputed._statusFlags & STATUS_PENDING) return visibleValue;
1364
1421
  if (stale && currentOptimisticLane && pendingComputed._optimisticLane) {
1365
1422
  const pcLane = findLane(pendingComputed._optimisticLane);
1366
1423
  const curLane = findLane(currentOptimisticLane);
1367
1424
  if (pcLane !== curLane && pcLane._pendingAsync.size > 0) {
1368
- return el._value;
1425
+ return visibleValue;
1369
1426
  }
1370
1427
  }
1371
1428
  return value;
@@ -1391,8 +1448,8 @@ function read(el) {
1391
1448
  const owner = el._firewall || el;
1392
1449
  if (strictRead && owner._statusFlags & STATUS_PENDING) {
1393
1450
  throw new Error(
1394
- `Reading a pending async value in ${strictRead}. ` +
1395
- `Async values must be read within a tracking scope (JSX, computations, effects).`
1451
+ `Reading a pending async value directly in ${strictRead}. ` +
1452
+ `Async values must be read within a tracking scope (JSX, a memo, or an effect's compute function).`
1396
1453
  );
1397
1454
  }
1398
1455
  if (c && tracking) {
@@ -1417,11 +1474,11 @@ function read(el) {
1417
1474
  const pendingLane = owner._optimisticLane;
1418
1475
  const lane = findLane(currentOptimisticLane);
1419
1476
  if (pendingLane && findLane(pendingLane) === lane && !hasActiveOverride(owner)) {
1420
- if (!tracking) link(el, c);
1477
+ if (!tracking && el !== c) link(el, c);
1421
1478
  throw owner._error;
1422
1479
  }
1423
1480
  } else {
1424
- if (!tracking) link(el, c);
1481
+ if (!tracking && el !== c) link(el, c);
1425
1482
  throw owner._error;
1426
1483
  }
1427
1484
  } else if (!c && owner._statusFlags & STATUS_UNINITIALIZED) {
@@ -1445,12 +1502,14 @@ function read(el) {
1445
1502
  }
1446
1503
  if (strictRead)
1447
1504
  console.warn(
1448
- `Reactive value read at the top level of ${strictRead} will not update. ` +
1449
- `Move it into a tracking scope (JSX, computations, effects).`
1505
+ `Reactive value read directly in ${strictRead} will not update. ` +
1506
+ `Move it into a tracking scope (JSX, a memo, or an effect's compute function).`
1450
1507
  );
1508
+ if (el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING)
1509
+ return el._overrideValue;
1451
1510
  return !c ||
1452
1511
  (currentOptimisticLane !== null &&
1453
- (el._optimistic ||
1512
+ (el._overrideValue !== undefined ||
1454
1513
  el._optimisticLane ||
1455
1514
  owner === el ||
1456
1515
  !!(owner._statusFlags & STATUS_PENDING))) ||
@@ -1464,9 +1523,12 @@ function setSignal(el, v) {
1464
1523
  console.warn("A Signal was written to in an owned scope.");
1465
1524
  if (el._transition && activeTransition !== el._transition)
1466
1525
  globalQueue.initTransition(el._transition);
1467
- const isOptimistic = el._optimistic && !projectionWriteActive;
1526
+ const isOptimistic = el._overrideValue !== undefined && !projectionWriteActive;
1527
+ const hasOverride = el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING;
1468
1528
  const currentValue = isOptimistic
1469
- ? el._value
1529
+ ? hasOverride
1530
+ ? el._overrideValue
1531
+ : el._value
1470
1532
  : el._pendingValue === NOT_PENDING
1471
1533
  ? el._value
1472
1534
  : el._pendingValue;
@@ -1474,27 +1536,23 @@ function setSignal(el, v) {
1474
1536
  const valueChanged =
1475
1537
  !el._equals || !el._equals(currentValue, v) || !!(el._statusFlags & STATUS_UNINITIALIZED);
1476
1538
  if (!valueChanged) {
1477
- if (isOptimistic && el._pendingValue !== NOT_PENDING && el._fn) {
1539
+ if (isOptimistic && hasOverride && el._fn) {
1478
1540
  insertSubs(el, true);
1479
1541
  schedule();
1480
1542
  }
1481
1543
  return v;
1482
1544
  }
1483
1545
  if (isOptimistic) {
1484
- const alreadyTracked = globalQueue._optimisticNodes.includes(el);
1485
- if (el._transition && alreadyTracked) {
1486
- globalQueue.initTransition(el._transition);
1487
- }
1488
- if (el._pendingValue === NOT_PENDING) {
1546
+ const firstOverride = el._overrideValue === NOT_PENDING;
1547
+ if (!firstOverride && el._transition) globalQueue.initTransition(el._transition);
1548
+ if (firstOverride) {
1489
1549
  el._pendingValue = el._value;
1490
- }
1491
- if (!alreadyTracked) {
1492
1550
  globalQueue._optimisticNodes.push(el);
1493
1551
  }
1494
- el._overrideVersion = (el._overrideVersion || 0) + 1;
1552
+ el._overrideSinceLane = true;
1495
1553
  const lane = getOrCreateLane(el);
1496
1554
  el._optimisticLane = lane;
1497
- el._value = v;
1555
+ el._overrideValue = v;
1498
1556
  } else {
1499
1557
  if (el._pendingValue === NOT_PENDING) globalQueue._pendingNodes.push(el);
1500
1558
  el._pendingValue = v;
@@ -1532,7 +1590,7 @@ function getPendingSignal(el) {
1532
1590
  }
1533
1591
  function computePendingState(el) {
1534
1592
  const comp = el;
1535
- if (el._optimistic && el._pendingValue !== NOT_PENDING) {
1593
+ if (el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING) {
1536
1594
  if (comp._statusFlags & STATUS_PENDING && !(comp._statusFlags & STATUS_UNINITIALIZED))
1537
1595
  return true;
1538
1596
  if (el._parentSource) {
@@ -1702,17 +1760,22 @@ function action(genFn) {
1702
1760
  step();
1703
1761
  });
1704
1762
  }
1763
+ function accessor(node) {
1764
+ const fn = read.bind(null, node);
1765
+ fn.$r = true;
1766
+ return fn;
1767
+ }
1705
1768
  function createSignal(first, second, third) {
1706
1769
  if (typeof first === "function") {
1707
1770
  const node = computed(first, second, third);
1708
- return [read.bind(null, node), setSignal.bind(null, node)];
1771
+ return [accessor(node), setSignal.bind(null, node)];
1709
1772
  }
1710
1773
  const node = signal(first, second);
1711
- return [read.bind(null, node), setSignal.bind(null, node)];
1774
+ return [accessor(node), setSignal.bind(null, node)];
1712
1775
  }
1713
1776
  function createMemo(compute, value, options) {
1714
1777
  let node = computed(compute, value, options);
1715
- return read.bind(null, node);
1778
+ return accessor(node);
1716
1779
  }
1717
1780
  function createEffect(compute, effectFn, value, options) {
1718
1781
  effect(compute, effectFn.effect || effectFn, effectFn.error, value, {
@@ -1769,7 +1832,7 @@ function createOptimistic(first, second, third) {
1769
1832
  typeof first === "function"
1770
1833
  ? optimisticComputed(first, second, third)
1771
1834
  : optimisticSignal(first, second);
1772
- return [read.bind(null, node), setSignal.bind(null, node)];
1835
+ return [accessor(node), setSignal.bind(null, node)];
1773
1836
  }
1774
1837
  function onSettled(callback) {
1775
1838
  getOwner()
@@ -1791,7 +1854,7 @@ function getAllKeys(value, override, next) {
1791
1854
  const nextKeys = Object.keys(next);
1792
1855
  return Array.from(new Set([...keys, ...nextKeys]));
1793
1856
  }
1794
- function applyState(next, state, keyFn, all) {
1857
+ function applyState(next, state, keyFn) {
1795
1858
  const target = state?.[$TARGET];
1796
1859
  if (!target) return;
1797
1860
  const previous = target[STORE_VALUE];
@@ -1814,7 +1877,7 @@ function applyState(next, state, keyFn, all) {
1814
1877
  (item && next[start] && keyFn(item) === keyFn(next[start])));
1815
1878
  start++
1816
1879
  ) {
1817
- applyState(next[start], wrap(item, target), keyFn, all);
1880
+ applyState(next[start], wrap(item, target), keyFn);
1818
1881
  }
1819
1882
  const temp = new Array(next.length),
1820
1883
  newIndices = new Map();
@@ -1837,7 +1900,7 @@ function applyState(next, state, keyFn, all) {
1837
1900
  changed = true;
1838
1901
  const wrapped = wrap(temp[j], target);
1839
1902
  target[STORE_NODE][j] && setSignal(target[STORE_NODE][j], wrapped);
1840
- applyState(next[j], wrapped, keyFn, all);
1903
+ applyState(next[j], wrapped, keyFn);
1841
1904
  }
1842
1905
  changed && target[STORE_NODE][$TRACK] && setSignal(target[STORE_NODE][$TRACK], void 0);
1843
1906
  prevLength !== next.length &&
@@ -1867,7 +1930,7 @@ function applyState(next, state, keyFn, all) {
1867
1930
  if (j in temp) {
1868
1931
  const wrapped = wrap(temp[j], target);
1869
1932
  target[STORE_NODE][j] && setSignal(target[STORE_NODE][j], wrapped);
1870
- applyState(next[j], wrapped, keyFn, all);
1933
+ applyState(next[j], wrapped, keyFn);
1871
1934
  } else target[STORE_NODE][j] && setSignal(target[STORE_NODE][j], wrap(next[j], target));
1872
1935
  }
1873
1936
  if (start < next.length) changed = true;
@@ -1875,7 +1938,7 @@ function applyState(next, state, keyFn, all) {
1875
1938
  for (let i = 0, len = next.length; i < len; i++) {
1876
1939
  const item = getOverrideValue(previous, override, nodes, i, optOverride);
1877
1940
  isWrappable(item)
1878
- ? applyState(next[i], wrap(item, target), keyFn, all)
1941
+ ? applyState(next[i], wrap(item, target), keyFn)
1879
1942
  : target[STORE_NODE][i] && setSignal(target[STORE_NODE][i], next[i]);
1880
1943
  }
1881
1944
  }
@@ -1888,7 +1951,7 @@ function applyState(next, state, keyFn, all) {
1888
1951
  }
1889
1952
  if (nodes) {
1890
1953
  const tracked = nodes[$TRACK];
1891
- const keys = tracked || all ? getAllKeys(previous, override, next) : Object.keys(nodes);
1954
+ const keys = tracked ? getAllKeys(previous, override, next) : Object.keys(nodes);
1892
1955
  for (let i = 0, len = keys.length; i < len; i++) {
1893
1956
  const key = keys[i];
1894
1957
  const node = nodes[key];
@@ -1903,7 +1966,7 @@ function applyState(next, state, keyFn, all) {
1903
1966
  ) {
1904
1967
  tracked && setSignal(tracked, void 0);
1905
1968
  node && setSignal(node, isWrappable(nextValue) ? wrap(nextValue, target) : nextValue);
1906
- } else applyState(nextValue, wrap(previousValue, target), keyFn, all);
1969
+ } else applyState(nextValue, wrap(previousValue, target), keyFn);
1907
1970
  }
1908
1971
  }
1909
1972
  if ((nodes = target[STORE_HAS])) {
@@ -1914,14 +1977,14 @@ function applyState(next, state, keyFn, all) {
1914
1977
  }
1915
1978
  }
1916
1979
  }
1917
- function reconcile(value, key, all = false) {
1980
+ function reconcile(value, key) {
1918
1981
  return state => {
1919
1982
  if (state == null) throw new Error("Cannot reconcile null or undefined state");
1920
1983
  const keyFn = typeof key === "string" ? item => item[key] : key;
1921
1984
  const eq = keyFn(state);
1922
1985
  if (eq !== undefined && keyFn(value) !== keyFn(state))
1923
1986
  throw new Error("Cannot reconcile states with different identity");
1924
- applyState(value, state, keyFn, all);
1987
+ applyState(value, state, keyFn);
1925
1988
  };
1926
1989
  }
1927
1990
  function createProjectionInternal(fn, initialValue = {}, options) {
@@ -1951,11 +2014,9 @@ function createProjectionInternal(fn, initialValue = {}, options) {
1951
2014
  const value = handleAsync(owner, fn(s), value => {
1952
2015
  value !== s &&
1953
2016
  value !== undefined &&
1954
- storeSetter(wrappedStore, reconcile(value, options?.key || "id", options?.all));
2017
+ storeSetter(wrappedStore, reconcile(value, options?.key || "id"));
1955
2018
  });
1956
- value !== s &&
1957
- value !== undefined &&
1958
- reconcile(value, options?.key || "id", options?.all)(wrappedStore);
2019
+ value !== s && value !== undefined && reconcile(value, options?.key || "id")(wrappedStore);
1959
2020
  });
1960
2021
  });
1961
2022
  node._preventAutoDisposal = true;
@@ -2001,11 +2062,9 @@ const writeTraps = {
2001
2062
  }
2002
2063
  };
2003
2064
  const $TRACK = Symbol("STORE_TRACK"),
2004
- $DEEP = Symbol("STORE_DEEP"),
2005
2065
  $TARGET = Symbol("STORE_TARGET"),
2006
2066
  $PROXY = Symbol("STORE_PROXY"),
2007
2067
  $DELETED = Symbol("STORE_DELETED");
2008
- const PARENTS = new WeakMap();
2009
2068
  const STORE_VALUE = "v",
2010
2069
  STORE_OVERRIDE = "o",
2011
2070
  STORE_OPTIMISTIC_OVERRIDE = "x",
@@ -2058,7 +2117,9 @@ function getNode(nodes, property, value, firewall, equals = isEqual, optimistic,
2058
2117
  },
2059
2118
  firewall
2060
2119
  );
2061
- if (optimistic) s._optimistic = true;
2120
+ if (optimistic) {
2121
+ s._overrideValue = NOT_PENDING;
2122
+ }
2062
2123
  if (snapshotProps && property in snapshotProps) {
2063
2124
  const sv = snapshotProps[property];
2064
2125
  s._snapshotValue = sv === undefined ? NO_SNAPSHOT : sv;
@@ -2104,8 +2165,8 @@ const storeTraps = {
2104
2165
  if (property === $TARGET) return target;
2105
2166
  if (property === $PROXY) return receiver;
2106
2167
  if (property === $REFRESH) return target[STORE_FIREWALL];
2107
- if (property === $TRACK || property === $DEEP) {
2108
- trackSelf(target, property);
2168
+ if (property === $TRACK) {
2169
+ trackSelf(target);
2109
2170
  return receiver;
2110
2171
  }
2111
2172
  const nodes = getNodes(target, STORE_NODE);
@@ -2127,11 +2188,11 @@ const storeTraps = {
2127
2188
  if (writeOnly(receiver)) {
2128
2189
  let value =
2129
2190
  tracked && (overridden || !proxySource)
2130
- ? tracked._pendingValue !== NOT_PENDING
2131
- ? tracked._optimistic
2132
- ? tracked._value
2133
- : tracked._pendingValue
2134
- : tracked._value
2191
+ ? tracked._overrideValue !== undefined && tracked._overrideValue !== NOT_PENDING
2192
+ ? tracked._overrideValue
2193
+ : tracked._pendingValue !== NOT_PENDING
2194
+ ? tracked._pendingValue
2195
+ : tracked._value
2135
2196
  : storeValue[property];
2136
2197
  value === $DELETED && (value = undefined);
2137
2198
  if (!isWrappable(value)) return value;
@@ -2169,8 +2230,8 @@ const storeTraps = {
2169
2230
  }
2170
2231
  if (strictRead && typeof property === "string")
2171
2232
  console.warn(
2172
- `Reactive value read at the top level of ${strictRead} will not update. ` +
2173
- `Move it into a tracking scope (JSX, computations, effects).`
2233
+ `Reactive value read directly in ${strictRead} will not update. ` +
2234
+ `Move it into a tracking scope (JSX, a memo, or an effect's compute function).`
2174
2235
  );
2175
2236
  return isWrappable(value) ? wrap(value, target) : value;
2176
2237
  },
@@ -2207,7 +2268,11 @@ const storeTraps = {
2207
2268
  untrack(() => {
2208
2269
  const state = target[STORE_VALUE];
2209
2270
  const base = state[property];
2210
- if (snapshotCaptureActive && typeof property !== "symbol") {
2271
+ if (
2272
+ snapshotCaptureActive &&
2273
+ typeof property !== "symbol" &&
2274
+ !((target[STORE_FIREWALL]?._statusFlags ?? 0) & STATUS_PENDING)
2275
+ ) {
2211
2276
  if (!target[STORE_SNAPSHOT_PROPS]) {
2212
2277
  target[STORE_SNAPSHOT_PROPS] = Object.create(null);
2213
2278
  snapshotSources?.add(target);
@@ -2234,11 +2299,6 @@ const storeTraps = {
2234
2299
  if (value !== undefined && value === base) delete target[overrideKey][property];
2235
2300
  else (target[overrideKey] || (target[overrideKey] = Object.create(null)))[property] = value;
2236
2301
  const wrappable = isWrappable(value);
2237
- if (isWrappable(prev)) {
2238
- const parents = PARENTS.get(prev);
2239
- parents && (parents instanceof Set ? parents.delete(store) : PARENTS.delete(prev));
2240
- }
2241
- if (recursivelyNotify(store, storeLookup) && wrappable) recursivelyAddParent(value, store);
2242
2302
  target[STORE_HAS]?.[property] && setSignal(target[STORE_HAS][property], true);
2243
2303
  const nodes = getNodes(target, STORE_NODE);
2244
2304
  nodes[property] &&
@@ -2278,10 +2338,6 @@ const storeTraps = {
2278
2338
  } else if (target[overrideKey] && property in target[overrideKey]) {
2279
2339
  delete target[overrideKey][property];
2280
2340
  } else return true;
2281
- if (isWrappable(prev)) {
2282
- const parents = PARENTS.get(prev);
2283
- parents && (parents instanceof Set ? parents.delete(target) : PARENTS.delete(prev));
2284
- }
2285
2341
  if (target[STORE_HAS]?.[property]) setSignal(target[STORE_HAS][property], false);
2286
2342
  const nodes = getNodes(target, STORE_NODE);
2287
2343
  nodes[property] && setSignal(nodes[property], undefined);
@@ -2352,59 +2408,6 @@ function createStore(first, second, options) {
2352
2408
  wrappedStore = derived ? createProjectionInternal(first, second, options).store : wrap(first);
2353
2409
  return [wrappedStore, fn => storeSetter(wrappedStore, fn)];
2354
2410
  }
2355
- function recursivelyNotify(state, lookup) {
2356
- let target = state[$TARGET] || lookup?.get(state)?.[$TARGET];
2357
- let notified = false;
2358
- if (target) {
2359
- const deep = getNodes(target, STORE_NODE)[$DEEP];
2360
- if (deep) {
2361
- setSignal(deep, undefined);
2362
- notified = true;
2363
- }
2364
- lookup = target[STORE_LOOKUP] || lookup;
2365
- }
2366
- const parents = PARENTS.get(target?.[STORE_VALUE] || state);
2367
- if (!parents) return notified;
2368
- if (parents instanceof Set) {
2369
- for (let parent of parents) notified = recursivelyNotify(parent, lookup) || notified;
2370
- } else notified = recursivelyNotify(parents, lookup) || notified;
2371
- return notified;
2372
- }
2373
- function recursivelyAddParent(state, parent) {
2374
- let override;
2375
- const target = state[$TARGET];
2376
- if (target) {
2377
- override = target[STORE_OVERRIDE];
2378
- state = target[STORE_VALUE];
2379
- }
2380
- if (parent) {
2381
- let parents = PARENTS.get(state);
2382
- if (!parents) PARENTS.set(state, parent);
2383
- else if (parents !== parent) {
2384
- if (!(parents instanceof Set)) PARENTS.set(state, (parents = new Set([parents])));
2385
- else if (parents.has(parent)) return;
2386
- parents.add(parent);
2387
- } else return;
2388
- }
2389
- if (Array.isArray(state)) {
2390
- const len = override?.length || state.length;
2391
- for (let i = 0; i < len; i++) {
2392
- const item = override && i in override ? override[i] : state[i];
2393
- isWrappable(item) && recursivelyAddParent(item, state);
2394
- }
2395
- } else {
2396
- const keys = getKeys(state, override);
2397
- for (let i = 0; i < keys.length; i++) {
2398
- const key = keys[i];
2399
- const item = override && key in override ? override[key] : state[key];
2400
- isWrappable(item) && recursivelyAddParent(item, state);
2401
- }
2402
- }
2403
- }
2404
- function deep(store) {
2405
- recursivelyAddParent(store);
2406
- return store[$DEEP];
2407
- }
2408
2411
  function createOptimisticStore(first, second, options) {
2409
2412
  GlobalQueue._clearOptimisticStore ||= clearOptimisticStore;
2410
2413
  const derived = typeof first === "function";
@@ -2475,14 +2478,14 @@ function createOptimisticProjectionInternal(fn, initialValue = {}, options) {
2475
2478
  try {
2476
2479
  value !== s &&
2477
2480
  value !== undefined &&
2478
- storeSetter(wrappedStore, reconcile(value, options?.key || "id", options?.all));
2481
+ storeSetter(wrappedStore, reconcile(value, options?.key || "id"));
2479
2482
  } finally {
2480
2483
  setProjectionWriteActive(false);
2481
2484
  }
2482
2485
  });
2483
2486
  value !== s &&
2484
2487
  value !== undefined &&
2485
- reconcile(value, options?.key || "id", options?.all)(wrappedStore);
2488
+ reconcile(value, options?.key || "id")(wrappedStore);
2486
2489
  });
2487
2490
  } finally {
2488
2491
  setProjectionWriteActive(false);
@@ -2557,12 +2560,13 @@ const storePath = Object.assign(
2557
2560
  },
2558
2561
  { DELETE: DELETE }
2559
2562
  );
2560
- function snapshot(item, map, lookup) {
2563
+ function snapshotImpl(item, track, map, lookup) {
2561
2564
  let target, isArray, override, result, unwrapped, v;
2562
2565
  if (!isWrappable(item)) return item;
2563
2566
  if (map && map.has(item)) return map.get(item);
2564
2567
  if (!map) map = new Map();
2565
2568
  if ((target = item[$TARGET] || lookup?.get(item)?.[$TARGET])) {
2569
+ if (track) trackSelf(target, $TRACK);
2566
2570
  override = target[STORE_OVERRIDE];
2567
2571
  isArray = Array.isArray(target[STORE_VALUE]);
2568
2572
  map.set(
@@ -2582,7 +2586,8 @@ function snapshot(item, map, lookup) {
2582
2586
  for (let i = 0; i < len; i++) {
2583
2587
  v = override && i in override ? override[i] : item[i];
2584
2588
  if (v === $DELETED) continue;
2585
- if ((unwrapped = snapshot(v, map, lookup)) !== v || result) {
2589
+ if (track && isWrappable(v)) wrap(v, target);
2590
+ if ((unwrapped = snapshotImpl(v, track, map, lookup)) !== v || result) {
2586
2591
  if (!result) map.set(item, (result = [...item]));
2587
2592
  result[i] = unwrapped;
2588
2593
  }
@@ -2594,7 +2599,8 @@ function snapshot(item, map, lookup) {
2594
2599
  const desc = getPropertyDescriptor(item, override, prop);
2595
2600
  if (desc.get) continue;
2596
2601
  v = override && prop in override ? override[prop] : item[prop];
2597
- if ((unwrapped = snapshot(v, map, lookup)) !== item[prop] || result) {
2602
+ if (track && isWrappable(v)) wrap(v, target);
2603
+ if ((unwrapped = snapshotImpl(v, track, map, lookup)) !== item[prop] || result) {
2598
2604
  if (!result) {
2599
2605
  result = Object.create(Object.getPrototypeOf(item));
2600
2606
  Object.assign(result, item);
@@ -2605,6 +2611,12 @@ function snapshot(item, map, lookup) {
2605
2611
  }
2606
2612
  return result || item;
2607
2613
  }
2614
+ function snapshot(item, map, lookup) {
2615
+ return snapshotImpl(item, false, map, lookup);
2616
+ }
2617
+ function deep(store) {
2618
+ return snapshotImpl(store, true);
2619
+ }
2608
2620
  function trueFn() {
2609
2621
  return true;
2610
2622
  }
@@ -2780,15 +2792,15 @@ function updateKeyedMap() {
2780
2792
  this._rows[j] = signal(newItems[j], pureOptions);
2781
2793
  this._indexes && (this._indexes[j] = signal(j, pureOptions));
2782
2794
  return this._map(
2783
- read.bind(null, this._rows[j]),
2784
- this._indexes ? read.bind(null, this._indexes[j]) : undefined
2795
+ accessor(this._rows[j]),
2796
+ this._indexes ? accessor(this._indexes[j]) : undefined
2785
2797
  );
2786
2798
  }
2787
2799
  : this._indexes
2788
2800
  ? () => {
2789
2801
  const item = newItems[j];
2790
2802
  this._indexes[j] = signal(j, pureOptions);
2791
- return this._map(() => item, read.bind(null, this._indexes[j]));
2803
+ return this._map(() => item, accessor(this._indexes[j]));
2792
2804
  }
2793
2805
  : () => {
2794
2806
  const item = newItems[j];
@@ -2992,11 +3004,14 @@ function createBoundChildren(owner, fn, queue, mask) {
2992
3004
  return boundaryComputed(() => staleValues(() => flatten(read(c))), mask);
2993
3005
  });
2994
3006
  }
3007
+ const ON_INIT = Symbol();
2995
3008
  class CollectionQueue extends Queue {
2996
3009
  _collectionType;
2997
3010
  _sources = new Set();
2998
3011
  _disabled = signal(false, { pureWrite: true });
2999
3012
  _initialized = false;
3013
+ _onFn;
3014
+ _prevOn = ON_INIT;
3000
3015
  constructor(type) {
3001
3016
  super();
3002
3017
  this._collectionType = type;
@@ -3006,10 +3021,22 @@ class CollectionQueue extends Queue {
3006
3021
  return super.run(type);
3007
3022
  }
3008
3023
  notify(node, type, flags, error) {
3009
- if (
3010
- !(type & this._collectionType) ||
3011
- (this._collectionType & STATUS_PENDING && this._initialized)
3012
- )
3024
+ if (!(type & this._collectionType)) return super.notify(node, type, flags, error);
3025
+ if (this._initialized && this._onFn) {
3026
+ const currentOn = untrack(() => {
3027
+ try {
3028
+ return this._onFn();
3029
+ } catch {
3030
+ return ON_INIT;
3031
+ }
3032
+ });
3033
+ if (currentOn !== this._prevOn) {
3034
+ this._prevOn = currentOn;
3035
+ this._initialized = false;
3036
+ this._sources.clear();
3037
+ }
3038
+ }
3039
+ if (this._collectionType & STATUS_PENDING && this._initialized)
3013
3040
  return super.notify(node, type, flags, error);
3014
3041
  if (flags & this._collectionType) {
3015
3042
  const source = error?.source || node._error?.source;
@@ -3026,12 +3053,20 @@ class CollectionQueue extends Queue {
3026
3053
  for (const source of this._sources) {
3027
3054
  if (!(source._statusFlags & this._collectionType)) this._sources.delete(source);
3028
3055
  }
3029
- if (!this._sources.size) setSignal(this._disabled, false);
3056
+ if (!this._sources.size) {
3057
+ setSignal(this._disabled, false);
3058
+ if (this._onFn) {
3059
+ try {
3060
+ this._prevOn = untrack(() => this._onFn());
3061
+ } catch {}
3062
+ }
3063
+ }
3030
3064
  }
3031
3065
  }
3032
- function createCollectionBoundary(type, fn, fallback) {
3066
+ function createCollectionBoundary(type, fn, fallback, onFn) {
3033
3067
  const owner = createOwner();
3034
3068
  const queue = new CollectionQueue(type);
3069
+ if (onFn) queue._onFn = onFn;
3035
3070
  const tree = createBoundChildren(owner, fn, queue, type);
3036
3071
  const decision = computed(() => {
3037
3072
  if (!read(queue._disabled)) {
@@ -3043,10 +3078,10 @@ function createCollectionBoundary(type, fn, fallback) {
3043
3078
  }
3044
3079
  return fallback(queue);
3045
3080
  });
3046
- return read.bind(null, decision);
3081
+ return accessor(decision);
3047
3082
  }
3048
- function createLoadBoundary(fn, fallback) {
3049
- return createCollectionBoundary(STATUS_PENDING, fn, () => fallback());
3083
+ function createLoadingBoundary(fn, fallback, options) {
3084
+ return createCollectionBoundary(STATUS_PENDING, fn, () => fallback(), options?.on);
3050
3085
  }
3051
3086
  function createErrorBoundary(fn, fallback) {
3052
3087
  return createCollectionBoundary(STATUS_ERROR, fn, queue => {
@@ -3128,7 +3163,7 @@ export {
3128
3163
  createContext,
3129
3164
  createEffect,
3130
3165
  createErrorBoundary,
3131
- createLoadBoundary,
3166
+ createLoadingBoundary,
3132
3167
  createMemo,
3133
3168
  createOptimistic,
3134
3169
  createOptimisticStore,
@@ -3141,6 +3176,8 @@ export {
3141
3176
  createStore,
3142
3177
  createTrackedEffect,
3143
3178
  deep,
3179
+ enableExternalSource,
3180
+ enforceLoadingBoundary,
3144
3181
  flatten,
3145
3182
  flush,
3146
3183
  getContext,
@@ -3167,7 +3204,6 @@ export {
3167
3204
  resolve,
3168
3205
  runWithOwner,
3169
3206
  setContext,
3170
- setOnUnhandledAsync,
3171
3207
  setSnapshotCapture,
3172
3208
  snapshot,
3173
3209
  storePath,