@legendapp/state 2.1.0 → 2.2.0-next.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.
package/index.mjs CHANGED
@@ -58,9 +58,18 @@ const extraPrimitiveActivators = new Map();
58
58
  const extraPrimitiveProps = new Map();
59
59
  const globalState = {
60
60
  isLoadingLocal: false,
61
- isLoadingRemote: false,
62
61
  isMerging: false,
62
+ isLoadingRemote$: undefined,
63
+ onChangeRemote: undefined,
64
+ activateNode: undefined,
63
65
  };
66
+ function isObservable(obs) {
67
+ return !!obs && !!obs[symbolGetNode];
68
+ }
69
+ function isComputed(obs) {
70
+ var _a;
71
+ return obs && ((_a = obs[symbolGetNode]) === null || _a === void 0 ? void 0 : _a.isComputed);
72
+ }
64
73
  function checkActivate(node) {
65
74
  var _a;
66
75
  const root = node.root;
@@ -87,12 +96,13 @@ function setNodeValue(node, newValue) {
87
96
  const prevValue = parentValue[key];
88
97
  const isFunc = isFunction(newValue);
89
98
  // Compute newValue if newValue is a function or an observable
90
- newValue =
91
- !parentNode.isAssigning && isFunc
92
- ? newValue(prevValue)
93
- : isObject(newValue) && (newValue === null || newValue === void 0 ? void 0 : newValue[symbolGetNode])
94
- ? newValue.peek()
95
- : newValue;
99
+ newValue = !parentNode.isAssigning && isFunc ? newValue(prevValue) : newValue;
100
+ // If setting an observable, set a link to the observable instead
101
+ if (isObservable(newValue) && !isComputed(newValue)) {
102
+ const val = newValue;
103
+ node.lazy = () => val;
104
+ newValue = undefined;
105
+ }
96
106
  try {
97
107
  parentNode.isSetting = (parentNode.isSetting || 0) + 1;
98
108
  // Save the new value
@@ -126,7 +136,13 @@ function getNodeValue(node) {
126
136
  }
127
137
  return child;
128
138
  }
129
- function getChildNode(node, key) {
139
+ const cloneFunction = (originalFunction) => {
140
+ const length = originalFunction.length;
141
+ return length > 1
142
+ ? (arg1, arg2) => originalFunction(arg1, arg2)
143
+ : (...args) => originalFunction(...args);
144
+ };
145
+ function getChildNode(node, key, asFunction) {
130
146
  var _a;
131
147
  // Get the child by key
132
148
  let child = (_a = node.children) === null || _a === void 0 ? void 0 : _a.get(key);
@@ -138,6 +154,12 @@ function getChildNode(node, key) {
138
154
  key,
139
155
  lazy: true,
140
156
  };
157
+ if (asFunction) {
158
+ child = Object.assign(cloneFunction(asFunction), child);
159
+ }
160
+ else if (node.proxyFn2) {
161
+ child = Object.assign(node.proxyFn2.bind(node, key), child);
162
+ }
141
163
  if (!node.children) {
142
164
  node.children = new Map();
143
165
  }
@@ -159,6 +181,7 @@ function ensureNodeValue(node) {
159
181
  return value;
160
182
  }
161
183
  function findIDKey(obj, node) {
184
+ var _a, _b;
162
185
  let idKey = isObject(obj)
163
186
  ? 'id' in obj
164
187
  ? 'id'
@@ -171,7 +194,8 @@ function findIDKey(obj, node) {
171
194
  : undefined
172
195
  : undefined;
173
196
  if (!idKey && node.parent) {
174
- const keyExtractor = getNodeValue(node.parent)[node.key + '_keyExtractor'];
197
+ const k = node.key + '_keyExtractor';
198
+ const keyExtractor = (_b = (_a = node.functions) === null || _a === void 0 ? void 0 : _a.get(k)) !== null && _b !== void 0 ? _b : getNodeValue(node.parent)[node.key + '_keyExtractor'];
175
199
  if (keyExtractor && isFunction(keyExtractor)) {
176
200
  idKey = keyExtractor;
177
201
  }
@@ -427,25 +451,200 @@ function endBatch(force) {
427
451
  }
428
452
  }
429
453
 
430
- function createObservable(value, makePrimitive, createObject, createPrimitive) {
454
+ function createObservable(value, makePrimitive, extractPromise, createObject, createPrimitive) {
431
455
  const valueIsPromise = isPromise(value);
456
+ const valueIsFunction = isFunction(value);
432
457
  const root = {
433
458
  _: value,
434
459
  };
435
- const node = {
460
+ let node = {
436
461
  root,
437
462
  lazy: true,
438
463
  };
464
+ if (valueIsFunction) {
465
+ node = Object.assign(cloneFunction(value), node);
466
+ }
439
467
  const prim = makePrimitive || isActualPrimitive(value);
440
468
  const obs = prim
441
469
  ? new createPrimitive(node)
442
470
  : createObject(node);
443
471
  if (valueIsPromise) {
472
+ setNodeValue(node, undefined);
444
473
  extractPromise(node, value);
445
474
  }
446
475
  return obs;
447
476
  }
448
477
 
478
+ function isEvent(obs) {
479
+ var _a;
480
+ return obs && ((_a = obs[symbolGetNode]) === null || _a === void 0 ? void 0 : _a.isEvent);
481
+ }
482
+ function computeSelector(selector, e, retainObservable) {
483
+ let c = selector;
484
+ if (isFunction(c)) {
485
+ c = e ? c(e) : c();
486
+ }
487
+ return isObservable(c) && !retainObservable ? c.get() : c;
488
+ }
489
+ function getObservableIndex(obs) {
490
+ const node = getNode(obs);
491
+ const n = +node.key;
492
+ return n - n < 1 ? +n : -1;
493
+ }
494
+ function opaqueObject(value) {
495
+ if (value) {
496
+ value[symbolOpaque] = true;
497
+ }
498
+ return value;
499
+ }
500
+ function lockObservable(obs, value) {
501
+ var _a;
502
+ const root = (_a = getNode(obs)) === null || _a === void 0 ? void 0 : _a.root;
503
+ if (root) {
504
+ root.locked = value;
505
+ }
506
+ }
507
+ function setAtPath(obj, path, pathTypes, value, fullObj, restore) {
508
+ let o = obj;
509
+ let oFull = fullObj;
510
+ if (path.length > 0) {
511
+ for (let i = 0; i < path.length; i++) {
512
+ const p = path[i];
513
+ if (i === path.length - 1) {
514
+ // Don't set if the value is the same. This prevents creating a new key
515
+ // when setting undefined on an object without this key
516
+ if (o[p] !== value) {
517
+ o[p] = value;
518
+ }
519
+ }
520
+ else if (o[p] === symbolDelete) {
521
+ // If this was previously deleted, restore it
522
+ if (oFull) {
523
+ o[p] = oFull[p];
524
+ restore === null || restore === void 0 ? void 0 : restore(path.slice(0, i + 1), o[p]);
525
+ }
526
+ break;
527
+ }
528
+ else if (o[p] === undefined || o[p] === null) {
529
+ o[p] = pathTypes[i] === 'array' ? [] : {};
530
+ }
531
+ o = o[p];
532
+ if (oFull) {
533
+ oFull = oFull[p];
534
+ }
535
+ }
536
+ }
537
+ else {
538
+ obj = value;
539
+ }
540
+ return obj;
541
+ }
542
+ function setInObservableAtPath(obs, path, pathTypes, value, mode) {
543
+ let o = obs;
544
+ let v = value;
545
+ for (let i = 0; i < path.length; i++) {
546
+ const p = path[i];
547
+ if (!o.peek()[p] && pathTypes[i] === 'array') {
548
+ o[p].set([]);
549
+ }
550
+ o = o[p];
551
+ v = v[p];
552
+ }
553
+ if (v === symbolDelete) {
554
+ o.delete();
555
+ }
556
+ // Assign if possible, or set otherwise
557
+ else if (mode === 'assign' && o.assign && isObject(o.peek())) {
558
+ o.assign(v);
559
+ }
560
+ else {
561
+ o.set(v);
562
+ }
563
+ }
564
+ function mergeIntoObservable(target, ...sources) {
565
+ beginBatch();
566
+ globalState.isMerging = true;
567
+ for (let i = 0; i < sources.length; i++) {
568
+ target = _mergeIntoObservable(target, sources[i]);
569
+ }
570
+ globalState.isMerging = false;
571
+ endBatch();
572
+ return target;
573
+ }
574
+ function _mergeIntoObservable(target, source) {
575
+ var _a;
576
+ if (isObservable(source)) {
577
+ source = source.peek();
578
+ }
579
+ const needsSet = isObservable(target);
580
+ const targetValue = needsSet ? target.peek() : target;
581
+ const isTargetArr = isArray(targetValue);
582
+ const isTargetObj = !isTargetArr && isObject(targetValue);
583
+ if ((isTargetObj && isObject(source) && !isEmpty(targetValue)) ||
584
+ (isTargetArr && isArray(source) && targetValue.length > 0)) {
585
+ const keys = Object.keys(source);
586
+ for (let i = 0; i < keys.length; i++) {
587
+ const key = keys[i];
588
+ const sourceValue = source[key];
589
+ if (sourceValue === symbolDelete) {
590
+ needsSet && ((_a = target[key]) === null || _a === void 0 ? void 0 : _a.delete) ? target[key].delete() : delete target[key];
591
+ }
592
+ else {
593
+ const isObj = isObject(sourceValue);
594
+ const isArr = !isObj && isArray(sourceValue);
595
+ const targetChild = target[key];
596
+ if ((isObj || isArr) && targetChild && (needsSet || !isEmpty(targetChild))) {
597
+ if (!needsSet && (!targetChild || (isObj ? !isObject(targetChild) : !isArray(targetChild)))) {
598
+ target[key] = sourceValue;
599
+ }
600
+ else {
601
+ _mergeIntoObservable(targetChild, sourceValue);
602
+ }
603
+ }
604
+ else {
605
+ needsSet
606
+ ? targetChild.set(sourceValue)
607
+ : (target[key] = sourceValue);
608
+ }
609
+ }
610
+ }
611
+ }
612
+ else if (source !== undefined) {
613
+ needsSet ? target.set(source) : (target = source);
614
+ }
615
+ return target;
616
+ }
617
+ function constructObjectWithPath(path, pathTypes, value) {
618
+ let out;
619
+ if (path.length > 0) {
620
+ let o = (out = {});
621
+ for (let i = 0; i < path.length; i++) {
622
+ const p = path[i];
623
+ o[p] = i === path.length - 1 ? value : pathTypes[i] === 'array' ? [] : {};
624
+ o = o[p];
625
+ }
626
+ }
627
+ else {
628
+ out = value;
629
+ }
630
+ return out;
631
+ }
632
+ function deconstructObjectWithPath(path, pathTypes, value) {
633
+ let o = value;
634
+ for (let i = 0; i < path.length; i++) {
635
+ const p = path[i];
636
+ o = o ? o[p] : pathTypes[i] === 'array' ? [] : {};
637
+ }
638
+ return o;
639
+ }
640
+ function isObservableValueReady(value) {
641
+ return !!value && ((!isObject(value) && !isArray(value)) || !isEmpty(value));
642
+ }
643
+ function setSilently(obs, newValue) {
644
+ const node = getNode(obs);
645
+ return setNodeValue(node, newValue).newValue;
646
+ }
647
+
449
648
  function onChange(node, callback, options = {}) {
450
649
  const { initial, immediate, noArgs } = options;
451
650
  const { trackingType } = options;
@@ -484,6 +683,23 @@ function onChange(node, callback, options = {}) {
484
683
  return () => listeners.delete(listener);
485
684
  }
486
685
 
686
+ function setupTracking(nodes, update, noArgs, immediate) {
687
+ let listeners = [];
688
+ // Listen to tracked nodes
689
+ nodes === null || nodes === void 0 ? void 0 : nodes.forEach((tracked) => {
690
+ const { node, track } = tracked;
691
+ listeners.push(onChange(node, update, { trackingType: track, immediate, noArgs }));
692
+ });
693
+ return () => {
694
+ if (listeners) {
695
+ for (let i = 0; i < listeners.length; i++) {
696
+ listeners[i]();
697
+ }
698
+ listeners = undefined;
699
+ }
700
+ };
701
+ }
702
+
487
703
  let trackCount = 0;
488
704
  const trackingQueue = [];
489
705
  const tracking = {
@@ -523,56 +739,148 @@ function updateTracking(node, track) {
523
739
  }
524
740
  }
525
741
 
526
- const ArrayModifiers = new Set([
527
- 'copyWithin',
528
- 'fill',
529
- 'from',
530
- 'pop',
531
- 'push',
532
- 'reverse',
533
- 'shift',
534
- 'sort',
535
- 'splice',
536
- 'unshift',
537
- ]);
538
- const ArrayLoopers = new Set([
539
- 'every',
540
- 'filter',
541
- 'find',
542
- 'findIndex',
543
- 'forEach',
544
- 'includes',
545
- 'join',
546
- 'map',
547
- 'some',
548
- ]);
549
- const ArrayLoopersReturn = new Set(['filter', 'find']);
550
- const observableProperties = new Map();
551
- const observableFns = new Map([
552
- ['get', get],
553
- ['set', set],
554
- ['peek', peek],
555
- ['onChange', onChange],
556
- ['assign', assign],
557
- ['delete', deleteFn],
558
- ['toggle', toggle],
559
- ]);
560
- if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
561
- // eslint-disable-next-line no-var
562
- var __devUpdateNodes = new Set();
563
- }
564
- function collectionSetter(node, target, prop, ...args) {
742
+ function trackSelector(selector, update, observeEvent, observeOptions, createResubscribe) {
565
743
  var _a;
566
- const prevValue = (isArray(target) && target.slice()) || target;
567
- const ret = target[prop].apply(target, args);
568
- if (node) {
569
- const hasParent = isChildNodeValue(node);
570
- const key = hasParent ? node.key : '_';
571
- const parentValue = hasParent ? getNodeValue(node.parent) : node.root;
572
- // Set the object to the previous value first
573
- parentValue[key] = prevValue;
574
- // Then set with the new value so it notifies with the correct prevValue
575
- setKey((_a = node.parent) !== null && _a !== void 0 ? _a : node, key, target);
744
+ let nodes;
745
+ let value;
746
+ let dispose;
747
+ let tracker;
748
+ let resubscribe;
749
+ let updateFn = update;
750
+ if (isObservable(selector)) {
751
+ value = selector.peek();
752
+ dispose = selector.onChange(update);
753
+ resubscribe = createResubscribe ? selector.onChange(update) : undefined;
754
+ }
755
+ else {
756
+ // Compute the selector inside a tracking context
757
+ beginTracking();
758
+ value = selector ? computeSelector(selector, observeEvent, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.fromComputed) : selector;
759
+ tracker = tracking.current;
760
+ nodes = tracker.nodes;
761
+ endTracking();
762
+ if ((process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') && tracker && nodes) {
763
+ (_a = tracker.traceListeners) === null || _a === void 0 ? void 0 : _a.call(tracker, nodes);
764
+ if (tracker.traceUpdates) {
765
+ updateFn = tracker.traceUpdates(update);
766
+ }
767
+ // Clear tracing so it doesn't leak to other components
768
+ tracker.traceListeners = undefined;
769
+ tracker.traceUpdates = undefined;
770
+ }
771
+ }
772
+ if (!(observeEvent === null || observeEvent === void 0 ? void 0 : observeEvent.cancel)) {
773
+ // Do tracing if it was requested
774
+ // useSyncExternalStore doesn't subscribe until after the component mount.
775
+ // We want to subscribe immediately so we don't miss any updates
776
+ dispose = setupTracking(nodes, updateFn, false, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.immediate);
777
+ resubscribe = createResubscribe ? () => setupTracking(nodes, updateFn) : undefined;
778
+ }
779
+ return { value, dispose, resubscribe };
780
+ }
781
+
782
+ function observe(selectorOrRun, reactionOrOptions, options) {
783
+ let reaction;
784
+ if (isFunction(reactionOrOptions)) {
785
+ reaction = reactionOrOptions;
786
+ }
787
+ else {
788
+ options = reactionOrOptions;
789
+ }
790
+ let dispose;
791
+ const e = { num: 0 };
792
+ // Wrap it in a function so it doesn't pass all the arguments to run()
793
+ const update = function () {
794
+ if (e.onCleanup) {
795
+ e.onCleanup();
796
+ e.onCleanup = undefined;
797
+ }
798
+ // Run in a batch so changes don't happen until we're done tracking here
799
+ beginBatch();
800
+ // Run the function/selector
801
+ delete e.value;
802
+ // Dispose listeners from previous run
803
+ dispose === null || dispose === void 0 ? void 0 : dispose();
804
+ const { dispose: _dispose, value } = trackSelector(selectorOrRun, update, e, options);
805
+ dispose = _dispose;
806
+ e.value = value;
807
+ if (e.onCleanupReaction) {
808
+ e.onCleanupReaction();
809
+ e.onCleanupReaction = undefined;
810
+ }
811
+ endBatch();
812
+ // Call the reaction if there is one and the value changed
813
+ if (reaction &&
814
+ ((options === null || options === void 0 ? void 0 : options.fromComputed) || ((e.num > 0 || !isEvent(selectorOrRun)) && e.previous !== e.value))) {
815
+ reaction(e);
816
+ }
817
+ // Update the previous value
818
+ e.previous = e.value;
819
+ // Increment the counter
820
+ e.num++;
821
+ };
822
+ update();
823
+ // Return function calling dispose because dispose may be changed in update()
824
+ return () => {
825
+ var _a, _b;
826
+ (_a = e.onCleanup) === null || _a === void 0 ? void 0 : _a.call(e);
827
+ e.onCleanup = undefined;
828
+ (_b = e.onCleanupReaction) === null || _b === void 0 ? void 0 : _b.call(e);
829
+ e.onCleanupReaction = undefined;
830
+ dispose === null || dispose === void 0 ? void 0 : dispose();
831
+ };
832
+ }
833
+
834
+ const ArrayModifiers = new Set([
835
+ 'copyWithin',
836
+ 'fill',
837
+ 'from',
838
+ 'pop',
839
+ 'push',
840
+ 'reverse',
841
+ 'shift',
842
+ 'sort',
843
+ 'splice',
844
+ 'unshift',
845
+ ]);
846
+ const ArrayLoopers = new Set([
847
+ 'every',
848
+ 'filter',
849
+ 'find',
850
+ 'findIndex',
851
+ 'forEach',
852
+ 'includes',
853
+ 'join',
854
+ 'map',
855
+ 'some',
856
+ ]);
857
+ const ArrayLoopersReturn = new Set(['filter', 'find']);
858
+ const observableProperties = new Map();
859
+ const observableFns = new Map([
860
+ ['get', get],
861
+ ['set', set],
862
+ ['peek', peek],
863
+ ['onChange', onChange],
864
+ ['assign', assign],
865
+ ['delete', deleteFn],
866
+ ['toggle', toggle],
867
+ ]);
868
+ if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
869
+ // eslint-disable-next-line no-var
870
+ var __devUpdateNodes = new Set();
871
+ }
872
+ function collectionSetter(node, target, prop, ...args) {
873
+ var _a;
874
+ const prevValue = (isArray(target) && target.slice()) || target;
875
+ const ret = target[prop].apply(target, args);
876
+ if (node) {
877
+ const hasParent = isChildNodeValue(node);
878
+ const key = hasParent ? node.key : '_';
879
+ const parentValue = hasParent ? getNodeValue(node.parent) : node.root;
880
+ // Set the object to the previous value first
881
+ parentValue[key] = prevValue;
882
+ // Then set with the new value so it notifies with the correct prevValue
883
+ setKey((_a = node.parent) !== null && _a !== void 0 ? _a : node, key, target);
576
884
  }
577
885
  // Return the original value
578
886
  return ret;
@@ -761,10 +1069,10 @@ function updateNodes(parent, obj, prevValue) {
761
1069
  }
762
1070
  return retValue !== null && retValue !== void 0 ? retValue : false;
763
1071
  }
764
- function getProxy(node, p) {
1072
+ function getProxy(node, p, asFunction) {
765
1073
  // Get the child node if p prop
766
1074
  if (p !== undefined)
767
- node = getChildNode(node, p);
1075
+ node = getChildNode(node, p, asFunction);
768
1076
  // Create a proxy if not already cached and return it
769
1077
  return (node.proxy || (node.proxy = new Proxy(node, proxyHandler)));
770
1078
  }
@@ -842,6 +1150,15 @@ const proxyHandler = {
842
1150
  if (isObject(value) && value[symbolOpaque]) {
843
1151
  return vProp;
844
1152
  }
1153
+ const fnOrComputed = (_a = node.functions) === null || _a === void 0 ? void 0 : _a.get(p);
1154
+ if (fnOrComputed) {
1155
+ if (isObservable(fnOrComputed)) {
1156
+ return fnOrComputed;
1157
+ }
1158
+ else {
1159
+ return getProxy(node, p, fnOrComputed);
1160
+ }
1161
+ }
845
1162
  // Handle function calls
846
1163
  if (isFunction(vProp)) {
847
1164
  if (isArray(value)) {
@@ -888,15 +1205,9 @@ const proxyHandler = {
888
1205
  // Update that this primitive node was accessed for observers
889
1206
  if (isArray(value) && p === 'length') {
890
1207
  updateTracking(node, true);
891
- // } else if (!isPrimitive(value)) {
892
- // updateTracking(getChildNode(node, p));
893
1208
  return vProp;
894
1209
  }
895
1210
  }
896
- const fnOrComputed = (_a = node.functions) === null || _a === void 0 ? void 0 : _a.get(p);
897
- if (fnOrComputed) {
898
- return fnOrComputed;
899
- }
900
1211
  // TODOV3: Remove "state"
901
1212
  if (vProp === undefined && (p === 'state' || p === '_state') && node.state) {
902
1213
  return node.state;
@@ -963,6 +1274,10 @@ const proxyHandler = {
963
1274
  const value = getNodeValue(node);
964
1275
  return Reflect.has(value, prop);
965
1276
  },
1277
+ apply(target, thisArg, argArray) {
1278
+ // If it's a function call it as a function
1279
+ return Reflect.apply(target, thisArg, argArray);
1280
+ },
966
1281
  };
967
1282
  function set(node, newValue) {
968
1283
  if (node.parent) {
@@ -1003,7 +1318,7 @@ function setKey(node, key, newValue, level) {
1003
1318
  }
1004
1319
  const isRoot = !node.parent && key === '_';
1005
1320
  // Get the child node for updating and notifying
1006
- const childNode = isRoot ? node : getChildNode(node, key);
1321
+ const childNode = isRoot ? node : getChildNode(node, key, isFunction(newValue) ? newValue : undefined);
1007
1322
  // Set the raw value on the parent object
1008
1323
  const { newValue: savedValue, prevValue, parentValue } = setNodeValue(childNode, newValue);
1009
1324
  const isFunc = isFunction(savedValue);
@@ -1142,7 +1457,7 @@ function extractPromise(node, value) {
1142
1457
  if (!node.state) {
1143
1458
  node.state = createObservable({
1144
1459
  isLoaded: false,
1145
- }, false, getProxy);
1460
+ }, false, extractPromise, getProxy);
1146
1461
  }
1147
1462
  value
1148
1463
  .then((value) => {
@@ -1159,6 +1474,7 @@ function extractFunctionOrComputed(node, obj, k, v) {
1159
1474
  }
1160
1475
  else if (typeof v === 'function') {
1161
1476
  extractFunction(node, k, v);
1477
+ delete obj[k];
1162
1478
  }
1163
1479
  else if (typeof v == 'object' && v !== null && v !== undefined) {
1164
1480
  const childNode = getNode(v);
@@ -1180,11 +1496,17 @@ function get(node, options) {
1180
1496
  function peek(node) {
1181
1497
  const value = getNodeValue(node);
1182
1498
  // If node is not yet lazily computed go do that
1183
- if (node.lazy) {
1499
+ const lazy = node.lazy;
1500
+ if (lazy) {
1184
1501
  delete node.lazy;
1185
- for (const key in value) {
1186
- if (hasOwnProperty.call(value, key)) {
1187
- extractFunctionOrComputed(node, value, key, value[key]);
1502
+ if (isFunction(node) || isFunction(lazy)) {
1503
+ activateNodeFunction(node, lazy);
1504
+ }
1505
+ else {
1506
+ for (const key in value) {
1507
+ if (hasOwnProperty.call(value, key)) {
1508
+ extractFunctionOrComputed(node, value, key, value[key]);
1509
+ }
1188
1510
  }
1189
1511
  }
1190
1512
  }
@@ -1192,184 +1514,143 @@ function peek(node) {
1192
1514
  checkActivate(node);
1193
1515
  return value;
1194
1516
  }
1195
-
1196
- function isObservable(obs) {
1197
- return obs && !!obs[symbolGetNode];
1198
- }
1199
- function isEvent(obs) {
1200
- var _a;
1201
- return obs && ((_a = obs[symbolGetNode]) === null || _a === void 0 ? void 0 : _a.isEvent);
1202
- }
1203
- function computeSelector(selector, e, retainObservable) {
1204
- let c = selector;
1205
- if (isFunction(c)) {
1206
- c = e ? c(e) : c();
1207
- }
1208
- return isObservable(c) && !retainObservable ? c.get() : c;
1209
- }
1210
- function getObservableIndex(obs) {
1211
- const node = getNode(obs);
1212
- const n = +node.key;
1213
- return n - n < 1 ? +n : -1;
1214
- }
1215
- function opaqueObject(value) {
1216
- if (value) {
1217
- value[symbolOpaque] = true;
1218
- }
1219
- return value;
1220
- }
1221
- function lockObservable(obs, value) {
1222
- var _a;
1223
- const root = (_a = getNode(obs)) === null || _a === void 0 ? void 0 : _a.root;
1224
- if (root) {
1225
- root.locked = value;
1226
- }
1227
- }
1228
- function setAtPath(obj, path, pathTypes, value, fullObj, restore) {
1229
- let o = obj;
1230
- let oFull = fullObj;
1231
- if (path.length > 0) {
1232
- for (let i = 0; i < path.length; i++) {
1233
- const p = path[i];
1234
- if (i === path.length - 1) {
1235
- // Don't set if the value is the same. This prevents creating a new key
1236
- // when setting undefined on an object without this key
1237
- if (o[p] !== value) {
1238
- o[p] = value;
1517
+ function activateNodeFunction(node, lazyFn) {
1518
+ let setter;
1519
+ let update;
1520
+ let subscriber;
1521
+ let cacheOptions;
1522
+ // The onSet function handles the observable being set
1523
+ // and forwards the set elsewhere
1524
+ const onSet = (setterParam) => {
1525
+ setter = setterParam;
1526
+ };
1527
+ // The subscribe function runs a function that listens to
1528
+ // a data source and sends updates into the observable
1529
+ const subscribe = (fn) => {
1530
+ if (!subscriber) {
1531
+ subscriber = fn;
1532
+ }
1533
+ };
1534
+ const cache = (fn) => {
1535
+ if (!cacheOptions) {
1536
+ cacheOptions = isFunction(fn) ? fn() : fn;
1537
+ }
1538
+ };
1539
+ // The proxy function simply marks the node as a proxy with this function
1540
+ // so that child nodes will be created with this function, and then simply
1541
+ // activated as a function
1542
+ const proxy = (fn) => {
1543
+ node.proxyFn2 = fn;
1544
+ };
1545
+ let prevTarget$;
1546
+ let curTarget$;
1547
+ const activator = (isFunction(node) ? node : lazyFn);
1548
+ let wasPromise;
1549
+ let isInitial = true;
1550
+ observe(() => {
1551
+ // Run the function at this node
1552
+ let value = activator({ onSet, subscribe, proxy, cache });
1553
+ // If target is an observable, get() it to make sure we listen to its changes
1554
+ // and set up an onSet to write changes back to it
1555
+ if (isObservable(value)) {
1556
+ prevTarget$ = curTarget$;
1557
+ curTarget$ = value;
1558
+ onSet(({ value: newValue, getPrevious }) => {
1559
+ // Don't set the target observable if the target has changed since the last run
1560
+ if (!prevTarget$ || curTarget$ === prevTarget$) {
1561
+ // Set the node value back to what it was before before setting it.
1562
+ // This is a workaround for linked objects because it might not notify
1563
+ // if setting a property of an object
1564
+ // TODO: Is there a way to not do this? Or at least only do it in a
1565
+ // small subset of cases?
1566
+ setNodeValue(getNode(curTarget$), getPrevious());
1567
+ // Set the value on the curTarget
1568
+ curTarget$.set(newValue);
1239
1569
  }
1240
- }
1241
- else if (o[p] === symbolDelete) {
1242
- // If this was previously deleted, restore it
1243
- if (oFull) {
1244
- o[p] = oFull[p];
1245
- restore === null || restore === void 0 ? void 0 : restore(path.slice(0, i + 1), o[p]);
1570
+ });
1571
+ // Get the value from the observable because we still want the raw value
1572
+ // for the effect.
1573
+ value = value.get();
1574
+ }
1575
+ wasPromise = isPromise(value) ? value : undefined;
1576
+ if (!node.activated) {
1577
+ node.activated = true;
1578
+ const activateNodeFn = wasPromise ? globalState.activateNode : activateNodeBase;
1579
+ update = activateNodeFn(node, value, setter, subscriber, cacheOptions).update;
1580
+ }
1581
+ if (wasPromise) {
1582
+ value = undefined;
1583
+ }
1584
+ return value;
1585
+ }, ({ value }) => {
1586
+ if (!globalState.isLoadingRemote$.peek()) {
1587
+ if (wasPromise) {
1588
+ wasPromise.then((newValue) => {
1589
+ update({ value: newValue });
1590
+ node.state.isLoaded.set(true);
1591
+ });
1592
+ if (isInitial && isFunction(getNodeValue(node))) {
1593
+ set(node, value);
1246
1594
  }
1247
- break;
1248
1595
  }
1249
- else if (o[p] === undefined || o[p] === null) {
1250
- o[p] = pathTypes[i] === 'array' ? [] : {};
1251
- }
1252
- o = o[p];
1253
- if (oFull) {
1254
- oFull = oFull[p];
1596
+ else {
1597
+ set(node, value);
1598
+ node.state.isLoaded.set(true);
1255
1599
  }
1256
1600
  }
1601
+ isInitial = false;
1602
+ }, { immediate: true, fromComputed: true });
1603
+ }
1604
+ const activateNodeBase = (globalState.activateNode = function activateNodeBase(node, newValue, setter, subscriber, cacheOptions) {
1605
+ let isSetting = false;
1606
+ if (!node.state) {
1607
+ node.state = createObservable({
1608
+ isLoaded: false,
1609
+ }, false, extractPromise, getProxy);
1610
+ }
1611
+ if (setter) {
1612
+ const doSet = (params) => {
1613
+ // Don't call the set if this is the first value coming in
1614
+ if (!isSetting) {
1615
+ if (params.changes.length > 1 || !isFunction(params.changes[0].prevAtPath)) {
1616
+ isSetting = true;
1617
+ params.isRemote = globalState.isLoadingRemote$.peek();
1618
+ batch(() => setter(params), () => {
1619
+ isSetting = false;
1620
+ });
1621
+ }
1622
+ }
1623
+ };
1624
+ onChange(node, doSet, { immediate: true });
1257
1625
  }
1258
- else {
1259
- obj = value;
1626
+ if (process.env.NODE_ENV === 'development' && cacheOptions) {
1627
+ // TODO Better message
1628
+ console.log('[legend-state] Using cacheOption without setting up persistence first');
1260
1629
  }
1261
- return obj;
1262
- }
1263
- function setInObservableAtPath(obs, path, pathTypes, value, mode) {
1264
- let o = obs;
1265
- let v = value;
1266
- for (let i = 0; i < path.length; i++) {
1267
- const p = path[i];
1268
- if (!o.peek()[p] && pathTypes[i] === 'array') {
1269
- o[p].set([]);
1630
+ const update = ({ value }) => {
1631
+ if (!isSetting) {
1632
+ globalState.onChangeRemote(() => {
1633
+ set(node, value);
1634
+ });
1270
1635
  }
1271
- o = o[p];
1272
- v = v[p];
1273
- }
1274
- if (v === symbolDelete) {
1275
- o.delete();
1636
+ };
1637
+ const isProm = isPromise(newValue);
1638
+ if (isProm) {
1639
+ extractPromise(node, newValue);
1276
1640
  }
1277
- // Assign if possible, or set otherwise
1278
- else if (mode === 'assign' && o.assign && isObject(o.peek())) {
1279
- o.assign(v);
1641
+ if (subscriber) {
1642
+ subscriber({ update });
1280
1643
  }
1281
- else {
1282
- o.set(v);
1283
- }
1284
- }
1285
- function mergeIntoObservable(target, ...sources) {
1286
- beginBatch();
1287
- globalState.isMerging = true;
1288
- for (let i = 0; i < sources.length; i++) {
1289
- target = _mergeIntoObservable(target, sources[i]);
1290
- }
1291
- globalState.isMerging = false;
1292
- endBatch();
1293
- return target;
1294
- }
1295
- function _mergeIntoObservable(target, source) {
1296
- var _a;
1297
- const needsSet = isObservable(target);
1298
- const targetValue = needsSet ? target.peek() : target;
1299
- const isTargetArr = isArray(targetValue);
1300
- const isTargetObj = !isTargetArr && isObject(targetValue);
1301
- if ((isTargetObj && isObject(source) && !isEmpty(targetValue)) ||
1302
- (isTargetArr && isArray(source) && targetValue.length > 0)) {
1303
- const keys = Object.keys(source);
1304
- for (let i = 0; i < keys.length; i++) {
1305
- const key = keys[i];
1306
- const sourceValue = source[key];
1307
- if (sourceValue === symbolDelete) {
1308
- needsSet && ((_a = target[key]) === null || _a === void 0 ? void 0 : _a.delete) ? target[key].delete() : delete target[key];
1309
- }
1310
- else {
1311
- const isObj = isObject(sourceValue);
1312
- const isArr = !isObj && isArray(sourceValue);
1313
- const targetChild = target[key];
1314
- if ((isObj || isArr) && targetChild && (needsSet || !isEmpty(targetChild))) {
1315
- if (!needsSet && (!targetChild || (isObj ? !isObject(targetChild) : !isArray(targetChild)))) {
1316
- target[key] = sourceValue;
1317
- }
1318
- else {
1319
- _mergeIntoObservable(targetChild, sourceValue);
1320
- }
1321
- }
1322
- else {
1323
- needsSet
1324
- ? targetChild.set(sourceValue)
1325
- : (target[key] = sourceValue);
1326
- }
1327
- }
1328
- }
1329
- }
1330
- else if (source !== undefined) {
1331
- needsSet ? target.set(source) : (target = source);
1332
- }
1333
- return target;
1334
- }
1335
- function constructObjectWithPath(path, pathTypes, value) {
1336
- let out;
1337
- if (path.length > 0) {
1338
- let o = (out = {});
1339
- for (let i = 0; i < path.length; i++) {
1340
- const p = path[i];
1341
- o[p] = i === path.length - 1 ? value : pathTypes[i] === 'array' ? [] : {};
1342
- o = o[p];
1343
- }
1344
- }
1345
- else {
1346
- out = value;
1347
- }
1348
- return out;
1349
- }
1350
- function deconstructObjectWithPath(path, pathTypes, value) {
1351
- let o = value;
1352
- for (let i = 0; i < path.length; i++) {
1353
- const p = path[i];
1354
- o = o ? o[p] : pathTypes[i] === 'array' ? [] : {};
1355
- }
1356
- return o;
1357
- }
1358
- function isObservableValueReady(value) {
1359
- return !!value && ((!isObject(value) && !isArray(value)) || !isEmpty(value));
1360
- }
1361
- function setSilently(obs, newValue) {
1362
- const node = getNode(obs);
1363
- return setNodeValue(node, newValue).newValue;
1364
- }
1365
-
1366
- const fns = ['get', 'set', 'peek', 'onChange', 'toggle'];
1367
- function ObservablePrimitiveClass(node) {
1368
- this._node = node;
1369
- // Bind to this
1370
- for (let i = 0; i < fns.length; i++) {
1371
- const key = fns[i];
1372
- this[key] = this[key].bind(this);
1644
+ return { update };
1645
+ });
1646
+
1647
+ const fns = ['get', 'set', 'peek', 'onChange', 'toggle'];
1648
+ function ObservablePrimitiveClass(node) {
1649
+ this._node = node;
1650
+ // Bind to this
1651
+ for (let i = 0; i < fns.length; i++) {
1652
+ const key = fns[i];
1653
+ this[key] = this[key].bind(this);
1373
1654
  }
1374
1655
  }
1375
1656
  // Add observable functions to prototype
@@ -1404,122 +1685,76 @@ ObservablePrimitiveClass.prototype.delete = function () {
1404
1685
  return this;
1405
1686
  };
1406
1687
 
1407
- function observable(value) {
1408
- return createObservable(value, false, getProxy, ObservablePrimitiveClass);
1409
- }
1410
- function observablePrimitive(value) {
1411
- return createObservable(value, true, getProxy, ObservablePrimitiveClass);
1412
- }
1413
-
1414
- function setupTracking(nodes, update, noArgs, immediate) {
1415
- let listeners = [];
1416
- // Listen to tracked nodes
1417
- nodes === null || nodes === void 0 ? void 0 : nodes.forEach((tracked) => {
1418
- const { node, track } = tracked;
1419
- listeners.push(onChange(node, update, { trackingType: track, immediate, noArgs }));
1420
- });
1421
- return () => {
1422
- if (listeners) {
1423
- for (let i = 0; i < listeners.length; i++) {
1424
- listeners[i]();
1425
- }
1426
- listeners = undefined;
1427
- }
1428
- };
1429
- }
1430
-
1431
- function trackSelector(selector, update, observeEvent, observeOptions, createResubscribe) {
1432
- var _a;
1433
- let nodes;
1434
- let value;
1435
- let dispose;
1436
- let tracker;
1437
- let resubscribe;
1438
- let updateFn = update;
1439
- if (isObservable(selector)) {
1440
- value = selector.peek();
1441
- dispose = selector.onChange(update);
1442
- resubscribe = createResubscribe ? selector.onChange(update) : undefined;
1688
+ function _when(predicate, effect, checkReady) {
1689
+ // If predicate is a regular Promise skip all the observable stuff
1690
+ if (isPromise(predicate)) {
1691
+ return effect ? predicate.then(effect) : predicate;
1443
1692
  }
1444
- else {
1445
- // Compute the selector inside a tracking context
1446
- beginTracking();
1447
- value = selector ? computeSelector(selector, observeEvent, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.fromComputed) : selector;
1448
- tracker = tracking.current;
1449
- nodes = tracker.nodes;
1450
- endTracking();
1451
- if ((process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') && tracker && nodes) {
1452
- (_a = tracker.traceListeners) === null || _a === void 0 ? void 0 : _a.call(tracker, nodes);
1453
- if (tracker.traceUpdates) {
1454
- updateFn = tracker.traceUpdates(update);
1455
- }
1456
- // Clear tracing so it doesn't leak to other components
1457
- tracker.traceListeners = undefined;
1458
- tracker.traceUpdates = undefined;
1693
+ let value;
1694
+ // Create a wrapping fn that calls the effect if predicate returns true
1695
+ function run(e) {
1696
+ const ret = computeSelector(predicate);
1697
+ if (!isPromise(ret) && (checkReady ? isObservableValueReady(ret) : ret)) {
1698
+ value = ret;
1699
+ // Set cancel so that observe does not track anymore
1700
+ e.cancel = true;
1459
1701
  }
1702
+ return value;
1460
1703
  }
1461
- if (!(observeEvent === null || observeEvent === void 0 ? void 0 : observeEvent.cancel)) {
1462
- // Do tracing if it was requested
1463
- // useSyncExternalStore doesn't subscribe until after the component mount.
1464
- // We want to subscribe immediately so we don't miss any updates
1465
- dispose = setupTracking(nodes, updateFn, false, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.immediate);
1466
- resubscribe = createResubscribe ? () => setupTracking(nodes, updateFn) : undefined;
1704
+ function doEffect() {
1705
+ // If value is truthy then run the effect
1706
+ effect === null || effect === void 0 ? void 0 : effect(value);
1467
1707
  }
1468
- return { value, dispose, resubscribe };
1469
- }
1470
-
1471
- function observe(selectorOrRun, reactionOrOptions, options) {
1472
- let reaction;
1473
- if (isFunction(reactionOrOptions)) {
1474
- reaction = reactionOrOptions;
1708
+ // Run in an observe
1709
+ observe(run, doEffect);
1710
+ // If first run resulted in a truthy value just return it.
1711
+ // It will have set e.cancel so no need to dispose
1712
+ if (isPromise(value)) {
1713
+ return effect ? value.then(effect) : value;
1714
+ }
1715
+ else if (value !== undefined) {
1716
+ return Promise.resolve(value);
1475
1717
  }
1476
1718
  else {
1477
- options = reactionOrOptions;
1719
+ // Wrap it in a promise
1720
+ const promise = new Promise((resolve) => {
1721
+ if (effect) {
1722
+ const originalEffect = effect;
1723
+ effect = (value) => {
1724
+ const effectValue = originalEffect(value);
1725
+ resolve(effectValue);
1726
+ };
1727
+ }
1728
+ else {
1729
+ effect = resolve;
1730
+ }
1731
+ });
1732
+ return promise;
1478
1733
  }
1479
- let dispose;
1480
- const e = { num: 0 };
1481
- // Wrap it in a function so it doesn't pass all the arguments to run()
1482
- const update = function () {
1483
- if (e.onCleanup) {
1484
- e.onCleanup();
1485
- e.onCleanup = undefined;
1486
- }
1487
- // Run in a batch so changes don't happen until we're done tracking here
1488
- beginBatch();
1489
- // Run the function/selector
1490
- delete e.value;
1491
- // Dispose listeners from previous run
1492
- dispose === null || dispose === void 0 ? void 0 : dispose();
1493
- const { dispose: _dispose, value } = trackSelector(selectorOrRun, update, e, options);
1494
- dispose = _dispose;
1495
- e.value = value;
1496
- if (e.onCleanupReaction) {
1497
- e.onCleanupReaction();
1498
- e.onCleanupReaction = undefined;
1499
- }
1500
- endBatch();
1501
- // Call the reaction if there is one and the value changed
1502
- if (reaction &&
1503
- (e.num > 0 || !isEvent(selectorOrRun)) &&
1504
- (e.previous !== e.value || (options === null || options === void 0 ? void 0 : options.fromComputed))) {
1505
- reaction(e);
1506
- }
1507
- // Update the previous value
1508
- e.previous = e.value;
1509
- // Increment the counter
1510
- e.num++;
1511
- };
1512
- update();
1513
- // Return function calling dispose because dispose may be changed in update()
1514
- return () => {
1515
- var _a, _b;
1516
- (_a = e.onCleanup) === null || _a === void 0 ? void 0 : _a.call(e);
1517
- e.onCleanup = undefined;
1518
- (_b = e.onCleanupReaction) === null || _b === void 0 ? void 0 : _b.call(e);
1519
- e.onCleanupReaction = undefined;
1520
- dispose === null || dispose === void 0 ? void 0 : dispose();
1521
- };
1522
1734
  }
1735
+ function when(predicate, effect) {
1736
+ return _when(predicate, effect, false);
1737
+ }
1738
+ function whenReady(predicate, effect) {
1739
+ return _when(predicate, effect, true);
1740
+ }
1741
+
1742
+ function observable(value) {
1743
+ return createObservable(value, false, extractPromise, getProxy, ObservablePrimitiveClass);
1744
+ }
1745
+ function observablePrimitive(value) {
1746
+ return createObservable(value, true, extractPromise, getProxy, ObservablePrimitiveClass);
1747
+ }
1748
+ globalState.isLoadingRemote$ = observable(false);
1749
+ globalState.onChangeRemote = function onChangeRemote(cb) {
1750
+ when(() => !globalState.isLoadingRemote$.get(), () => {
1751
+ // Remote changes should only update local state
1752
+ globalState.isLoadingRemote$.set(true);
1753
+ batch(cb, () => {
1754
+ globalState.isLoadingRemote$.set(false);
1755
+ });
1756
+ });
1757
+ };
1523
1758
 
1524
1759
  function computed(compute, set$1) {
1525
1760
  // Create an observable for this computed variable
@@ -1683,60 +1918,6 @@ function proxy(get, set) {
1683
1918
  return obs;
1684
1919
  }
1685
1920
 
1686
- function _when(predicate, effect, checkReady) {
1687
- // If predicate is a regular Promise skip all the observable stuff
1688
- if (isPromise(predicate)) {
1689
- return effect ? predicate.then(effect) : predicate;
1690
- }
1691
- let value;
1692
- // Create a wrapping fn that calls the effect if predicate returns true
1693
- function run(e) {
1694
- const ret = computeSelector(predicate);
1695
- if (!isPromise(ret) && (checkReady ? isObservableValueReady(ret) : ret)) {
1696
- value = ret;
1697
- // Set cancel so that observe does not track anymore
1698
- e.cancel = true;
1699
- }
1700
- return value;
1701
- }
1702
- function doEffect() {
1703
- // If value is truthy then run the effect
1704
- effect === null || effect === void 0 ? void 0 : effect(value);
1705
- }
1706
- // Run in an observe
1707
- observe(run, doEffect);
1708
- // If first run resulted in a truthy value just return it.
1709
- // It will have set e.cancel so no need to dispose
1710
- if (isPromise(value)) {
1711
- return effect ? value.then(effect) : value;
1712
- }
1713
- else if (value !== undefined) {
1714
- return Promise.resolve(value);
1715
- }
1716
- else {
1717
- // Wrap it in a promise
1718
- const promise = new Promise((resolve) => {
1719
- if (effect) {
1720
- const originalEffect = effect;
1721
- effect = (value) => {
1722
- const effectValue = originalEffect(value);
1723
- resolve(effectValue);
1724
- };
1725
- }
1726
- else {
1727
- effect = resolve;
1728
- }
1729
- });
1730
- return promise;
1731
- }
1732
- }
1733
- function when(predicate, effect) {
1734
- return _when(predicate, effect, false);
1735
- }
1736
- function whenReady(predicate, effect) {
1737
- return _when(predicate, effect, true);
1738
- }
1739
-
1740
1921
  const internal = {
1741
1922
  ensureNodeValue,
1742
1923
  findIDKey,