mobx
Version:
Simple, scalable state management.
5,334 lines • 181 kB
JavaScript
const __MOBX_DEV__ = process.env.NODE_ENV !== "production";
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 = __MOBX_DEV__ ? niceErrors : {};
function die(error, ...args) {
if (__MOBX_DEV__) {
let e = typeof error === "string" ? error : errors[error];
if (typeof e === "function") e = e.apply(null, args);
throw new Error(`[MobX] ${e}`);
}
throw new Error(`[MobX] minified error nr: ${error}${args.length ? " " + args.map(String).join(",") : ""}. See mobx.js.org/errors`);
}
// 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 (__MOBX_DEV__ && !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_ = __MOBX_DEV__ ? "Atom@" + getNextId() : "Atom") {
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
});
}
if (__MOBX_DEV__) {
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 (__MOBX_DEV__ && 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 (__MOBX_DEV__ && 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 (__MOBX_DEV__ && !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) {
if (__MOBX_DEV__) {
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 (__MOBX_DEV__ && !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_;
if (__MOBX_DEV__) {
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 (__MOBX_DEV__ && !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) {
if (__MOBX_DEV__) {
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 = __MOBX_DEV__ ? `${adm.name_}.${key.toString()}` : `ObservableObject.${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 (__MOBX_DEV__ && !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 (__MOBX_DEV__) {
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, __MOBX_DEV__ ? `${adm.name_}.${name.toString()}` : `ObservableObject.${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 (__MOBX_DEV__ && !("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);
}
if (__MOBX_DEV__) {
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 (__MOBX_DEV__) {
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 (__MOBX_DEV__) {
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_ = __MOBX_DEV__ && 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();
if (__MOBX_DEV__) {
prevAllowStateChanges_ = allowStateChangesStart(true);
}
}
const prevAllowStateReads_ = globalState.allowStateReads;
if (__MOBX_DEV__) {
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;
}
if (__MOBX_DEV__) {
allowStateChangesEnd(runInfo.prevAllowStateChanges_);
allowStateReadsEnd(runInfo.prevAllowStateReads_);
}
endBatch();
if (runInfo.runAsAction_) {
untrackedEnd(runInfo.prevDerivation_);
}
if (__MOBX_DEV__ && 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_ = __MOBX_DEV__ ? "ObservableValue@" + getNextId() : "ObservableValue", 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 (__MOBX_DEV__ && 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 = __MOBX_DEV__ && isSpyEnabled();
if (__MOBX_DEV__ && notifySpy) {
spyReportStart({
type: UPDATE,
object: this,
observableKind: "value",
debugObjectName: this.name_,
newValue,
oldValue
});
}
this.setNewValue_(newValue);
if (__MOBX_DEV__ && 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 || (__MOBX_DEV__ ? "ComputedValue@" + getNextId() : "ComputedValue");
if (options.set) {
this.setter_ = createAction(__MOBX_DEV__ ? this.name_ + "-setter" : "ComputedValue-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 (__MOBX_DEV__ && 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 = __MOBX_DEV__ ? allowStateChangesStart(false) : 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);
}
}
}
if (__MOBX_DEV__) {
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 (!__MOBX_DEV__) {
return;
}
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 = __MOBX_DEV__ ? allowStateReadsStart(true) : 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);
if (__MOBX_DEV__) {
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);
if (__MOBX_DEV__) {
allowStateReadsEnd(prevAllowStateReads);
}
return true;
}
}
}
changeDependenciesStateTo0(derivation);
untrackedEnd(prevUntracked);
if (__MOBX_DEV__) {
allowStateReadsEnd(prevAllowStateReads);
}
return false;
}
}
}
function isComputingDerivation() {
return globalState.trackingDerivation !== null; // filter out actions inside computations
}
function checkIfStateModificationsAreAllowed(atom) {
if (!__MOBX_DEV__) {
return;
}
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 (__MOBX_DEV__ && !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 = __MOBX_DEV__ ? allowStateReadsStart(true) : 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);
if (__MOBX_DEV__) {
allowStateReadsEnd(prevAllowStateReads);
}
return result;
}
function warnAboutDerivationWithoutDependencies(derivation) {
if (!__MOBX_DEV__) {
return;
}
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_ = __MOBX_DEV__ ? "Reaction@" + getNextId() : "Reaction", 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 (__MOBX_DEV__ && 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 = __MOBX_DEV__ && isSpyEnabled();
let startTime;
if (__MOBX_DEV__ && 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 (__MOBX_DEV__ && 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_DEV__ ? `[mobx] Encountered an uncaught exception that was thrown by a reaction or observer component, in: '${this}'` : `[mobx] uncaught error in '${this}'`;
if (!globalState.suppressReactionErrors) {
console.error(message, error);
/** If debugging brought you here, please, read the above message :-). Tnx! */
} else if (__MOBX_DEV__) {
console.warn(`[mobx] (error in reaction '${this.name_}' suppressed, fix error of causing action below)`);
} // prettier-ignore
if (__MOBX_DEV__ && 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(__MOBX_DEV__ ? `Reaction doesn't converge to a stable state after ${MAX_REACTION_ITERATIONS} iterations.` + ` Probably there is a cycle in the reactive function: ${allReactions[0]}` : `[mobx] cycle in reaction: ${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 __MOBX_DEV__ && !!globalState.spyListeners.length;
}
function spyReport(event) {
if (!__MOBX_DEV__) {
return;
} // dead code elimination can do the rest
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) {
if (!__MOBX_DEV__) {
return;
}
const change = assign({}, event, {
spyReportStart: true
});
spyReport(change);
}
const END_EVENT = {
type: "report-end",
spyReportEnd: true
};
function spyReportEnd(change) {
if (!__MOBX_DEV__) {
return;
}
if (change) {
spyReport(assign({}, change, {
type: "report-end",
spyReportEnd: true
}));
} else {
spyReport(END_EVENT);
}
}
function spy(listener) {
if (!__MOBX_DEV__) {
console.warn(`[mobx.spy] Is a no-op in production builds`);
return function () {};
} else {
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
}));
}
if (__MOBX_DEV__) {
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 (__MOBX_DEV__) {
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 : __MOBX_DEV__ ? view.name || "Autorun@" + getNextId() : "Autorun";
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 (__MOBX_DEV__) {
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 : __MOBX_DEV__ ? "Reaction@" + getNextId() : "Reaction";
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 (__MOBX_DEV__ && 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 (__MOBX_DEV__) {
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 (__MOBX_DEV__ && arguments.length !== 1) {
die(`Flow expects single argument with generator function`);
}
const generator = arg1;
const name = generator.name || (__MOBX_DEV__ ? "<unnamed flow>" : "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 = __MOBX_DEV__ ? ++generatorId : 0;
const gen = action(__MOBX_DEV__ ? `${name} - runid: ${runId} - init` : name, 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(__MOBX_DEV__ ? `${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(__MOBX_DEV__ ? `${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 = __MOBX_DEV__ ? `${name} - runid: ${runId} - cancel` : name;
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 (__MOBX_DEV__ && !isStringish(propOrHandler)) {
return die(`InterceptReads can only be used with a specific property, not with an object in general`);
}
target = getAdministration(thing, propOrHandler);
} else if (__MOBX_DEV__) {
return die(`Expected observable map, object or array as first array`);
}
if (__MOBX_DEV__ && 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 (__MOBX_DEV__ && 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 (__MOBX_DEV__ && !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 (__MOBX_DEV__ && (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 (__MOBX_DEV__ && 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 (__MOBX_DEV__ && !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 (__MOBX_DEV__ && fireImmediately === true) {
die("`observe` doesn't support fireImmediately=true in combination with maps.");
}
} else if (isObservableSet(thing)) {
if (__MOBX_DEV__ && fireImmediately === true) {
die("`observe` doesn't support fireImmediately=true in combination with sets.");
}
} else if (isObservableObject(thing)) {
if (__MOBX_DEV__ && 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 (__MOBX_DEV__ && 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 = __MOBX_DEV__ ? opts.name || "When@" + getNextId() : "When";
const effectAction = createAction(__MOBX_DEV__ ? opts.name + "-effect" : "When-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 (__MOBX_DEV__ && 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 (__MOBX_DEV__) {
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 = __MOBX_DEV__ ? "ObservableArray@" + getNextId() : "ObservableArray", 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, __MOBX_DEV__ ? name + "[..]" : "ObservableArray[..]");
}
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));
if (__MOBX_DEV__) {
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 = __MOBX_DEV__ && !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 (__MOBX_DEV__ && notifySpy) {
spyReportStart(change);
}
this.atom_.reportChanged();
if (notify) {
notifyListeners(this, change);
}
if (__MOBX_DEV__ && notifySpy) {
spyReportEnd();
}
}
notifyArraySplice_(index, added, removed) {
const notifySpy = __MOBX_DEV__ && !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 (__MOBX_DEV__ && 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 (__MOBX_DEV__ && 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 = __MOBX_DEV__ ? "ObservableArray@" + getNextId() : "ObservableArray", 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_ = __MOBX_DEV__ ? "ObservableMap@" + getNextId() : "ObservableMap") {
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(__MOBX_DEV__ ? `${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, __MOBX_DEV__ ? `${this.name_}.${stringifyKey(key)}?` : "ObservableMap.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 = __MOBX_DEV__ && 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 (__MOBX_DEV__ && 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 (__MOBX_DEV__ && 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 = __MOBX_DEV__ && 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 (__MOBX_DEV__ && notifySpy) {
spyReportStart(change);
} // TODO fix type
observable.setNewValue_(newValue);
if (notify) {
notifyListeners(this, change);
}
if (__MOBX_DEV__ && notifySpy) {
spyReportEnd();
}
}
}
addValue_(key, newValue) {
checkIfStateModificationsAreAllowed(this.keysAtom_);
transaction(() => {
var _this$hasMap_$get2;
const observable = new ObservableValue(newValue, this.enhancer_, __MOBX_DEV__ ? `${this.name_}.${stringifyKey(key)}` : "ObservableMap.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 = __MOBX_DEV__ && isSpyEnabled();
const notify = hasListeners(this);
const change = notify || notifySpy ? {
observableKind: "map",
debugObjectName: this.name_,
type: ADD,
object: this,
name: key,
newValue
} : null;
if (__MOBX_DEV__ && notifySpy) {
spyReportStart(change);
} // TODO fix type
if (notify) {
notifyListeners(this, change);
}
if (__MOBX_DEV__ && 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_ = __MOBX_DEV__ ? "ObservableSet@" + getNextId() : "ObservableSet") {
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 = __MOBX_DEV__ && isSpyEnabled();
const notify = hasListeners(this);
const change = notify || notifySpy ? {
observableKind: "set",
debugObjectName: this.name_,
type: ADD,
object: this,
newValue: value
} : null;
if (notifySpy && __MOBX_DEV__) {
spyReportStart(change);
}
if (notify) {
notifyListeners(this, change);
}
if (notifySpy && __MOBX_DEV__) {
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 = __MOBX_DEV__ && isSpyEnabled();
const notify = hasListeners(this);
const change = notify || notifySpy ? {
observableKind: "set",
debugObjectName: this.name_,
type: DELETE,
object: this,
oldValue: value
} : null;
if (notifySpy && __MOBX_DEV__) {
spyReportStart(change);
}
transaction(() => {
this.atom_.reportChanged();
this.data_.delete(value);
});
if (notify) {
notifyListeners(this, change);
}
if (notifySpy && __MOBX_DEV__) {
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(__MOBX_DEV__ ? `${this.name_}.keys` : "ObservableObject.keys");
// Optimization: we use this frequently
this.isPlainObject_ = isPlainObject(this.target_);
if (__MOBX_DEV__ && !isAnnotation(this.defaultAnnotation_)) {
die(`defaultAnnotation must be valid annotation`);
}
if (__MOBX_DEV__) {
// 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 = __MOBX_DEV__ && 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 (__MOBX_DEV__ && notifySpy) {
spyReportStart(change);
}
observable.setNewValue_(newValue);
if (notify) {
notifyListeners(this, change);
}
if (__MOBX_DEV__ && 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, __MOBX_DEV__ ? `${this.name_}.${stringifyKey(key)}?` : "ObservableObject.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, __MOBX_DEV__ ? `${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 = __MOBX_DEV__ ? `${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 = __MOBX_DEV__ && 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 (__MOBX_DEV__) {
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 (__MOBX_DEV__ && notifySpy) {
spyReportStart(change);
}
if (notify) {
notifyListeners(this, change);
}
if (__MOBX_DEV__ && notifySpy) {
spyReportEnd();
}
}
} finally {
endBatch();
}
return true;
}
notifyPropertyAddition_(key, value) {
var _this$pendingKeys_2;
const notify = hasListeners(this);
const notifySpy = __MOBX_DEV__ && 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 (__MOBX_DEV__ && notifySpy) {
spyReportStart(change);
}
if (notify) {
notifyListeners(this, change);
}
if (__MOBX_DEV__ && 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 (__MOBX_DEV__ && options && isObservableObject(target)) {
die(`Options can't be provided for already observable objects.`);
}
if (hasProp(target, $mobx)) {
if (__MOBX_DEV__ && !(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 (__MOBX_DEV__ && !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 : __MOBX_DEV__ ? `${isPlainObject(target) ? "ObservableObject" : target.constructor.name}@${getNextId()}` : "ObservableObject";
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) {
if (__MOBX_DEV__) {
adm.appliedAnnotations_[key] = annotation;
}
}
function assertAnnotable(adm, annotation, key) {
// Valid annotation
if (__MOBX_DEV__ && !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 (__MOBX_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 (__MOBX_DEV__ && !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 = __MOBX_DEV__ ? allowStateChangesStart(true) : true;
startBatch();
try {
return cb();
} finally {
endBatch();
if (__MOBX_DEV__) {
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.
*
*/
if (__MOBX_DEV__) {
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 (__MOBX_DEV__ && typeof __MOBX_DEVTOOLS_GLOBAL_HOOK__ === "object") {
// See: https://github.com/andykog/mobx-devtools/
__MOBX_DEVTOOLS_GLOBAL_HOOK__.injectMobx({
spy,
extras: {
getDebugName
},
$mobx
});
}
export { $mobx, FlowCancellationError, ObservableMap, ObservableSet, Reaction, allowStateChanges as _allowStateChanges, runInAction as _allowStateChangesInsideComputed, allowStateReadsEnd as _allowStateReadsEnd, allowStateReadsStart as _allowStateReadsStart, autoAction as _autoAction, autoActionBound as _autoActionBound, _endAction, getAdministration as _getAdministration, getGlobalState as _getGlobalState, interceptReads as _interceptReads, isComputingDerivation as _isComputingDerivation, resetGlobalState as _resetGlobalState, _startAction, action, actionBound, autorun, compareDefault, compareIdentity, compareShallow, compareStructural, computed, computedStruct, configure, createAtom, apiDefineProperty as defineProperty, entries, extendObservable, flow, flowBound, flowResult, get, getAtom, getDebugName, getDependencyTree, getObserverTree, has, intercept, isAction, isObservableValue as isBoxedObservable, isComputed, isComputedProp, isFlow, isFlowCancellationError, isObservable, isObservableArray, isObservableMap, isObservableObject, isObservableProp, isObservableSet, keys, makeAutoObservable, makeObservable, observable, observableDeep, observableRef, observableShallow, observableStruct, observe, onBecomeObserved, onBecomeUnobserved, onReactionError, override, apiOwnKeys as ownKeys, reaction, remove, runInAction, set, spy, toJS, transaction, untracked, values, when };
//# sourceMappingURL=mobx.esm.js.map