@legendapp/state 2.2.0-next.4 → 2.2.0-next.41

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 (110) 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 +13 -4
  23. package/index.js +655 -448
  24. package/index.js.map +1 -1
  25. package/index.mjs +652 -447
  26. package/index.mjs.map +1 -1
  27. package/package.json +1 -12
  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 +412 -180
  46. package/persist.js.map +1 -1
  47. package/persist.mjs +413 -181
  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 +4 -3
  66. package/react-hooks/usePersistedObservable.js.map +1 -1
  67. package/react-hooks/usePersistedObservable.mjs +4 -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 +6 -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 +3 -3
  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 +10 -8
  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 -3
  87. package/src/history/trackHistory.d.ts +1 -1
  88. package/src/is.d.ts +2 -0
  89. package/src/observable.d.ts +7 -15
  90. package/src/observableInterfaces.d.ts +56 -348
  91. package/src/observableTypes.d.ts +85 -0
  92. package/src/persist/observablePersistRemoteFunctionsAdapter.d.ts +1 -1
  93. package/src/persist/persistActivateNode.d.ts +0 -17
  94. package/src/persist/persistHelpers.d.ts +1 -1
  95. package/src/persist/persistObservable.d.ts +2 -3
  96. package/src/persistTypes.d.ts +196 -0
  97. package/src/proxy.d.ts +5 -5
  98. package/src/react/Computed.d.ts +1 -1
  99. package/src/react/Switch.d.ts +3 -3
  100. package/src/react/reactInterfaces.d.ts +2 -1
  101. package/src/react/useComputed.d.ts +5 -5
  102. package/src/react/usePauseProvider.d.ts +3 -3
  103. package/src/react/useWhen.d.ts +2 -2
  104. package/src/react-hooks/useFetch.d.ts +4 -3
  105. package/src/react-hooks/usePersistedObservable.d.ts +2 -2
  106. package/src/retry.d.ts +6 -0
  107. package/src/trackSelector.d.ts +3 -2
  108. package/src/when.d.ts +6 -2
  109. package/trace.js.map +1 -1
  110. package/trace.mjs.map +1 -1
package/index.js CHANGED
@@ -8,14 +8,17 @@ 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';
15
15
  }
16
16
  function isPrimitive(arg) {
17
17
  const type = typeof arg;
18
- return arg !== undefined && type !== 'object' && type !== 'function';
18
+ return arg !== undefined && (isDate(arg) || (type !== 'object' && type !== 'function'));
19
+ }
20
+ function isDate(obj) {
21
+ return obj instanceof Date;
19
22
  }
20
23
  function isSymbol(obj) {
21
24
  return typeof obj === 'symbol';
@@ -40,6 +43,9 @@ function isEmpty(obj) {
40
43
  }
41
44
  return true;
42
45
  }
46
+ function isNullOrUndefined(value) {
47
+ return value === undefined || value === null;
48
+ }
43
49
  const setPrimitives = new Set(['boolean', 'string', 'number']);
44
50
  /** @internal */
45
51
  function isActualPrimitive(arg) {
@@ -55,6 +61,7 @@ const symbolGetNode = Symbol('getNode');
55
61
  const symbolDelete = /* @__PURE__ */ Symbol('delete');
56
62
  const symbolOpaque = Symbol('opaque');
57
63
  const optimized = Symbol('optimized');
64
+ const symbolActivated = Symbol('activated');
58
65
  // TODOV3 Remove these
59
66
  const extraPrimitiveActivators = new Map();
60
67
  const extraPrimitiveProps = new Map();
@@ -63,28 +70,17 @@ const globalState = {
63
70
  isMerging: false,
64
71
  isLoadingRemote$: undefined,
65
72
  activateNode: undefined,
73
+ pendingNodes: new Map(),
74
+ dirtyNodes: new Set(),
66
75
  };
67
76
  function isObservable(obs) {
68
77
  return !!obs && !!obs[symbolGetNode];
69
78
  }
70
- function isComputed(obs) {
71
- var _a;
72
- return obs && ((_a = obs[symbolGetNode]) === null || _a === void 0 ? void 0 : _a.isComputed);
73
- }
74
- function checkActivate(node) {
75
- var _a;
76
- const root = node.root;
77
- (_a = root.activate) === null || _a === void 0 ? void 0 : _a.call(root);
78
- if (root.toActivate) {
79
- root.toActivate.forEach(checkActivate);
80
- delete root.toActivate;
81
- }
82
- }
83
79
  function getNode(obs) {
84
80
  return obs && obs[symbolGetNode];
85
81
  }
86
82
  function setNodeValue(node, newValue) {
87
- var _a;
83
+ var _a, _b, _c;
88
84
  const parentNode = (_a = node.parent) !== null && _a !== void 0 ? _a : node;
89
85
  const key = node.parent ? node.key : '_';
90
86
  const isDelete = newValue === symbolDelete;
@@ -99,27 +95,30 @@ function setNodeValue(node, newValue) {
99
95
  // Compute newValue if newValue is a function or an observable
100
96
  newValue = !parentNode.isAssigning && isFunc ? newValue(prevValue) : newValue;
101
97
  // If setting an observable, set a link to the observable instead
102
- if (isObservable(newValue) && !isComputed(newValue)) {
98
+ if (isObservable(newValue)) {
103
99
  const val = newValue;
104
- node.lazy = () => val;
100
+ node.lazy = true;
101
+ node.lazyFn = () => val;
105
102
  newValue = undefined;
106
103
  }
107
- try {
108
- parentNode.isSetting = (parentNode.isSetting || 0) + 1;
109
- // Save the new value
110
- if (isDelete) {
111
- delete parentValue[key];
104
+ if (!globalState.isMerging ||
105
+ prevValue === undefined ||
106
+ isFunction(prevValue) ||
107
+ !((_c = (_b = node.parent) === null || _b === void 0 ? void 0 : _b.functions) === null || _c === void 0 ? void 0 : _c.get(key))) {
108
+ try {
109
+ parentNode.isSetting = (parentNode.isSetting || 0) + 1;
110
+ // Save the new value
111
+ if (isDelete) {
112
+ delete parentValue[key];
113
+ }
114
+ else {
115
+ parentValue[key] = newValue;
116
+ }
112
117
  }
113
- else {
114
- parentValue[key] = newValue;
118
+ finally {
119
+ parentNode.isSetting--;
115
120
  }
116
121
  }
117
- finally {
118
- parentNode.isSetting--;
119
- }
120
- if (parentNode.root.locked && parentNode.root.set) {
121
- parentNode.root.set(parentNode.root._);
122
- }
123
122
  return { prevValue, newValue, parentValue };
124
123
  }
125
124
  const arrNodeKeys = [];
@@ -137,12 +136,6 @@ function getNodeValue(node) {
137
136
  }
138
137
  return child;
139
138
  }
140
- const cloneFunction = (originalFunction) => {
141
- const length = originalFunction.length;
142
- return length > 1
143
- ? (arg1, arg2) => originalFunction(arg1, arg2)
144
- : (...args) => originalFunction(...args);
145
- };
146
139
  function getChildNode(node, key, asFunction) {
147
140
  var _a;
148
141
  // Get the child by key
@@ -155,11 +148,15 @@ function getChildNode(node, key, asFunction) {
155
148
  key,
156
149
  lazy: true,
157
150
  };
158
- if (asFunction) {
159
- child = Object.assign(cloneFunction(asFunction), child);
151
+ if (node.activationState) {
152
+ const { lookup } = node.activationState;
153
+ if (lookup) {
154
+ asFunction = lookup.bind(node, key);
155
+ }
160
156
  }
161
- else if (node.proxyFn2) {
162
- child = Object.assign(node.proxyFn2.bind(node, key), child);
157
+ if (asFunction) {
158
+ child = Object.assign(() => { }, child);
159
+ child.lazyFn = asFunction;
163
160
  }
164
161
  if (!node.children) {
165
162
  node.children = new Map();
@@ -183,17 +180,19 @@ function ensureNodeValue(node) {
183
180
  }
184
181
  function findIDKey(obj, node) {
185
182
  var _a, _b;
186
- let idKey = isObject(obj)
187
- ? 'id' in obj
188
- ? 'id'
189
- : 'key' in obj
190
- ? 'key'
191
- : '_id' in obj
192
- ? '_id'
193
- : '__id' in obj
194
- ? '__id'
195
- : undefined
196
- : undefined;
183
+ let idKey = isObservable(obj)
184
+ ? undefined
185
+ : isObject(obj)
186
+ ? 'id' in obj
187
+ ? 'id'
188
+ : 'key' in obj
189
+ ? 'key'
190
+ : '_id' in obj
191
+ ? '_id'
192
+ : '__id' in obj
193
+ ? '__id'
194
+ : undefined
195
+ : undefined;
197
196
  if (!idKey && node.parent) {
198
197
  const k = node.key + '_keyExtractor';
199
198
  const keyExtractor = (_b = (_a = node.functions) === null || _a === void 0 ? void 0 : _a.get(k)) !== null && _b !== void 0 ? _b : getNodeValue(node.parent)[node.key + '_keyExtractor'];
@@ -203,18 +202,17 @@ function findIDKey(obj, node) {
203
202
  }
204
203
  return idKey;
205
204
  }
206
- function extractFunction(node, key, fnOrComputed, computedChildNode) {
205
+ function extractFunction(node, key, fnOrComputed) {
207
206
  if (!node.functions) {
208
207
  node.functions = new Map();
209
208
  }
210
209
  node.functions.set(key, fnOrComputed);
211
- if (computedChildNode) {
212
- computedChildNode.parentOther = getChildNode(node, key);
213
- if (!node.root.toActivate) {
214
- node.root.toActivate = [];
215
- }
216
- node.root.toActivate.push(computedChildNode);
217
- }
210
+ }
211
+
212
+ function activated(params) {
213
+ return (() => ({
214
+ [symbolActivated]: params,
215
+ }));
218
216
  }
219
217
 
220
218
  let timeout;
@@ -241,23 +239,28 @@ function isArraySubset(mainArr, subsetArr) {
241
239
  return true;
242
240
  }
243
241
  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]];
242
+ try {
243
+ // Clones the current state and inject the previous data at the changed path
244
+ let clone = value ? JSON.parse(JSON.stringify(value)) : {};
245
+ for (let i = 0; i < changes.length; i++) {
246
+ const { path, prevAtPath } = changes[i];
247
+ let o = clone;
248
+ if (path.length > 0) {
249
+ let i;
250
+ for (i = 0; i < path.length - 1; i++) {
251
+ o = o[path[i]];
252
+ }
253
+ o[path[i]] = prevAtPath;
254
+ }
255
+ else {
256
+ clone = prevAtPath;
253
257
  }
254
- o[path[i]] = prevAtPath;
255
- }
256
- else {
257
- clone = prevAtPath;
258
258
  }
259
+ return clone;
260
+ }
261
+ catch (_a) {
262
+ return undefined;
259
263
  }
260
- return clone;
261
264
  }
262
265
  function createPreviousHandler(value, changes) {
263
266
  // Create a function that generates the previous state
@@ -366,8 +369,15 @@ function batchNotifyChanges(changesInBatch, immediate) {
366
369
  });
367
370
  }
368
371
  function runBatch() {
372
+ const dirtyNodes = Array.from(globalState.dirtyNodes);
373
+ globalState.dirtyNodes.clear();
374
+ dirtyNodes.forEach((node) => {
375
+ var _a;
376
+ (_a = node.dirtyFn) === null || _a === void 0 ? void 0 : _a.call(node);
377
+ node.dirtyFn = undefined;
378
+ });
369
379
  // Save batch locally and reset _batchMap first because a new batch could begin while looping over callbacks.
370
- // This can happen with observableComputed for example.
380
+ // This can happen with computeds for example.
371
381
  const map = _batchMap;
372
382
  _batchMap = new Map();
373
383
  const changesInBatch = new Map();
@@ -421,7 +431,7 @@ function endBatch(force) {
421
431
  }
422
432
  numInBatch = 0;
423
433
  // Save batch locally and reset _batch first because a new batch could begin while looping over callbacks.
424
- // This can happen with observableComputed for example.
434
+ // This can happen with computeds for example.
425
435
  const after = _afterBatch;
426
436
  if (after.length) {
427
437
  _afterBatch = [];
@@ -463,7 +473,8 @@ function createObservable(value, makePrimitive, extractPromise, createObject, cr
463
473
  lazy: true,
464
474
  };
465
475
  if (valueIsFunction) {
466
- node = Object.assign(cloneFunction(value), node);
476
+ node = Object.assign(() => { }, node);
477
+ node.lazyFn = value;
467
478
  }
468
479
  const prim = makePrimitive || isActualPrimitive(value);
469
480
  const obs = prim
@@ -482,7 +493,7 @@ function isEvent(obs) {
482
493
  }
483
494
  function computeSelector(selector, e, retainObservable) {
484
495
  let c = selector;
485
- if (isFunction(c)) {
496
+ if (!isObservable(c) && isFunction(c)) {
486
497
  c = e ? c(e) : c();
487
498
  }
488
499
  return isObservable(c) && !retainObservable ? c.get() : c;
@@ -498,13 +509,6 @@ function opaqueObject(value) {
498
509
  }
499
510
  return value;
500
511
  }
501
- function lockObservable(obs, value) {
502
- var _a;
503
- const root = (_a = getNode(obs)) === null || _a === void 0 ? void 0 : _a.root;
504
- if (root) {
505
- root.locked = value;
506
- }
507
- }
508
512
  function setAtPath(obj, path, pathTypes, value, fullObj, restore) {
509
513
  let o = obj;
510
514
  let oFull = fullObj;
@@ -581,14 +585,15 @@ function _mergeIntoObservable(target, source) {
581
585
  const targetValue = needsSet ? target.peek() : target;
582
586
  const isTargetArr = isArray(targetValue);
583
587
  const isTargetObj = !isTargetArr && isObject(targetValue);
584
- if ((isTargetObj && isObject(source) && !isEmpty(targetValue)) ||
585
- (isTargetArr && isArray(source) && targetValue.length > 0)) {
588
+ if ((isTargetObj && isObject(source) && !isEmpty(targetValue)) || (isTargetArr && targetValue.length > 0)) {
586
589
  const keys = Object.keys(source);
587
590
  for (let i = 0; i < keys.length; i++) {
588
591
  const key = keys[i];
589
592
  const sourceValue = source[key];
590
593
  if (sourceValue === symbolDelete) {
591
- needsSet && ((_a = target[key]) === null || _a === void 0 ? void 0 : _a.delete) ? target[key].delete() : delete target[key];
594
+ needsSet && ((_a = target[key]) === null || _a === void 0 ? void 0 : _a.delete)
595
+ ? target[key].delete()
596
+ : delete target[key];
592
597
  }
593
598
  else {
594
599
  const isObj = isObject(sourceValue);
@@ -659,7 +664,6 @@ function onChange(node, callback, options = {}) {
659
664
  node.listeners = listeners;
660
665
  }
661
666
  }
662
- checkActivate(node);
663
667
  const listener = {
664
668
  listener: callback,
665
669
  track: trackingType,
@@ -681,7 +685,14 @@ function onChange(node, callback, options = {}) {
681
685
  getPrevious: () => undefined,
682
686
  });
683
687
  }
684
- return () => listeners.delete(listener);
688
+ let extraDispose;
689
+ if (node.linkedToNode) {
690
+ extraDispose = onChange(node.linkedToNode, callback, options);
691
+ }
692
+ return () => {
693
+ listeners.delete(listener);
694
+ extraDispose === null || extraDispose === void 0 ? void 0 : extraDispose();
695
+ };
685
696
  }
686
697
 
687
698
  function setupTracking(nodes, update, noArgs, immediate) {
@@ -777,7 +788,7 @@ function trackSelector(selector, update, observeEvent, observeOptions, createRes
777
788
  dispose = setupTracking(nodes, updateFn, false, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.immediate);
778
789
  resubscribe = createResubscribe ? () => setupTracking(nodes, updateFn) : undefined;
779
790
  }
780
- return { value, dispose, resubscribe };
791
+ return { value, nodes, dispose, resubscribe };
781
792
  }
782
793
 
783
794
  function observe(selectorOrRun, reactionOrOptions, options) {
@@ -802,9 +813,11 @@ function observe(selectorOrRun, reactionOrOptions, options) {
802
813
  delete e.value;
803
814
  // Dispose listeners from previous run
804
815
  dispose === null || dispose === void 0 ? void 0 : dispose();
805
- const { dispose: _dispose, value } = trackSelector(selectorOrRun, update, e, options);
816
+ const { dispose: _dispose, value, nodes } = trackSelector(selectorOrRun, update, e, options);
806
817
  dispose = _dispose;
807
818
  e.value = value;
819
+ e.nodes = nodes;
820
+ e.refresh = update;
808
821
  if (e.onCleanupReaction) {
809
822
  e.onCleanupReaction();
810
823
  e.onCleanupReaction = undefined;
@@ -812,7 +825,9 @@ function observe(selectorOrRun, reactionOrOptions, options) {
812
825
  endBatch();
813
826
  // Call the reaction if there is one and the value changed
814
827
  if (reaction &&
815
- ((options === null || options === void 0 ? void 0 : options.fromComputed) || ((e.num > 0 || !isEvent(selectorOrRun)) && e.previous !== e.value))) {
828
+ ((options === null || options === void 0 ? void 0 : options.fromComputed) ||
829
+ ((e.num > 0 || !isEvent(selectorOrRun)) &&
830
+ (e.previous !== e.value || typeof e.value === 'object')))) {
816
831
  reaction(e);
817
832
  }
818
833
  // Update the previous value
@@ -832,6 +847,120 @@ function observe(selectorOrRun, reactionOrOptions, options) {
832
847
  };
833
848
  }
834
849
 
850
+ function _when(predicate, effect, checkReady) {
851
+ // If predicate is a regular Promise skip all the observable stuff
852
+ if (isPromise(predicate)) {
853
+ return effect ? predicate.then(effect) : predicate;
854
+ }
855
+ let value;
856
+ let effectValue;
857
+ // Create a wrapping fn that calls the effect if predicate returns true
858
+ function run(e) {
859
+ const ret = computeSelector(predicate);
860
+ if (!isPromise(ret) && (checkReady ? isObservableValueReady(ret) : ret)) {
861
+ value = ret;
862
+ // Set cancel so that observe does not track anymore
863
+ e.cancel = true;
864
+ }
865
+ return value;
866
+ }
867
+ function doEffect() {
868
+ // If value is truthy then run the effect
869
+ effectValue = effect === null || effect === void 0 ? void 0 : effect(value);
870
+ }
871
+ // Run in an observe
872
+ observe(run, doEffect);
873
+ // If first run resulted in a truthy value just return it.
874
+ // It will have set e.cancel so no need to dispose
875
+ if (isPromise(value)) {
876
+ return effect ? value.then(effect) : value;
877
+ }
878
+ else if (value !== undefined) {
879
+ return effect ? effectValue : Promise.resolve(value);
880
+ }
881
+ else {
882
+ // Wrap it in a promise
883
+ const promise = new Promise((resolve) => {
884
+ if (effect) {
885
+ const originalEffect = effect;
886
+ effect = ((value) => {
887
+ const effectValue = originalEffect(value);
888
+ resolve(isPromise(effectValue) ? effectValue.then((value) => value) : effectValue);
889
+ });
890
+ }
891
+ else {
892
+ effect = resolve;
893
+ }
894
+ });
895
+ return promise;
896
+ }
897
+ }
898
+ function when(predicate, effect) {
899
+ return _when(predicate, effect, false);
900
+ }
901
+ function whenReady(predicate, effect) {
902
+ return _when(predicate, effect, true);
903
+ }
904
+
905
+ function calculateRetryDelay(retryOptions, attemptNum) {
906
+ const { backoff, delay = 1000, infinite, times = 3, maxDelay = 30000 } = retryOptions;
907
+ if (infinite || attemptNum < times) {
908
+ const delayTime = Math.min(delay * (backoff === 'constant' ? 1 : 2 ** attemptNum), maxDelay);
909
+ return delayTime;
910
+ }
911
+ return null;
912
+ }
913
+ function createRetryTimeout(retryOptions, attemptNum, fn) {
914
+ const delayTime = calculateRetryDelay(retryOptions, attemptNum);
915
+ if (delayTime) {
916
+ return setTimeout(fn, delayTime);
917
+ }
918
+ }
919
+ function runWithRetry(node, state, fn) {
920
+ const { retry, waitFor } = node.activationState;
921
+ const e = { cancel: false };
922
+ let value = undefined;
923
+ if (waitFor) {
924
+ value = whenReady(waitFor, () => {
925
+ node.activationState.waitFor = undefined;
926
+ return fn(e);
927
+ });
928
+ }
929
+ else {
930
+ value = fn(e);
931
+ }
932
+ if (isPromise(value) && retry) {
933
+ let timeoutRetry;
934
+ return new Promise((resolve) => {
935
+ const run = () => {
936
+ value
937
+ .then((val) => {
938
+ node.activationState.persistedRetry = false;
939
+ resolve(val);
940
+ })
941
+ .catch(() => {
942
+ state.attemptNum++;
943
+ if (timeoutRetry) {
944
+ clearTimeout(timeoutRetry);
945
+ }
946
+ if (!e.cancel) {
947
+ timeoutRetry = createRetryTimeout(retry, state.attemptNum, () => {
948
+ value = fn(e);
949
+ run();
950
+ });
951
+ }
952
+ })
953
+ .finally(() => {
954
+ node.activationState.persistedRetry = false;
955
+ });
956
+ };
957
+ run();
958
+ });
959
+ }
960
+ return value;
961
+ }
962
+
963
+ const noop = () => { };
835
964
  const ArrayModifiers = new Set([
836
965
  'copyWithin',
837
966
  'fill',
@@ -872,7 +1001,8 @@ if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
872
1001
  }
873
1002
  function collectionSetter(node, target, prop, ...args) {
874
1003
  var _a;
875
- if (prop === 'push') {
1004
+ if (prop === 'push' && args.length === 1) {
1005
+ // Fast path for push to just append to the end
876
1006
  setKey(node, target.length + '', args[0]);
877
1007
  }
878
1008
  else {
@@ -924,8 +1054,9 @@ function updateNodes(parent, obj, prevValue) {
924
1054
  let prevChildrenById;
925
1055
  let moved;
926
1056
  const isMap = obj instanceof Map;
1057
+ const isPrevMap = prevValue instanceof Map;
927
1058
  const keys = getKeys(obj, isArr, isMap);
928
- const keysPrev = getKeys(prevValue, isArr, isMap);
1059
+ const keysPrev = getKeys(prevValue, isArr, isPrevMap);
929
1060
  const length = ((_a = (keys || obj)) === null || _a === void 0 ? void 0 : _a.length) || 0;
930
1061
  const lengthPrev = ((_b = (keysPrev || prevValue)) === null || _b === void 0 ? void 0 : _b.length) || 0;
931
1062
  let idField;
@@ -977,7 +1108,7 @@ function updateNodes(parent, obj, prevValue) {
977
1108
  if (!keys.includes(key)) {
978
1109
  hasADiff = true;
979
1110
  const child = getChildNode(parent, key);
980
- const prev = isMap ? prevValue.get(key) : prevValue[key];
1111
+ const prev = isPrevMap ? prevValue.get(key) : prevValue[key];
981
1112
  if (prev !== undefined) {
982
1113
  if (!isPrimitive(prev)) {
983
1114
  updateNodes(child, undefined, prev);
@@ -995,16 +1126,24 @@ function updateNodes(parent, obj, prevValue) {
995
1126
  let didMove = false;
996
1127
  for (let i = 0; i < length; i++) {
997
1128
  const key = isArr ? i + '' : keys[i];
998
- const value = isMap ? obj.get(key) : obj[key];
999
- const prev = isMap ? prevValue === null || prevValue === void 0 ? void 0 : prevValue.get(key) : prevValue === null || prevValue === void 0 ? void 0 : prevValue[key];
1000
- let isDiff = value !== prev;
1129
+ let value = isMap ? obj.get(key) : obj[key];
1130
+ const prev = isPrevMap ? prevValue === null || prevValue === void 0 ? void 0 : prevValue.get(key) : prevValue === null || prevValue === void 0 ? void 0 : prevValue[key];
1131
+ let isDiff = isDate(value) ? +value !== +prev : value !== prev;
1001
1132
  if (isDiff) {
1002
1133
  const id = idField && value
1003
1134
  ? isIdFieldFunction
1004
1135
  ? idField(value)
1005
1136
  : value[idField]
1006
1137
  : undefined;
1007
- let child = getChildNode(parent, key);
1138
+ if (isObservable(value)) {
1139
+ const obs = value;
1140
+ value = () => obs;
1141
+ }
1142
+ let child = getChildNode(parent, key, isFunction(value) ? value : undefined);
1143
+ if (!child.lazy && (isFunction(value) || isObservable(value))) {
1144
+ reactivateNode(child, value);
1145
+ peek(child);
1146
+ }
1008
1147
  // Detect moves within an array. Need to move the original proxy to the new position to keep
1009
1148
  // the proxy stable, so that listeners to this node will be unaffected by the array shift.
1010
1149
  if (isArr && id !== undefined) {
@@ -1034,7 +1173,10 @@ function updateNodes(parent, obj, prevValue) {
1034
1173
  if (isDiff) {
1035
1174
  // Array has a new / modified element
1036
1175
  // If object iterate through its children
1037
- if (isPrimitive(value)) {
1176
+ if (isFunction(value) || isObservable(value)) {
1177
+ extractFunctionOrComputed(parent, obj, key, value);
1178
+ }
1179
+ else if (isPrimitive(value)) {
1038
1180
  hasADiff = true;
1039
1181
  }
1040
1182
  else {
@@ -1082,6 +1224,15 @@ function getProxy(node, p, asFunction) {
1082
1224
  // Create a proxy if not already cached and return it
1083
1225
  return (node.proxy || (node.proxy = new Proxy(node, proxyHandler)));
1084
1226
  }
1227
+ function flushPending() {
1228
+ // Need to short circuit the computed batching because the user called get() or peek()
1229
+ // in which case the set needs to run immediately so that the values are up to date.
1230
+ if (globalState.pendingNodes.size > 0) {
1231
+ const nodes = Array.from(globalState.pendingNodes.values());
1232
+ globalState.pendingNodes.clear();
1233
+ nodes.forEach((fn) => fn());
1234
+ }
1235
+ }
1085
1236
  const proxyHandler = {
1086
1237
  get(node, p, receiver) {
1087
1238
  var _a;
@@ -1093,11 +1244,12 @@ const proxyHandler = {
1093
1244
  if (p === symbolGetNode) {
1094
1245
  return node;
1095
1246
  }
1096
- const value = peek(node);
1247
+ let value = peek(node);
1097
1248
  // If this node is linked to another observable then forward to the target's handler.
1098
1249
  // The exception is onChange because it needs to listen to this node for changes.
1099
1250
  // This needs to be below peek because it activates there.
1100
1251
  if (node.linkedToNode && p !== 'onChange') {
1252
+ updateTracking(node);
1101
1253
  return proxyHandler.get(node.linkedToNode, p, receiver);
1102
1254
  }
1103
1255
  if (value instanceof Map || value instanceof WeakMap || value instanceof Set || value instanceof WeakSet) {
@@ -1109,6 +1261,9 @@ const proxyHandler = {
1109
1261
  const fn = observableFns.get(p);
1110
1262
  // If this is an observable function, call it
1111
1263
  if (fn) {
1264
+ if (p === 'get' || p === 'peek') {
1265
+ flushPending();
1266
+ }
1112
1267
  return function (a, b, c) {
1113
1268
  const l = arguments.length;
1114
1269
  // Array call and apply are slow so micro-optimize this hot path.
@@ -1126,14 +1281,6 @@ const proxyHandler = {
1126
1281
  }
1127
1282
  };
1128
1283
  }
1129
- if (node.isComputed) {
1130
- if (node.proxyFn && !fn) {
1131
- return node.proxyFn(p);
1132
- }
1133
- else {
1134
- checkActivate(node);
1135
- }
1136
- }
1137
1284
  const property = observableProperties.get(p);
1138
1285
  if (property) {
1139
1286
  return property.get(node);
@@ -1152,7 +1299,7 @@ const proxyHandler = {
1152
1299
  }
1153
1300
  }
1154
1301
  // /TODOV3 Remove this
1155
- const vProp = value === null || value === void 0 ? void 0 : value[p];
1302
+ let vProp = value === null || value === void 0 ? void 0 : value[p];
1156
1303
  if (isObject(value) && value[symbolOpaque]) {
1157
1304
  return vProp;
1158
1305
  }
@@ -1165,6 +1312,11 @@ const proxyHandler = {
1165
1312
  return getProxy(node, p, fnOrComputed);
1166
1313
  }
1167
1314
  }
1315
+ if (isNullOrUndefined(value) && vProp === undefined && (ArrayModifiers.has(p) || ArrayLoopers.has(p))) {
1316
+ value = [];
1317
+ setNodeValue(node, value);
1318
+ vProp = value[p];
1319
+ }
1168
1320
  // Handle function calls
1169
1321
  if (isFunction(vProp)) {
1170
1322
  if (isArray(value)) {
@@ -1214,10 +1366,6 @@ const proxyHandler = {
1214
1366
  return vProp;
1215
1367
  }
1216
1368
  }
1217
- // TODOV3: Remove "state"
1218
- if (vProp === undefined && (p === 'state' || p === '_state') && node.state) {
1219
- return node.state;
1220
- }
1221
1369
  // Return an observable proxy to the property
1222
1370
  return getProxy(node, p);
1223
1371
  },
@@ -1282,7 +1430,7 @@ const proxyHandler = {
1282
1430
  },
1283
1431
  apply(target, thisArg, argArray) {
1284
1432
  // If it's a function call it as a function
1285
- return Reflect.apply(target, thisArg, argArray);
1433
+ return Reflect.apply(target.lazyFn || target, thisArg, argArray);
1286
1434
  },
1287
1435
  };
1288
1436
  function set(node, newValue) {
@@ -1295,7 +1443,7 @@ function set(node, newValue) {
1295
1443
  }
1296
1444
  function toggle(node) {
1297
1445
  const value = getNodeValue(node);
1298
- if (value === undefined || isBoolean(value)) {
1446
+ if (value === undefined || value === null || isBoolean(value)) {
1299
1447
  set(node, !value);
1300
1448
  return !value;
1301
1449
  }
@@ -1309,31 +1457,32 @@ function setKey(node, key, newValue, level) {
1309
1457
  console.warn(`[legend-state] Set an HTMLElement into state. You probably don't want to do that.`);
1310
1458
  }
1311
1459
  }
1312
- if (node.root.locked && !node.root.set) {
1313
- // This happens when modifying a locked observable such as a computed.
1314
- // If merging this could be happening deep in a hierarchy so we don't want to throw errors so we'll just do nothing.
1315
- // This could happen during persistence local load for example.
1316
- if (globalState.isMerging) {
1317
- return;
1318
- }
1319
- else {
1320
- throw new Error(process.env.NODE_ENV === 'development'
1321
- ? '[legend-state] Cannot modify an observable while it is locked. Please make sure that you unlock the observable before making changes.'
1322
- : '[legend-state] Modified locked observable');
1323
- }
1324
- }
1325
1460
  const isRoot = !node.parent && key === '_';
1461
+ if (node.parent && !getNodeValue(node)) {
1462
+ return set(node, { [key]: newValue });
1463
+ }
1326
1464
  // Get the child node for updating and notifying
1327
1465
  const childNode = isRoot ? node : getChildNode(node, key, isFunction(newValue) ? newValue : undefined);
1328
- // Set the raw value on the parent object
1329
- const { newValue: savedValue, prevValue, parentValue } = setNodeValue(childNode, newValue);
1330
- const isFunc = isFunction(savedValue);
1331
- const isPrim = isPrimitive(savedValue) || savedValue instanceof Date;
1332
- if (savedValue !== prevValue) {
1333
- updateNodesAndNotify(node, savedValue, prevValue, childNode, isPrim, isRoot, level);
1466
+ if (isObservable(newValue)) {
1467
+ setToObservable(childNode, newValue);
1468
+ }
1469
+ else {
1470
+ // Set the raw value on the parent object
1471
+ const { newValue: savedValue, prevValue, parentValue } = setNodeValue(childNode, newValue);
1472
+ const isFunc = isFunction(savedValue);
1473
+ const isPrim = isPrimitive(savedValue) || savedValue instanceof Date;
1474
+ if (savedValue !== prevValue) {
1475
+ updateNodesAndNotify(node, savedValue, prevValue, childNode, isPrim, isRoot, level);
1476
+ }
1477
+ if (!isPrim) {
1478
+ childNode.needsExtract = true;
1479
+ }
1480
+ extractFunctionOrComputed(node, parentValue, key, savedValue);
1481
+ if (isFunc) {
1482
+ return savedValue;
1483
+ }
1334
1484
  }
1335
- extractFunctionOrComputed(node, parentValue, key, savedValue);
1336
- return isFunc ? savedValue : isRoot ? getProxy(node) : getProxy(node, key);
1485
+ return isRoot ? getProxy(node) : getProxy(node, key);
1337
1486
  }
1338
1487
  function assign(node, value) {
1339
1488
  const proxy = getProxy(node);
@@ -1358,7 +1507,13 @@ function deleteFn(node, key) {
1358
1507
  key = node.key;
1359
1508
  node = node.parent;
1360
1509
  }
1361
- setKey(node, key !== null && key !== void 0 ? key : '_', symbolDelete, /*level*/ -1);
1510
+ const value = getNodeValue(node);
1511
+ if (isArray(value)) {
1512
+ collectionSetter(node, value, 'splice', key, 1);
1513
+ }
1514
+ else {
1515
+ setKey(node, key !== null && key !== void 0 ? key : '_', symbolDelete, /*level*/ -1);
1516
+ }
1362
1517
  }
1363
1518
  function handlerMapSet(node, p, value) {
1364
1519
  const vProp = value === null || value === void 0 ? void 0 : value[p];
@@ -1438,7 +1593,7 @@ function handlerMapSet(node, p, value) {
1438
1593
  function updateNodesAndNotify(node, newValue, prevValue, childNode, isPrim, isRoot, level) {
1439
1594
  if (!childNode)
1440
1595
  childNode = node;
1441
- // Make sure we don't call too many listeners for ever property set
1596
+ // Make sure we don't call too many listeners for every property set
1442
1597
  beginBatch();
1443
1598
  let hasADiff = isPrim;
1444
1599
  let whenOptimizedOnlyIf = false;
@@ -1468,30 +1623,41 @@ function extractPromise(node, value, setter) {
1468
1623
  value
1469
1624
  .then((value) => {
1470
1625
  setter ? setter({ value }) : set(node, value);
1471
- node.state.isLoaded.set(true);
1626
+ node.state.assign({
1627
+ isLoaded: true,
1628
+ error: undefined,
1629
+ });
1472
1630
  })
1473
1631
  .catch((error) => {
1474
1632
  node.state.error.set(error);
1475
1633
  });
1476
1634
  }
1477
1635
  function extractFunctionOrComputed(node, obj, k, v) {
1636
+ var _a;
1478
1637
  if (isPromise(v)) {
1479
1638
  const childNode = getChildNode(node, k);
1480
1639
  extractPromise(childNode, v);
1481
1640
  setNodeValue(childNode, undefined);
1482
1641
  }
1642
+ else if (isObservable(v)) {
1643
+ const value = getNodeValue(node);
1644
+ value[k] = () => v;
1645
+ extractFunction(node, k, value[k]);
1646
+ }
1483
1647
  else if (typeof v === 'function') {
1648
+ const childNode = (_a = node.children) === null || _a === void 0 ? void 0 : _a.get(k);
1484
1649
  extractFunction(node, k, v);
1485
- delete obj[k];
1650
+ // If child was previously activated, then peek the new linked observable to make sure it's activated
1651
+ if (childNode && !childNode.lazy) {
1652
+ if (isObservable(v)) {
1653
+ const vNode = getNode(v);
1654
+ peek(vNode);
1655
+ }
1656
+ }
1486
1657
  }
1487
1658
  else if (typeof v == 'object' && v !== null && v !== undefined) {
1488
- const childNode = getNode(v);
1489
- if (childNode === null || childNode === void 0 ? void 0 : childNode.isComputed) {
1490
- extractFunction(node, k, v, childNode);
1491
- delete obj[k];
1492
- }
1493
- else {
1494
- return true;
1659
+ if (isObservable(v)) {
1660
+ extractFunction(node, k, v);
1495
1661
  }
1496
1662
  }
1497
1663
  }
@@ -1502,181 +1668,348 @@ function get(node, options) {
1502
1668
  return peek(node);
1503
1669
  }
1504
1670
  function peek(node) {
1505
- const value = getNodeValue(node);
1671
+ if (node.dirtyFn) {
1672
+ node.dirtyFn();
1673
+ globalState.dirtyNodes.delete(node);
1674
+ node.dirtyFn = undefined;
1675
+ }
1676
+ let value = getNodeValue(node);
1506
1677
  // If node is not yet lazily computed go do that
1507
1678
  const lazy = node.lazy;
1508
1679
  if (lazy) {
1680
+ const lazyFn = node.lazyFn;
1509
1681
  delete node.lazy;
1510
- if (isFunction(node) || isFunction(lazy)) {
1511
- activateNodeFunction(node, lazy);
1682
+ if (isFunction(node) || isFunction(lazyFn)) {
1683
+ value = activateNodeFunction(node, lazyFn);
1512
1684
  }
1513
- else {
1514
- for (const key in value) {
1515
- if (hasOwnProperty.call(value, key)) {
1516
- extractFunctionOrComputed(node, value, key, value[key]);
1517
- }
1685
+ }
1686
+ if (lazy || node.needsExtract) {
1687
+ for (const key in value) {
1688
+ if (hasOwnProperty.call(value, key)) {
1689
+ extractFunctionOrComputed(node, value, key, value[key]);
1518
1690
  }
1519
1691
  }
1520
1692
  }
1521
- // Check if computed needs to activate
1522
- checkActivate(node);
1523
1693
  return value;
1524
1694
  }
1525
- function createNodeActivationParams(node) {
1526
- node.activationState = {
1527
- lastSync: {},
1528
- };
1529
- const state = node.activationState;
1530
- // The onSet function handles the observable being set
1531
- // and forwards the set elsewhere
1532
- const onSet = (onSetFnParam) => {
1533
- state.onSetFn = onSetFnParam;
1534
- };
1535
- // The onSet function handles the observable being set
1536
- // and forwards the set elsewhere
1537
- const updateLastSync = (fn) => {
1538
- state.lastSync.value = fn;
1539
- };
1540
- // The subscribe function runs a function that listens to
1541
- // a data source and sends updates into the observable
1542
- const subscribe = (fn) => {
1543
- if (!state.subscriber) {
1544
- state.subscriber = fn;
1545
- }
1546
- };
1547
- const cache = (fn) => {
1548
- if (!state.cacheOptions) {
1549
- state.cacheOptions = isFunction(fn) ? fn() : fn;
1550
- }
1551
- };
1552
- const retry = (params) => {
1553
- if (!state.retryOptions) {
1554
- state.retryOptions = params;
1555
- }
1556
- };
1557
- // The proxy function simply marks the node as a proxy with this function
1558
- // so that child nodes will be created with this function, and then simply
1559
- // activated as a function
1560
- const proxy = (fn) => {
1561
- node.proxyFn2 = fn;
1562
- };
1563
- return {
1564
- onSet,
1565
- proxy,
1566
- cache,
1567
- retry,
1568
- subscribe,
1569
- updateLastSync,
1570
- obs$: getProxy(node),
1571
- };
1695
+ function reactivateNode(node, lazyFn) {
1696
+ var _a, _b;
1697
+ (_a = node.activatedObserveDispose) === null || _a === void 0 ? void 0 : _a.call(node);
1698
+ node.activatedObserveDispose = undefined;
1699
+ (_b = node.linkedToNodeDispose) === null || _b === void 0 ? void 0 : _b.call(node);
1700
+ node.linkedToNodeDispose = undefined;
1701
+ node.linkedToNode = undefined;
1702
+ node.lazyFn = lazyFn;
1703
+ node.lazy = true;
1572
1704
  }
1573
1705
  function activateNodeFunction(node, lazyFn) {
1574
- let prevTarget$;
1575
- let curTarget$;
1706
+ // let prevTarget$: Observable<any>;
1707
+ // let curTarget$: Observable<any>;
1576
1708
  let update;
1577
- const activator = (isFunction(node) ? node : lazyFn);
1578
1709
  let wasPromise;
1579
- const refresh = () => node.state.refreshNum.set((v) => v + 1);
1580
- observe(() => {
1581
- const params = createNodeActivationParams(node);
1710
+ let ignoreThisUpdate;
1711
+ const activateFn = lazyFn;
1712
+ const doRetry = () => { var _a; return (_a = node.state) === null || _a === void 0 ? void 0 : _a.refreshNum.set((v) => v + 1); };
1713
+ let activatedValue;
1714
+ let disposes = [];
1715
+ let refreshFn;
1716
+ function markDirty() {
1717
+ node.dirtyFn = refreshFn;
1718
+ globalState.dirtyNodes.add(node);
1719
+ }
1720
+ node.activatedObserveDispose = observe(() => {
1721
+ var _a, _b, _c, _d, _e, _f;
1722
+ // const params = createNodeActivationParams(node);
1582
1723
  // Run the function at this node
1583
- let value = activator(params);
1584
- // If target is an observable, get() it to make sure we listen to its changes
1585
- // and set up an onSet to write changes back to it
1724
+ let value = activateFn();
1725
+ // If target is an observable, make this node a link to it
1586
1726
  if (isObservable(value)) {
1587
- prevTarget$ = curTarget$;
1588
- curTarget$ = value;
1589
- params.onSet(({ value: newValue, getPrevious }) => {
1590
- // Don't set the target observable if the target has changed since the last run
1591
- if (!prevTarget$ || curTarget$ === prevTarget$) {
1592
- // Set the node value back to what it was before before setting it.
1593
- // This is a workaround for linked objects because it might not notify
1594
- // if setting a property of an object
1595
- // TODO: Is there a way to not do this? Or at least only do it in a
1596
- // small subset of cases?
1597
- setNodeValue(getNode(curTarget$), getPrevious());
1598
- // Set the value on the curTarget
1599
- curTarget$.set(newValue);
1600
- }
1601
- });
1602
- // Get the value from the observable because we still want the raw value
1603
- // for the effect.
1604
- value = value.get();
1727
+ value = setToObservable(node, value);
1605
1728
  }
1606
- else {
1607
- wasPromise = isPromise(value) ? value : undefined;
1729
+ if (isFunction(value)) {
1730
+ value = value();
1731
+ }
1732
+ const activated = value === null || value === void 0 ? void 0 : value[symbolActivated];
1733
+ if (activated) {
1734
+ node.activationState = activated;
1735
+ value = undefined;
1608
1736
  }
1737
+ ignoreThisUpdate = false;
1738
+ wasPromise = isPromise(value);
1609
1739
  // Activate this node if not activated already (may be called recursively)
1610
1740
  // TODO: Is calling recursively bad? If so can it be fixed?
1611
1741
  if (!node.activated) {
1612
1742
  node.activated = true;
1743
+ const isCached = !!((_a = node.activationState) === null || _a === void 0 ? void 0 : _a.cache);
1744
+ wasPromise = wasPromise || !!isCached;
1613
1745
  const activateNodeFn = wasPromise ? globalState.activateNode : activateNodeBase;
1614
- update = activateNodeFn(node, refresh, !!wasPromise, value).update;
1615
- }
1616
- node.state.refreshNum.get();
1617
- return value;
1618
- }, ({ value }) => {
1619
- if (!globalState.isLoadingRemote$.peek()) {
1620
- if (wasPromise) {
1621
- // Extract the promise to make it set the value/error when it comes in
1622
- extractPromise(node, value, update);
1623
- // Set this to undefined only if it's replacing the activation function,
1624
- // so we don't overwrite it if it already has real data from either local
1625
- // cache or a previous run
1626
- if (isFunction(getNodeValue(node))) {
1627
- setNodeValue(node, undefined);
1746
+ const { update: newUpdate, value: newValue } = activateNodeFn(node, doRetry, !!wasPromise, value);
1747
+ update = newUpdate;
1748
+ value = newValue !== null && newValue !== void 0 ? newValue : activated === null || activated === void 0 ? void 0 : activated.initial;
1749
+ }
1750
+ else if (node.activationState) {
1751
+ if (!node.activationState.persistedRetry && !node.activationState.waitFor) {
1752
+ const activated = node.activationState;
1753
+ if ((_c = (_b = node.state) === null || _b === void 0 ? void 0 : _b.peek()) === null || _c === void 0 ? void 0 : _c.sync) {
1754
+ node.state.sync();
1755
+ ignoreThisUpdate = true;
1756
+ }
1757
+ else {
1758
+ value =
1759
+ (_e = (_d = activated.get) === null || _d === void 0 ? void 0 : _d.call(activated, {
1760
+ updateLastSync: noop,
1761
+ setMode: noop,
1762
+ lastSync: undefined,
1763
+ value: undefined,
1764
+ refresh: doRetry,
1765
+ })) !== null && _e !== void 0 ? _e : activated.initial;
1628
1766
  }
1629
1767
  }
1630
1768
  else {
1631
- set(node, value);
1632
- node.state.isLoaded.set(true);
1769
+ ignoreThisUpdate = true;
1770
+ }
1771
+ }
1772
+ // value is undefined if it's in a persisted retry
1773
+ wasPromise = wasPromise || isPromise(value);
1774
+ get(getNode((_f = node.state) === null || _f === void 0 ? void 0 : _f.refreshNum));
1775
+ return value;
1776
+ }, (e) => {
1777
+ if (!ignoreThisUpdate) {
1778
+ const { value, nodes, refresh } = e;
1779
+ refreshFn = refresh;
1780
+ if (!wasPromise || !globalState.isLoadingRemote$.peek()) {
1781
+ if (wasPromise) {
1782
+ if (node.activationState) {
1783
+ const { initial } = node.activationState;
1784
+ if (value && isPromise(value)) {
1785
+ // Extract the promise to make it set the value/error when it comes in
1786
+ extractPromise(node, value, update);
1787
+ }
1788
+ // Set this to undefined only if it's replacing the activation function,
1789
+ // so we don't overwrite it if it already has real data from either local
1790
+ // cache or a previous run
1791
+ if (isFunction(getNodeValue(node))) {
1792
+ setNodeValue(node, initial !== null && initial !== void 0 ? initial : undefined);
1793
+ }
1794
+ }
1795
+ else if (node.activated) {
1796
+ // Extract the promise to make it set the value/error when it comes in
1797
+ extractPromise(node, value, update);
1798
+ // Set this to undefined only if it's replacing the activation function,
1799
+ // so we don't overwrite it if it already has real data from either local
1800
+ // cache or a previous run
1801
+ if (isFunction(getNodeValue(node))) {
1802
+ setNodeValue(node, undefined);
1803
+ }
1804
+ }
1805
+ }
1806
+ else {
1807
+ activatedValue = value;
1808
+ if (node.state.isLoaded.peek()) {
1809
+ set(node, value);
1810
+ }
1811
+ else {
1812
+ setNodeValue(node, value);
1813
+ node.state.assign({
1814
+ isLoaded: true,
1815
+ error: undefined,
1816
+ });
1817
+ }
1818
+ }
1633
1819
  }
1820
+ disposes.forEach((fn) => fn());
1821
+ disposes = [];
1822
+ nodes === null || nodes === void 0 ? void 0 : nodes.forEach(({ node }) => {
1823
+ disposes.push(onChange(node, markDirty, { immediate: true }));
1824
+ });
1634
1825
  }
1635
- }, { immediate: true, fromComputed: true });
1826
+ e.cancel = true;
1827
+ }, { fromComputed: true });
1828
+ return activatedValue;
1636
1829
  }
1637
- const activateNodeBase = (globalState.activateNode = function activateNodeBase(node, refresh, wasPromise) {
1638
- const { onSetFn, subscriber } = node.activationState;
1639
- let isSetting = false;
1830
+ const activateNodeBase = (globalState.activateNode = function activateNodeBase(node, refresh, wasPromise, value) {
1640
1831
  if (!node.state) {
1641
1832
  node.state = createObservable({
1642
1833
  isLoaded: false,
1643
1834
  }, false, extractPromise, getProxy);
1644
1835
  }
1645
- if (onSetFn) {
1646
- const doSet = (params) => {
1647
- // Don't call the set if this is the first value coming in
1648
- if (!isSetting) {
1649
- if ((!wasPromise || node.state.isLoaded.get()) &&
1650
- (params.changes.length > 1 || !isFunction(params.changes[0].prevAtPath))) {
1651
- isSetting = true;
1652
- batch(() => onSetFn(params, { update }), () => {
1653
- isSetting = false;
1654
- });
1836
+ let isSetting = false;
1837
+ let isSettingFromSubscribe = false;
1838
+ let _mode = 'set';
1839
+ if (node.activationState) {
1840
+ const { onSet, subscribe, get: getFn, initial } = node.activationState;
1841
+ value = getFn
1842
+ ? runWithRetry(node, { attemptNum: 0 }, () => {
1843
+ return getFn({
1844
+ updateLastSync: noop,
1845
+ setMode: (mode) => (_mode = mode),
1846
+ lastSync: undefined,
1847
+ value: undefined,
1848
+ refresh,
1849
+ });
1850
+ })
1851
+ : undefined;
1852
+ // TODO Should this have lastSync and value somehow?
1853
+ if (value == undefined || value === null) {
1854
+ value = initial;
1855
+ }
1856
+ if (onSet) {
1857
+ let allChanges = [];
1858
+ let latestValue = undefined;
1859
+ let runNumber = 0;
1860
+ const runChanges = (listenerParams) => {
1861
+ // Don't call the set if this is the first value coming in
1862
+ if (allChanges.length > 0) {
1863
+ let changes;
1864
+ let value;
1865
+ let getPrevious;
1866
+ if (listenerParams) {
1867
+ changes = listenerParams.changes;
1868
+ value = listenerParams.value;
1869
+ getPrevious = listenerParams.getPrevious;
1870
+ }
1871
+ else {
1872
+ // If this is called by flushPending then get the change array
1873
+ // that we've been building up.
1874
+ changes = allChanges;
1875
+ value = latestValue;
1876
+ getPrevious = createPreviousHandler(value, changes);
1877
+ }
1878
+ allChanges = [];
1879
+ latestValue = undefined;
1880
+ globalState.pendingNodes.delete(node);
1881
+ runNumber++;
1882
+ const thisRunNumber = runNumber;
1883
+ const run = () => {
1884
+ if (thisRunNumber !== runNumber) {
1885
+ // set may get called multiple times before it loads so ignore any previous runs
1886
+ return;
1887
+ }
1888
+ const retryAttempts = { attemptNum: 0 };
1889
+ return runWithRetry(node, retryAttempts, (eventRetry) => {
1890
+ const cancelRetry = () => {
1891
+ eventRetry.cancel = true;
1892
+ };
1893
+ return new Promise((resolve, reject) => {
1894
+ isSetting = true;
1895
+ let isProm = false;
1896
+ batch(() => {
1897
+ try {
1898
+ const val = onSet({
1899
+ value,
1900
+ changes,
1901
+ getPrevious,
1902
+ node,
1903
+ update,
1904
+ refresh,
1905
+ retryNum: retryAttempts.attemptNum,
1906
+ cancelRetry,
1907
+ fromSubscribe: isSettingFromSubscribe,
1908
+ });
1909
+ isProm = isPromise(val);
1910
+ if (isProm) {
1911
+ val.then(resolve).catch(reject);
1912
+ }
1913
+ }
1914
+ catch (e) {
1915
+ reject(e);
1916
+ }
1917
+ }, () => {
1918
+ if (!isProm) {
1919
+ isSetting = false;
1920
+ resolve();
1921
+ }
1922
+ });
1923
+ });
1924
+ });
1925
+ };
1926
+ whenReady(node.state.isLoaded, run);
1655
1927
  }
1656
- }
1657
- };
1658
- onChange(node, doSet, wasPromise ? undefined : { immediate: true });
1659
- }
1660
- if (process.env.NODE_ENV === 'development' && node.activationState.cacheOptions) {
1661
- // TODO Better message
1662
- console.log('[legend-state] Using cacheOptions without setting up persistence first');
1663
- }
1664
- if (process.env.NODE_ENV === 'development' && node.activationState.retryOptions) {
1665
- // TODO Better message
1666
- console.log('[legend-state] Using retryOptions without setting up persistence first');
1928
+ };
1929
+ const onChangeImmediate = ({ value, changes }) => {
1930
+ if (!isSetting || isSettingFromSubscribe) {
1931
+ if (changes.length > 1 || !isFunction(changes[0].prevAtPath)) {
1932
+ latestValue = value;
1933
+ if (allChanges.length > 0) {
1934
+ changes = changes.filter((change) => !isArraySubset(allChanges[0].path, change.path));
1935
+ }
1936
+ allChanges.push(...changes);
1937
+ globalState.pendingNodes.set(node, runChanges);
1938
+ }
1939
+ }
1940
+ };
1941
+ // Create an immediate listener to mark this node as pending. Then actually run
1942
+ // the changes at the end of the batch so everything is properly batched.
1943
+ // However, this can be short circuited if the user calls get() or peek()
1944
+ // in which case the set needs to run immediately so that the values are up to date.
1945
+ onChange(node, onChangeImmediate, { immediate: true });
1946
+ onChange(node, runChanges);
1947
+ }
1948
+ if (process.env.NODE_ENV === 'development' && node.activationState.cache) {
1949
+ // TODO Better message
1950
+ console.log('[legend-state] Using cache without setting up persistence first');
1951
+ }
1952
+ if (process.env.NODE_ENV === 'development' && node.activationState.retry) {
1953
+ // TODO Better message
1954
+ console.log('[legend-state] Using retry without setting up persistence first');
1955
+ }
1956
+ if (subscribe) {
1957
+ const updateFromSubscribe = (params) => {
1958
+ whenReady(node.state.isLoaded, () => {
1959
+ isSettingFromSubscribe = true;
1960
+ update(params);
1961
+ isSettingFromSubscribe = false;
1962
+ });
1963
+ };
1964
+ subscribe({ node, update: updateFromSubscribe, refresh });
1965
+ }
1667
1966
  }
1668
- const update = ({ value }) => {
1967
+ const update = ({ value, mode }) => {
1669
1968
  // TODO: This isSetting might not be necessary? Tests still work if removing it.
1670
1969
  // Write tests that would break it if removed? I'd guess a combination of subscribe and
1671
1970
  if (!isSetting) {
1672
- set(node, value);
1971
+ isSetting = true;
1972
+ if (_mode === 'assign' || mode === 'assign') {
1973
+ assign(node, value);
1974
+ }
1975
+ else if (_mode === 'merge' || mode === 'merge') {
1976
+ mergeIntoObservable(getProxy(node), value);
1977
+ }
1978
+ else {
1979
+ set(node, value);
1980
+ }
1981
+ isSetting = false;
1673
1982
  }
1674
1983
  };
1675
- if (subscriber) {
1676
- subscriber({ update, refresh });
1677
- }
1678
- return { update };
1984
+ return { update, value };
1679
1985
  });
1986
+ function setToObservable(node, value) {
1987
+ var _a;
1988
+ // If the computed is a proxy to another observable
1989
+ // link it to the target observable
1990
+ const linkedNode = getNode(value);
1991
+ if (linkedNode !== node && (linkedNode === null || linkedNode === void 0 ? void 0 : linkedNode.linkedToNode) !== node) {
1992
+ const prevNode = node.linkedToNode;
1993
+ node.linkedToNode = linkedNode;
1994
+ if (!linkedNode.linkedFromNodes) {
1995
+ linkedNode.linkedFromNodes = new Set();
1996
+ }
1997
+ linkedNode.linkedFromNodes.add(node);
1998
+ peek(linkedNode);
1999
+ (_a = node.linkedToNodeDispose) === null || _a === void 0 ? void 0 : _a.call(node);
2000
+ node.linkedToNodeDispose = onChange(linkedNode, () => {
2001
+ value = peek(linkedNode);
2002
+ set(node, value);
2003
+ }, { initial: true });
2004
+ // If the target observable is different then notify for the change
2005
+ if (prevNode) {
2006
+ const value = getNodeValue(linkedNode);
2007
+ const prevValue = getNodeValue(prevNode);
2008
+ notify(node, value, prevValue, 0);
2009
+ }
2010
+ }
2011
+ return value;
2012
+ }
1680
2013
 
1681
2014
  const fns = ['get', 'set', 'peek', 'onChange', 'toggle'];
1682
2015
  function ObservablePrimitiveClass(node) {
@@ -1693,8 +2026,14 @@ function proto(key, fn) {
1693
2026
  return fn.call(this, this._node, ...args);
1694
2027
  };
1695
2028
  }
1696
- proto('peek', peek);
1697
- proto('get', get);
2029
+ proto('peek', (node) => {
2030
+ flushPending();
2031
+ return peek(node);
2032
+ });
2033
+ proto('get', (node, options) => {
2034
+ flushPending();
2035
+ return get(node, options);
2036
+ });
1698
2037
  proto('set', set);
1699
2038
  proto('onChange', onChange);
1700
2039
  // Getters
@@ -1706,7 +2045,7 @@ Object.defineProperty(ObservablePrimitiveClass.prototype, symbolGetNode, {
1706
2045
  });
1707
2046
  ObservablePrimitiveClass.prototype.toggle = function () {
1708
2047
  const value = this.peek();
1709
- if (value === undefined || isBoolean(value)) {
2048
+ if (value === undefined || value === null || isBoolean(value)) {
1710
2049
  this.set(!value);
1711
2050
  }
1712
2051
  else if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
@@ -1725,89 +2064,25 @@ function observable(value) {
1725
2064
  function observablePrimitive(value) {
1726
2065
  return createObservable(value, true, extractPromise, getProxy, ObservablePrimitiveClass);
1727
2066
  }
1728
- globalState.isLoadingRemote$ = observable(false);
1729
-
1730
- function computed(compute, set$1) {
1731
- // Create an observable for this computed variable
1732
- const obs = observable();
1733
- lockObservable(obs, true);
2067
+ function syncState(obs) {
1734
2068
  const node = getNode(obs);
1735
- node.isComputed = true;
1736
- let isSetAfterActivated = false;
1737
- const setInner = function (val) {
1738
- const prevNode = node.linkedToNode;
1739
- // If it was previously linked to a node remove self
1740
- // from its linkedFromNodes
1741
- if (prevNode) {
1742
- prevNode.linkedFromNodes.delete(node);
1743
- node.linkedToNode = undefined;
1744
- }
1745
- const { parentOther } = node;
1746
- if (isObservable(val)) {
1747
- // If the computed is a proxy to another observable
1748
- // link it to the target observable
1749
- const linkedNode = getNode(val);
1750
- node.linkedToNode = linkedNode;
1751
- if (!linkedNode.linkedFromNodes) {
1752
- linkedNode.linkedFromNodes = new Set();
1753
- }
1754
- linkedNode.linkedFromNodes.add(node);
1755
- if (node.parentOther) {
1756
- onChange(linkedNode, ({ value }) => {
1757
- setNodeValue(node.parentOther, value);
1758
- }, { initial: true });
1759
- }
1760
- // If the target observable is different then notify for the change
1761
- if (prevNode) {
1762
- const value = getNodeValue(linkedNode);
1763
- const prevValue = getNodeValue(prevNode);
1764
- notify(node, value, prevValue, 0);
1765
- }
1766
- }
1767
- else if (val !== obs.peek()) {
1768
- // Unlock computed node before setting the value
1769
- lockObservable(obs, false);
1770
- const setter = isSetAfterActivated ? set : setNodeValue;
1771
- // Update the computed value
1772
- setter(node, val);
1773
- // If the computed is a child of an observable set the value on it
1774
- if (parentOther) {
1775
- let didUnlock = false;
1776
- if (parentOther.root.locked) {
1777
- parentOther.root.locked = false;
1778
- didUnlock = true;
1779
- }
1780
- setter(parentOther, val);
1781
- if (didUnlock) {
1782
- parentOther.root.locked = true;
1783
- }
1784
- }
1785
- // Re-lock the computed node
1786
- lockObservable(obs, true);
1787
- }
1788
- else if (parentOther) {
1789
- setNodeValue(parentOther, val);
1790
- }
1791
- isSetAfterActivated = true;
1792
- };
1793
- // Lazily activate the observable when get is called
1794
- node.root.activate = () => {
1795
- node.root.activate = undefined;
1796
- observe(compute, ({ value }) => {
1797
- if (isPromise(value)) {
1798
- value.then((v) => setInner(v));
1799
- }
1800
- else {
1801
- setInner(value);
1802
- }
1803
- }, { immediate: true, fromComputed: true });
1804
- };
1805
- if (set$1) {
1806
- node.root.set = (value) => {
1807
- batch(() => set$1(value));
1808
- };
2069
+ if (!node.state) {
2070
+ peek(node);
1809
2071
  }
1810
- return obs;
2072
+ if (!node.state) {
2073
+ node.state = observable({});
2074
+ }
2075
+ return node.state;
2076
+ }
2077
+ globalState.isLoadingRemote$ = observable(false);
2078
+
2079
+ function computed(compute, set) {
2080
+ return observable(set
2081
+ ? activated({
2082
+ get: compute,
2083
+ onSet: ({ value }) => set(value),
2084
+ })
2085
+ : compute);
1811
2086
  }
1812
2087
 
1813
2088
  function configureLegendState({ observableFunctions, observableProperties: observableProperties$1, }) {
@@ -1861,86 +2136,14 @@ function event() {
1861
2136
  }
1862
2137
 
1863
2138
  function proxy(get, set) {
1864
- // Create an observable for this computed variable
1865
- const obs = observable({});
1866
- lockObservable(obs, true);
1867
- const mapTargets = new Map();
1868
- const node = getNode(obs);
1869
- node.isComputed = true;
1870
- node.proxyFn = (key) => {
1871
- let target = mapTargets.get(key);
1872
- if (!target) {
1873
- // Note: Coercing typescript to allow undefined for set in computed because we don't want the public interface to allow undefined
1874
- target = computed(() => get(key), (set ? (value) => set(key, value) : undefined));
1875
- mapTargets.set(key, target);
1876
- extractFunction(node, key, target, getNode(target));
1877
- if (node.parentOther) {
1878
- onChange(getNode(target), ({ value, getPrevious }) => {
1879
- const previous = getPrevious();
1880
- // Set the raw value on the proxy's parent
1881
- setNodeValue(node.parentOther, node.root._);
1882
- // Notify the proxy
1883
- notify(getChildNode(node, key), value, previous, 0);
1884
- });
1885
- }
1886
- }
1887
- return target;
1888
- };
1889
- return obs;
1890
- }
1891
-
1892
- function _when(predicate, effect, checkReady) {
1893
- // If predicate is a regular Promise skip all the observable stuff
1894
- if (isPromise(predicate)) {
1895
- return effect ? predicate.then(effect) : predicate;
1896
- }
1897
- let value;
1898
- // Create a wrapping fn that calls the effect if predicate returns true
1899
- function run(e) {
1900
- const ret = computeSelector(predicate);
1901
- if (!isPromise(ret) && (checkReady ? isObservableValueReady(ret) : ret)) {
1902
- value = ret;
1903
- // Set cancel so that observe does not track anymore
1904
- e.cancel = true;
1905
- }
1906
- return value;
1907
- }
1908
- function doEffect() {
1909
- // If value is truthy then run the effect
1910
- effect === null || effect === void 0 ? void 0 : effect(value);
1911
- }
1912
- // Run in an observe
1913
- observe(run, doEffect);
1914
- // If first run resulted in a truthy value just return it.
1915
- // It will have set e.cancel so no need to dispose
1916
- if (isPromise(value)) {
1917
- return effect ? value.then(effect) : value;
1918
- }
1919
- else if (value !== undefined) {
1920
- return Promise.resolve(value);
1921
- }
1922
- else {
1923
- // Wrap it in a promise
1924
- const promise = new Promise((resolve) => {
1925
- if (effect) {
1926
- const originalEffect = effect;
1927
- effect = (value) => {
1928
- const effectValue = originalEffect(value);
1929
- resolve(effectValue);
1930
- };
1931
- }
1932
- else {
1933
- effect = resolve;
1934
- }
1935
- });
1936
- return promise;
1937
- }
1938
- }
1939
- function when(predicate, effect) {
1940
- return _when(predicate, effect, false);
1941
- }
1942
- function whenReady(predicate, effect) {
1943
- return _when(predicate, effect, true);
2139
+ return observable(activated({
2140
+ lookup: (key) => set
2141
+ ? activated({
2142
+ get: () => get(key),
2143
+ onSet: ({ value }) => set(key, value),
2144
+ })
2145
+ : get(key),
2146
+ }));
1944
2147
  }
1945
2148
 
1946
2149
  const internal = {
@@ -1953,17 +2156,19 @@ const internal = {
1953
2156
  observableFns,
1954
2157
  optimized,
1955
2158
  peek,
2159
+ runWithRetry,
1956
2160
  set,
1957
2161
  setAtPath,
1958
2162
  setNodeValue,
2163
+ symbolActivated,
1959
2164
  symbolDelete,
1960
2165
  };
1961
2166
 
1962
2167
  exports.ObservablePrimitiveClass = ObservablePrimitiveClass;
2168
+ exports.activated = activated;
1963
2169
  exports.batch = batch;
1964
2170
  exports.beginBatch = beginBatch;
1965
2171
  exports.beginTracking = beginTracking;
1966
- exports.checkActivate = checkActivate;
1967
2172
  exports.computeSelector = computeSelector;
1968
2173
  exports.computed = computed;
1969
2174
  exports.configureLegendState = configureLegendState;
@@ -1982,8 +2187,10 @@ exports.hasOwnProperty = hasOwnProperty;
1982
2187
  exports.internal = internal;
1983
2188
  exports.isArray = isArray;
1984
2189
  exports.isBoolean = isBoolean;
2190
+ exports.isDate = isDate;
1985
2191
  exports.isEmpty = isEmpty;
1986
2192
  exports.isFunction = isFunction;
2193
+ exports.isNullOrUndefined = isNullOrUndefined;
1987
2194
  exports.isObject = isObject;
1988
2195
  exports.isObservable = isObservable;
1989
2196
  exports.isObservableValueReady = isObservableValueReady;
@@ -1991,7 +2198,6 @@ exports.isPrimitive = isPrimitive;
1991
2198
  exports.isPromise = isPromise;
1992
2199
  exports.isString = isString;
1993
2200
  exports.isSymbol = isSymbol;
1994
- exports.lockObservable = lockObservable;
1995
2201
  exports.mergeIntoObservable = mergeIntoObservable;
1996
2202
  exports.observable = observable;
1997
2203
  exports.observablePrimitive = observablePrimitive;
@@ -2004,6 +2210,7 @@ exports.setInObservableAtPath = setInObservableAtPath;
2004
2210
  exports.setSilently = setSilently;
2005
2211
  exports.setupTracking = setupTracking;
2006
2212
  exports.symbolDelete = symbolDelete;
2213
+ exports.syncState = syncState;
2007
2214
  exports.trackSelector = trackSelector;
2008
2215
  exports.tracking = tracking;
2009
2216
  exports.updateTracking = updateTracking;