@solidjs/signals 0.11.3 → 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,6 +151,14 @@ let clock = 0;
151
151
  let activeTransition = null;
152
152
  let scheduled = false;
153
153
  let projectionWriteActive = false;
154
+ let _enforceLoadingBoundary = false;
155
+ let _hitUnhandledAsync = false;
156
+ function resetUnhandledAsync() {
157
+ _hitUnhandledAsync = false;
158
+ }
159
+ function enforceLoadingBoundary(enabled) {
160
+ _enforceLoadingBoundary = enabled;
161
+ }
154
162
  function runLaneEffects(type) {
155
163
  for (const lane of activeLanes) {
156
164
  if (lane._mergedInto || lane._pendingAsync.size > 0) continue;
@@ -167,7 +175,7 @@ function setProjectionWriteActive(value) {
167
175
  function schedule() {
168
176
  if (scheduled) return;
169
177
  scheduled = true;
170
- if (!globalQueue._running) queueMicrotask(flush);
178
+ if (!globalQueue._running && !projectionWriteActive) queueMicrotask(flush);
171
179
  }
172
180
  class Queue {
173
181
  _parent = null;
@@ -297,6 +305,7 @@ class GlobalQueue extends Queue {
297
305
  activeTransition._asyncNodes.push(actualError.source);
298
306
  schedule();
299
307
  }
308
+ if (_enforceLoadingBoundary) _hitUnhandledAsync = true;
300
309
  }
301
310
  return true;
302
311
  }
@@ -380,35 +389,40 @@ function insertSubs(node, optimistic = false) {
380
389
  insertIntoHeap(s._sub, queue);
381
390
  }
382
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
+ }
383
406
  function finalizePureQueue(completingTransition = null, incomplete = false) {
384
407
  let resolvePending = !incomplete;
408
+ if (resolvePending) commitPendingNodes();
385
409
  if (!incomplete) checkBoundaryChildren(globalQueue);
386
410
  if (dirtyQueue._max >= dirtyQueue._min) runHeap(dirtyQueue, GlobalQueue._update);
387
411
  if (resolvePending) {
388
- const pendingNodes = globalQueue._pendingNodes;
389
- for (let i = 0; i < pendingNodes.length; i++) {
390
- const n = pendingNodes[i];
391
- if (n._pendingValue !== NOT_PENDING) {
392
- n._value = n._pendingValue;
393
- n._pendingValue = NOT_PENDING;
394
- if (n._type && n._type !== EFFECT_TRACKED) n._modified = true;
395
- }
396
- if (!(n._statusFlags & STATUS_PENDING)) n._statusFlags &= ~STATUS_UNINITIALIZED;
397
- if (n._fn) GlobalQueue._dispose(n, false, true);
398
- }
399
- pendingNodes.length = 0;
412
+ commitPendingNodes();
400
413
  const optimisticNodes = completingTransition
401
414
  ? completingTransition._optimisticNodes
402
415
  : globalQueue._optimisticNodes;
403
416
  for (let i = 0; i < optimisticNodes.length; i++) {
404
417
  const n = optimisticNodes[i];
405
- const original = n._pendingValue;
406
418
  n._optimisticLane = undefined;
407
- if (original !== NOT_PENDING && n._value !== original) {
408
- n._value = original;
409
- insertSubs(n, true);
419
+ if (n._pendingValue !== NOT_PENDING) {
420
+ n._value = n._pendingValue;
421
+ n._pendingValue = NOT_PENDING;
410
422
  }
411
- 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);
412
426
  n._transition = null;
413
427
  }
414
428
  optimisticNodes.length = 0;
@@ -515,7 +529,7 @@ function getOrCreateLane(signal) {
515
529
  };
516
530
  signalLanes.set(signal, lane);
517
531
  activeLanes.add(lane);
518
- signal._laneVersion = signal._overrideVersion || 0;
532
+ signal._overrideSinceLane = false;
519
533
  return lane;
520
534
  }
521
535
  function findLane(lane) {
@@ -541,7 +555,7 @@ function resolveLane(el) {
541
555
  return undefined;
542
556
  }
543
557
  function hasActiveOverride(el) {
544
- return !!(el._optimistic && el._pendingValue !== NOT_PENDING);
558
+ return !!(el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING);
545
559
  }
546
560
  function assignOrMergeLane(el, sourceLane) {
547
561
  const sourceRoot = findLane(sourceLane);
@@ -588,10 +602,10 @@ function handleAsync(el, result, setter) {
588
602
  const lane = resolveLane(el);
589
603
  if (lane) lane._pendingAsync.delete(el);
590
604
  if (setter) setter(value);
591
- else if (el._optimistic) {
592
- const hadOverride = el._pendingValue !== NOT_PENDING;
593
- if (el._fn) el._pendingValue = value;
594
- if (!hadOverride) {
605
+ else if (el._overrideValue !== undefined) {
606
+ if (el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING)
607
+ el._pendingValue = value;
608
+ else {
595
609
  el._value = value;
596
610
  insertSubs(el);
597
611
  }
@@ -647,6 +661,10 @@ function handleAsync(el, result, setter) {
647
661
  syncResult = r;
648
662
  resolved = true;
649
663
  } else if (!r.done) asyncWrite(r.value, iterate);
664
+ else {
665
+ schedule();
666
+ flush();
667
+ }
650
668
  },
651
669
  e => {
652
670
  if (!isSync) handleError(e);
@@ -682,7 +700,8 @@ function notifyStatus(el, status, error, blockStatus, lane) {
682
700
  )
683
701
  error = new StatusError(el, error);
684
702
  const isSource = error instanceof NotReadyError && error.source === el;
685
- const isOptimisticBoundary = status === STATUS_PENDING && el._optimistic && !isSource;
703
+ const isOptimisticBoundary =
704
+ status === STATUS_PENDING && el._overrideValue !== undefined && !isSource;
686
705
  const startsBlocking = isOptimisticBoundary && hasActiveOverride(el);
687
706
  if (!blockStatus) {
688
707
  el._statusFlags =
@@ -833,7 +852,6 @@ function disposeChildren(node, self = false, zombie) {
833
852
  node._pendingFirstChild = null;
834
853
  } else {
835
854
  node._firstChild = null;
836
- node._nextSibling = null;
837
855
  node._childCount = 0;
838
856
  }
839
857
  runDisposal(node, zombie);
@@ -967,8 +985,14 @@ function effect(compute, effect, error, initialValue, options) {
967
985
  }
968
986
  }
969
987
  if (!node._queue.notify(node, STATUS_ERROR, STATUS_ERROR)) throw err;
970
- } else if (node._type === EFFECT_RENDER)
988
+ } else if (node._type === EFFECT_RENDER) {
971
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
+ }
972
996
  };
973
997
  recompute(node, true);
974
998
  !options?.defer &&
@@ -982,6 +1006,10 @@ function effect(compute, effect, error, initialValue, options) {
982
1006
  }
983
1007
  function runEffect() {
984
1008
  if (!this._modified || this._flags & REACTIVE_DISPOSED) return;
1009
+ let prevStrictRead = false;
1010
+ {
1011
+ prevStrictRead = setStrictRead("an effect callback");
1012
+ }
985
1013
  this._cleanup?.();
986
1014
  this._cleanup = undefined;
987
1015
  try {
@@ -989,6 +1017,7 @@ function runEffect() {
989
1017
  } catch (error) {
990
1018
  if (!this._queue.notify(this, STATUS_ERROR, STATUS_ERROR)) throw error;
991
1019
  } finally {
1020
+ setStrictRead(prevStrictRead);
992
1021
  this._prevValue = this._value;
993
1022
  this._modified = false;
994
1023
  }
@@ -1022,6 +1051,29 @@ function trackedEffect(fn, options) {
1022
1051
  if (!node._parent)
1023
1052
  console.warn("Effects created outside a reactive context will never be disposed");
1024
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
+ }
1025
1077
  GlobalQueue._update = recompute;
1026
1078
  GlobalQueue._dispose = disposeChildren;
1027
1079
  let tracking = false;
@@ -1101,7 +1153,7 @@ function recompute(el, create = false) {
1101
1153
  }
1102
1154
  }
1103
1155
  const isOptimisticDirty = !!(el._flags & REACTIVE_OPTIMISTIC_DIRTY);
1104
- const hasOverride = hasActiveOverride(el);
1156
+ const hasOverride = el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING;
1105
1157
  const wasPending = !!(el._statusFlags & STATUS_PENDING);
1106
1158
  const oldcontext = context;
1107
1159
  context = el;
@@ -1163,24 +1215,24 @@ function recompute(el, create = false) {
1163
1215
  else el._deps = null;
1164
1216
  }
1165
1217
  const compareValue = hasOverride
1166
- ? el._value
1218
+ ? el._overrideValue
1167
1219
  : el._pendingValue === NOT_PENDING
1168
1220
  ? el._value
1169
1221
  : el._pendingValue;
1170
1222
  const valueChanged = !el._equals || !el._equals(compareValue, value);
1171
1223
  if (valueChanged) {
1172
- const prevVisible = hasOverride ? el._value : undefined;
1173
- if (create || (isEffect && activeTransition !== el._transition) || isOptimisticDirty)
1224
+ const prevVisible = hasOverride ? el._overrideValue : undefined;
1225
+ if (create || (isEffect && activeTransition !== el._transition) || isOptimisticDirty) {
1174
1226
  el._value = value;
1175
- else el._pendingValue = value;
1176
- if (hasOverride && !isOptimisticDirty && wasPending) {
1177
- const ov = el._overrideVersion || 0;
1178
- const lv = el._laneVersion || 0;
1179
- if (ov <= lv) el._value = value;
1180
- }
1181
- 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)
1182
1235
  insertSubs(el, isOptimisticDirty || hasOverride);
1183
- }
1184
1236
  } else if (hasOverride) {
1185
1237
  el._pendingValue = value;
1186
1238
  } else if (el._height != oldHeight) {
@@ -1192,7 +1244,7 @@ function recompute(el, create = false) {
1192
1244
  currentOptimisticLane = prevLane;
1193
1245
  (!create || el._statusFlags & STATUS_PENDING) &&
1194
1246
  !el._transition &&
1195
- !(activeTransition && el._optimistic) &&
1247
+ !(activeTransition && hasOverride) &&
1196
1248
  globalQueue._pendingNodes.push(el);
1197
1249
  el._transition &&
1198
1250
  isEffect &&
@@ -1212,7 +1264,10 @@ function updateIfNecessary(el) {
1212
1264
  }
1213
1265
  }
1214
1266
  }
1215
- if (el._flags & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY) || (el._error && el._time < clock)) {
1267
+ if (
1268
+ el._flags & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY) ||
1269
+ (el._error && el._time < clock && !el._inFlight)
1270
+ ) {
1216
1271
  recompute(el);
1217
1272
  }
1218
1273
  el._flags = REACTIVE_NONE | (el._flags & REACTIVE_SNAPSHOT_STALE);
@@ -1270,6 +1325,17 @@ function computed(fn, initialValue, options) {
1270
1325
  }
1271
1326
  if (parent) self._height = parent._height + 1;
1272
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
+ }
1273
1339
  !options?.lazy && recompute(self, true);
1274
1340
  if (snapshotCaptureActive && !options?.lazy) {
1275
1341
  if (!(self._statusFlags & STATUS_PENDING)) {
@@ -1294,7 +1360,7 @@ function signal(v, options, firewall = null) {
1294
1360
  };
1295
1361
  s._name = options?.name ?? "signal";
1296
1362
  firewall && (firewall._child = s);
1297
- if (snapshotCaptureActive && !s._pureWrite) {
1363
+ if (snapshotCaptureActive && !s._pureWrite && !((firewall?._statusFlags ?? 0) & STATUS_PENDING)) {
1298
1364
  s._snapshotValue = v === undefined ? NO_SNAPSHOT : v;
1299
1365
  snapshotSources.add(s);
1300
1366
  }
@@ -1302,12 +1368,12 @@ function signal(v, options, firewall = null) {
1302
1368
  }
1303
1369
  function optimisticSignal(v, options) {
1304
1370
  const s = signal(v, options);
1305
- s._optimistic = true;
1371
+ s._overrideValue = NOT_PENDING;
1306
1372
  return s;
1307
1373
  }
1308
1374
  function optimisticComputed(fn, initialValue, options) {
1309
1375
  const c = computed(fn, initialValue, options);
1310
- c._optimistic = true;
1376
+ c._overrideValue = NOT_PENDING;
1311
1377
  return c;
1312
1378
  }
1313
1379
  function isEqual(a, b) {
@@ -1319,23 +1385,18 @@ function setStrictRead(v) {
1319
1385
  strictRead = v;
1320
1386
  return prev;
1321
1387
  }
1322
- function untrack(fn) {
1323
- if (!tracking && !strictRead) return fn();
1388
+ function untrack(fn, strictReadLabel) {
1389
+ if (!externalSourceConfig && !tracking && !strictRead && !strictReadLabel) return fn();
1390
+ const prevTracking = tracking;
1391
+ const prevStrictRead = strictRead;
1324
1392
  tracking = false;
1325
- if (strictRead) {
1326
- const prev = strictRead;
1327
- strictRead = false;
1328
- try {
1329
- return fn();
1330
- } finally {
1331
- tracking = true;
1332
- strictRead = prev;
1333
- }
1334
- }
1393
+ strictRead = strictReadLabel || false;
1335
1394
  try {
1395
+ if (externalSourceConfig) return externalSourceConfig.untrack(fn);
1336
1396
  return fn();
1337
1397
  } finally {
1338
- tracking = true;
1398
+ tracking = prevTracking;
1399
+ strictRead = prevStrictRead;
1339
1400
  }
1340
1401
  }
1341
1402
  function read(el) {
@@ -1343,21 +1404,25 @@ function read(el) {
1343
1404
  const pendingComputed = getLatestValueComputed(el);
1344
1405
  const prevPending = latestReadActive;
1345
1406
  latestReadActive = false;
1407
+ const visibleValue =
1408
+ el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING
1409
+ ? el._overrideValue
1410
+ : el._value;
1346
1411
  let value;
1347
1412
  try {
1348
1413
  value = read(pendingComputed);
1349
1414
  } catch (e) {
1350
- if (!context && e instanceof NotReadyError) return el._value;
1415
+ if (!context && e instanceof NotReadyError) return visibleValue;
1351
1416
  throw e;
1352
1417
  } finally {
1353
1418
  latestReadActive = prevPending;
1354
1419
  }
1355
- if (pendingComputed._statusFlags & STATUS_PENDING) return el._value;
1420
+ if (pendingComputed._statusFlags & STATUS_PENDING) return visibleValue;
1356
1421
  if (stale && currentOptimisticLane && pendingComputed._optimisticLane) {
1357
1422
  const pcLane = findLane(pendingComputed._optimisticLane);
1358
1423
  const curLane = findLane(currentOptimisticLane);
1359
1424
  if (pcLane !== curLane && pcLane._pendingAsync.size > 0) {
1360
- return el._value;
1425
+ return visibleValue;
1361
1426
  }
1362
1427
  }
1363
1428
  return value;
@@ -1383,8 +1448,8 @@ function read(el) {
1383
1448
  const owner = el._firewall || el;
1384
1449
  if (strictRead && owner._statusFlags & STATUS_PENDING) {
1385
1450
  throw new Error(
1386
- `Reading a pending async value in ${strictRead}. ` +
1387
- `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).`
1388
1453
  );
1389
1454
  }
1390
1455
  if (c && tracking) {
@@ -1409,11 +1474,11 @@ function read(el) {
1409
1474
  const pendingLane = owner._optimisticLane;
1410
1475
  const lane = findLane(currentOptimisticLane);
1411
1476
  if (pendingLane && findLane(pendingLane) === lane && !hasActiveOverride(owner)) {
1412
- if (!tracking) link(el, c);
1477
+ if (!tracking && el !== c) link(el, c);
1413
1478
  throw owner._error;
1414
1479
  }
1415
1480
  } else {
1416
- if (!tracking) link(el, c);
1481
+ if (!tracking && el !== c) link(el, c);
1417
1482
  throw owner._error;
1418
1483
  }
1419
1484
  } else if (!c && owner._statusFlags & STATUS_UNINITIALIZED) {
@@ -1435,13 +1500,19 @@ function read(el) {
1435
1500
  return snapshot;
1436
1501
  }
1437
1502
  }
1438
- if (strictRead && !tracking)
1503
+ if (strictRead)
1439
1504
  console.warn(
1440
- `Reactive value read at the top level of ${strictRead} will not update. ` +
1441
- `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).`
1442
1507
  );
1508
+ if (el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING)
1509
+ return el._overrideValue;
1443
1510
  return !c ||
1444
- currentOptimisticLane !== null ||
1511
+ (currentOptimisticLane !== null &&
1512
+ (el._overrideValue !== undefined ||
1513
+ el._optimisticLane ||
1514
+ owner === el ||
1515
+ !!(owner._statusFlags & STATUS_PENDING))) ||
1445
1516
  el._pendingValue === NOT_PENDING ||
1446
1517
  (stale && el._transition && activeTransition !== el._transition)
1447
1518
  ? el._value
@@ -1452,9 +1523,12 @@ function setSignal(el, v) {
1452
1523
  console.warn("A Signal was written to in an owned scope.");
1453
1524
  if (el._transition && activeTransition !== el._transition)
1454
1525
  globalQueue.initTransition(el._transition);
1455
- const isOptimistic = el._optimistic && !projectionWriteActive;
1526
+ const isOptimistic = el._overrideValue !== undefined && !projectionWriteActive;
1527
+ const hasOverride = el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING;
1456
1528
  const currentValue = isOptimistic
1457
- ? el._value
1529
+ ? hasOverride
1530
+ ? el._overrideValue
1531
+ : el._value
1458
1532
  : el._pendingValue === NOT_PENDING
1459
1533
  ? el._value
1460
1534
  : el._pendingValue;
@@ -1462,27 +1536,23 @@ function setSignal(el, v) {
1462
1536
  const valueChanged =
1463
1537
  !el._equals || !el._equals(currentValue, v) || !!(el._statusFlags & STATUS_UNINITIALIZED);
1464
1538
  if (!valueChanged) {
1465
- if (isOptimistic && el._pendingValue !== NOT_PENDING && el._fn) {
1539
+ if (isOptimistic && hasOverride && el._fn) {
1466
1540
  insertSubs(el, true);
1467
1541
  schedule();
1468
1542
  }
1469
1543
  return v;
1470
1544
  }
1471
1545
  if (isOptimistic) {
1472
- const alreadyTracked = globalQueue._optimisticNodes.includes(el);
1473
- if (el._transition && alreadyTracked) {
1474
- globalQueue.initTransition(el._transition);
1475
- }
1476
- if (el._pendingValue === NOT_PENDING) {
1546
+ const firstOverride = el._overrideValue === NOT_PENDING;
1547
+ if (!firstOverride && el._transition) globalQueue.initTransition(el._transition);
1548
+ if (firstOverride) {
1477
1549
  el._pendingValue = el._value;
1478
- }
1479
- if (!alreadyTracked) {
1480
1550
  globalQueue._optimisticNodes.push(el);
1481
1551
  }
1482
- el._overrideVersion = (el._overrideVersion || 0) + 1;
1552
+ el._overrideSinceLane = true;
1483
1553
  const lane = getOrCreateLane(el);
1484
1554
  el._optimisticLane = lane;
1485
- el._value = v;
1555
+ el._overrideValue = v;
1486
1556
  } else {
1487
1557
  if (el._pendingValue === NOT_PENDING) globalQueue._pendingNodes.push(el);
1488
1558
  el._pendingValue = v;
@@ -1520,7 +1590,7 @@ function getPendingSignal(el) {
1520
1590
  }
1521
1591
  function computePendingState(el) {
1522
1592
  const comp = el;
1523
- if (el._optimistic && el._pendingValue !== NOT_PENDING) {
1593
+ if (el._overrideValue !== undefined && el._overrideValue !== NOT_PENDING) {
1524
1594
  if (comp._statusFlags & STATUS_PENDING && !(comp._statusFlags & STATUS_UNINITIALIZED))
1525
1595
  return true;
1526
1596
  if (el._parentSource) {
@@ -1690,17 +1760,22 @@ function action(genFn) {
1690
1760
  step();
1691
1761
  });
1692
1762
  }
1763
+ function accessor(node) {
1764
+ const fn = read.bind(null, node);
1765
+ fn.$r = true;
1766
+ return fn;
1767
+ }
1693
1768
  function createSignal(first, second, third) {
1694
1769
  if (typeof first === "function") {
1695
1770
  const node = computed(first, second, third);
1696
- return [read.bind(null, node), setSignal.bind(null, node)];
1771
+ return [accessor(node), setSignal.bind(null, node)];
1697
1772
  }
1698
1773
  const node = signal(first, second);
1699
- return [read.bind(null, node), setSignal.bind(null, node)];
1774
+ return [accessor(node), setSignal.bind(null, node)];
1700
1775
  }
1701
1776
  function createMemo(compute, value, options) {
1702
1777
  let node = computed(compute, value, options);
1703
- return read.bind(null, node);
1778
+ return accessor(node);
1704
1779
  }
1705
1780
  function createEffect(compute, effectFn, value, options) {
1706
1781
  effect(compute, effectFn.effect || effectFn, effectFn.error, value, {
@@ -1757,7 +1832,7 @@ function createOptimistic(first, second, third) {
1757
1832
  typeof first === "function"
1758
1833
  ? optimisticComputed(first, second, third)
1759
1834
  : optimisticSignal(first, second);
1760
- return [read.bind(null, node), setSignal.bind(null, node)];
1835
+ return [accessor(node), setSignal.bind(null, node)];
1761
1836
  }
1762
1837
  function onSettled(callback) {
1763
1838
  getOwner()
@@ -1779,7 +1854,7 @@ function getAllKeys(value, override, next) {
1779
1854
  const nextKeys = Object.keys(next);
1780
1855
  return Array.from(new Set([...keys, ...nextKeys]));
1781
1856
  }
1782
- function applyState(next, state, keyFn, all) {
1857
+ function applyState(next, state, keyFn) {
1783
1858
  const target = state?.[$TARGET];
1784
1859
  if (!target) return;
1785
1860
  const previous = target[STORE_VALUE];
@@ -1802,7 +1877,7 @@ function applyState(next, state, keyFn, all) {
1802
1877
  (item && next[start] && keyFn(item) === keyFn(next[start])));
1803
1878
  start++
1804
1879
  ) {
1805
- applyState(next[start], wrap(item, target), keyFn, all);
1880
+ applyState(next[start], wrap(item, target), keyFn);
1806
1881
  }
1807
1882
  const temp = new Array(next.length),
1808
1883
  newIndices = new Map();
@@ -1825,7 +1900,7 @@ function applyState(next, state, keyFn, all) {
1825
1900
  changed = true;
1826
1901
  const wrapped = wrap(temp[j], target);
1827
1902
  target[STORE_NODE][j] && setSignal(target[STORE_NODE][j], wrapped);
1828
- applyState(next[j], wrapped, keyFn, all);
1903
+ applyState(next[j], wrapped, keyFn);
1829
1904
  }
1830
1905
  changed && target[STORE_NODE][$TRACK] && setSignal(target[STORE_NODE][$TRACK], void 0);
1831
1906
  prevLength !== next.length &&
@@ -1855,7 +1930,7 @@ function applyState(next, state, keyFn, all) {
1855
1930
  if (j in temp) {
1856
1931
  const wrapped = wrap(temp[j], target);
1857
1932
  target[STORE_NODE][j] && setSignal(target[STORE_NODE][j], wrapped);
1858
- applyState(next[j], wrapped, keyFn, all);
1933
+ applyState(next[j], wrapped, keyFn);
1859
1934
  } else target[STORE_NODE][j] && setSignal(target[STORE_NODE][j], wrap(next[j], target));
1860
1935
  }
1861
1936
  if (start < next.length) changed = true;
@@ -1863,7 +1938,7 @@ function applyState(next, state, keyFn, all) {
1863
1938
  for (let i = 0, len = next.length; i < len; i++) {
1864
1939
  const item = getOverrideValue(previous, override, nodes, i, optOverride);
1865
1940
  isWrappable(item)
1866
- ? applyState(next[i], wrap(item, target), keyFn, all)
1941
+ ? applyState(next[i], wrap(item, target), keyFn)
1867
1942
  : target[STORE_NODE][i] && setSignal(target[STORE_NODE][i], next[i]);
1868
1943
  }
1869
1944
  }
@@ -1876,7 +1951,7 @@ function applyState(next, state, keyFn, all) {
1876
1951
  }
1877
1952
  if (nodes) {
1878
1953
  const tracked = nodes[$TRACK];
1879
- const keys = tracked || all ? getAllKeys(previous, override, next) : Object.keys(nodes);
1954
+ const keys = tracked ? getAllKeys(previous, override, next) : Object.keys(nodes);
1880
1955
  for (let i = 0, len = keys.length; i < len; i++) {
1881
1956
  const key = keys[i];
1882
1957
  const node = nodes[key];
@@ -1891,7 +1966,7 @@ function applyState(next, state, keyFn, all) {
1891
1966
  ) {
1892
1967
  tracked && setSignal(tracked, void 0);
1893
1968
  node && setSignal(node, isWrappable(nextValue) ? wrap(nextValue, target) : nextValue);
1894
- } else applyState(nextValue, wrap(previousValue, target), keyFn, all);
1969
+ } else applyState(nextValue, wrap(previousValue, target), keyFn);
1895
1970
  }
1896
1971
  }
1897
1972
  if ((nodes = target[STORE_HAS])) {
@@ -1902,14 +1977,14 @@ function applyState(next, state, keyFn, all) {
1902
1977
  }
1903
1978
  }
1904
1979
  }
1905
- function reconcile(value, key, all = false) {
1980
+ function reconcile(value, key) {
1906
1981
  return state => {
1907
1982
  if (state == null) throw new Error("Cannot reconcile null or undefined state");
1908
1983
  const keyFn = typeof key === "string" ? item => item[key] : key;
1909
1984
  const eq = keyFn(state);
1910
1985
  if (eq !== undefined && keyFn(value) !== keyFn(state))
1911
1986
  throw new Error("Cannot reconcile states with different identity");
1912
- applyState(value, state, keyFn, all);
1987
+ applyState(value, state, keyFn);
1913
1988
  };
1914
1989
  }
1915
1990
  function createProjectionInternal(fn, initialValue = {}, options) {
@@ -1939,12 +2014,9 @@ function createProjectionInternal(fn, initialValue = {}, options) {
1939
2014
  const value = handleAsync(owner, fn(s), value => {
1940
2015
  value !== s &&
1941
2016
  value !== undefined &&
1942
- storeSetter(wrappedStore, reconcile(value, options?.key || "id", options?.all));
1943
- setSignal(owner, undefined);
2017
+ storeSetter(wrappedStore, reconcile(value, options?.key || "id"));
1944
2018
  });
1945
- value !== s &&
1946
- value !== undefined &&
1947
- reconcile(value, options?.key || "id", options?.all)(wrappedStore);
2019
+ value !== s && value !== undefined && reconcile(value, options?.key || "id")(wrappedStore);
1948
2020
  });
1949
2021
  });
1950
2022
  node._preventAutoDisposal = true;
@@ -1990,11 +2062,9 @@ const writeTraps = {
1990
2062
  }
1991
2063
  };
1992
2064
  const $TRACK = Symbol("STORE_TRACK"),
1993
- $DEEP = Symbol("STORE_DEEP"),
1994
2065
  $TARGET = Symbol("STORE_TARGET"),
1995
2066
  $PROXY = Symbol("STORE_PROXY"),
1996
2067
  $DELETED = Symbol("STORE_DELETED");
1997
- const PARENTS = new WeakMap();
1998
2068
  const STORE_VALUE = "v",
1999
2069
  STORE_OVERRIDE = "o",
2000
2070
  STORE_OPTIMISTIC_OVERRIDE = "x",
@@ -2047,7 +2117,9 @@ function getNode(nodes, property, value, firewall, equals = isEqual, optimistic,
2047
2117
  },
2048
2118
  firewall
2049
2119
  );
2050
- if (optimistic) s._optimistic = true;
2120
+ if (optimistic) {
2121
+ s._overrideValue = NOT_PENDING;
2122
+ }
2051
2123
  if (snapshotProps && property in snapshotProps) {
2052
2124
  const sv = snapshotProps[property];
2053
2125
  s._snapshotValue = sv === undefined ? NO_SNAPSHOT : sv;
@@ -2093,8 +2165,8 @@ const storeTraps = {
2093
2165
  if (property === $TARGET) return target;
2094
2166
  if (property === $PROXY) return receiver;
2095
2167
  if (property === $REFRESH) return target[STORE_FIREWALL];
2096
- if (property === $TRACK || property === $DEEP) {
2097
- trackSelf(target, property);
2168
+ if (property === $TRACK) {
2169
+ trackSelf(target);
2098
2170
  return receiver;
2099
2171
  }
2100
2172
  const nodes = getNodes(target, STORE_NODE);
@@ -2116,11 +2188,11 @@ const storeTraps = {
2116
2188
  if (writeOnly(receiver)) {
2117
2189
  let value =
2118
2190
  tracked && (overridden || !proxySource)
2119
- ? tracked._pendingValue !== NOT_PENDING
2120
- ? tracked._optimistic
2121
- ? tracked._value
2122
- : tracked._pendingValue
2123
- : tracked._value
2191
+ ? tracked._overrideValue !== undefined && tracked._overrideValue !== NOT_PENDING
2192
+ ? tracked._overrideValue
2193
+ : tracked._pendingValue !== NOT_PENDING
2194
+ ? tracked._pendingValue
2195
+ : tracked._value
2124
2196
  : storeValue[property];
2125
2197
  value === $DELETED && (value = undefined);
2126
2198
  if (!isWrappable(value)) return value;
@@ -2156,10 +2228,10 @@ const storeTraps = {
2156
2228
  );
2157
2229
  }
2158
2230
  }
2159
- if (strictRead && !tracking && typeof property === "string")
2231
+ if (strictRead && typeof property === "string")
2160
2232
  console.warn(
2161
- `Reactive value read at the top level of ${strictRead} will not update. ` +
2162
- `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).`
2163
2235
  );
2164
2236
  return isWrappable(value) ? wrap(value, target) : value;
2165
2237
  },
@@ -2196,7 +2268,11 @@ const storeTraps = {
2196
2268
  untrack(() => {
2197
2269
  const state = target[STORE_VALUE];
2198
2270
  const base = state[property];
2199
- if (snapshotCaptureActive && typeof property !== "symbol") {
2271
+ if (
2272
+ snapshotCaptureActive &&
2273
+ typeof property !== "symbol" &&
2274
+ !((target[STORE_FIREWALL]?._statusFlags ?? 0) & STATUS_PENDING)
2275
+ ) {
2200
2276
  if (!target[STORE_SNAPSHOT_PROPS]) {
2201
2277
  target[STORE_SNAPSHOT_PROPS] = Object.create(null);
2202
2278
  snapshotSources?.add(target);
@@ -2223,11 +2299,6 @@ const storeTraps = {
2223
2299
  if (value !== undefined && value === base) delete target[overrideKey][property];
2224
2300
  else (target[overrideKey] || (target[overrideKey] = Object.create(null)))[property] = value;
2225
2301
  const wrappable = isWrappable(value);
2226
- if (isWrappable(prev)) {
2227
- const parents = PARENTS.get(prev);
2228
- parents && (parents instanceof Set ? parents.delete(store) : PARENTS.delete(prev));
2229
- }
2230
- if (recursivelyNotify(store, storeLookup) && wrappable) recursivelyAddParent(value, store);
2231
2302
  target[STORE_HAS]?.[property] && setSignal(target[STORE_HAS][property], true);
2232
2303
  const nodes = getNodes(target, STORE_NODE);
2233
2304
  nodes[property] &&
@@ -2267,10 +2338,6 @@ const storeTraps = {
2267
2338
  } else if (target[overrideKey] && property in target[overrideKey]) {
2268
2339
  delete target[overrideKey][property];
2269
2340
  } else return true;
2270
- if (isWrappable(prev)) {
2271
- const parents = PARENTS.get(prev);
2272
- parents && (parents instanceof Set ? parents.delete(target) : PARENTS.delete(prev));
2273
- }
2274
2341
  if (target[STORE_HAS]?.[property]) setSignal(target[STORE_HAS][property], false);
2275
2342
  const nodes = getNodes(target, STORE_NODE);
2276
2343
  nodes[property] && setSignal(nodes[property], undefined);
@@ -2341,59 +2408,6 @@ function createStore(first, second, options) {
2341
2408
  wrappedStore = derived ? createProjectionInternal(first, second, options).store : wrap(first);
2342
2409
  return [wrappedStore, fn => storeSetter(wrappedStore, fn)];
2343
2410
  }
2344
- function recursivelyNotify(state, lookup) {
2345
- let target = state[$TARGET] || lookup?.get(state)?.[$TARGET];
2346
- let notified = false;
2347
- if (target) {
2348
- const deep = getNodes(target, STORE_NODE)[$DEEP];
2349
- if (deep) {
2350
- setSignal(deep, undefined);
2351
- notified = true;
2352
- }
2353
- lookup = target[STORE_LOOKUP] || lookup;
2354
- }
2355
- const parents = PARENTS.get(target?.[STORE_VALUE] || state);
2356
- if (!parents) return notified;
2357
- if (parents instanceof Set) {
2358
- for (let parent of parents) notified = recursivelyNotify(parent, lookup) || notified;
2359
- } else notified = recursivelyNotify(parents, lookup) || notified;
2360
- return notified;
2361
- }
2362
- function recursivelyAddParent(state, parent) {
2363
- let override;
2364
- const target = state[$TARGET];
2365
- if (target) {
2366
- override = target[STORE_OVERRIDE];
2367
- state = target[STORE_VALUE];
2368
- }
2369
- if (parent) {
2370
- let parents = PARENTS.get(state);
2371
- if (!parents) PARENTS.set(state, parent);
2372
- else if (parents !== parent) {
2373
- if (!(parents instanceof Set)) PARENTS.set(state, (parents = new Set([parents])));
2374
- else if (parents.has(parent)) return;
2375
- parents.add(parent);
2376
- } else return;
2377
- }
2378
- if (Array.isArray(state)) {
2379
- const len = override?.length || state.length;
2380
- for (let i = 0; i < len; i++) {
2381
- const item = override && i in override ? override[i] : state[i];
2382
- isWrappable(item) && recursivelyAddParent(item, state);
2383
- }
2384
- } else {
2385
- const keys = getKeys(state, override);
2386
- for (let i = 0; i < keys.length; i++) {
2387
- const key = keys[i];
2388
- const item = override && key in override ? override[key] : state[key];
2389
- isWrappable(item) && recursivelyAddParent(item, state);
2390
- }
2391
- }
2392
- }
2393
- function deep(store) {
2394
- recursivelyAddParent(store);
2395
- return store[$DEEP];
2396
- }
2397
2411
  function createOptimisticStore(first, second, options) {
2398
2412
  GlobalQueue._clearOptimisticStore ||= clearOptimisticStore;
2399
2413
  const derived = typeof first === "function";
@@ -2464,14 +2478,14 @@ function createOptimisticProjectionInternal(fn, initialValue = {}, options) {
2464
2478
  try {
2465
2479
  value !== s &&
2466
2480
  value !== undefined &&
2467
- storeSetter(wrappedStore, reconcile(value, options?.key || "id", options?.all));
2481
+ storeSetter(wrappedStore, reconcile(value, options?.key || "id"));
2468
2482
  } finally {
2469
2483
  setProjectionWriteActive(false);
2470
2484
  }
2471
2485
  });
2472
2486
  value !== s &&
2473
2487
  value !== undefined &&
2474
- reconcile(value, options?.key || "id", options?.all)(wrappedStore);
2488
+ reconcile(value, options?.key || "id")(wrappedStore);
2475
2489
  });
2476
2490
  } finally {
2477
2491
  setProjectionWriteActive(false);
@@ -2546,12 +2560,13 @@ const storePath = Object.assign(
2546
2560
  },
2547
2561
  { DELETE: DELETE }
2548
2562
  );
2549
- function snapshot(item, map, lookup) {
2563
+ function snapshotImpl(item, track, map, lookup) {
2550
2564
  let target, isArray, override, result, unwrapped, v;
2551
2565
  if (!isWrappable(item)) return item;
2552
2566
  if (map && map.has(item)) return map.get(item);
2553
2567
  if (!map) map = new Map();
2554
2568
  if ((target = item[$TARGET] || lookup?.get(item)?.[$TARGET])) {
2569
+ if (track) trackSelf(target, $TRACK);
2555
2570
  override = target[STORE_OVERRIDE];
2556
2571
  isArray = Array.isArray(target[STORE_VALUE]);
2557
2572
  map.set(
@@ -2571,7 +2586,8 @@ function snapshot(item, map, lookup) {
2571
2586
  for (let i = 0; i < len; i++) {
2572
2587
  v = override && i in override ? override[i] : item[i];
2573
2588
  if (v === $DELETED) continue;
2574
- 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) {
2575
2591
  if (!result) map.set(item, (result = [...item]));
2576
2592
  result[i] = unwrapped;
2577
2593
  }
@@ -2583,7 +2599,8 @@ function snapshot(item, map, lookup) {
2583
2599
  const desc = getPropertyDescriptor(item, override, prop);
2584
2600
  if (desc.get) continue;
2585
2601
  v = override && prop in override ? override[prop] : item[prop];
2586
- 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) {
2587
2604
  if (!result) {
2588
2605
  result = Object.create(Object.getPrototypeOf(item));
2589
2606
  Object.assign(result, item);
@@ -2594,6 +2611,12 @@ function snapshot(item, map, lookup) {
2594
2611
  }
2595
2612
  return result || item;
2596
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
+ }
2597
2620
  function trueFn() {
2598
2621
  return true;
2599
2622
  }
@@ -2769,15 +2792,15 @@ function updateKeyedMap() {
2769
2792
  this._rows[j] = signal(newItems[j], pureOptions);
2770
2793
  this._indexes && (this._indexes[j] = signal(j, pureOptions));
2771
2794
  return this._map(
2772
- read.bind(null, this._rows[j]),
2773
- this._indexes ? read.bind(null, this._indexes[j]) : undefined
2795
+ accessor(this._rows[j]),
2796
+ this._indexes ? accessor(this._indexes[j]) : undefined
2774
2797
  );
2775
2798
  }
2776
2799
  : this._indexes
2777
2800
  ? () => {
2778
2801
  const item = newItems[j];
2779
2802
  this._indexes[j] = signal(j, pureOptions);
2780
- return this._map(() => item, read.bind(null, this._indexes[j]));
2803
+ return this._map(() => item, accessor(this._indexes[j]));
2781
2804
  }
2782
2805
  : () => {
2783
2806
  const item = newItems[j];
@@ -2981,11 +3004,14 @@ function createBoundChildren(owner, fn, queue, mask) {
2981
3004
  return boundaryComputed(() => staleValues(() => flatten(read(c))), mask);
2982
3005
  });
2983
3006
  }
3007
+ const ON_INIT = Symbol();
2984
3008
  class CollectionQueue extends Queue {
2985
3009
  _collectionType;
2986
3010
  _sources = new Set();
2987
3011
  _disabled = signal(false, { pureWrite: true });
2988
3012
  _initialized = false;
3013
+ _onFn;
3014
+ _prevOn = ON_INIT;
2989
3015
  constructor(type) {
2990
3016
  super();
2991
3017
  this._collectionType = type;
@@ -2995,10 +3021,22 @@ class CollectionQueue extends Queue {
2995
3021
  return super.run(type);
2996
3022
  }
2997
3023
  notify(node, type, flags, error) {
2998
- if (
2999
- !(type & this._collectionType) ||
3000
- (this._collectionType & STATUS_PENDING && this._initialized)
3001
- )
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)
3002
3040
  return super.notify(node, type, flags, error);
3003
3041
  if (flags & this._collectionType) {
3004
3042
  const source = error?.source || node._error?.source;
@@ -3015,12 +3053,20 @@ class CollectionQueue extends Queue {
3015
3053
  for (const source of this._sources) {
3016
3054
  if (!(source._statusFlags & this._collectionType)) this._sources.delete(source);
3017
3055
  }
3018
- 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
+ }
3019
3064
  }
3020
3065
  }
3021
- function createCollectionBoundary(type, fn, fallback) {
3066
+ function createCollectionBoundary(type, fn, fallback, onFn) {
3022
3067
  const owner = createOwner();
3023
3068
  const queue = new CollectionQueue(type);
3069
+ if (onFn) queue._onFn = onFn;
3024
3070
  const tree = createBoundChildren(owner, fn, queue, type);
3025
3071
  const decision = computed(() => {
3026
3072
  if (!read(queue._disabled)) {
@@ -3032,10 +3078,10 @@ function createCollectionBoundary(type, fn, fallback) {
3032
3078
  }
3033
3079
  return fallback(queue);
3034
3080
  });
3035
- return read.bind(null, decision);
3081
+ return accessor(decision);
3036
3082
  }
3037
- function createLoadBoundary(fn, fallback) {
3038
- return createCollectionBoundary(STATUS_PENDING, fn, () => fallback());
3083
+ function createLoadingBoundary(fn, fallback, options) {
3084
+ return createCollectionBoundary(STATUS_PENDING, fn, () => fallback(), options?.on);
3039
3085
  }
3040
3086
  function createErrorBoundary(fn, fallback) {
3041
3087
  return createCollectionBoundary(STATUS_ERROR, fn, queue => {
@@ -3105,6 +3151,7 @@ function flattenArray(children, results = [], options) {
3105
3151
  }
3106
3152
  export {
3107
3153
  $PROXY,
3154
+ $REFRESH,
3108
3155
  $TARGET,
3109
3156
  $TRACK,
3110
3157
  ContextNotFoundError,
@@ -3116,7 +3163,7 @@ export {
3116
3163
  createContext,
3117
3164
  createEffect,
3118
3165
  createErrorBoundary,
3119
- createLoadBoundary,
3166
+ createLoadingBoundary,
3120
3167
  createMemo,
3121
3168
  createOptimistic,
3122
3169
  createOptimisticStore,
@@ -3129,6 +3176,8 @@ export {
3129
3176
  createStore,
3130
3177
  createTrackedEffect,
3131
3178
  deep,
3179
+ enableExternalSource,
3180
+ enforceLoadingBoundary,
3132
3181
  flatten,
3133
3182
  flush,
3134
3183
  getContext,
@@ -3156,7 +3205,6 @@ export {
3156
3205
  runWithOwner,
3157
3206
  setContext,
3158
3207
  setSnapshotCapture,
3159
- setStrictRead,
3160
3208
  snapshot,
3161
3209
  storePath,
3162
3210
  untrack