@legendapp/state 2.1.1 → 2.2.0-next.2

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