@legendapp/state 1.11.1 → 2.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/CHANGELOG.md +0 -4
  2. package/index.d.ts +2 -3
  3. package/index.js +220 -226
  4. package/index.js.map +1 -1
  5. package/index.mjs +220 -224
  6. package/index.mjs.map +1 -1
  7. package/package.json +21 -24
  8. package/persist-plugins/async-storage.d.ts +14 -0
  9. package/persist-plugins/async-storage.js +100 -0
  10. package/persist-plugins/async-storage.js.map +1 -0
  11. package/persist-plugins/async-storage.mjs +98 -0
  12. package/persist-plugins/async-storage.mjs.map +1 -0
  13. package/persist-plugins/fetch.d.ts +10 -0
  14. package/persist-plugins/fetch.js +22 -0
  15. package/persist-plugins/fetch.js.map +1 -0
  16. package/persist-plugins/fetch.mjs +20 -0
  17. package/persist-plugins/fetch.mjs.map +1 -0
  18. package/persist-plugins/firebase.d.ts +51 -0
  19. package/persist-plugins/firebase.js +692 -0
  20. package/persist-plugins/firebase.js.map +1 -0
  21. package/persist-plugins/firebase.mjs +690 -0
  22. package/persist-plugins/firebase.mjs.map +1 -0
  23. package/persist-plugins/indexeddb.d.ts +2 -3
  24. package/persist-plugins/indexeddb.js +1 -16
  25. package/persist-plugins/indexeddb.js.map +1 -1
  26. package/persist-plugins/indexeddb.mjs +1 -16
  27. package/persist-plugins/indexeddb.mjs.map +1 -1
  28. package/persist-plugins/local-storage.d.ts +1 -2
  29. package/persist-plugins/local-storage.js +1 -5
  30. package/persist-plugins/local-storage.js.map +1 -1
  31. package/persist-plugins/local-storage.mjs +1 -5
  32. package/persist-plugins/local-storage.mjs.map +1 -1
  33. package/persist-plugins/mmkv.d.ts +4 -4
  34. package/persist-plugins/mmkv.js +6 -4
  35. package/persist-plugins/mmkv.js.map +1 -1
  36. package/persist-plugins/mmkv.mjs +6 -4
  37. package/persist-plugins/mmkv.mjs.map +1 -1
  38. package/persist-plugins/query.d.ts +20 -0
  39. package/persist-plugins/query.js +89 -0
  40. package/persist-plugins/query.js.map +1 -0
  41. package/persist-plugins/query.mjs +87 -0
  42. package/persist-plugins/query.mjs.map +1 -0
  43. package/persist.js +170 -143
  44. package/persist.js.map +1 -1
  45. package/persist.mjs +171 -144
  46. package/persist.mjs.map +1 -1
  47. package/react-hooks/usePersistedObservable.d.ts +2 -2
  48. package/react-hooks/usePersistedObservable.js +1 -6
  49. package/react-hooks/usePersistedObservable.js.map +1 -1
  50. package/react-hooks/usePersistedObservable.mjs +1 -6
  51. package/react-hooks/usePersistedObservable.mjs.map +1 -1
  52. package/react.d.ts +1 -6
  53. package/react.js +47 -99
  54. package/react.js.map +1 -1
  55. package/react.mjs +47 -100
  56. package/react.mjs.map +1 -1
  57. package/src/ObservableObject.d.ts +4 -0
  58. package/src/batching.d.ts +0 -7
  59. package/src/globals.d.ts +1 -5
  60. package/src/is.d.ts +1 -0
  61. package/src/observable.d.ts +5 -3
  62. package/src/observableInterfaces.d.ts +93 -69
  63. package/src/observe.d.ts +1 -1
  64. package/src/persist/observablePersistRemoteFunctionsAdapter.d.ts +2 -0
  65. package/src/persist/persistObservable.d.ts +10 -13
  66. package/src/persist-plugins/async-storage.d.ts +14 -0
  67. package/src/persist-plugins/fetch.d.ts +10 -0
  68. package/src/persist-plugins/firebase.d.ts +51 -0
  69. package/src/persist-plugins/indexeddb.d.ts +2 -3
  70. package/src/persist-plugins/local-storage.d.ts +1 -2
  71. package/src/persist-plugins/mmkv.d.ts +4 -4
  72. package/src/persist-plugins/query.d.ts +20 -0
  73. package/src/react/For.d.ts +1 -1
  74. package/src/react/react-globals.d.ts +3 -0
  75. package/src/react/reactInterfaces.d.ts +1 -1
  76. package/src/react/reactive-observer.d.ts +1 -5
  77. package/src/react/useObservableState.d.ts +2 -0
  78. package/src/react/useSelector.d.ts +1 -1
  79. package/src/react/useWhen.d.ts +3 -0
  80. package/src/react-hooks/usePersistedObservable.d.ts +2 -2
  81. package/src/trackSelector.d.ts +1 -1
  82. package/src/tracking.d.ts +2 -3
  83. package/config/disableDeprecationWarnings.d.ts +0 -1
  84. package/config/disableDeprecationWarnings.js +0 -10
  85. package/config/disableDeprecationWarnings.js.map +0 -1
  86. package/config/disableDeprecationWarnings.mjs +0 -8
  87. package/config/disableDeprecationWarnings.mjs.map +0 -1
  88. package/config/enableReactDirectRender.d.ts +0 -7
  89. package/config/enableReactDirectRender.js +0 -77
  90. package/config/enableReactDirectRender.js.map +0 -1
  91. package/config/enableReactDirectRender.mjs +0 -74
  92. package/config/enableReactDirectRender.mjs.map +0 -1
  93. package/react-components.d.ts +0 -2
  94. package/react-components.js +0 -44
  95. package/react-components.js.map +0 -1
  96. package/react-components.mjs +0 -31
  97. package/react-components.mjs.map +0 -1
  98. package/react-native-components.d.ts +0 -2
  99. package/react-native-components.js +0 -67
  100. package/react-native-components.js.map +0 -1
  101. package/react-native-components.mjs +0 -54
  102. package/react-native-components.mjs.map +0 -1
  103. package/src/config/disableDeprecationWarnings.d.ts +0 -1
  104. package/src/config/enableReactDirectRender.d.ts +0 -7
  105. package/src/react/enableLegendStateReact.d.ts +0 -1
  106. package/src/react-components/react-components.d.ts +0 -7
  107. package/src/react-native-components/rn-components.d.ts +0 -21
  108. package/types.d.ts +0 -13
package/index.js CHANGED
@@ -54,25 +54,20 @@ let trackCount = 0;
54
54
  const trackingQueue = [];
55
55
  const tracking = {
56
56
  current: undefined,
57
- inRender: false,
58
57
  };
59
- function beginTracking(inRender) {
58
+ function beginTracking() {
60
59
  // Keep a copy of the previous tracking context so it can be restored
61
60
  // when this context is complete
62
61
  trackingQueue.push(tracking.current);
63
62
  trackCount++;
64
- tracking.inRender = inRender;
65
63
  tracking.current = {};
66
64
  }
67
- function endTracking(fromRender) {
65
+ function endTracking() {
68
66
  // Restore the previous tracking context
69
67
  trackCount--;
70
68
  if (trackCount < 0) {
71
69
  trackCount = 0;
72
70
  }
73
- if (fromRender) {
74
- tracking.inRender = false;
75
- }
76
71
  tracking.current = trackingQueue.pop();
77
72
  }
78
73
  function updateTracking(node, track) {
@@ -99,22 +94,18 @@ const symbolGetNode = Symbol('getNode');
99
94
  const symbolDelete = /* @__PURE__ */ Symbol('delete');
100
95
  const symbolOpaque = Symbol('opaque');
101
96
  const optimized = Symbol('optimized');
102
- const extraPrimitiveActivators = new Map();
103
- const extraPrimitiveProps = new Map();
104
- const __devExtractFunctionsAndComputedsNodes = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test' ? new Set() : undefined;
105
97
  const globalState = {
106
98
  isLoadingLocal: false,
107
99
  isLoadingRemote: false,
108
100
  isMerging: false,
109
- noDepWarn: false,
110
101
  };
111
102
  function checkActivate(node) {
112
103
  var _a;
113
104
  const root = node.root;
114
105
  (_a = root.activate) === null || _a === void 0 ? void 0 : _a.call(root);
115
- if (root.computedChildrenNeedingActivation) {
116
- root.computedChildrenNeedingActivation.forEach(checkActivate);
117
- delete root.computedChildrenNeedingActivation;
106
+ if (root.toActivate) {
107
+ root.toActivate.forEach(checkActivate);
108
+ delete root.toActivate;
118
109
  }
119
110
  }
120
111
  function getNode(obs) {
@@ -165,7 +156,7 @@ function setNodeValue(node, newValue) {
165
156
  if (parentNode.root.locked && parentNode.root.set) {
166
157
  parentNode.root.set(parentNode.root._);
167
158
  }
168
- return { prevValue, newValue };
159
+ return { prevValue, newValue, parentValue };
169
160
  }
170
161
  const arrNodeKeys = [];
171
162
  function getNodeValue(node) {
@@ -239,37 +230,11 @@ function extractFunction(node, key, fnOrComputed, computedChildNode) {
239
230
  }
240
231
  node.functions.set(key, fnOrComputed);
241
232
  if (computedChildNode) {
242
- computedChildNode.computedChildOfNode = getChildNode(node, key);
243
- if (!node.root.computedChildrenNeedingActivation) {
244
- node.root.computedChildrenNeedingActivation = [];
245
- }
246
- node.root.computedChildrenNeedingActivation.push(computedChildNode);
247
- }
248
- }
249
- function extractFunctionsAndComputeds(obj, node) {
250
- if ((process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') &&
251
- typeof __devExtractFunctionsAndComputedsNodes !== 'undefined') {
252
- if (__devExtractFunctionsAndComputedsNodes.has(obj)) {
253
- console.error('[legend-state] Circular reference detected in object. You may want to use opaqueObject to stop traversing child nodes.', obj);
254
- return false;
255
- }
256
- __devExtractFunctionsAndComputedsNodes.add(obj);
257
- }
258
- for (const k in obj) {
259
- const v = obj[k];
260
- if (typeof v === 'function') {
261
- extractFunction(node, k, v);
262
- }
263
- else if (typeof v == 'object' && v !== null && v !== undefined) {
264
- const childNode = getNode(v);
265
- if (childNode === null || childNode === void 0 ? void 0 : childNode.isComputed) {
266
- extractFunction(node, k, v, childNode);
267
- delete obj[k];
268
- }
269
- else if (!v[symbolOpaque]) {
270
- extractFunctionsAndComputeds(obj[k], getChildNode(node, k));
271
- }
233
+ computedChildNode.parentOther = getChildNode(node, key);
234
+ if (!node.root.toActivate) {
235
+ node.root.toActivate = [];
272
236
  }
237
+ node.root.toActivate.push(computedChildNode);
273
238
  }
274
239
  }
275
240
 
@@ -278,6 +243,7 @@ let numInBatch = 0;
278
243
  let isRunningBatch = false;
279
244
  let didDelayEndBatch = false;
280
245
  let _afterBatch = [];
246
+ let _queuedBatches = [];
281
247
  let _batchMap = new Map();
282
248
  function onActionTimeout() {
283
249
  if (_batchMap.size > 0) {
@@ -399,11 +365,7 @@ function batchNotifyChanges(changesInBatch, immediate) {
399
365
  const listenerFn = arr[i];
400
366
  const { track, noArgs, listener } = listenerFn;
401
367
  if (!listenersNotified.has(listener)) {
402
- const ok = track === true || track === 'shallow'
403
- ? level <= 0
404
- : track === optimized
405
- ? whenOptimizedOnlyIf && level <= 0
406
- : true;
368
+ const ok = track === true ? level <= 0 : track === optimized ? whenOptimizedOnlyIf && level <= 0 : true;
407
369
  // Notify if listener is not shallow or if this is the first level
408
370
  if (ok) {
409
371
  // Create listenerParams if not already created
@@ -425,6 +387,8 @@ function batchNotifyChanges(changesInBatch, immediate) {
425
387
  });
426
388
  }
427
389
  function runBatch() {
390
+ // Save batch locally and reset _batchMap first because a new batch could begin while looping over callbacks.
391
+ // This can happen with observableComputed for example.
428
392
  const map = _batchMap;
429
393
  _batchMap = new Map();
430
394
  const changesInBatch = new Map();
@@ -439,7 +403,15 @@ function runBatch() {
439
403
  }
440
404
  function batch(fn, onComplete) {
441
405
  if (onComplete) {
442
- _afterBatch.push(onComplete);
406
+ // If there's an onComplete we need a batch that's fully isolated from others to ensure it wraps only the given changes.
407
+ // So if already batching, push this batch onto a queue and run it after the current batch is fully done.
408
+ if (isRunningBatch) {
409
+ _queuedBatches.push([fn, onComplete]);
410
+ return;
411
+ }
412
+ else {
413
+ _afterBatch.push(onComplete);
414
+ }
443
415
  }
444
416
  beginBatch();
445
417
  try {
@@ -489,29 +461,21 @@ function endBatch(force) {
489
461
  didDelayEndBatch = false;
490
462
  endBatch(true);
491
463
  }
464
+ const queued = _queuedBatches;
465
+ if (queued.length) {
466
+ _queuedBatches = [];
467
+ for (let i = 0; i < queued.length; i++) {
468
+ const [fn, onComplete] = queued[i];
469
+ batch(fn, onComplete);
470
+ }
471
+ }
492
472
  }
493
473
  }
494
474
  }
495
- function afterBatch(fn) {
496
- if (numInBatch > 0) {
497
- _afterBatch.push(fn);
498
- }
499
- else {
500
- fn();
501
- }
502
- }
503
475
 
504
476
  function onChange(node, callback, options = {}) {
505
477
  const { initial, immediate, noArgs } = options;
506
- let { trackingType } = options;
507
- // Temporary migration of string to symbol
508
- // TODOV2 remove this
509
- if (trackingType === 'optimize') {
510
- if (process.env.NODE_ENV === 'development' && !globalState.noDepWarn) {
511
- console.warn('[legend-state]: "optimize" prop is deprecated and will be removed in version 2.0. Please import { optimize } from "@legendapp/state" and use that instead.');
512
- }
513
- trackingType = optimized;
514
- }
478
+ const { trackingType } = options;
515
479
  let listeners = immediate ? node.listenersImmediate : node.listeners;
516
480
  if (!listeners) {
517
481
  listeners = new Set();
@@ -529,11 +493,6 @@ function onChange(node, callback, options = {}) {
529
493
  noArgs,
530
494
  };
531
495
  listeners.add(listener);
532
- let parent = node.parent;
533
- while (parent && !parent.descendantHasListener) {
534
- parent.descendantHasListener = true;
535
- parent = parent.parent;
536
- }
537
496
  if (initial) {
538
497
  const value = getNodeValue(node);
539
498
  callback({
@@ -606,7 +565,11 @@ function collectionSetter(node, target, prop, ...args) {
606
565
  // Return the original value
607
566
  return ret;
608
567
  }
568
+ function getKeys(obj, isArr, isMap) {
569
+ return isArr ? undefined : obj ? (isMap ? Array.from(obj.keys()) : Object.keys(obj)) : [];
570
+ }
609
571
  function updateNodes(parent, obj, prevValue) {
572
+ var _a, _b;
610
573
  if ((process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') &&
611
574
  typeof __devUpdateNodes !== 'undefined' &&
612
575
  isObject(obj)) {
@@ -635,8 +598,10 @@ function updateNodes(parent, obj, prevValue) {
635
598
  let prevChildrenById;
636
599
  let moved;
637
600
  const isMap = obj instanceof Map;
638
- const keys = obj ? (isMap ? Array.from(obj.keys()) : Object.keys(obj)) : [];
639
- const keysPrev = prevValue ? (isMap ? Array.from(prevValue.keys()) : Object.keys(prevValue)) : [];
601
+ const keys = getKeys(obj, isArr, isMap);
602
+ const keysPrev = getKeys(prevValue, isArr, isMap);
603
+ const length = ((_a = (keys || obj)) === null || _a === void 0 ? void 0 : _a.length) || 0;
604
+ const lengthPrev = ((_b = (keysPrev || prevValue)) === null || _b === void 0 ? void 0 : _b.length) || 0;
640
605
  let idField;
641
606
  let isIdFieldFunction;
642
607
  let hasADiff = false;
@@ -699,79 +664,77 @@ function updateNodes(parent, obj, prevValue) {
699
664
  }
700
665
  }
701
666
  if (obj && !isPrimitive(obj)) {
702
- const length = keys.length;
703
- hasADiff = hasADiff || (keys === null || keys === void 0 ? void 0 : keys.length) !== (keysPrev === null || keysPrev === void 0 ? void 0 : keysPrev.length);
667
+ hasADiff = hasADiff || length !== lengthPrev;
704
668
  const isArrDiff = hasADiff;
705
669
  let didMove = false;
706
- if (parent.descendantHasListener || !hasADiff) {
707
- for (let i = 0; i < length; i++) {
708
- const key = keys[i];
709
- const value = isMap ? obj.get(key) : obj[key];
710
- const prev = isMap ? prevValue === null || prevValue === void 0 ? void 0 : prevValue.get(key) : prevValue === null || prevValue === void 0 ? void 0 : prevValue[key];
711
- let isDiff = value !== prev;
712
- if (isDiff) {
713
- const id = idField && value
714
- ? isIdFieldFunction
715
- ? idField(value)
716
- : value[idField]
717
- : undefined;
718
- let child = getChildNode(parent, key);
719
- // Detect moves within an array. Need to move the original proxy to the new position to keep
720
- // the proxy stable, so that listeners to this node will be unaffected by the array shift.
721
- if (isArr && id !== undefined) {
722
- // Find the previous position of this element in the array
723
- const prevChild = id !== undefined ? prevChildrenById === null || prevChildrenById === void 0 ? void 0 : prevChildrenById.get(id) : undefined;
724
- if (!prevChild) {
725
- // This id was not in the array before so it does not need to notify children
726
- isDiff = false;
727
- hasADiff = true;
728
- }
729
- else if (prevChild !== undefined && prevChild.key !== key) {
730
- const valuePrevChild = prevValue[prevChild.key];
731
- // If array length changed then move the original node to the current position.
732
- // That should be faster than notifying every single element that
733
- // it's in a new position.
734
- if (isArrDiff) {
735
- child = prevChild;
736
- parent.children.delete(child.key);
737
- child.key = key;
738
- moved.push([key, child]);
739
- }
740
- didMove = true;
741
- // And check for diff against the previous value in the previous position
742
- isDiff = valuePrevChild !== value;
743
- }
670
+ for (let i = 0; i < length; i++) {
671
+ const key = isArr ? i + '' : keys[i];
672
+ const value = isMap ? obj.get(key) : obj[key];
673
+ const prev = isMap ? prevValue === null || prevValue === void 0 ? void 0 : prevValue.get(key) : prevValue === null || prevValue === void 0 ? void 0 : prevValue[key];
674
+ let isDiff = value !== prev;
675
+ if (isDiff) {
676
+ extractFunctionOrComputed(parent, obj, key, value);
677
+ const id = idField && value
678
+ ? isIdFieldFunction
679
+ ? idField(value)
680
+ : value[idField]
681
+ : undefined;
682
+ let child = getChildNode(parent, key);
683
+ // Detect moves within an array. Need to move the original proxy to the new position to keep
684
+ // the proxy stable, so that listeners to this node will be unaffected by the array shift.
685
+ if (isArr && id !== undefined) {
686
+ // Find the previous position of this element in the array
687
+ const prevChild = id !== undefined ? prevChildrenById === null || prevChildrenById === void 0 ? void 0 : prevChildrenById.get(id) : undefined;
688
+ if (!prevChild) {
689
+ // This id was not in the array before so it does not need to notify children
690
+ isDiff = false;
691
+ hasADiff = true;
744
692
  }
745
- if (isDiff) {
746
- // Array has a new / modified element
747
- // If object iterate through its children
748
- if (isPrimitive(value)) {
749
- hasADiff = true;
750
- }
751
- else {
752
- // Always need to updateNodes so we notify through all children
753
- const updatedNodes = (!hasADiff || !!child.descendantHasListener) && updateNodes(child, value, prev);
754
- hasADiff = hasADiff || updatedNodes;
693
+ else if (prevChild !== undefined && prevChild.key !== key) {
694
+ const valuePrevChild = prevValue[prevChild.key];
695
+ // If array length changed then move the original node to the current position.
696
+ // That should be faster than notifying every single element that
697
+ // it's in a new position.
698
+ if (isArrDiff) {
699
+ child = prevChild;
700
+ parent.children.delete(child.key);
701
+ child.key = key;
702
+ moved.push([key, child]);
755
703
  }
704
+ didMove = true;
705
+ // And check for diff against the previous value in the previous position
706
+ isDiff = valuePrevChild !== value;
756
707
  }
757
- if (isDiff || !isArrDiff) {
758
- // Notify for this child if this element is different and it has listeners
759
- // Or if the position changed in an array whose length did not change
760
- // But do not notify child if the parent is an array with changing length -
761
- // the array's listener will cover it
762
- if (child.listeners || child.listenersImmediate) {
763
- notify(child, value, prev, 0, !isArrDiff);
764
- }
708
+ }
709
+ if (isDiff) {
710
+ // Array has a new / modified element
711
+ // If object iterate through its children
712
+ if (isPrimitive(value)) {
713
+ hasADiff = true;
714
+ }
715
+ else {
716
+ // Always need to updateNodes so we notify through all children
717
+ const updatedNodes = updateNodes(child, value, prev);
718
+ hasADiff = hasADiff || updatedNodes;
765
719
  }
766
720
  }
767
- }
768
- if (moved) {
769
- for (let i = 0; i < moved.length; i++) {
770
- const [key, child] = moved[i];
771
- parent.children.set(key, child);
721
+ if (isDiff || !isArrDiff) {
722
+ // Notify for this child if this element is different and it has listeners
723
+ // Or if the position changed in an array whose length did not change
724
+ // But do not notify child if the parent is an array with changing length -
725
+ // the array's listener will cover it
726
+ if (child.listeners || child.listenersImmediate) {
727
+ notify(child, value, prev, 0, !isArrDiff);
728
+ }
772
729
  }
773
730
  }
774
731
  }
732
+ if (moved) {
733
+ for (let i = 0; i < moved.length; i++) {
734
+ const [key, child] = moved[i];
735
+ parent.children.set(key, child);
736
+ }
737
+ }
775
738
  // The full array does not need to re-render if the length is the same
776
739
  // So don't notify shallow listeners
777
740
  retValue = hasADiff || didMove;
@@ -849,18 +812,6 @@ const proxyHandler = {
849
812
  if (property) {
850
813
  return property.get(node);
851
814
  }
852
- const isValuePrimitive = isPrimitive(value);
853
- // If accessing a key that doesn't already exist, and this node has been activated with extra keys
854
- // then return the values that were set. This is used by enableLegendStateReact for example.
855
- if (value === undefined || value === null || isValuePrimitive) {
856
- if (extraPrimitiveProps.size && (node.isActivatedPrimitive || extraPrimitiveActivators.has(p))) {
857
- node.isActivatedPrimitive = true;
858
- const vPrim = extraPrimitiveProps.get(p);
859
- if (vPrim !== undefined) {
860
- return isFunction(vPrim) ? vPrim(getProxy(node)) : vPrim;
861
- }
862
- }
863
- }
864
815
  const vProp = value === null || value === void 0 ? void 0 : value[p];
865
816
  if (isObject(value) && value[symbolOpaque]) {
866
817
  return vProp;
@@ -984,10 +935,7 @@ const proxyHandler = {
984
935
  },
985
936
  };
986
937
  function set(node, newValue) {
987
- if (isPromise(newValue)) {
988
- newValue.then((v) => set(node, v)).catch((error) => set(node, { error }));
989
- }
990
- else if (node.parent) {
938
+ if (node.parent) {
991
939
  return setKey(node.parent, node.key, newValue);
992
940
  }
993
941
  else {
@@ -1027,12 +975,13 @@ function setKey(node, key, newValue, level) {
1027
975
  // Get the child node for updating and notifying
1028
976
  const childNode = isRoot ? node : getChildNode(node, key);
1029
977
  // Set the raw value on the parent object
1030
- const { newValue: savedValue, prevValue } = setNodeValue(childNode, newValue);
1031
- const isFunc = isFunction(newValue);
978
+ const { newValue: savedValue, prevValue, parentValue } = setNodeValue(childNode, newValue);
979
+ const isFunc = isFunction(savedValue);
1032
980
  const isPrim = isPrimitive(savedValue) || savedValue instanceof Date;
1033
981
  if (savedValue !== prevValue) {
1034
982
  updateNodesAndNotify(node, savedValue, prevValue, childNode, isPrim, isRoot, level);
1035
983
  }
984
+ extractFunctionOrComputed(node, parentValue, key, savedValue);
1036
985
  return isFunc ? savedValue : isRoot ? getProxy(node) : getProxy(node, key);
1037
986
  }
1038
987
  function assign(node, value) {
@@ -1159,6 +1108,51 @@ function updateNodesAndNotify(node, newValue, prevValue, childNode, isPrim, isRo
1159
1108
  }
1160
1109
  endBatch();
1161
1110
  }
1111
+ function extractPromise(node, value) {
1112
+ value.status = 'pending';
1113
+ value
1114
+ .then((value) => {
1115
+ set(node, value);
1116
+ })
1117
+ .catch((error) => {
1118
+ set(node, { error, status: 'rejected' });
1119
+ });
1120
+ }
1121
+ const __devExtractFunctionsAndComputedsNodes = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test' ? new Set() : undefined;
1122
+ function extractFunctionOrComputed(node, obj, k, v) {
1123
+ if (isPromise(v)) {
1124
+ extractPromise(getChildNode(node, k), v);
1125
+ }
1126
+ else if (typeof v === 'function') {
1127
+ extractFunction(node, k, v);
1128
+ }
1129
+ else if (typeof v == 'object' && v !== null && v !== undefined) {
1130
+ const childNode = getNode(v);
1131
+ if (childNode === null || childNode === void 0 ? void 0 : childNode.isComputed) {
1132
+ extractFunction(node, k, v, childNode);
1133
+ delete obj[k];
1134
+ }
1135
+ else {
1136
+ return true;
1137
+ }
1138
+ }
1139
+ }
1140
+ function extractFunctionsAndComputeds(node, obj) {
1141
+ if ((process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') &&
1142
+ typeof __devExtractFunctionsAndComputedsNodes !== 'undefined') {
1143
+ if (__devExtractFunctionsAndComputedsNodes.has(obj)) {
1144
+ console.error('[legend-state] Circular reference detected in object. You may want to use opaqueObject to stop traversing child nodes.', obj);
1145
+ return false;
1146
+ }
1147
+ __devExtractFunctionsAndComputedsNodes.add(obj);
1148
+ }
1149
+ for (const k in obj) {
1150
+ const v = obj[k];
1151
+ if (v && extractFunctionOrComputed(node, obj, k, v) && !v[symbolOpaque]) {
1152
+ extractFunctionsAndComputeds(getChildNode(node, k), v);
1153
+ }
1154
+ }
1155
+ }
1162
1156
 
1163
1157
  function isObservable(obs) {
1164
1158
  return obs && !!obs[symbolGetNode];
@@ -1249,53 +1243,50 @@ function setInObservableAtPath(obs, path, value, mode) {
1249
1243
  function mergeIntoObservable(target, ...sources) {
1250
1244
  beginBatch();
1251
1245
  globalState.isMerging = true;
1252
- const value = _mergeIntoObservable(target, ...sources);
1246
+ for (let i = 0; i < sources.length; i++) {
1247
+ target = _mergeIntoObservable(target, sources[i]);
1248
+ }
1253
1249
  globalState.isMerging = false;
1254
1250
  endBatch();
1255
- return value;
1251
+ return target;
1256
1252
  }
1257
- function _mergeIntoObservable(target, ...sources) {
1253
+ function _mergeIntoObservable(target, source) {
1258
1254
  var _a;
1259
- if (!sources.length)
1260
- return target;
1261
- for (let u = 0; u < sources.length; u++) {
1262
- const source = sources[u];
1263
- const needsSet = isObservable(target);
1264
- const targetValue = needsSet ? target.peek() : target;
1265
- const isTargetArr = isArray(targetValue);
1266
- const isTargetObj = !isTargetArr && isObject(targetValue);
1267
- if ((isTargetObj && isObject(source) && !isEmpty(targetValue)) ||
1268
- (isTargetArr && isArray(source) && targetValue.length > 0)) {
1269
- for (const key in source) {
1270
- const sourceValue = source[key];
1271
- if (sourceValue === symbolDelete) {
1272
- needsSet && ((_a = target[key]) === null || _a === void 0 ? void 0 : _a.delete)
1273
- ? target[key].delete()
1274
- : delete target[key];
1275
- }
1276
- else {
1277
- const isObj = isObject(sourceValue);
1278
- const isArr = !isObj && isArray(sourceValue);
1279
- const targetChild = target[key];
1280
- if ((isObj || isArr) && targetChild && (needsSet || !isEmpty(targetChild))) {
1281
- if (!needsSet && (!targetChild || (isObj ? !isObject(targetChild) : !isArray(targetChild)))) {
1282
- target[key] = sourceValue;
1283
- }
1284
- else {
1285
- _mergeIntoObservable(targetChild, sourceValue);
1286
- }
1255
+ const needsSet = isObservable(target);
1256
+ const targetValue = needsSet ? target.peek() : target;
1257
+ const isTargetArr = isArray(targetValue);
1258
+ const isTargetObj = !isTargetArr && isObject(targetValue);
1259
+ if ((isTargetObj && isObject(source) && !isEmpty(targetValue)) ||
1260
+ (isTargetArr && isArray(source) && targetValue.length > 0)) {
1261
+ const keys = Object.keys(source);
1262
+ for (let i = 0; i < keys.length; i++) {
1263
+ const key = keys[i];
1264
+ const sourceValue = source[key];
1265
+ if (sourceValue === symbolDelete) {
1266
+ needsSet && ((_a = target[key]) === null || _a === void 0 ? void 0 : _a.delete) ? target[key].delete() : delete target[key];
1267
+ }
1268
+ else {
1269
+ const isObj = isObject(sourceValue);
1270
+ const isArr = !isObj && isArray(sourceValue);
1271
+ const targetChild = target[key];
1272
+ if ((isObj || isArr) && targetChild && (needsSet || !isEmpty(targetChild))) {
1273
+ if (!needsSet && (!targetChild || (isObj ? !isObject(targetChild) : !isArray(targetChild)))) {
1274
+ target[key] = sourceValue;
1287
1275
  }
1288
1276
  else {
1289
- needsSet
1290
- ? targetChild.set(sourceValue)
1291
- : (target[key] = sourceValue);
1277
+ _mergeIntoObservable(targetChild, sourceValue);
1292
1278
  }
1293
1279
  }
1280
+ else {
1281
+ needsSet
1282
+ ? targetChild.set(sourceValue)
1283
+ : (target[key] = sourceValue);
1284
+ }
1294
1285
  }
1295
1286
  }
1296
- else if (source !== undefined) {
1297
- needsSet ? target.set(source) : (target = source);
1298
- }
1287
+ }
1288
+ else if (source !== undefined) {
1289
+ needsSet ? target.set(source) : (target = source);
1299
1290
  }
1300
1291
  return target;
1301
1292
  }
@@ -1374,7 +1365,7 @@ ObservablePrimitiveClass.prototype.delete = function () {
1374
1365
  function createObservable(value, makePrimitive) {
1375
1366
  const valueIsPromise = isPromise(value);
1376
1367
  const root = {
1377
- _: valueIsPromise ? undefined : value,
1368
+ _: value,
1378
1369
  };
1379
1370
  const node = {
1380
1371
  root,
@@ -1384,19 +1375,14 @@ function createObservable(value, makePrimitive) {
1384
1375
  ? new ObservablePrimitiveClass(node)
1385
1376
  : getProxy(node);
1386
1377
  if (valueIsPromise) {
1387
- value.catch((error) => {
1388
- obs.set({ error });
1389
- });
1390
- value.then((value) => {
1391
- obs.set(value);
1392
- });
1378
+ extractPromise(node, value);
1393
1379
  }
1394
1380
  else if (!prim) {
1395
1381
  if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
1396
1382
  __devExtractFunctionsAndComputedsNodes.clear();
1397
1383
  }
1398
1384
  if (value) {
1399
- extractFunctionsAndComputeds(value, node);
1385
+ extractFunctionsAndComputeds(node, value);
1400
1386
  }
1401
1387
  }
1402
1388
  return obs;
@@ -1425,7 +1411,7 @@ function setupTracking(nodes, update, noArgs, immediate) {
1425
1411
  };
1426
1412
  }
1427
1413
 
1428
- function trackSelector(selector, update, observeEvent, observeOptions, createResubscribe, inRender) {
1414
+ function trackSelector(selector, update, observeEvent, observeOptions, createResubscribe) {
1429
1415
  var _a;
1430
1416
  let nodes;
1431
1417
  let value;
@@ -1441,11 +1427,11 @@ function trackSelector(selector, update, observeEvent, observeOptions, createRes
1441
1427
  }
1442
1428
  else {
1443
1429
  // Compute the selector inside a tracking context
1444
- beginTracking(inRender);
1430
+ beginTracking();
1445
1431
  value = selector ? computeSelector(selector, observeEvent, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.retainObservable) : selector;
1446
1432
  tracker = tracking.current;
1447
1433
  nodes = tracker.nodes;
1448
- endTracking(inRender);
1434
+ endTracking();
1449
1435
  if ((process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') && tracker && nodes) {
1450
1436
  (_a = tracker.traceListeners) === null || _a === void 0 ? void 0 : _a.call(tracker, nodes);
1451
1437
  if (tracker.traceUpdates) {
@@ -1496,6 +1482,7 @@ function observe(selectorOrRun, reactionOrOptions, options) {
1496
1482
  e.onCleanupReaction();
1497
1483
  e.onCleanupReaction = undefined;
1498
1484
  }
1485
+ endBatch();
1499
1486
  // Call the reaction if there is one and the value changed
1500
1487
  if (reaction && (e.num > 0 || !isEvent(selectorOrRun)) && e.previous !== e.value) {
1501
1488
  reaction(e);
@@ -1504,7 +1491,6 @@ function observe(selectorOrRun, reactionOrOptions, options) {
1504
1491
  e.previous = e.value;
1505
1492
  // Increment the counter
1506
1493
  e.num++;
1507
- endBatch();
1508
1494
  };
1509
1495
  update();
1510
1496
  // Return function calling dispose because dispose may be changed in update()
@@ -1533,7 +1519,7 @@ function computed(compute, set$1) {
1533
1519
  prevNode.linkedFromNodes.delete(node);
1534
1520
  node.linkedToNode = undefined;
1535
1521
  }
1536
- const childNode = node.computedChildOfNode;
1522
+ const { parentOther } = node;
1537
1523
  if (isObservable(val)) {
1538
1524
  // If the computed is a proxy to another observable
1539
1525
  // link it to the target observable
@@ -1543,9 +1529,9 @@ function computed(compute, set$1) {
1543
1529
  linkedNode.linkedFromNodes = new Set();
1544
1530
  }
1545
1531
  linkedNode.linkedFromNodes.add(node);
1546
- if (node.computedChildOfNode) {
1532
+ if (node.parentOther) {
1547
1533
  onChange(linkedNode, ({ value }) => {
1548
- setNodeValue(node.computedChildOfNode, value);
1534
+ setNodeValue(node.parentOther, value);
1549
1535
  }, { initial: true });
1550
1536
  }
1551
1537
  // If the target observable is different then notify for the change
@@ -1562,22 +1548,22 @@ function computed(compute, set$1) {
1562
1548
  // Update the computed value
1563
1549
  setter(node, val);
1564
1550
  // If the computed is a child of an observable set the value on it
1565
- if (childNode) {
1551
+ if (parentOther) {
1566
1552
  let didUnlock = false;
1567
- if (childNode.root.locked) {
1568
- childNode.root.locked = false;
1553
+ if (parentOther.root.locked) {
1554
+ parentOther.root.locked = false;
1569
1555
  didUnlock = true;
1570
1556
  }
1571
- setter(childNode, val);
1557
+ setter(parentOther, val);
1572
1558
  if (didUnlock) {
1573
- childNode.root.locked = true;
1559
+ parentOther.root.locked = true;
1574
1560
  }
1575
1561
  }
1576
1562
  // Re-lock the computed node
1577
1563
  lockObservable(obs, true);
1578
1564
  }
1579
- else if (childNode) {
1580
- setNodeValue(childNode, val);
1565
+ else if (parentOther) {
1566
+ setNodeValue(parentOther, val);
1581
1567
  }
1582
1568
  isSetAfterActivated = true;
1583
1569
  };
@@ -1665,11 +1651,11 @@ function proxy(get, set) {
1665
1651
  target = computed(() => get(key), (set ? (value) => set(key, value) : undefined));
1666
1652
  mapTargets.set(key, target);
1667
1653
  extractFunction(node, key, target, getNode(target));
1668
- if (node.computedChildOfNode) {
1654
+ if (node.parentOther) {
1669
1655
  onChange(getNode(target), ({ value, getPrevious }) => {
1670
1656
  const previous = getPrevious();
1671
1657
  // Set the raw value on the proxy's parent
1672
- setNodeValue(node.computedChildOfNode, node.root._);
1658
+ setNodeValue(node.parentOther, node.root._);
1673
1659
  // Notify the proxy
1674
1660
  notify(getChildNode(node, key), value, previous, 0);
1675
1661
  });
@@ -1681,23 +1667,33 @@ function proxy(get, set) {
1681
1667
  }
1682
1668
 
1683
1669
  function _when(predicate, effect, checkReady) {
1670
+ // If predicate is a regular Promise skip all the observable stuff
1671
+ if (isPromise(predicate)) {
1672
+ return effect ? predicate.then(effect) : predicate;
1673
+ }
1684
1674
  let value;
1685
1675
  // Create a wrapping fn that calls the effect if predicate returns true
1686
1676
  function run(e) {
1687
1677
  const ret = computeSelector(predicate);
1688
- if (checkReady ? isObservableValueReady(ret) : ret) {
1678
+ if (!isPromise(ret) && (checkReady ? isObservableValueReady(ret) : ret)) {
1689
1679
  value = ret;
1690
- // If value is truthy then run the effect
1691
- effect === null || effect === void 0 ? void 0 : effect(ret);
1692
- // Set cancel so that observe does not track
1680
+ // Set cancel so that observe does not track anymore
1693
1681
  e.cancel = true;
1694
1682
  }
1683
+ return value;
1684
+ }
1685
+ function doEffect() {
1686
+ // If value is truthy then run the effect
1687
+ effect === null || effect === void 0 ? void 0 : effect(value);
1695
1688
  }
1696
1689
  // Run in an observe
1697
- observe(run);
1690
+ observe(run, doEffect);
1698
1691
  // If first run resulted in a truthy value just return it.
1699
1692
  // It will have set e.cancel so no need to dispose
1700
- if (value !== undefined) {
1693
+ if (isPromise(value)) {
1694
+ return effect ? value.then(effect) : value;
1695
+ }
1696
+ else if (value !== undefined) {
1701
1697
  return Promise.resolve(value);
1702
1698
  }
1703
1699
  else {
@@ -1740,7 +1736,6 @@ const internal = {
1740
1736
  };
1741
1737
 
1742
1738
  exports.ObservablePrimitiveClass = ObservablePrimitiveClass;
1743
- exports.afterBatch = afterBatch;
1744
1739
  exports.batch = batch;
1745
1740
  exports.beginBatch = beginBatch;
1746
1741
  exports.beginTracking = beginTracking;
@@ -1753,12 +1748,11 @@ exports.deconstructObjectWithPath = deconstructObjectWithPath;
1753
1748
  exports.endBatch = endBatch;
1754
1749
  exports.endTracking = endTracking;
1755
1750
  exports.event = event;
1756
- exports.extraPrimitiveActivators = extraPrimitiveActivators;
1757
- exports.extraPrimitiveProps = extraPrimitiveProps;
1758
1751
  exports.findIDKey = findIDKey;
1759
1752
  exports.getNode = getNode;
1760
1753
  exports.getNodeValue = getNodeValue;
1761
1754
  exports.getObservableIndex = getObservableIndex;
1755
+ exports.hasOwnProperty = hasOwnProperty;
1762
1756
  exports.internal = internal;
1763
1757
  exports.isArray = isArray;
1764
1758
  exports.isBoolean = isBoolean;