mobx 5.10.1 → 5.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +29 -1
  2. package/LICENSE +0 -0
  3. package/README.md +66 -52
  4. package/lib/api/action.d.ts +0 -0
  5. package/lib/api/actiondecorator.d.ts +0 -0
  6. package/lib/api/autorun.d.ts +5 -0
  7. package/lib/api/become-observed.d.ts +0 -0
  8. package/lib/api/computed.d.ts +0 -0
  9. package/lib/api/configure.d.ts +11 -0
  10. package/lib/api/decorate.d.ts +0 -0
  11. package/lib/api/extendobservable.d.ts +0 -0
  12. package/lib/api/extras.d.ts +0 -0
  13. package/lib/api/flow.d.ts +0 -0
  14. package/lib/api/intercept-read.d.ts +0 -0
  15. package/lib/api/intercept.d.ts +0 -0
  16. package/lib/api/iscomputed.d.ts +0 -0
  17. package/lib/api/isobservable.d.ts +0 -0
  18. package/lib/api/object-api.d.ts +2 -2
  19. package/lib/api/observable.d.ts +0 -0
  20. package/lib/api/observabledecorator.d.ts +0 -0
  21. package/lib/api/observe.d.ts +0 -0
  22. package/lib/api/tojs.d.ts +0 -0
  23. package/lib/api/trace.d.ts +0 -0
  24. package/lib/api/transaction.d.ts +0 -0
  25. package/lib/api/when.d.ts +5 -0
  26. package/lib/core/action.d.ts +13 -0
  27. package/lib/core/atom.d.ts +2 -2
  28. package/lib/core/computedvalue.d.ts +0 -0
  29. package/lib/core/derivation.d.ts +7 -0
  30. package/lib/core/globalstate.d.ts +22 -0
  31. package/lib/core/observable.d.ts +0 -0
  32. package/lib/core/reaction.d.ts +2 -1
  33. package/lib/core/spy.d.ts +0 -0
  34. package/lib/internal.d.ts +0 -0
  35. package/lib/mobx.d.ts +1 -1
  36. package/lib/mobx.es6.js +471 -382
  37. package/lib/mobx.js +485 -371
  38. package/lib/mobx.js.flow +20 -2
  39. package/lib/mobx.min.js +1 -1
  40. package/lib/mobx.module.js +485 -373
  41. package/lib/mobx.umd.js +485 -371
  42. package/lib/mobx.umd.min.js +1 -1
  43. package/lib/types/dynamicobject.d.ts +0 -0
  44. package/lib/types/intercept-utils.d.ts +0 -0
  45. package/lib/types/listen-utils.d.ts +0 -0
  46. package/lib/types/modifiers.d.ts +0 -0
  47. package/lib/types/observablearray.d.ts +0 -0
  48. package/lib/types/observablemap.d.ts +0 -0
  49. package/lib/types/observableobject.d.ts +0 -0
  50. package/lib/types/observableset.d.ts +0 -0
  51. package/lib/types/observablevalue.d.ts +0 -0
  52. package/lib/types/type-utils.d.ts +0 -0
  53. package/lib/utils/comparer.d.ts +0 -0
  54. package/lib/utils/decorators.d.ts +0 -0
  55. package/lib/utils/eq.d.ts +0 -0
  56. package/lib/utils/iterable.d.ts +0 -0
  57. package/lib/utils/utils.d.ts +0 -0
  58. package/package.json +7 -6
package/lib/mobx.es6.js CHANGED
@@ -512,6 +512,274 @@ const computed = function computed(arg1, arg2, arg3) {
512
512
  };
513
513
  computed.struct = computedStructDecorator;
514
514
 
515
+ var IDerivationState;
516
+ (function (IDerivationState) {
517
+ // before being run or (outside batch and not being observed)
518
+ // at this point derivation is not holding any data about dependency tree
519
+ IDerivationState[IDerivationState["NOT_TRACKING"] = -1] = "NOT_TRACKING";
520
+ // no shallow dependency changed since last computation
521
+ // won't recalculate derivation
522
+ // this is what makes mobx fast
523
+ IDerivationState[IDerivationState["UP_TO_DATE"] = 0] = "UP_TO_DATE";
524
+ // some deep dependency changed, but don't know if shallow dependency changed
525
+ // will require to check first if UP_TO_DATE or POSSIBLY_STALE
526
+ // currently only ComputedValue will propagate POSSIBLY_STALE
527
+ //
528
+ // having this state is second big optimization:
529
+ // don't have to recompute on every dependency change, but only when it's needed
530
+ IDerivationState[IDerivationState["POSSIBLY_STALE"] = 1] = "POSSIBLY_STALE";
531
+ // A shallow dependency has changed since last computation and the derivation
532
+ // will need to recompute when it's needed next.
533
+ IDerivationState[IDerivationState["STALE"] = 2] = "STALE";
534
+ })(IDerivationState || (IDerivationState = {}));
535
+ var TraceMode;
536
+ (function (TraceMode) {
537
+ TraceMode[TraceMode["NONE"] = 0] = "NONE";
538
+ TraceMode[TraceMode["LOG"] = 1] = "LOG";
539
+ TraceMode[TraceMode["BREAK"] = 2] = "BREAK";
540
+ })(TraceMode || (TraceMode = {}));
541
+ class CaughtException {
542
+ constructor(cause) {
543
+ this.cause = cause;
544
+ // Empty
545
+ }
546
+ }
547
+ function isCaughtException(e) {
548
+ return e instanceof CaughtException;
549
+ }
550
+ /**
551
+ * Finds out whether any dependency of the derivation has actually changed.
552
+ * If dependenciesState is 1 then it will recalculate dependencies,
553
+ * if any dependency changed it will propagate it by changing dependenciesState to 2.
554
+ *
555
+ * By iterating over the dependencies in the same order that they were reported and
556
+ * stopping on the first change, all the recalculations are only called for ComputedValues
557
+ * that will be tracked by derivation. That is because we assume that if the first x
558
+ * dependencies of the derivation doesn't change then the derivation should run the same way
559
+ * up until accessing x-th dependency.
560
+ */
561
+ function shouldCompute(derivation) {
562
+ switch (derivation.dependenciesState) {
563
+ case IDerivationState.UP_TO_DATE:
564
+ return false;
565
+ case IDerivationState.NOT_TRACKING:
566
+ case IDerivationState.STALE:
567
+ return true;
568
+ case IDerivationState.POSSIBLY_STALE: {
569
+ const prevUntracked = untrackedStart(); // no need for those computeds to be reported, they will be picked up in trackDerivedFunction.
570
+ const obs = derivation.observing, l = obs.length;
571
+ for (let i = 0; i < l; i++) {
572
+ const obj = obs[i];
573
+ if (isComputedValue(obj)) {
574
+ if (globalState.disableErrorBoundaries) {
575
+ obj.get();
576
+ }
577
+ else {
578
+ try {
579
+ obj.get();
580
+ }
581
+ catch (e) {
582
+ // we are not interested in the value *or* exception at this moment, but if there is one, notify all
583
+ untrackedEnd(prevUntracked);
584
+ return true;
585
+ }
586
+ }
587
+ // if ComputedValue `obj` actually changed it will be computed and propagated to its observers.
588
+ // and `derivation` is an observer of `obj`
589
+ // invariantShouldCompute(derivation)
590
+ if (derivation.dependenciesState === IDerivationState.STALE) {
591
+ untrackedEnd(prevUntracked);
592
+ return true;
593
+ }
594
+ }
595
+ }
596
+ changeDependenciesStateTo0(derivation);
597
+ untrackedEnd(prevUntracked);
598
+ return false;
599
+ }
600
+ }
601
+ }
602
+ // function invariantShouldCompute(derivation: IDerivation) {
603
+ // const newDepState = (derivation as any).dependenciesState
604
+ // if (
605
+ // process.env.NODE_ENV === "production" &&
606
+ // (newDepState === IDerivationState.POSSIBLY_STALE ||
607
+ // newDepState === IDerivationState.NOT_TRACKING)
608
+ // )
609
+ // fail("Illegal dependency state")
610
+ // }
611
+ function isComputingDerivation() {
612
+ return globalState.trackingDerivation !== null; // filter out actions inside computations
613
+ }
614
+ function checkIfStateModificationsAreAllowed(atom) {
615
+ const hasObservers = atom.observers.size > 0;
616
+ // Should never be possible to change an observed observable from inside computed, see #798
617
+ if (globalState.computationDepth > 0 && hasObservers)
618
+ fail(process.env.NODE_ENV !== "production" &&
619
+ `Computed values are not allowed to cause side effects by changing observables that are already being observed. Tried to modify: ${atom.name}`);
620
+ // Should not be possible to change observed state outside strict mode, except during initialization, see #563
621
+ if (!globalState.allowStateChanges && (hasObservers || globalState.enforceActions === "strict"))
622
+ fail(process.env.NODE_ENV !== "production" &&
623
+ (globalState.enforceActions
624
+ ? "Since strict-mode is enabled, changing observed observable values outside actions is not allowed. Please wrap the code in an `action` if this change is intended. Tried to modify: "
625
+ : "Side effects like changing state are not allowed at this point. Are you trying to modify state from, for example, the render function of a React component? Tried to modify: ") +
626
+ atom.name);
627
+ }
628
+ function checkIfStateReadsAreAllowed(observable) {
629
+ if (process.env.NODE_ENV !== "production" &&
630
+ !globalState.allowStateReads &&
631
+ globalState.observableRequiresReaction) {
632
+ console.warn(`[mobx] Observable ${observable.name} being read outside a reactive context`);
633
+ }
634
+ }
635
+ /**
636
+ * Executes the provided function `f` and tracks which observables are being accessed.
637
+ * The tracking information is stored on the `derivation` object and the derivation is registered
638
+ * as observer of any of the accessed observables.
639
+ */
640
+ function trackDerivedFunction(derivation, f, context) {
641
+ const prevAllowStateReads = allowStateReadsStart(true);
642
+ // pre allocate array allocation + room for variation in deps
643
+ // array will be trimmed by bindDependencies
644
+ changeDependenciesStateTo0(derivation);
645
+ derivation.newObserving = new Array(derivation.observing.length + 100);
646
+ derivation.unboundDepsCount = 0;
647
+ derivation.runId = ++globalState.runId;
648
+ const prevTracking = globalState.trackingDerivation;
649
+ globalState.trackingDerivation = derivation;
650
+ let result;
651
+ if (globalState.disableErrorBoundaries === true) {
652
+ result = f.call(context);
653
+ }
654
+ else {
655
+ try {
656
+ result = f.call(context);
657
+ }
658
+ catch (e) {
659
+ result = new CaughtException(e);
660
+ }
661
+ }
662
+ globalState.trackingDerivation = prevTracking;
663
+ bindDependencies(derivation);
664
+ warnAboutDerivationWithoutDependencies(derivation);
665
+ allowStateReadsEnd(prevAllowStateReads);
666
+ return result;
667
+ }
668
+ function warnAboutDerivationWithoutDependencies(derivation) {
669
+ if (process.env.NODE_ENV === "production")
670
+ return;
671
+ if (derivation.observing.length !== 0)
672
+ return;
673
+ if (globalState.reactionRequiresObservable || derivation.requiresObservable) {
674
+ console.warn(`[mobx] Derivation ${derivation.name} is created/updated without reading any observable value`);
675
+ }
676
+ }
677
+ /**
678
+ * diffs newObserving with observing.
679
+ * update observing to be newObserving with unique observables
680
+ * notify observers that become observed/unobserved
681
+ */
682
+ function bindDependencies(derivation) {
683
+ // invariant(derivation.dependenciesState !== IDerivationState.NOT_TRACKING, "INTERNAL ERROR bindDependencies expects derivation.dependenciesState !== -1");
684
+ const prevObserving = derivation.observing;
685
+ const observing = (derivation.observing = derivation.newObserving);
686
+ let lowestNewObservingDerivationState = IDerivationState.UP_TO_DATE;
687
+ // Go through all new observables and check diffValue: (this list can contain duplicates):
688
+ // 0: first occurrence, change to 1 and keep it
689
+ // 1: extra occurrence, drop it
690
+ let i0 = 0, l = derivation.unboundDepsCount;
691
+ for (let i = 0; i < l; i++) {
692
+ const dep = observing[i];
693
+ if (dep.diffValue === 0) {
694
+ dep.diffValue = 1;
695
+ if (i0 !== i)
696
+ observing[i0] = dep;
697
+ i0++;
698
+ }
699
+ // Upcast is 'safe' here, because if dep is IObservable, `dependenciesState` will be undefined,
700
+ // not hitting the condition
701
+ if (dep.dependenciesState > lowestNewObservingDerivationState) {
702
+ lowestNewObservingDerivationState = dep.dependenciesState;
703
+ }
704
+ }
705
+ observing.length = i0;
706
+ derivation.newObserving = null; // newObserving shouldn't be needed outside tracking (statement moved down to work around FF bug, see #614)
707
+ // Go through all old observables and check diffValue: (it is unique after last bindDependencies)
708
+ // 0: it's not in new observables, unobserve it
709
+ // 1: it keeps being observed, don't want to notify it. change to 0
710
+ l = prevObserving.length;
711
+ while (l--) {
712
+ const dep = prevObserving[l];
713
+ if (dep.diffValue === 0) {
714
+ removeObserver(dep, derivation);
715
+ }
716
+ dep.diffValue = 0;
717
+ }
718
+ // Go through all new observables and check diffValue: (now it should be unique)
719
+ // 0: it was set to 0 in last loop. don't need to do anything.
720
+ // 1: it wasn't observed, let's observe it. set back to 0
721
+ while (i0--) {
722
+ const dep = observing[i0];
723
+ if (dep.diffValue === 1) {
724
+ dep.diffValue = 0;
725
+ addObserver(dep, derivation);
726
+ }
727
+ }
728
+ // Some new observed derivations may become stale during this derivation computation
729
+ // so they have had no chance to propagate staleness (#916)
730
+ if (lowestNewObservingDerivationState !== IDerivationState.UP_TO_DATE) {
731
+ derivation.dependenciesState = lowestNewObservingDerivationState;
732
+ derivation.onBecomeStale();
733
+ }
734
+ }
735
+ function clearObserving(derivation) {
736
+ // invariant(globalState.inBatch > 0, "INTERNAL ERROR clearObserving should be called only inside batch");
737
+ const obs = derivation.observing;
738
+ derivation.observing = [];
739
+ let i = obs.length;
740
+ while (i--)
741
+ removeObserver(obs[i], derivation);
742
+ derivation.dependenciesState = IDerivationState.NOT_TRACKING;
743
+ }
744
+ function untracked(action) {
745
+ const prev = untrackedStart();
746
+ try {
747
+ return action();
748
+ }
749
+ finally {
750
+ untrackedEnd(prev);
751
+ }
752
+ }
753
+ function untrackedStart() {
754
+ const prev = globalState.trackingDerivation;
755
+ globalState.trackingDerivation = null;
756
+ return prev;
757
+ }
758
+ function untrackedEnd(prev) {
759
+ globalState.trackingDerivation = prev;
760
+ }
761
+ function allowStateReadsStart(allowStateReads) {
762
+ const prev = globalState.allowStateReads;
763
+ globalState.allowStateReads = allowStateReads;
764
+ return prev;
765
+ }
766
+ function allowStateReadsEnd(prev) {
767
+ globalState.allowStateReads = prev;
768
+ }
769
+ /**
770
+ * needed to keep `lowestObserverState` correct. when changing from (2 or 1) to 0
771
+ *
772
+ */
773
+ function changeDependenciesStateTo0(derivation) {
774
+ if (derivation.dependenciesState === IDerivationState.UP_TO_DATE)
775
+ return;
776
+ derivation.dependenciesState = IDerivationState.UP_TO_DATE;
777
+ const obs = derivation.observing;
778
+ let i = obs.length;
779
+ while (i--)
780
+ obs[i].lowestObserverState = IDerivationState.UP_TO_DATE;
781
+ }
782
+
515
783
  function createAction(actionName, fn, ref) {
516
784
  if (process.env.NODE_ENV !== "production") {
517
785
  invariant(typeof fn === "function", "`action` can only be invoked on functions");
@@ -525,25 +793,19 @@ function createAction(actionName, fn, ref) {
525
793
  return res;
526
794
  }
527
795
  function executeAction(actionName, fn, scope, args) {
528
- const runInfo = startAction(actionName, fn, scope, args);
529
- let shouldSupressReactionError = true;
796
+ const runInfo = _startAction(actionName, scope, args);
530
797
  try {
531
- const res = fn.apply(scope, args);
532
- shouldSupressReactionError = false;
533
- return res;
798
+ return fn.apply(scope, args);
799
+ }
800
+ catch (err) {
801
+ runInfo.error = err;
802
+ throw err;
534
803
  }
535
804
  finally {
536
- if (shouldSupressReactionError) {
537
- globalState.suppressReactionErrors = shouldSupressReactionError;
538
- endAction(runInfo);
539
- globalState.suppressReactionErrors = false;
540
- }
541
- else {
542
- endAction(runInfo);
543
- }
805
+ _endAction(runInfo);
544
806
  }
545
807
  }
546
- function startAction(actionName, fn, scope, args) {
808
+ function _startAction(actionName, scope, args) {
547
809
  const notifySpy = isSpyEnabled() && !!actionName;
548
810
  let startTime = 0;
549
811
  if (notifySpy && process.env.NODE_ENV !== "production") {
@@ -563,19 +825,35 @@ function startAction(actionName, fn, scope, args) {
563
825
  const prevDerivation = untrackedStart();
564
826
  startBatch();
565
827
  const prevAllowStateChanges = allowStateChangesStart(true);
566
- return {
828
+ const prevAllowStateReads = allowStateReadsStart(true);
829
+ const runInfo = {
567
830
  prevDerivation,
568
831
  prevAllowStateChanges,
832
+ prevAllowStateReads,
569
833
  notifySpy,
570
- startTime
834
+ startTime,
835
+ actionId: globalState.nextActionId++,
836
+ parentActionId: globalState.currentActionId
571
837
  };
838
+ globalState.currentActionId = runInfo.actionId;
839
+ return runInfo;
572
840
  }
573
- function endAction(runInfo) {
841
+ function _endAction(runInfo) {
842
+ if (globalState.currentActionId !== runInfo.actionId) {
843
+ fail("invalid action stack. did you forget to finish an action?");
844
+ }
845
+ globalState.currentActionId = runInfo.parentActionId;
846
+ if (runInfo.error !== undefined) {
847
+ globalState.suppressReactionErrors = true;
848
+ }
574
849
  allowStateChangesEnd(runInfo.prevAllowStateChanges);
850
+ allowStateReadsEnd(runInfo.prevAllowStateReads);
575
851
  endBatch();
576
852
  untrackedEnd(runInfo.prevDerivation);
577
- if (runInfo.notifySpy && process.env.NODE_ENV !== "production")
853
+ if (runInfo.notifySpy && process.env.NODE_ENV !== "production") {
578
854
  spyReportEnd({ time: Date.now() - runInfo.startTime });
855
+ }
856
+ globalState.suppressReactionErrors = false;
579
857
  }
580
858
  function allowStateChanges(allowStateChanges, func) {
581
859
  const prev = allowStateChangesStart(allowStateChanges);
@@ -823,351 +1101,110 @@ class ComputedValue {
823
1101
  this.setter.call(this.scope, value);
824
1102
  }
825
1103
  finally {
826
- this.isRunningSetter = false;
827
- }
828
- }
829
- else
830
- invariant(false, process.env.NODE_ENV !== "production" &&
831
- `[ComputedValue '${this.name}'] It is not possible to assign a new value to a computed value.`);
832
- }
833
- trackAndCompute() {
834
- if (isSpyEnabled() && process.env.NODE_ENV !== "production") {
835
- spyReport({
836
- object: this.scope,
837
- type: "compute",
838
- name: this.name
839
- });
840
- }
841
- const oldValue = this.value;
842
- const wasSuspended =
843
- /* see #1208 */ this.dependenciesState === IDerivationState.NOT_TRACKING;
844
- const newValue = this.computeValue(true);
845
- const changed = wasSuspended ||
846
- isCaughtException(oldValue) ||
847
- isCaughtException(newValue) ||
848
- !this.equals(oldValue, newValue);
849
- if (changed) {
850
- this.value = newValue;
851
- }
852
- return changed;
853
- }
854
- computeValue(track) {
855
- this.isComputing = true;
856
- globalState.computationDepth++;
857
- let res;
858
- if (track) {
859
- res = trackDerivedFunction(this, this.derivation, this.scope);
860
- }
861
- else {
862
- if (globalState.disableErrorBoundaries === true) {
863
- res = this.derivation.call(this.scope);
864
- }
865
- else {
866
- try {
867
- res = this.derivation.call(this.scope);
868
- }
869
- catch (e) {
870
- res = new CaughtException(e);
871
- }
872
- }
873
- }
874
- globalState.computationDepth--;
875
- this.isComputing = false;
876
- return res;
877
- }
878
- suspend() {
879
- if (!this.keepAlive) {
880
- clearObserving(this);
881
- this.value = undefined; // don't hold on to computed value!
882
- }
883
- }
884
- observe(listener, fireImmediately) {
885
- let firstTime = true;
886
- let prevValue = undefined;
887
- return autorun(() => {
888
- let newValue = this.get();
889
- if (!firstTime || fireImmediately) {
890
- const prevU = untrackedStart();
891
- listener({
892
- type: "update",
893
- object: this,
894
- newValue,
895
- oldValue: prevValue
896
- });
897
- untrackedEnd(prevU);
898
- }
899
- firstTime = false;
900
- prevValue = newValue;
901
- });
902
- }
903
- warnAboutUntrackedRead() {
904
- if (process.env.NODE_ENV === "production")
905
- return;
906
- if (this.requiresReaction === true) {
907
- fail(`[mobx] Computed value ${this.name} is read outside a reactive context`);
908
- }
909
- if (this.isTracing !== TraceMode.NONE) {
910
- console.log(`[mobx.trace] '${this.name}' is being read outside a reactive context. Doing a full recompute`);
911
- }
912
- if (globalState.computedRequiresReaction) {
913
- console.warn(`[mobx] Computed value ${this.name} is being read outside a reactive context. Doing a full recompute`);
914
- }
915
- }
916
- toJSON() {
917
- return this.get();
918
- }
919
- toString() {
920
- return `${this.name}[${this.derivation.toString()}]`;
921
- }
922
- valueOf() {
923
- return toPrimitive(this.get());
924
- }
925
- [Symbol.toPrimitive]() {
926
- return this.valueOf();
927
- }
928
- }
929
- const isComputedValue = createInstanceofPredicate("ComputedValue", ComputedValue);
930
-
931
- var IDerivationState;
932
- (function (IDerivationState) {
933
- // before being run or (outside batch and not being observed)
934
- // at this point derivation is not holding any data about dependency tree
935
- IDerivationState[IDerivationState["NOT_TRACKING"] = -1] = "NOT_TRACKING";
936
- // no shallow dependency changed since last computation
937
- // won't recalculate derivation
938
- // this is what makes mobx fast
939
- IDerivationState[IDerivationState["UP_TO_DATE"] = 0] = "UP_TO_DATE";
940
- // some deep dependency changed, but don't know if shallow dependency changed
941
- // will require to check first if UP_TO_DATE or POSSIBLY_STALE
942
- // currently only ComputedValue will propagate POSSIBLY_STALE
943
- //
944
- // having this state is second big optimization:
945
- // don't have to recompute on every dependency change, but only when it's needed
946
- IDerivationState[IDerivationState["POSSIBLY_STALE"] = 1] = "POSSIBLY_STALE";
947
- // A shallow dependency has changed since last computation and the derivation
948
- // will need to recompute when it's needed next.
949
- IDerivationState[IDerivationState["STALE"] = 2] = "STALE";
950
- })(IDerivationState || (IDerivationState = {}));
951
- var TraceMode;
952
- (function (TraceMode) {
953
- TraceMode[TraceMode["NONE"] = 0] = "NONE";
954
- TraceMode[TraceMode["LOG"] = 1] = "LOG";
955
- TraceMode[TraceMode["BREAK"] = 2] = "BREAK";
956
- })(TraceMode || (TraceMode = {}));
957
- class CaughtException {
958
- constructor(cause) {
959
- this.cause = cause;
960
- // Empty
961
- }
962
- }
963
- function isCaughtException(e) {
964
- return e instanceof CaughtException;
965
- }
966
- /**
967
- * Finds out whether any dependency of the derivation has actually changed.
968
- * If dependenciesState is 1 then it will recalculate dependencies,
969
- * if any dependency changed it will propagate it by changing dependenciesState to 2.
970
- *
971
- * By iterating over the dependencies in the same order that they were reported and
972
- * stopping on the first change, all the recalculations are only called for ComputedValues
973
- * that will be tracked by derivation. That is because we assume that if the first x
974
- * dependencies of the derivation doesn't change then the derivation should run the same way
975
- * up until accessing x-th dependency.
976
- */
977
- function shouldCompute(derivation) {
978
- switch (derivation.dependenciesState) {
979
- case IDerivationState.UP_TO_DATE:
980
- return false;
981
- case IDerivationState.NOT_TRACKING:
982
- case IDerivationState.STALE:
983
- return true;
984
- case IDerivationState.POSSIBLY_STALE: {
985
- const prevUntracked = untrackedStart(); // no need for those computeds to be reported, they will be picked up in trackDerivedFunction.
986
- const obs = derivation.observing, l = obs.length;
987
- for (let i = 0; i < l; i++) {
988
- const obj = obs[i];
989
- if (isComputedValue(obj)) {
990
- if (globalState.disableErrorBoundaries) {
991
- obj.get();
992
- }
993
- else {
994
- try {
995
- obj.get();
996
- }
997
- catch (e) {
998
- // we are not interested in the value *or* exception at this moment, but if there is one, notify all
999
- untrackedEnd(prevUntracked);
1000
- return true;
1001
- }
1002
- }
1003
- // if ComputedValue `obj` actually changed it will be computed and propagated to its observers.
1004
- // and `derivation` is an observer of `obj`
1005
- // invariantShouldCompute(derivation)
1006
- if (derivation.dependenciesState === IDerivationState.STALE) {
1007
- untrackedEnd(prevUntracked);
1008
- return true;
1009
- }
1010
- }
1011
- }
1012
- changeDependenciesStateTo0(derivation);
1013
- untrackedEnd(prevUntracked);
1014
- return false;
1104
+ this.isRunningSetter = false;
1105
+ }
1015
1106
  }
1107
+ else
1108
+ invariant(false, process.env.NODE_ENV !== "production" &&
1109
+ `[ComputedValue '${this.name}'] It is not possible to assign a new value to a computed value.`);
1016
1110
  }
1017
- }
1018
- // function invariantShouldCompute(derivation: IDerivation) {
1019
- // const newDepState = (derivation as any).dependenciesState
1020
- // if (
1021
- // process.env.NODE_ENV === "production" &&
1022
- // (newDepState === IDerivationState.POSSIBLY_STALE ||
1023
- // newDepState === IDerivationState.NOT_TRACKING)
1024
- // )
1025
- // fail("Illegal dependency state")
1026
- // }
1027
- function isComputingDerivation() {
1028
- return globalState.trackingDerivation !== null; // filter out actions inside computations
1029
- }
1030
- function checkIfStateModificationsAreAllowed(atom) {
1031
- const hasObservers = atom.observers.size > 0;
1032
- // Should never be possible to change an observed observable from inside computed, see #798
1033
- if (globalState.computationDepth > 0 && hasObservers)
1034
- fail(process.env.NODE_ENV !== "production" &&
1035
- `Computed values are not allowed to cause side effects by changing observables that are already being observed. Tried to modify: ${atom.name}`);
1036
- // Should not be possible to change observed state outside strict mode, except during initialization, see #563
1037
- if (!globalState.allowStateChanges && (hasObservers || globalState.enforceActions === "strict"))
1038
- fail(process.env.NODE_ENV !== "production" &&
1039
- (globalState.enforceActions
1040
- ? "Since strict-mode is enabled, changing observed observable values outside actions is not allowed. Please wrap the code in an `action` if this change is intended. Tried to modify: "
1041
- : "Side effects like changing state are not allowed at this point. Are you trying to modify state from, for example, the render function of a React component? Tried to modify: ") +
1042
- atom.name);
1043
- }
1044
- /**
1045
- * Executes the provided function `f` and tracks which observables are being accessed.
1046
- * The tracking information is stored on the `derivation` object and the derivation is registered
1047
- * as observer of any of the accessed observables.
1048
- */
1049
- function trackDerivedFunction(derivation, f, context) {
1050
- // pre allocate array allocation + room for variation in deps
1051
- // array will be trimmed by bindDependencies
1052
- changeDependenciesStateTo0(derivation);
1053
- derivation.newObserving = new Array(derivation.observing.length + 100);
1054
- derivation.unboundDepsCount = 0;
1055
- derivation.runId = ++globalState.runId;
1056
- const prevTracking = globalState.trackingDerivation;
1057
- globalState.trackingDerivation = derivation;
1058
- let result;
1059
- if (globalState.disableErrorBoundaries === true) {
1060
- result = f.call(context);
1061
- }
1062
- else {
1063
- try {
1064
- result = f.call(context);
1111
+ trackAndCompute() {
1112
+ if (isSpyEnabled() && process.env.NODE_ENV !== "production") {
1113
+ spyReport({
1114
+ object: this.scope,
1115
+ type: "compute",
1116
+ name: this.name
1117
+ });
1065
1118
  }
1066
- catch (e) {
1067
- result = new CaughtException(e);
1119
+ const oldValue = this.value;
1120
+ const wasSuspended =
1121
+ /* see #1208 */ this.dependenciesState === IDerivationState.NOT_TRACKING;
1122
+ const newValue = this.computeValue(true);
1123
+ const changed = wasSuspended ||
1124
+ isCaughtException(oldValue) ||
1125
+ isCaughtException(newValue) ||
1126
+ !this.equals(oldValue, newValue);
1127
+ if (changed) {
1128
+ this.value = newValue;
1068
1129
  }
1130
+ return changed;
1069
1131
  }
1070
- globalState.trackingDerivation = prevTracking;
1071
- bindDependencies(derivation);
1072
- return result;
1073
- }
1074
- /**
1075
- * diffs newObserving with observing.
1076
- * update observing to be newObserving with unique observables
1077
- * notify observers that become observed/unobserved
1078
- */
1079
- function bindDependencies(derivation) {
1080
- // invariant(derivation.dependenciesState !== IDerivationState.NOT_TRACKING, "INTERNAL ERROR bindDependencies expects derivation.dependenciesState !== -1");
1081
- const prevObserving = derivation.observing;
1082
- const observing = (derivation.observing = derivation.newObserving);
1083
- let lowestNewObservingDerivationState = IDerivationState.UP_TO_DATE;
1084
- // Go through all new observables and check diffValue: (this list can contain duplicates):
1085
- // 0: first occurrence, change to 1 and keep it
1086
- // 1: extra occurrence, drop it
1087
- let i0 = 0, l = derivation.unboundDepsCount;
1088
- for (let i = 0; i < l; i++) {
1089
- const dep = observing[i];
1090
- if (dep.diffValue === 0) {
1091
- dep.diffValue = 1;
1092
- if (i0 !== i)
1093
- observing[i0] = dep;
1094
- i0++;
1132
+ computeValue(track) {
1133
+ this.isComputing = true;
1134
+ globalState.computationDepth++;
1135
+ let res;
1136
+ if (track) {
1137
+ res = trackDerivedFunction(this, this.derivation, this.scope);
1095
1138
  }
1096
- // Upcast is 'safe' here, because if dep is IObservable, `dependenciesState` will be undefined,
1097
- // not hitting the condition
1098
- if (dep.dependenciesState > lowestNewObservingDerivationState) {
1099
- lowestNewObservingDerivationState = dep.dependenciesState;
1139
+ else {
1140
+ if (globalState.disableErrorBoundaries === true) {
1141
+ res = this.derivation.call(this.scope);
1142
+ }
1143
+ else {
1144
+ try {
1145
+ res = this.derivation.call(this.scope);
1146
+ }
1147
+ catch (e) {
1148
+ res = new CaughtException(e);
1149
+ }
1150
+ }
1100
1151
  }
1152
+ globalState.computationDepth--;
1153
+ this.isComputing = false;
1154
+ return res;
1101
1155
  }
1102
- observing.length = i0;
1103
- derivation.newObserving = null; // newObserving shouldn't be needed outside tracking (statement moved down to work around FF bug, see #614)
1104
- // Go through all old observables and check diffValue: (it is unique after last bindDependencies)
1105
- // 0: it's not in new observables, unobserve it
1106
- // 1: it keeps being observed, don't want to notify it. change to 0
1107
- l = prevObserving.length;
1108
- while (l--) {
1109
- const dep = prevObserving[l];
1110
- if (dep.diffValue === 0) {
1111
- removeObserver(dep, derivation);
1156
+ suspend() {
1157
+ if (!this.keepAlive) {
1158
+ clearObserving(this);
1159
+ this.value = undefined; // don't hold on to computed value!
1112
1160
  }
1113
- dep.diffValue = 0;
1114
1161
  }
1115
- // Go through all new observables and check diffValue: (now it should be unique)
1116
- // 0: it was set to 0 in last loop. don't need to do anything.
1117
- // 1: it wasn't observed, let's observe it. set back to 0
1118
- while (i0--) {
1119
- const dep = observing[i0];
1120
- if (dep.diffValue === 1) {
1121
- dep.diffValue = 0;
1122
- addObserver(dep, derivation);
1162
+ observe(listener, fireImmediately) {
1163
+ let firstTime = true;
1164
+ let prevValue = undefined;
1165
+ return autorun(() => {
1166
+ let newValue = this.get();
1167
+ if (!firstTime || fireImmediately) {
1168
+ const prevU = untrackedStart();
1169
+ listener({
1170
+ type: "update",
1171
+ object: this,
1172
+ newValue,
1173
+ oldValue: prevValue
1174
+ });
1175
+ untrackedEnd(prevU);
1176
+ }
1177
+ firstTime = false;
1178
+ prevValue = newValue;
1179
+ });
1180
+ }
1181
+ warnAboutUntrackedRead() {
1182
+ if (process.env.NODE_ENV === "production")
1183
+ return;
1184
+ if (this.requiresReaction === true) {
1185
+ fail(`[mobx] Computed value ${this.name} is read outside a reactive context`);
1186
+ }
1187
+ if (this.isTracing !== TraceMode.NONE) {
1188
+ console.log(`[mobx.trace] '${this.name}' is being read outside a reactive context. Doing a full recompute`);
1189
+ }
1190
+ if (globalState.computedRequiresReaction) {
1191
+ console.warn(`[mobx] Computed value ${this.name} is being read outside a reactive context. Doing a full recompute`);
1123
1192
  }
1124
1193
  }
1125
- // Some new observed derivations may become stale during this derivation computation
1126
- // so they have had no chance to propagate staleness (#916)
1127
- if (lowestNewObservingDerivationState !== IDerivationState.UP_TO_DATE) {
1128
- derivation.dependenciesState = lowestNewObservingDerivationState;
1129
- derivation.onBecomeStale();
1194
+ toJSON() {
1195
+ return this.get();
1130
1196
  }
1131
- }
1132
- function clearObserving(derivation) {
1133
- // invariant(globalState.inBatch > 0, "INTERNAL ERROR clearObserving should be called only inside batch");
1134
- const obs = derivation.observing;
1135
- derivation.observing = [];
1136
- let i = obs.length;
1137
- while (i--)
1138
- removeObserver(obs[i], derivation);
1139
- derivation.dependenciesState = IDerivationState.NOT_TRACKING;
1140
- }
1141
- function untracked(action) {
1142
- const prev = untrackedStart();
1143
- try {
1144
- return action();
1197
+ toString() {
1198
+ return `${this.name}[${this.derivation.toString()}]`;
1145
1199
  }
1146
- finally {
1147
- untrackedEnd(prev);
1200
+ valueOf() {
1201
+ return toPrimitive(this.get());
1202
+ }
1203
+ [Symbol.toPrimitive]() {
1204
+ return this.valueOf();
1148
1205
  }
1149
1206
  }
1150
- function untrackedStart() {
1151
- const prev = globalState.trackingDerivation;
1152
- globalState.trackingDerivation = null;
1153
- return prev;
1154
- }
1155
- function untrackedEnd(prev) {
1156
- globalState.trackingDerivation = prev;
1157
- }
1158
- /**
1159
- * needed to keep `lowestObserverState` correct. when changing from (2 or 1) to 0
1160
- *
1161
- */
1162
- function changeDependenciesStateTo0(derivation) {
1163
- if (derivation.dependenciesState === IDerivationState.UP_TO_DATE)
1164
- return;
1165
- derivation.dependenciesState = IDerivationState.UP_TO_DATE;
1166
- const obs = derivation.observing;
1167
- let i = obs.length;
1168
- while (i--)
1169
- obs[i].lowestObserverState = IDerivationState.UP_TO_DATE;
1170
- }
1207
+ const isComputedValue = createInstanceofPredicate("ComputedValue", ComputedValue);
1171
1208
 
1172
1209
  /**
1173
1210
  * These values will persist if global state is reset
@@ -1177,6 +1214,9 @@ const persistentKeys = [
1177
1214
  "spyListeners",
1178
1215
  "enforceActions",
1179
1216
  "computedRequiresReaction",
1217
+ "reactionRequiresObservable",
1218
+ "observableRequiresReaction",
1219
+ "allowStateReads",
1180
1220
  "disableErrorBoundaries",
1181
1221
  "runId",
1182
1222
  "UNCHANGED"
@@ -1237,6 +1277,11 @@ class MobXGlobals {
1237
1277
  * To ensure that those functions stay pure.
1238
1278
  */
1239
1279
  this.allowStateChanges = true;
1280
+ /**
1281
+ * Is it allowed to read observables at this point?
1282
+ * Used to hold the state needed for `observableRequiresReaction`
1283
+ */
1284
+ this.allowStateReads = true;
1240
1285
  /**
1241
1286
  * If strict mode is enabled, state changes are by default not allowed
1242
1287
  */
@@ -1253,16 +1298,39 @@ class MobXGlobals {
1253
1298
  * Warn if computed values are accessed outside a reactive context
1254
1299
  */
1255
1300
  this.computedRequiresReaction = false;
1301
+ /**
1302
+ * (Experimental)
1303
+ * Warn if you try to create to derivation / reactive context without accessing any observable.
1304
+ */
1305
+ this.reactionRequiresObservable = false;
1306
+ /**
1307
+ * (Experimental)
1308
+ * Warn if observables are accessed outside a reactive context
1309
+ */
1310
+ this.observableRequiresReaction = false;
1311
+ /**
1312
+ * Allows overwriting of computed properties, useful in tests but not prod as it can cause
1313
+ * memory leaks. See https://github.com/mobxjs/mobx/issues/1867
1314
+ */
1315
+ this.computedConfigurable = false;
1256
1316
  /*
1257
1317
  * Don't catch and rethrow exceptions. This is useful for inspecting the state of
1258
1318
  * the stack when an exception occurs while debugging.
1259
1319
  */
1260
1320
  this.disableErrorBoundaries = false;
1261
1321
  /*
1262
- * If true, we are already handling an exception in an action. Any errors in reactions should be supressed, as
1322
+ * If true, we are already handling an exception in an action. Any errors in reactions should be suppressed, as
1263
1323
  * they are not the cause, see: https://github.com/mobxjs/mobx/issues/1836
1264
1324
  */
1265
1325
  this.suppressReactionErrors = false;
1326
+ /*
1327
+ * Current action id.
1328
+ */
1329
+ this.currentActionId = 0;
1330
+ /*
1331
+ * Next action id.
1332
+ */
1333
+ this.nextActionId = 1;
1266
1334
  }
1267
1335
  }
1268
1336
  let canMergeGlobalState = true;
@@ -1318,8 +1386,15 @@ function resetGlobalState() {
1318
1386
  globalState[key] = defaultGlobals[key];
1319
1387
  globalState.allowStateChanges = !globalState.enforceActions;
1320
1388
  }
1389
+ const mockGlobal = {};
1321
1390
  function getGlobal() {
1322
- return typeof window !== "undefined" ? window : global;
1391
+ if (typeof window !== "undefined") {
1392
+ return window;
1393
+ }
1394
+ if (typeof global !== "undefined") {
1395
+ return global;
1396
+ }
1397
+ return mockGlobal;
1323
1398
  }
1324
1399
 
1325
1400
  function hasObservers(observable) {
@@ -1407,6 +1482,7 @@ function endBatch() {
1407
1482
  }
1408
1483
  }
1409
1484
  function reportObserved(observable) {
1485
+ checkIfStateReadsAreAllowed(observable);
1410
1486
  const derivation = globalState.trackingDerivation;
1411
1487
  if (derivation !== null) {
1412
1488
  /**
@@ -1534,10 +1610,11 @@ function printDepTree(tree, lines, depth) {
1534
1610
  }
1535
1611
 
1536
1612
  class Reaction {
1537
- constructor(name = "Reaction@" + getNextId(), onInvalidate, errorHandler) {
1613
+ constructor(name = "Reaction@" + getNextId(), onInvalidate, errorHandler, requiresObservable = false) {
1538
1614
  this.name = name;
1539
1615
  this.onInvalidate = onInvalidate;
1540
1616
  this.errorHandler = errorHandler;
1617
+ this.requiresObservable = requiresObservable;
1541
1618
  this.observing = []; // nodes we are looking at. Our value depends on these nodes
1542
1619
  this.newObserving = [];
1543
1620
  this.dependenciesState = IDerivationState.NOT_TRACKING;
@@ -1896,7 +1973,7 @@ function autorun(view, opts = EMPTY_OBJECT) {
1896
1973
  // normal autorun
1897
1974
  reaction = new Reaction(name, function () {
1898
1975
  this.track(reactionRunner);
1899
- }, opts.onError);
1976
+ }, opts.onError, opts.requiresObservable);
1900
1977
  }
1901
1978
  else {
1902
1979
  const scheduler = createSchedulerFromOptions(opts);
@@ -1911,7 +1988,7 @@ function autorun(view, opts = EMPTY_OBJECT) {
1911
1988
  reaction.track(reactionRunner);
1912
1989
  });
1913
1990
  }
1914
- }, opts.onError);
1991
+ }, opts.onError, opts.requiresObservable);
1915
1992
  }
1916
1993
  function reactionRunner() {
1917
1994
  view(reaction);
@@ -1950,7 +2027,7 @@ function reaction(expression, effect, opts = EMPTY_OBJECT) {
1950
2027
  isScheduled = true;
1951
2028
  scheduler(reactionRunner);
1952
2029
  }
1953
- }, opts.onError);
2030
+ }, opts.onError, opts.requiresObservable);
1954
2031
  function reactionRunner() {
1955
2032
  isScheduled = false; // Q: move into reaction runner?
1956
2033
  if (r.isDisposed)
@@ -1989,8 +2066,8 @@ function onBecomeUnobserved(thing, arg2, arg3) {
1989
2066
  return interceptHook("onBecomeUnobserved", thing, arg2, arg3);
1990
2067
  }
1991
2068
  function interceptHook(hook, thing, arg2, arg3) {
1992
- const atom = typeof arg2 === "string" ? getAtom(thing, arg2) : getAtom(thing);
1993
- const cb = typeof arg2 === "string" ? arg3 : arg2;
2069
+ const atom = typeof arg3 === "function" ? getAtom(thing, arg2) : getAtom(thing);
2070
+ const cb = typeof arg3 === "function" ? arg3 : arg2;
1994
2071
  const listenersKey = `${hook}Listeners`;
1995
2072
  if (atom[listenersKey]) {
1996
2073
  atom[listenersKey].add(cb);
@@ -2013,7 +2090,7 @@ function interceptHook(hook, thing, arg2, arg3) {
2013
2090
  }
2014
2091
 
2015
2092
  function configure(options) {
2016
- const { enforceActions, computedRequiresReaction, disableErrorBoundaries, reactionScheduler } = options;
2093
+ const { enforceActions, computedRequiresReaction, computedConfigurable, disableErrorBoundaries, reactionScheduler, reactionRequiresObservable, observableRequiresReaction } = options;
2017
2094
  if (options.isolateGlobalState === true) {
2018
2095
  isolateGlobalState();
2019
2096
  }
@@ -2043,6 +2120,16 @@ function configure(options) {
2043
2120
  if (computedRequiresReaction !== undefined) {
2044
2121
  globalState.computedRequiresReaction = !!computedRequiresReaction;
2045
2122
  }
2123
+ if (reactionRequiresObservable !== undefined) {
2124
+ globalState.reactionRequiresObservable = !!reactionRequiresObservable;
2125
+ }
2126
+ if (observableRequiresReaction !== undefined) {
2127
+ globalState.observableRequiresReaction = !!observableRequiresReaction;
2128
+ globalState.allowStateReads = !globalState.observableRequiresReaction;
2129
+ }
2130
+ if (computedConfigurable !== undefined) {
2131
+ globalState.computedConfigurable = !!computedConfigurable;
2132
+ }
2046
2133
  if (disableErrorBoundaries !== undefined) {
2047
2134
  if (disableErrorBoundaries === true)
2048
2135
  console.warn("WARNING: Debug feature only. MobX will NOT recover from errors when `disableErrorBoundaries` is enabled.");
@@ -2094,8 +2181,7 @@ function extendObservableObjectWithProperties(target, properties, decorators, de
2094
2181
  invariant(!isObservable(properties), "Extending an object with another observable (object) is not supported. Please construct an explicit propertymap, using `toJS` if need. See issue #540");
2095
2182
  if (decorators) {
2096
2183
  const keys = getPlainObjectKeys(decorators);
2097
- for (let i in keys) {
2098
- const key = keys[i];
2184
+ for (const key of keys) {
2099
2185
  if (!(key in properties))
2100
2186
  fail(`Trying to declare a decorator for unspecified property '${stringifyKey(key)}'`);
2101
2187
  }
@@ -2104,10 +2190,11 @@ function extendObservableObjectWithProperties(target, properties, decorators, de
2104
2190
  startBatch();
2105
2191
  try {
2106
2192
  const keys = getPlainObjectKeys(properties);
2107
- for (let i in keys) {
2108
- const key = keys[i];
2193
+ for (const key of keys) {
2109
2194
  const descriptor = Object.getOwnPropertyDescriptor(properties, key);
2110
2195
  if (process.env.NODE_ENV !== "production") {
2196
+ if (!isPlainObject(properties))
2197
+ fail(`'extendObservabe' only accepts plain objects as second argument`);
2111
2198
  if (Object.getOwnPropertyDescriptor(target, key))
2112
2199
  fail(`'extendObservable' can only be used to introduce new properties. Use 'set' or 'decorate' instead. The property '${stringifyKey(key)}' already exists on '${target}'`);
2113
2200
  if (isComputed(descriptor.value))
@@ -2813,17 +2900,18 @@ const arrayTraps = {
2813
2900
  set(target, name, value) {
2814
2901
  if (name === "length") {
2815
2902
  target[$mobx].setArrayLength(value);
2816
- return true;
2817
2903
  }
2818
2904
  if (typeof name === "number") {
2819
2905
  arrayExtensions.set.call(target, name, value);
2820
- return true;
2821
2906
  }
2822
- if (!isNaN(name)) {
2907
+ if (typeof name === "symbol" || isNaN(name)) {
2908
+ target[name] = value;
2909
+ }
2910
+ else {
2911
+ // numeric string
2823
2912
  arrayExtensions.set.call(target, parseInt(name), value);
2824
- return true;
2825
2913
  }
2826
- return false;
2914
+ return true;
2827
2915
  },
2828
2916
  preventExtensions(target) {
2829
2917
  fail(`Observable arrays cannot be frozen`);
@@ -3069,7 +3157,7 @@ const arrayExtensions = {
3069
3157
  // which makes it both a 'derivation' and a 'mutation'.
3070
3158
  // so we deviate from the default and just make it an dervitation
3071
3159
  if (process.env.NODE_ENV !== "production") {
3072
- console.warn("[mobx] `observableArray.reverse()` will not update the array in place. Use `observableArray.slice().reverse()` to supress this warning and perform the operation on a copy, or `observableArray.replace(observableArray.slice().reverse())` to reverse & update in place");
3160
+ console.warn("[mobx] `observableArray.reverse()` will not update the array in place. Use `observableArray.slice().reverse()` to suppress this warning and perform the operation on a copy, or `observableArray.replace(observableArray.slice().reverse())` to reverse & update in place");
3073
3161
  }
3074
3162
  const clone = this.slice();
3075
3163
  return clone.reverse.apply(clone, arguments);
@@ -3078,7 +3166,7 @@ const arrayExtensions = {
3078
3166
  // sort by default mutates in place before returning the result
3079
3167
  // which goes against all good practices. Let's not change the array in place!
3080
3168
  if (process.env.NODE_ENV !== "production") {
3081
- console.warn("[mobx] `observableArray.sort()` will not update the array in place. Use `observableArray.slice().sort()` to supress this warning and perform the operation on a copy, or `observableArray.replace(observableArray.slice().sort())` to sort & update in place");
3169
+ console.warn("[mobx] `observableArray.sort()` will not update the array in place. Use `observableArray.slice().sort()` to suppress this warning and perform the operation on a copy, or `observableArray.replace(observableArray.slice().sort())` to sort & update in place");
3082
3170
  }
3083
3171
  const clone = this.slice();
3084
3172
  return clone.sort.apply(clone, arguments);
@@ -3188,9 +3276,16 @@ class ObservableMap {
3188
3276
  return this._data.has(key);
3189
3277
  }
3190
3278
  has(key) {
3191
- if (this._hasMap.has(key))
3192
- return this._hasMap.get(key).get();
3193
- return this._updateHasMapEntry(key, false).get();
3279
+ if (!globalState.trackingDerivation)
3280
+ return this._has(key);
3281
+ let entry = this._hasMap.get(key);
3282
+ if (!entry) {
3283
+ // todo: replace with atom (breaking change)
3284
+ const newEntry = (entry = new ObservableValue(this._has(key), referenceEnhancer, `${this.name}.${stringifyKey(key)}?`, false));
3285
+ this._hasMap.set(key, newEntry);
3286
+ onBecomeUnobserved(newEntry, () => this._hasMap.delete(key));
3287
+ }
3288
+ return entry.get();
3194
3289
  }
3195
3290
  set(key, value) {
3196
3291
  const hasKey = this._has(key);
@@ -3252,16 +3347,10 @@ class ObservableMap {
3252
3347
  return false;
3253
3348
  }
3254
3349
  _updateHasMapEntry(key, value) {
3255
- // optimization; don't fill the hasMap if we are not observing, or remove entry if there are no observers anymore
3256
3350
  let entry = this._hasMap.get(key);
3257
3351
  if (entry) {
3258
3352
  entry.setNewValue(value);
3259
3353
  }
3260
- else {
3261
- entry = new ObservableValue(value, referenceEnhancer, `${this.name}.${stringifyKey(key)}?`, false);
3262
- this._hasMap.set(key, entry);
3263
- }
3264
- return entry;
3265
3354
  }
3266
3355
  _updateValue(key, newValue) {
3267
3356
  const observable = this._data.get(key);
@@ -3896,7 +3985,7 @@ function getAdministrationForComputedPropOwner(owner) {
3896
3985
  function generateComputedPropConfig(propName) {
3897
3986
  return (computedPropertyConfigs[propName] ||
3898
3987
  (computedPropertyConfigs[propName] = {
3899
- configurable: false,
3988
+ configurable: globalState.computedConfigurable,
3900
3989
  enumerable: false,
3901
3990
  get() {
3902
3991
  return getAdministrationForComputedPropOwner(this).read(propName);
@@ -4122,17 +4211,17 @@ function has$1(a, key) {
4122
4211
  }
4123
4212
 
4124
4213
  function makeIterable(iterator) {
4125
- iterator[Symbol.iterator] = self;
4214
+ iterator[Symbol.iterator] = getSelf;
4126
4215
  return iterator;
4127
4216
  }
4128
- function self() {
4217
+ function getSelf() {
4129
4218
  return this;
4130
4219
  }
4131
4220
 
4132
4221
  /*
4133
4222
  The only reason for this file to exist is pure horror:
4134
4223
  Without it rollup can make the bundling fail at any point in time; when it rolls up the files in the wrong order
4135
- it will cause undefined errors (for example because super classes or local variables not being hosted).
4224
+ it will cause undefined errors (for example because super classes or local variables not being hoisted).
4136
4225
  With this file that will still happen,
4137
4226
  but at least in this file we can magically reorder the imports with trial and error until the build succeeds again.
4138
4227
  */
@@ -4164,7 +4253,7 @@ try {
4164
4253
  process.env.NODE_ENV;
4165
4254
  }
4166
4255
  catch (e) {
4167
- const g = typeof window !== "undefined" ? window : global;
4256
+ const g = getGlobal();
4168
4257
  if (typeof process === "undefined")
4169
4258
  g.process = {};
4170
4259
  g.process.env = {};
@@ -4190,4 +4279,4 @@ if (typeof __MOBX_DEVTOOLS_GLOBAL_HOOK__ === "object") {
4190
4279
  });
4191
4280
  }
4192
4281
 
4193
- export { $mobx, IDerivationState, ObservableMap, ObservableSet, Reaction, allowStateChanges as _allowStateChanges, allowStateChangesInsideComputed as _allowStateChangesInsideComputed, getAdministration as _getAdministration, getGlobalState as _getGlobalState, interceptReads as _interceptReads, isComputingDerivation as _isComputingDerivation, resetGlobalState as _resetGlobalState, action, autorun, comparer, computed, configure, createAtom, decorate, entries, extendObservable, flow, get, getAtom, getDebugName, getDependencyTree, getObserverTree, has, intercept, isAction, isArrayLike, isObservableValue as isBoxedObservable, isComputed, isComputedProp, isObservable, isObservableArray, isObservableMap, isObservableObject, isObservableProp, isObservableSet, keys, observable, observe, onBecomeObserved, onBecomeUnobserved, onReactionError, reaction, remove, runInAction, set, spy, toJS, trace, transaction, untracked, values, when };
4282
+ export { $mobx, IDerivationState, ObservableMap, ObservableSet, Reaction, allowStateChanges as _allowStateChanges, allowStateChangesInsideComputed as _allowStateChangesInsideComputed, _endAction, getAdministration as _getAdministration, getGlobalState as _getGlobalState, interceptReads as _interceptReads, isComputingDerivation as _isComputingDerivation, resetGlobalState as _resetGlobalState, _startAction, action, autorun, comparer, computed, configure, createAtom, decorate, entries, extendObservable, flow, get, getAtom, getDebugName, getDependencyTree, getObserverTree, has, intercept, isAction, isArrayLike, isObservableValue as isBoxedObservable, isComputed, isComputedProp, isObservable, isObservableArray, isObservableMap, isObservableObject, isObservableProp, isObservableSet, keys, observable, observe, onBecomeObserved, onBecomeUnobserved, onReactionError, reaction, remove, runInAction, set, spy, toJS, trace, transaction, untracked, values, when };