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