@solidjs/signals 0.12.0 → 0.13.1

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,46 @@ 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) {
402
+ const _src = n._error?.source;
403
+ if (_src && !(_src._statusFlags & STATUS_PENDING)) {
404
+ n._statusFlags &= -6;
405
+ n._error = null;
406
+ }
407
+ } else n._statusFlags &= ~STATUS_UNINITIALIZED;
408
+ if (n._fn) GlobalQueue._dispose(n, false, true);
409
+ }
410
+ pendingNodes.length = 0;
411
+ }
390
412
  function finalizePureQueue(completingTransition = null, incomplete = false) {
391
413
  let resolvePending = !incomplete;
414
+ if (resolvePending) commitPendingNodes();
392
415
  if (!incomplete) checkBoundaryChildren(globalQueue);
393
416
  if (dirtyQueue._max >= dirtyQueue._min) runHeap(dirtyQueue, GlobalQueue._update);
394
417
  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;
418
+ commitPendingNodes();
407
419
  const optimisticNodes = completingTransition
408
420
  ? completingTransition._optimisticNodes
409
421
  : globalQueue._optimisticNodes;
410
422
  for (let i = 0; i < optimisticNodes.length; i++) {
411
423
  const n = optimisticNodes[i];
412
- const original = n._pendingValue;
413
424
  n._optimisticLane = undefined;
414
- if (original !== NOT_PENDING && n._value !== original) {
415
- n._value = original;
416
- insertSubs(n, true);
425
+ if (n._pendingValue !== NOT_PENDING) {
426
+ n._value = n._pendingValue;
427
+ n._pendingValue = NOT_PENDING;
417
428
  }
418
- n._pendingValue = NOT_PENDING;
429
+ const prevOverride = n._overrideValue;
430
+ n._overrideValue = NOT_PENDING;
431
+ if (prevOverride !== NOT_PENDING && n._value !== prevOverride) insertSubs(n, true);
419
432
  n._transition = null;
420
433
  }
421
434
  optimisticNodes.length = 0;
@@ -522,7 +535,7 @@ function getOrCreateLane(signal) {
522
535
  };
523
536
  signalLanes.set(signal, lane);
524
537
  activeLanes.add(lane);
525
- signal._laneVersion = signal._overrideVersion || 0;
538
+ signal._overrideSinceLane = false;
526
539
  return lane;
527
540
  }
528
541
  function findLane(lane) {
@@ -548,7 +561,7 @@ function resolveLane(el) {
548
561
  return undefined;
549
562
  }
550
563
  function hasActiveOverride(el) {
551
- return !!(el._optimistic && el._pendingValue !== NOT_PENDING);
564
+ return !!(el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING);
552
565
  }
553
566
  function assignOrMergeLane(el, sourceLane) {
554
567
  const sourceRoot = findLane(sourceLane);
@@ -595,10 +608,10 @@ function handleAsync(el, result, setter) {
595
608
  const lane = resolveLane(el);
596
609
  if (lane) lane._pendingAsync.delete(el);
597
610
  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) {
611
+ else if (el._overrideValue !== undefined) {
612
+ if (el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING)
613
+ el._pendingValue = value;
614
+ else {
602
615
  el._value = value;
603
616
  insertSubs(el);
604
617
  }
@@ -693,7 +706,8 @@ function notifyStatus(el, status, error, blockStatus, lane) {
693
706
  )
694
707
  error = new StatusError(el, error);
695
708
  const isSource = error instanceof NotReadyError && error.source === el;
696
- const isOptimisticBoundary = status === STATUS_PENDING && el._optimistic && !isSource;
709
+ const isOptimisticBoundary =
710
+ status === STATUS_PENDING && el._overrideValue !== undefined && !isSource;
697
711
  const startsBlocking = isOptimisticBoundary && hasActiveOverride(el);
698
712
  if (!blockStatus) {
699
713
  el._statusFlags =
@@ -844,7 +858,6 @@ function disposeChildren(node, self = false, zombie) {
844
858
  node._pendingFirstChild = null;
845
859
  } else {
846
860
  node._firstChild = null;
847
- node._nextSibling = null;
848
861
  node._childCount = 0;
849
862
  }
850
863
  runDisposal(node, zombie);
@@ -978,8 +991,14 @@ function effect(compute, effect, error, initialValue, options) {
978
991
  }
979
992
  }
980
993
  if (!node._queue.notify(node, STATUS_ERROR, STATUS_ERROR)) throw err;
981
- } else if (node._type === EFFECT_RENDER)
994
+ } else if (node._type === EFFECT_RENDER) {
982
995
  node._queue.notify(node, STATUS_PENDING | STATUS_ERROR, actualStatus, actualError);
996
+ if (_hitUnhandledAsync) {
997
+ resetUnhandledAsync();
998
+ const err = new Error("An async value must be rendered inside a Loading boundary.");
999
+ if (!node._queue.notify(node, STATUS_ERROR, STATUS_ERROR)) throw err;
1000
+ }
1001
+ }
983
1002
  };
984
1003
  recompute(node, true);
985
1004
  !options?.defer &&
@@ -993,6 +1012,10 @@ function effect(compute, effect, error, initialValue, options) {
993
1012
  }
994
1013
  function runEffect() {
995
1014
  if (!this._modified || this._flags & REACTIVE_DISPOSED) return;
1015
+ let prevStrictRead = false;
1016
+ {
1017
+ prevStrictRead = setStrictRead("an effect callback");
1018
+ }
996
1019
  this._cleanup?.();
997
1020
  this._cleanup = undefined;
998
1021
  try {
@@ -1000,6 +1023,7 @@ function runEffect() {
1000
1023
  } catch (error) {
1001
1024
  if (!this._queue.notify(this, STATUS_ERROR, STATUS_ERROR)) throw error;
1002
1025
  } finally {
1026
+ setStrictRead(prevStrictRead);
1003
1027
  this._prevValue = this._value;
1004
1028
  this._modified = false;
1005
1029
  }
@@ -1033,6 +1057,29 @@ function trackedEffect(fn, options) {
1033
1057
  if (!node._parent)
1034
1058
  console.warn("Effects created outside a reactive context will never be disposed");
1035
1059
  }
1060
+ let externalSourceConfig = null;
1061
+ function enableExternalSource(config) {
1062
+ const { factory: factory, untrack: untrackFn = fn => fn() } = config;
1063
+ if (externalSourceConfig) {
1064
+ const { factory: oldFactory, untrack: oldUntrack } = externalSourceConfig;
1065
+ externalSourceConfig = {
1066
+ factory: (fn, trigger) => {
1067
+ const oldSource = oldFactory(fn, trigger);
1068
+ const source = factory(x => oldSource.track(x), trigger);
1069
+ return {
1070
+ track: x => source.track(x),
1071
+ dispose() {
1072
+ source.dispose();
1073
+ oldSource.dispose();
1074
+ }
1075
+ };
1076
+ },
1077
+ untrack: fn => oldUntrack(() => untrackFn(fn))
1078
+ };
1079
+ } else {
1080
+ externalSourceConfig = { factory: factory, untrack: untrackFn };
1081
+ }
1082
+ }
1036
1083
  GlobalQueue._update = recompute;
1037
1084
  GlobalQueue._dispose = disposeChildren;
1038
1085
  let tracking = false;
@@ -1112,7 +1159,7 @@ function recompute(el, create = false) {
1112
1159
  }
1113
1160
  }
1114
1161
  const isOptimisticDirty = !!(el._flags & REACTIVE_OPTIMISTIC_DIRTY);
1115
- const hasOverride = hasActiveOverride(el);
1162
+ const hasOverride = el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING;
1116
1163
  const wasPending = !!(el._statusFlags & STATUS_PENDING);
1117
1164
  const oldcontext = context;
1118
1165
  context = el;
@@ -1174,24 +1221,24 @@ function recompute(el, create = false) {
1174
1221
  else el._deps = null;
1175
1222
  }
1176
1223
  const compareValue = hasOverride
1177
- ? el._value
1224
+ ? el._overrideValue
1178
1225
  : el._pendingValue === NOT_PENDING
1179
1226
  ? el._value
1180
1227
  : el._pendingValue;
1181
1228
  const valueChanged = !el._equals || !el._equals(compareValue, value);
1182
1229
  if (valueChanged) {
1183
- const prevVisible = hasOverride ? el._value : undefined;
1184
- if (create || (isEffect && activeTransition !== el._transition) || isOptimisticDirty)
1230
+ const prevVisible = hasOverride ? el._overrideValue : undefined;
1231
+ if (create || (isEffect && activeTransition !== el._transition) || isOptimisticDirty) {
1185
1232
  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) {
1233
+ if (hasOverride && isOptimisticDirty) {
1234
+ el._overrideValue = value;
1235
+ el._pendingValue = value;
1236
+ }
1237
+ } else el._pendingValue = value;
1238
+ if (hasOverride && !isOptimisticDirty && wasPending && !el._overrideSinceLane)
1239
+ el._overrideValue = value;
1240
+ if (!hasOverride || isOptimisticDirty || el._overrideValue !== prevVisible)
1193
1241
  insertSubs(el, isOptimisticDirty || hasOverride);
1194
- }
1195
1242
  } else if (hasOverride) {
1196
1243
  el._pendingValue = value;
1197
1244
  } else if (el._height != oldHeight) {
@@ -1203,7 +1250,7 @@ function recompute(el, create = false) {
1203
1250
  currentOptimisticLane = prevLane;
1204
1251
  (!create || el._statusFlags & STATUS_PENDING) &&
1205
1252
  !el._transition &&
1206
- !(activeTransition && el._optimistic) &&
1253
+ !(activeTransition && hasOverride) &&
1207
1254
  globalQueue._pendingNodes.push(el);
1208
1255
  el._transition &&
1209
1256
  isEffect &&
@@ -1284,6 +1331,17 @@ function computed(fn, initialValue, options) {
1284
1331
  }
1285
1332
  if (parent) self._height = parent._height + 1;
1286
1333
  if (snapshotCaptureActive && ownerInSnapshotScope(context)) self._inSnapshotScope = true;
1334
+ if (externalSourceConfig) {
1335
+ const bridgeSignal = signal(undefined, { equals: false, pureWrite: true });
1336
+ const source = externalSourceConfig.factory(self._fn, () => {
1337
+ setSignal(bridgeSignal, undefined);
1338
+ });
1339
+ onCleanup(() => source.dispose());
1340
+ self._fn = prev => {
1341
+ read(bridgeSignal);
1342
+ return source.track(prev);
1343
+ };
1344
+ }
1287
1345
  !options?.lazy && recompute(self, true);
1288
1346
  if (snapshotCaptureActive && !options?.lazy) {
1289
1347
  if (!(self._statusFlags & STATUS_PENDING)) {
@@ -1308,7 +1366,7 @@ function signal(v, options, firewall = null) {
1308
1366
  };
1309
1367
  s._name = options?.name ?? "signal";
1310
1368
  firewall && (firewall._child = s);
1311
- if (snapshotCaptureActive && !s._pureWrite) {
1369
+ if (snapshotCaptureActive && !s._pureWrite && !((firewall?._statusFlags ?? 0) & STATUS_PENDING)) {
1312
1370
  s._snapshotValue = v === undefined ? NO_SNAPSHOT : v;
1313
1371
  snapshotSources.add(s);
1314
1372
  }
@@ -1316,12 +1374,12 @@ function signal(v, options, firewall = null) {
1316
1374
  }
1317
1375
  function optimisticSignal(v, options) {
1318
1376
  const s = signal(v, options);
1319
- s._optimistic = true;
1377
+ s._overrideValue = NOT_PENDING;
1320
1378
  return s;
1321
1379
  }
1322
1380
  function optimisticComputed(fn, initialValue, options) {
1323
1381
  const c = computed(fn, initialValue, options);
1324
- c._optimistic = true;
1382
+ c._overrideValue = NOT_PENDING;
1325
1383
  return c;
1326
1384
  }
1327
1385
  function isEqual(a, b) {
@@ -1334,12 +1392,13 @@ function setStrictRead(v) {
1334
1392
  return prev;
1335
1393
  }
1336
1394
  function untrack(fn, strictReadLabel) {
1337
- if (!tracking && !strictRead && !strictReadLabel) return fn();
1395
+ if (!externalSourceConfig && !tracking && !strictRead && !strictReadLabel) return fn();
1338
1396
  const prevTracking = tracking;
1339
1397
  const prevStrictRead = strictRead;
1340
1398
  tracking = false;
1341
1399
  strictRead = strictReadLabel || false;
1342
1400
  try {
1401
+ if (externalSourceConfig) return externalSourceConfig.untrack(fn);
1343
1402
  return fn();
1344
1403
  } finally {
1345
1404
  tracking = prevTracking;
@@ -1351,21 +1410,25 @@ function read(el) {
1351
1410
  const pendingComputed = getLatestValueComputed(el);
1352
1411
  const prevPending = latestReadActive;
1353
1412
  latestReadActive = false;
1413
+ const visibleValue =
1414
+ el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING
1415
+ ? el._overrideValue
1416
+ : el._value;
1354
1417
  let value;
1355
1418
  try {
1356
1419
  value = read(pendingComputed);
1357
1420
  } catch (e) {
1358
- if (!context && e instanceof NotReadyError) return el._value;
1421
+ if (!context && e instanceof NotReadyError) return visibleValue;
1359
1422
  throw e;
1360
1423
  } finally {
1361
1424
  latestReadActive = prevPending;
1362
1425
  }
1363
- if (pendingComputed._statusFlags & STATUS_PENDING) return el._value;
1426
+ if (pendingComputed._statusFlags & STATUS_PENDING) return visibleValue;
1364
1427
  if (stale && currentOptimisticLane && pendingComputed._optimisticLane) {
1365
1428
  const pcLane = findLane(pendingComputed._optimisticLane);
1366
1429
  const curLane = findLane(currentOptimisticLane);
1367
1430
  if (pcLane !== curLane && pcLane._pendingAsync.size > 0) {
1368
- return el._value;
1431
+ return visibleValue;
1369
1432
  }
1370
1433
  }
1371
1434
  return value;
@@ -1391,8 +1454,8 @@ function read(el) {
1391
1454
  const owner = el._firewall || el;
1392
1455
  if (strictRead && owner._statusFlags & STATUS_PENDING) {
1393
1456
  throw new Error(
1394
- `Reading a pending async value in ${strictRead}. ` +
1395
- `Async values must be read within a tracking scope (JSX, computations, effects).`
1457
+ `Reading a pending async value directly in ${strictRead}. ` +
1458
+ `Async values must be read within a tracking scope (JSX, a memo, or an effect's compute function).`
1396
1459
  );
1397
1460
  }
1398
1461
  if (c && tracking) {
@@ -1412,16 +1475,18 @@ function read(el) {
1412
1475
  }
1413
1476
  }
1414
1477
  if (owner._statusFlags & STATUS_PENDING) {
1415
- if (c && !(stale && owner._transition && activeTransition !== owner._transition)) {
1478
+ const _errSource = owner._error?.source;
1479
+ if (_errSource && !(_errSource._statusFlags & STATUS_PENDING)) clearStatus(owner);
1480
+ else if (c && !(stale && owner._transition && activeTransition !== owner._transition)) {
1416
1481
  if (currentOptimisticLane) {
1417
1482
  const pendingLane = owner._optimisticLane;
1418
1483
  const lane = findLane(currentOptimisticLane);
1419
1484
  if (pendingLane && findLane(pendingLane) === lane && !hasActiveOverride(owner)) {
1420
- if (!tracking) link(el, c);
1485
+ if (!tracking && el !== c) link(el, c);
1421
1486
  throw owner._error;
1422
1487
  }
1423
1488
  } else {
1424
- if (!tracking) link(el, c);
1489
+ if (!tracking && el !== c) link(el, c);
1425
1490
  throw owner._error;
1426
1491
  }
1427
1492
  } else if (!c && owner._statusFlags & STATUS_UNINITIALIZED) {
@@ -1445,12 +1510,14 @@ function read(el) {
1445
1510
  }
1446
1511
  if (strictRead)
1447
1512
  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).`
1513
+ `Reactive value read directly in ${strictRead} will not update. ` +
1514
+ `Move it into a tracking scope (JSX, a memo, or an effect's compute function).`
1450
1515
  );
1516
+ if (el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING)
1517
+ return el._overrideValue;
1451
1518
  return !c ||
1452
1519
  (currentOptimisticLane !== null &&
1453
- (el._optimistic ||
1520
+ (el._overrideValue !== undefined ||
1454
1521
  el._optimisticLane ||
1455
1522
  owner === el ||
1456
1523
  !!(owner._statusFlags & STATUS_PENDING))) ||
@@ -1464,9 +1531,12 @@ function setSignal(el, v) {
1464
1531
  console.warn("A Signal was written to in an owned scope.");
1465
1532
  if (el._transition && activeTransition !== el._transition)
1466
1533
  globalQueue.initTransition(el._transition);
1467
- const isOptimistic = el._optimistic && !projectionWriteActive;
1534
+ const isOptimistic = el._overrideValue !== undefined && !projectionWriteActive;
1535
+ const hasOverride = el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING;
1468
1536
  const currentValue = isOptimistic
1469
- ? el._value
1537
+ ? hasOverride
1538
+ ? el._overrideValue
1539
+ : el._value
1470
1540
  : el._pendingValue === NOT_PENDING
1471
1541
  ? el._value
1472
1542
  : el._pendingValue;
@@ -1474,27 +1544,23 @@ function setSignal(el, v) {
1474
1544
  const valueChanged =
1475
1545
  !el._equals || !el._equals(currentValue, v) || !!(el._statusFlags & STATUS_UNINITIALIZED);
1476
1546
  if (!valueChanged) {
1477
- if (isOptimistic && el._pendingValue !== NOT_PENDING && el._fn) {
1547
+ if (isOptimistic && hasOverride && el._fn) {
1478
1548
  insertSubs(el, true);
1479
1549
  schedule();
1480
1550
  }
1481
1551
  return v;
1482
1552
  }
1483
1553
  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) {
1554
+ const firstOverride = el._overrideValue === NOT_PENDING;
1555
+ if (!firstOverride && el._transition) globalQueue.initTransition(el._transition);
1556
+ if (firstOverride) {
1489
1557
  el._pendingValue = el._value;
1490
- }
1491
- if (!alreadyTracked) {
1492
1558
  globalQueue._optimisticNodes.push(el);
1493
1559
  }
1494
- el._overrideVersion = (el._overrideVersion || 0) + 1;
1560
+ el._overrideSinceLane = true;
1495
1561
  const lane = getOrCreateLane(el);
1496
1562
  el._optimisticLane = lane;
1497
- el._value = v;
1563
+ el._overrideValue = v;
1498
1564
  } else {
1499
1565
  if (el._pendingValue === NOT_PENDING) globalQueue._pendingNodes.push(el);
1500
1566
  el._pendingValue = v;
@@ -1532,7 +1598,7 @@ function getPendingSignal(el) {
1532
1598
  }
1533
1599
  function computePendingState(el) {
1534
1600
  const comp = el;
1535
- if (el._optimistic && el._pendingValue !== NOT_PENDING) {
1601
+ if (el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING) {
1536
1602
  if (comp._statusFlags & STATUS_PENDING && !(comp._statusFlags & STATUS_UNINITIALIZED))
1537
1603
  return true;
1538
1604
  if (el._parentSource) {
@@ -1702,17 +1768,22 @@ function action(genFn) {
1702
1768
  step();
1703
1769
  });
1704
1770
  }
1771
+ function accessor(node) {
1772
+ const fn = read.bind(null, node);
1773
+ fn.$r = true;
1774
+ return fn;
1775
+ }
1705
1776
  function createSignal(first, second, third) {
1706
1777
  if (typeof first === "function") {
1707
1778
  const node = computed(first, second, third);
1708
- return [read.bind(null, node), setSignal.bind(null, node)];
1779
+ return [accessor(node), setSignal.bind(null, node)];
1709
1780
  }
1710
1781
  const node = signal(first, second);
1711
- return [read.bind(null, node), setSignal.bind(null, node)];
1782
+ return [accessor(node), setSignal.bind(null, node)];
1712
1783
  }
1713
1784
  function createMemo(compute, value, options) {
1714
1785
  let node = computed(compute, value, options);
1715
- return read.bind(null, node);
1786
+ return accessor(node);
1716
1787
  }
1717
1788
  function createEffect(compute, effectFn, value, options) {
1718
1789
  effect(compute, effectFn.effect || effectFn, effectFn.error, value, {
@@ -1769,7 +1840,7 @@ function createOptimistic(first, second, third) {
1769
1840
  typeof first === "function"
1770
1841
  ? optimisticComputed(first, second, third)
1771
1842
  : optimisticSignal(first, second);
1772
- return [read.bind(null, node), setSignal.bind(null, node)];
1843
+ return [accessor(node), setSignal.bind(null, node)];
1773
1844
  }
1774
1845
  function onSettled(callback) {
1775
1846
  getOwner()
@@ -1791,7 +1862,7 @@ function getAllKeys(value, override, next) {
1791
1862
  const nextKeys = Object.keys(next);
1792
1863
  return Array.from(new Set([...keys, ...nextKeys]));
1793
1864
  }
1794
- function applyState(next, state, keyFn, all) {
1865
+ function applyState(next, state, keyFn) {
1795
1866
  const target = state?.[$TARGET];
1796
1867
  if (!target) return;
1797
1868
  const previous = target[STORE_VALUE];
@@ -1814,7 +1885,7 @@ function applyState(next, state, keyFn, all) {
1814
1885
  (item && next[start] && keyFn(item) === keyFn(next[start])));
1815
1886
  start++
1816
1887
  ) {
1817
- applyState(next[start], wrap(item, target), keyFn, all);
1888
+ applyState(next[start], wrap(item, target), keyFn);
1818
1889
  }
1819
1890
  const temp = new Array(next.length),
1820
1891
  newIndices = new Map();
@@ -1837,7 +1908,7 @@ function applyState(next, state, keyFn, all) {
1837
1908
  changed = true;
1838
1909
  const wrapped = wrap(temp[j], target);
1839
1910
  target[STORE_NODE][j] && setSignal(target[STORE_NODE][j], wrapped);
1840
- applyState(next[j], wrapped, keyFn, all);
1911
+ applyState(next[j], wrapped, keyFn);
1841
1912
  }
1842
1913
  changed && target[STORE_NODE][$TRACK] && setSignal(target[STORE_NODE][$TRACK], void 0);
1843
1914
  prevLength !== next.length &&
@@ -1867,7 +1938,7 @@ function applyState(next, state, keyFn, all) {
1867
1938
  if (j in temp) {
1868
1939
  const wrapped = wrap(temp[j], target);
1869
1940
  target[STORE_NODE][j] && setSignal(target[STORE_NODE][j], wrapped);
1870
- applyState(next[j], wrapped, keyFn, all);
1941
+ applyState(next[j], wrapped, keyFn);
1871
1942
  } else target[STORE_NODE][j] && setSignal(target[STORE_NODE][j], wrap(next[j], target));
1872
1943
  }
1873
1944
  if (start < next.length) changed = true;
@@ -1875,7 +1946,7 @@ function applyState(next, state, keyFn, all) {
1875
1946
  for (let i = 0, len = next.length; i < len; i++) {
1876
1947
  const item = getOverrideValue(previous, override, nodes, i, optOverride);
1877
1948
  isWrappable(item)
1878
- ? applyState(next[i], wrap(item, target), keyFn, all)
1949
+ ? applyState(next[i], wrap(item, target), keyFn)
1879
1950
  : target[STORE_NODE][i] && setSignal(target[STORE_NODE][i], next[i]);
1880
1951
  }
1881
1952
  }
@@ -1888,7 +1959,7 @@ function applyState(next, state, keyFn, all) {
1888
1959
  }
1889
1960
  if (nodes) {
1890
1961
  const tracked = nodes[$TRACK];
1891
- const keys = tracked || all ? getAllKeys(previous, override, next) : Object.keys(nodes);
1962
+ const keys = tracked ? getAllKeys(previous, override, next) : Object.keys(nodes);
1892
1963
  for (let i = 0, len = keys.length; i < len; i++) {
1893
1964
  const key = keys[i];
1894
1965
  const node = nodes[key];
@@ -1903,7 +1974,7 @@ function applyState(next, state, keyFn, all) {
1903
1974
  ) {
1904
1975
  tracked && setSignal(tracked, void 0);
1905
1976
  node && setSignal(node, isWrappable(nextValue) ? wrap(nextValue, target) : nextValue);
1906
- } else applyState(nextValue, wrap(previousValue, target), keyFn, all);
1977
+ } else applyState(nextValue, wrap(previousValue, target), keyFn);
1907
1978
  }
1908
1979
  }
1909
1980
  if ((nodes = target[STORE_HAS])) {
@@ -1914,14 +1985,14 @@ function applyState(next, state, keyFn, all) {
1914
1985
  }
1915
1986
  }
1916
1987
  }
1917
- function reconcile(value, key, all = false) {
1988
+ function reconcile(value, key) {
1918
1989
  return state => {
1919
1990
  if (state == null) throw new Error("Cannot reconcile null or undefined state");
1920
1991
  const keyFn = typeof key === "string" ? item => item[key] : key;
1921
1992
  const eq = keyFn(state);
1922
1993
  if (eq !== undefined && keyFn(value) !== keyFn(state))
1923
1994
  throw new Error("Cannot reconcile states with different identity");
1924
- applyState(value, state, keyFn, all);
1995
+ applyState(value, state, keyFn);
1925
1996
  };
1926
1997
  }
1927
1998
  function createProjectionInternal(fn, initialValue = {}, options) {
@@ -1951,11 +2022,9 @@ function createProjectionInternal(fn, initialValue = {}, options) {
1951
2022
  const value = handleAsync(owner, fn(s), value => {
1952
2023
  value !== s &&
1953
2024
  value !== undefined &&
1954
- storeSetter(wrappedStore, reconcile(value, options?.key || "id", options?.all));
2025
+ storeSetter(wrappedStore, reconcile(value, options?.key || "id"));
1955
2026
  });
1956
- value !== s &&
1957
- value !== undefined &&
1958
- reconcile(value, options?.key || "id", options?.all)(wrappedStore);
2027
+ value !== s && value !== undefined && reconcile(value, options?.key || "id")(wrappedStore);
1959
2028
  });
1960
2029
  });
1961
2030
  node._preventAutoDisposal = true;
@@ -2001,11 +2070,9 @@ const writeTraps = {
2001
2070
  }
2002
2071
  };
2003
2072
  const $TRACK = Symbol("STORE_TRACK"),
2004
- $DEEP = Symbol("STORE_DEEP"),
2005
2073
  $TARGET = Symbol("STORE_TARGET"),
2006
2074
  $PROXY = Symbol("STORE_PROXY"),
2007
2075
  $DELETED = Symbol("STORE_DELETED");
2008
- const PARENTS = new WeakMap();
2009
2076
  const STORE_VALUE = "v",
2010
2077
  STORE_OVERRIDE = "o",
2011
2078
  STORE_OPTIMISTIC_OVERRIDE = "x",
@@ -2058,7 +2125,9 @@ function getNode(nodes, property, value, firewall, equals = isEqual, optimistic,
2058
2125
  },
2059
2126
  firewall
2060
2127
  );
2061
- if (optimistic) s._optimistic = true;
2128
+ if (optimistic) {
2129
+ s._overrideValue = NOT_PENDING;
2130
+ }
2062
2131
  if (snapshotProps && property in snapshotProps) {
2063
2132
  const sv = snapshotProps[property];
2064
2133
  s._snapshotValue = sv === undefined ? NO_SNAPSHOT : sv;
@@ -2104,8 +2173,8 @@ const storeTraps = {
2104
2173
  if (property === $TARGET) return target;
2105
2174
  if (property === $PROXY) return receiver;
2106
2175
  if (property === $REFRESH) return target[STORE_FIREWALL];
2107
- if (property === $TRACK || property === $DEEP) {
2108
- trackSelf(target, property);
2176
+ if (property === $TRACK) {
2177
+ trackSelf(target);
2109
2178
  return receiver;
2110
2179
  }
2111
2180
  const nodes = getNodes(target, STORE_NODE);
@@ -2127,11 +2196,11 @@ const storeTraps = {
2127
2196
  if (writeOnly(receiver)) {
2128
2197
  let value =
2129
2198
  tracked && (overridden || !proxySource)
2130
- ? tracked._pendingValue !== NOT_PENDING
2131
- ? tracked._optimistic
2132
- ? tracked._value
2133
- : tracked._pendingValue
2134
- : tracked._value
2199
+ ? tracked._overrideValue !== undefined && tracked._overrideValue !== NOT_PENDING
2200
+ ? tracked._overrideValue
2201
+ : tracked._pendingValue !== NOT_PENDING
2202
+ ? tracked._pendingValue
2203
+ : tracked._value
2135
2204
  : storeValue[property];
2136
2205
  value === $DELETED && (value = undefined);
2137
2206
  if (!isWrappable(value)) return value;
@@ -2169,8 +2238,8 @@ const storeTraps = {
2169
2238
  }
2170
2239
  if (strictRead && typeof property === "string")
2171
2240
  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).`
2241
+ `Reactive value read directly in ${strictRead} will not update. ` +
2242
+ `Move it into a tracking scope (JSX, a memo, or an effect's compute function).`
2174
2243
  );
2175
2244
  return isWrappable(value) ? wrap(value, target) : value;
2176
2245
  },
@@ -2207,7 +2276,11 @@ const storeTraps = {
2207
2276
  untrack(() => {
2208
2277
  const state = target[STORE_VALUE];
2209
2278
  const base = state[property];
2210
- if (snapshotCaptureActive && typeof property !== "symbol") {
2279
+ if (
2280
+ snapshotCaptureActive &&
2281
+ typeof property !== "symbol" &&
2282
+ !((target[STORE_FIREWALL]?._statusFlags ?? 0) & STATUS_PENDING)
2283
+ ) {
2211
2284
  if (!target[STORE_SNAPSHOT_PROPS]) {
2212
2285
  target[STORE_SNAPSHOT_PROPS] = Object.create(null);
2213
2286
  snapshotSources?.add(target);
@@ -2234,11 +2307,6 @@ const storeTraps = {
2234
2307
  if (value !== undefined && value === base) delete target[overrideKey][property];
2235
2308
  else (target[overrideKey] || (target[overrideKey] = Object.create(null)))[property] = value;
2236
2309
  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
2310
  target[STORE_HAS]?.[property] && setSignal(target[STORE_HAS][property], true);
2243
2311
  const nodes = getNodes(target, STORE_NODE);
2244
2312
  nodes[property] &&
@@ -2278,10 +2346,6 @@ const storeTraps = {
2278
2346
  } else if (target[overrideKey] && property in target[overrideKey]) {
2279
2347
  delete target[overrideKey][property];
2280
2348
  } 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
2349
  if (target[STORE_HAS]?.[property]) setSignal(target[STORE_HAS][property], false);
2286
2350
  const nodes = getNodes(target, STORE_NODE);
2287
2351
  nodes[property] && setSignal(nodes[property], undefined);
@@ -2352,59 +2416,6 @@ function createStore(first, second, options) {
2352
2416
  wrappedStore = derived ? createProjectionInternal(first, second, options).store : wrap(first);
2353
2417
  return [wrappedStore, fn => storeSetter(wrappedStore, fn)];
2354
2418
  }
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
2419
  function createOptimisticStore(first, second, options) {
2409
2420
  GlobalQueue._clearOptimisticStore ||= clearOptimisticStore;
2410
2421
  const derived = typeof first === "function";
@@ -2475,14 +2486,14 @@ function createOptimisticProjectionInternal(fn, initialValue = {}, options) {
2475
2486
  try {
2476
2487
  value !== s &&
2477
2488
  value !== undefined &&
2478
- storeSetter(wrappedStore, reconcile(value, options?.key || "id", options?.all));
2489
+ storeSetter(wrappedStore, reconcile(value, options?.key || "id"));
2479
2490
  } finally {
2480
2491
  setProjectionWriteActive(false);
2481
2492
  }
2482
2493
  });
2483
2494
  value !== s &&
2484
2495
  value !== undefined &&
2485
- reconcile(value, options?.key || "id", options?.all)(wrappedStore);
2496
+ reconcile(value, options?.key || "id")(wrappedStore);
2486
2497
  });
2487
2498
  } finally {
2488
2499
  setProjectionWriteActive(false);
@@ -2557,12 +2568,13 @@ const storePath = Object.assign(
2557
2568
  },
2558
2569
  { DELETE: DELETE }
2559
2570
  );
2560
- function snapshot(item, map, lookup) {
2571
+ function snapshotImpl(item, track, map, lookup) {
2561
2572
  let target, isArray, override, result, unwrapped, v;
2562
2573
  if (!isWrappable(item)) return item;
2563
2574
  if (map && map.has(item)) return map.get(item);
2564
2575
  if (!map) map = new Map();
2565
2576
  if ((target = item[$TARGET] || lookup?.get(item)?.[$TARGET])) {
2577
+ if (track) trackSelf(target, $TRACK);
2566
2578
  override = target[STORE_OVERRIDE];
2567
2579
  isArray = Array.isArray(target[STORE_VALUE]);
2568
2580
  map.set(
@@ -2582,7 +2594,8 @@ function snapshot(item, map, lookup) {
2582
2594
  for (let i = 0; i < len; i++) {
2583
2595
  v = override && i in override ? override[i] : item[i];
2584
2596
  if (v === $DELETED) continue;
2585
- if ((unwrapped = snapshot(v, map, lookup)) !== v || result) {
2597
+ if (track && isWrappable(v)) wrap(v, target);
2598
+ if ((unwrapped = snapshotImpl(v, track, map, lookup)) !== v || result) {
2586
2599
  if (!result) map.set(item, (result = [...item]));
2587
2600
  result[i] = unwrapped;
2588
2601
  }
@@ -2594,7 +2607,8 @@ function snapshot(item, map, lookup) {
2594
2607
  const desc = getPropertyDescriptor(item, override, prop);
2595
2608
  if (desc.get) continue;
2596
2609
  v = override && prop in override ? override[prop] : item[prop];
2597
- if ((unwrapped = snapshot(v, map, lookup)) !== item[prop] || result) {
2610
+ if (track && isWrappable(v)) wrap(v, target);
2611
+ if ((unwrapped = snapshotImpl(v, track, map, lookup)) !== item[prop] || result) {
2598
2612
  if (!result) {
2599
2613
  result = Object.create(Object.getPrototypeOf(item));
2600
2614
  Object.assign(result, item);
@@ -2605,6 +2619,12 @@ function snapshot(item, map, lookup) {
2605
2619
  }
2606
2620
  return result || item;
2607
2621
  }
2622
+ function snapshot(item, map, lookup) {
2623
+ return snapshotImpl(item, false, map, lookup);
2624
+ }
2625
+ function deep(store) {
2626
+ return snapshotImpl(store, true);
2627
+ }
2608
2628
  function trueFn() {
2609
2629
  return true;
2610
2630
  }
@@ -2780,15 +2800,15 @@ function updateKeyedMap() {
2780
2800
  this._rows[j] = signal(newItems[j], pureOptions);
2781
2801
  this._indexes && (this._indexes[j] = signal(j, pureOptions));
2782
2802
  return this._map(
2783
- read.bind(null, this._rows[j]),
2784
- this._indexes ? read.bind(null, this._indexes[j]) : undefined
2803
+ accessor(this._rows[j]),
2804
+ this._indexes ? accessor(this._indexes[j]) : undefined
2785
2805
  );
2786
2806
  }
2787
2807
  : this._indexes
2788
2808
  ? () => {
2789
2809
  const item = newItems[j];
2790
2810
  this._indexes[j] = signal(j, pureOptions);
2791
- return this._map(() => item, read.bind(null, this._indexes[j]));
2811
+ return this._map(() => item, accessor(this._indexes[j]));
2792
2812
  }
2793
2813
  : () => {
2794
2814
  const item = newItems[j];
@@ -2992,11 +3012,14 @@ function createBoundChildren(owner, fn, queue, mask) {
2992
3012
  return boundaryComputed(() => staleValues(() => flatten(read(c))), mask);
2993
3013
  });
2994
3014
  }
3015
+ const ON_INIT = Symbol();
2995
3016
  class CollectionQueue extends Queue {
2996
3017
  _collectionType;
2997
3018
  _sources = new Set();
2998
3019
  _disabled = signal(false, { pureWrite: true });
2999
3020
  _initialized = false;
3021
+ _onFn;
3022
+ _prevOn = ON_INIT;
3000
3023
  constructor(type) {
3001
3024
  super();
3002
3025
  this._collectionType = type;
@@ -3006,10 +3029,22 @@ class CollectionQueue extends Queue {
3006
3029
  return super.run(type);
3007
3030
  }
3008
3031
  notify(node, type, flags, error) {
3009
- if (
3010
- !(type & this._collectionType) ||
3011
- (this._collectionType & STATUS_PENDING && this._initialized)
3012
- )
3032
+ if (!(type & this._collectionType)) return super.notify(node, type, flags, error);
3033
+ if (this._initialized && this._onFn) {
3034
+ const currentOn = untrack(() => {
3035
+ try {
3036
+ return this._onFn();
3037
+ } catch {
3038
+ return ON_INIT;
3039
+ }
3040
+ });
3041
+ if (currentOn !== this._prevOn) {
3042
+ this._prevOn = currentOn;
3043
+ this._initialized = false;
3044
+ this._sources.clear();
3045
+ }
3046
+ }
3047
+ if (this._collectionType & STATUS_PENDING && this._initialized)
3013
3048
  return super.notify(node, type, flags, error);
3014
3049
  if (flags & this._collectionType) {
3015
3050
  const source = error?.source || node._error?.source;
@@ -3026,12 +3061,20 @@ class CollectionQueue extends Queue {
3026
3061
  for (const source of this._sources) {
3027
3062
  if (!(source._statusFlags & this._collectionType)) this._sources.delete(source);
3028
3063
  }
3029
- if (!this._sources.size) setSignal(this._disabled, false);
3064
+ if (!this._sources.size) {
3065
+ setSignal(this._disabled, false);
3066
+ if (this._onFn) {
3067
+ try {
3068
+ this._prevOn = untrack(() => this._onFn());
3069
+ } catch {}
3070
+ }
3071
+ }
3030
3072
  }
3031
3073
  }
3032
- function createCollectionBoundary(type, fn, fallback) {
3074
+ function createCollectionBoundary(type, fn, fallback, onFn) {
3033
3075
  const owner = createOwner();
3034
3076
  const queue = new CollectionQueue(type);
3077
+ if (onFn) queue._onFn = onFn;
3035
3078
  const tree = createBoundChildren(owner, fn, queue, type);
3036
3079
  const decision = computed(() => {
3037
3080
  if (!read(queue._disabled)) {
@@ -3043,10 +3086,10 @@ function createCollectionBoundary(type, fn, fallback) {
3043
3086
  }
3044
3087
  return fallback(queue);
3045
3088
  });
3046
- return read.bind(null, decision);
3089
+ return accessor(decision);
3047
3090
  }
3048
- function createLoadBoundary(fn, fallback) {
3049
- return createCollectionBoundary(STATUS_PENDING, fn, () => fallback());
3091
+ function createLoadingBoundary(fn, fallback, options) {
3092
+ return createCollectionBoundary(STATUS_PENDING, fn, () => fallback(), options?.on);
3050
3093
  }
3051
3094
  function createErrorBoundary(fn, fallback) {
3052
3095
  return createCollectionBoundary(STATUS_ERROR, fn, queue => {
@@ -3128,7 +3171,7 @@ export {
3128
3171
  createContext,
3129
3172
  createEffect,
3130
3173
  createErrorBoundary,
3131
- createLoadBoundary,
3174
+ createLoadingBoundary,
3132
3175
  createMemo,
3133
3176
  createOptimistic,
3134
3177
  createOptimisticStore,
@@ -3141,6 +3184,8 @@ export {
3141
3184
  createStore,
3142
3185
  createTrackedEffect,
3143
3186
  deep,
3187
+ enableExternalSource,
3188
+ enforceLoadingBoundary,
3144
3189
  flatten,
3145
3190
  flush,
3146
3191
  getContext,
@@ -3167,7 +3212,6 @@ export {
3167
3212
  resolve,
3168
3213
  runWithOwner,
3169
3214
  setContext,
3170
- setOnUnhandledAsync,
3171
3215
  setSnapshotCapture,
3172
3216
  snapshot,
3173
3217
  storePath,