mobx
Version:
Simple, scalable state management.
5,397 lines • 201 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.mobx = {}));
})(this, (function (exports) { 'use strict';
const niceErrors = {
0: `Invalid value for configuration 'enforceActions', expected 'never', 'always' or 'observed'`,
1(annotationType, key) {
return `Cannot apply '${annotationType}' to '${key.toString()}': Field not found.`;
},
/*
2(prop) {
return `invalid decorator for '${prop.toString()}'`
},
3(prop) {
return `Cannot decorate '${prop.toString()}': action can only be used on properties with a function value.`
},
4(prop) {
return `Cannot decorate '${prop.toString()}': computed can only be used on getter properties.`
},
*/
5: "'keys()' can only be used on observable objects, arrays, sets and maps",
6: "'values()' can only be used on observable objects, arrays, sets and maps",
7: "'entries()' can only be used on observable objects, arrays and maps",
8: "'set()' can only be used on observable objects, arrays and maps",
9: "'remove()' can only be used on observable objects, arrays and maps",
10: "'has()' can only be used on observable objects, arrays and maps",
11: "'get()' can only be used on observable objects, arrays and maps",
12: `Invalid annotation`,
13: `Dynamic observable objects cannot be frozen. If you're passing observables to 3rd party component/function that calls Object.freeze, pass copy instead: toJS(observable)`,
14: "Intercept handlers should return nothing or a change object",
15: `Observable arrays cannot be frozen. If you're passing observables to 3rd party component/function that calls Object.freeze, pass copy instead: toJS(observable)`,
16: `Modification exception: the internal structure of an observable array was changed.`,
19(other) {
return "Cannot initialize from classes that inherit from Map: " + other.constructor.name;
},
20(other) {
return "Cannot initialize map from " + other;
},
21(dataStructure) {
return `Cannot convert to map from '${dataStructure}'`;
},
23: "It is not possible to get index atoms from arrays",
24(thing) {
return "Cannot obtain administration from " + thing;
},
25(property, name) {
return `the entry '${property}' does not exist in the observable map '${name}'`;
},
26: "please specify a property",
27(property, name) {
return `no observable property '${property.toString()}' found on the observable object '${name}'`;
},
28(thing) {
return "Cannot obtain atom from " + thing;
},
29: "Expecting some object",
30: "invalid action stack. did you forget to finish an action?",
31: "missing option for computed: get",
32(name, derivation) {
return `Cycle detected in computation ${name}: ${derivation}`;
},
33(name) {
return `The setter of computed value '${name}' is trying to update itself. Did you intend to update an _observable_ value, instead of the computed property?`;
},
34(name) {
return `[ComputedValue '${name}'] It is not possible to assign a new value to a computed value.`;
},
35: "There are multiple, different versions of MobX active. Make sure MobX is loaded only once or use `configure({ isolateGlobalState: true })`",
36: "isolateGlobalState should be called before MobX is running any reactions",
37(method) {
return `[mobx] \`observableArray.${method}()\` mutates the array in-place, which is not allowed inside a derivation. Use \`array.slice().${method}()\` instead`;
},
38: "'ownKeys()' can only be used on observable objects",
39: "'defineProperty()' can only be used on observable objects",
40(length) {
return "Out of range: " + length;
},
41(other) {
return "Cannot initialize set from " + other;
},
42(key) {
return `Invalid index: '${key}'`;
},
43(annotationType, name, kind) {
return `Cannot apply '${annotationType}' to '${name}' (kind: ${kind}):` + `\n'${annotationType}' can only be used on properties with a function value.`;
},
44(annotationType) {
return `'${annotationType}' can only be used with 'makeObservable'`;
}
};
const errors = niceErrors ;
function die(error, ...args) {
{
let e = typeof error === "string" ? error : errors[error];
if (typeof e === "function") e = e.apply(null, args);
throw new Error(`[MobX] ${e}`);
}
}
// We shorten anything used > 5 times
const assign = Object.assign;
const getDescriptor = Object.getOwnPropertyDescriptor;
const defineProperty = Object.defineProperty;
const objectPrototype = Object.prototype;
const EMPTY_ARRAY = [];
Object.freeze(EMPTY_ARRAY);
const EMPTY_OBJECT = {};
Object.freeze(EMPTY_OBJECT);
const plainObjectString = /*#__PURE__*/Object.toString();
function getNextId() {
return ++globalState.mobxGuid;
}
/**
* Makes sure that the provided function is invoked at most once.
*/
function once(func) {
let invoked = false;
return function () {
if (invoked) {
return;
}
invoked = true;
return func.apply(this, arguments);
};
}
const noop = () => {};
function isFunction(fn) {
return typeof fn === "function";
}
function isStringish(value) {
const t = typeof value;
switch (t) {
case "string":
case "symbol":
case "number":
return true;
}
return false;
}
function isObject(value) {
return value !== null && typeof value === "object";
}
function isPlainObject(value) {
if (!isObject(value)) {
return false;
}
const proto = Object.getPrototypeOf(value);
if (proto == null) {
return true;
}
const protoConstructor = hasProp(proto, "constructor") && proto.constructor;
return typeof protoConstructor === "function" && protoConstructor.toString() === plainObjectString;
}
// https://stackoverflow.com/a/37865170
function isGenerator(obj) {
const constructor = obj == null ? void 0 : obj.constructor;
if (!constructor) {
return false;
}
if ("GeneratorFunction" === constructor.name || "GeneratorFunction" === constructor.displayName) {
return true;
}
return false;
}
function addHiddenProp(object, propName, value) {
defineProperty(object, propName, {
enumerable: false,
writable: true,
configurable: true,
value
});
}
function addHiddenFinalProp(object, propName, value) {
defineProperty(object, propName, {
enumerable: false,
writable: false,
configurable: true,
value
});
}
function createInstanceofPredicate(name, theClass) {
const propName = "isMobX" + name;
theClass.prototype[propName] = true;
return function (x) {
return isObject(x) && x[propName] === true;
};
}
/**
* Yields true for both native and observable Map, even across different windows.
*/
function isES6Map(thing) {
return thing != null && Object.prototype.toString.call(thing) === "[object Map]";
}
/**
* Makes sure a Map is an instance of non-inherited native or observable Map.
*/
function isPlainES6Map(thing) {
const mapProto = Object.getPrototypeOf(thing);
const objectProto = Object.getPrototypeOf(mapProto);
const nullProto = Object.getPrototypeOf(objectProto);
return nullProto === null;
}
/**
* Yields true for both native and observable Set, even across different windows.
*/
function isES6Set(thing) {
return thing != null && Object.prototype.toString.call(thing) === "[object Set]";
}
/**
* Returns the following: own enumerable keys and symbols.
*/
function getPlainObjectKeys(object) {
const keys = Object.keys(object);
const symbols = Object.getOwnPropertySymbols(object);
if (!symbols.length) {
return keys;
}
return [...keys, ...symbols.filter(s => objectPrototype.propertyIsEnumerable.call(object, s))];
}
// From Immer utils
// Returns all own keys, including non-enumerable and symbolic
const ownKeys = Reflect.ownKeys;
function stringifyKey(key) {
if (typeof key === "string") {
return key;
}
if (typeof key === "symbol") {
return key.toString();
}
return new String(key).toString();
}
function toPrimitive(value) {
return value === null ? null : typeof value === "object" ? "" + value : value;
}
function hasProp(target, prop) {
return objectPrototype.hasOwnProperty.call(target, prop);
}
const getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors;
function getFlag(flags, mask) {
return !!(flags & mask);
}
function setFlag(flags, mask, newValue) {
if (newValue) {
flags |= mask;
} else {
flags &= ~mask;
}
return flags;
}
function assert20223DecoratorType(context, types) {
if (!types.includes(context.kind)) {
die(`The decorator applied to '${String(context.name)}' cannot be used on a ${context.kind} element`);
}
}
const $mobx = /*#__PURE__*/Symbol("mobx administration");
class Atom {
/**
* Create a new atom. For debugging purposes it is recommended to give it a name.
* The onBecomeObserved and onBecomeUnobserved callbacks can be used for resource management.
*/
constructor(name_ = "Atom@" + getNextId() ) {
this.name_ = void 0;
this.flags_ = 0b000;
// Allocated lazily on first observer to save memory.
this.observers_ = null;
this.lastAccessedBy_ = 0;
this.lowestObserverState_ = -1 /* IDerivationState_.NOT_TRACKING_ */;
// onBecomeObservedListeners
this.onBOL = void 0;
// onBecomeUnobservedListeners
this.onBUOL = void 0;
this.name_ = name_;
}
// for effective unobserving. BaseAtom has true, for extra optimization, so its onBecomeUnobserved never gets called, because it's not needed
get isBeingObserved() {
return getFlag(this.flags_, 1 /* AtomFlags.isBeingObserved */);
}
set isBeingObserved(newValue) {
this.flags_ = setFlag(this.flags_, 1 /* AtomFlags.isBeingObserved */, newValue);
}
get isPendingUnobservation() {
return getFlag(this.flags_, 2 /* AtomFlags.isPendingUnobservation */);
}
set isPendingUnobservation(newValue) {
this.flags_ = setFlag(this.flags_, 2 /* AtomFlags.isPendingUnobservation */, newValue);
}
get diffValue() {
return getFlag(this.flags_, 4 /* AtomFlags.diffValue */) ? 1 : 0;
}
set diffValue(newValue) {
this.flags_ = setFlag(this.flags_, 4 /* AtomFlags.diffValue */, newValue === 1 ? true : false);
}
onBO() {
if (this.onBOL) {
this.onBOL.forEach(listener => listener());
}
}
onBUO() {
if (this.onBUOL) {
this.onBUOL.forEach(listener => listener());
}
}
/**
* Invoke this method to notify mobx that your atom has been used somehow.
* Returns true if there is currently a reactive context.
*/
reportObserved() {
return reportObserved(this);
}
/**
* Invoke this method _after_ this method has changed to signal mobx that all its observers should invalidate.
*/
reportChanged() {
startBatch();
propagateChanged(this);
endBatch();
}
toString() {
return this.name_;
}
}
const isAtom = /*#__PURE__*/createInstanceofPredicate("Atom", Atom);
function createAtom(name, onBecomeObservedHandler = noop, onBecomeUnobservedHandler = noop) {
const atom = new Atom(name);
// default `noop` listener will not initialize the hook Set
if (onBecomeObservedHandler !== noop) {
atom.onBOL = new Set([onBecomeObservedHandler]);
}
if (onBecomeUnobservedHandler !== noop) {
atom.onBUOL = new Set([onBecomeUnobservedHandler]);
}
return atom;
}
function compareIdentity(a, b) {
return a === b;
}
function compareStructural(a, b) {
return deepEqual(a, b);
}
function compareShallow(a, b) {
return deepEqual(a, b, 1);
}
const compareDefault = Object.is;
function deepEnhancer(v, _, name) {
// primitives can never be made observable; skip the type checks below
if (v === null || typeof v !== "object" && typeof v !== "function") {
return v;
}
// it is an observable already, done
if (isObservable(v)) {
return v;
}
// something that can be converted and mutated?
if (Array.isArray(v)) {
return observable.array(v, {
name
});
}
if (isPlainObject(v)) {
return observable.object(v, undefined, {
name
});
}
if (isES6Map(v)) {
return observable.map(v, {
name
});
}
if (isES6Set(v)) {
return observable.set(v, {
name
});
}
if (typeof v === "function" && !isAction(v) && !isFlow(v)) {
if (isGenerator(v)) {
return flow(v);
} else {
return autoAction(name, v);
}
}
return v;
}
function shallowEnhancer(v, _, name) {
if (v === undefined || v === null) {
return v;
}
if (isObservableObject(v) || isObservableArray(v) || isObservableMap(v) || isObservableSet(v)) {
return v;
}
if (Array.isArray(v)) {
return observable.array(v, {
name,
deep: false
});
}
if (isPlainObject(v)) {
return observable.object(v, undefined, {
name,
deep: false
});
}
if (isES6Map(v)) {
return observable.map(v, {
name,
deep: false
});
}
if (isES6Set(v)) {
return observable.set(v, {
name,
deep: false
});
}
{
die("The shallow modifier / decorator can only used in combination with arrays, objects, maps and sets");
}
}
function referenceEnhancer(newValue) {
// never turn into an observable
return newValue;
}
function refStructEnhancer(v, oldValue) {
if (isObservable(v)) {
die(`observable.struct should not be used with observable values`);
}
if (deepEqual(v, oldValue)) {
return oldValue;
}
return v;
}
const OVERRIDE = "override";
const override = {
annotationType_: OVERRIDE,
make_: make_$6,
extend_: extend_$5
};
function isOverride(annotation) {
return annotation.annotationType_ === OVERRIDE;
}
function make_$6(adm, key) {
// Must not be plain object
if (adm.isPlainObject_) {
die(`Cannot apply '${this.annotationType_}' to '${adm.name_}.${key.toString()}':` + `\n'${this.annotationType_}' cannot be used on plain objects.`);
}
// Must override something
if (!hasProp(adm.appliedAnnotations_, key)) {
die(`'${adm.name_}.${key.toString()}' is annotated with '${this.annotationType_}', ` + `but no such annotated member was found on prototype.`);
}
return 0 /* MakeResult.Cancel */;
}
function extend_$5(adm, key, descriptor, proxyTrap) {
die(44, this.annotationType_);
}
function createActionAnnotation(name, options) {
return {
annotationType_: name,
options_: options,
make_: make_$5,
extend_: extend_$4
};
}
function make_$5(adm, key, descriptor, source) {
var _this$options_;
// bound
if ((_this$options_ = this.options_) != null && _this$options_.bound) {
return this.extend_(adm, key, descriptor, false) === null ? 0 /* MakeResult.Cancel */ : 1 /* MakeResult.Break */;
}
// own
if (source === adm.target_) {
return this.extend_(adm, key, descriptor, false) === null ? 0 /* MakeResult.Cancel */ : 2 /* MakeResult.Continue */;
}
// prototype
if (isAction(descriptor.value)) {
// A prototype could have been annotated already by other constructor,
// rest of the proto chain must be annotated already
return 1 /* MakeResult.Break */;
}
const actionDescriptor = createActionDescriptor(adm, this, key, descriptor, false);
defineProperty(source, key, actionDescriptor);
return 2 /* MakeResult.Continue */;
}
function extend_$4(adm, key, descriptor, proxyTrap) {
const actionDescriptor = createActionDescriptor(adm, this, key, descriptor);
return adm.defineProperty_(key, actionDescriptor, proxyTrap);
}
function decorateAction20223_(annotation, mthd, context) {
{
assert20223DecoratorType(context, ["method", "field"]);
}
const {
kind,
name,
addInitializer
} = context;
const ann = annotation;
const _createAction = m => {
var _ann$options_$name, _ann$options_, _ann$options_$autoAct, _ann$options_2;
return createAction((_ann$options_$name = (_ann$options_ = ann.options_) == null ? void 0 : _ann$options_.name) != null ? _ann$options_$name : name.toString(), m, (_ann$options_$autoAct = (_ann$options_2 = ann.options_) == null ? void 0 : _ann$options_2.autoAction) != null ? _ann$options_$autoAct : false);
};
if (kind == "field") {
return function (initMthd) {
var _ann$options_3;
let mthd = initMthd;
if (!isAction(mthd)) {
mthd = _createAction(mthd);
}
if ((_ann$options_3 = ann.options_) != null && _ann$options_3.bound) {
mthd = mthd.bind(this);
mthd.isMobxAction = true;
}
return mthd;
};
}
if (kind == "method") {
var _ann$options_4;
if (!isAction(mthd)) {
mthd = _createAction(mthd);
}
if ((_ann$options_4 = ann.options_) != null && _ann$options_4.bound) {
addInitializer(function () {
const self = this;
const bound = self[name].bind(self);
bound.isMobxAction = true;
self[name] = bound;
});
}
return mthd;
}
die(43, ann.annotationType_, String(name), kind);
}
function assertActionDescriptor(adm, {
annotationType_
}, key, {
value
}) {
if (!isFunction(value)) {
die(`Cannot apply '${annotationType_}' to '${adm.name_}.${key.toString()}':` + `\n'${annotationType_}' can only be used on properties with a function value.`);
}
}
function createActionDescriptor(adm, annotation, key, descriptor,
// provides ability to disable safeDescriptors for prototypes
safeDescriptors = globalState.safeDescriptors) {
var _annotation$options_, _annotation$options_$, _annotation$options_2, _annotation$options_$2, _annotation$options_3, _annotation$options_4, _adm$proxy_2;
assertActionDescriptor(adm, annotation, key, descriptor);
let {
value
} = descriptor;
if ((_annotation$options_ = annotation.options_) != null && _annotation$options_.bound) {
var _adm$proxy_;
value = value.bind((_adm$proxy_ = adm.proxy_) != null ? _adm$proxy_ : adm.target_);
}
return {
value: createAction((_annotation$options_$ = (_annotation$options_2 = annotation.options_) == null ? void 0 : _annotation$options_2.name) != null ? _annotation$options_$ : key.toString(), value, (_annotation$options_$2 = (_annotation$options_3 = annotation.options_) == null ? void 0 : _annotation$options_3.autoAction) != null ? _annotation$options_$2 : false,
// https://github.com/mobxjs/mobx/discussions/3140
(_annotation$options_4 = annotation.options_) != null && _annotation$options_4.bound ? (_adm$proxy_2 = adm.proxy_) != null ? _adm$proxy_2 : adm.target_ : undefined),
// Non-configurable for classes
// prevents accidental field redefinition in subclass
configurable: safeDescriptors ? adm.isPlainObject_ : true,
// https://github.com/mobxjs/mobx/pull/2641#issuecomment-737292058
enumerable: false,
// Non-obsevable, therefore non-writable
// Also prevents rewriting in subclass constructor
writable: safeDescriptors ? false : true
};
}
function createFlowAnnotation(name, options) {
return {
annotationType_: name,
options_: options,
make_: make_$4,
extend_: extend_$3
};
}
function make_$4(adm, key, descriptor, source) {
var _this$options_;
// own
if (source === adm.target_) {
return this.extend_(adm, key, descriptor, false) === null ? 0 /* MakeResult.Cancel */ : 2 /* MakeResult.Continue */;
}
// prototype
// bound - must annotate protos to support super.flow()
if ((_this$options_ = this.options_) != null && _this$options_.bound && (!hasProp(adm.target_, key) || !isFlow(adm.target_[key]))) {
if (this.extend_(adm, key, descriptor, false) === null) {
return 0 /* MakeResult.Cancel */;
}
}
if (isFlow(descriptor.value)) {
// A prototype could have been annotated already by other constructor,
// rest of the proto chain must be annotated already
return 1 /* MakeResult.Break */;
}
const flowDescriptor = createFlowDescriptor(adm, this, key, descriptor, false, false);
defineProperty(source, key, flowDescriptor);
return 2 /* MakeResult.Continue */;
}
function extend_$3(adm, key, descriptor, proxyTrap) {
var _this$options_2;
const flowDescriptor = createFlowDescriptor(adm, this, key, descriptor, (_this$options_2 = this.options_) == null ? void 0 : _this$options_2.bound);
return adm.defineProperty_(key, flowDescriptor, proxyTrap);
}
function decorateFlow20223_(annotation, mthd, context) {
var _annotation$options_;
{
assert20223DecoratorType(context, ["method"]);
}
const {
name,
addInitializer
} = context;
if (!isFlow(mthd)) {
mthd = flow(mthd);
}
if ((_annotation$options_ = annotation.options_) != null && _annotation$options_.bound) {
addInitializer(function () {
const self = this;
const bound = self[name].bind(self);
bound.isMobXFlow = true;
self[name] = bound;
});
}
return mthd;
}
function assertFlowDescriptor(adm, {
annotationType_
}, key, {
value
}) {
if (!isFunction(value)) {
die(`Cannot apply '${annotationType_}' to '${adm.name_}.${key.toString()}':` + `\n'${annotationType_}' can only be used on properties with a generator function value.`);
}
}
function createFlowDescriptor(adm, annotation, key, descriptor, bound,
// provides ability to disable safeDescriptors for prototypes
safeDescriptors = globalState.safeDescriptors) {
assertFlowDescriptor(adm, annotation, key, descriptor);
let {
value
} = descriptor;
// In case of flow.bound, the descriptor can be from already annotated prototype
if (!isFlow(value)) {
value = flow(value);
}
if (bound) {
var _adm$proxy_;
// We do not keep original function around, so we bind the existing flow
value = value.bind((_adm$proxy_ = adm.proxy_) != null ? _adm$proxy_ : adm.target_);
// This is normally set by `flow`, but `bind` returns new function...
value.isMobXFlow = true;
}
return {
value,
// Non-configurable for classes
// prevents accidental field redefinition in subclass
configurable: safeDescriptors ? adm.isPlainObject_ : true,
// https://github.com/mobxjs/mobx/pull/2641#issuecomment-737292058
enumerable: false,
// Non-obsevable, therefore non-writable
// Also prevents rewriting in subclass constructor
writable: safeDescriptors ? false : true
};
}
function createComputedAnnotation(name, options) {
return {
annotationType_: name,
options_: options,
make_: make_$3,
extend_: extend_$2
};
}
function make_$3(adm, key, descriptor) {
return this.extend_(adm, key, descriptor, false) === null ? 0 /* MakeResult.Cancel */ : 1 /* MakeResult.Break */;
}
function extend_$2(adm, key, descriptor, proxyTrap) {
assertComputedDescriptor(adm, this, key, descriptor);
return adm.defineComputedProperty_(key, assign({}, this.options_, {
get: descriptor.get,
set: descriptor.set
}), proxyTrap);
}
function decorateComputed20223_(annotation, get, context) {
{
assert20223DecoratorType(context, ["getter"]);
}
const ann = annotation;
const {
name: key,
addInitializer
} = context;
let computedValues;
// Defer ComputedValue creation until first access — avoids allocating
// ComputedValues for getters that are never read on a given instance.
// The factory is materialised by ObservableObjectAdministration on demand.
function createComputedValue(target, adm) {
const options = assign({}, ann.options_, {
get,
context: target
});
options.name || (options.name = `${adm.name_}.${key.toString()}` );
return new ComputedValue(options);
}
addInitializer(function () {
var _adm$lazyComputedKeys;
const adm = asObservableObject(this)[$mobx];
const target = this;
const observable = adm.values_.get(key);
if (observable instanceof ComputedValue && observable.derivation !== get) {
adm.values_.delete(key);
}
((_adm$lazyComputedKeys = adm.lazyComputedKeys_) != null ? _adm$lazyComputedKeys : adm.lazyComputedKeys_ = new Map()).set(key, () => createComputedValue(target, adm));
});
return function () {
const adm = this[$mobx];
const observable = adm.values_.get(key);
if (observable instanceof ComputedValue && observable.derivation !== get) {
var _computedValues;
let computed = (_computedValues = computedValues) == null ? void 0 : _computedValues.get(this);
if (!computed) {
var _computedValues2;
computed = createComputedValue(this, adm);
((_computedValues2 = computedValues) != null ? _computedValues2 : computedValues = new WeakMap()).set(this, computed);
}
return computed.get();
}
return adm.getObservablePropValue_(key);
};
}
function assertComputedDescriptor(adm, {
annotationType_
}, key, {
get
}) {
if (!get) {
die(`Cannot apply '${annotationType_}' to '${adm.name_}.${key.toString()}':` + `\n'${annotationType_}' can only be used on getter(+setter) properties.`);
}
}
function createObservableAnnotation(name, options) {
return {
annotationType_: name,
options_: options,
make_: make_$2,
extend_: extend_$1
};
}
function make_$2(adm, key, descriptor) {
return this.extend_(adm, key, descriptor, false) === null ? 0 /* MakeResult.Cancel */ : 1 /* MakeResult.Break */;
}
function extend_$1(adm, key, descriptor, proxyTrap) {
var _this$options_$enhanc, _this$options_;
assertObservableDescriptor(adm, this, key, descriptor);
return adm.defineObservableProperty_(key, descriptor.value, (_this$options_$enhanc = (_this$options_ = this.options_) == null ? void 0 : _this$options_.enhancer_) != null ? _this$options_$enhanc : deepEnhancer, proxyTrap);
}
function decorateObservable20223_(annotation, desc, context) {
{
if (context.kind === "field") {
throw die(`Please use \`@observable accessor ${String(context.name)}\` instead of \`@observable ${String(context.name)}\``);
}
assert20223DecoratorType(context, ["accessor"]);
}
const ann = annotation;
const {
kind,
name
} = context;
if (kind !== "accessor") {
return;
}
// Defer ObservableValue construction until first access. The factory is
// materialised by ObservableObjectAdministration on demand, so unused
// fields on wide classes never pay the per-instance allocation cost.
function registerLazy(target, value) {
var _adm$lazyObservableKe;
const adm = asObservableObject(target)[$mobx];
((_adm$lazyObservableKe = adm.lazyObservableKeys_) != null ? _adm$lazyObservableKe : adm.lazyObservableKeys_ = new Map()).set(name, () => {
var _ann$options_$enhance, _ann$options_;
return new ObservableValue(value, (_ann$options_$enhance = (_ann$options_ = ann.options_) == null ? void 0 : _ann$options_.enhancer_) != null ? _ann$options_$enhance : deepEnhancer, `${adm.name_}.${name.toString()}` , false);
});
return adm;
}
return {
get() {
var _this$$mobx;
const adm = (_this$$mobx = this[$mobx]) != null ? _this$$mobx : registerLazy(this, desc.get.call(this));
return adm.getObservablePropValue_(name);
},
set(value) {
var _this$$mobx2;
const adm = (_this$$mobx2 = this[$mobx]) != null ? _this$$mobx2 : registerLazy(this, value);
return adm.setObservablePropValue_(name, value);
},
init(value) {
registerLazy(this, value);
return value;
}
};
}
function assertObservableDescriptor(adm, {
annotationType_
}, key, descriptor) {
if (!("value" in descriptor)) {
die(`Cannot apply '${annotationType_}' to '${adm.name_}.${key.toString()}':` + `\n'${annotationType_}' cannot be used on getter/setter properties`);
}
}
const AUTO = "true";
const autoAnnotation = /*#__PURE__*/createAutoAnnotation();
function createAutoAnnotation(options) {
return {
annotationType_: AUTO,
options_: options,
make_: make_$1,
extend_
};
}
function make_$1(adm, key, descriptor, source) {
var _this$options_3, _this$options_4;
// getter -> computed
if (descriptor.get) {
return computed.make_(adm, key, descriptor, source);
}
// lone setter -> action setter
if (descriptor.set) {
// TODO make action applicable to setter and delegate to action.make_
const set = isAction(descriptor.set) ? descriptor.set // See #4553
: createAction(key.toString(), descriptor.set);
// own
if (source === adm.target_) {
return adm.defineProperty_(key, {
configurable: globalState.safeDescriptors ? adm.isPlainObject_ : true,
set
}) === null ? 0 /* MakeResult.Cancel */ : 2 /* MakeResult.Continue */;
}
// proto
defineProperty(source, key, {
configurable: true,
set
});
return 2 /* MakeResult.Continue */;
}
// function on proto -> autoAction/flow
if (source !== adm.target_ && typeof descriptor.value === "function") {
var _this$options_2;
if (isGenerator(descriptor.value)) {
var _this$options_;
const flowAnnotation = (_this$options_ = this.options_) != null && _this$options_.autoBind ? flowBound : flow;
return flowAnnotation.make_(adm, key, descriptor, source);
}
const actionAnnotation = (_this$options_2 = this.options_) != null && _this$options_2.autoBind ? autoActionBound : autoAction;
return actionAnnotation.make_(adm, key, descriptor, source);
}
// other -> observable
// Copy props from proto as well, see test:
// "decorate should work with Object.create"
let observableAnnotation = ((_this$options_3 = this.options_) == null ? void 0 : _this$options_3.deep) === false ? observableRef : observable;
// if function respect autoBind option
if (typeof descriptor.value === "function" && (_this$options_4 = this.options_) != null && _this$options_4.autoBind) {
var _adm$proxy_;
descriptor.value = descriptor.value.bind((_adm$proxy_ = adm.proxy_) != null ? _adm$proxy_ : adm.target_);
}
return observableAnnotation.make_(adm, key, descriptor, source);
}
function extend_(adm, key, descriptor, proxyTrap) {
var _this$options_5, _this$options_6;
// getter -> computed
if (descriptor.get) {
return computed.extend_(adm, key, descriptor, proxyTrap);
}
// lone setter -> action setter
if (descriptor.set) {
// TODO make action applicable to setter and delegate to action.extend_
return adm.defineProperty_(key, {
configurable: globalState.safeDescriptors ? adm.isPlainObject_ : true,
set: createAction(key.toString(), descriptor.set)
}, proxyTrap);
}
// other -> observable
// if function respect autoBind option
if (typeof descriptor.value === "function" && (_this$options_5 = this.options_) != null && _this$options_5.autoBind) {
var _adm$proxy_2;
descriptor.value = descriptor.value.bind((_adm$proxy_2 = adm.proxy_) != null ? _adm$proxy_2 : adm.target_);
}
let observableAnnotation = ((_this$options_6 = this.options_) == null ? void 0 : _this$options_6.deep) === false ? observableRef : observable;
return observableAnnotation.extend_(adm, key, descriptor, proxyTrap);
}
function createDecoratorAnnotation(annotation, decorate) {
return assign(function decoratorAnnotation(value, context) {
if (context && typeof context.kind === "string") {
return decorate(annotation, value, context);
}
{
die(`Invalid arguments for \`${annotation.annotationType_}\``);
}
return undefined;
}, annotation);
}
const OBSERVABLE = "observable";
const OBSERVABLE_REF = "observable.ref";
const OBSERVABLE_SHALLOW = "observable.shallow";
const OBSERVABLE_STRUCT = "observable.struct";
// Predefined bags of create observable options, to avoid allocating temporarily option objects
// in the majority of cases
const defaultCreateObservableOptions = {
deep: true,
name: undefined,
defaultDecorator: undefined
};
Object.freeze(defaultCreateObservableOptions);
function asCreateObservableOptions(thing) {
return thing || defaultCreateObservableOptions;
}
const observableAnnotation = /*#__PURE__*/createObservableAnnotation(OBSERVABLE);
const observableRefAnnotation = /*#__PURE__*/createObservableAnnotation(OBSERVABLE_REF, {
enhancer_: referenceEnhancer
});
const observableShallowAnnotation = /*#__PURE__*/createObservableAnnotation(OBSERVABLE_SHALLOW, {
enhancer_: shallowEnhancer
});
const observableStructAnnotation = /*#__PURE__*/createObservableAnnotation(OBSERVABLE_STRUCT, {
enhancer_: refStructEnhancer
});
function createObservableDecoratorAnnotation(annotation) {
return createDecoratorAnnotation(annotation, decorateObservable20223_);
}
function getEnhancerFromOptions(options) {
return options.deep === true ? deepEnhancer : options.deep === false ? referenceEnhancer : getEnhancerFromAnnotation(options.defaultDecorator);
}
function getAnnotationFromOptions(options) {
var _options$defaultDecor;
return options ? (_options$defaultDecor = options.defaultDecorator) != null ? _options$defaultDecor : createAutoAnnotation(options) : undefined;
}
function getEnhancerFromAnnotation(annotation) {
var _annotation$options_$, _annotation$options_;
return !annotation ? deepEnhancer : (_annotation$options_$ = (_annotation$options_ = annotation.options_) == null ? void 0 : _annotation$options_.enhancer_) != null ? _annotation$options_$ : deepEnhancer;
}
/**
* Turns an object, array or function into a reactive structure.
* @param v the value which should become observable.
*/
function createObservable(v, arg2, arg3) {
if (arg2 && typeof arg2.kind === "string") {
return decorateObservable20223_(observableAnnotation, v, arg2);
}
// already observable - ignore
if (isObservable(v)) {
return v;
}
// plain object
if (isPlainObject(v)) {
return observable.object(v, arg2, arg3);
}
// Array
if (Array.isArray(v)) {
return observable.array(v, arg2);
}
// Map
if (isES6Map(v)) {
return observable.map(v, arg2);
}
// Set
if (isES6Set(v)) {
return observable.set(v, arg2);
}
// other object - ignore
if (typeof v === "object" && v !== null) {
return v;
}
// anything else
return observable.box(v, arg2);
}
const observableFactories = {
box(value, options) {
const o = asCreateObservableOptions(options);
return new ObservableValue(value, getEnhancerFromOptions(o), o.name, true, o.equals);
},
array(initialValues, options) {
const o = asCreateObservableOptions(options);
return createObservableArray(initialValues, getEnhancerFromOptions(o), o.name);
},
map(initialValues, options) {
const o = asCreateObservableOptions(options);
return new ObservableMap(initialValues, getEnhancerFromOptions(o), o.name);
},
set(initialValues, options) {
const o = asCreateObservableOptions(options);
return new ObservableSet(initialValues, getEnhancerFromOptions(o), o.name);
},
object(props, annotations, options) {
return initObservable(() => extendObservable(asDynamicObservableObject({}, options), props, annotations));
}
};
const observableRef = /*#__PURE__*/createObservableDecoratorAnnotation(observableRefAnnotation);
const observableShallow = /*#__PURE__*/createObservableDecoratorAnnotation(observableShallowAnnotation);
const observableDeep = /*#__PURE__*/createObservableDecoratorAnnotation(observableAnnotation);
const observableStruct = /*#__PURE__*/createObservableDecoratorAnnotation(observableStructAnnotation);
// eslint-disable-next-line
var observable = /*#__PURE__*/assign(createObservable, observableAnnotation, observableFactories);
const COMPUTED = "computed";
const COMPUTED_STRUCT = "computed.struct";
function createComputedDecoratorAnnotation(annotation) {
return createDecoratorAnnotation(annotation, decorateComputed20223_);
}
const computedAnnotation = /*#__PURE__*/createComputedAnnotation(COMPUTED);
const computedStructAnnotation = /*#__PURE__*/createComputedAnnotation(COMPUTED_STRUCT, {
equals: compareStructural
});
const computedStruct = /*#__PURE__*/createComputedDecoratorAnnotation(computedStructAnnotation);
const computed = function computed(arg1, arg2) {
if (arg2 && typeof arg2.kind === "string") {
return decorateComputed20223_(computedAnnotation, arg1, arg2);
}
if (isPlainObject(arg1)) {
// computed annotation with options
return createComputedDecoratorAnnotation(createComputedAnnotation(COMPUTED, arg1));
}
// computed(expr, options?)
{
if (!isFunction(arg1)) {
die("First argument to `computed` should be an expression.");
}
if (isFunction(arg2)) {
die("A setter as second argument is no longer supported, use `{ set: fn }` option instead");
}
}
const opts = isPlainObject(arg2) ? arg2 : {};
opts.get = arg1;
opts.name || (opts.name = arg1.name || ""); /* for generated name */
return new ComputedValue(opts);
};
assign(computed, computedAnnotation);
var _getDescriptor$config, _getDescriptor;
// we don't use globalState for these in order to avoid possible issues with multiple
// mobx versions
let currentActionId = 0;
let nextActionId = 1;
const isFunctionNameConfigurable = (_getDescriptor$config = (_getDescriptor = /*#__PURE__*/getDescriptor(() => {}, "name")) == null ? void 0 : _getDescriptor.configurable) != null ? _getDescriptor$config : false;
// we can safely recycle this object
const tmpNameDescriptor = {
value: "action",
configurable: true,
writable: false,
enumerable: false
};
function createAction(actionName, fn, autoAction = false, ref) {
{
if (!isFunction(fn)) {
die("`action` can only be invoked on functions");
}
if (typeof actionName !== "string" || !actionName) {
die(`actions should have valid names, got: '${actionName}'`);
}
}
function res() {
return executeAction(actionName, autoAction, fn, ref || this, arguments);
}
res.isMobxAction = true;
res.toString = () => fn.toString();
if (isFunctionNameConfigurable) {
tmpNameDescriptor.value = actionName;
defineProperty(res, "name", tmpNameDescriptor);
}
return res;
}
function executeAction(actionName, canRunAsDerivation, fn, scope, args) {
const runInfo = _startAction(actionName, canRunAsDerivation, scope, args);
try {
return fn.apply(scope, args);
} catch (err) {
runInfo.error_ = err;
throw err;
} finally {
_endAction(runInfo);
}
}
function _startAction(actionName, canRunAsDerivation,
// true for autoAction
scope, args) {
const notifySpy_ = isSpyEnabled() && !!actionName;
let startTime_ = 0;
if (notifySpy_) {
startTime_ = Date.now();
const flattenedArgs = args ? Array.from(args) : EMPTY_ARRAY;
spyReportStart({
type: ACTION,
name: actionName,
object: scope,
arguments: flattenedArgs
});
}
const prevDerivation_ = globalState.trackingDerivation;
const runAsAction = !canRunAsDerivation || !prevDerivation_;
startBatch();
let prevAllowStateChanges_ = globalState.allowStateChanges; // by default preserve previous allow
if (runAsAction) {
untrackedStart();
{
prevAllowStateChanges_ = allowStateChangesStart(true);
}
}
const prevAllowStateReads_ = globalState.allowStateReads;
{
allowStateReadsStart(true);
}
const runInfo = {
runAsAction_: runAsAction,
prevDerivation_,
prevAllowStateChanges_,
prevAllowStateReads_,
notifySpy_,
startTime_,
actionId_: nextActionId++,
parentActionId_: currentActionId
};
currentActionId = runInfo.actionId_;
return runInfo;
}
function _endAction(runInfo) {
if (currentActionId !== runInfo.actionId_) {
die(30);
}
currentActionId = runInfo.parentActionId_;
if (runInfo.error_ !== undefined) {
globalState.suppressReactionErrors = true;
}
{
allowStateChangesEnd(runInfo.prevAllowStateChanges_);
allowStateReadsEnd(runInfo.prevAllowStateReads_);
}
endBatch();
if (runInfo.runAsAction_) {
untrackedEnd(runInfo.prevDerivation_);
}
if (runInfo.notifySpy_) {
spyReportEnd({
time: Date.now() - runInfo.startTime_
});
}
globalState.suppressReactionErrors = false;
}
function allowStateChanges(allowStateChanges, func) {
const prev = allowStateChangesStart(allowStateChanges);
try {
return func();
} finally {
allowStateChangesEnd(prev);
}
}
function allowStateChangesStart(allowStateChanges) {
const prev = globalState.allowStateChanges;
globalState.allowStateChanges = allowStateChanges;
return prev;
}
function allowStateChangesEnd(prev) {
globalState.allowStateChanges = prev;
}
const CREATE = "create";
class ObservableValue extends Atom {
constructor(value, enhancer_, name_ = "ObservableValue@" + getNextId() , notifySpy = true, equals_ = compareDefault) {
super(name_);
this.enhancer_ = void 0;
this.name_ = void 0;
this.equals_ = void 0;
this.hasUnreportedChange_ = false;
this.interceptors_ = void 0;
this.changeListeners_ = void 0;
this.value_ = void 0;
this.dehancer = void 0;
this.enhancer_ = enhancer_;
this.name_ = name_;
this.equals_ = equals_;
this.value_ = enhancer_(value, undefined, name_);
if (notifySpy && isSpyEnabled()) {
var _this$value_;
// only notify spy if this is a stand-alone observable
spyReport({
type: CREATE,
object: this,
observableKind: "value",
debugObjectName: this.name_,
newValue: "" + ((_this$value_ = this.value_) == null ? void 0 : _this$value_.toString())
});
}
}
dehanceValue(value) {
if (this.dehancer !== undefined) {
return this.dehancer(value);
}
return value;
}
set(newValue) {
const oldValue = this.value_;
newValue = this.prepareNewValue_(newValue);
if (newValue !== globalState.UNCHANGED) {
const notifySpy = isSpyEnabled();
if (notifySpy) {
spyReportStart({
type: UPDATE,
object: this,
observableKind: "value",
debugObjectName: this.name_,
newValue,
oldValue
});
}
this.setNewValue_(newValue);
if (notifySpy) {
spyReportEnd();
}
}
}
prepareNewValue_(newValue) {
checkIfStateModificationsAreAllowed(this);
if (hasInterceptors(this)) {
const change = interceptChange(this, {
object: this,
type: UPDATE,
newValue
});
if (!change) {
return globalState.UNCHANGED;
}
newValue = change.newValue;
}
// apply modifier
newValue = this.enhancer_(newValue, this.value_, this.name_);
return this.equals_(this.value_, newValue) ? globalState.UNCHANGED : newValue;
}
setNewValue_(newValue) {
const oldValue = this.value_;
this.value_ = newValue;
this.reportChanged();
if (hasListeners(this)) {
notifyListeners(this, {
type: UPDATE,
object: this,
newValue,
oldValue
});
}
}
get() {
this.reportObserved();
return this.dehanceValue(this.value_);
}
raw() {
// used by MST ot get undehanced value
return this.value_;
}
toJSON() {
return this.get();
}
toString() {
return `${this.name_}[${this.value_}]`;
}
valueOf() {
return toPrimitive(this.get());
}
[Symbol.toPrimitive]() {
return this.valueOf();
}
}
const isObservableValue = /*#__PURE__*/createInstanceofPredicate("ObservableValue", ObservableValue);
class ComputedValue {
/**
* Create a new computed value based on a function expression.
*
* The `name` property is for debug purposes only.
*
* The `equals` property specifies the comparer function used to determine if a newly produced
* value differs from the previous value. Structural comparison can be convenient if you always
* produce a new aggregated object and don't want to notify observers if it is structurally the same.
* This is useful for working with vectors, mouse coordinates etc.
*/
constructor(options) {
this.dependenciesState_ = -1 /* IDerivationState_.NOT_TRACKING_ */;
this.observing_ = [];
// nodes we are looking at. Our value depends on these nodes
this.newObserving_ = null;
// during tracking it's an array with new observed observers
// Lazily allocated on first observer - see Atom.observers_.
this.observers_ = null;
this.runId_ = 0;
this.lastAccessedBy_ = 0;
this.lowestObserverState_ = 0 /* IDerivationState_.UP_TO_DATE_ */;
this.unboundDepsCount_ = 0;
this.value_ = new CaughtException(null);
this.name_ = void 0;
this.triggeredBy_ = void 0;
this.flags_ = 0b00000;
this.derivation = void 0;
// N.B: unminified as it is used by MST
this.setter_ = void 0;
this.scope_ = void 0;
this.equals_ = void 0;
this.requiresReaction_ = void 0;
this.keepAlive_ = void 0;
this.onBOL = void 0;
this.onBUOL = void 0;
if (!options.get) {
die(31);
}
this.derivation = options.get;
this.name_ = options.name || ("ComputedValue@" + getNextId() );
if (options.set) {
this.setter_ = createAction(this.name_ + "-setter" , options.set);
}
this.equals_ = options.equals || compareDefault;
this.scope_ = options.context;
this.requiresReaction_ = options.requiresReaction;
this.keepAlive_ = !!options.keepAlive;
}
onBecomeStale_() {
propagateMaybeChanged(this);
}
onBO() {
if (this.onBOL) {
this.onBOL.forEach(listener => listener());
}
}
onBUO() {
if (this.onBUOL) {
this.onBUOL.forEach(listener => listener());
}
}
// to check for cycles
get isComputing() {
return getFlag(this.flags_, 1 /* ComputedValueFlags.isComputing */);
}
set isComputing(newValue) {
this.flags_ = setFlag(this.flags_, 1 /* ComputedValueFlags.isComputing */, newValue);
}
get isRunningSetter() {
return getFlag(this.flags_, 2 /* ComputedValueFlags.isRunningSetter */);
}
set isRunningSetter(newValue) {
this.flags_ = setFlag(this.flags_, 2 /* ComputedValueFlags.isRunningSetter */, newValue);
}
get isBeingObserved() {
return getFlag(this.flags_, 4 /* ComputedValueFlags.isBeingObserved */);
}
set isBeingObserved(newValue) {
this.flags_ = setFlag(this.flags_, 4 /* ComputedValueFlags.isBeingObserved */, newValue);
}
get isPendingUnobservation() {
return getFlag(this.flags_, 8 /* ComputedValueFlags.isPendingUnobservation */);
}
set isPendingUnobservation(newValue) {
this.flags_ = setFlag(this.flags_, 8 /* ComputedValueFlags.isPendingUnobservation */, newValue);
}
get diffValue() {
return getFlag(this.flags_, 16 /* ComputedValueFlags.diffValue */) ? 1 : 0;
}
set diffValue(newValue) {
this.flags_ = setFlag(this.flags_, 16 /* ComputedValueFlags.diffValue */, newValue === 1 ? true : false);
}
/**
* Returns the current value of this computed value.
* Will evaluate its computation first if needed.
*/
get() {
if (this.isComputing) {
die(32, this.name_, this.derivation);
}
if (globalState.inBatch === 0 && (
// !globalState.trackingDerivatpion &&
!this.observers_ || this.observers_.size === 0) && !this.keepAlive_) {
if (shouldCompute(this)) {
this.warnAboutUntrackedRead_();
startBatch(); // See perf test 'computed memoization'
this.value_ = this.computeValue_(false);
endBatch();
}
} else {
const wasBeingObserved = this.isBeingObserved;
reportObserved(this);
if (shouldCompute(this)) {
let prevTrackingContext = globalState.trackingContext;
if (this.keepAlive_ && !prevTrackingContext) {
globalState.trackingContext = this;
}
if (this.trackAndCompute()) {
propagateChangeConfirmed(this);
}
globalState.trackingContext = prevTrackingContext;
} else if (!wasBeingObserved && this.isBeingObserved) {
// We just became observed while serving a cached value, so the getter
// won't run and won't re-report our dependencies. Cascade to them. #4547
this.observing_.forEach(markObserved);
}
}
const result = this.value_;
if (isCaughtException(result)) {
throw result.cause;
}
return result;
}
set(value) {
if (this.setter_) {
if (this.isRunningSetter) {
die(33, this.name_);
}
this.isRunningSetter = true;
try {
this.setter_.call(this.scope_, value);
} finally {
this.isRunningSetter = false;
}
} else {
die(34, this.name_);
}
}
trackAndCompute() {
// N.B: unminified as it is used by MST
const oldValue = this.value_;
const wasSuspended = /* see #1208 */this.dependenciesState_ === -1 /* IDerivationState_.NOT_TRACKING_ */;
const newValue = this.computeValue_(true);
const changed = wasSuspended || isCaughtException(oldValue) || isCaughtException(newValue) || !this.equals_(oldValue, newValue);
if (changed) {
this.value_ = newValue;
if (isSpyEnabled()) {
spyReport({
observableKind: "computed",
debugObjectName: this.name_,
object: this.scope_,
type: "update",
oldValue,
newValue
});
}
}
return changed;
}
computeValue_(track) {
this.isComputing = true;
// don't allow state changes during computation
const prev = allowStateChangesStart(false) ;
let res;
if (track) {
res = trackDerivedFunction(this, this.derivation, this.scope_);
} else {
if (globalState.disableErrorBoundaries === true) {
res = this.derivation.call(this.scope_);
} else {
try {
res = this.derivation.call(this.scope_);
} catch (e) {
res = new CaughtException(e);
}
}
}
{
allowStateChangesEnd(prev);
}
this.isComputing = false;
return res;
}
suspend_() {
if (!this.keepAlive_) {
clearObserving(this);
this.value_ = undefined; // don't hold on to computed value!
}
}
warnAboutUntrackedRead_() {
if (typeof this.requiresReaction_ === "boolean" ? this.requiresReaction_ : globalState.computedRequiresReaction) {
console.warn(`[mobx] Computed value '${this.name_}' is being read outside a reactive context. Doing a full recompute.`);
}
}
toString() {
return `${this.name_}[${this.derivation.toString()}]`;
}
valueOf() {
return toPrimitive(this.get());
}
[Symbol.toPrimitive]() {
return this.valueOf();
}
}
const isComputedValue = /*#__PURE__*/createInstanceofPredicate("ComputedValue", ComputedValue);
class CaughtException {
constructor(cause) {
this.cause = void 0;
this.cause = cause;
// Empty
}
}
function isCaughtException(e) {
return e instanceof CaughtException;
}
/**
* Finds out whether any dependency of the derivation has actually changed.
* If dependenciesState is 1 then it will recalculate dependencies,
* if any dependency changed it will propagate it by changing dependenciesState to 2.
*
* By iterating over the dependencies in the same order that they were reported and
* stopping on the first change, all the recalculations are only called for ComputedValues
* that will be tracked by derivation. That is because we assume that if the first x
* dependencies of the derivation doesn't change then the derivation should run the same way
* up until accessing x-th dependency.
*/
function shouldCompute(derivation) {
switch (derivation.dependenciesState_) {
case 0 /* IDerivationState_.UP_TO_DATE_ */:
return false;
case -1 /* IDerivationState_.NOT_TRACKING_ */:
case 2 /* IDerivationState_.STALE_ */:
return true;
case 1 /* IDerivationState_.POSSIBLY_STALE_ */:
{
// state propagation can occur outside of action/reactive context #2195
const prevAllowStateReads = allowStateReadsStart(true) ;
const prevUntracked = untrackedStart(); // no need for those computeds to be reported, they will be picked up in trackDerivedFunction.
const obs = derivation.observing_,
l = obs.length;
for (let i = 0; i < l; i++) {
const obj = obs[i];
if (isComputedValue(obj)) {
if (globalState.disableErrorBoundaries) {
obj.get();
} else {
try {
obj.get();
} catch (e) {
// we are not interested in the value *or* exception at this moment, but if there is one, notify all
untrackedEnd(prevUntracked);
{
allowStateReadsEnd(prevAllowStateReads);
}
return true;
}
}
// if ComputedValue `obj` actually changed it will be computed and propagated to its observers.
// and `derivation` is an observer of `obj`
// invariantShouldCompute(derivation)
if (derivation.dependenciesState_ === 2 /* IDerivationState_.STALE_ */) {
untrackedEnd(prevUntracked);
{
allowStateReadsEnd(prevAllowStateReads);
}
return true;
}
}
}
changeDependenciesStateTo0(derivation);
untrackedEnd(prevUntracked);
{
allowStateReadsEnd(prevAllowStateReads);
}
return false;
}
}
}
function isComputingDerivation() {
return globalState.trackingDerivation !== null; // filter out actions inside computations
}
function checkIfStateModificationsAreAllowed(atom) {
const hasObservers = !!atom.observers_ && atom.observers_.size > 0;
// Should not be possible to change observed state outside strict mode, except during initialization, see #563
if (!globalState.allowStateChanges && (hasObservers || globalState.enforceActions === "always")) {
console.warn("[MobX] " + (globalState.enforceActions ? "Since strict-mode is enabled, changing (observed) observable values without using an action is not allowed. Tried to modify: " : "Side effects like changing state are not allowed at this point. Are you trying to modify state from, for example, a computed value or the render function of a React component? You can wrap side effects in 'runInAction' (or decorate functions with 'action') if needed. Tried to modify: ") + atom.name_);
}
}
function checkIfStateReadsAreAllowed(observable) {
if (!globalState.allowStateReads && globalState.observableRequiresReaction) {
console.warn(`[mobx] Observable '${observable.name_}' being read outside a reactive context.`);
}
}
/**
* Executes the provided function `f` and tracks which observables are being accessed.
* The tracking information is stored on the `derivation` object and the derivation is registered
* as observer of any of the accessed observables.
*/
function trackDerivedFunction(derivation, f, context) {
const prevAllowStateReads = allowStateReadsStart(true) ;
changeDependenciesStateTo0(derivation);
// Preallocate array; will be trimmed by bindDependencies.
derivation.newObserving_ = new Array(
// Reserve constant space for initial dependencies, dynamic space otherwise.
// See https://github.com/mobxjs/mobx/pull/3833
derivation.runId_ === 0 ? 100 : derivation.observing_.length);
derivation.unboundDepsCount_ = 0;
derivation.runId_ = ++globalState.runId;
const prevTracking = globalState.trackingDerivation;
globalState.trackingDerivation = derivation;
globalState.inBatch++;
let result;
if (globalState.disableErrorBoundaries === true) {
result = f.call(context);
} else {
try {
result = f.call(context);
} catch (e) {
result = new CaughtException(e);
}
}
globalState.inBatch--;
globalState.trackingDerivation = prevTracking;
bindDependencies(derivation);
warnAboutDerivationWithoutDependencies(derivation);
{
allowStateReadsEnd(prevAllowStateReads);
}
return result;
}
function warnAboutDerivationWithoutDependencies(derivation) {
if (derivation.observing_.length !== 0) {
return;
}
if (typeof derivation.requiresObservable_ === "boolean" ? derivation.requiresObservable_ : globalState.reactionRequiresObservable) {
console.warn(`[mobx] Derivation '${derivation.name_}' is created/updated without reading any observable value.`);
}
}
/**
* diffs newObserving with observing.
* update observing to be newObserving with unique observables
* notify observers that become observed/unobserved
*/
function bindDependencies(derivation) {
// invariant(derivation.dependenciesState !== IDerivationState.NOT_TRACKING, "INTERNAL ERROR bindDependencies expects derivation.dependenciesState !== -1");
const prevObserving = derivation.observing_;
const observing = derivation.observing_ = derivation.newObserving_;
let lowestNewObservingDerivationState = 0 /* IDerivationState_.UP_TO_DATE_ */;
// Go through all new observables and check diffValue: (this list can contain duplicates):
// 0: first occurrence, change to 1 and keep it
// 1: extra occurrence, drop it
let i0 = 0,
l = derivation.unboundDepsCount_;
for (let i = 0; i < l; i++) {
const dep = observing[i];
if (dep.diffValue === 0) {
dep.diffValue = 1;
if (i0 !== i) {
observing[i0] = dep;
}
i0++;
}
// Upcast is 'safe' here, because if dep is IObservable, `dependenciesState` will be undefined,
// not hitting the condition
if (dep.dependenciesState_ > lowestNewObservingDerivationState) {
lowestNewObservingDerivationState = dep.dependenciesState_;
}
}
observing.length = i0;
derivation.newObserving_ = null; // newObserving shouldn't be needed outside tracking (statement moved down to work around FF bug, see #614)
// Go through all old observables and check diffValue: (it is unique after last bindDependencies)
// 0: it's not in new observables, unobserve it
// 1: it keeps being observed, don't want to notify it. change to 0
l = prevObserving.length;
while (l--) {
const dep = prevObserving[l];
if (dep.diffValue === 0) {
removeObserver(dep, derivation);
}
dep.diffValue = 0;
}
// Go through all new observables and check diffValue: (now it should be unique)
// 0: it was set to 0 in last loop. don't need to do anything.
// 1: it wasn't observed, let's observe it. set back to 0
while (i0--) {
const dep = observing[i0];
if (dep.diffValue === 1) {
dep.diffValue = 0;
addObserver(dep, derivation);
}
}
// Some new observed derivations may become stale during this derivation computation
// so they have had no chance to propagate staleness (#916)
if (lowestNewObservingDerivationState !== 0 /* IDerivationState_.UP_TO_DATE_ */) {
derivation.dependenciesState_ = lowestNewObservingDerivationState;
derivation.onBecomeStale_();
}
}
function clearObserving(derivation) {
// invariant(globalState.inBatch > 0, "INTERNAL ERROR clearObserving should be called only inside batch");
const obs = derivation.observing_;
derivation.observing_ = [];
let i = obs.length;
while (i--) {
removeObserver(obs[i], derivation);
}
derivation.dependenciesState_ = -1 /* IDerivationState_.NOT_TRACKING_ */;
}
function untracked(action) {
const prev = untrackedStart();
try {
return action();
} finally {
untrackedEnd(prev);
}
}
function untrackedStart() {
const prev = globalState.trackingDerivation;
globalState.trackingDerivation = null;
return prev;
}
function untrackedEnd(prev) {
globalState.trackingDerivation = prev;
}
function allowStateReadsStart(allowStateReads) {
const prev = globalState.allowStateReads;
globalState.allowStateReads = allowStateReads;
return prev;
}
function allowStateReadsEnd(prev) {
globalState.allowStateReads = prev;
}
/**
* needed to keep `lowestObserverState` correct. when changing from (2 or 1) to 0
*
*/
function changeDependenciesStateTo0(derivation) {
if (derivation.dependenciesState_ === 0 /* IDerivationState_.UP_TO_DATE_ */) {
return;
}
derivation.dependenciesState_ = 0 /* IDerivationState_.UP_TO_DATE_ */;
const obs = derivation.observing_;
let i = obs.length;
while (i--) {
obs[i].lowestObserverState_ = 0 /* IDerivationState_.UP_TO_DATE_ */;
}
}
const MOBX_GLOBALS_VERSION = 7;
/**
* These values will persist if global state is reset
*/
const persistentKeys = ["mobxGuid", "spyListeners", "enforceActions", "computedRequiresReaction", "reactionRequiresObservable", "observableRequiresReaction", "allowStateReads", "disableErrorBoundaries", "runId", "UNCHANGED"];
class MobXGlobals {
constructor() {
/**
* MobXGlobals version.
* MobX compatiblity with other versions loaded in memory as long as this version matches.
* It indicates that the global state still stores similar information
*
* N.B: this version is unrelated to the package version of MobX, and is only the version of the
* internal state storage of MobX, and can be the same across many different package versions
*/
this.version = MOBX_GLOBALS_VERSION;
/**
* globally unique token to signal unchanged
*/
this.UNCHANGED = {};
/**
* Currently running derivation
*/
this.trackingDerivation = null;
/**
* Currently running reaction. This determines if we currently have a reactive context.
* (Tracking derivation is also set for temporal tracking of computed values inside actions,
* but trackingReaction can only be set by a form of Reaction)
*/
this.trackingContext = null;
/**
* Each time a derivation is tracked, it is assigned a unique run-id
*/
this.runId = 0;
/**
* 'guid' for general purpose. Will be persisted amongst resets.
*/
this.mobxGuid = 0;
/**
* Are we in a batch block? (and how many of them)
*/
this.inBatch = 0;
/**
* Observables that don't have observers anymore, and are about to be
* suspended, unless somebody else accesses it in the same batch
*
* @type {IObservable[]}
*/
this.pendingUnobservations = [];
/**
* List of scheduled, not yet executed, reactions.
*/
this.pendingReactions = [];
/**
* Are we currently processing reactions?
*/
this.isRunningReactions = false;
/**
* Are we currently draining pendingUnobservations in endBatch?
* An onBecomeUnobserved handler can dispose a Reaction, which calls
* startBatch/endBatch again; this guards against re-entering the same
* drain loop recursively (see endBatch in observable.ts).
*/
this.isRunningUnobservations = false;
/**
* Is it allowed to change observables at this point?
* In general, MobX doesn't allow that when running computations and React.render.
* To ensure that those functions stay pure.
*/
this.allowStateChanges = false;
/**
* Is it allowed to read observables at this point?
* Used to hold the state needed for `observableRequiresReaction`
*/
this.allowStateReads = true;
/**
* If strict mode is enabled, state changes are by default not allowed
*/
this.enforceActions = true;
/**
* Spy callbacks
*/
this.spyListeners = [];
/**
* Globally attached error handlers that react specifically to errors in reactions
*/
this.globalReactionErrorHandlers = [];
/**
* Warn if computed values are accessed outside a reactive context
*/
this.computedRequiresReaction = false;
/**
* (Experimental)
* Warn if you try to create to derivation / reactive context without accessing any observable.
*/
this.reactionRequiresObservable = false;
/**
* (Experimental)
* Warn if observables are accessed outside a reactive context
*/
this.observableRequiresReaction = false;
/*
* Don't catch and rethrow exceptions. This is useful for inspecting the state of
* the stack when an exception occurs while debugging.
*/
this.disableErrorBoundaries = false;
/*
* If true, we are already handling an exception in an action. Any errors in reactions should be suppressed, as
* they are not the cause, see: https://github.com/mobxjs/mobx/issues/1836
*/
this.suppressReactionErrors = false;
/**
* False forces all object's descriptors to
* writable: true
* configurable: true
*/
this.safeDescriptors = true;
}
}
let canMergeGlobalState = true;
let isolateCalled = false;
let globalState = /*#__PURE__*/function () {
let global = globalThis;
if (global.__mobxInstanceCount > 0 && !global.__mobxGlobals) {
canMergeGlobalState = false;
}
if (global.__mobxGlobals && global.__mobxGlobals.version !== MOBX_GLOBALS_VERSION) {
canMergeGlobalState = false;
}
if (!canMergeGlobalState) {
// Because this is a IIFE we need to let isolateCalled a chance to change
// so we run it after the event loop completed at least 1 iteration
setTimeout(() => {
if (!isolateCalled) {
die(35);
}
}, 1);
return new MobXGlobals();
} else if (global.__mobxGlobals) {
global.__mobxInstanceCount += 1;
if (!global.__mobxGlobals.UNCHANGED) {
global.__mobxGlobals.UNCHANGED = {};
} // make merge backward compatible
return global.__mobxGlobals;
} else {
global.__mobxInstanceCount = 1;
return global.__mobxGlobals = /*#__PURE__*/new MobXGlobals();
}
}();
function isolateGlobalState() {
if (globalState.pendingReactions.length || globalState.inBatch || globalState.isRunningReactions) {
die(36);
}
isolateCalled = true;
if (canMergeGlobalState) {
let global = globalThis;
if (--global.__mobxInstanceCount === 0) {
global.__mobxGlobals = undefined;
}
globalState = new MobXGlobals();
}
}
function getGlobalState() {
return globalState;
}
/**
* For testing purposes only; this will break the internal state of existing observables,
* but can be used to get back at a stable state after throwing errors
*/
function resetGlobalState() {
const defaultGlobals = new MobXGlobals();
for (let key in defaultGlobals) {
if (persistentKeys.indexOf(key) === -1) {
globalState[key] = defaultGlobals[key];
}
}
globalState.allowStateChanges = !globalState.enforceActions;
}
function hasObservers(observable) {
return !!observable.observers_ && observable.observers_.size > 0;
}
function getObservers(observable) {
var _observable$observers;
return (_observable$observers = observable.observers_) != null ? _observable$observers : new Set();
}
// function invariantObservers(observable: IObservable) {
// const list = observable.observers
// const map = observable.observersIndexes
// const l = list.length
// for (let i = 0; i < l; i++) {
// const id = list[i].__mapid
// if (i) {
// invariant(map[id] === i, "INTERNAL ERROR maps derivation.__mapid to index in list") // for performance
// } else {
// invariant(!(id in map), "INTERNAL ERROR observer on index 0 shouldn't be held in map.") // for performance
// }
// }
// invariant(
// list.length === 0 || Object.keys(map).length === list.length - 1,
// "INTERNAL ERROR there is no junk in map"
// )
// }
function addObserver(observable, node) {
var _observable$observers2;
((_observable$observers2 = observable.observers_) != null ? _observable$observers2 : observable.observers_ = new Set()).add(node);
if (observable.lowestObserverState_ > node.dependenciesState_) {
observable.lowestObserverState_ = node.dependenciesState_;
}
// invariantObservers(observable);
// invariant(observable._observers.indexOf(node) !== -1, "INTERNAL ERROR didn't add node");
}
function removeObserver(observable, node) {
// invariant(globalState.inBatch > 0, "INTERNAL ERROR, remove should be called only inside batch");
// invariant(observable._observers.indexOf(node) !== -1, "INTERNAL ERROR remove already removed node");
// invariantObservers(observable);
const observers = observable.observers_;
if (!observers) {
return;
}
observers.delete(node);
if (observers.size === 0) {
// deleting last observer
queueForUnobservation(observable);
}
// invariantObservers(observable);
// invariant(observable._observers.indexOf(node) === -1, "INTERNAL ERROR remove already removed node2");
}
function queueForUnobservation(observable) {
if (observable.isPendingUnobservation === false) {
// invariant(observable._observers.length === 0, "INTERNAL ERROR, should only queue for unobservation unobserved observables");
observable.isPendingUnobservation = true;
globalState.pendingUnobservations.push(observable);
}
}
/**
* Batch starts a transaction, at least for purposes of memoizing ComputedValues when nothing else does.
* During a batch `onBecomeUnobserved` will be called at most once per observable.
* Avoids unnecessary recalculations.
*/
function startBatch() {
globalState.inBatch++;
}
function endBatch() {
if (--globalState.inBatch === 0) {
runReactions();
// the batch is actually about to finish, all unobserving should happen here.
// Guard against re-entering this loop: an onBUO handler can dispose a Reaction,
// which calls startBatch/endBatch again while we're still iterating. Bail out of
// the nested call instead of recursing; the outer loop re-reads list.length on
// every iteration, so it picks up anything the nested dispose() pushes onto the
// same pendingUnobservations array.
if (!globalState.isRunningUnobservations) {
globalState.isRunningUnobservations = true;
try {
const list = globalState.pendingUnobservations;
for (let i = 0; i < list.length; i++) {
const observable = list[i];
observable.isPendingUnobservation = false;
if (!observable.observers_ || observable.observers_.size === 0) {
if (observable.isBeingObserved) {
// if this observable had reactive observers, trigger the hooks
observable.isBeingObserved = false;
observable.onBUO();
}
if (observable instanceof ComputedValue) {
// computed values are automatically teared down when the last observer leaves
// this process happens recursively, this computed might be the last observabe of another, etc..
observable.suspend_();
}
}
}
globalState.pendingUnobservations = [];
} finally {
// Always release the guard, even if an onBUO handler (user code) threw,
// otherwise every future endBatch() would see isRunningUnobservations
// stuck true and silently stop draining pendingUnobservations forever.
globalState.isRunningUnobservations = false;
}
}
}
}
/**
* Marks an observable as observed, cascading into the dependencies of a ComputedValue.
* Unobservation already cascades (`suspend_` -> `clearObserving`), observation normally
* only does so by accident: a newly observed computed usually recomputes and re-reports
* its dependencies. When it serves a cached value instead nothing re-reports them, so the
* transition has to be propagated by hand. See #4547.
*/
function markObserved(observable) {
var _observable$observing;
if (observable.isBeingObserved) {
return;
}
observable.isBeingObserved = true;
observable.onBO();
// No queueForUnobservation here: the observer links already exist, so the regular
// suspend_ -> clearObserving -> removeObserver teardown still delivers the onBUO.
(_observable$observing = observable.observing_) == null || _observable$observing.forEach(markObserved);
}
function reportObserved(observable) {
checkIfStateReadsAreAllowed(observable);
const derivation = globalState.trackingDerivation;
if (derivation !== null) {
/**
* Simple optimization, give each derivation run an unique id (runId)
* Check if last time this observable was accessed the same runId is used
* if this is the case, the relation is already known
*/
if (derivation.runId_ !== observable.lastAccessedBy_) {
observable.lastAccessedBy_ = derivation.runId_;
// Tried storing newObserving, or observing, or both as Set, but performance didn't come close...
derivation.newObserving_[derivation.unboundDepsCount_++] = observable;
if (!observable.isBeingObserved && globalState.trackingContext) {
observable.isBeingObserved = true;
observable.onBO();
}
}
return observable.isBeingObserved;
} else if ((!observable.observers_ || observable.observers_.size === 0) && globalState.inBatch > 0) {
queueForUnobservation(observable);
}
return false;
}
// function invariantLOS(observable: IObservable, msg: string) {
// // it's expensive so better not run it in produciton. but temporarily helpful for testing
// const min = getObservers(observable).reduce((a, b) => Math.min(a, b.dependenciesState), 2)
// if (min >= observable.lowestObserverState) return // <- the only assumption about `lowestObserverState`
// throw new Error(
// "lowestObserverState is wrong for " +
// msg +
// " because " +
// min +
// " < " +
// observable.lowestObserverState
// )
// }
/**
* NOTE: current propagation mechanism will in case of self reruning autoruns behave unexpectedly
* It will propagate changes to observers from previous run
* It's hard or maybe impossible (with reasonable perf) to get it right with current approach
* Hopefully self reruning autoruns aren't a feature people should depend on
* Also most basic use cases should be ok
*/
// Called by Atom when its value changes
function propagateChanged(observable) {
var _observable$observers3;
// invariantLOS(observable, "changed start");
if (observable.lowestObserverState_ === 2 /* IDerivationState_.STALE_ */) {
return;
}
observable.lowestObserverState_ = 2 /* IDerivationState_.STALE_ */;
// Ideally we use for..of here, but the downcompiled version is really slow...
(_observable$observers3 = observable.observers_) == null || _observable$observers3.forEach(d => {
if (d.dependenciesState_ === 0 /* IDerivationState_.UP_TO_DATE_ */) {
d.onBecomeStale_();
}
d.dependenciesState_ = 2 /* IDerivationState_.STALE_ */;
});
// invariantLOS(observable, "changed end");
}
// Called by ComputedValue when it recalculate and its value changed
function propagateChangeConfirmed(observable) {
var _observable$observers4;
// invariantLOS(observable, "confirmed start");
if (observable.lowestObserverState_ === 2 /* IDerivationState_.STALE_ */) {
return;
}
observable.lowestObserverState_ = 2 /* IDerivationState_.STALE_ */;
(_observable$observers4 = observable.observers_) == null || _observable$observers4.forEach(d => {
if (d.dependenciesState_ === 1 /* IDerivationState_.POSSIBLY_STALE_ */) {
d.dependenciesState_ = 2 /* IDerivationState_.STALE_ */;
} else if (d.dependenciesState_ === 0 /* IDerivationState_.UP_TO_DATE_ */ // this happens during computing of `d`, just keep lowestObserverState up to date.
) {
observable.lowestObserverState_ = 0 /* IDerivationState_.UP_TO_DATE_ */;
}
});
// invariantLOS(observable, "confirmed end");
}
// Used by computed when its dependency changed, but we don't wan't to immediately recompute.
function propagateMaybeChanged(observable) {
var _observable$observers5;
// invariantLOS(observable, "maybe start");
if (observable.lowestObserverState_ !== 0 /* IDerivationState_.UP_TO_DATE_ */) {
return;
}
observable.lowestObserverState_ = 1 /* IDerivationState_.POSSIBLY_STALE_ */;
(_observable$observers5 = observable.observers_) == null || _observable$observers5.forEach(d => {
if (d.dependenciesState_ === 0 /* IDerivationState_.UP_TO_DATE_ */) {
d.dependenciesState_ = 1 /* IDerivationState_.POSSIBLY_STALE_ */;
d.onBecomeStale_();
}
});
// invariantLOS(observable, "maybe end");
}
class Reaction {
constructor(name_ = "Reaction@" + getNextId() , onInvalidate_, errorHandler_, requiresObservable_) {
this.name_ = void 0;
this.onInvalidate_ = void 0;
this.errorHandler_ = void 0;
this.requiresObservable_ = void 0;
this.observing_ = [];
// nodes we are looking at. Our value depends on these nodes
this.newObserving_ = [];
this.dependenciesState_ = -1 /* IDerivationState_.NOT_TRACKING_ */;
this.runId_ = 0;
this.unboundDepsCount_ = 0;
this.flags_ = 0b00000;
this.name_ = name_;
this.onInvalidate_ = onInvalidate_;
this.errorHandler_ = errorHandler_;
this.requiresObservable_ = requiresObservable_;
}
get isDisposed() {
return getFlag(this.flags_, 1 /* ReactionFlags.isDisposed */);
}
set isDisposed(newValue) {
this.flags_ = setFlag(this.flags_, 1 /* ReactionFlags.isDisposed */, newValue);
}
get isScheduled() {
return getFlag(this.flags_, 2 /* ReactionFlags.isScheduled */);
}
set isScheduled(newValue) {
this.flags_ = setFlag(this.flags_, 2 /* ReactionFlags.isScheduled */, newValue);
}
get isTrackPending() {
return getFlag(this.flags_, 4 /* ReactionFlags.isTrackPending */);
}
set isTrackPending(newValue) {
this.flags_ = setFlag(this.flags_, 4 /* ReactionFlags.isTrackPending */, newValue);
}
get isRunning() {
return getFlag(this.flags_, 8 /* ReactionFlags.isRunning */);
}
set isRunning(newValue) {
this.flags_ = setFlag(this.flags_, 8 /* ReactionFlags.isRunning */, newValue);
}
get diffValue() {
return getFlag(this.flags_, 16 /* ReactionFlags.diffValue */) ? 1 : 0;
}
set diffValue(newValue) {
this.flags_ = setFlag(this.flags_, 16 /* ReactionFlags.diffValue */, newValue === 1 ? true : false);
}
onBecomeStale_() {
this.schedule_();
}
schedule_() {
if (!this.isScheduled) {
this.isScheduled = true;
globalState.pendingReactions.push(this);
runReactions();
}
}
/**
* internal, use schedule() if you intend to kick off a reaction
*/
runReaction_() {
if (!this.isDisposed) {
startBatch();
this.isScheduled = false;
const prev = globalState.trackingContext;
globalState.trackingContext = this;
if (shouldCompute(this)) {
this.isTrackPending = true;
try {
this.onInvalidate_();
if ("development" !== "production" && this.isTrackPending && isSpyEnabled()) {
// onInvalidate didn't trigger track right away..
spyReport({
name: this.name_,
type: "scheduled-reaction"
});
}
} catch (e) {
this.reportExceptionInDerivation_(e);
}
}
globalState.trackingContext = prev;
endBatch();
}
}
track(fn) {
if (this.isDisposed) {
return;
// console.warn("Reaction already disposed") // Note: Not a warning / error in mobx 4 either
}
startBatch();
const notify = isSpyEnabled();
let startTime;
if (notify) {
startTime = Date.now();
spyReportStart({
name: this.name_,
type: "reaction"
});
}
this.isRunning = true;
const prevReaction = globalState.trackingContext; // reactions could create reactions...
globalState.trackingContext = this;
const result = trackDerivedFunction(this, fn, undefined);
globalState.trackingContext = prevReaction;
this.isRunning = false;
this.isTrackPending = false;
if (this.isDisposed) {
// disposed during last run. Clean up everything that was bound after the dispose call.
clearObserving(this);
}
if (isCaughtException(result)) {
this.reportExceptionInDerivation_(result.cause);
}
if (notify) {
spyReportEnd({
time: Date.now() - startTime
});
}
endBatch();
}
reportExceptionInDerivation_(error) {
if (this.errorHandler_) {
this.errorHandler_(error, this);
return;
}
if (globalState.disableErrorBoundaries) {
throw error;
}
const message = `[mobx] Encountered an uncaught exception that was thrown by a reaction or observer component, in: '${this}'` ;
if (!globalState.suppressReactionErrors) {
console.error(message, error);
/** If debugging brought you here, please, read the above message :-). Tnx! */
} else {
console.warn(`[mobx] (error in reaction '${this.name_}' suppressed, fix error of causing action below)`);
} // prettier-ignore
if (isSpyEnabled()) {
spyReport({
type: "error",
name: this.name_,
message,
error: "" + error
});
}
globalState.globalReactionErrorHandlers.forEach(f => f(error, this));
}
dispose() {
if (!this.isDisposed) {
this.isDisposed = true;
if (!this.isRunning) {
// if disposed while running, clean up later. Maybe not optimal, but rare case
startBatch();
clearObserving(this);
endBatch();
}
}
}
getDisposer_(abortSignal) {
const dispose = () => {
this.dispose();
abortSignal == null || abortSignal.removeEventListener == null || abortSignal.removeEventListener("abort", dispose);
};
abortSignal == null || abortSignal.addEventListener == null || abortSignal.addEventListener("abort", dispose);
dispose[$mobx] = this;
if ("dispose" in Symbol && typeof Symbol.dispose === "symbol") {
dispose[Symbol.dispose] = dispose;
}
return dispose;
}
toString() {
return `Reaction[${this.name_}]`;
}
}
function onReactionError(handler) {
globalState.globalReactionErrorHandlers.push(handler);
return () => {
const idx = globalState.globalReactionErrorHandlers.indexOf(handler);
if (idx >= 0) {
globalState.globalReactionErrorHandlers.splice(idx, 1);
}
};
}
/**
* Magic number alert!
* Defines within how many times a reaction is allowed to re-trigger itself
* until it is assumed that this is gonna be a never ending loop...
*/
const MAX_REACTION_ITERATIONS = 100;
let reactionScheduler = f => f();
function runReactions() {
// Trampolining, if runReactions are already running, new reactions will be picked up
if (globalState.inBatch > 0 || globalState.isRunningReactions) {
return;
}
reactionScheduler(runReactionsHelper);
}
function runReactionsHelper() {
globalState.isRunningReactions = true;
const allReactions = globalState.pendingReactions;
let iterations = 0;
// While running reactions, new reactions might be triggered.
// Hence we work with two variables and check whether
// we converge to no remaining reactions after a while.
while (allReactions.length > 0) {
if (++iterations === MAX_REACTION_ITERATIONS) {
console.error(`Reaction doesn't converge to a stable state after ${MAX_REACTION_ITERATIONS} iterations.` + ` Probably there is a cycle in the reactive function: ${allReactions[0]}` );
allReactions.splice(0); // clear reactions
}
let remainingReactions = allReactions.splice(0);
for (let i = 0, l = remainingReactions.length; i < l; i++) {
remainingReactions[i].runReaction_();
}
}
globalState.isRunningReactions = false;
}
const isReaction = /*#__PURE__*/createInstanceofPredicate("Reaction", Reaction);
function setReactionScheduler(fn) {
const baseScheduler = reactionScheduler;
reactionScheduler = f => fn(() => baseScheduler(f));
}
function isSpyEnabled() {
return !!globalState.spyListeners.length;
}
function spyReport(event) {
if (!globalState.spyListeners.length) {
return;
}
const listeners = globalState.spyListeners;
for (let i = 0, l = listeners.length; i < l; i++) {
listeners[i](event);
}
}
function spyReportStart(event) {
const change = assign({}, event, {
spyReportStart: true
});
spyReport(change);
}
const END_EVENT = {
type: "report-end",
spyReportEnd: true
};
function spyReportEnd(change) {
if (change) {
spyReport(assign({}, change, {
type: "report-end",
spyReportEnd: true
}));
} else {
spyReport(END_EVENT);
}
}
function spy(listener) {
{
globalState.spyListeners.push(listener);
return once(() => {
globalState.spyListeners = globalState.spyListeners.filter(l => l !== listener);
});
}
}
const ACTION = "action";
const ACTION_BOUND = "action.bound";
const AUTOACTION = "autoAction";
const AUTOACTION_BOUND = "autoAction.bound";
const DEFAULT_ACTION_NAME = "<unnamed action>";
const actionAnnotation = /*#__PURE__*/createActionAnnotation(ACTION);
const actionBoundAnnotation = /*#__PURE__*/createActionAnnotation(ACTION_BOUND, {
bound: true
});
const autoActionAnnotation = /*#__PURE__*/createActionAnnotation(AUTOACTION, {
autoAction: true
});
const autoActionBoundAnnotation = /*#__PURE__*/createActionAnnotation(AUTOACTION_BOUND, {
autoAction: true,
bound: true
});
function createActionDecoratorAnnotation(annotation) {
return createDecoratorAnnotation(annotation, decorateAction20223_);
}
function createActionFactory(autoAction) {
const res = function action(arg1, arg2) {
if (arg2 && typeof arg2.kind === "string") {
return decorateAction20223_(autoAction ? autoActionAnnotation : actionAnnotation, arg1, arg2);
}
// action(fn() {})
if (isFunction(arg1)) {
return createAction(arg1.name || DEFAULT_ACTION_NAME, arg1, autoAction);
}
// action("name", fn() {})
if (isFunction(arg2)) {
return createAction(arg1, arg2, autoAction);
}
// action("name") annotation
if (isStringish(arg1)) {
return createActionDecoratorAnnotation(createActionAnnotation(autoAction ? AUTOACTION : ACTION, {
name: arg1,
autoAction
}));
}
{
die("Invalid arguments for `action`");
}
};
return res;
}
const action = /*#__PURE__*/createActionFactory(false);
assign(action, actionAnnotation);
const autoAction = /*#__PURE__*/createActionFactory(true);
assign(autoAction, autoActionAnnotation);
const actionBound = /*#__PURE__*/createActionDecoratorAnnotation(actionBoundAnnotation);
const autoActionBound = /*#__PURE__*/createActionDecoratorAnnotation(autoActionBoundAnnotation);
function runInAction(fn) {
return executeAction(fn.name || DEFAULT_ACTION_NAME, false, fn, this, undefined);
}
function isAction(thing) {
return isFunction(thing) && thing.isMobxAction === true;
}
/**
* Creates a named reactive view and keeps it alive, so that the view is always
* updated if one of the dependencies changes, even when the view is not further used by something else.
* @param view The reactive view
* @returns disposer function, which can be used to stop the view from being updated in the future.
*/
function autorun(view, opts = EMPTY_OBJECT) {
var _opts$name, _opts$signal;
{
if (!isFunction(view)) {
die("Autorun expects a function as first argument");
}
if (isAction(view)) {
die("Autorun does not accept actions since actions are untrackable");
}
}
const name = (_opts$name = opts == null ? void 0 : opts.name) != null ? _opts$name : view.name || "Autorun@" + getNextId() ;
const runSync = !opts.scheduler && !opts.delay;
let reaction;
if (runSync) {
// normal autorun
reaction = new Reaction(name, function () {
this.track(reactionRunner);
}, opts.onError, opts.requiresObservable);
} else {
const scheduler = createSchedulerFromOptions(opts);
// debounced autorun
let isScheduled = false;
reaction = new Reaction(name, () => {
if (!isScheduled) {
isScheduled = true;
scheduler(() => {
isScheduled = false;
if (!reaction.isDisposed) {
reaction.track(reactionRunner);
}
});
}
}, opts.onError, opts.requiresObservable);
}
function reactionRunner() {
view(reaction);
}
if (!(opts != null && (_opts$signal = opts.signal) != null && _opts$signal.aborted)) {
reaction.schedule_();
}
return reaction.getDisposer_(opts == null ? void 0 : opts.signal);
}
const run = f => f();
function createSchedulerFromOptions(opts) {
return opts.scheduler ? opts.scheduler : opts.delay ? f => setTimeout(f, opts.delay) : run;
}
function reaction(expression, effect, opts = EMPTY_OBJECT) {
var _opts$name2, _opts$signal2;
{
if (!isFunction(expression) || !isFunction(effect)) {
die("First and second argument to reaction should be functions");
}
if (!isPlainObject(opts)) {
die("Third argument of reactions should be an object");
}
}
const name = (_opts$name2 = opts.name) != null ? _opts$name2 : "Reaction@" + getNextId() ;
const effectAction = action(name, opts.onError ? wrapErrorHandler(opts.onError, effect) : effect);
const runSync = !opts.scheduler && !opts.delay;
const scheduler = createSchedulerFromOptions(opts);
let firstTime = true;
let isScheduled = false;
let value;
const equals = opts.equals || compareDefault;
const r = new Reaction(name, () => {
if (firstTime || runSync) {
reactionRunner();
} else if (!isScheduled) {
isScheduled = true;
scheduler(reactionRunner);
}
}, opts.onError, opts.requiresObservable);
function reactionRunner() {
isScheduled = false;
if (r.isDisposed) {
return;
}
let changed = false;
const oldValue = value;
r.track(() => {
const nextValue = allowStateChanges(false, () => expression(r));
changed = firstTime || !equals(value, nextValue);
value = nextValue;
});
if (firstTime && opts.fireImmediately) {
effectAction(value, oldValue, r);
} else if (!firstTime && changed) {
effectAction(value, oldValue, r);
}
firstTime = false;
}
if (!(opts != null && (_opts$signal2 = opts.signal) != null && _opts$signal2.aborted)) {
r.schedule_();
}
return r.getDisposer_(opts == null ? void 0 : opts.signal);
}
function wrapErrorHandler(errorHandler, baseFn) {
return function () {
try {
return baseFn.apply(this, arguments);
} catch (e) {
errorHandler.call(this, e);
}
};
}
const ON_BECOME_OBSERVED = "onBO";
const ON_BECOME_UNOBSERVED = "onBUO";
function onBecomeObserved(thing, arg2, arg3) {
return interceptHook(ON_BECOME_OBSERVED, thing, arg2, arg3);
}
function onBecomeUnobserved(thing, arg2, arg3) {
return interceptHook(ON_BECOME_UNOBSERVED, thing, arg2, arg3);
}
function interceptHook(hook, thing, arg2, arg3) {
const atom = typeof arg3 === "function" ? getAtom(thing, arg2) : getAtom(thing);
const cb = isFunction(arg3) ? arg3 : arg2;
const listenersKey = `${hook}L`;
if (atom[listenersKey]) {
atom[listenersKey].add(cb);
} else {
atom[listenersKey] = new Set([cb]);
}
return function () {
const hookListeners = atom[listenersKey];
if (hookListeners) {
hookListeners.delete(cb);
if (hookListeners.size === 0) {
delete atom[listenersKey];
}
}
};
}
const ALWAYS = "always";
const OBSERVED = "observed";
function configure(options) {
if (options.isolateGlobalState === true) {
isolateGlobalState();
}
const {
enforceActions
} = options;
if (enforceActions !== undefined) {
const ea = enforceActions === ALWAYS ? ALWAYS : enforceActions === OBSERVED;
globalState.enforceActions = ea;
globalState.allowStateChanges = ea === true || ea === ALWAYS ? false : true;
}
["computedRequiresReaction", "reactionRequiresObservable", "observableRequiresReaction", "disableErrorBoundaries", "safeDescriptors"].forEach(key => {
if (key in options) {
globalState[key] = !!options[key];
}
});
globalState.allowStateReads = !globalState.observableRequiresReaction;
if (globalState.disableErrorBoundaries === true) {
console.warn("WARNING: Debug feature only. MobX will NOT recover from errors when `disableErrorBoundaries` is enabled.");
}
if (options.reactionScheduler) {
setReactionScheduler(options.reactionScheduler);
}
}
function extendObservable(target, properties, annotations, options) {
{
if (arguments.length > 4) {
die("'extendObservable' expected 2-4 arguments");
}
if (typeof target !== "object") {
die("'extendObservable' expects an object as first argument");
}
if (isObservableMap(target)) {
die("'extendObservable' should not be used on maps, use map.merge instead");
}
if (!isPlainObject(properties)) {
die(`'extendObservable' only accepts plain objects as second argument`);
}
if (isObservable(properties) || isObservable(annotations)) {
die(`Extending an object with another observable (object) is not supported`);
}
}
// Pull descriptors first, so we don't have to deal with props added by administration ($mobx)
const descriptors = getOwnPropertyDescriptors(properties);
initObservable(() => {
const adm = asObservableObject(target, options)[$mobx];
ownKeys(descriptors).forEach(key => {
adm.extend_(key, descriptors[key],
// must pass "undefined" for { key: undefined }
!annotations ? true : key in annotations ? annotations[key] : true);
});
});
return target;
}
function getDependencyTree(thing, property) {
return nodeToDependencyTree(getAtom(thing, property));
}
function nodeToDependencyTree(node) {
const result = {
name: node.name_
};
if (node.observing_ && node.observing_.length > 0) {
result.dependencies = unique(node.observing_).map(nodeToDependencyTree);
}
return result;
}
function getObserverTree(thing, property) {
return nodeToObserverTree(getAtom(thing, property));
}
function nodeToObserverTree(node) {
const result = {
name: node.name_
};
if (hasObservers(node)) {
result.observers = Array.from(getObservers(node), nodeToObserverTree);
}
return result;
}
function unique(list) {
return Array.from(new Set(list));
}
let generatorId = 0;
class FlowCancellationError extends Error {
constructor() {
super("FLOW_CANCELLED");
Object.setPrototypeOf(this, new.target.prototype);
this.name = "FlowCancellationError";
}
toString() {
return `Error: ${this.message}`;
}
}
function isFlowCancellationError(error) {
return error instanceof FlowCancellationError;
}
function createFlowDecoratorAnnotation(annotation) {
return createDecoratorAnnotation(annotation, decorateFlow20223_);
}
const flowAnnotation = /*#__PURE__*/createFlowAnnotation("flow");
const flowBoundAnnotation = /*#__PURE__*/createFlowAnnotation("flow.bound", {
bound: true
});
const flow = /*#__PURE__*/assign(function flow(arg1, arg2) {
if (arg2 && typeof arg2.kind === "string") {
return decorateFlow20223_(flowAnnotation, arg1, arg2);
}
// flow(fn)
if (arguments.length !== 1) {
die(`Flow expects single argument with generator function`);
}
const generator = arg1;
const name = generator.name || ("<unnamed flow>" );
// Implementation based on https://github.com/tj/co/blob/master/index.js
const res = function res() {
const ctx = this;
const args = arguments;
const runId = ++generatorId ;
const gen = action(`${name} - runid: ${runId} - init` , generator).apply(ctx, args);
let rejector;
let pendingPromise = undefined;
const promise = new Promise(function (resolve, reject) {
let stepId = 0;
rejector = reject;
function onFulfilled(res) {
pendingPromise = undefined;
let ret;
try {
ret = action("development" !== "production" ? `${name} - runid: ${runId} - yield ${stepId++}` : name, gen.next).call(gen, res);
} catch (e) {
return reject(e);
}
next(ret);
}
function onRejected(err) {
pendingPromise = undefined;
let ret;
try {
ret = action("development" !== "production" ? `${name} - runid: ${runId} - yield ${stepId++}` : name, gen.throw).call(gen, err);
} catch (e) {
return reject(e);
}
next(ret);
}
function next(ret) {
if (isFunction(ret == null ? void 0 : ret.then)) {
// an async iterator
ret.then(next, reject);
return;
}
if (ret.done) {
return resolve(ret.value);
}
pendingPromise = Promise.resolve(ret.value);
return pendingPromise.then(onFulfilled, onRejected);
}
onFulfilled(undefined); // kick off the process
});
const cancelActionName = `${name} - runid: ${runId} - cancel` ;
promise.cancel = action(cancelActionName, function () {
try {
if (pendingPromise) {
cancelPromise(pendingPromise);
}
// Finally block can return (or yield) stuff..
const res = gen.return(undefined);
// eat anything that promise would do, it's cancelled!
const yieldedPromise = Promise.resolve(res.value);
yieldedPromise.then(noop, noop);
cancelPromise(yieldedPromise); // maybe it can be cancelled :)
// reject our original promise
rejector(new FlowCancellationError());
} catch (e) {
rejector(e); // there could be a throwing finally block
}
});
return promise;
};
res.isMobXFlow = true;
return res;
}, flowAnnotation);
const flowBound = /*#__PURE__*/createFlowDecoratorAnnotation(flowBoundAnnotation);
function cancelPromise(promise) {
if (isFunction(promise.cancel)) {
promise.cancel();
}
}
function flowResult(result) {
return result; // just tricking TypeScript :)
}
function isFlow(fn) {
return (fn == null ? void 0 : fn.isMobXFlow) === true;
}
function interceptReads(thing, propOrHandler, handler) {
let target;
if (isObservableMap(thing) || isObservableArray(thing) || isObservableValue(thing)) {
target = getAdministration(thing);
} else if (isObservableObject(thing)) {
if (!isStringish(propOrHandler)) {
return die(`InterceptReads can only be used with a specific property, not with an object in general`);
}
target = getAdministration(thing, propOrHandler);
} else {
return die(`Expected observable map, object or array as first array`);
}
if (target.dehancer !== undefined) {
return die(`An intercept reader was already established`);
}
target.dehancer = typeof propOrHandler === "function" ? propOrHandler : handler;
return () => {
target.dehancer = undefined;
};
}
function intercept(thing, propOrHandler, handler) {
if (isFunction(handler)) {
return interceptProperty(thing, propOrHandler, handler);
} else {
return interceptInterceptable(thing, propOrHandler);
}
}
function interceptInterceptable(thing, handler) {
return registerInterceptor(getAdministration(thing), handler);
}
function interceptProperty(thing, property, handler) {
return registerInterceptor(getAdministration(thing, property), handler);
}
function _isComputed(value, property) {
var _adm$lazyComputedKeys;
if (property === undefined) {
return isComputedValue(value);
}
if (isObservableObject(value) === false) {
return false;
}
const adm = value[$mobx];
if ((_adm$lazyComputedKeys = adm.lazyComputedKeys_) != null && _adm$lazyComputedKeys.has(property)) {
return true;
}
if (!adm.values_.has(property)) {
return false;
}
const atom = getAtom(value, property);
return isComputedValue(atom);
}
function isComputed(value) {
if (arguments.length > 1) {
return die(`isComputed expects only 1 argument. Use isComputedProp to inspect the observability of a property`);
}
return _isComputed(value);
}
function isComputedProp(value, propName) {
if (!isStringish(propName)) {
return die(`isComputed expected a property name as second argument`);
}
return _isComputed(value, propName);
}
function _isObservable(value, property) {
if (!value) {
return false;
}
if (property !== undefined) {
if ((isObservableMap(value) || isObservableArray(value))) {
return die("isObservable(object, propertyName) is not supported for arrays and maps. Use map.has or array.length instead.");
}
if (isObservableObject(value)) {
var _adm$lazyComputedKeys, _adm$lazyObservableKe;
const adm = value[$mobx];
return adm.values_.has(property) || !!((_adm$lazyComputedKeys = adm.lazyComputedKeys_) != null && _adm$lazyComputedKeys.has(property)) || !!((_adm$lazyObservableKe = adm.lazyObservableKeys_) != null && _adm$lazyObservableKe.has(property));
}
return false;
}
// For first check, see #701
return isObservableObject(value) || !!value[$mobx] || isAtom(value) || isReaction(value) || isComputedValue(value);
}
function isObservable(value) {
if (arguments.length !== 1) {
die(`isObservable expects only 1 argument. Use isObservableProp to inspect the observability of a property`);
}
return _isObservable(value);
}
function isObservableProp(value, propName) {
if (!isStringish(propName)) {
return die(`expected a property name as second argument`);
}
return _isObservable(value, propName);
}
function keys(obj) {
if (isObservableObject(obj)) {
return obj[$mobx].keys_();
}
if (isObservableMap(obj) || isObservableSet(obj)) {
return Array.from(obj.keys());
}
if (isObservableArray(obj)) {
return obj.map((_, index) => index);
}
die(5);
}
function values(obj) {
if (isObservableObject(obj)) {
return keys(obj).map(key => obj[key]);
}
if (isObservableMap(obj)) {
return keys(obj).map(key => obj.get(key));
}
if (isObservableSet(obj)) {
return Array.from(obj.values());
}
if (isObservableArray(obj)) {
return obj.slice();
}
die(6);
}
function entries(obj) {
if (isObservableObject(obj)) {
return keys(obj).map(key => [key, obj[key]]);
}
if (isObservableMap(obj)) {
return keys(obj).map(key => [key, obj.get(key)]);
}
if (isObservableSet(obj)) {
return Array.from(obj.entries());
}
if (isObservableArray(obj)) {
return obj.map((key, index) => [index, key]);
}
die(7);
}
function set(obj, key, value) {
if (arguments.length === 2 && !isObservableSet(obj)) {
startBatch();
const values = key;
try {
for (let _key in values) {
set(obj, _key, values[_key]);
}
} finally {
endBatch();
}
return;
}
if (isObservableObject(obj)) {
obj[$mobx].set_(key, value);
} else if (isObservableMap(obj)) {
obj.set(key, value);
} else if (isObservableSet(obj)) {
obj.add(key);
} else if (isObservableArray(obj)) {
if (typeof key !== "number") {
key = parseInt(key, 10);
}
if (key < 0) {
die(42, key);
}
startBatch();
if (key >= obj.length) {
obj.length = key + 1;
}
obj[key] = value;
endBatch();
} else {
die(8);
}
}
function remove(obj, key) {
if (isObservableObject(obj)) {
obj[$mobx].delete_(key);
} else if (isObservableMap(obj)) {
obj.delete(key);
} else if (isObservableSet(obj)) {
obj.delete(key);
} else if (isObservableArray(obj)) {
if (typeof key !== "number") {
key = parseInt(key, 10);
}
obj.splice(key, 1);
} else {
die(9);
}
}
function has(obj, key) {
if (isObservableObject(obj)) {
return obj[$mobx].has_(key);
} else if (isObservableMap(obj)) {
return obj.has(key);
} else if (isObservableSet(obj)) {
return obj.has(key);
} else if (isObservableArray(obj)) {
return key >= 0 && key < obj.length;
}
die(10);
}
function get(obj, key) {
if (!has(obj, key)) {
return undefined;
}
if (isObservableObject(obj)) {
return obj[$mobx].get_(key);
} else if (isObservableMap(obj)) {
return obj.get(key);
} else if (isObservableArray(obj)) {
return obj[key];
}
die(11);
}
function apiDefineProperty(obj, key, descriptor) {
if (isObservableObject(obj)) {
return obj[$mobx].defineProperty_(key, descriptor);
}
die(39);
}
function apiOwnKeys(obj) {
if (isObservableObject(obj)) {
return obj[$mobx].ownKeys_();
}
die(38);
}
function observe(thing, propOrCb, cbOrFire, fireImmediately) {
if (isFunction(cbOrFire)) {
return observeObservableProperty(thing, propOrCb, cbOrFire, fireImmediately);
} else {
return observeObservable(thing, propOrCb, cbOrFire);
}
}
function observeObservable(thing, listener, fireImmediately) {
const adm = getAdministration(thing);
if (isObservableArray(thing)) {
if (fireImmediately) {
listener({
observableKind: "array",
object: adm.proxy_,
debugObjectName: adm.atom_.name_,
type: "splice",
index: 0,
added: adm.values_.slice(),
addedCount: adm.values_.length,
removed: [],
removedCount: 0
});
}
} else if (isObservableMap(thing)) {
if (fireImmediately === true) {
die("`observe` doesn't support fireImmediately=true in combination with maps.");
}
} else if (isObservableSet(thing)) {
if (fireImmediately === true) {
die("`observe` doesn't support fireImmediately=true in combination with sets.");
}
} else if (isObservableObject(thing)) {
if (fireImmediately === true) {
die("`observe` doesn't support the fire immediately property for observable objects.");
}
} else {
return observeValue(adm, listener, fireImmediately);
}
return registerListener(adm, listener);
}
function observeObservableProperty(thing, property, listener, fireImmediately) {
return observeValue(getAdministration(thing, property), listener, fireImmediately);
}
function observeValue(adm, listener, fireImmediately) {
if (isComputedValue(adm)) {
let firstTime = true;
let prevValue = undefined;
return autorun(() => {
const newValue = adm.get();
if (!firstTime || fireImmediately) {
const prevU = untrackedStart();
listener({
observableKind: "computed",
debugObjectName: adm.name_,
type: UPDATE,
object: adm,
newValue,
oldValue: prevValue
});
untrackedEnd(prevU);
}
firstTime = false;
prevValue = newValue;
});
}
if (fireImmediately) {
listener({
observableKind: "value",
debugObjectName: adm.name_,
object: adm,
type: UPDATE,
newValue: adm.value_,
oldValue: undefined
});
}
return registerListener(adm, listener);
}
function cache(map, key, value) {
map.set(key, value);
return value;
}
function toJSHelper(source, __alreadySeen) {
if (source == null || typeof source !== "object" || source instanceof Date || !isObservable(source)) {
return source;
}
if (isObservableValue(source) || isComputedValue(source)) {
return toJSHelper(source.get(), __alreadySeen);
}
if (__alreadySeen.has(source)) {
return __alreadySeen.get(source);
}
if (isObservableArray(source)) {
const res = cache(__alreadySeen, source, new Array(source.length));
source.forEach((value, idx) => {
res[idx] = toJSHelper(value, __alreadySeen);
});
return res;
}
if (isObservableSet(source)) {
const res = cache(__alreadySeen, source, new Set());
source.forEach(value => {
res.add(toJSHelper(value, __alreadySeen));
});
return res;
}
if (isObservableMap(source)) {
const res = cache(__alreadySeen, source, new Map());
source.forEach((value, key) => {
res.set(key, toJSHelper(value, __alreadySeen));
});
return res;
} else {
// must be observable object
const res = cache(__alreadySeen, source, {});
apiOwnKeys(source).forEach(key => {
if (objectPrototype.propertyIsEnumerable.call(source, key)) {
res[key] = toJSHelper(source[key], __alreadySeen);
}
});
return res;
}
}
/**
* Recursively converts an observable to it's non-observable native counterpart.
* It does NOT recurse into non-observables, these are left as they are, even if they contain observables.
* Computed and other non-enumerable properties are completely ignored.
* Complex scenarios require custom solution, eg implementing `toJSON` or using `serializr` lib.
*/
function toJS(source, options) {
if (options) {
die("toJS no longer supports options");
}
return toJSHelper(source, new Map());
}
/**
* During a transaction no views are updated until the end of the transaction.
* The transaction will be run synchronously nonetheless.
*
* @param action a function that updates some reactive state
* @returns any value that was returned by the 'action' parameter.
*/
function transaction(action, thisArg = undefined) {
startBatch();
try {
return action.apply(thisArg);
} finally {
endBatch();
}
}
function when(predicate, arg1, arg2) {
if (arguments.length === 1 || arg1 && typeof arg1 === "object") {
return whenPromise(predicate, arg1);
}
return _when(predicate, arg1, arg2 || {});
}
function _when(predicate, effect, opts) {
let timeoutHandle;
if (typeof opts.timeout === "number") {
const error = new Error("WHEN_TIMEOUT");
timeoutHandle = setTimeout(() => {
if (!disposer[$mobx].isDisposed) {
disposer();
if (opts.onError) {
opts.onError(error);
} else {
throw error;
}
}
}, opts.timeout);
}
opts.name = opts.name || "When@" + getNextId() ;
const effectAction = createAction(opts.name + "-effect" , effect);
// eslint-disable-next-line
var disposer = autorun(r => {
// predicate should not change state
let cond = allowStateChanges(false, predicate);
if (cond) {
r.dispose();
if (timeoutHandle) {
clearTimeout(timeoutHandle);
}
effectAction();
}
}, opts);
return disposer;
}
function whenPromise(predicate, opts) {
var _opts$signal;
if (opts && opts.onError) {
return die(`the options 'onError' and 'promise' cannot be combined`);
}
if (opts != null && (_opts$signal = opts.signal) != null && _opts$signal.aborted) {
return assign(Promise.reject(new Error("WHEN_ABORTED")), {
cancel: () => null
});
}
let cancel;
let abort;
const res = new Promise((resolve, reject) => {
var _opts$signal2;
let disposer = _when(predicate, resolve, assign({}, opts, {
onError: reject
}));
cancel = () => {
disposer();
reject(new Error("WHEN_CANCELLED"));
};
abort = () => {
disposer();
reject(new Error("WHEN_ABORTED"));
};
opts == null || (_opts$signal2 = opts.signal) == null || _opts$signal2.addEventListener == null || _opts$signal2.addEventListener("abort", abort);
}).finally(() => {
var _opts$signal3;
return opts == null || (_opts$signal3 = opts.signal) == null || _opts$signal3.removeEventListener == null ? void 0 : _opts$signal3.removeEventListener("abort", abort);
});
res.cancel = cancel;
return res;
}
function getAdm(target) {
return target[$mobx];
}
// Optimization: we don't need the intermediate objects and could have a completely custom administration for DynamicObjects,
// and skip either the internal values map, or the base object with its property descriptors!
const objectProxyTraps = {
has(target, name) {
return getAdm(target).has_(name);
},
get(target, name) {
return getAdm(target).get_(name);
},
set(target, name, value) {
var _getAdm$set_;
if (!isStringish(name)) {
return false;
}
// null (intercepted) -> true (success)
return (_getAdm$set_ = getAdm(target).set_(name, value, true)) != null ? _getAdm$set_ : true;
},
deleteProperty(target, name) {
var _getAdm$delete_;
if (!isStringish(name)) {
return false;
}
// null (intercepted) -> true (success)
return (_getAdm$delete_ = getAdm(target).delete_(name, true)) != null ? _getAdm$delete_ : true;
},
defineProperty(target, name, descriptor) {
var _getAdm$definePropert;
// null (intercepted) -> true (success)
return (_getAdm$definePropert = getAdm(target).defineProperty_(name, descriptor)) != null ? _getAdm$definePropert : true;
},
ownKeys(target) {
return getAdm(target).ownKeys_();
},
preventExtensions(target) {
die(13);
}
};
function asDynamicObservableObject(target, options) {
var _target$$mobx, _target$$mobx$proxy_;
target = asObservableObject(target, options);
return (_target$$mobx$proxy_ = (_target$$mobx = target[$mobx]).proxy_) != null ? _target$$mobx$proxy_ : _target$$mobx.proxy_ = new Proxy(target, objectProxyTraps);
}
function hasInterceptors(interceptable) {
return interceptable.interceptors_ !== undefined && interceptable.interceptors_.length > 0;
}
function registerInterceptor(interceptable, handler) {
const interceptors = interceptable.interceptors_ || (interceptable.interceptors_ = []);
interceptors.push(handler);
return once(() => {
const idx = interceptors.indexOf(handler);
if (idx !== -1) {
interceptors.splice(idx, 1);
}
});
}
function interceptChange(interceptable, change) {
const prevU = untrackedStart();
try {
// Interceptor can modify the array, copy it to avoid concurrent modification, see #1950
const interceptors = [...(interceptable.interceptors_ || [])];
for (let i = 0, l = interceptors.length; i < l; i++) {
change = interceptors[i](change);
if (change && !change.type) {
die(14);
}
if (!change) {
break;
}
}
return change;
} finally {
untrackedEnd(prevU);
}
}
function hasListeners(listenable) {
return listenable.changeListeners_ !== undefined && listenable.changeListeners_.length > 0;
}
function registerListener(listenable, handler) {
const listeners = listenable.changeListeners_ || (listenable.changeListeners_ = []);
listeners.push(handler);
return once(() => {
const idx = listeners.indexOf(handler);
if (idx !== -1) {
listeners.splice(idx, 1);
}
});
}
function notifyListeners(listenable, change) {
const prevU = untrackedStart();
let listeners = listenable.changeListeners_;
if (!listeners) {
return;
}
listeners = listeners.slice();
for (let i = 0, l = listeners.length; i < l; i++) {
listeners[i](change);
}
untrackedEnd(prevU);
}
function makeObservable(target, annotations, options) {
initObservable(() => {
const adm = asObservableObject(target, options)[$mobx];
// Annotate
ownKeys(annotations).forEach(key => make_(adm, key, annotations[key]));
});
return target;
}
// proto[keysSymbol] = new Set<PropertyKey>()
const keysSymbol = /*#__PURE__*/Symbol("mobx-keys");
function makeAutoObservable(target, overrides, options) {
{
if (!isPlainObject(target) && !isPlainObject(Object.getPrototypeOf(target))) {
die(`'makeAutoObservable' can only be used for classes that don't have a superclass`);
}
if (isObservableObject(target)) {
die(`makeAutoObservable can only be used on objects not already made observable`);
}
}
// Optimization: avoid visiting protos
// Assumes that annotation.make_/.extend_ works the same for plain objects
if (isPlainObject(target)) {
return extendObservable(target, target, overrides, options);
}
initObservable(() => {
const adm = asObservableObject(target, options)[$mobx];
// Optimization: cache keys on proto
// Assumes makeAutoObservable can be called only once per object and can't be used in subclass
if (!target[keysSymbol]) {
const proto = Object.getPrototypeOf(target);
const keys = new Set([...ownKeys(target), ...ownKeys(proto)]);
keys.delete("constructor");
keys.delete($mobx);
addHiddenProp(proto, keysSymbol, keys);
}
target[keysSymbol].forEach(key => make_(adm, key,
// must pass "undefined" for { key: undefined }
!overrides ? true : key in overrides ? overrides[key] : true));
});
return target;
}
function make_(adm, key, annotation) {
if (annotation === true) {
annotation = adm.defaultAnnotation_;
}
if (annotation === false) {
return;
}
assertAnnotable(adm, annotation, key);
if (!(key in adm.target_)) {
die(1, annotation.annotationType_, `${adm.name_}.${key.toString()}`);
}
let source = adm.target_;
while (source && source !== objectPrototype) {
const descriptor = getDescriptor(source, key);
if (descriptor) {
const outcome = annotation.make_(adm, key, descriptor, source);
if (outcome === 0 /* MakeResult.Cancel */) {
return;
}
if (outcome === 1 /* MakeResult.Break */) {
break;
}
}
source = Object.getPrototypeOf(source);
}
recordAnnotationApplied(adm, annotation, key);
}
const SPLICE = "splice";
const UPDATE = "update";
const MAX_SPLICE_SIZE = 10000; // See e.g. https://github.com/mobxjs/mobx/issues/859
const arrayTraps = {
get(target, name) {
const adm = target[$mobx];
if (name === $mobx) {
return adm;
}
if (name === "length") {
return adm.getArrayLength_();
}
if (typeof name === "string" && !isNaN(name)) {
return adm.get_(parseInt(name));
}
if (hasProp(arrayExtensions, name)) {
return arrayExtensions[name];
}
return target[name];
},
set(target, name, value) {
const adm = target[$mobx];
if (name === "length") {
adm.setArrayLength_(value);
}
if (typeof name === "symbol" || isNaN(name)) {
target[name] = value;
} else {
// numeric string
adm.set_(parseInt(name), value);
}
return true;
},
preventExtensions() {
die(15);
}
};
class ObservableArrayAdministration {
constructor(name = "ObservableArray@" + getNextId() , enhancer, owned_) {
this.owned_ = void 0;
this.atom_ = void 0;
this.values_ = [];
// this is the prop that gets proxied, so can't replace it!
this.interceptors_ = void 0;
this.changeListeners_ = void 0;
this.enhancer_ = void 0;
this.dehancer = void 0;
this.proxy_ = void 0;
this.lastKnownLength_ = 0;
this.owned_ = owned_;
this.atom_ = new Atom(name);
this.enhancer_ = (newV, oldV) => enhancer(newV, oldV, name + "[..]" );
}
dehanceValue_(value) {
if (this.dehancer !== undefined) {
return this.dehancer(value);
}
return value;
}
dehanceValues_(values) {
if (this.dehancer !== undefined && values.length > 0) {
return values.map(this.dehancer);
}
return values;
}
getArrayLength_() {
this.atom_.reportObserved();
return this.values_.length;
}
setArrayLength_(newLength) {
if (typeof newLength !== "number" || isNaN(newLength) || newLength < 0) {
die(40, newLength);
}
let currentLength = this.values_.length;
if (newLength === currentLength) {
return;
} else if (newLength > currentLength) {
const newItems = Array.from({
length: newLength - currentLength
});
this.spliceWithArray_(currentLength, 0, newItems);
} else {
this.spliceWithArray_(newLength, currentLength - newLength);
}
}
updateArrayLength_(oldLength, delta) {
if (oldLength !== this.lastKnownLength_) {
die(16);
}
this.lastKnownLength_ += delta;
}
spliceWithArray_(index, deleteCount, newItems) {
checkIfStateModificationsAreAllowed(this.atom_);
const length = this.values_.length;
if (index === undefined) {
index = 0;
} else if (index > length) {
index = length;
} else if (index < 0) {
index = Math.max(0, length + index);
}
if (arguments.length === 1) {
deleteCount = length - index;
} else if (deleteCount === undefined || deleteCount === null) {
deleteCount = 0;
} else {
deleteCount = Math.max(0, Math.min(deleteCount, length - index));
}
if (newItems === undefined) {
newItems = EMPTY_ARRAY;
}
if (hasInterceptors(this)) {
const change = interceptChange(this, {
object: this.proxy_,
type: SPLICE,
index,
removedCount: deleteCount,
added: newItems
});
if (!change) {
return EMPTY_ARRAY;
}
deleteCount = change.removedCount;
newItems = change.added;
}
newItems = newItems.length === 0 ? newItems : newItems.map(v => this.enhancer_(v, undefined));
{
const lengthDelta = newItems.length - deleteCount;
this.updateArrayLength_(length, lengthDelta); // checks if internal array wasn't modified
}
const res = this.spliceItemsIntoValues_(index, deleteCount, newItems);
if (deleteCount !== 0 || newItems.length !== 0) {
this.notifyArraySplice_(index, newItems, res);
}
return this.dehanceValues_(res);
}
spliceItemsIntoValues_(index, deleteCount, newItems) {
if (newItems.length < MAX_SPLICE_SIZE) {
return this.values_.splice(index, deleteCount, ...newItems);
} else {
// The items removed by the splice
const res = this.values_.slice(index, index + deleteCount);
// The items that that should remain at the end of the array
let oldItems = this.values_.slice(index + deleteCount);
// New length is the previous length + addition count - deletion count
this.values_.length += newItems.length - deleteCount;
for (let i = 0; i < newItems.length; i++) {
this.values_[index + i] = newItems[i];
}
for (let i = 0; i < oldItems.length; i++) {
this.values_[index + newItems.length + i] = oldItems[i];
}
return res;
}
}
notifyArrayChildUpdate_(index, newValue, oldValue) {
const notifySpy = !this.owned_ && isSpyEnabled();
const notify = hasListeners(this);
const change = notify || notifySpy ? {
observableKind: "array",
object: this.proxy_,
type: UPDATE,
debugObjectName: this.atom_.name_,
index,
newValue,
oldValue
} : null;
// The reason why this is on right hand side here (and not above), is this way the uglifier will drop it, but it won't
// cause any runtime overhead in development mode without NODE_ENV set, unless spying is enabled
if (notifySpy) {
spyReportStart(change);
}
this.atom_.reportChanged();
if (notify) {
notifyListeners(this, change);
}
if (notifySpy) {
spyReportEnd();
}
}
notifyArraySplice_(index, added, removed) {
const notifySpy = !this.owned_ && isSpyEnabled();
const notify = hasListeners(this);
const change = notify || notifySpy ? {
observableKind: "array",
object: this.proxy_,
debugObjectName: this.atom_.name_,
type: SPLICE,
index,
removed,
added,
removedCount: removed.length,
addedCount: added.length
} : null;
if (notifySpy) {
spyReportStart(change);
}
this.atom_.reportChanged();
// conform: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/observe
if (notify) {
notifyListeners(this, change);
}
if (notifySpy) {
spyReportEnd();
}
}
get_(index) {
this.atom_.reportObserved();
return this.dehanceValue_(this.values_[index]);
}
set_(index, newValue) {
const values = this.values_;
if (index < values.length) {
// update at index in range
checkIfStateModificationsAreAllowed(this.atom_);
const oldValue = values[index];
if (hasInterceptors(this)) {
const change = interceptChange(this, {
type: UPDATE,
object: this.proxy_,
// since "this" is the real array we need to pass its proxy
index,
newValue
});
if (!change) {
return;
}
newValue = change.newValue;
}
newValue = this.enhancer_(newValue, oldValue);
const changed = newValue !== oldValue;
if (changed) {
values[index] = newValue;
this.notifyArrayChildUpdate_(index, newValue, oldValue);
}
} else {
// For out of bound index, we don't create an actual sparse array,
// but rather fill the holes with undefined (same as setArrayLength_).
// This could be considered a bug.
const newItems = Array.from({
length: index + 1 - values.length
});
newItems[newItems.length - 1] = newValue;
this.spliceWithArray_(values.length, 0, newItems);
}
}
}
function createObservableArray(initialValues, enhancer, name = "ObservableArray@" + getNextId() , owned = false) {
return initObservable(() => {
const adm = new ObservableArrayAdministration(name, enhancer, owned);
addHiddenFinalProp(adm.values_, $mobx, adm);
const proxy = new Proxy(adm.values_, arrayTraps);
adm.proxy_ = proxy;
if (initialValues && initialValues.length) {
adm.spliceWithArray_(0, 0, initialValues);
}
return proxy;
});
}
// eslint-disable-next-line
var arrayExtensions = {
clear() {
return this.splice(0);
},
replace(newItems) {
const adm = this[$mobx];
return adm.spliceWithArray_(0, adm.values_.length, newItems);
},
// Used by JSON.stringify
toJSON() {
return this.slice();
},
/*
* functions that do alter the internal structure of the array, (based on lib.es6.d.ts)
* since these functions alter the inner structure of the array, the have side effects.
* Because the have side effects, they should not be used in computed function,
* and for that reason the do not call dependencyState.notifyObserved
*/
splice(index, deleteCount, ...newItems) {
const adm = this[$mobx];
switch (arguments.length) {
case 0:
return [];
case 1:
return adm.spliceWithArray_(index);
case 2:
return adm.spliceWithArray_(index, deleteCount);
}
return adm.spliceWithArray_(index, deleteCount, newItems);
},
spliceWithArray(index, deleteCount, newItems) {
return this[$mobx].spliceWithArray_(index, deleteCount, newItems);
},
push(...items) {
const adm = this[$mobx];
adm.spliceWithArray_(adm.values_.length, 0, items);
return adm.values_.length;
},
pop() {
return this.splice(Math.max(this[$mobx].values_.length - 1, 0), 1)[0];
},
shift() {
return this.splice(0, 1)[0];
},
unshift(...items) {
const adm = this[$mobx];
adm.spliceWithArray_(0, 0, items);
return adm.values_.length;
},
reverse() {
// reverse by default mutates in place before returning the result
// which makes it both a 'derivation' and a 'mutation'.
if (globalState.trackingDerivation) {
die(37, "reverse");
}
this.replace(this.slice().reverse());
return this;
},
sort() {
// sort by default mutates in place before returning the result
// which goes against all good practices. Let's not change the array in place!
if (globalState.trackingDerivation) {
die(37, "sort");
}
const copy = this.slice();
copy.sort.apply(copy, arguments);
this.replace(copy);
return this;
},
remove(value) {
const adm = this[$mobx];
const idx = adm.dehanceValues_(adm.values_).indexOf(value);
if (idx > -1) {
this.splice(idx, 1);
return true;
}
return false;
}
};
/**
* Wrap function from prototype
* Without this, everything works as well, but this works
* faster as everything works on unproxied values
*/
addArrayExtension("at", simpleFunc);
addArrayExtension("concat", simpleFunc);
addArrayExtension("flat", simpleFunc);
addArrayExtension("includes", simpleFunc);
addArrayExtension("indexOf", simpleFunc);
addArrayExtension("join", simpleFunc);
addArrayExtension("lastIndexOf", simpleFunc);
addArrayExtension("slice", simpleFunc);
addArrayExtension("toString", simpleFunc);
addArrayExtension("toLocaleString", simpleFunc);
addArrayExtension("toSorted", simpleFunc);
addArrayExtension("toSpliced", simpleFunc);
addArrayExtension("with", simpleFunc);
// map
addArrayExtension("every", mapLikeFunc);
addArrayExtension("filter", mapLikeFunc);
addArrayExtension("find", mapLikeFunc);
addArrayExtension("findIndex", mapLikeFunc);
addArrayExtension("findLast", mapLikeFunc);
addArrayExtension("findLastIndex", mapLikeFunc);
addArrayExtension("flatMap", mapLikeFunc);
addArrayExtension("forEach", mapLikeFunc);
addArrayExtension("map", mapLikeFunc);
addArrayExtension("some", mapLikeFunc);
addArrayExtension("toReversed", mapLikeFunc);
// reduce
addArrayExtension("reduce", reduceLikeFunc);
addArrayExtension("reduceRight", reduceLikeFunc);
function addArrayExtension(funcName, funcFactory) {
if (typeof Array.prototype[funcName] === "function") {
arrayExtensions[funcName] = funcFactory(funcName);
}
}
// Report and delegate to dehanced array
function simpleFunc(funcName) {
return function () {
const adm = this[$mobx];
adm.atom_.reportObserved();
const dehancedValues = adm.dehanceValues_(adm.values_);
return dehancedValues[funcName].apply(dehancedValues, arguments);
};
}
// Make sure callbacks receive correct array arg #2326
function mapLikeFunc(funcName) {
return function (callback, thisArg) {
const adm = this[$mobx];
adm.atom_.reportObserved();
const dehancedValues = adm.dehanceValues_(adm.values_);
return dehancedValues[funcName]((element, index) => {
return callback.call(thisArg, element, index, this);
});
};
}
// Make sure callbacks receive correct array arg #2326
function reduceLikeFunc(funcName) {
return function () {
const adm = this[$mobx];
adm.atom_.reportObserved();
const dehancedValues = adm.dehanceValues_(adm.values_);
// #2432 - reduce behavior depends on arguments.length
const callback = arguments[0];
arguments[0] = (accumulator, currentValue, index) => {
return callback(accumulator, currentValue, index, this);
};
return dehancedValues[funcName].apply(dehancedValues, arguments);
};
}
const isObservableArrayAdministration = /*#__PURE__*/createInstanceofPredicate("ObservableArrayAdministration", ObservableArrayAdministration);
function isObservableArray(thing) {
return isObject(thing) && isObservableArrayAdministration(thing[$mobx]);
}
const ObservableMapMarker = {};
const ADD = "add";
const DELETE = "delete";
// just extend Map? See also https://gist.github.com/nestharus/13b4d74f2ef4a2f4357dbd3fc23c1e54
// But: https://github.com/mobxjs/mobx/issues/1556
class ObservableMap {
constructor(initialData, enhancer_ = deepEnhancer, name_ = "ObservableMap@" + getNextId() ) {
this.enhancer_ = void 0;
this.name_ = void 0;
this[$mobx] = ObservableMapMarker;
this.data_ = void 0;
this.hasMap_ = void 0;
// hasMap, not hashMap >-).
this.keysAtom_ = void 0;
this.interceptors_ = void 0;
this.changeListeners_ = void 0;
this.dehancer = void 0;
this.enhancer_ = enhancer_;
this.name_ = name_;
initObservable(() => {
this.keysAtom_ = createAtom("development" !== "production" ? `${this.name_}.keys()` : "ObservableMap.keys()");
this.data_ = new Map();
this.hasMap_ = new Map();
if (initialData) {
this.merge(initialData);
}
});
}
has_(key) {
return this.data_.has(key);
}
has(key) {
if (!globalState.trackingDerivation) {
return this.has_(key);
}
let entry = this.hasMap_.get(key);
if (!entry) {
const newEntry = entry = new ObservableValue(this.has_(key), referenceEnhancer, `${this.name_}.${stringifyKey(key)}?` , false);
this.hasMap_.set(key, newEntry);
newEntry.onBUOL = new Set([() => this.hasMap_.delete(key)]);
}
return entry.get();
}
set(key, value) {
const hasKey = this.has_(key);
if (hasInterceptors(this)) {
const change = interceptChange(this, {
type: hasKey ? UPDATE : ADD,
object: this,
newValue: value,
name: key
});
if (!change) {
return this;
}
value = change.newValue;
}
if (hasKey) {
this.updateValue_(key, value);
} else {
this.addValue_(key, value);
}
return this;
}
delete(key) {
checkIfStateModificationsAreAllowed(this.keysAtom_);
if (hasInterceptors(this)) {
const change = interceptChange(this, {
type: DELETE,
object: this,
name: key
});
if (!change) {
return false;
}
}
if (this.has_(key)) {
const notifySpy = isSpyEnabled();
const notify = hasListeners(this);
const change = notify || notifySpy ? {
observableKind: "map",
debugObjectName: this.name_,
type: DELETE,
object: this,
oldValue: this.data_.get(key).value_,
name: key
} : null;
if (notifySpy) {
spyReportStart(change);
} // TODO fix type
transaction(() => {
var _this$hasMap_$get;
this.keysAtom_.reportChanged();
(_this$hasMap_$get = this.hasMap_.get(key)) == null || _this$hasMap_$get.setNewValue_(false);
const observable = this.data_.get(key);
observable.setNewValue_(undefined);
this.data_.delete(key);
});
if (notify) {
notifyListeners(this, change);
}
if (notifySpy) {
spyReportEnd();
}
return true;
}
return false;
}
updateValue_(key, newValue) {
const observable = this.data_.get(key);
newValue = observable.prepareNewValue_(newValue);
if (newValue !== globalState.UNCHANGED) {
const notifySpy = isSpyEnabled();
const notify = hasListeners(this);
const change = notify || notifySpy ? {
observableKind: "map",
debugObjectName: this.name_,
type: UPDATE,
object: this,
oldValue: observable.value_,
name: key,
newValue
} : null;
if (notifySpy) {
spyReportStart(change);
} // TODO fix type
observable.setNewValue_(newValue);
if (notify) {
notifyListeners(this, change);
}
if (notifySpy) {
spyReportEnd();
}
}
}
addValue_(key, newValue) {
checkIfStateModificationsAreAllowed(this.keysAtom_);
transaction(() => {
var _this$hasMap_$get2;
const observable = new ObservableValue(newValue, this.enhancer_, `${this.name_}.${stringifyKey(key)}` , false);
this.data_.set(key, observable);
newValue = observable.value_; // value might have been changed
(_this$hasMap_$get2 = this.hasMap_.get(key)) == null || _this$hasMap_$get2.setNewValue_(true);
this.keysAtom_.reportChanged();
});
const notifySpy = isSpyEnabled();
const notify = hasListeners(this);
const change = notify || notifySpy ? {
observableKind: "map",
debugObjectName: this.name_,
type: ADD,
object: this,
name: key,
newValue
} : null;
if (notifySpy) {
spyReportStart(change);
} // TODO fix type
if (notify) {
notifyListeners(this, change);
}
if (notifySpy) {
spyReportEnd();
}
}
get(key) {
if (this.has(key)) {
return this.dehanceValue_(this.data_.get(key).get());
}
return this.dehanceValue_(undefined);
}
getOrInsert(key, value) {
if (!this.has(key)) {
this.set(key, value);
}
return this.get(key);
}
getOrInsertComputed(key, callback) {
if (!this.has(key)) {
this.set(key, callback(key));
}
return this.get(key);
}
dehanceValue_(value) {
if (this.dehancer !== undefined) {
return this.dehancer(value);
}
return value;
}
keys() {
this.keysAtom_.reportObserved();
return this.data_.keys();
}
values() {
const self = this;
const keys = this.keys();
return makeIterableForMap({
next() {
const {
done,
value
} = keys.next();
return {
done,
value: done ? undefined : self.get(value)
};
}
});
}
entries() {
const self = this;
const keys = this.keys();
return makeIterableForMap({
next() {
const {
done,
value
} = keys.next();
return {
done,
value: done ? undefined : [value, self.get(value)]
};
}
});
}
[Symbol.iterator]() {
return this.entries();
}
forEach(callback, thisArg) {
for (const [key, value] of this) {
callback.call(thisArg, value, key, this);
}
}
/** Merge another object into this object, returns this. */
merge(other) {
if (isObservableMap(other)) {
other = new Map(other);
}
transaction(() => {
if (isPlainObject(other)) {
getPlainObjectKeys(other).forEach(key => this.set(key, other[key]));
} else if (Array.isArray(other)) {
other.forEach(([key, value]) => this.set(key, value));
} else if (isES6Map(other)) {
if (!isPlainES6Map(other)) {
die(19, other);
}
other.forEach((value, key) => this.set(key, value));
} else if (other !== null && other !== undefined) {
die(20, other);
}
});
return this;
}
clear() {
transaction(() => {
untracked(() => {
for (const key of this.keys()) {
this.delete(key);
}
});
});
}
replace(values) {
// Implementation requirements:
// - respect ordering of replacement map
// - allow interceptors to run and potentially prevent individual operations
// - don't recreate observables that already exist in original map (so we don't destroy existing subscriptions)
// - don't _keysAtom.reportChanged if the keys of resulting map are indentical (order matters!)
// - note that result map may differ from replacement map due to the interceptors
transaction(() => {
// Convert to map so we can do quick key lookups
const replacementMap = convertToMap(values);
const orderedData = new Map();
// Used for optimization
let keysReportChangedCalled = false;
// Delete keys that don't exist in replacement map
// if the key deletion is prevented by interceptor
// add entry at the beginning of the result map
for (const key of this.data_.keys()) {
// Concurrently iterating/deleting keys
// iterator should handle this correctly
if (!replacementMap.has(key)) {
const deleted = this.delete(key);
// Was the key removed?
if (deleted) {
// _keysAtom.reportChanged() was already called
keysReportChangedCalled = true;
} else {
// Delete prevented by interceptor
const value = this.data_.get(key);
orderedData.set(key, value);
}
}
}
// Merge entries
for (const [key, value] of replacementMap.entries()) {
// We will want to know whether a new key is added
const keyExisted = this.data_.has(key);
// Add or update value
this.set(key, value);
// The addition could have been prevent by interceptor
if (this.data_.has(key)) {
// The update could have been prevented by interceptor
// and also we want to preserve existing values
// so use value from _data map (instead of replacement map)
const _value = this.data_.get(key);
orderedData.set(key, _value);
// Was a new key added?
if (!keyExisted) {
// _keysAtom.reportChanged() was already called
keysReportChangedCalled = true;
}
}
}
// Check for possible key order change
if (!keysReportChangedCalled) {
if (this.data_.size !== orderedData.size) {
// If size differs, keys are definitely modified
this.keysAtom_.reportChanged();
} else {
const iter1 = this.data_.keys();
const iter2 = orderedData.keys();
let next1 = iter1.next();
let next2 = iter2.next();
while (!next1.done) {
if (next1.value !== next2.value) {
this.keysAtom_.reportChanged();
break;
}
next1 = iter1.next();
next2 = iter2.next();
}
}
}
// Use correctly ordered map
this.data_ = orderedData;
});
return this;
}
get size() {
this.keysAtom_.reportObserved();
return this.data_.size;
}
toString() {
return "[object ObservableMap]";
}
toJSON() {
return Array.from(this);
}
get [Symbol.toStringTag]() {
return "Map";
}
}
// eslint-disable-next-line
var isObservableMap = /*#__PURE__*/createInstanceofPredicate("ObservableMap", ObservableMap);
function makeIterableForMap(iterator) {
iterator[Symbol.toStringTag] = "MapIterator";
return makeIterable(iterator);
}
function convertToMap(dataStructure) {
if (isES6Map(dataStructure) || isObservableMap(dataStructure)) {
return dataStructure;
} else if (Array.isArray(dataStructure)) {
return new Map(dataStructure);
} else if (isPlainObject(dataStructure)) {
const map = new Map();
for (const key in dataStructure) {
map.set(key, dataStructure[key]);
}
return map;
} else {
return die(21, dataStructure);
}
}
const ObservableSetMarker = {};
class ObservableSet {
constructor(initialData, enhancer = deepEnhancer, name_ = "ObservableSet@" + getNextId() ) {
this.name_ = void 0;
this[$mobx] = ObservableSetMarker;
this.data_ = new Set();
this.atom_ = void 0;
this.changeListeners_ = void 0;
this.interceptors_ = void 0;
this.dehancer = void 0;
this.enhancer_ = void 0;
this.name_ = name_;
this.enhancer_ = (newV, oldV) => enhancer(newV, oldV, name_);
initObservable(() => {
this.atom_ = createAtom(this.name_);
if (initialData) {
this.replace(initialData);
}
});
}
dehanceValue_(value) {
if (this.dehancer !== undefined) {
return this.dehancer(value);
}
return value;
}
clear() {
transaction(() => {
untracked(() => {
for (const value of this.data_.values()) {
this.delete(value);
}
});
});
}
forEach(callbackFn, thisArg) {
for (const value of this) {
callbackFn.call(thisArg, value, value, this);
}
}
get size() {
this.atom_.reportObserved();
return this.data_.size;
}
add(value) {
checkIfStateModificationsAreAllowed(this.atom_);
if (hasInterceptors(this)) {
const change = interceptChange(this, {
type: ADD,
object: this,
newValue: value
});
if (!change) {
return this;
}
// implemented reassignment same as it's done for ObservableMap
value = change.newValue;
}
if (!this.has(value)) {
transaction(() => {
this.data_.add(this.enhancer_(value, undefined));
this.atom_.reportChanged();
});
const notifySpy = isSpyEnabled();
const notify = hasListeners(this);
const change = notify || notifySpy ? {
observableKind: "set",
debugObjectName: this.name_,
type: ADD,
object: this,
newValue: value
} : null;
if (notifySpy && "development" !== "production") {
spyReportStart(change);
}
if (notify) {
notifyListeners(this, change);
}
if (notifySpy && "development" !== "production") {
spyReportEnd();
}
}
return this;
}
delete(value) {
if (hasInterceptors(this)) {
const change = interceptChange(this, {
type: DELETE,
object: this,
oldValue: value
});
if (!change) {
return false;
}
}
if (this.has(value)) {
const notifySpy = isSpyEnabled();
const notify = hasListeners(this);
const change = notify || notifySpy ? {
observableKind: "set",
debugObjectName: this.name_,
type: DELETE,
object: this,
oldValue: value
} : null;
if (notifySpy && "development" !== "production") {
spyReportStart(change);
}
transaction(() => {
this.atom_.reportChanged();
this.data_.delete(value);
});
if (notify) {
notifyListeners(this, change);
}
if (notifySpy && "development" !== "production") {
spyReportEnd();
}
return true;
}
return false;
}
has(value) {
this.atom_.reportObserved();
return this.data_.has(this.dehanceValue_(value));
}
entries() {
const values = this.values();
return makeIterableForSet({
next() {
const {
value,
done
} = values.next();
return !done ? {
value: [value, value],
done
} : {
value: undefined,
done
};
}
});
}
keys() {
return this.values();
}
values() {
this.atom_.reportObserved();
const self = this;
const values = this.data_.values();
return makeIterableForSet({
next() {
const {
value,
done
} = values.next();
return !done ? {
value: self.dehanceValue_(value),
done
} : {
value: undefined,
done
};
}
});
}
intersection(otherSet) {
return new Set(this).intersection(otherSet);
}
union(otherSet) {
return new Set(this).union(otherSet);
}
difference(otherSet) {
return new Set(this).difference(otherSet);
}
symmetricDifference(otherSet) {
return new Set(this).symmetricDifference(otherSet);
}
isSubsetOf(otherSet) {
return new Set(this).isSubsetOf(otherSet);
}
isSupersetOf(otherSet) {
return new Set(this).isSupersetOf(otherSet);
}
isDisjointFrom(otherSet) {
return new Set(this).isDisjointFrom(otherSet);
}
replace(other) {
if (isObservableSet(other)) {
other = new Set(other);
}
if (Array.isArray(other) || isES6Set(other)) {
// Only emit `delete`/`add` events (and `reportChanged`) for values that
// actually change, instead of clearing and re-adding everything. `add` and
// `delete` are already no-ops for values that are respectively already
// present or already absent, so we just need to avoid deleting values that
// are part of the replacement. See #3761.
transaction(() => {
// Collect the desired values for quick lookup. `other` is already a Set
// here when it was passed (or snapshotted from an observable set) as one,
// so reuse it rather than allocating another; arrays are wrapped (which
// also dedupes them).
const replacementValues = isES6Set(other) ? other : new Set(other);
// Short-circuit the trivial cases: an empty replacement is just a clear,
// and replacing into an empty set only needs the adds.
if (replacementValues.size === 0) {
this.clear();
return;
}
if (this.data_.size === 0) {
replacementValues.forEach(value => this.add(value));
return;
}
// Delete values that are not part of the replacement.
for (const value of this.data_.values()) {
if (!replacementValues.has(this.dehanceValue_(value))) {
this.delete(value);
}
}
// Add new values; values that are already present are a no-op.
replacementValues.forEach(value => this.add(value));
});
} else if (other !== null && other !== undefined) {
die(41, other);
}
return this;
}
toJSON() {
return Array.from(this);
}
toString() {
return "[object ObservableSet]";
}
[Symbol.iterator]() {
return this.values();
}
get [Symbol.toStringTag]() {
return "Set";
}
}
// eslint-disable-next-line
var isObservableSet = /*#__PURE__*/createInstanceofPredicate("ObservableSet", ObservableSet);
function makeIterableForSet(iterator) {
iterator[Symbol.toStringTag] = "SetIterator";
return makeIterable(iterator);
}
const descriptorCache = /*#__PURE__*/Object.create(null);
const REMOVE = "remove";
class ObservableObjectAdministration {
constructor(target_, values_ = new Map(), name_,
// Used anytime annotation is not explicitely provided
defaultAnnotation_ = autoAnnotation) {
this.target_ = void 0;
this.values_ = void 0;
this.name_ = void 0;
this.defaultAnnotation_ = void 0;
this.keysAtom_ = void 0;
this.changeListeners_ = void 0;
this.interceptors_ = void 0;
this.proxy_ = void 0;
this.isPlainObject_ = void 0;
this.appliedAnnotations_ = void 0;
this.pendingKeys_ = void 0;
this.lazyComputedKeys_ = void 0;
this.lazyObservableKeys_ = void 0;
this.target_ = target_;
this.values_ = values_;
this.name_ = name_;
this.defaultAnnotation_ = defaultAnnotation_;
this.keysAtom_ = new Atom(`${this.name_}.keys` );
// Optimization: we use this frequently
this.isPlainObject_ = isPlainObject(this.target_);
if (!isAnnotation(this.defaultAnnotation_)) {
die(`defaultAnnotation must be valid annotation`);
}
{
// Prepare structure for tracking which fields were already annotated
this.appliedAnnotations_ = {};
}
}
getObservablePropValue_(key) {
var _ref, _this$values_$get;
// Hot path: single map lookup. Lazy entries (rare) take the materialise branch.
const observable = (_ref = (_this$values_$get = this.values_.get(key)) != null ? _this$values_$get : this.materializeLazyComputed_(key)) != null ? _ref : this.materializeLazyObservable_(key);
return observable.get();
}
materializeLazyComputed_(key) {
var _this$lazyComputedKey;
const factory = (_this$lazyComputedKey = this.lazyComputedKeys_) == null ? void 0 : _this$lazyComputedKey.get(key);
if (!factory) {
return undefined;
}
this.lazyComputedKeys_.delete(key);
if (this.lazyComputedKeys_.size === 0) {
this.lazyComputedKeys_ = undefined;
}
const computed = factory();
this.values_.set(key, computed);
return computed;
}
materializeLazyObservable_(key) {
var _this$lazyObservableK;
const factory = (_this$lazyObservableK = this.lazyObservableKeys_) == null ? void 0 : _this$lazyObservableK.get(key);
if (!factory) {
return undefined;
}
this.lazyObservableKeys_.delete(key);
if (this.lazyObservableKeys_.size === 0) {
this.lazyObservableKeys_ = undefined;
}
const observable = factory();
this.values_.set(key, observable);
return observable;
}
setObservablePropValue_(key, newValue) {
var _ref2, _this$values_$get2;
const observable = (_ref2 = (_this$values_$get2 = this.values_.get(key)) != null ? _this$values_$get2 : this.materializeLazyComputed_(key)) != null ? _ref2 : this.materializeLazyObservable_(key);
if (observable instanceof ComputedValue) {
observable.set(newValue);
return true;
}
// intercept
if (hasInterceptors(this)) {
const change = interceptChange(this, {
type: UPDATE,
object: this.proxy_ || this.target_,
name: key,
newValue
});
if (!change) {
return null;
}
newValue = change.newValue;
}
newValue = observable.prepareNewValue_(newValue);
// notify spy & observers
if (newValue !== globalState.UNCHANGED) {
const notify = hasListeners(this);
const notifySpy = isSpyEnabled();
const change = notify || notifySpy ? {
type: UPDATE,
observableKind: "object",
debugObjectName: this.name_,
object: this.proxy_ || this.target_,
oldValue: observable.value_,
name: key,
newValue
} : null;
if (notifySpy) {
spyReportStart(change);
}
observable.setNewValue_(newValue);
if (notify) {
notifyListeners(this, change);
}
if (notifySpy) {
spyReportEnd();
}
}
return true;
}
get_(key) {
if (globalState.trackingDerivation && !hasProp(this.target_, key)) {
// Key doesn't exist yet, subscribe for it in case it's added later
this.has_(key);
}
return this.target_[key];
}
/**
* @param {PropertyKey} key
* @param {any} value
* @param {Annotation|boolean} annotation true - use default annotation, false - copy as is
* @param {boolean} proxyTrap whether it's called from proxy trap
* @returns {boolean|null} true on success, false on failure (proxyTrap + non-configurable), null when cancelled by interceptor
*/
set_(key, value, proxyTrap = false) {
// Don't use .has(key) - we care about own
if (hasProp(this.target_, key)) {
// Existing prop
if (this.values_.has(key)) {
// Observable (can be intercepted)
return this.setObservablePropValue_(key, value);
} else if (proxyTrap) {
// Non-observable - proxy
return Reflect.set(this.target_, key, value);
} else {
// Non-observable
this.target_[key] = value;
return true;
}
} else {
// New prop
return this.extend_(key, {
value,
enumerable: true,
writable: true,
configurable: true
}, this.defaultAnnotation_, proxyTrap);
}
}
// Trap for "in"
has_(key) {
if (!globalState.trackingDerivation) {
// Skip key subscription outside derivation
return key in this.target_;
}
this.pendingKeys_ || (this.pendingKeys_ = new Map());
let entry = this.pendingKeys_.get(key);
if (!entry) {
entry = new ObservableValue(key in this.target_, referenceEnhancer, `${this.name_}.${stringifyKey(key)}?` , false);
this.pendingKeys_.set(key, entry);
}
return entry.get();
}
/**
* @param {PropertyKey} key
* @param {PropertyDescriptor} descriptor
* @param {Annotation|boolean} annotation true - use default annotation, false - copy as is
* @param {boolean} proxyTrap whether it's called from proxy trap
* @returns {boolean|null} true on success, false on failure (proxyTrap + non-configurable), null when cancelled by interceptor
*/
extend_(key, descriptor, annotation, proxyTrap = false) {
if (annotation === true) {
annotation = this.defaultAnnotation_;
}
if (annotation === false) {
return this.defineProperty_(key, descriptor, proxyTrap);
}
assertAnnotable(this, annotation, key);
const outcome = annotation.extend_(this, key, descriptor, proxyTrap);
if (outcome) {
recordAnnotationApplied(this, annotation, key);
}
return outcome;
}
/**
* @param {PropertyKey} key
* @param {PropertyDescriptor} descriptor
* @param {boolean} proxyTrap whether it's called from proxy trap
* @returns {boolean|null} true on success, false on failure (proxyTrap + non-configurable), null when cancelled by interceptor
*/
defineProperty_(key, descriptor, proxyTrap = false) {
checkIfStateModificationsAreAllowed(this.keysAtom_);
try {
startBatch();
// Delete
const deleteOutcome = this.delete_(key);
if (!deleteOutcome) {
// Failure or intercepted
return deleteOutcome;
}
// ADD interceptor
if (hasInterceptors(this)) {
const change = interceptChange(this, {
object: this.proxy_ || this.target_,
name: key,
type: ADD,
newValue: descriptor.value
});
if (!change) {
return null;
}
const {
newValue
} = change;
if (descriptor.value !== newValue) {
descriptor = assign({}, descriptor, {
value: newValue
});
}
}
// Define
if (proxyTrap) {
if (!Reflect.defineProperty(this.target_, key, descriptor)) {
return false;
}
} else {
defineProperty(this.target_, key, descriptor);
}
// Notify
this.notifyPropertyAddition_(key, descriptor.value);
} finally {
endBatch();
}
return true;
}
// If original descriptor becomes relevant, move this to annotation directly
defineObservableProperty_(key, value, enhancer, proxyTrap = false) {
checkIfStateModificationsAreAllowed(this.keysAtom_);
try {
startBatch();
// Delete
const deleteOutcome = this.delete_(key);
if (!deleteOutcome) {
// Failure or intercepted
return deleteOutcome;
}
// ADD interceptor
if (hasInterceptors(this)) {
const change = interceptChange(this, {
object: this.proxy_ || this.target_,
name: key,
type: ADD,
newValue: value
});
if (!change) {
return null;
}
value = change.newValue;
}
const cachedDescriptor = getCachedObservablePropDescriptor(key);
const descriptor = {
configurable: globalState.safeDescriptors ? this.isPlainObject_ : true,
enumerable: true,
get: cachedDescriptor.get,
set: cachedDescriptor.set
};
// Define
if (proxyTrap) {
if (!Reflect.defineProperty(this.target_, key, descriptor)) {
return false;
}
} else {
defineProperty(this.target_, key, descriptor);
}
const observable = new ObservableValue(value, enhancer, "development" !== "production" ? `${this.name_}.${key.toString()}` : "ObservableObject.key", false);
this.values_.set(key, observable);
// Notify (value possibly changed by ObservableValue)
this.notifyPropertyAddition_(key, observable.value_);
} finally {
endBatch();
}
return true;
}
// If original descriptor becomes relevant, move this to annotation directly
defineComputedProperty_(key, options, proxyTrap = false) {
checkIfStateModificationsAreAllowed(this.keysAtom_);
try {
startBatch();
// Delete
const deleteOutcome = this.delete_(key);
if (!deleteOutcome) {
// Failure or intercepted
return deleteOutcome;
}
// ADD interceptor
if (hasInterceptors(this)) {
const change = interceptChange(this, {
object: this.proxy_ || this.target_,
name: key,
type: ADD,
newValue: undefined
});
if (!change) {
return null;
}
}
options.name || (options.name = "development" !== "production" ? `${this.name_}.${key.toString()}` : "ObservableObject.key");
options.context = this.proxy_ || this.target_;
const cachedDescriptor = getCachedObservablePropDescriptor(key);
const descriptor = {
configurable: globalState.safeDescriptors ? this.isPlainObject_ : true,
enumerable: false,
get: cachedDescriptor.get,
set: cachedDescriptor.set
};
// Define
if (proxyTrap) {
if (!Reflect.defineProperty(this.target_, key, descriptor)) {
return false;
}
} else {
defineProperty(this.target_, key, descriptor);
}
this.values_.set(key, new ComputedValue(options));
// Notify
this.notifyPropertyAddition_(key, undefined);
} finally {
endBatch();
}
return true;
}
/**
* @param {PropertyKey} key
* @param {PropertyDescriptor} descriptor
* @param {boolean} proxyTrap whether it's called from proxy trap
* @returns {boolean|null} true on success, false on failure (proxyTrap + non-configurable), null when cancelled by interceptor
*/
delete_(key, proxyTrap = false) {
checkIfStateModificationsAreAllowed(this.keysAtom_);
// No such prop
if (!hasProp(this.target_, key)) {
return true;
}
// Intercept
if (hasInterceptors(this)) {
const change = interceptChange(this, {
object: this.proxy_ || this.target_,
name: key,
type: REMOVE
});
// Cancelled
if (!change) {
return null;
}
}
// Delete
try {
var _this$pendingKeys_;
startBatch();
const notify = hasListeners(this);
const notifySpy = "development" !== "production" && isSpyEnabled();
const observable = this.values_.get(key);
// Value needed for spies/listeners
let value = undefined;
// Optimization: don't pull the value unless we will need it
if (!observable && (notify || notifySpy)) {
var _getDescriptor;
value = (_getDescriptor = getDescriptor(this.target_, key)) == null ? void 0 : _getDescriptor.value;
}
// delete prop (do first, may fail)
if (proxyTrap) {
if (!Reflect.deleteProperty(this.target_, key)) {
return false;
}
} else {
delete this.target_[key];
}
// Allow re-annotating this field
if ("development" !== "production") {
delete this.appliedAnnotations_[key];
}
// Clear observable
if (observable) {
this.values_.delete(key);
// for computed, value is undefined
if (observable instanceof ObservableValue) {
value = observable.value_;
}
// Notify: autorun(() => obj[key]), see #1796
propagateChanged(observable);
}
// Notify "keys/entries/values" observers
this.keysAtom_.reportChanged();
// Notify "has" observers
// "in" as it may still exist in proto
(_this$pendingKeys_ = this.pendingKeys_) == null || (_this$pendingKeys_ = _this$pendingKeys_.get(key)) == null || _this$pendingKeys_.set(key in this.target_);
// Notify spies/listeners
if (notify || notifySpy) {
const change = {
type: REMOVE,
observableKind: "object",
object: this.proxy_ || this.target_,
debugObjectName: this.name_,
oldValue: value,
name: key
};
if ("development" !== "production" && notifySpy) {
spyReportStart(change);
}
if (notify) {
notifyListeners(this, change);
}
if ("development" !== "production" && notifySpy) {
spyReportEnd();
}
}
} finally {
endBatch();
}
return true;
}
notifyPropertyAddition_(key, value) {
var _this$pendingKeys_2;
const notify = hasListeners(this);
const notifySpy = isSpyEnabled();
if (notify || notifySpy) {
const change = notify || notifySpy ? {
type: ADD,
observableKind: "object",
debugObjectName: this.name_,
object: this.proxy_ || this.target_,
name: key,
newValue: value
} : null;
if (notifySpy) {
spyReportStart(change);
}
if (notify) {
notifyListeners(this, change);
}
if (notifySpy) {
spyReportEnd();
}
}
(_this$pendingKeys_2 = this.pendingKeys_) == null || (_this$pendingKeys_2 = _this$pendingKeys_2.get(key)) == null || _this$pendingKeys_2.set(true);
// Notify "keys/entries/values" observers
this.keysAtom_.reportChanged();
}
ownKeys_() {
this.keysAtom_.reportObserved();
return ownKeys(this.target_);
}
keys_() {
// Returns enumerable && own, but unfortunately keysAtom will report on ANY key change.
// There is no way to distinguish between Object.keys(object) and Reflect.ownKeys(object) - both are handled by ownKeys trap.
// We can either over-report in Object.keys(object) or under-report in Reflect.ownKeys(object)
// We choose to over-report in Object.keys(object), because:
// - typically it's used with simple data objects
// - when symbolic/non-enumerable keys are relevant Reflect.ownKeys works as expected
this.keysAtom_.reportObserved();
return Object.keys(this.target_);
}
}
function asObservableObject(target, options) {
var _options$name;
if (options && isObservableObject(target)) {
die(`Options can't be provided for already observable objects.`);
}
if (hasProp(target, $mobx)) {
if (!(getAdministration(target) instanceof ObservableObjectAdministration)) {
die(`Cannot convert '${getDebugName(target)}' into observable object:` + `\nThe target is already observable of different type.` + `\nExtending builtins is not supported.`);
}
return target;
}
if (!Object.isExtensible(target)) {
die("Cannot make the designated object observable; it is not extensible");
}
const name = (_options$name = options == null ? void 0 : options.name) != null ? _options$name : `${isPlainObject(target) ? "ObservableObject" : target.constructor.name}@${getNextId()}` ;
const adm = new ObservableObjectAdministration(target, new Map(), String(name), getAnnotationFromOptions(options));
addHiddenProp(target, $mobx, adm);
return target;
}
const isObservableObjectAdministration = /*#__PURE__*/createInstanceofPredicate("ObservableObjectAdministration", ObservableObjectAdministration);
function getCachedObservablePropDescriptor(key) {
return descriptorCache[key] || (descriptorCache[key] = {
get() {
return this[$mobx].getObservablePropValue_(key);
},
set(value) {
return this[$mobx].setObservablePropValue_(key, value);
}
});
}
function isObservableObject(thing) {
if (isObject(thing)) {
return isObservableObjectAdministration(thing[$mobx]);
}
return false;
}
function recordAnnotationApplied(adm, annotation, key) {
{
adm.appliedAnnotations_[key] = annotation;
}
}
function assertAnnotable(adm, annotation, key) {
// Valid annotation
if (!isAnnotation(annotation)) {
die(`Cannot annotate '${adm.name_}.${key.toString()}': Invalid annotation.`);
}
/*
// Configurable, not sealed, not frozen
// Possibly not needed, just a little better error then the one thrown by engine.
// Cases where this would be useful the most (subclass field initializer) are not interceptable by this.
if (__DEV__) {
const configurable = getDescriptor(adm.target_, key)?.configurable
const frozen = Object.isFrozen(adm.target_)
const sealed = Object.isSealed(adm.target_)
if (!configurable || frozen || sealed) {
const fieldName = `${adm.name_}.${key.toString()}`
const requestedAnnotationType = annotation.annotationType_
let error = `Cannot apply '${requestedAnnotationType}' to '${fieldName}':`
if (frozen) {
error += `\nObject is frozen.`
}
if (sealed) {
error += `\nObject is sealed.`
}
if (!configurable) {
error += `\nproperty is not configurable.`
// Mention only if caused by us to avoid confusion
if (hasProp(adm.appliedAnnotations!, key)) {
error += `\nTo prevent accidental re-definition of a field by a subclass, `
error += `all annotated fields of non-plain objects (classes) are not configurable.`
}
}
die(error)
}
}
*/
// Not annotated
if (!isOverride(annotation) && hasProp(adm.appliedAnnotations_, key)) {
const fieldName = `${adm.name_}.${key.toString()}`;
const currentAnnotationType = adm.appliedAnnotations_[key].annotationType_;
const requestedAnnotationType = annotation.annotationType_;
die(`Cannot apply '${requestedAnnotationType}' to '${fieldName}':` + `\nThe field is already annotated with '${currentAnnotationType}'.` + `\nRe-annotating fields is not allowed.` + `\nUse 'override' annotation for methods overridden by subclass.`);
}
}
function getAtom(thing, property) {
if (typeof thing === "object" && thing !== null) {
if (isObservableArray(thing)) {
if (property !== undefined) {
die(23);
}
return thing[$mobx].atom_;
}
if (isObservableSet(thing)) {
return thing.atom_;
}
if (isObservableMap(thing)) {
if (property === undefined) {
return thing.keysAtom_;
}
const observable = thing.data_.get(property) || thing.hasMap_.get(property);
if (!observable) {
die(25, property, getDebugName(thing));
}
return observable;
}
if (isObservableObject(thing)) {
var _ref, _adm$values_$get;
if (!property) {
return die(26);
}
const adm = thing[$mobx];
const observable = (_ref = (_adm$values_$get = adm.values_.get(property)) != null ? _adm$values_$get : adm.materializeLazyComputed_(property)) != null ? _ref : adm.materializeLazyObservable_(property);
if (!observable) {
die(27, property, getDebugName(thing));
}
return observable;
}
if (isAtom(thing) || isComputedValue(thing) || isReaction(thing)) {
return thing;
}
} else if (isFunction(thing)) {
if (isReaction(thing[$mobx])) {
// disposer function
return thing[$mobx];
}
}
die(28);
}
function getAdministration(thing, property) {
if (!thing) {
die(29);
}
if (property !== undefined) {
return getAdministration(getAtom(thing, property));
}
if (isAtom(thing) || isComputedValue(thing) || isReaction(thing)) {
return thing;
}
if (isObservableMap(thing) || isObservableSet(thing)) {
return thing;
}
if (thing[$mobx]) {
return thing[$mobx];
}
die(24, thing);
}
function getDebugName(thing, property) {
let named;
if (property !== undefined) {
named = getAtom(thing, property);
} else if (isAction(thing)) {
return thing.name;
} else if (isObservableObject(thing) || isObservableMap(thing) || isObservableSet(thing)) {
named = getAdministration(thing);
} else {
// valid for arrays as well
named = getAtom(thing);
}
return named.name_;
}
/**
* Helper function for initializing observable structures, it applies:
* 1. allowStateChanges so we don't violate enforceActions.
* 2. untracked so we don't accidentaly subscribe to anything observable accessed during init in case the observable is created inside derivation.
* 3. batch to avoid state version updates
*/
function initObservable(cb) {
const derivation = untrackedStart();
const allowStateChanges = allowStateChangesStart(true) ;
startBatch();
try {
return cb();
} finally {
endBatch();
{
allowStateChangesEnd(allowStateChanges);
}
untrackedEnd(derivation);
}
}
const toString = objectPrototype.toString;
function deepEqual(a, b, depth = -1) {
return eq(a, b, depth);
}
// Copied from https://github.com/jashkenas/underscore/blob/5c237a7c682fb68fd5378203f0bf22dce1624854/underscore.js#L1186-L1289
// Modified: "Deep compare objects" part to iterate over keys in forward order instead of reverse order.
//
// Internal recursive comparison function for `isEqual`.
function eq(a, b, depth, aStack, bStack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
if (a === b) {
return a !== 0 || 1 / a === 1 / b;
}
// `null` or `undefined` only equal to itself (strict comparison).
if (a == null || b == null) {
return false;
}
// `NaN`s are equivalent, but non-reflexive.
if (a !== a) {
return b !== b;
}
// Exhaust primitive checks
const type = typeof a;
if (type !== "function" && type !== "object" && typeof b != "object") {
return false;
}
// Compare `[[Class]]` names.
const className = toString.call(a);
if (className !== toString.call(b)) {
return false;
}
switch (className) {
// Strings, numbers, regular expressions, dates, and booleans are compared by value.
case "[object RegExp]":
// RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
case "[object String]":
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
return "" + a === "" + b;
case "[object Number]":
// `NaN`s are equivalent, but non-reflexive.
// Object(NaN) is equivalent to NaN.
if (+a !== +a) {
return +b !== +b;
}
// An `egal` comparison is performed for other numeric values.
return +a === 0 ? 1 / +a === 1 / b : +a === +b;
case "[object Date]":
case "[object Boolean]":
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
return +a === +b;
case "[object Symbol]":
return typeof Symbol !== "undefined" && Symbol.valueOf.call(a) === Symbol.valueOf.call(b);
case "[object Map]":
case "[object Set]":
// Maps and Sets are unwrapped to arrays of entry-pairs, adding an incidental level.
// Hide this extra level by increasing the depth.
if (depth >= 0) {
depth++;
}
break;
}
// Unwrap any wrapped objects.
a = unwrap(a);
b = unwrap(b);
const areArrays = className === "[object Array]";
if (!areArrays) {
if (typeof a != "object" || typeof b != "object") {
return false;
}
// Objects with different constructors are not equivalent, but `Object`s or `Array`s
// from different frames are.
const aCtor = a.constructor,
bCtor = b.constructor;
if (aCtor !== bCtor && !(isFunction(aCtor) && aCtor instanceof aCtor && isFunction(bCtor) && bCtor instanceof bCtor) && "constructor" in a && "constructor" in b) {
return false;
}
}
if (depth === 0) {
return false;
} else if (depth < 0) {
depth = -1;
}
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
// Initializing stack of traversed objects.
// It's done here since we only need them for objects and arrays comparison.
aStack = aStack || [];
bStack = bStack || [];
let length = aStack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (aStack[length] === a) {
return bStack[length] === b;
}
}
// Add the first object to the stack of traversed objects.
aStack.push(a);
bStack.push(b);
// Recursively compare objects and arrays.
if (areArrays) {
// Compare array lengths to determine if a deep comparison is necessary.
length = a.length;
if (length !== b.length) {
return false;
}
// Deep compare the contents, ignoring non-numeric properties.
while (length--) {
if (!eq(a[length], b[length], depth - 1, aStack, bStack)) {
return false;
}
}
} else {
// Deep compare objects.
const keys = Object.keys(a);
const _length = keys.length;
// Ensure that both objects contain the same number of properties before comparing deep equality.
if (Object.keys(b).length !== _length) {
return false;
}
for (let i = 0; i < _length; i++) {
// Deep compare each member
const key = keys[i];
if (!(hasProp(b, key) && eq(a[key], b[key], depth - 1, aStack, bStack))) {
return false;
}
}
}
// Remove the first object from the stack of traversed objects.
aStack.pop();
bStack.pop();
return true;
}
function unwrap(a) {
if (isObservableArray(a)) {
return a.slice();
}
if (isES6Map(a) || isObservableMap(a)) {
return Array.from(a.entries());
}
if (isES6Set(a) || isObservableSet(a)) {
return Array.from(a.entries());
}
return a;
}
var _globalThis$Iterator;
// safely get iterator prototype if available
const maybeIteratorPrototype = ((_globalThis$Iterator = globalThis.Iterator) == null ? void 0 : _globalThis$Iterator.prototype) || {};
function makeIterable(iterator) {
iterator[Symbol.iterator] = getSelf;
return assign(Object.create(maybeIteratorPrototype), iterator);
}
function getSelf() {
return this;
}
function isAnnotation(thing) {
return (
// Can be function
thing instanceof Object && typeof thing.annotationType_ === "string" && isFunction(thing.make_) && isFunction(thing.extend_)
);
}
/**
* (c) Michel Weststrate 2015 - 2020
* MIT Licensed
*
* Welcome to the mobx sources! To get a global overview of how MobX internally works,
* this is a good place to start:
* https://medium.com/@mweststrate/becoming-fully-reactive-an-in-depth-explanation-of-mobservable-55995262a254#.xvbh6qd74
*
* Source folders:
* ===============
*
* - api/ Most of the public static methods exposed by the module can be found here.
* - core/ Implementation of the MobX algorithm; atoms, derivations, reactions, dependency trees, optimizations. Cool stuff can be found here.
* - types/ All the magic that is need to have observable objects, arrays and values is in this folder. Including the modifiers like `asFlat`.
* - utils/ Utility stuff.
*
*/
{
const g = globalThis;
["Symbol", "Map", "Set", "Proxy"].forEach(m => {
if (typeof g[m] === "undefined") {
die(`MobX requires global '${m}' to be available or polyfilled`);
}
});
}
if (typeof __MOBX_DEVTOOLS_GLOBAL_HOOK__ === "object") {
// See: https://github.com/andykog/mobx-devtools/
__MOBX_DEVTOOLS_GLOBAL_HOOK__.injectMobx({
spy,
extras: {
getDebugName
},
$mobx
});
}
exports.$mobx = $mobx;
exports.FlowCancellationError = FlowCancellationError;
exports.ObservableMap = ObservableMap;
exports.ObservableSet = ObservableSet;
exports.Reaction = Reaction;
exports._allowStateChanges = allowStateChanges;
exports._allowStateChangesInsideComputed = runInAction;
exports._allowStateReadsEnd = allowStateReadsEnd;
exports._allowStateReadsStart = allowStateReadsStart;
exports._autoAction = autoAction;
exports._autoActionBound = autoActionBound;
exports._endAction = _endAction;
exports._getAdministration = getAdministration;
exports._getGlobalState = getGlobalState;
exports._interceptReads = interceptReads;
exports._isComputingDerivation = isComputingDerivation;
exports._resetGlobalState = resetGlobalState;
exports._startAction = _startAction;
exports.action = action;
exports.actionBound = actionBound;
exports.autorun = autorun;
exports.compareDefault = compareDefault;
exports.compareIdentity = compareIdentity;
exports.compareShallow = compareShallow;
exports.compareStructural = compareStructural;
exports.computed = computed;
exports.computedStruct = computedStruct;
exports.configure = configure;
exports.createAtom = createAtom;
exports.defineProperty = apiDefineProperty;
exports.entries = entries;
exports.extendObservable = extendObservable;
exports.flow = flow;
exports.flowBound = flowBound;
exports.flowResult = flowResult;
exports.get = get;
exports.getAtom = getAtom;
exports.getDebugName = getDebugName;
exports.getDependencyTree = getDependencyTree;
exports.getObserverTree = getObserverTree;
exports.has = has;
exports.intercept = intercept;
exports.isAction = isAction;
exports.isBoxedObservable = isObservableValue;
exports.isComputed = isComputed;
exports.isComputedProp = isComputedProp;
exports.isFlow = isFlow;
exports.isFlowCancellationError = isFlowCancellationError;
exports.isObservable = isObservable;
exports.isObservableArray = isObservableArray;
exports.isObservableMap = isObservableMap;
exports.isObservableObject = isObservableObject;
exports.isObservableProp = isObservableProp;
exports.isObservableSet = isObservableSet;
exports.keys = keys;
exports.makeAutoObservable = makeAutoObservable;
exports.makeObservable = makeObservable;
exports.observable = observable;
exports.observableDeep = observableDeep;
exports.observableRef = observableRef;
exports.observableShallow = observableShallow;
exports.observableStruct = observableStruct;
exports.observe = observe;
exports.onBecomeObserved = onBecomeObserved;
exports.onBecomeUnobserved = onBecomeUnobserved;
exports.onReactionError = onReactionError;
exports.override = override;
exports.ownKeys = apiOwnKeys;
exports.reaction = reaction;
exports.remove = remove;
exports.runInAction = runInAction;
exports.set = set;
exports.spy = spy;
exports.toJS = toJS;
exports.transaction = transaction;
exports.untracked = untracked;
exports.values = values;
exports.when = when;
Object.defineProperty(exports, '__esModule', { value: true });
}));
//# sourceMappingURL=mobx.umd.development.js.map