@legendapp/state 2.0.2 → 2.1.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,17 @@ 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,
63
64
  };
65
+ function isObservable(obs) {
66
+ return !!obs && !!obs[symbolGetNode];
67
+ }
68
+ function isComputed(obs) {
69
+ var _a;
70
+ return obs && ((_a = obs[symbolGetNode]) === null || _a === void 0 ? void 0 : _a.isComputed);
71
+ }
64
72
  function checkActivate(node) {
65
73
  var _a;
66
74
  const root = node.root;
@@ -87,12 +95,13 @@ function setNodeValue(node, newValue) {
87
95
  const prevValue = parentValue[key];
88
96
  const isFunc = isFunction(newValue);
89
97
  // 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;
98
+ newValue = !parentNode.isAssigning && isFunc ? newValue(prevValue) : newValue;
99
+ // If setting an observable, set a link to the observable instead
100
+ if (isObservable(newValue) && !isComputed(newValue)) {
101
+ const val = newValue;
102
+ node.lazy = () => val;
103
+ newValue = undefined;
104
+ }
96
105
  try {
97
106
  parentNode.isSetting = (parentNode.isSetting || 0) + 1;
98
107
  // Save the new value
@@ -126,7 +135,13 @@ function getNodeValue(node) {
126
135
  }
127
136
  return child;
128
137
  }
129
- function getChildNode(node, key) {
138
+ const cloneFunction = (originalFunction) => {
139
+ const length = originalFunction.length;
140
+ return length > 1
141
+ ? (arg1, arg2) => originalFunction(arg1, arg2)
142
+ : (...args) => originalFunction(...args);
143
+ };
144
+ function getChildNode(node, key, asFunction) {
130
145
  var _a;
131
146
  // Get the child by key
132
147
  let child = (_a = node.children) === null || _a === void 0 ? void 0 : _a.get(key);
@@ -138,6 +153,12 @@ function getChildNode(node, key) {
138
153
  key,
139
154
  lazy: true,
140
155
  };
156
+ if (asFunction) {
157
+ child = Object.assign(cloneFunction(asFunction), child);
158
+ }
159
+ else if (node.proxyFn2) {
160
+ child = Object.assign(node.proxyFn2.bind(node, key), child);
161
+ }
141
162
  if (!node.children) {
142
163
  node.children = new Map();
143
164
  }
@@ -159,6 +180,7 @@ function ensureNodeValue(node) {
159
180
  return value;
160
181
  }
161
182
  function findIDKey(obj, node) {
183
+ var _a, _b;
162
184
  let idKey = isObject(obj)
163
185
  ? 'id' in obj
164
186
  ? 'id'
@@ -171,7 +193,8 @@ function findIDKey(obj, node) {
171
193
  : undefined
172
194
  : undefined;
173
195
  if (!idKey && node.parent) {
174
- const keyExtractor = getNodeValue(node.parent)[node.key + '_keyExtractor'];
196
+ const k = node.key + '_keyExtractor';
197
+ 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
198
  if (keyExtractor && isFunction(keyExtractor)) {
176
199
  idKey = keyExtractor;
177
200
  }
@@ -427,15 +450,19 @@ function endBatch(force) {
427
450
  }
428
451
  }
429
452
 
430
- function createObservable(value, makePrimitive, createObject, createPrimitive) {
453
+ function createObservable(value, makePrimitive, extractPromise, createObject, createPrimitive) {
431
454
  const valueIsPromise = isPromise(value);
455
+ const valueIsFunction = isFunction(value);
432
456
  const root = {
433
457
  _: value,
434
458
  };
435
- const node = {
459
+ let node = {
436
460
  root,
437
461
  lazy: true,
438
462
  };
463
+ if (valueIsFunction) {
464
+ node = Object.assign(cloneFunction(value), node);
465
+ }
439
466
  const prim = makePrimitive || isActualPrimitive(value);
440
467
  const obs = prim
441
468
  ? new createPrimitive(node)
@@ -446,6 +473,176 @@ function createObservable(value, makePrimitive, createObject, createPrimitive) {
446
473
  return obs;
447
474
  }
448
475
 
476
+ function isEvent(obs) {
477
+ var _a;
478
+ return obs && ((_a = obs[symbolGetNode]) === null || _a === void 0 ? void 0 : _a.isEvent);
479
+ }
480
+ function computeSelector(selector, e, retainObservable) {
481
+ let c = selector;
482
+ if (isFunction(c)) {
483
+ c = e ? c(e) : c();
484
+ }
485
+ return isObservable(c) && !retainObservable ? c.get() : c;
486
+ }
487
+ function getObservableIndex(obs) {
488
+ const node = getNode(obs);
489
+ const n = +node.key;
490
+ return n - n < 1 ? +n : -1;
491
+ }
492
+ function opaqueObject(value) {
493
+ if (value) {
494
+ value[symbolOpaque] = true;
495
+ }
496
+ return value;
497
+ }
498
+ function lockObservable(obs, value) {
499
+ var _a;
500
+ const root = (_a = getNode(obs)) === null || _a === void 0 ? void 0 : _a.root;
501
+ if (root) {
502
+ root.locked = value;
503
+ }
504
+ }
505
+ function setAtPath(obj, path, pathTypes, value, fullObj, restore) {
506
+ let o = obj;
507
+ let oFull = fullObj;
508
+ if (path.length > 0) {
509
+ for (let i = 0; i < path.length; i++) {
510
+ const p = path[i];
511
+ if (i === path.length - 1) {
512
+ // Don't set if the value is the same. This prevents creating a new key
513
+ // when setting undefined on an object without this key
514
+ if (o[p] !== value) {
515
+ o[p] = value;
516
+ }
517
+ }
518
+ else if (o[p] === symbolDelete) {
519
+ // If this was previously deleted, restore it
520
+ if (oFull) {
521
+ o[p] = oFull[p];
522
+ restore === null || restore === void 0 ? void 0 : restore(path.slice(0, i + 1), o[p]);
523
+ }
524
+ break;
525
+ }
526
+ else if (o[p] === undefined || o[p] === null) {
527
+ o[p] = pathTypes[i] === 'array' ? [] : {};
528
+ }
529
+ o = o[p];
530
+ if (oFull) {
531
+ oFull = oFull[p];
532
+ }
533
+ }
534
+ }
535
+ else {
536
+ obj = value;
537
+ }
538
+ return obj;
539
+ }
540
+ function setInObservableAtPath(obs, path, pathTypes, value, mode) {
541
+ let o = obs;
542
+ let v = value;
543
+ for (let i = 0; i < path.length; i++) {
544
+ const p = path[i];
545
+ if (!o.peek()[p] && pathTypes[i] === 'array') {
546
+ o[p].set([]);
547
+ }
548
+ o = o[p];
549
+ v = v[p];
550
+ }
551
+ if (v === symbolDelete) {
552
+ o.delete();
553
+ }
554
+ // Assign if possible, or set otherwise
555
+ else if (mode === 'assign' && o.assign && isObject(o.peek())) {
556
+ o.assign(v);
557
+ }
558
+ else {
559
+ o.set(v);
560
+ }
561
+ }
562
+ function mergeIntoObservable(target, ...sources) {
563
+ beginBatch();
564
+ globalState.isMerging = true;
565
+ for (let i = 0; i < sources.length; i++) {
566
+ target = _mergeIntoObservable(target, sources[i]);
567
+ }
568
+ globalState.isMerging = false;
569
+ endBatch();
570
+ return target;
571
+ }
572
+ function _mergeIntoObservable(target, source) {
573
+ var _a;
574
+ if (isObservable(source)) {
575
+ source = source.peek();
576
+ }
577
+ const needsSet = isObservable(target);
578
+ const targetValue = needsSet ? target.peek() : target;
579
+ const isTargetArr = isArray(targetValue);
580
+ const isTargetObj = !isTargetArr && isObject(targetValue);
581
+ if ((isTargetObj && isObject(source) && !isEmpty(targetValue)) ||
582
+ (isTargetArr && isArray(source) && targetValue.length > 0)) {
583
+ const keys = Object.keys(source);
584
+ for (let i = 0; i < keys.length; i++) {
585
+ const key = keys[i];
586
+ const sourceValue = source[key];
587
+ if (sourceValue === symbolDelete) {
588
+ needsSet && ((_a = target[key]) === null || _a === void 0 ? void 0 : _a.delete) ? target[key].delete() : delete target[key];
589
+ }
590
+ else {
591
+ const isObj = isObject(sourceValue);
592
+ const isArr = !isObj && isArray(sourceValue);
593
+ const targetChild = target[key];
594
+ if ((isObj || isArr) && targetChild && (needsSet || !isEmpty(targetChild))) {
595
+ if (!needsSet && (!targetChild || (isObj ? !isObject(targetChild) : !isArray(targetChild)))) {
596
+ target[key] = sourceValue;
597
+ }
598
+ else {
599
+ _mergeIntoObservable(targetChild, sourceValue);
600
+ }
601
+ }
602
+ else {
603
+ needsSet
604
+ ? targetChild.set(sourceValue)
605
+ : (target[key] = sourceValue);
606
+ }
607
+ }
608
+ }
609
+ }
610
+ else if (source !== undefined) {
611
+ needsSet ? target.set(source) : (target = source);
612
+ }
613
+ return target;
614
+ }
615
+ function constructObjectWithPath(path, pathTypes, value) {
616
+ let out;
617
+ if (path.length > 0) {
618
+ let o = (out = {});
619
+ for (let i = 0; i < path.length; i++) {
620
+ const p = path[i];
621
+ o[p] = i === path.length - 1 ? value : pathTypes[i] === 'array' ? [] : {};
622
+ o = o[p];
623
+ }
624
+ }
625
+ else {
626
+ out = value;
627
+ }
628
+ return out;
629
+ }
630
+ function deconstructObjectWithPath(path, pathTypes, value) {
631
+ let o = value;
632
+ for (let i = 0; i < path.length; i++) {
633
+ const p = path[i];
634
+ o = o ? o[p] : pathTypes[i] === 'array' ? [] : {};
635
+ }
636
+ return o;
637
+ }
638
+ function isObservableValueReady(value) {
639
+ return !!value && ((!isObject(value) && !isArray(value)) || !isEmpty(value));
640
+ }
641
+ function setSilently(obs, newValue) {
642
+ const node = getNode(obs);
643
+ return setNodeValue(node, newValue).newValue;
644
+ }
645
+
449
646
  function onChange(node, callback, options = {}) {
450
647
  const { initial, immediate, noArgs } = options;
451
648
  const { trackingType } = options;
@@ -484,6 +681,23 @@ function onChange(node, callback, options = {}) {
484
681
  return () => listeners.delete(listener);
485
682
  }
486
683
 
684
+ function setupTracking(nodes, update, noArgs, immediate) {
685
+ let listeners = [];
686
+ // Listen to tracked nodes
687
+ nodes === null || nodes === void 0 ? void 0 : nodes.forEach((tracked) => {
688
+ const { node, track } = tracked;
689
+ listeners.push(onChange(node, update, { trackingType: track, immediate, noArgs }));
690
+ });
691
+ return () => {
692
+ if (listeners) {
693
+ for (let i = 0; i < listeners.length; i++) {
694
+ listeners[i]();
695
+ }
696
+ listeners = undefined;
697
+ }
698
+ };
699
+ }
700
+
487
701
  let trackCount = 0;
488
702
  const trackingQueue = [];
489
703
  const tracking = {
@@ -523,56 +737,148 @@ function updateTracking(node, track) {
523
737
  }
524
738
  }
525
739
 
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) {
740
+ function trackSelector(selector, update, observeEvent, observeOptions, createResubscribe) {
565
741
  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);
742
+ let nodes;
743
+ let value;
744
+ let dispose;
745
+ let tracker;
746
+ let resubscribe;
747
+ let updateFn = update;
748
+ if (isObservable(selector)) {
749
+ value = selector.peek();
750
+ dispose = selector.onChange(update);
751
+ resubscribe = createResubscribe ? selector.onChange(update) : undefined;
752
+ }
753
+ else {
754
+ // Compute the selector inside a tracking context
755
+ beginTracking();
756
+ value = selector ? computeSelector(selector, observeEvent, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.fromComputed) : selector;
757
+ tracker = tracking.current;
758
+ nodes = tracker.nodes;
759
+ endTracking();
760
+ if ((process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') && tracker && nodes) {
761
+ (_a = tracker.traceListeners) === null || _a === void 0 ? void 0 : _a.call(tracker, nodes);
762
+ if (tracker.traceUpdates) {
763
+ updateFn = tracker.traceUpdates(update);
764
+ }
765
+ // Clear tracing so it doesn't leak to other components
766
+ tracker.traceListeners = undefined;
767
+ tracker.traceUpdates = undefined;
768
+ }
769
+ }
770
+ if (!(observeEvent === null || observeEvent === void 0 ? void 0 : observeEvent.cancel)) {
771
+ // Do tracing if it was requested
772
+ // useSyncExternalStore doesn't subscribe until after the component mount.
773
+ // We want to subscribe immediately so we don't miss any updates
774
+ dispose = setupTracking(nodes, updateFn, false, observeOptions === null || observeOptions === void 0 ? void 0 : observeOptions.immediate);
775
+ resubscribe = createResubscribe ? () => setupTracking(nodes, updateFn) : undefined;
776
+ }
777
+ return { value, dispose, resubscribe };
778
+ }
779
+
780
+ function observe(selectorOrRun, reactionOrOptions, options) {
781
+ let reaction;
782
+ if (isFunction(reactionOrOptions)) {
783
+ reaction = reactionOrOptions;
784
+ }
785
+ else {
786
+ options = reactionOrOptions;
787
+ }
788
+ let dispose;
789
+ const e = { num: 0 };
790
+ // Wrap it in a function so it doesn't pass all the arguments to run()
791
+ const update = function () {
792
+ if (e.onCleanup) {
793
+ e.onCleanup();
794
+ e.onCleanup = undefined;
795
+ }
796
+ // Run in a batch so changes don't happen until we're done tracking here
797
+ beginBatch();
798
+ // Run the function/selector
799
+ delete e.value;
800
+ // Dispose listeners from previous run
801
+ dispose === null || dispose === void 0 ? void 0 : dispose();
802
+ const { dispose: _dispose, value } = trackSelector(selectorOrRun, update, e, options);
803
+ dispose = _dispose;
804
+ e.value = value;
805
+ if (e.onCleanupReaction) {
806
+ e.onCleanupReaction();
807
+ e.onCleanupReaction = undefined;
808
+ }
809
+ endBatch();
810
+ // Call the reaction if there is one and the value changed
811
+ if (reaction &&
812
+ ((options === null || options === void 0 ? void 0 : options.fromComputed) || ((e.num > 0 || !isEvent(selectorOrRun)) && e.previous !== e.value))) {
813
+ reaction(e);
814
+ }
815
+ // Update the previous value
816
+ e.previous = e.value;
817
+ // Increment the counter
818
+ e.num++;
819
+ };
820
+ update();
821
+ // Return function calling dispose because dispose may be changed in update()
822
+ return () => {
823
+ var _a, _b;
824
+ (_a = e.onCleanup) === null || _a === void 0 ? void 0 : _a.call(e);
825
+ e.onCleanup = undefined;
826
+ (_b = e.onCleanupReaction) === null || _b === void 0 ? void 0 : _b.call(e);
827
+ e.onCleanupReaction = undefined;
828
+ dispose === null || dispose === void 0 ? void 0 : dispose();
829
+ };
830
+ }
831
+
832
+ const ArrayModifiers = new Set([
833
+ 'copyWithin',
834
+ 'fill',
835
+ 'from',
836
+ 'pop',
837
+ 'push',
838
+ 'reverse',
839
+ 'shift',
840
+ 'sort',
841
+ 'splice',
842
+ 'unshift',
843
+ ]);
844
+ const ArrayLoopers = new Set([
845
+ 'every',
846
+ 'filter',
847
+ 'find',
848
+ 'findIndex',
849
+ 'forEach',
850
+ 'includes',
851
+ 'join',
852
+ 'map',
853
+ 'some',
854
+ ]);
855
+ const ArrayLoopersReturn = new Set(['filter', 'find']);
856
+ const observableProperties = new Map();
857
+ const observableFns = new Map([
858
+ ['get', get],
859
+ ['set', set],
860
+ ['peek', peek],
861
+ ['onChange', onChange],
862
+ ['assign', assign],
863
+ ['delete', deleteFn],
864
+ ['toggle', toggle],
865
+ ]);
866
+ if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
867
+ // eslint-disable-next-line no-var
868
+ var __devUpdateNodes = new Set();
869
+ }
870
+ function collectionSetter(node, target, prop, ...args) {
871
+ var _a;
872
+ const prevValue = (isArray(target) && target.slice()) || target;
873
+ const ret = target[prop].apply(target, args);
874
+ if (node) {
875
+ const hasParent = isChildNodeValue(node);
876
+ const key = hasParent ? node.key : '_';
877
+ const parentValue = hasParent ? getNodeValue(node.parent) : node.root;
878
+ // Set the object to the previous value first
879
+ parentValue[key] = prevValue;
880
+ // Then set with the new value so it notifies with the correct prevValue
881
+ setKey((_a = node.parent) !== null && _a !== void 0 ? _a : node, key, target);
576
882
  }
577
883
  // Return the original value
578
884
  return ret;
@@ -761,10 +1067,10 @@ function updateNodes(parent, obj, prevValue) {
761
1067
  }
762
1068
  return retValue !== null && retValue !== void 0 ? retValue : false;
763
1069
  }
764
- function getProxy(node, p) {
1070
+ function getProxy(node, p, asFunction) {
765
1071
  // Get the child node if p prop
766
1072
  if (p !== undefined)
767
- node = getChildNode(node, p);
1073
+ node = getChildNode(node, p, asFunction);
768
1074
  // Create a proxy if not already cached and return it
769
1075
  return (node.proxy || (node.proxy = new Proxy(node, proxyHandler)));
770
1076
  }
@@ -842,6 +1148,15 @@ const proxyHandler = {
842
1148
  if (isObject(value) && value[symbolOpaque]) {
843
1149
  return vProp;
844
1150
  }
1151
+ const fnOrComputed = (_a = node.functions) === null || _a === void 0 ? void 0 : _a.get(p);
1152
+ if (fnOrComputed) {
1153
+ if (isObservable(fnOrComputed)) {
1154
+ return fnOrComputed;
1155
+ }
1156
+ else {
1157
+ return getProxy(node, p, fnOrComputed);
1158
+ }
1159
+ }
845
1160
  // Handle function calls
846
1161
  if (isFunction(vProp)) {
847
1162
  if (isArray(value)) {
@@ -888,15 +1203,9 @@ const proxyHandler = {
888
1203
  // Update that this primitive node was accessed for observers
889
1204
  if (isArray(value) && p === 'length') {
890
1205
  updateTracking(node, true);
891
- // } else if (!isPrimitive(value)) {
892
- // updateTracking(getChildNode(node, p));
893
1206
  return vProp;
894
1207
  }
895
1208
  }
896
- const fnOrComputed = (_a = node.functions) === null || _a === void 0 ? void 0 : _a.get(p);
897
- if (fnOrComputed) {
898
- return fnOrComputed;
899
- }
900
1209
  // TODOV3: Remove "state"
901
1210
  if (vProp === undefined && (p === 'state' || p === '_state') && node.state) {
902
1211
  return node.state;
@@ -963,6 +1272,10 @@ const proxyHandler = {
963
1272
  const value = getNodeValue(node);
964
1273
  return Reflect.has(value, prop);
965
1274
  },
1275
+ apply(target, thisArg, argArray) {
1276
+ // If it's a function call it as a function
1277
+ return Reflect.apply(target, thisArg, argArray);
1278
+ },
966
1279
  };
967
1280
  function set(node, newValue) {
968
1281
  if (node.parent) {
@@ -1003,7 +1316,7 @@ function setKey(node, key, newValue, level) {
1003
1316
  }
1004
1317
  const isRoot = !node.parent && key === '_';
1005
1318
  // Get the child node for updating and notifying
1006
- const childNode = isRoot ? node : getChildNode(node, key);
1319
+ const childNode = isRoot ? node : getChildNode(node, key, isFunction(newValue) ? newValue : undefined);
1007
1320
  // Set the raw value on the parent object
1008
1321
  const { newValue: savedValue, prevValue, parentValue } = setNodeValue(childNode, newValue);
1009
1322
  const isFunc = isFunction(savedValue);
@@ -1142,7 +1455,7 @@ function extractPromise(node, value) {
1142
1455
  if (!node.state) {
1143
1456
  node.state = createObservable({
1144
1457
  isLoaded: false,
1145
- }, false, getProxy);
1458
+ }, false, extractPromise, getProxy);
1146
1459
  }
1147
1460
  value
1148
1461
  .then((value) => {
@@ -1159,6 +1472,7 @@ function extractFunctionOrComputed(node, obj, k, v) {
1159
1472
  }
1160
1473
  else if (typeof v === 'function') {
1161
1474
  extractFunction(node, k, v);
1475
+ delete obj[k];
1162
1476
  }
1163
1477
  else if (typeof v == 'object' && v !== null && v !== undefined) {
1164
1478
  const childNode = getNode(v);
@@ -1181,195 +1495,130 @@ function peek(node) {
1181
1495
  const value = getNodeValue(node);
1182
1496
  // If node is not yet lazily computed go do that
1183
1497
  if (node.lazy) {
1184
- delete node.lazy;
1185
- for (const key in value) {
1186
- if (hasOwnProperty.call(value, key)) {
1187
- extractFunctionOrComputed(node, value, key, value[key]);
1498
+ if (isFunction(node) || isFunction(node.lazy)) {
1499
+ activateNodeFunction(node);
1500
+ }
1501
+ else {
1502
+ for (const key in value) {
1503
+ if (hasOwnProperty.call(value, key)) {
1504
+ extractFunctionOrComputed(node, value, key, value[key]);
1505
+ }
1188
1506
  }
1189
1507
  }
1508
+ delete node.lazy;
1190
1509
  }
1191
1510
  // Check if computed needs to activate
1192
1511
  checkActivate(node);
1193
1512
  return value;
1194
1513
  }
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;
1514
+ function activateNodeFunction(node) {
1515
+ let dispose;
1516
+ let subscribed = false;
1517
+ let isSetting = false;
1518
+ // The onSet function handles the observable being set
1519
+ // and forwards the set elsewhere
1520
+ const onSet = (setter) => {
1521
+ const doSet = (params) => {
1522
+ // Don't call the set if this is the first value coming in
1523
+ if (params.changes.length > 1 || !isFunction(params.changes[0].prevAtPath)) {
1524
+ isSetting = true;
1525
+ try {
1526
+ params.isRemote = globalState.isLoadingRemote$.peek();
1527
+ setter(params);
1239
1528
  }
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]);
1529
+ finally {
1530
+ isSetting = false;
1246
1531
  }
1247
- break;
1248
1532
  }
1249
- else if (o[p] === undefined || o[p] === null) {
1250
- o[p] = pathTypes[i] === 'array' ? [] : {};
1533
+ };
1534
+ dispose === null || dispose === void 0 ? void 0 : dispose();
1535
+ dispose = onChange(node, doSet, { immediate: true });
1536
+ };
1537
+ // The subscribe function runs a function that listens to
1538
+ // a data source and sends updates into the observable
1539
+ const subscribe = (fn) => {
1540
+ if (!subscribed) {
1541
+ subscribed = true;
1542
+ fn({
1543
+ update: ({ value }) => {
1544
+ globalState.onChangeRemote(() => {
1545
+ set(node, value);
1546
+ });
1547
+ },
1548
+ });
1549
+ }
1550
+ };
1551
+ // The proxy function simply marks the node as a proxy with this function
1552
+ // so that child nodes will be created with this function, and then simply
1553
+ // activated as a function
1554
+ const proxy = (fn) => {
1555
+ node.proxyFn2 = fn;
1556
+ };
1557
+ if (!node.state) {
1558
+ node.state = createObservable({
1559
+ isLoaded: false,
1560
+ }, false, extractPromise, getProxy);
1561
+ }
1562
+ const { isLoaded } = node.state;
1563
+ let prevTarget$;
1564
+ let curTarget$;
1565
+ let wasPromise;
1566
+ const activator = (isFunction(node) ? node : node.lazy);
1567
+ observe(() => {
1568
+ // Run the function at this node
1569
+ let value = activator({ onSet, subscribe, proxy });
1570
+ // If target is an observable, get() it to make sure we listen to its changes
1571
+ // and set up an onSet to write changes back to it
1572
+ if (isObservable(value)) {
1573
+ prevTarget$ = curTarget$;
1574
+ curTarget$ = value;
1575
+ onSet(({ value: newValue, getPrevious }) => {
1576
+ // Don't set the target observable if the target has changed since the last run
1577
+ if (!prevTarget$ || curTarget$ === prevTarget$) {
1578
+ // Set the node value back to what it was before before setting it.
1579
+ // This is a workaround for linked objects because it might not notify
1580
+ // if setting a property of an object
1581
+ // TODO: Is there a way to not do this? Or at least only do it in a
1582
+ // small subset of cases?
1583
+ setNodeValue(getNode(curTarget$), getPrevious());
1584
+ // Set the value on the curTarget
1585
+ curTarget$.set(newValue);
1586
+ }
1587
+ });
1588
+ // Get the value from the observable because we still want the raw value
1589
+ // for the effect.
1590
+ value = value.get();
1591
+ }
1592
+ if (isPromise(value)) {
1593
+ wasPromise = true;
1594
+ extractPromise(node, value);
1595
+ value = undefined;
1596
+ }
1597
+ return value;
1598
+ }, ({ value }) => {
1599
+ if (!isSetting) {
1600
+ const doSet = () => {
1601
+ set(node, value);
1602
+ isLoaded.set(true);
1603
+ };
1604
+ if (wasPromise) {
1605
+ wasPromise = false;
1606
+ globalState.onChangeRemote(doSet);
1251
1607
  }
1252
- o = o[p];
1253
- if (oFull) {
1254
- oFull = oFull[p];
1608
+ else {
1609
+ doSet();
1255
1610
  }
1256
1611
  }
1257
- }
1258
- else {
1259
- obj = value;
1260
- }
1261
- return obj;
1612
+ }, { immediate: true, fromComputed: true });
1262
1613
  }
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([]);
1270
- }
1271
- o = o[p];
1272
- v = v[p];
1273
- }
1274
- if (v === symbolDelete) {
1275
- o.delete();
1276
- }
1277
- // Assign if possible, or set otherwise
1278
- else if (mode === 'assign' && o.assign && isObject(o.peek())) {
1279
- o.assign(v);
1280
- }
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);
1614
+
1615
+ const fns = ['get', 'set', 'peek', 'onChange', 'toggle'];
1616
+ function ObservablePrimitiveClass(node) {
1617
+ this._node = node;
1618
+ // Bind to this
1619
+ for (let i = 0; i < fns.length; i++) {
1620
+ const key = fns[i];
1621
+ this[key] = this[key].bind(this);
1373
1622
  }
1374
1623
  }
1375
1624
  // Add observable functions to prototype
@@ -1404,122 +1653,76 @@ ObservablePrimitiveClass.prototype.delete = function () {
1404
1653
  return this;
1405
1654
  };
1406
1655
 
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;
1656
+ function _when(predicate, effect, checkReady) {
1657
+ // If predicate is a regular Promise skip all the observable stuff
1658
+ if (isPromise(predicate)) {
1659
+ return effect ? predicate.then(effect) : predicate;
1443
1660
  }
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;
1661
+ let value;
1662
+ // Create a wrapping fn that calls the effect if predicate returns true
1663
+ function run(e) {
1664
+ const ret = computeSelector(predicate);
1665
+ if (!isPromise(ret) && (checkReady ? isObservableValueReady(ret) : ret)) {
1666
+ value = ret;
1667
+ // Set cancel so that observe does not track anymore
1668
+ e.cancel = true;
1459
1669
  }
1670
+ return value;
1460
1671
  }
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;
1672
+ function doEffect() {
1673
+ // If value is truthy then run the effect
1674
+ effect === null || effect === void 0 ? void 0 : effect(value);
1467
1675
  }
1468
- return { value, dispose, resubscribe };
1469
- }
1470
-
1471
- function observe(selectorOrRun, reactionOrOptions, options) {
1472
- let reaction;
1473
- if (isFunction(reactionOrOptions)) {
1474
- reaction = reactionOrOptions;
1676
+ // Run in an observe
1677
+ observe(run, doEffect);
1678
+ // If first run resulted in a truthy value just return it.
1679
+ // It will have set e.cancel so no need to dispose
1680
+ if (isPromise(value)) {
1681
+ return effect ? value.then(effect) : value;
1682
+ }
1683
+ else if (value !== undefined) {
1684
+ return Promise.resolve(value);
1475
1685
  }
1476
1686
  else {
1477
- options = reactionOrOptions;
1687
+ // Wrap it in a promise
1688
+ const promise = new Promise((resolve) => {
1689
+ if (effect) {
1690
+ const originalEffect = effect;
1691
+ effect = (value) => {
1692
+ const effectValue = originalEffect(value);
1693
+ resolve(effectValue);
1694
+ };
1695
+ }
1696
+ else {
1697
+ effect = resolve;
1698
+ }
1699
+ });
1700
+ return promise;
1478
1701
  }
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
1702
  }
1703
+ function when(predicate, effect) {
1704
+ return _when(predicate, effect, false);
1705
+ }
1706
+ function whenReady(predicate, effect) {
1707
+ return _when(predicate, effect, true);
1708
+ }
1709
+
1710
+ function observable(value) {
1711
+ return createObservable(value, false, extractPromise, getProxy, ObservablePrimitiveClass);
1712
+ }
1713
+ function observablePrimitive(value) {
1714
+ return createObservable(value, true, extractPromise, getProxy, ObservablePrimitiveClass);
1715
+ }
1716
+ globalState.isLoadingRemote$ = observable(false);
1717
+ globalState.onChangeRemote = function onChangeRemote(cb) {
1718
+ when(() => !globalState.isLoadingRemote$.get(), () => {
1719
+ // Remote changes should only update local state
1720
+ globalState.isLoadingRemote$.set(true);
1721
+ batch(cb, () => {
1722
+ globalState.isLoadingRemote$.set(false);
1723
+ });
1724
+ });
1725
+ };
1523
1726
 
1524
1727
  function computed(compute, set$1) {
1525
1728
  // Create an observable for this computed variable
@@ -1683,60 +1886,6 @@ function proxy(get, set) {
1683
1886
  return obs;
1684
1887
  }
1685
1888
 
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
1889
  const internal = {
1741
1890
  ensureNodeValue,
1742
1891
  findIDKey,