@legendapp/state 2.2.0-next.0 → 2.2.0-next.3

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/index.d.ts CHANGED
@@ -24,12 +24,7 @@ export declare const internal: {
24
24
  isLoadingLocal: boolean;
25
25
  isMerging: boolean;
26
26
  isLoadingRemote$: import("./src/observableInterfaces").ObservablePrimitiveBaseFns<boolean> & import("./src/observableInterfaces").ObservablePrimitiveBooleanFns<boolean>;
27
- onChangeRemote: (cb: () => void) => void;
28
- activateNode: (node: import("./src/observableInterfaces").NodeValue, newValue: any, setter: (value: any) => void, subscriber: (params: {
29
- update: any;
30
- }) => void, cacheOptions: import("./src/observableInterfaces").CacheOptions<any>) => {
31
- update: any;
32
- };
27
+ activateNode: (node: import("./src/observableInterfaces").NodeValue, refresh: () => void, newValue: any) => void;
33
28
  };
34
29
  observableFns: Map<string, (node: import("./src/observableInterfaces").NodeValue, ...args: any[]) => any>;
35
30
  optimized: symbol;
package/index.js CHANGED
@@ -62,7 +62,6 @@ const globalState = {
62
62
  isLoadingLocal: false,
63
63
  isMerging: false,
64
64
  isLoadingRemote$: undefined,
65
- onChangeRemote: undefined,
66
65
  activateNode: undefined,
67
66
  };
68
67
  function isObservable(obs) {
@@ -1472,7 +1471,9 @@ function extractPromise(node, value) {
1472
1471
  }
1473
1472
  function extractFunctionOrComputed(node, obj, k, v) {
1474
1473
  if (isPromise(v)) {
1475
- extractPromise(getChildNode(node, k), v);
1474
+ const childNode = getChildNode(node, k);
1475
+ extractPromise(childNode, v);
1476
+ setNodeValue(childNode, undefined);
1476
1477
  }
1477
1478
  else if (typeof v === 'function') {
1478
1479
  extractFunction(node, k, v);
@@ -1516,26 +1517,36 @@ function peek(node) {
1516
1517
  checkActivate(node);
1517
1518
  return value;
1518
1519
  }
1519
- function activateNodeFunction(node, lazyFn) {
1520
- let setter;
1521
- let update;
1522
- let subscriber;
1523
- let cacheOptions;
1520
+ function createNodeActivationParams(node) {
1521
+ node.activationState = {
1522
+ lastSync: {},
1523
+ };
1524
+ const state = node.activationState;
1525
+ // The onSet function handles the observable being set
1526
+ // and forwards the set elsewhere
1527
+ const onSet = (onSetFnParam) => {
1528
+ state.onSetFn = onSetFnParam;
1529
+ };
1524
1530
  // The onSet function handles the observable being set
1525
1531
  // and forwards the set elsewhere
1526
- const onSet = (setterParam) => {
1527
- setter = setterParam;
1532
+ const updateLastSync = (fn) => {
1533
+ state.lastSync.value = fn;
1528
1534
  };
1529
1535
  // The subscribe function runs a function that listens to
1530
1536
  // a data source and sends updates into the observable
1531
1537
  const subscribe = (fn) => {
1532
- if (!subscriber) {
1533
- subscriber = fn;
1538
+ if (!state.subscriber) {
1539
+ state.subscriber = fn;
1534
1540
  }
1535
1541
  };
1536
1542
  const cache = (fn) => {
1537
- if (!cacheOptions) {
1538
- cacheOptions = isFunction(fn) ? fn() : fn;
1543
+ if (!state.cacheOptions) {
1544
+ state.cacheOptions = isFunction(fn) ? fn() : fn;
1545
+ }
1546
+ };
1547
+ const retry = (params) => {
1548
+ if (!state.retryOptions) {
1549
+ state.retryOptions = params;
1539
1550
  }
1540
1551
  };
1541
1552
  // The proxy function simply marks the node as a proxy with this function
@@ -1544,20 +1555,31 @@ function activateNodeFunction(node, lazyFn) {
1544
1555
  const proxy = (fn) => {
1545
1556
  node.proxyFn2 = fn;
1546
1557
  };
1558
+ return {
1559
+ onSet,
1560
+ proxy,
1561
+ cache,
1562
+ retry,
1563
+ subscribe,
1564
+ updateLastSync,
1565
+ };
1566
+ }
1567
+ function activateNodeFunction(node, lazyFn) {
1547
1568
  let prevTarget$;
1548
1569
  let curTarget$;
1549
1570
  const activator = (isFunction(node) ? node : lazyFn);
1550
1571
  let wasPromise;
1551
- let isInitial = true;
1572
+ const refresh = () => node.state.refreshNum.set((v) => v + 1);
1552
1573
  observe(() => {
1574
+ const params = createNodeActivationParams(node);
1553
1575
  // Run the function at this node
1554
- let value = activator({ onSet, subscribe, proxy, cache });
1576
+ let value = activator(params);
1555
1577
  // If target is an observable, get() it to make sure we listen to its changes
1556
1578
  // and set up an onSet to write changes back to it
1557
1579
  if (isObservable(value)) {
1558
1580
  prevTarget$ = curTarget$;
1559
1581
  curTarget$ = value;
1560
- onSet(({ value: newValue, getPrevious }) => {
1582
+ params.onSet(({ value: newValue, getPrevious }) => {
1561
1583
  // Don't set the target observable if the target has changed since the last run
1562
1584
  if (!prevTarget$ || curTarget$ === prevTarget$) {
1563
1585
  // Set the node value back to what it was before before setting it.
@@ -1574,25 +1596,28 @@ function activateNodeFunction(node, lazyFn) {
1574
1596
  // for the effect.
1575
1597
  value = value.get();
1576
1598
  }
1577
- wasPromise = isPromise(value) ? value : undefined;
1599
+ else {
1600
+ wasPromise = isPromise(value) ? value : undefined;
1601
+ }
1602
+ // Activate this node if not activated already (may be called recursively)
1603
+ // TODO: Is calling recursively bad? If so can it be fixed?
1578
1604
  if (!node.activated) {
1579
1605
  node.activated = true;
1580
1606
  const activateNodeFn = wasPromise ? globalState.activateNode : activateNodeBase;
1581
- update = activateNodeFn(node, value, setter, subscriber, cacheOptions).update;
1582
- }
1583
- if (wasPromise) {
1584
- value = undefined;
1607
+ activateNodeFn(node, refresh, value);
1585
1608
  }
1609
+ node.state.refreshNum.get();
1586
1610
  return value;
1587
1611
  }, ({ value }) => {
1588
1612
  if (!globalState.isLoadingRemote$.peek()) {
1589
1613
  if (wasPromise) {
1590
- wasPromise.then((newValue) => {
1591
- update({ value: newValue });
1592
- node.state.isLoaded.set(true);
1593
- });
1594
- if (isInitial && isFunction(getNodeValue(node))) {
1595
- set(node, value);
1614
+ // Extract the promise to make it set the value/error when it comes in
1615
+ extractPromise(node, value);
1616
+ // Set this to undefined only if it's replacing the activation function,
1617
+ // so we don't overwrite it if it already has real data from either local
1618
+ // cache or a previous run
1619
+ if (isFunction(getNodeValue(node))) {
1620
+ setNodeValue(node, undefined);
1596
1621
  }
1597
1622
  }
1598
1623
  else {
@@ -1600,24 +1625,23 @@ function activateNodeFunction(node, lazyFn) {
1600
1625
  node.state.isLoaded.set(true);
1601
1626
  }
1602
1627
  }
1603
- isInitial = false;
1604
1628
  }, { immediate: true, fromComputed: true });
1605
1629
  }
1606
- const activateNodeBase = (globalState.activateNode = function activateNodeBase(node, newValue, setter, subscriber, cacheOptions) {
1630
+ const activateNodeBase = (globalState.activateNode = function activateNodeBase(node, refresh) {
1631
+ const { onSetFn, subscriber } = node.activationState;
1607
1632
  let isSetting = false;
1608
1633
  if (!node.state) {
1609
1634
  node.state = createObservable({
1610
1635
  isLoaded: false,
1611
1636
  }, false, extractPromise, getProxy);
1612
1637
  }
1613
- if (setter) {
1638
+ if (onSetFn) {
1614
1639
  const doSet = (params) => {
1615
1640
  // Don't call the set if this is the first value coming in
1616
1641
  if (!isSetting) {
1617
1642
  if (params.changes.length > 1 || !isFunction(params.changes[0].prevAtPath)) {
1618
1643
  isSetting = true;
1619
- params.isRemote = globalState.isLoadingRemote$.peek();
1620
- batch(() => setter(params), () => {
1644
+ batch(() => onSetFn(params), () => {
1621
1645
  isSetting = false;
1622
1646
  });
1623
1647
  }
@@ -1625,25 +1649,24 @@ const activateNodeBase = (globalState.activateNode = function activateNodeBase(n
1625
1649
  };
1626
1650
  onChange(node, doSet, { immediate: true });
1627
1651
  }
1628
- if (process.env.NODE_ENV === 'development' && cacheOptions) {
1652
+ if (process.env.NODE_ENV === 'development' && node.activationState.cacheOptions) {
1653
+ // TODO Better message
1654
+ console.log('[legend-state] Using cacheOptions without setting up persistence first');
1655
+ }
1656
+ if (process.env.NODE_ENV === 'development' && node.activationState.retryOptions) {
1629
1657
  // TODO Better message
1630
- console.log('[legend-state] Using cacheOption without setting up persistence first');
1658
+ console.log('[legend-state] Using retryOptions without setting up persistence first');
1631
1659
  }
1632
1660
  const update = ({ value }) => {
1661
+ // TODO: This isSetting might not be necessary? Tests still work if removing it.
1662
+ // Write tests that would break it if removed? I'd guess a combination of subscribe and
1633
1663
  if (!isSetting) {
1634
- globalState.onChangeRemote(() => {
1635
- set(node, value);
1636
- });
1664
+ set(node, value);
1637
1665
  }
1638
1666
  };
1639
- const isProm = isPromise(newValue);
1640
- if (isProm) {
1641
- extractPromise(node, newValue);
1642
- }
1643
1667
  if (subscriber) {
1644
- subscriber({ update });
1668
+ subscriber({ update, refresh });
1645
1669
  }
1646
- return { update };
1647
1670
  });
1648
1671
 
1649
1672
  const fns = ['get', 'set', 'peek', 'onChange', 'toggle'];
@@ -1687,60 +1710,6 @@ ObservablePrimitiveClass.prototype.delete = function () {
1687
1710
  return this;
1688
1711
  };
1689
1712
 
1690
- function _when(predicate, effect, checkReady) {
1691
- // If predicate is a regular Promise skip all the observable stuff
1692
- if (isPromise(predicate)) {
1693
- return effect ? predicate.then(effect) : predicate;
1694
- }
1695
- let value;
1696
- // Create a wrapping fn that calls the effect if predicate returns true
1697
- function run(e) {
1698
- const ret = computeSelector(predicate);
1699
- if (!isPromise(ret) && (checkReady ? isObservableValueReady(ret) : ret)) {
1700
- value = ret;
1701
- // Set cancel so that observe does not track anymore
1702
- e.cancel = true;
1703
- }
1704
- return value;
1705
- }
1706
- function doEffect() {
1707
- // If value is truthy then run the effect
1708
- effect === null || effect === void 0 ? void 0 : effect(value);
1709
- }
1710
- // Run in an observe
1711
- observe(run, doEffect);
1712
- // If first run resulted in a truthy value just return it.
1713
- // It will have set e.cancel so no need to dispose
1714
- if (isPromise(value)) {
1715
- return effect ? value.then(effect) : value;
1716
- }
1717
- else if (value !== undefined) {
1718
- return Promise.resolve(value);
1719
- }
1720
- else {
1721
- // Wrap it in a promise
1722
- const promise = new Promise((resolve) => {
1723
- if (effect) {
1724
- const originalEffect = effect;
1725
- effect = (value) => {
1726
- const effectValue = originalEffect(value);
1727
- resolve(effectValue);
1728
- };
1729
- }
1730
- else {
1731
- effect = resolve;
1732
- }
1733
- });
1734
- return promise;
1735
- }
1736
- }
1737
- function when(predicate, effect) {
1738
- return _when(predicate, effect, false);
1739
- }
1740
- function whenReady(predicate, effect) {
1741
- return _when(predicate, effect, true);
1742
- }
1743
-
1744
1713
  function observable(value) {
1745
1714
  return createObservable(value, false, extractPromise, getProxy, ObservablePrimitiveClass);
1746
1715
  }
@@ -1748,15 +1717,6 @@ function observablePrimitive(value) {
1748
1717
  return createObservable(value, true, extractPromise, getProxy, ObservablePrimitiveClass);
1749
1718
  }
1750
1719
  globalState.isLoadingRemote$ = observable(false);
1751
- globalState.onChangeRemote = function onChangeRemote(cb) {
1752
- when(() => !globalState.isLoadingRemote$.get(), () => {
1753
- // Remote changes should only update local state
1754
- globalState.isLoadingRemote$.set(true);
1755
- batch(cb, () => {
1756
- globalState.isLoadingRemote$.set(false);
1757
- });
1758
- });
1759
- };
1760
1720
 
1761
1721
  function computed(compute, set$1) {
1762
1722
  // Create an observable for this computed variable
@@ -1920,6 +1880,60 @@ function proxy(get, set) {
1920
1880
  return obs;
1921
1881
  }
1922
1882
 
1883
+ function _when(predicate, effect, checkReady) {
1884
+ // If predicate is a regular Promise skip all the observable stuff
1885
+ if (isPromise(predicate)) {
1886
+ return effect ? predicate.then(effect) : predicate;
1887
+ }
1888
+ let value;
1889
+ // Create a wrapping fn that calls the effect if predicate returns true
1890
+ function run(e) {
1891
+ const ret = computeSelector(predicate);
1892
+ if (!isPromise(ret) && (checkReady ? isObservableValueReady(ret) : ret)) {
1893
+ value = ret;
1894
+ // Set cancel so that observe does not track anymore
1895
+ e.cancel = true;
1896
+ }
1897
+ return value;
1898
+ }
1899
+ function doEffect() {
1900
+ // If value is truthy then run the effect
1901
+ effect === null || effect === void 0 ? void 0 : effect(value);
1902
+ }
1903
+ // Run in an observe
1904
+ observe(run, doEffect);
1905
+ // If first run resulted in a truthy value just return it.
1906
+ // It will have set e.cancel so no need to dispose
1907
+ if (isPromise(value)) {
1908
+ return effect ? value.then(effect) : value;
1909
+ }
1910
+ else if (value !== undefined) {
1911
+ return Promise.resolve(value);
1912
+ }
1913
+ else {
1914
+ // Wrap it in a promise
1915
+ const promise = new Promise((resolve) => {
1916
+ if (effect) {
1917
+ const originalEffect = effect;
1918
+ effect = (value) => {
1919
+ const effectValue = originalEffect(value);
1920
+ resolve(effectValue);
1921
+ };
1922
+ }
1923
+ else {
1924
+ effect = resolve;
1925
+ }
1926
+ });
1927
+ return promise;
1928
+ }
1929
+ }
1930
+ function when(predicate, effect) {
1931
+ return _when(predicate, effect, false);
1932
+ }
1933
+ function whenReady(predicate, effect) {
1934
+ return _when(predicate, effect, true);
1935
+ }
1936
+
1923
1937
  const internal = {
1924
1938
  ensureNodeValue,
1925
1939
  findIDKey,