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