@solidjs/signals 0.13.4 → 0.13.6

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,7 @@ let clock = 0;
151
151
  let activeTransition = null;
152
152
  let scheduled = false;
153
153
  let projectionWriteActive = false;
154
+ let inTrackedQueueCallback = false;
154
155
  let _enforceLoadingBoundary = false;
155
156
  let _hitUnhandledAsync = false;
156
157
  let stashedOptimisticReads = null;
@@ -192,6 +193,9 @@ function queueStashedOptimisticEffects(node) {
192
193
  function setProjectionWriteActive(value) {
193
194
  projectionWriteActive = value;
194
195
  }
196
+ function setTrackedQueueCallback(value) {
197
+ inTrackedQueueCallback = value;
198
+ }
195
199
  function mergeTransitionState(target, outgoing) {
196
200
  outgoing._done = target;
197
201
  target._actions.push(...outgoing._actions);
@@ -242,20 +246,6 @@ function schedule() {
242
246
  scheduled = true;
243
247
  if (!globalQueue._running && !projectionWriteActive) queueMicrotask(flush);
244
248
  }
245
- function addTransitionBlocker(node) {
246
- if (activeTransition && !activeTransition._asyncNodes.includes(node)) {
247
- activeTransition._asyncNodes.push(node);
248
- }
249
- }
250
- function removeTransitionBlocker(node) {
251
- const remove = list => {
252
- if (!list) return;
253
- const index = list.indexOf(node);
254
- if (index >= 0) list.splice(index, 1);
255
- };
256
- remove(node._transition?._asyncNodes);
257
- remove(activeTransition?._asyncNodes);
258
- }
259
249
  class Queue {
260
250
  _parent = null;
261
251
  _queues = [[], []];
@@ -532,6 +522,14 @@ function reassignPendingTransition(pendingNodes) {
532
522
  }
533
523
  const globalQueue = new GlobalQueue();
534
524
  function flush() {
525
+ if (globalQueue._running) {
526
+ if (inTrackedQueueCallback) {
527
+ throw new Error(
528
+ "Cannot call flush() from inside onSettled or createTrackedEffect. flush() is not reentrant there."
529
+ );
530
+ }
531
+ return;
532
+ }
535
533
  let count = 0;
536
534
  while (scheduled || activeTransition) {
537
535
  if (++count === 1e5) throw new Error("Potential Infinite Loop Detected.");
@@ -552,6 +550,21 @@ function transitionComplete(transition) {
552
550
  break;
553
551
  }
554
552
  }
553
+ if (done) {
554
+ for (let i = 0; i < transition._optimisticNodes.length; i++) {
555
+ const node = transition._optimisticNodes[i];
556
+ if (
557
+ hasActiveOverride(node) &&
558
+ "_statusFlags" in node &&
559
+ node._statusFlags & STATUS_PENDING &&
560
+ node._error instanceof NotReadyError &&
561
+ node._error.source !== node
562
+ ) {
563
+ done = false;
564
+ break;
565
+ }
566
+ }
567
+ }
555
568
  done && (transition._done = true);
556
569
  return done;
557
570
  }
@@ -843,7 +856,6 @@ function handleAsync(el, result, setter) {
843
856
  }
844
857
  function clearStatus(el, clearUninitialized = false) {
845
858
  clearPendingSources(el);
846
- removeTransitionBlocker(el);
847
859
  el._blocked = false;
848
860
  el._statusFlags = clearUninitialized ? 0 : el._statusFlags & STATUS_UNINITIALIZED;
849
861
  setPendingError(el);
@@ -867,11 +879,9 @@ function notifyStatus(el, status, error, blockStatus, lane) {
867
879
  if (status === STATUS_PENDING && pendingSource) {
868
880
  addPendingSource(el, pendingSource);
869
881
  el._statusFlags = STATUS_PENDING | (el._statusFlags & STATUS_UNINITIALIZED);
870
- setPendingError(el, el._pendingSource ?? el._pendingSources?.values().next().value, error);
871
- if (pendingSource === el) addTransitionBlocker(el);
882
+ setPendingError(el, pendingSource, error);
872
883
  } else {
873
884
  clearPendingSources(el);
874
- removeTransitionBlocker(el);
875
885
  el._statusFlags =
876
886
  status | (status !== STATUS_ERROR ? el._statusFlags & STATUS_UNINITIALIZED : 0);
877
887
  el._error = error;
@@ -884,6 +894,9 @@ function notifyStatus(el, status, error, blockStatus, lane) {
884
894
  const downstreamBlockStatus = blockStatus || startsBlocking;
885
895
  const downstreamLane = blockStatus || isOptimisticBoundary ? undefined : lane;
886
896
  if (el._notifyStatus) {
897
+ if (blockStatus && status === STATUS_PENDING) {
898
+ return;
899
+ }
887
900
  if (downstreamBlockStatus) {
888
901
  el._notifyStatus(status, error);
889
902
  } else {
@@ -901,7 +914,7 @@ function notifyStatus(el, status, error, blockStatus, lane) {
901
914
  (status !== STATUS_PENDING &&
902
915
  (sub._error !== error || sub._pendingSource || sub._pendingSources))
903
916
  ) {
904
- !sub._transition && globalQueue._pendingNodes.push(sub);
917
+ if (!downstreamBlockStatus && !sub._transition) globalQueue._pendingNodes.push(sub);
905
918
  notifyStatus(sub, status, error, downstreamBlockStatus, downstreamLane);
906
919
  }
907
920
  });
@@ -1016,16 +1029,8 @@ function getObserver() {
1016
1029
  function getOwner() {
1017
1030
  return context;
1018
1031
  }
1019
- function onCleanup(fn) {
1020
- if (!context) {
1021
- console.warn("onCleanup called outside a reactive context will never be run");
1022
- return fn;
1023
- }
1024
- if (context._childrenForbidden) {
1025
- throw new Error(
1026
- "Cannot use onCleanup inside createTrackedEffect or onSettled; return a cleanup function instead"
1027
- );
1028
- }
1032
+ function cleanup(fn) {
1033
+ if (!context) return fn;
1029
1034
  if (!context._disposal) context._disposal = fn;
1030
1035
  else if (Array.isArray(context._disposal)) context._disposal.push(fn);
1031
1036
  else context._disposal = [context._disposal, fn];
@@ -1398,7 +1403,7 @@ function computed(fn, initialValue, options) {
1398
1403
  const source = externalSourceConfig.factory(self._fn, () => {
1399
1404
  setSignal(bridgeSignal, undefined);
1400
1405
  });
1401
- onCleanup(() => source.dispose());
1406
+ cleanup(() => source.dispose());
1402
1407
  self._fn = prev => {
1403
1408
  read(bridgeSignal);
1404
1409
  return source.track(prev);
@@ -1504,8 +1509,17 @@ function read(el) {
1504
1509
  const firewall = el._firewall;
1505
1510
  const prevCheck = pendingCheckActive;
1506
1511
  pendingCheckActive = false;
1507
- if (read(getPendingSignal(el))) foundPending = true;
1508
- if (firewall && read(getPendingSignal(firewall))) foundPending = true;
1512
+ if (firewall && el._overrideValue !== undefined) {
1513
+ if (
1514
+ el._overrideValue !== NOT_PENDING &&
1515
+ (firewall._inFlight || !!(firewall._statusFlags & STATUS_PENDING))
1516
+ ) {
1517
+ foundPending = true;
1518
+ }
1519
+ } else {
1520
+ if (read(getPendingSignal(el))) foundPending = true;
1521
+ if (firewall && read(getPendingSignal(firewall))) foundPending = true;
1522
+ }
1509
1523
  pendingCheckActive = prevCheck;
1510
1524
  return el._value;
1511
1525
  }
@@ -1558,6 +1572,9 @@ function read(el) {
1558
1572
  if (!tracking && el !== c) link(el, c);
1559
1573
  throw owner._error;
1560
1574
  }
1575
+ } else if (c && owner !== el && owner._statusFlags & STATUS_UNINITIALIZED) {
1576
+ if (!tracking && el !== c) link(el, c);
1577
+ throw owner._error;
1561
1578
  } else if (!c && owner._statusFlags & STATUS_UNINITIALIZED) {
1562
1579
  throw owner._error;
1563
1580
  }
@@ -1686,6 +1703,9 @@ function computePendingState(el) {
1686
1703
  }
1687
1704
  return true;
1688
1705
  }
1706
+ if (el._overrideValue !== undefined && el._overrideValue === NOT_PENDING && !el._parentSource) {
1707
+ return false;
1708
+ }
1689
1709
  if (el._pendingValue !== NOT_PENDING && !(comp._statusFlags & STATUS_UNINITIALIZED)) return true;
1690
1710
  return !!(comp._statusFlags & STATUS_PENDING && !(comp._statusFlags & STATUS_UNINITIALIZED));
1691
1711
  }
@@ -1857,7 +1877,7 @@ function effect(compute, effect, error, initialValue, options) {
1857
1877
  ? node._queue.enqueue(node._type, runEffect.bind(node))
1858
1878
  : runEffect.call(node));
1859
1879
  initialized = true;
1860
- onCleanup(() => node._cleanup?.());
1880
+ cleanup(() => node._cleanup?.());
1861
1881
  if (!node._parent)
1862
1882
  console.warn("Effects created outside a reactive context will never be disposed");
1863
1883
  }
@@ -1884,8 +1904,13 @@ function runEffect() {
1884
1904
  function trackedEffect(fn, options) {
1885
1905
  const run = () => {
1886
1906
  if (!node._modified || node._flags & REACTIVE_DISPOSED) return;
1887
- node._modified = false;
1888
- recompute(node);
1907
+ setTrackedQueueCallback(true);
1908
+ try {
1909
+ node._modified = false;
1910
+ recompute(node);
1911
+ } finally {
1912
+ setTrackedQueueCallback(false);
1913
+ }
1889
1914
  };
1890
1915
  const node = computed(
1891
1916
  () => {
@@ -1910,7 +1935,7 @@ function trackedEffect(fn, options) {
1910
1935
  };
1911
1936
  node._run = run;
1912
1937
  node._queue.enqueue(EFFECT_USER, run);
1913
- onCleanup(() => node._cleanup?.());
1938
+ cleanup(() => node._cleanup?.());
1914
1939
  if (!node._parent)
1915
1940
  console.warn("Effects created outside a reactive context will never be disposed");
1916
1941
  }
@@ -1958,6 +1983,17 @@ function action(genFn) {
1958
1983
  step();
1959
1984
  });
1960
1985
  }
1986
+ function onCleanup(fn) {
1987
+ {
1988
+ const owner = getOwner();
1989
+ if (!owner) console.warn("onCleanup called outside a reactive context will never be run");
1990
+ else if (owner._childrenForbidden)
1991
+ throw new Error(
1992
+ "Cannot use onCleanup inside createTrackedEffect or onSettled; return a cleanup function instead"
1993
+ );
1994
+ }
1995
+ return cleanup(fn);
1996
+ }
1961
1997
  function accessor(node) {
1962
1998
  const fn = read.bind(null, node);
1963
1999
  fn.$r = true;
@@ -1991,16 +2027,16 @@ function createTrackedEffect(compute, options) {
1991
2027
  trackedEffect(compute, { ...options, name: options?.name ?? "trackedEffect" });
1992
2028
  }
1993
2029
  function createReaction(effectFn, options) {
1994
- let cleanup = undefined;
1995
- onCleanup(() => cleanup?.());
2030
+ let cl = undefined;
2031
+ cleanup(() => cl?.());
1996
2032
  const owner = getOwner();
1997
2033
  return tracking => {
1998
2034
  runWithOwner(owner, () => {
1999
2035
  effect(
2000
2036
  () => (tracking(), getOwner()),
2001
2037
  node => {
2002
- cleanup?.();
2003
- cleanup = (effectFn.effect || effectFn)?.();
2038
+ cl?.();
2039
+ cl = (effectFn.effect || effectFn)?.();
2004
2040
  dispose(node);
2005
2041
  },
2006
2042
  effectFn.error,
@@ -2214,8 +2250,16 @@ function createProjectionInternal(fn, initialValue = {}, options) {
2214
2250
  const wrappedStore = wrapProjection(initialValue);
2215
2251
  node = computed(() => {
2216
2252
  const owner = getOwner();
2217
- storeSetter(new Proxy(wrappedStore, writeTraps), s => {
2218
- const value = handleAsync(owner, fn(s), value => {
2253
+ let settled = false;
2254
+ let result;
2255
+ const draft = new Proxy(
2256
+ wrappedStore,
2257
+ createWriteTraps(() => !settled || owner._inFlight === result)
2258
+ );
2259
+ storeSetter(draft, s => {
2260
+ result = fn(s);
2261
+ settled = true;
2262
+ const value = handleAsync(owner, result, value => {
2219
2263
  value !== s &&
2220
2264
  value !== undefined &&
2221
2265
  storeSetter(wrappedStore, reconcile(value, options?.key || "id"));
@@ -2229,42 +2273,47 @@ function createProjectionInternal(fn, initialValue = {}, options) {
2229
2273
  function createProjection(fn, initialValue = {}, options) {
2230
2274
  return createProjectionInternal(fn, initialValue, options).store;
2231
2275
  }
2232
- const writeTraps = {
2233
- get(_, prop) {
2234
- let value;
2235
- setWriteOverride(true);
2236
- setProjectionWriteActive(true);
2237
- try {
2238
- value = _[prop];
2239
- } finally {
2240
- setWriteOverride(false);
2241
- setProjectionWriteActive(false);
2242
- }
2243
- return typeof value === "object" && value !== null ? new Proxy(value, writeTraps) : value;
2244
- },
2245
- set(_, prop, value) {
2246
- setWriteOverride(true);
2247
- setProjectionWriteActive(true);
2248
- try {
2249
- _[prop] = value;
2250
- } finally {
2251
- setWriteOverride(false);
2252
- setProjectionWriteActive(false);
2253
- }
2254
- return true;
2255
- },
2256
- deleteProperty(_, prop) {
2257
- setWriteOverride(true);
2258
- setProjectionWriteActive(true);
2259
- try {
2260
- delete _[prop];
2261
- } finally {
2262
- setWriteOverride(false);
2263
- setProjectionWriteActive(false);
2276
+ function createWriteTraps(isActive) {
2277
+ const traps = {
2278
+ get(_, prop) {
2279
+ let value;
2280
+ setWriteOverride(true);
2281
+ setProjectionWriteActive(true);
2282
+ try {
2283
+ value = _[prop];
2284
+ } finally {
2285
+ setWriteOverride(false);
2286
+ setProjectionWriteActive(false);
2287
+ }
2288
+ return typeof value === "object" && value !== null ? new Proxy(value, traps) : value;
2289
+ },
2290
+ set(_, prop, value) {
2291
+ if (isActive && !isActive()) return true;
2292
+ setWriteOverride(true);
2293
+ setProjectionWriteActive(true);
2294
+ try {
2295
+ _[prop] = value;
2296
+ } finally {
2297
+ setWriteOverride(false);
2298
+ setProjectionWriteActive(false);
2299
+ }
2300
+ return true;
2301
+ },
2302
+ deleteProperty(_, prop) {
2303
+ if (isActive && !isActive()) return true;
2304
+ setWriteOverride(true);
2305
+ setProjectionWriteActive(true);
2306
+ try {
2307
+ delete _[prop];
2308
+ } finally {
2309
+ setWriteOverride(false);
2310
+ setProjectionWriteActive(false);
2311
+ }
2312
+ return true;
2264
2313
  }
2265
- return true;
2266
- }
2267
- };
2314
+ };
2315
+ return traps;
2316
+ }
2268
2317
  const $TRACK = Symbol("STORE_TRACK"),
2269
2318
  $TARGET = Symbol("STORE_TARGET"),
2270
2319
  $PROXY = Symbol("STORE_PROXY"),
@@ -2674,10 +2723,18 @@ function createOptimisticProjectionInternal(fn, initialValue = {}, options) {
2674
2723
  if (fn) {
2675
2724
  node = computed(() => {
2676
2725
  const owner = getOwner();
2726
+ let settled = false;
2727
+ let result;
2728
+ const draft = new Proxy(
2729
+ wrappedStore,
2730
+ createWriteTraps(() => !settled || owner._inFlight === result)
2731
+ );
2677
2732
  setProjectionWriteActive(true);
2678
2733
  try {
2679
- storeSetter(new Proxy(wrappedStore, writeTraps), s => {
2680
- const value = handleAsync(owner, fn(s), value => {
2734
+ storeSetter(draft, s => {
2735
+ result = fn(s);
2736
+ settled = true;
2737
+ const value = handleAsync(owner, result, value => {
2681
2738
  setProjectionWriteActive(true);
2682
2739
  try {
2683
2740
  value !== s &&
@@ -3202,7 +3259,7 @@ function boundaryComputed(fn, propagationMask) {
3202
3259
  function createBoundChildren(owner, fn, queue, mask) {
3203
3260
  const parentQueue = owner._queue;
3204
3261
  parentQueue.addChild((owner._queue = queue));
3205
- onCleanup(() => parentQueue.removeChild(owner._queue));
3262
+ cleanup(() => parentQueue.removeChild(owner._queue));
3206
3263
  return runWithOwner(owner, () => {
3207
3264
  const c = computed(fn);
3208
3265
  return boundaryComputed(() => staleValues(() => flatten(read(c))), mask);