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

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.
Files changed (108) hide show
  1. package/babel.js.map +1 -1
  2. package/config/enableDirectAccess.d.ts +1 -1
  3. package/config/enableDirectPeek.d.ts +1 -1
  4. package/config/enableReactDirectRender.js.map +1 -1
  5. package/config/enableReactDirectRender.mjs.map +1 -1
  6. package/config/enableReactTracking.d.ts +4 -3
  7. package/config/enableReactTracking.js.map +1 -1
  8. package/config/enableReactTracking.mjs.map +1 -1
  9. package/config/enableReactUse.d.ts +1 -1
  10. package/helpers/fetch.d.ts +4 -3
  11. package/helpers/fetch.js.map +1 -1
  12. package/helpers/fetch.mjs.map +1 -1
  13. package/helpers/pageHash.js.map +1 -1
  14. package/helpers/pageHash.mjs.map +1 -1
  15. package/helpers/pageHashParams.js.map +1 -1
  16. package/helpers/pageHashParams.mjs.map +1 -1
  17. package/helpers/time.d.ts +2 -2
  18. package/helpers/time.js.map +1 -1
  19. package/helpers/time.mjs.map +1 -1
  20. package/history.js.map +1 -1
  21. package/history.mjs.map +1 -1
  22. package/index.d.ts +15 -4
  23. package/index.js +534 -242
  24. package/index.js.map +1 -1
  25. package/index.mjs +533 -243
  26. package/index.mjs.map +1 -1
  27. package/package.json +2 -10
  28. package/persist-plugins/async-storage.js.map +1 -1
  29. package/persist-plugins/async-storage.mjs.map +1 -1
  30. package/persist-plugins/fetch.js.map +1 -1
  31. package/persist-plugins/fetch.mjs.map +1 -1
  32. package/persist-plugins/firebase.js.map +1 -1
  33. package/persist-plugins/firebase.mjs.map +1 -1
  34. package/persist-plugins/indexeddb.js.map +1 -1
  35. package/persist-plugins/indexeddb.mjs.map +1 -1
  36. package/persist-plugins/local-storage.js +10 -2
  37. package/persist-plugins/local-storage.js.map +1 -1
  38. package/persist-plugins/local-storage.mjs +10 -2
  39. package/persist-plugins/local-storage.mjs.map +1 -1
  40. package/persist-plugins/mmkv.js.map +1 -1
  41. package/persist-plugins/mmkv.mjs.map +1 -1
  42. package/persist-plugins/query.js.map +1 -1
  43. package/persist-plugins/query.mjs.map +1 -1
  44. package/persist.d.ts +15 -1
  45. package/persist.js +365 -116
  46. package/persist.js.map +1 -1
  47. package/persist.mjs +366 -117
  48. package/persist.mjs.map +1 -1
  49. package/react-hooks/createObservableHook.js +1 -1
  50. package/react-hooks/createObservableHook.js.map +1 -1
  51. package/react-hooks/createObservableHook.mjs +1 -1
  52. package/react-hooks/createObservableHook.mjs.map +1 -1
  53. package/react-hooks/useFetch.d.ts +4 -3
  54. package/react-hooks/useFetch.js.map +1 -1
  55. package/react-hooks/useFetch.mjs.map +1 -1
  56. package/react-hooks/useHover.js.map +1 -1
  57. package/react-hooks/useHover.mjs.map +1 -1
  58. package/react-hooks/useMeasure.js.map +1 -1
  59. package/react-hooks/useMeasure.mjs.map +1 -1
  60. package/react-hooks/useObservableNextRouter.js.map +1 -1
  61. package/react-hooks/useObservableNextRouter.mjs.map +1 -1
  62. package/react-hooks/useObservableQuery.js.map +1 -1
  63. package/react-hooks/useObservableQuery.mjs.map +1 -1
  64. package/react-hooks/usePersistedObservable.d.ts +2 -2
  65. package/react-hooks/usePersistedObservable.js +3 -3
  66. package/react-hooks/usePersistedObservable.js.map +1 -1
  67. package/react-hooks/usePersistedObservable.mjs +3 -3
  68. package/react-hooks/usePersistedObservable.mjs.map +1 -1
  69. package/react.js +13 -8
  70. package/react.js.map +1 -1
  71. package/react.mjs +14 -9
  72. package/react.mjs.map +1 -1
  73. package/src/ObservableObject.d.ts +8 -4
  74. package/src/ObservablePrimitive.d.ts +2 -1
  75. package/src/activated.d.ts +3 -0
  76. package/src/batching.d.ts +3 -1
  77. package/src/computed.d.ts +1 -1
  78. package/src/config/enableDirectAccess.d.ts +1 -1
  79. package/src/config/enableDirectPeek.d.ts +1 -1
  80. package/src/config/enableReactTracking.d.ts +4 -3
  81. package/src/config/enableReactUse.d.ts +1 -1
  82. package/src/createObservable.d.ts +2 -2
  83. package/src/globals.d.ts +11 -4
  84. package/src/helpers/fetch.d.ts +4 -3
  85. package/src/helpers/time.d.ts +2 -2
  86. package/src/helpers.d.ts +3 -2
  87. package/src/history/trackHistory.d.ts +1 -1
  88. package/src/observable.d.ts +6 -15
  89. package/src/observableInterfaces.d.ts +60 -331
  90. package/src/observableTypes.d.ts +93 -0
  91. package/src/persist/observablePersistRemoteFunctionsAdapter.d.ts +1 -1
  92. package/src/persist/persistActivateNode.d.ts +1 -0
  93. package/src/persist/persistHelpers.d.ts +1 -1
  94. package/src/persist/persistObservable.d.ts +3 -12
  95. package/src/persistTypes.d.ts +229 -0
  96. package/src/proxy.d.ts +2 -1
  97. package/src/react/Computed.d.ts +1 -1
  98. package/src/react/Switch.d.ts +3 -3
  99. package/src/react/reactInterfaces.d.ts +2 -1
  100. package/src/react/usePauseProvider.d.ts +3 -3
  101. package/src/react/useWhen.d.ts +2 -2
  102. package/src/react-hooks/useFetch.d.ts +4 -3
  103. package/src/react-hooks/usePersistedObservable.d.ts +2 -2
  104. package/src/retry.d.ts +6 -0
  105. package/src/trackSelector.d.ts +3 -2
  106. package/src/when.d.ts +6 -2
  107. package/trace.js.map +1 -1
  108. package/trace.mjs.map +1 -1
package/index.mjs CHANGED
@@ -6,7 +6,7 @@ function isString(obj) {
6
6
  return typeof obj === 'string';
7
7
  }
8
8
  function isObject(obj) {
9
- return !!obj && typeof obj === 'object' && !isArray(obj);
9
+ return !!obj && typeof obj === 'object' && !(obj instanceof Date) && !isArray(obj);
10
10
  }
11
11
  function isFunction(obj) {
12
12
  return typeof obj === 'function';
@@ -53,6 +53,7 @@ const symbolGetNode = Symbol('getNode');
53
53
  const symbolDelete = /* @__PURE__ */ Symbol('delete');
54
54
  const symbolOpaque = Symbol('opaque');
55
55
  const optimized = Symbol('optimized');
56
+ const symbolActivated = Symbol('activated');
56
57
  // TODOV3 Remove these
57
58
  const extraPrimitiveActivators = new Map();
58
59
  const extraPrimitiveProps = new Map();
@@ -61,6 +62,8 @@ const globalState = {
61
62
  isMerging: false,
62
63
  isLoadingRemote$: undefined,
63
64
  activateNode: undefined,
65
+ pendingNodes: new Map(),
66
+ dirtyNodes: new Set(),
64
67
  };
65
68
  function isObservable(obs) {
66
69
  return !!obs && !!obs[symbolGetNode];
@@ -156,8 +159,16 @@ function getChildNode(node, key, asFunction) {
156
159
  if (asFunction) {
157
160
  child = Object.assign(cloneFunction(asFunction), child);
158
161
  }
159
- else if (node.proxyFn2) {
160
- child = Object.assign(node.proxyFn2.bind(node, key), child);
162
+ else {
163
+ if (node.activationState) {
164
+ const { lookup } = node.activationState;
165
+ if (lookup) {
166
+ child = Object.assign(lookup.bind(node, key), child);
167
+ if (isFunction(child)) {
168
+ extractFunction(node, key, child);
169
+ }
170
+ }
171
+ }
161
172
  }
162
173
  if (!node.children) {
163
174
  node.children = new Map();
@@ -202,9 +213,11 @@ function findIDKey(obj, node) {
202
213
  return idKey;
203
214
  }
204
215
  function extractFunction(node, key, fnOrComputed, computedChildNode) {
216
+ var _a;
205
217
  if (!node.functions) {
206
218
  node.functions = new Map();
207
219
  }
220
+ (_a = node.children) === null || _a === void 0 ? void 0 : _a.delete(key);
208
221
  node.functions.set(key, fnOrComputed);
209
222
  if (computedChildNode) {
210
223
  computedChildNode.parentOther = getChildNode(node, key);
@@ -215,6 +228,12 @@ function extractFunction(node, key, fnOrComputed, computedChildNode) {
215
228
  }
216
229
  }
217
230
 
231
+ function activated(params) {
232
+ return (() => ({
233
+ [symbolActivated]: params,
234
+ }));
235
+ }
236
+
218
237
  let timeout;
219
238
  let numInBatch = 0;
220
239
  let isRunningBatch = false;
@@ -239,23 +258,28 @@ function isArraySubset(mainArr, subsetArr) {
239
258
  return true;
240
259
  }
241
260
  function createPreviousHandlerInner(value, changes) {
242
- // Clones the current state and inject the previous data at the changed path
243
- let clone = value ? JSON.parse(JSON.stringify(value)) : {};
244
- for (let i = 0; i < changes.length; i++) {
245
- const { path, prevAtPath } = changes[i];
246
- let o = clone;
247
- if (path.length > 0) {
248
- let i;
249
- for (i = 0; i < path.length - 1; i++) {
250
- o = o[path[i]];
261
+ try {
262
+ // Clones the current state and inject the previous data at the changed path
263
+ let clone = value ? JSON.parse(JSON.stringify(value)) : {};
264
+ for (let i = 0; i < changes.length; i++) {
265
+ const { path, prevAtPath } = changes[i];
266
+ let o = clone;
267
+ if (path.length > 0) {
268
+ let i;
269
+ for (i = 0; i < path.length - 1; i++) {
270
+ o = o[path[i]];
271
+ }
272
+ o[path[i]] = prevAtPath;
273
+ }
274
+ else {
275
+ clone = prevAtPath;
251
276
  }
252
- o[path[i]] = prevAtPath;
253
- }
254
- else {
255
- clone = prevAtPath;
256
277
  }
278
+ return clone;
279
+ }
280
+ catch (_a) {
281
+ return undefined;
257
282
  }
258
- return clone;
259
283
  }
260
284
  function createPreviousHandler(value, changes) {
261
285
  // Create a function that generates the previous state
@@ -364,6 +388,13 @@ function batchNotifyChanges(changesInBatch, immediate) {
364
388
  });
365
389
  }
366
390
  function runBatch() {
391
+ const dirtyNodes = Array.from(globalState.dirtyNodes);
392
+ globalState.dirtyNodes.clear();
393
+ dirtyNodes.forEach((node) => {
394
+ var _a;
395
+ (_a = node.dirtyFn) === null || _a === void 0 ? void 0 : _a.call(node);
396
+ node.dirtyFn = undefined;
397
+ });
367
398
  // Save batch locally and reset _batchMap first because a new batch could begin while looping over callbacks.
368
399
  // This can happen with observableComputed for example.
369
400
  const map = _batchMap;
@@ -480,7 +511,7 @@ function isEvent(obs) {
480
511
  }
481
512
  function computeSelector(selector, e, retainObservable) {
482
513
  let c = selector;
483
- if (isFunction(c)) {
514
+ if (!isObservable(c) && isFunction(c)) {
484
515
  c = e ? c(e) : c();
485
516
  }
486
517
  return isObservable(c) && !retainObservable ? c.get() : c;
@@ -579,14 +610,15 @@ function _mergeIntoObservable(target, source) {
579
610
  const targetValue = needsSet ? target.peek() : target;
580
611
  const isTargetArr = isArray(targetValue);
581
612
  const isTargetObj = !isTargetArr && isObject(targetValue);
582
- if ((isTargetObj && isObject(source) && !isEmpty(targetValue)) ||
583
- (isTargetArr && isArray(source) && targetValue.length > 0)) {
613
+ if ((isTargetObj && isObject(source) && !isEmpty(targetValue)) || (isTargetArr && targetValue.length > 0)) {
584
614
  const keys = Object.keys(source);
585
615
  for (let i = 0; i < keys.length; i++) {
586
616
  const key = keys[i];
587
617
  const sourceValue = source[key];
588
618
  if (sourceValue === symbolDelete) {
589
- needsSet && ((_a = target[key]) === null || _a === void 0 ? void 0 : _a.delete) ? target[key].delete() : delete target[key];
619
+ needsSet && ((_a = target[key]) === null || _a === void 0 ? void 0 : _a.delete)
620
+ ? target[key].delete()
621
+ : delete target[key];
590
622
  }
591
623
  else {
592
624
  const isObj = isObject(sourceValue);
@@ -775,7 +807,7 @@ function trackSelector(selector, update, observeEvent, observeOptions, createRes
775
807
  dispose = setupTracking(nodes, updateFn, false, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.immediate);
776
808
  resubscribe = createResubscribe ? () => setupTracking(nodes, updateFn) : undefined;
777
809
  }
778
- return { value, dispose, resubscribe };
810
+ return { value, nodes, dispose, resubscribe };
779
811
  }
780
812
 
781
813
  function observe(selectorOrRun, reactionOrOptions, options) {
@@ -800,9 +832,11 @@ function observe(selectorOrRun, reactionOrOptions, options) {
800
832
  delete e.value;
801
833
  // Dispose listeners from previous run
802
834
  dispose === null || dispose === void 0 ? void 0 : dispose();
803
- const { dispose: _dispose, value } = trackSelector(selectorOrRun, update, e, options);
835
+ const { dispose: _dispose, value, nodes } = trackSelector(selectorOrRun, update, e, options);
804
836
  dispose = _dispose;
805
837
  e.value = value;
838
+ e.nodes = nodes;
839
+ e.refresh = update;
806
840
  if (e.onCleanupReaction) {
807
841
  e.onCleanupReaction();
808
842
  e.onCleanupReaction = undefined;
@@ -810,7 +844,9 @@ function observe(selectorOrRun, reactionOrOptions, options) {
810
844
  endBatch();
811
845
  // Call the reaction if there is one and the value changed
812
846
  if (reaction &&
813
- ((options === null || options === void 0 ? void 0 : options.fromComputed) || ((e.num > 0 || !isEvent(selectorOrRun)) && e.previous !== e.value))) {
847
+ ((options === null || options === void 0 ? void 0 : options.fromComputed) ||
848
+ ((e.num > 0 || !isEvent(selectorOrRun)) &&
849
+ (e.previous !== e.value || typeof e.value === 'object')))) {
814
850
  reaction(e);
815
851
  }
816
852
  // Update the previous value
@@ -830,6 +866,118 @@ function observe(selectorOrRun, reactionOrOptions, options) {
830
866
  };
831
867
  }
832
868
 
869
+ function _when(predicate, effect, checkReady) {
870
+ // If predicate is a regular Promise skip all the observable stuff
871
+ if (isPromise(predicate)) {
872
+ return effect ? predicate.then(effect) : predicate;
873
+ }
874
+ let value;
875
+ let effectValue;
876
+ // Create a wrapping fn that calls the effect if predicate returns true
877
+ function run(e) {
878
+ const ret = computeSelector(predicate);
879
+ if (!isPromise(ret) && (checkReady ? isObservableValueReady(ret) : ret)) {
880
+ value = ret;
881
+ // Set cancel so that observe does not track anymore
882
+ e.cancel = true;
883
+ }
884
+ return value;
885
+ }
886
+ function doEffect() {
887
+ // If value is truthy then run the effect
888
+ effectValue = effect === null || effect === void 0 ? void 0 : effect(value);
889
+ }
890
+ // Run in an observe
891
+ observe(run, doEffect);
892
+ // If first run resulted in a truthy value just return it.
893
+ // It will have set e.cancel so no need to dispose
894
+ if (isPromise(value)) {
895
+ return effect ? value.then(effect) : value;
896
+ }
897
+ else if (value !== undefined) {
898
+ return effect ? effectValue : Promise.resolve(value);
899
+ }
900
+ else {
901
+ // Wrap it in a promise
902
+ const promise = new Promise((resolve) => {
903
+ if (effect) {
904
+ const originalEffect = effect;
905
+ effect = ((value) => {
906
+ const effectValue = originalEffect(value);
907
+ resolve(isPromise(effectValue) ? effectValue.then((value) => value) : effectValue);
908
+ });
909
+ }
910
+ else {
911
+ effect = resolve;
912
+ }
913
+ });
914
+ return promise;
915
+ }
916
+ }
917
+ function when(predicate, effect) {
918
+ return _when(predicate, effect, false);
919
+ }
920
+ function whenReady(predicate, effect) {
921
+ return _when(predicate, effect, true);
922
+ }
923
+
924
+ function calculateRetryDelay(retryOptions, attemptNum) {
925
+ const { backoff, delay = 1000, infinite, times = 3, maxDelay = 30000 } = retryOptions;
926
+ if (infinite || attemptNum < times) {
927
+ const delayTime = Math.min(delay * (backoff === 'constant' ? 1 : 2 ** attemptNum), maxDelay);
928
+ return delayTime;
929
+ }
930
+ return null;
931
+ }
932
+ function createRetryTimeout(retryOptions, attemptNum, fn) {
933
+ const delayTime = calculateRetryDelay(retryOptions, attemptNum);
934
+ if (delayTime) {
935
+ return setTimeout(fn, delayTime);
936
+ }
937
+ }
938
+ function runWithRetry(node, state, fn) {
939
+ const { retry, waitFor } = node.activationState;
940
+ const e = { cancel: false };
941
+ let value = undefined;
942
+ if (waitFor) {
943
+ value = whenReady(waitFor, () => {
944
+ node.activationState.waitFor = undefined;
945
+ return fn(e);
946
+ });
947
+ }
948
+ else {
949
+ value = fn(e);
950
+ }
951
+ if (isPromise(value) && retry) {
952
+ let timeoutRetry;
953
+ return new Promise((resolve) => {
954
+ const run = () => {
955
+ value
956
+ .then((val) => {
957
+ node.activationState.persistedRetry = false;
958
+ resolve(val);
959
+ })
960
+ .catch(() => {
961
+ node.activationState.persistedRetry = true;
962
+ state.attemptNum++;
963
+ if (timeoutRetry) {
964
+ clearTimeout(timeoutRetry);
965
+ }
966
+ if (!e.cancel) {
967
+ timeoutRetry = createRetryTimeout(retry, state.attemptNum, () => {
968
+ value = fn(e);
969
+ run();
970
+ });
971
+ }
972
+ });
973
+ };
974
+ run();
975
+ });
976
+ }
977
+ return value;
978
+ }
979
+
980
+ const noop = () => { };
833
981
  const ArrayModifiers = new Set([
834
982
  'copyWithin',
835
983
  'fill',
@@ -870,19 +1018,24 @@ if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
870
1018
  }
871
1019
  function collectionSetter(node, target, prop, ...args) {
872
1020
  var _a;
873
- const prevValue = (isArray(target) && target.slice()) || target;
874
- const ret = target[prop].apply(target, args);
875
- if (node) {
876
- const hasParent = isChildNodeValue(node);
877
- const key = hasParent ? node.key : '_';
878
- const parentValue = hasParent ? getNodeValue(node.parent) : node.root;
879
- // Set the object to the previous value first
880
- parentValue[key] = prevValue;
881
- // Then set with the new value so it notifies with the correct prevValue
882
- setKey((_a = node.parent) !== null && _a !== void 0 ? _a : node, key, target);
883
- }
884
- // Return the original value
885
- return ret;
1021
+ if (prop === 'push' && args.length === 1) {
1022
+ setKey(node, target.length + '', args[0]);
1023
+ }
1024
+ else {
1025
+ const prevValue = target.slice();
1026
+ const ret = target[prop].apply(target, args);
1027
+ if (node) {
1028
+ const hasParent = isChildNodeValue(node);
1029
+ const key = hasParent ? node.key : '_';
1030
+ const parentValue = hasParent ? getNodeValue(node.parent) : node.root;
1031
+ // Set the object to the previous value first
1032
+ parentValue[key] = prevValue;
1033
+ // Then set with the new value so it notifies with the correct prevValue
1034
+ setKey((_a = node.parent) !== null && _a !== void 0 ? _a : node, key, target);
1035
+ }
1036
+ // Return the original value
1037
+ return ret;
1038
+ }
886
1039
  }
887
1040
  function getKeys(obj, isArr, isMap) {
888
1041
  return isArr ? undefined : obj ? (isMap ? Array.from(obj.keys()) : Object.keys(obj)) : [];
@@ -917,8 +1070,9 @@ function updateNodes(parent, obj, prevValue) {
917
1070
  let prevChildrenById;
918
1071
  let moved;
919
1072
  const isMap = obj instanceof Map;
1073
+ const isPrevMap = prevValue instanceof Map;
920
1074
  const keys = getKeys(obj, isArr, isMap);
921
- const keysPrev = getKeys(prevValue, isArr, isMap);
1075
+ const keysPrev = getKeys(prevValue, isArr, isPrevMap);
922
1076
  const length = ((_a = (keys || obj)) === null || _a === void 0 ? void 0 : _a.length) || 0;
923
1077
  const lengthPrev = ((_b = (keysPrev || prevValue)) === null || _b === void 0 ? void 0 : _b.length) || 0;
924
1078
  let idField;
@@ -970,7 +1124,7 @@ function updateNodes(parent, obj, prevValue) {
970
1124
  if (!keys.includes(key)) {
971
1125
  hasADiff = true;
972
1126
  const child = getChildNode(parent, key);
973
- const prev = isMap ? prevValue.get(key) : prevValue[key];
1127
+ const prev = isPrevMap ? prevValue.get(key) : prevValue[key];
974
1128
  if (prev !== undefined) {
975
1129
  if (!isPrimitive(prev)) {
976
1130
  updateNodes(child, undefined, prev);
@@ -989,7 +1143,7 @@ function updateNodes(parent, obj, prevValue) {
989
1143
  for (let i = 0; i < length; i++) {
990
1144
  const key = isArr ? i + '' : keys[i];
991
1145
  const value = isMap ? obj.get(key) : obj[key];
992
- const prev = isMap ? prevValue === null || prevValue === void 0 ? void 0 : prevValue.get(key) : prevValue === null || prevValue === void 0 ? void 0 : prevValue[key];
1146
+ const prev = isPrevMap ? prevValue === null || prevValue === void 0 ? void 0 : prevValue.get(key) : prevValue === null || prevValue === void 0 ? void 0 : prevValue[key];
993
1147
  let isDiff = value !== prev;
994
1148
  if (isDiff) {
995
1149
  const id = idField && value
@@ -1027,7 +1181,10 @@ function updateNodes(parent, obj, prevValue) {
1027
1181
  if (isDiff) {
1028
1182
  // Array has a new / modified element
1029
1183
  // If object iterate through its children
1030
- if (isPrimitive(value)) {
1184
+ if (isFunction(value)) {
1185
+ extractFunctionOrComputed(parent, obj, key, value);
1186
+ }
1187
+ else if (isPrimitive(value)) {
1031
1188
  hasADiff = true;
1032
1189
  }
1033
1190
  else {
@@ -1075,6 +1232,15 @@ function getProxy(node, p, asFunction) {
1075
1232
  // Create a proxy if not already cached and return it
1076
1233
  return (node.proxy || (node.proxy = new Proxy(node, proxyHandler)));
1077
1234
  }
1235
+ function flushPending() {
1236
+ // Need to short circuit the computed batching because the user called get() or peek()
1237
+ // in which case the set needs to run immediately so that the values are up to date.
1238
+ if (globalState.pendingNodes.size > 0) {
1239
+ const nodes = Array.from(globalState.pendingNodes.values());
1240
+ globalState.pendingNodes.clear();
1241
+ nodes.forEach((fn) => fn());
1242
+ }
1243
+ }
1078
1244
  const proxyHandler = {
1079
1245
  get(node, p, receiver) {
1080
1246
  var _a;
@@ -1102,6 +1268,9 @@ const proxyHandler = {
1102
1268
  const fn = observableFns.get(p);
1103
1269
  // If this is an observable function, call it
1104
1270
  if (fn) {
1271
+ if (p === 'get' || p === 'peek') {
1272
+ flushPending();
1273
+ }
1105
1274
  return function (a, b, c) {
1106
1275
  const l = arguments.length;
1107
1276
  // Array call and apply are slow so micro-optimize this hot path.
@@ -1207,10 +1376,6 @@ const proxyHandler = {
1207
1376
  return vProp;
1208
1377
  }
1209
1378
  }
1210
- // TODOV3: Remove "state"
1211
- if (vProp === undefined && (p === 'state' || p === '_state') && node.state) {
1212
- return node.state;
1213
- }
1214
1379
  // Return an observable proxy to the property
1215
1380
  return getProxy(node, p);
1216
1381
  },
@@ -1288,7 +1453,7 @@ function set(node, newValue) {
1288
1453
  }
1289
1454
  function toggle(node) {
1290
1455
  const value = getNodeValue(node);
1291
- if (value === undefined || isBoolean(value)) {
1456
+ if (value === undefined || value === null || isBoolean(value)) {
1292
1457
  set(node, !value);
1293
1458
  return !value;
1294
1459
  }
@@ -1302,12 +1467,14 @@ function setKey(node, key, newValue, level) {
1302
1467
  console.warn(`[legend-state] Set an HTMLElement into state. You probably don't want to do that.`);
1303
1468
  }
1304
1469
  }
1470
+ const isRoot = !node.parent && key === '_';
1471
+ // TODOv3 root locking will be removed with old computeds
1305
1472
  if (node.root.locked && !node.root.set) {
1306
1473
  // This happens when modifying a locked observable such as a computed.
1307
1474
  // If merging this could be happening deep in a hierarchy so we don't want to throw errors so we'll just do nothing.
1308
1475
  // This could happen during persistence local load for example.
1309
1476
  if (globalState.isMerging) {
1310
- return;
1477
+ return isRoot ? getProxy(node) : getProxy(node, key);
1311
1478
  }
1312
1479
  else {
1313
1480
  throw new Error(process.env.NODE_ENV === 'development'
@@ -1315,18 +1482,28 @@ function setKey(node, key, newValue, level) {
1315
1482
  : '[legend-state] Modified locked observable');
1316
1483
  }
1317
1484
  }
1318
- const isRoot = !node.parent && key === '_';
1485
+ if (node.parent && !getNodeValue(node)) {
1486
+ return set(node, { [key]: newValue });
1487
+ }
1319
1488
  // Get the child node for updating and notifying
1320
1489
  const childNode = isRoot ? node : getChildNode(node, key, isFunction(newValue) ? newValue : undefined);
1321
- // Set the raw value on the parent object
1322
- const { newValue: savedValue, prevValue, parentValue } = setNodeValue(childNode, newValue);
1323
- const isFunc = isFunction(savedValue);
1324
- const isPrim = isPrimitive(savedValue) || savedValue instanceof Date;
1325
- if (savedValue !== prevValue) {
1326
- updateNodesAndNotify(node, savedValue, prevValue, childNode, isPrim, isRoot, level);
1490
+ if (isObservable(newValue)) {
1491
+ setToObservable(childNode, newValue);
1492
+ }
1493
+ else {
1494
+ // Set the raw value on the parent object
1495
+ const { newValue: savedValue, prevValue, parentValue } = setNodeValue(childNode, newValue);
1496
+ const isFunc = isFunction(savedValue);
1497
+ const isPrim = isPrimitive(savedValue) || savedValue instanceof Date;
1498
+ if (savedValue !== prevValue) {
1499
+ updateNodesAndNotify(node, savedValue, prevValue, childNode, isPrim, isRoot, level);
1500
+ }
1501
+ extractFunctionOrComputed(node, parentValue, key, savedValue);
1502
+ if (isFunc) {
1503
+ return savedValue;
1504
+ }
1327
1505
  }
1328
- extractFunctionOrComputed(node, parentValue, key, savedValue);
1329
- return isFunc ? savedValue : isRoot ? getProxy(node) : getProxy(node, key);
1506
+ return isRoot ? getProxy(node) : getProxy(node, key);
1330
1507
  }
1331
1508
  function assign(node, value) {
1332
1509
  const proxy = getProxy(node);
@@ -1431,7 +1608,7 @@ function handlerMapSet(node, p, value) {
1431
1608
  function updateNodesAndNotify(node, newValue, prevValue, childNode, isPrim, isRoot, level) {
1432
1609
  if (!childNode)
1433
1610
  childNode = node;
1434
- // Make sure we don't call too many listeners for ever property set
1611
+ // Make sure we don't call too many listeners for every property set
1435
1612
  beginBatch();
1436
1613
  let hasADiff = isPrim;
1437
1614
  let whenOptimizedOnlyIf = false;
@@ -1452,7 +1629,7 @@ function updateNodesAndNotify(node, newValue, prevValue, childNode, isPrim, isRo
1452
1629
  }
1453
1630
  endBatch();
1454
1631
  }
1455
- function extractPromise(node, value) {
1632
+ function extractPromise(node, value, setter) {
1456
1633
  if (!node.state) {
1457
1634
  node.state = createObservable({
1458
1635
  isLoaded: false,
@@ -1460,28 +1637,38 @@ function extractPromise(node, value) {
1460
1637
  }
1461
1638
  value
1462
1639
  .then((value) => {
1463
- set(node, value);
1464
- node.state.isLoaded.set(true);
1640
+ setter ? setter({ value }) : set(node, value);
1641
+ node.state.assign({
1642
+ isLoaded: true,
1643
+ error: undefined,
1644
+ });
1465
1645
  })
1466
1646
  .catch((error) => {
1467
1647
  node.state.error.set(error);
1468
1648
  });
1469
1649
  }
1470
1650
  function extractFunctionOrComputed(node, obj, k, v) {
1651
+ var _a;
1471
1652
  if (isPromise(v)) {
1472
1653
  const childNode = getChildNode(node, k);
1473
1654
  extractPromise(childNode, v);
1474
1655
  setNodeValue(childNode, undefined);
1475
1656
  }
1476
1657
  else if (typeof v === 'function') {
1658
+ const childNode = (_a = node.children) === null || _a === void 0 ? void 0 : _a.get(k);
1477
1659
  extractFunction(node, k, v);
1478
- delete obj[k];
1660
+ // If child was previously activated, then peek the new linked observable to make sure it's activated
1661
+ if (childNode && !childNode.lazy) {
1662
+ if (isObservable(v)) {
1663
+ const vNode = getNode(v);
1664
+ peek(vNode);
1665
+ }
1666
+ }
1479
1667
  }
1480
1668
  else if (typeof v == 'object' && v !== null && v !== undefined) {
1481
1669
  const childNode = getNode(v);
1482
1670
  if (childNode === null || childNode === void 0 ? void 0 : childNode.isComputed) {
1483
1671
  extractFunction(node, k, v, childNode);
1484
- delete obj[k];
1485
1672
  }
1486
1673
  else {
1487
1674
  return true;
@@ -1495,177 +1682,316 @@ function get(node, options) {
1495
1682
  return peek(node);
1496
1683
  }
1497
1684
  function peek(node) {
1498
- const value = getNodeValue(node);
1685
+ if (node.dirtyFn) {
1686
+ node.dirtyFn();
1687
+ globalState.dirtyNodes.delete(node);
1688
+ node.dirtyFn = undefined;
1689
+ }
1690
+ let value = getNodeValue(node);
1499
1691
  // If node is not yet lazily computed go do that
1500
1692
  const lazy = node.lazy;
1501
1693
  if (lazy) {
1502
1694
  delete node.lazy;
1503
1695
  if (isFunction(node) || isFunction(lazy)) {
1504
- activateNodeFunction(node, lazy);
1505
- }
1506
- else {
1507
- for (const key in value) {
1508
- if (hasOwnProperty.call(value, key)) {
1509
- extractFunctionOrComputed(node, value, key, value[key]);
1696
+ if (node.parent) {
1697
+ const parentValue = getNodeValue(node.parent);
1698
+ if (parentValue) {
1699
+ delete parentValue[node.key];
1510
1700
  }
1511
1701
  }
1702
+ value = activateNodeFunction(node, lazy);
1703
+ }
1704
+ for (const key in value) {
1705
+ if (hasOwnProperty.call(value, key)) {
1706
+ extractFunctionOrComputed(node, value, key, value[key]);
1707
+ }
1512
1708
  }
1513
1709
  }
1514
1710
  // Check if computed needs to activate
1515
1711
  checkActivate(node);
1516
1712
  return value;
1517
1713
  }
1518
- function createNodeActivationParams(node) {
1519
- node.activationState = {
1520
- lastSync: {},
1521
- };
1522
- const state = node.activationState;
1523
- // The onSet function handles the observable being set
1524
- // and forwards the set elsewhere
1525
- const onSet = (onSetFnParam) => {
1526
- state.onSetFn = onSetFnParam;
1527
- };
1528
- // The onSet function handles the observable being set
1529
- // and forwards the set elsewhere
1530
- const updateLastSync = (fn) => {
1531
- state.lastSync.value = fn;
1532
- };
1533
- // The subscribe function runs a function that listens to
1534
- // a data source and sends updates into the observable
1535
- const subscribe = (fn) => {
1536
- if (!state.subscriber) {
1537
- state.subscriber = fn;
1538
- }
1539
- };
1540
- const cache = (fn) => {
1541
- if (!state.cacheOptions) {
1542
- state.cacheOptions = isFunction(fn) ? fn() : fn;
1543
- }
1544
- };
1545
- const retry = (params) => {
1546
- if (!state.retryOptions) {
1547
- state.retryOptions = params;
1548
- }
1549
- };
1550
- // The proxy function simply marks the node as a proxy with this function
1551
- // so that child nodes will be created with this function, and then simply
1552
- // activated as a function
1553
- const proxy = (fn) => {
1554
- node.proxyFn2 = fn;
1555
- };
1556
- return {
1557
- onSet,
1558
- proxy,
1559
- cache,
1560
- retry,
1561
- subscribe,
1562
- updateLastSync,
1563
- };
1564
- }
1565
1714
  function activateNodeFunction(node, lazyFn) {
1566
- let prevTarget$;
1567
- let curTarget$;
1568
- const activator = (isFunction(node) ? node : lazyFn);
1715
+ // let prevTarget$: Observable<any>;
1716
+ // let curTarget$: Observable<any>;
1717
+ let update;
1569
1718
  let wasPromise;
1570
- const refresh = () => node.state.refreshNum.set((v) => v + 1);
1719
+ const activateFn = (isFunction(node) ? node : lazyFn);
1720
+ const doRetry = () => { var _a; return (_a = node.state) === null || _a === void 0 ? void 0 : _a.refreshNum.set((v) => v + 1); };
1721
+ let activatedValue;
1722
+ let disposes = [];
1723
+ let refreshFn;
1724
+ function markDirty() {
1725
+ node.dirtyFn = refreshFn;
1726
+ globalState.dirtyNodes.add(node);
1727
+ }
1571
1728
  observe(() => {
1572
- const params = createNodeActivationParams(node);
1729
+ var _a, _b, _c, _d;
1730
+ // const params = createNodeActivationParams(node);
1573
1731
  // Run the function at this node
1574
- let value = activator(params);
1575
- // If target is an observable, get() it to make sure we listen to its changes
1576
- // and set up an onSet to write changes back to it
1732
+ let value = activateFn();
1733
+ // If target is an observable, make this node a link to it
1577
1734
  if (isObservable(value)) {
1578
- prevTarget$ = curTarget$;
1579
- curTarget$ = value;
1580
- params.onSet(({ value: newValue, getPrevious }) => {
1581
- // Don't set the target observable if the target has changed since the last run
1582
- if (!prevTarget$ || curTarget$ === prevTarget$) {
1583
- // Set the node value back to what it was before before setting it.
1584
- // This is a workaround for linked objects because it might not notify
1585
- // if setting a property of an object
1586
- // TODO: Is there a way to not do this? Or at least only do it in a
1587
- // small subset of cases?
1588
- setNodeValue(getNode(curTarget$), getPrevious());
1589
- // Set the value on the curTarget
1590
- curTarget$.set(newValue);
1591
- }
1592
- });
1593
- // Get the value from the observable because we still want the raw value
1594
- // for the effect.
1595
- value = value.get();
1735
+ value = setToObservable(node, value);
1596
1736
  }
1597
- else {
1598
- wasPromise = isPromise(value) ? value : undefined;
1737
+ if (isFunction(value)) {
1738
+ value = value();
1599
1739
  }
1740
+ const activated = value === null || value === void 0 ? void 0 : value[symbolActivated];
1741
+ if (activated) {
1742
+ node.activationState = activated;
1743
+ value = undefined;
1744
+ }
1745
+ wasPromise = isPromise(value);
1600
1746
  // Activate this node if not activated already (may be called recursively)
1601
1747
  // TODO: Is calling recursively bad? If so can it be fixed?
1602
1748
  if (!node.activated) {
1603
1749
  node.activated = true;
1750
+ const isCached = !!((_a = node.activationState) === null || _a === void 0 ? void 0 : _a.cache);
1751
+ wasPromise = wasPromise || !!isCached;
1604
1752
  const activateNodeFn = wasPromise ? globalState.activateNode : activateNodeBase;
1605
- activateNodeFn(node, refresh, value);
1753
+ const { update: newUpdate, value: newValue } = activateNodeFn(node, doRetry, !!wasPromise, value);
1754
+ update = newUpdate;
1755
+ value = newValue !== null && newValue !== void 0 ? newValue : activated === null || activated === void 0 ? void 0 : activated.initial;
1756
+ }
1757
+ else if (node.activationState) {
1758
+ if (!node.activationState.persistedRetry && !node.activationState.waitFor) {
1759
+ const activated = node.activationState;
1760
+ // TODO Should this have lastSync and value somehow?
1761
+ value =
1762
+ (_c = (_b = activated.get) === null || _b === void 0 ? void 0 : _b.call(activated, {
1763
+ updateLastSync: noop,
1764
+ setMode: noop,
1765
+ lastSync: undefined,
1766
+ value: undefined,
1767
+ refresh: doRetry,
1768
+ })) !== null && _c !== void 0 ? _c : activated.initial;
1769
+ }
1606
1770
  }
1607
- node.state.refreshNum.get();
1771
+ wasPromise = wasPromise || isPromise(value);
1772
+ get(getNode((_d = node.state) === null || _d === void 0 ? void 0 : _d.refreshNum));
1608
1773
  return value;
1609
- }, ({ value }) => {
1610
- if (!globalState.isLoadingRemote$.peek()) {
1774
+ }, (e) => {
1775
+ const { value, nodes, refresh } = e;
1776
+ refreshFn = refresh;
1777
+ if (!wasPromise || !globalState.isLoadingRemote$.peek()) {
1611
1778
  if (wasPromise) {
1612
- // Extract the promise to make it set the value/error when it comes in
1613
- extractPromise(node, value);
1614
- // Set this to undefined only if it's replacing the activation function,
1615
- // so we don't overwrite it if it already has real data from either local
1616
- // cache or a previous run
1617
- if (isFunction(getNodeValue(node))) {
1618
- setNodeValue(node, undefined);
1779
+ if (node.activationState) {
1780
+ const { initial } = node.activationState;
1781
+ if (value && isPromise(value)) {
1782
+ // Extract the promise to make it set the value/error when it comes in
1783
+ extractPromise(node, value, update);
1784
+ }
1785
+ // Set this to undefined only if it's replacing the activation function,
1786
+ // so we don't overwrite it if it already has real data from either local
1787
+ // cache or a previous run
1788
+ if (isFunction(getNodeValue(node))) {
1789
+ setNodeValue(node, initial !== null && initial !== void 0 ? initial : undefined);
1790
+ }
1791
+ }
1792
+ else if (node.activated) {
1793
+ // Extract the promise to make it set the value/error when it comes in
1794
+ extractPromise(node, value, update);
1795
+ // Set this to undefined only if it's replacing the activation function,
1796
+ // so we don't overwrite it if it already has real data from either local
1797
+ // cache or a previous run
1798
+ if (isFunction(getNodeValue(node))) {
1799
+ setNodeValue(node, undefined);
1800
+ }
1619
1801
  }
1620
1802
  }
1621
1803
  else {
1804
+ activatedValue = value;
1622
1805
  set(node, value);
1623
- node.state.isLoaded.set(true);
1806
+ node.state.assign({
1807
+ isLoaded: true,
1808
+ error: undefined,
1809
+ });
1624
1810
  }
1625
1811
  }
1626
- }, { immediate: true, fromComputed: true });
1812
+ disposes.forEach((fn) => fn());
1813
+ disposes = [];
1814
+ nodes === null || nodes === void 0 ? void 0 : nodes.forEach(({ node }) => {
1815
+ disposes.push(onChange(node, markDirty, { immediate: true }));
1816
+ });
1817
+ e.cancel = true;
1818
+ }, { fromComputed: true });
1819
+ return activatedValue;
1627
1820
  }
1628
- const activateNodeBase = (globalState.activateNode = function activateNodeBase(node, refresh) {
1629
- const { onSetFn, subscriber } = node.activationState;
1630
- let isSetting = false;
1821
+ const activateNodeBase = (globalState.activateNode = function activateNodeBase(node, refresh, wasPromise, value) {
1631
1822
  if (!node.state) {
1632
1823
  node.state = createObservable({
1633
1824
  isLoaded: false,
1634
1825
  }, false, extractPromise, getProxy);
1635
1826
  }
1636
- if (onSetFn) {
1637
- const doSet = (params) => {
1638
- // Don't call the set if this is the first value coming in
1639
- if (!isSetting) {
1640
- if (params.changes.length > 1 || !isFunction(params.changes[0].prevAtPath)) {
1641
- isSetting = true;
1642
- batch(() => onSetFn(params), () => {
1643
- isSetting = false;
1644
- });
1827
+ let isSetting = false;
1828
+ let isSettingFromSubscribe = false;
1829
+ let _mode = 'set';
1830
+ if (node.activationState) {
1831
+ const { onSet, subscribe, get: getFn, initial } = node.activationState;
1832
+ value = getFn
1833
+ ? runWithRetry(node, { attemptNum: 0 }, () => {
1834
+ return getFn({
1835
+ updateLastSync: noop,
1836
+ setMode: (mode) => (_mode = mode),
1837
+ lastSync: undefined,
1838
+ value: undefined,
1839
+ refresh,
1840
+ });
1841
+ })
1842
+ : undefined;
1843
+ // TODO Should this have lastSync and value somehow?
1844
+ if (value == undefined || value === null) {
1845
+ value = initial;
1846
+ }
1847
+ if (onSet) {
1848
+ let allChanges = [];
1849
+ let latestValue = undefined;
1850
+ let runNumber = 0;
1851
+ const runChanges = (listenerParams) => {
1852
+ // Don't call the set if this is the first value coming in
1853
+ if (allChanges.length > 0) {
1854
+ let changes;
1855
+ let value;
1856
+ let getPrevious;
1857
+ if (listenerParams) {
1858
+ changes = listenerParams.changes;
1859
+ value = listenerParams.value;
1860
+ getPrevious = listenerParams.getPrevious;
1861
+ }
1862
+ else {
1863
+ // If this is called by flushPending then get the change array
1864
+ // that we've been building up.
1865
+ changes = allChanges;
1866
+ value = latestValue;
1867
+ getPrevious = createPreviousHandler(value, changes);
1868
+ }
1869
+ allChanges = [];
1870
+ latestValue = undefined;
1871
+ globalState.pendingNodes.delete(node);
1872
+ runNumber++;
1873
+ const thisRunNumber = runNumber;
1874
+ const run = () => {
1875
+ if (thisRunNumber !== runNumber) {
1876
+ // set may get called multiple times before it loads so ignore any previous runs
1877
+ return;
1878
+ }
1879
+ const retryAttempts = { attemptNum: 0 };
1880
+ return runWithRetry(node, retryAttempts, (eventRetry) => {
1881
+ const cancelRetry = () => {
1882
+ eventRetry.cancel = true;
1883
+ };
1884
+ return new Promise((resolve, reject) => {
1885
+ isSetting = true;
1886
+ batch(() => {
1887
+ try {
1888
+ return onSet({
1889
+ value,
1890
+ changes,
1891
+ getPrevious,
1892
+ node,
1893
+ update,
1894
+ refresh,
1895
+ retryNum: retryAttempts.attemptNum,
1896
+ cancelRetry,
1897
+ fromSubscribe: isSettingFromSubscribe,
1898
+ });
1899
+ }
1900
+ catch (e) {
1901
+ reject(e);
1902
+ }
1903
+ }, () => {
1904
+ isSetting = false;
1905
+ resolve();
1906
+ });
1907
+ });
1908
+ });
1909
+ };
1910
+ whenReady(node.state.isLoaded, run);
1645
1911
  }
1646
- }
1647
- };
1648
- onChange(node, doSet, { immediate: true });
1649
- }
1650
- if (process.env.NODE_ENV === 'development' && node.activationState.cacheOptions) {
1651
- // TODO Better message
1652
- console.log('[legend-state] Using cacheOptions without setting up persistence first');
1653
- }
1654
- if (process.env.NODE_ENV === 'development' && node.activationState.retryOptions) {
1655
- // TODO Better message
1656
- console.log('[legend-state] Using retryOptions without setting up persistence first');
1912
+ };
1913
+ const onChangeImmediate = ({ value, changes }) => {
1914
+ if (!isSetting || isSettingFromSubscribe) {
1915
+ if (changes.length > 1 || !isFunction(changes[0].prevAtPath)) {
1916
+ latestValue = value;
1917
+ if (allChanges.length > 0) {
1918
+ changes = changes.filter((change) => !isArraySubset(allChanges[0].path, change.path));
1919
+ }
1920
+ allChanges.push(...changes);
1921
+ globalState.pendingNodes.set(node, runChanges);
1922
+ }
1923
+ }
1924
+ };
1925
+ // Create an immediate listener to mark this node as pending. Then actually run
1926
+ // the changes at the end of the batch so everything is properly batched.
1927
+ // However, this can be short circuited if the user calls get() or peek()
1928
+ // in which case the set needs to run immediately so that the values are up to date.
1929
+ onChange(node, onChangeImmediate, { immediate: true });
1930
+ onChange(node, runChanges);
1931
+ }
1932
+ if (process.env.NODE_ENV === 'development' && node.activationState.cache) {
1933
+ // TODO Better message
1934
+ console.log('[legend-state] Using cache without setting up persistence first');
1935
+ }
1936
+ if (process.env.NODE_ENV === 'development' && node.activationState.retry) {
1937
+ // TODO Better message
1938
+ console.log('[legend-state] Using retry without setting up persistence first');
1939
+ }
1940
+ if (subscribe) {
1941
+ const updateFromSubscribe = (params) => {
1942
+ whenReady(node.state.isLoaded, () => {
1943
+ isSettingFromSubscribe = true;
1944
+ update(params);
1945
+ isSettingFromSubscribe = false;
1946
+ });
1947
+ };
1948
+ subscribe({ node, update: updateFromSubscribe, refresh });
1949
+ }
1657
1950
  }
1658
- const update = ({ value }) => {
1951
+ const update = ({ value, mode }) => {
1659
1952
  // TODO: This isSetting might not be necessary? Tests still work if removing it.
1660
1953
  // Write tests that would break it if removed? I'd guess a combination of subscribe and
1661
1954
  if (!isSetting) {
1662
- set(node, value);
1955
+ isSetting = true;
1956
+ if (_mode === 'assign' || mode === 'assign') {
1957
+ assign(node, value);
1958
+ }
1959
+ else if (_mode === 'merge' || mode === 'merge') {
1960
+ mergeIntoObservable(getProxy(node), value);
1961
+ }
1962
+ else {
1963
+ set(node, value);
1964
+ }
1965
+ isSetting = false;
1663
1966
  }
1664
1967
  };
1665
- if (subscriber) {
1666
- subscriber({ update, refresh });
1667
- }
1968
+ return { update, value };
1668
1969
  });
1970
+ function setToObservable(node, value) {
1971
+ // If the computed is a proxy to another observable
1972
+ // link it to the target observable
1973
+ const linkedNode = getNode(value);
1974
+ if (linkedNode !== node) {
1975
+ const prevNode = node.linkedToNode;
1976
+ node.linkedToNode = linkedNode;
1977
+ if (!linkedNode.linkedFromNodes) {
1978
+ linkedNode.linkedFromNodes = new Set();
1979
+ }
1980
+ linkedNode.linkedFromNodes.add(node);
1981
+ peek(linkedNode);
1982
+ onChange(linkedNode, ({ value: newValue }) => {
1983
+ value = newValue;
1984
+ set(node, value);
1985
+ }, { initial: true });
1986
+ // If the target observable is different then notify for the change
1987
+ if (prevNode) {
1988
+ const value = getNodeValue(linkedNode);
1989
+ const prevValue = getNodeValue(prevNode);
1990
+ notify(node, value, prevValue, 0);
1991
+ }
1992
+ }
1993
+ return value;
1994
+ }
1669
1995
 
1670
1996
  const fns = ['get', 'set', 'peek', 'onChange', 'toggle'];
1671
1997
  function ObservablePrimitiveClass(node) {
@@ -1682,8 +2008,14 @@ function proto(key, fn) {
1682
2008
  return fn.call(this, this._node, ...args);
1683
2009
  };
1684
2010
  }
1685
- proto('peek', peek);
1686
- proto('get', get);
2011
+ proto('peek', (node) => {
2012
+ flushPending();
2013
+ return peek(node);
2014
+ });
2015
+ proto('get', (node, options) => {
2016
+ flushPending();
2017
+ return get(node, options);
2018
+ });
1687
2019
  proto('set', set);
1688
2020
  proto('onChange', onChange);
1689
2021
  // Getters
@@ -1695,7 +2027,7 @@ Object.defineProperty(ObservablePrimitiveClass.prototype, symbolGetNode, {
1695
2027
  });
1696
2028
  ObservablePrimitiveClass.prototype.toggle = function () {
1697
2029
  const value = this.peek();
1698
- if (value === undefined || isBoolean(value)) {
2030
+ if (value === undefined || value === null || isBoolean(value)) {
1699
2031
  this.set(!value);
1700
2032
  }
1701
2033
  else if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
@@ -1714,6 +2046,16 @@ function observable(value) {
1714
2046
  function observablePrimitive(value) {
1715
2047
  return createObservable(value, true, extractPromise, getProxy, ObservablePrimitiveClass);
1716
2048
  }
2049
+ function syncState(obs) {
2050
+ const node = getNode(obs);
2051
+ if (!node.state) {
2052
+ peek(node);
2053
+ }
2054
+ if (!node.state) {
2055
+ node.state = observable({});
2056
+ }
2057
+ return node.state;
2058
+ }
1717
2059
  globalState.isLoadingRemote$ = observable(false);
1718
2060
 
1719
2061
  function computed(compute, set$1) {
@@ -1878,60 +2220,6 @@ function proxy(get, set) {
1878
2220
  return obs;
1879
2221
  }
1880
2222
 
1881
- function _when(predicate, effect, checkReady) {
1882
- // If predicate is a regular Promise skip all the observable stuff
1883
- if (isPromise(predicate)) {
1884
- return effect ? predicate.then(effect) : predicate;
1885
- }
1886
- let value;
1887
- // Create a wrapping fn that calls the effect if predicate returns true
1888
- function run(e) {
1889
- const ret = computeSelector(predicate);
1890
- if (!isPromise(ret) && (checkReady ? isObservableValueReady(ret) : ret)) {
1891
- value = ret;
1892
- // Set cancel so that observe does not track anymore
1893
- e.cancel = true;
1894
- }
1895
- return value;
1896
- }
1897
- function doEffect() {
1898
- // If value is truthy then run the effect
1899
- effect === null || effect === void 0 ? void 0 : effect(value);
1900
- }
1901
- // Run in an observe
1902
- observe(run, doEffect);
1903
- // If first run resulted in a truthy value just return it.
1904
- // It will have set e.cancel so no need to dispose
1905
- if (isPromise(value)) {
1906
- return effect ? value.then(effect) : value;
1907
- }
1908
- else if (value !== undefined) {
1909
- return Promise.resolve(value);
1910
- }
1911
- else {
1912
- // Wrap it in a promise
1913
- const promise = new Promise((resolve) => {
1914
- if (effect) {
1915
- const originalEffect = effect;
1916
- effect = (value) => {
1917
- const effectValue = originalEffect(value);
1918
- resolve(effectValue);
1919
- };
1920
- }
1921
- else {
1922
- effect = resolve;
1923
- }
1924
- });
1925
- return promise;
1926
- }
1927
- }
1928
- function when(predicate, effect) {
1929
- return _when(predicate, effect, false);
1930
- }
1931
- function whenReady(predicate, effect) {
1932
- return _when(predicate, effect, true);
1933
- }
1934
-
1935
2223
  const internal = {
1936
2224
  ensureNodeValue,
1937
2225
  findIDKey,
@@ -1942,11 +2230,13 @@ const internal = {
1942
2230
  observableFns,
1943
2231
  optimized,
1944
2232
  peek,
2233
+ runWithRetry,
1945
2234
  set,
1946
2235
  setAtPath,
1947
2236
  setNodeValue,
2237
+ symbolActivated,
1948
2238
  symbolDelete,
1949
2239
  };
1950
2240
 
1951
- export { ObservablePrimitiveClass, batch, beginBatch, beginTracking, checkActivate, computeSelector, computed, configureLegendState, constructObjectWithPath, deconstructObjectWithPath, endBatch, endTracking, event, extraPrimitiveActivators, extraPrimitiveProps, findIDKey, getNode, getNodeValue, getObservableIndex, hasOwnProperty, internal, isArray, isBoolean, isEmpty, isFunction, isObject, isObservable, isObservableValueReady, isPrimitive, isPromise, isString, isSymbol, lockObservable, mergeIntoObservable, observable, observablePrimitive, observe, opaqueObject, optimized, proxy, setAtPath, setInObservableAtPath, setSilently, setupTracking, symbolDelete, trackSelector, tracking, updateTracking, when, whenReady };
2241
+ export { ObservablePrimitiveClass, activated, batch, beginBatch, beginTracking, checkActivate, computeSelector, computed, configureLegendState, constructObjectWithPath, deconstructObjectWithPath, endBatch, endTracking, event, extraPrimitiveActivators, extraPrimitiveProps, findIDKey, getNode, getNodeValue, getObservableIndex, hasOwnProperty, internal, isArray, isBoolean, isEmpty, isFunction, isObject, isObservable, isObservableValueReady, isPrimitive, isPromise, isString, isSymbol, lockObservable, mergeIntoObservable, observable, observablePrimitive, observe, opaqueObject, optimized, proxy, setAtPath, setInObservableAtPath, setSilently, setupTracking, symbolDelete, syncState, trackSelector, tracking, updateTracking, when, whenReady };
1952
2242
  //# sourceMappingURL=index.mjs.map