import { isComputedProp, isObservableProp, createAtom, action, _allowStateChangesInsideComputed, reaction, computed, runInAction, observable, values, entries, isObservableArray, getAtom, $mobx, ObservableMap, _interceptReads, intercept, observe, _getAdministration, set } from 'mobx'; var livelinessChecking = "warn"; /** * Defines what MST should do when running into reads / writes to objects that have died. * By default it will print a warning. * Use the `"error"` option to easy debugging to see where the error was thrown and when the offending read / write took place * * @param mode `"warn"`, `"error"` or `"ignore"` */ function setLivelinessChecking(mode) { livelinessChecking = mode; } /** * Returns the current liveliness checking mode. * * @returns `"warn"`, `"error"` or `"ignore"` */ function getLivelinessChecking() { return livelinessChecking; } /** * @deprecated use setLivelinessChecking instead * @hidden * * Defines what MST should do when running into reads / writes to objects that have died. * By default it will print a warning. * Use the `"error"` option to easy debugging to see where the error was thrown and when the offending read / write took place * * @param mode `"warn"`, `"error"` or `"ignore"` */ function setLivelynessChecking(mode) { setLivelinessChecking(mode); } /** * @hidden */ var Hook; (function (Hook) { Hook["afterCreate"] = "afterCreate"; Hook["afterAttach"] = "afterAttach"; Hook["afterCreationFinalization"] = "afterCreationFinalization"; Hook["beforeDetach"] = "beforeDetach"; Hook["beforeDestroy"] = "beforeDestroy"; })(Hook || (Hook = {})); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } function __decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } function __values(o) { var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; if (m) return m.call(o); return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; } function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } function __spread() { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; } /** * Returns the _actual_ type of the given tree node. (Or throws) * * @param object * @returns */ function getType(object) { assertIsStateTreeNode(object, 1); return getStateTreeNode(object).type; } /** * Returns the _declared_ type of the given sub property of an object, array or map. * In the case of arrays and maps the property name is optional and will be ignored. * * Example: * ```ts * const Box = types.model({ x: 0, y: 0 }) * const box = Box.create() * * console.log(getChildType(box, "x").name) // 'number' * ``` * * @param object * @param propertyName * @returns */ function getChildType(object, propertyName) { assertIsStateTreeNode(object, 1); return getStateTreeNode(object).getChildType(propertyName); } /** * Registers a function that will be invoked for each mutation that is applied to the provided model instance, or to any of its children. * See [patches](https://github.com/mobxjs/mobx-state-tree#patches) for more details. onPatch events are emitted immediately and will not await the end of a transaction. * Patches can be used to deep observe a model tree. * * @param target the model instance from which to receive patches * @param callback the callback that is invoked for each patch. The reversePatch is a patch that would actually undo the emitted patch * @returns function to remove the listener */ function onPatch(target, callback) { // check all arguments assertIsStateTreeNode(target, 1); assertIsFunction(callback, 2); return getStateTreeNode(target).onPatch(callback); } /** * Registers a function that is invoked whenever a new snapshot for the given model instance is available. * The listener will only be fire at the end of the current MobX (trans)action. * See [snapshots](https://github.com/mobxjs/mobx-state-tree#snapshots) for more details. * * @param target * @param callback * @returns */ function onSnapshot(target, callback) { // check all arguments assertIsStateTreeNode(target, 1); assertIsFunction(callback, 2); return getStateTreeNode(target).onSnapshot(callback); } /** * Applies a JSON-patch to the given model instance or bails out if the patch couldn't be applied * See [patches](https://github.com/mobxjs/mobx-state-tree#patches) for more details. * * Can apply a single past, or an array of patches. * * @param target * @param patch * @returns */ function applyPatch(target, patch) { // check all arguments assertIsStateTreeNode(target, 1); assertArg(patch, function (p) { return typeof p === "object"; }, "object or array", 2); getStateTreeNode(target).applyPatches(asArray(patch)); } /** * Small abstraction around `onPatch` and `applyPatch`, attaches a patch listener to a tree and records all the patches. * Returns an recorder object with the following signature: * * Example: * ```ts * export interface IPatchRecorder { * // the recorded patches * patches: IJsonPatch[] * // the inverse of the recorded patches * inversePatches: IJsonPatch[] * // true if currently recording * recording: boolean * // stop recording patches * stop(): void * // resume recording patches * resume(): void * // apply all the recorded patches on the given target (the original subject if omitted) * replay(target?: IAnyStateTreeNode): void * // reverse apply the recorded patches on the given target (the original subject if omitted) * // stops the recorder if not already stopped * undo(): void * } * ``` * * The optional filter function allows to skip recording certain patches. * * @param subject * @param filter * @returns */ function recordPatches(subject, filter) { // check all arguments assertIsStateTreeNode(subject, 1); var data = { patches: [], reversedInversePatches: [] }; // we will generate the immutable copy of patches on demand for public consumption var publicData = {}; var disposer; var recorder = { get recording() { return !!disposer; }, get patches() { if (!publicData.patches) { publicData.patches = data.patches.slice(); } return publicData.patches; }, get reversedInversePatches() { if (!publicData.reversedInversePatches) { publicData.reversedInversePatches = data.reversedInversePatches.slice(); } return publicData.reversedInversePatches; }, get inversePatches() { if (!publicData.inversePatches) { publicData.inversePatches = data.reversedInversePatches.slice().reverse(); } return publicData.inversePatches; }, stop: function () { if (disposer) { disposer(); disposer = undefined; } }, resume: function () { if (disposer) return; disposer = onPatch(subject, function (patch, inversePatch) { // skip patches that are asked to be filtered if there's a filter in place if (filter && !filter(patch, inversePatch, getRunningActionContext())) { return; } data.patches.push(patch); data.reversedInversePatches.unshift(inversePatch); // mark immutable public patches as dirty publicData.patches = undefined; publicData.inversePatches = undefined; publicData.reversedInversePatches = undefined; }); }, replay: function (target) { applyPatch(target || subject, data.patches); }, undo: function (target) { applyPatch(target || subject, data.reversedInversePatches); } }; recorder.resume(); return recorder; } /** * The inverse of `unprotect`. * * @param target */ function protect(target) { // check all arguments assertIsStateTreeNode(target, 1); var node = getStateTreeNode(target); if (!node.isRoot) throw fail$1("`protect` can only be invoked on root nodes"); node.isProtectionEnabled = true; } /** * By default it is not allowed to directly modify a model. Models can only be modified through actions. * However, in some cases you don't care about the advantages (like replayability, traceability, etc) this yields. * For example because you are building a PoC or don't have any middleware attached to your tree. * * In that case you can disable this protection by calling `unprotect` on the root of your tree. * * Example: * ```ts * const Todo = types.model({ * done: false * }).actions(self => ({ * toggle() { * self.done = !self.done * } * })) * * const todo = Todo.create() * todo.done = true // throws! * todo.toggle() // OK * unprotect(todo) * todo.done = false // OK * ``` */ function unprotect(target) { // check all arguments assertIsStateTreeNode(target, 1); var node = getStateTreeNode(target); if (!node.isRoot) throw fail$1("`unprotect` can only be invoked on root nodes"); node.isProtectionEnabled = false; } /** * Returns true if the object is in protected mode, @see protect */ function isProtected(target) { return getStateTreeNode(target).isProtected; } /** * Applies a snapshot to a given model instances. Patch and snapshot listeners will be invoked as usual. * * @param target * @param snapshot * @returns */ function applySnapshot(target, snapshot) { // check all arguments assertIsStateTreeNode(target, 1); return getStateTreeNode(target).applySnapshot(snapshot); } /** * Calculates a snapshot from the given model instance. The snapshot will always reflect the latest state but use * structural sharing where possible. Doesn't require MobX transactions to be completed. * * @param target * @param applyPostProcess If true (the default) then postProcessSnapshot gets applied. * @returns */ function getSnapshot(target, applyPostProcess) { if (applyPostProcess === void 0) { applyPostProcess = true; } // check all arguments assertIsStateTreeNode(target, 1); var node = getStateTreeNode(target); if (applyPostProcess) return node.snapshot; return freeze(node.type.getSnapshot(node, false)); } /** * Given a model instance, returns `true` if the object has a parent, that is, is part of another object, map or array. * * @param target * @param depth How far should we look upward? 1 by default. * @returns */ function hasParent(target, depth) { if (depth === void 0) { depth = 1; } // check all arguments assertIsStateTreeNode(target, 1); assertIsNumber(depth, 2, 0); var parent = getStateTreeNode(target).parent; while (parent) { if (--depth === 0) return true; parent = parent.parent; } return false; } /** * Returns the immediate parent of this object, or throws. * * Note that the immediate parent can be either an object, map or array, and * doesn't necessarily refer to the parent model. * * Please note that in child nodes access to the root is only possible * once the `afterAttach` hook has fired. * * @param target * @param depth How far should we look upward? 1 by default. * @returns */ function getParent(target, depth) { if (depth === void 0) { depth = 1; } // check all arguments assertIsStateTreeNode(target, 1); assertIsNumber(depth, 2, 0); var d = depth; var parent = getStateTreeNode(target).parent; while (parent) { if (--d === 0) return parent.storedValue; parent = parent.parent; } throw fail$1("Failed to find the parent of " + getStateTreeNode(target) + " at depth " + depth); } /** * Given a model instance, returns `true` if the object has a parent of given type, that is, is part of another object, map or array * * @param target * @param type * @returns */ function hasParentOfType(target, type) { // check all arguments assertIsStateTreeNode(target, 1); assertIsType(type, 2); var parent = getStateTreeNode(target).parent; while (parent) { if (type.is(parent.storedValue)) return true; parent = parent.parent; } return false; } /** * Returns the target's parent of a given type, or throws. * * @param target * @param type * @returns */ function getParentOfType(target, type) { // check all arguments assertIsStateTreeNode(target, 1); assertIsType(type, 2); var parent = getStateTreeNode(target).parent; while (parent) { if (type.is(parent.storedValue)) return parent.storedValue; parent = parent.parent; } throw fail$1("Failed to find the parent of " + getStateTreeNode(target) + " of a given type"); } /** * Given an object in a model tree, returns the root object of that tree. * * Please note that in child nodes access to the root is only possible * once the `afterAttach` hook has fired. * * @param target * @returns */ function getRoot(target) { // check all arguments assertIsStateTreeNode(target, 1); return getStateTreeNode(target).root.storedValue; } /** * Returns the path of the given object in the model tree * * @param target * @returns */ function getPath(target) { // check all arguments assertIsStateTreeNode(target, 1); return getStateTreeNode(target).path; } /** * Returns the path of the given object as unescaped string array. * * @param target * @returns */ function getPathParts(target) { // check all arguments assertIsStateTreeNode(target, 1); return splitJsonPath(getStateTreeNode(target).path); } /** * Returns true if the given object is the root of a model tree. * * @param target * @returns */ function isRoot(target) { // check all arguments assertIsStateTreeNode(target, 1); return getStateTreeNode(target).isRoot; } /** * Resolves a path relatively to a given object. * Returns undefined if no value can be found. * * @param target * @param path escaped json path * @returns */ function resolvePath(target, path) { // check all arguments assertIsStateTreeNode(target, 1); assertIsString(path, 2); var node = resolveNodeByPath(getStateTreeNode(target), path); return node ? node.value : undefined; } /** * Resolves a model instance given a root target, the type and the identifier you are searching for. * Returns undefined if no value can be found. * * @param type * @param target * @param identifier * @returns */ function resolveIdentifier(type, target, identifier) { // check all arguments assertIsType(type, 1); assertIsStateTreeNode(target, 2); assertIsValidIdentifier(identifier, 3); var node = getStateTreeNode(target).root.identifierCache.resolve(type, normalizeIdentifier(identifier)); return node ? node.value : undefined; } /** * Returns the identifier of the target node. * This is the *string normalized* identifier, which might not match the type of the identifier attribute * * @param target * @returns */ function getIdentifier(target) { // check all arguments assertIsStateTreeNode(target, 1); return getStateTreeNode(target).identifier; } /** * Tests if a reference is valid (pointing to an existing node and optionally if alive) and returns such reference if it the check passes, * else it returns undefined. * * @param getter Function to access the reference. * @param checkIfAlive true to also make sure the referenced node is alive (default), false to skip this check. * @returns */ function tryReference(getter, checkIfAlive) { if (checkIfAlive === void 0) { checkIfAlive = true; } try { var node = getter(); if (node === undefined || node === null) { return undefined; } else if (isStateTreeNode(node)) { if (!checkIfAlive) { return node; } else { return isAlive(node) ? node : undefined; } } else { throw fail$1("The reference to be checked is not one of node, null or undefined"); } } catch (e) { if (e instanceof InvalidReferenceError) { return undefined; } throw e; } } /** * Tests if a reference is valid (pointing to an existing node and optionally if alive) and returns if the check passes or not. * * @param getter Function to access the reference. * @param checkIfAlive true to also make sure the referenced node is alive (default), false to skip this check. * @returns */ function isValidReference(getter, checkIfAlive) { if (checkIfAlive === void 0) { checkIfAlive = true; } try { var node = getter(); if (node === undefined || node === null) { return false; } else if (isStateTreeNode(node)) { return checkIfAlive ? isAlive(node) : true; } else { throw fail$1("The reference to be checked is not one of node, null or undefined"); } } catch (e) { if (e instanceof InvalidReferenceError) { return false; } throw e; } } /** * Try to resolve a given path relative to a given node. * * @param target * @param path * @returns */ function tryResolve(target, path) { // check all arguments assertIsStateTreeNode(target, 1); assertIsString(path, 2); var node = resolveNodeByPath(getStateTreeNode(target), path, false); if (node === undefined) return undefined; try { return node.value; } catch (e) { // For what ever reason not resolvable (e.g. totally not existing path, or value that cannot be fetched) // see test / issue: 'try resolve doesn't work #686' return undefined; } } /** * Given two state tree nodes that are part of the same tree, * returns the shortest jsonpath needed to navigate from the one to the other * * @param base * @param target * @returns */ function getRelativePath(base, target) { // check all arguments assertIsStateTreeNode(base, 1); assertIsStateTreeNode(target, 2); return getRelativePathBetweenNodes(getStateTreeNode(base), getStateTreeNode(target)); } /** * Returns a deep copy of the given state tree node as new tree. * Short hand for `snapshot(x) = getType(x).create(getSnapshot(x))` * * _Tip: clone will create a literal copy, including the same identifiers. To modify identifiers etc during cloning, don't use clone but take a snapshot of the tree, modify it, and create new instance_ * * @param source * @param keepEnvironment indicates whether the clone should inherit the same environment (`true`, the default), or not have an environment (`false`). If an object is passed in as second argument, that will act as the environment for the cloned tree. * @returns */ function clone(source, keepEnvironment) { if (keepEnvironment === void 0) { keepEnvironment = true; } // check all arguments assertIsStateTreeNode(source, 1); var node = getStateTreeNode(source); return node.type.create(node.snapshot, keepEnvironment === true ? node.root.environment : keepEnvironment === false ? undefined : keepEnvironment); // it's an object or something else } /** * Removes a model element from the state tree, and let it live on as a new state tree */ function detach(target) { // check all arguments assertIsStateTreeNode(target, 1); getStateTreeNode(target).detach(); return target; } /** * Removes a model element from the state tree, and mark it as end-of-life; the element should not be used anymore */ function destroy(target) { // check all arguments assertIsStateTreeNode(target, 1); var node = getStateTreeNode(target); if (node.isRoot) node.die(); else node.parent.removeChild(node.subpath); } /** * Returns true if the given state tree node is not killed yet. * This means that the node is still a part of a tree, and that `destroy` * has not been called. If a node is not alive anymore, the only thing one can do with it * is requesting it's last path and snapshot * * @param target * @returns */ function isAlive(target) { // check all arguments assertIsStateTreeNode(target, 1); return getStateTreeNode(target).observableIsAlive; } /** * Use this utility to register a function that should be called whenever the * targeted state tree node is destroyed. This is a useful alternative to managing * cleanup methods yourself using the `beforeDestroy` hook. * * This methods returns the same disposer that was passed as argument. * * Example: * ```ts * const Todo = types.model({ * title: types.string * }).actions(self => ({ * afterCreate() { * const autoSaveDisposer = reaction( * () => getSnapshot(self), * snapshot => sendSnapshotToServerSomehow(snapshot) * ) * // stop sending updates to server if this * // instance is destroyed * addDisposer(self, autoSaveDisposer) * } * })) * ``` * * @param target * @param disposer * @returns The same disposer that was passed as argument */ function addDisposer(target, disposer) { // check all arguments assertIsStateTreeNode(target, 1); assertIsFunction(disposer, 2); var node = getStateTreeNode(target); node.addDisposer(disposer); return disposer; } /** * Returns the environment of the current state tree. For more info on environments, * see [Dependency injection](https://github.com/mobxjs/mobx-state-tree#dependency-injection) * * Please note that in child nodes access to the root is only possible * once the `afterAttach` hook has fired * * Returns an empty environment if the tree wasn't initialized with an environment * * @param target * @returns */ function getEnv(target) { // check all arguments assertIsStateTreeNode(target, 1); var node = getStateTreeNode(target); var env = node.root.environment; if (!env) return EMPTY_OBJECT; return env; } /** * Performs a depth first walk through a tree. */ function walk(target, processor) { // check all arguments assertIsStateTreeNode(target, 1); assertIsFunction(processor, 2); var node = getStateTreeNode(target); // tslint:disable-next-line:no_unused-variable node.getChildren().forEach(function (child) { if (isStateTreeNode(child.storedValue)) walk(child.storedValue, processor); }); processor(node.storedValue); } /** * Returns a reflection of the model type properties and name for either a model type or model node. * * @param typeOrNode * @returns */ function getPropertyMembers(typeOrNode) { var type; if (isStateTreeNode(typeOrNode)) { type = getType(typeOrNode); } else { type = typeOrNode; } assertArg(type, function (t) { return isModelType(t); }, "model type or model instance", 1); return { name: type.name, properties: __assign({}, type.properties) }; } /** * Returns a reflection of the model node, including name, properties, views, volatile and actions. * * @param target * @returns */ function getMembers(target) { var type = getStateTreeNode(target).type; var reflected = __assign(__assign({}, getPropertyMembers(type)), { actions: [], volatile: [], views: [] }); var props = Object.getOwnPropertyNames(target); props.forEach(function (key) { if (key in reflected.properties) return; var descriptor = Object.getOwnPropertyDescriptor(target, key); if (descriptor.get) { if (isComputedProp(target, key)) reflected.views.push(key); else reflected.volatile.push(key); return; } if (descriptor.value._isMSTAction === true) reflected.actions.push(key); else if (isObservableProp(target, key)) reflected.volatile.push(key); else reflected.views.push(key); }); return reflected; } /** * Casts a node snapshot or instance type to an instance type so it can be assigned to a type instance. * Note that this is just a cast for the type system, this is, it won't actually convert a snapshot to an instance, * but just fool typescript into thinking so. * Either way, casting when outside an assignation operation won't compile. * * Example: * ```ts * const ModelA = types.model({ * n: types.number * }).actions(self => ({ * setN(aNumber: number) { * self.n = aNumber * } * })) * * const ModelB = types.model({ * innerModel: ModelA * }).actions(self => ({ * someAction() { * // this will allow the compiler to assign a snapshot to the property * self.innerModel = cast({ a: 5 }) * } * })) * ``` * * @param snapshotOrInstance Snapshot or instance * @returns The same object casted as an instance */ function cast(snapshotOrInstance) { return snapshotOrInstance; } /** * Casts a node instance type to an snapshot type so it can be assigned to a type snapshot (e.g. to be used inside a create call). * Note that this is just a cast for the type system, this is, it won't actually convert an instance to a snapshot, * but just fool typescript into thinking so. * * Example: * ```ts * const ModelA = types.model({ * n: types.number * }).actions(self => ({ * setN(aNumber: number) { * self.n = aNumber * } * })) * * const ModelB = types.model({ * innerModel: ModelA * }) * * const a = ModelA.create({ n: 5 }); * // this will allow the compiler to use a model as if it were a snapshot * const b = ModelB.create({ innerModel: castToSnapshot(a)}) * ``` * * @param snapshotOrInstance Snapshot or instance * @returns The same object casted as an input (creation) snapshot */ function castToSnapshot(snapshotOrInstance) { return snapshotOrInstance; } /** * Casts a node instance type to a reference snapshot type so it can be assigned to a refernence snapshot (e.g. to be used inside a create call). * Note that this is just a cast for the type system, this is, it won't actually convert an instance to a refererence snapshot, * but just fool typescript into thinking so. * * Example: * ```ts * const ModelA = types.model({ * id: types.identifier, * n: types.number * }).actions(self => ({ * setN(aNumber: number) { * self.n = aNumber * } * })) * * const ModelB = types.model({ * refA: types.reference(ModelA) * }) * * const a = ModelA.create({ id: 'someId', n: 5 }); * // this will allow the compiler to use a model as if it were a reference snapshot * const b = ModelB.create({ refA: castToReference(a)}) * ``` * * @param instance Instance * @returns The same object casted as an reference snapshot (string or number) */ function castToReferenceSnapshot(instance) { return instance; } /** * Returns the unique node id (not to be confused with the instance identifier) for a * given instance. * This id is a number that is unique for each instance. * * @export * @param target * @returns */ function getNodeId(target) { assertIsStateTreeNode(target, 1); return getStateTreeNode(target).nodeId; } /** * @internal * @hidden */ var BaseNode = /** @class */ (function () { function BaseNode(type, parent, subpath, environment) { this.type = type; this.environment = environment; this._state = NodeLifeCycle.INITIALIZING; this.environment = environment; this.baseSetParent(parent, subpath); } Object.defineProperty(BaseNode.prototype, "subpath", { get: function () { return this._subpath; }, enumerable: true, configurable: true }); Object.defineProperty(BaseNode.prototype, "subpathUponDeath", { get: function () { return this._subpathUponDeath; }, enumerable: true, configurable: true }); Object.defineProperty(BaseNode.prototype, "pathUponDeath", { get: function () { return this._pathUponDeath; }, enumerable: true, configurable: true }); Object.defineProperty(BaseNode.prototype, "value", { get: function () { return this.type.getValue(this); }, enumerable: true, configurable: true }); Object.defineProperty(BaseNode.prototype, "state", { get: function () { return this._state; }, set: function (val) { var wasAlive = this.isAlive; this._state = val; var isAlive = this.isAlive; if (this.aliveAtom && wasAlive !== isAlive) { this.aliveAtom.reportChanged(); } }, enumerable: true, configurable: true }); BaseNode.prototype.fireInternalHook = function (name) { if (this._hookSubscribers) { this._hookSubscribers.emit(name, this, name); } }; BaseNode.prototype.registerHook = function (hook, hookHandler) { if (!this._hookSubscribers) { this._hookSubscribers = new EventHandlers(); } return this._hookSubscribers.register(hook, hookHandler); }; Object.defineProperty(BaseNode.prototype, "parent", { get: function () { return this._parent; }, enumerable: true, configurable: true }); BaseNode.prototype.baseSetParent = function (parent, subpath) { this._parent = parent; this._subpath = subpath; this._escapedSubpath = undefined; // regenerate when needed if (this.pathAtom) { this.pathAtom.reportChanged(); } }; Object.defineProperty(BaseNode.prototype, "path", { /* * Returns (escaped) path representation as string */ get: function () { return this.getEscapedPath(true); }, enumerable: true, configurable: true }); BaseNode.prototype.getEscapedPath = function (reportObserved) { if (reportObserved) { if (!this.pathAtom) { this.pathAtom = createAtom("path"); } this.pathAtom.reportObserved(); } if (!this.parent) return ""; // regenerate escaped subpath if needed if (this._escapedSubpath === undefined) { this._escapedSubpath = !this._subpath ? "" : escapeJsonPath(this._subpath); } return this.parent.getEscapedPath(reportObserved) + "/" + this._escapedSubpath; }; Object.defineProperty(BaseNode.prototype, "isRoot", { get: function () { return this.parent === null; }, enumerable: true, configurable: true }); Object.defineProperty(BaseNode.prototype, "isAlive", { get: function () { return this.state !== NodeLifeCycle.DEAD; }, enumerable: true, configurable: true }); Object.defineProperty(BaseNode.prototype, "isDetaching", { get: function () { return this.state === NodeLifeCycle.DETACHING; }, enumerable: true, configurable: true }); Object.defineProperty(BaseNode.prototype, "observableIsAlive", { get: function () { if (!this.aliveAtom) { this.aliveAtom = createAtom("alive"); } this.aliveAtom.reportObserved(); return this.isAlive; }, enumerable: true, configurable: true }); BaseNode.prototype.baseFinalizeCreation = function (whenFinalized) { if (devMode()) { if (!this.isAlive) { // istanbul ignore next throw fail("assertion failed: cannot finalize the creation of a node that is already dead"); } } // goal: afterCreate hooks runs depth-first. After attach runs parent first, so on afterAttach the parent has completed already if (this.state === NodeLifeCycle.CREATED) { if (this.parent) { if (this.parent.state !== NodeLifeCycle.FINALIZED) { // parent not ready yet, postpone return; } this.fireHook(Hook.afterAttach); } this.state = NodeLifeCycle.FINALIZED; if (whenFinalized) { whenFinalized(); } } }; BaseNode.prototype.baseFinalizeDeath = function () { if (this._hookSubscribers) { this._hookSubscribers.clearAll(); } this._subpathUponDeath = this._subpath; this._pathUponDeath = this.getEscapedPath(false); this.baseSetParent(null, ""); this.state = NodeLifeCycle.DEAD; }; BaseNode.prototype.baseAboutToDie = function () { this.fireHook(Hook.beforeDestroy); }; return BaseNode; }()); /** * @internal * @hidden */ var ScalarNode = /** @class */ (function (_super) { __extends(ScalarNode, _super); function ScalarNode(simpleType, parent, subpath, environment, initialSnapshot) { var _this = _super.call(this, simpleType, parent, subpath, environment) || this; try { _this.storedValue = simpleType.createNewInstance(initialSnapshot); } catch (e) { // short-cut to die the instance, to avoid the snapshot computed starting to throw... _this.state = NodeLifeCycle.DEAD; throw e; } _this.state = NodeLifeCycle.CREATED; // for scalar nodes there's no point in firing this event since it would fire on the constructor, before // anybody can actually register for/listen to it // this.fireHook(Hook.AfterCreate) _this.finalizeCreation(); return _this; } Object.defineProperty(ScalarNode.prototype, "root", { get: function () { // future optimization: store root ref in the node and maintain it if (!this.parent) throw fail$1("This scalar node is not part of a tree"); return this.parent.root; }, enumerable: true, configurable: true }); ScalarNode.prototype.setParent = function (newParent, subpath) { var parentChanged = this.parent !== newParent; var subpathChanged = this.subpath !== subpath; if (!parentChanged && !subpathChanged) { return; } if (devMode()) { if (!subpath) { // istanbul ignore next throw fail$1("assertion failed: subpath expected"); } if (!newParent) { // istanbul ignore next throw fail$1("assertion failed: parent expected"); } if (parentChanged) { // istanbul ignore next throw fail$1("assertion failed: scalar nodes cannot change their parent"); } } this.environment = undefined; // use parent's this.baseSetParent(this.parent, subpath); }; Object.defineProperty(ScalarNode.prototype, "snapshot", { get: function () { return freeze(this.getSnapshot()); }, enumerable: true, configurable: true }); ScalarNode.prototype.getSnapshot = function () { return this.type.getSnapshot(this); }; ScalarNode.prototype.toString = function () { var path = (this.isAlive ? this.path : this.pathUponDeath) || ""; return this.type.name + "@" + path + (this.isAlive ? "" : " [dead]"); }; ScalarNode.prototype.die = function () { if (!this.isAlive || this.state === NodeLifeCycle.DETACHING) return; this.aboutToDie(); this.finalizeDeath(); }; ScalarNode.prototype.finalizeCreation = function () { this.baseFinalizeCreation(); }; ScalarNode.prototype.aboutToDie = function () { this.baseAboutToDie(); }; ScalarNode.prototype.finalizeDeath = function () { this.baseFinalizeDeath(); }; ScalarNode.prototype.fireHook = function (name) { this.fireInternalHook(name); }; __decorate([ action ], ScalarNode.prototype, "die", null); return ScalarNode; }(BaseNode)); var nextNodeId = 1; var snapshotReactionOptions = { onError: function (e) { throw e; } }; /** * @internal * @hidden */ var ObjectNode = /** @class */ (function (_super) { __extends(ObjectNode, _super); function ObjectNode(complexType, parent, subpath, environment, initialValue) { var _this = _super.call(this, complexType, parent, subpath, environment) || this; _this.nodeId = ++nextNodeId; _this.isProtectionEnabled = true; _this._autoUnbox = true; // unboxing is disabled when reading child nodes _this._isRunningAction = false; // only relevant for root _this._hasSnapshotReaction = false; _this._observableInstanceState = 0 /* UNINITIALIZED */; _this._cachedInitialSnapshotCreated = false; _this.unbox = _this.unbox.bind(_this); _this._initialSnapshot = freeze(initialValue); _this.identifierAttribute = complexType.identifierAttribute; if (!parent) { _this.identifierCache = new IdentifierCache(); } _this._childNodes = complexType.initializeChildNodes(_this, _this._initialSnapshot); // identifier can not be changed during lifecycle of a node // so we safely can read it from initial snapshot _this.identifier = null; _this.unnormalizedIdentifier = null; if (_this.identifierAttribute && _this._initialSnapshot) { var id = _this._initialSnapshot[_this.identifierAttribute]; if (id === undefined) { // try with the actual node if not (for optional identifiers) var childNode = _this._childNodes[_this.identifierAttribute]; if (childNode) { id = childNode.value; } } if (typeof id !== "string" && typeof id !== "number") { throw fail$1("Instance identifier '" + _this.identifierAttribute + "' for type '" + _this.type.name + "' must be a string or a number"); } // normalize internal identifier to string _this.identifier = normalizeIdentifier(id); _this.unnormalizedIdentifier = id; } if (!parent) { _this.identifierCache.addNodeToCache(_this); } else { parent.root.identifierCache.addNodeToCache(_this); } return _this; } ObjectNode.prototype.applyPatches = function (patches) { this.createObservableInstanceIfNeeded(); this._applyPatches(patches); }; ObjectNode.prototype.applySnapshot = function (snapshot) { this.createObservableInstanceIfNeeded(); this._applySnapshot(snapshot); }; ObjectNode.prototype.createObservableInstanceIfNeeded = function () { var e_1, _a; if (this._observableInstanceState !== 0 /* UNINITIALIZED */) { return; } if (devMode()) { if (this.state !== NodeLifeCycle.INITIALIZING) { // istanbul ignore next throw fail$1("assertion failed: the creation of the observable instance must be done on the initializing phase"); } } this._observableInstanceState = 1 /* CREATING */; // make sure the parent chain is created as well // array with parent chain from parent to child var parentChain = []; var parent = this.parent; // for performance reasons we never go back further than the most direct // uninitialized parent // this is done to avoid traversing the whole tree to the root when using // the same reference again while (parent && parent._observableInstanceState === 0 /* UNINITIALIZED */) { parentChain.unshift(parent); parent = parent.parent; } try { // initialize the uninitialized parent chain from parent to child for (var parentChain_1 = __values(parentChain), parentChain_1_1 = parentChain_1.next(); !parentChain_1_1.done; parentChain_1_1 = parentChain_1.next()) { var p = parentChain_1_1.value; p.createObservableInstanceIfNeeded(); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (parentChain_1_1 && !parentChain_1_1.done && (_a = parentChain_1.return)) _a.call(parentChain_1); } finally { if (e_1) throw e_1.error; } } var type = this.type; try { this.storedValue = type.createNewInstance(this._childNodes); this.preboot(); this._isRunningAction = true; type.finalizeNewInstance(this, this.storedValue); } catch (e) { // short-cut to die the instance, to avoid the snapshot computed starting to throw... this.state = NodeLifeCycle.DEAD; throw e; } finally { this._isRunningAction = false; } this._observableInstanceState = 2 /* CREATED */; // NOTE: we need to touch snapshot, because non-observable // "_observableInstanceState" field was touched invalidateComputed(this, "snapshot"); if (this.isRoot) this._addSnapshotReaction(); this._childNodes = EMPTY_OBJECT; this.state = NodeLifeCycle.CREATED; this.fireHook(Hook.afterCreate); this.finalizeCreation(); }; Object.defineProperty(ObjectNode.prototype, "root", { get: function () { var parent = this.parent; return parent ? parent.root : this; }, enumerable: true, configurable: true }); ObjectNode.prototype.clearParent = function () { if (!this.parent) return; // detach if attached this.fireHook(Hook.beforeDetach); var previousState = this.state; this.state = NodeLifeCycle.DETACHING; var root = this.root; var newEnv = root.environment; var newIdCache = root.identifierCache.splitCache(this); try { this.parent.removeChild(this.subpath); this.baseSetParent(null, ""); this.environment = newEnv; this.identifierCache = newIdCache; } finally { this.state = previousState; } }; ObjectNode.prototype.setParent = function (newParent, subpath) { var parentChanged = newParent !== this.parent; var subpathChanged = subpath !== this.subpath; if (!parentChanged && !subpathChanged) { return; } if (devMode()) { if (!subpath) { // istanbul ignore next throw fail$1("assertion failed: subpath expected"); } if (!newParent) { // istanbul ignore next throw fail$1("assertion failed: new parent expected"); } if (this.parent && parentChanged) { throw fail$1("A node cannot exists twice in the state tree. Failed to add " + this + " to path '" + newParent.path + "/" + subpath + "'."); } if (!this.parent && newParent.root === this) { throw fail$1("A state tree is not allowed to contain itself. Cannot assign " + this + " to path '" + newParent.path + "/" + subpath + "'"); } if (!this.parent && !!this.environment && this.environment !== newParent.root.environment) { throw fail$1("A state tree cannot be made part of another state tree as long as their environments are different."); } } if (parentChanged) { // attach to new parent this.environment = undefined; // will use root's newParent.root.identifierCache.mergeCache(this); this.baseSetParent(newParent, subpath); this.fireHook(Hook.afterAttach); } else if (subpathChanged) { // moving to a new subpath on the same parent this.baseSetParent(this.parent, subpath); } }; ObjectNode.prototype.fireHook = function (name) { var _this = this; this.fireInternalHook(name); var fn = this.storedValue && typeof this.storedValue === "object" && this.storedValue[name]; if (typeof fn === "function") { // we check for it to allow old mobx peer dependencies that don't have the method to work (even when still bugged) if (_allowStateChangesInsideComputed) { _allowStateChangesInsideComputed(function () { fn.apply(_this.storedValue); }); } else { fn.apply(this.storedValue); } } }; Object.defineProperty(ObjectNode.prototype, "snapshot", { // advantage of using computed for a snapshot is that nicely respects transactions etc. get: function () { return freeze(this.getSnapshot()); }, enumerable: true, configurable: true }); // NOTE: we use this method to get snapshot without creating @computed overhead ObjectNode.prototype.getSnapshot = function () { if (!this.isAlive) return this._snapshotUponDeath; return this._observableInstanceState === 2 /* CREATED */ ? this._getActualSnapshot() : this._getCachedInitialSnapshot(); }; ObjectNode.prototype._getActualSnapshot = function () { return this.type.getSnapshot(this); }; ObjectNode.prototype._getCachedInitialSnapshot = function () { if (!this._cachedInitialSnapshotCreated) { var type = this.type; var childNodes = this._childNodes; var snapshot = this._initialSnapshot; this._cachedInitialSnapshot = type.processInitialSnapshot(childNodes, snapshot); this._cachedInitialSnapshotCreated = true; } return this._cachedInitialSnapshot; }; ObjectNode.prototype.isRunningAction = function () { if (this._isRunningAction) return true; if (this.isRoot) return false; return this.parent.isRunningAction(); }; ObjectNode.prototype.assertAlive = function (context) { var livelinessChecking = getLivelinessChecking(); if (!this.isAlive && livelinessChecking !== "ignore") { var error = this._getAssertAliveError(context); switch (livelinessChecking) { case "error": throw fail$1(error); case "warn": warnError(error); } } }; ObjectNode.prototype._getAssertAliveError = function (context) { var escapedPath = this.getEscapedPath(false) || this.pathUponDeath || ""; var subpath = (context.subpath && escapeJsonPath(context.subpath)) || ""; var actionContext = context.actionContext || getCurrentActionContext(); // try to use a real action context if possible since it includes the action name if (actionContext && actionContext.type !== "action" && actionContext.parentActionEvent) { actionContext = actionContext.parentActionEvent; } var actionFullPath = ""; if (actionContext && actionContext.name != null) { // try to use the context, and if it not available use the node one var actionPath = (actionContext && actionContext.context && getPath(actionContext.context)) || escapedPath; actionFullPath = actionPath + "." + actionContext.name + "()"; } return "You are trying to read or write to an object that is no longer part of a state tree. (Object type: '" + this.type.name + "', Path upon death: '" + escapedPath + "', Subpath: '" + subpath + "', Action: '" + actionFullPath + "'). Either detach nodes first, or don't use objects after removing / replacing them in the tree."; }; ObjectNode.prototype.getChildNode = function (subpath) { this.assertAlive({ subpath: subpath }); this._autoUnbox = false; try { return this._observableInstanceState === 2 /* CREATED */ ? this.type.getChildNode(this, subpath) : this._childNodes[subpath]; } finally { this._autoUnbox = true; } }; ObjectNode.prototype.getChildren = function () { this.assertAlive(EMPTY_OBJECT); this._autoUnbox = false; try { return this._observableInstanceState === 2 /* CREATED */ ? this.type.getChildren(this) : convertChildNodesToArray(this._childNodes); } finally { this._autoUnbox = true; } }; ObjectNode.prototype.getChildType = function (propertyName) { return this.type.getChildType(propertyName); }; Object.defineProperty(ObjectNode.prototype, "isProtected", { get: function () { return this.root.isProtectionEnabled; }, enumerable: true, configurable: true }); ObjectNode.prototype.assertWritable = function (context) { this.assertAlive(context); if (!this.isRunningAction() && this.isProtected) { throw fail$1("Cannot modify '" + this + "', the object is protected and can only be modified by using an action."); } }; ObjectNode.prototype.removeChild = function (subpath) { this.type.removeChild(this, subpath); }; // bound on the constructor ObjectNode.prototype.unbox = function (childNode) { if (!childNode) return childNode; this.assertAlive({ subpath: childNode.subpath || childNode.subpathUponDeath }); return this._autoUnbox ? childNode.value : childNode; }; ObjectNode.prototype.toString = function () { var path = (this.isAlive ? this.path : this.pathUponDeath) || ""; var identifier = this.identifier ? "(id: " + this.identifier + ")" : ""; return this.type.name + "@" + path + identifier + (this.isAlive ? "" : " [dead]"); }; ObjectNode.prototype.finalizeCreation = function () { var _this = this; this.baseFinalizeCreation(function () { var e_2, _a; try { for (var _b = __values(_this.getChildren()), _c = _b.next(); !_c.done; _c = _b.next()) { var child = _c.value; child.finalizeCreation(); } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_2) throw e_2.error; } } _this.fireInternalHook(Hook.afterCreationFinalization); }); }; ObjectNode.prototype.detach = function () { if (!this.isAlive) throw fail$1("Error while detaching, node is not alive."); this.clearParent(); }; ObjectNode.prototype.preboot = function () { var self = this; this._applyPatches = createActionInvoker(this.storedValue, "@APPLY_PATCHES", function (patches) { patches.forEach(function (patch) { var parts = splitJsonPath(patch.path); var node = resolveNodeByPathParts(self, parts.slice(0, -1)); node.applyPatchLocally(parts[parts.length - 1], patch); }); }); this._applySnapshot = createActionInvoker(this.storedValue, "@APPLY_SNAPSHOT", function (snapshot) { // if the snapshot is the same as the current one, avoid performing a reconcile if (snapshot === self.snapshot) return; // else, apply it by calling the type logic return self.type.applySnapshot(self, snapshot); }); addHiddenFinalProp(this.storedValue, "$treenode", this); addHiddenFinalProp(this.storedValue, "toJSON", toJSON); }; ObjectNode.prototype.die = function () { if (!this.isAlive || this.state === NodeLifeCycle.DETACHING) return; this.aboutToDie(); this.finalizeDeath(); }; ObjectNode.prototype.aboutToDie = function () { if (this._observableInstanceState === 0 /* UNINITIALIZED */) { return; } this.getChildren().forEach(function (node) { node.aboutToDie(); }); // beforeDestroy should run before the disposers since else we could end up in a situation where // a disposer added with addDisposer at this stage (beforeDestroy) is actually never released this.baseAboutToDie(); this._internalEventsEmit("dispose" /* Dispose */); this._internalEventsClear("dispose" /* Dispose */); }; ObjectNode.prototype.finalizeDeath = function () { // invariant: not called directly but from "die" this.getChildren().forEach(function (node) { node.finalizeDeath(); }); this.root.identifierCache.notifyDied(this); // "kill" the computed prop and just store the last snapshot var snapshot = this.snapshot; this._snapshotUponDeath = snapshot; this._internalEventsClearAll(); this.baseFinalizeDeath(); }; ObjectNode.prototype.onSnapshot = function (onChange) { this._addSnapshotReaction(); return this._internalEventsRegister("snapshot" /* Snapshot */, onChange); }; ObjectNode.prototype.emitSnapshot = function (snapshot) { this._internalEventsEmit("snapshot" /* Snapshot */, snapshot); }; ObjectNode.prototype.onPatch = function (handler) { return this._internalEventsRegister("patch" /* Patch */, handler); }; ObjectNode.prototype.emitPatch = function (basePatch, source) { if (this._internalEventsHasSubscribers("patch" /* Patch */)) { var localizedPatch = extend({}, basePatch, { path: source.path.substr(this.path.length) + "/" + basePatch.path // calculate the relative path of the patch }); var _a = __read(splitPatch(localizedPatch), 2), patch = _a[0], reversePatch = _a[1]; this._internalEventsEmit("patch" /* Patch */, patch, reversePatch); } if (this.parent) this.parent.emitPatch(basePatch, source); }; ObjectNode.prototype.hasDisposer = function (disposer) { return this._internalEventsHas("dispose" /* Dispose */, disposer); }; ObjectNode.prototype.addDisposer = function (disposer) { if (!this.hasDisposer(disposer)) { this._internalEventsRegister("dispose" /* Dispose */, disposer, true); return; } throw fail$1("cannot add a disposer when it is already registered for execution"); }; ObjectNode.prototype.removeDisposer = function (disposer) { if (!this._internalEventsHas("dispose" /* Dispose */, disposer)) { throw fail$1("cannot remove a disposer which was never registered for execution"); } this._internalEventsUnregister("dispose" /* Dispose */, disposer); }; ObjectNode.prototype.removeMiddleware = function (middleware) { if (this.middlewares) { var index = this.middlewares.indexOf(middleware); if (index >= 0) { this.middlewares.splice(index, 1); } } }; ObjectNode.prototype.addMiddleWare = function (handler, includeHooks) { var _this = this; if (includeHooks === void 0) { includeHooks = true; } var middleware = { handler: handler, includeHooks: includeHooks }; if (!this.middlewares) this.middlewares = [middleware]; else this.middlewares.push(middleware); return function () { _this.removeMiddleware(middleware); }; }; ObjectNode.prototype.applyPatchLocally = function (subpath, patch) { this.assertWritable({ subpath: subpath }); this.createObservableInstanceIfNeeded(); this.type.applyPatchLocally(this, subpath, patch); }; ObjectNode.prototype._addSnapshotReaction = function () { var _this = this; if (!this._hasSnapshotReaction) { var snapshotDisposer = reaction(function () { return _this.snapshot; }, function (snapshot) { return _this.emitSnapshot(snapshot); }, snapshotReactionOptions); this.addDisposer(snapshotDisposer); this._hasSnapshotReaction = true; } }; // we proxy the methods to avoid creating an EventHandlers instance when it is not needed ObjectNode.prototype._internalEventsHasSubscribers = function (event) { return !!this._internalEvents && this._internalEvents.hasSubscribers(event); }; ObjectNode.prototype._internalEventsRegister = function (event, eventHandler, atTheBeginning) { if (atTheBeginning === void 0) { atTheBeginning = false; } if (!this._internalEvents) { this._internalEvents = new EventHandlers(); } return this._internalEvents.register(event, eventHandler, atTheBeginning); }; ObjectNode.prototype._internalEventsHas = function (event, eventHandler) { return !!this._internalEvents && this._internalEvents.has(event, eventHandler); }; ObjectNode.prototype._internalEventsUnregister = function (event, eventHandler) { if (this._internalEvents) { this._internalEvents.unregister(event, eventHandler); } }; ObjectNode.prototype._internalEventsEmit = function (event) { var _a; var args = []; for (var _i = 1; _i < arguments.length; _i++) { args[_i - 1] = arguments[_i]; } if (this._internalEvents) { (_a = this._internalEvents).emit.apply(_a, __spread([event], args)); } }; ObjectNode.prototype._internalEventsClear = function (event) { if (this._internalEvents) { this._internalEvents.clear(event); } }; ObjectNode.prototype._internalEventsClearAll = function () { if (this._internalEvents) { this._internalEvents.clearAll(); } }; __decorate([ action ], ObjectNode.prototype, "createObservableInstanceIfNeeded", null); __decorate([ computed ], ObjectNode.prototype, "snapshot", null); __decorate([ action ], ObjectNode.prototype, "detach", null); __decorate([ action ], ObjectNode.prototype, "die", null); return ObjectNode; }(BaseNode)); /** * @internal * @hidden */ var TypeFlags; (function (TypeFlags) { TypeFlags[TypeFlags["String"] = 1] = "String"; TypeFlags[TypeFlags["Number"] = 2] = "Number"; TypeFlags[TypeFlags["Boolean"] = 4] = "Boolean"; TypeFlags[TypeFlags["Date"] = 8] = "Date"; TypeFlags[TypeFlags["Literal"] = 16] = "Literal"; TypeFlags[TypeFlags["Array"] = 32] = "Array"; TypeFlags[TypeFlags["Map"] = 64] = "Map"; TypeFlags[TypeFlags["Object"] = 128] = "Object"; TypeFlags[TypeFlags["Frozen"] = 256] = "Frozen"; TypeFlags[TypeFlags["Optional"] = 512] = "Optional"; TypeFlags[TypeFlags["Reference"] = 1024] = "Reference"; TypeFlags[TypeFlags["Identifier"] = 2048] = "Identifier"; TypeFlags[TypeFlags["Late"] = 4096] = "Late"; TypeFlags[TypeFlags["Refinement"] = 8192] = "Refinement"; TypeFlags[TypeFlags["Union"] = 16384] = "Union"; TypeFlags[TypeFlags["Null"] = 32768] = "Null"; TypeFlags[TypeFlags["Undefined"] = 65536] = "Undefined"; TypeFlags[TypeFlags["Integer"] = 131072] = "Integer"; TypeFlags[TypeFlags["Custom"] = 262144] = "Custom"; TypeFlags[TypeFlags["SnapshotProcessor"] = 524288] = "SnapshotProcessor"; })(TypeFlags || (TypeFlags = {})); /** * @internal * @hidden */ var cannotDetermineSubtype = "cannotDetermine"; /** * A base type produces a MST node (Node in the state tree) * * @internal * @hidden */ var BaseType = /** @class */ (function () { function BaseType(name) { this.isType = true; this.name = name; } BaseType.prototype.create = function (snapshot, environment) { typecheckInternal(this, snapshot); return this.instantiate(null, "", environment, snapshot).value; }; BaseType.prototype.getSnapshot = function (node, applyPostProcess) { // istanbul ignore next throw fail$1("unimplemented method"); }; BaseType.prototype.isAssignableFrom = function (type) { return type === this; }; BaseType.prototype.validate = function (value, context) { var node = getStateTreeNodeSafe(value); if (node) { var valueType = getType(value); return this.isAssignableFrom(valueType) ? typeCheckSuccess() : typeCheckFailure(context, value); // it is tempting to compare snapshots, but in that case we should always clone on assignments... } return this.isValidSnapshot(value, context); }; BaseType.prototype.is = function (thing) { return this.validate(thing, [{ path: "", type: this }]).length === 0; }; Object.defineProperty(BaseType.prototype, "Type", { get: function () { // istanbul ignore next throw fail$1("Factory.Type should not be actually called. It is just a Type signature that can be used at compile time with Typescript, by using `typeof type.Type`"); }, enumerable: true, configurable: true }); Object.defineProperty(BaseType.prototype, "TypeWithoutSTN", { get: function () { // istanbul ignore next throw fail$1("Factory.TypeWithoutSTN should not be actually called. It is just a Type signature that can be used at compile time with Typescript, by using `typeof type.TypeWithoutSTN`"); }, enumerable: true, configurable: true }); Object.defineProperty(BaseType.prototype, "SnapshotType", { get: function () { // istanbul ignore next throw fail$1("Factory.SnapshotType should not be actually called. It is just a Type signature that can be used at compile time with Typescript, by using `typeof type.SnapshotType`"); }, enumerable: true, configurable: true }); Object.defineProperty(BaseType.prototype, "CreationType", { get: function () { // istanbul ignore next throw fail$1("Factory.CreationType should not be actually called. It is just a Type signature that can be used at compile time with Typescript, by using `typeof type.CreationType`"); }, enumerable: true, configurable: true }); __decorate([ action ], BaseType.prototype, "create", null); return BaseType; }()); /** * A complex type produces a MST node (Node in the state tree) * * @internal * @hidden */ var ComplexType = /** @class */ (function (_super) { __extends(ComplexType, _super); function ComplexType(name) { return _super.call(this, name) || this; } ComplexType.prototype.create = function (snapshot, environment) { if (snapshot === void 0) { snapshot = this.getDefaultSnapshot(); } return _super.prototype.create.call(this, snapshot, environment); }; ComplexType.prototype.getValue = function (node) { node.createObservableInstanceIfNeeded(); return node.storedValue; }; ComplexType.prototype.tryToReconcileNode = function (current, newValue) { if (current.isDetaching) return false; if (current.snapshot === newValue) { // newValue is the current snapshot of the node, noop return true; } if (isStateTreeNode(newValue) && getStateTreeNode(newValue) === current) { // the current node is the same as the new one return true; } if (current.type === this && isMutable(newValue) && !isStateTreeNode(newValue) && (!current.identifierAttribute || current.identifier === normalizeIdentifier(newValue[current.identifierAttribute]))) { // the newValue has no node, so can be treated like a snapshot // we can reconcile current.applySnapshot(newValue); return true; } return false; }; ComplexType.prototype.reconcile = function (current, newValue, parent, subpath) { var nodeReconciled = this.tryToReconcileNode(current, newValue); if (nodeReconciled) { current.setParent(parent, subpath); return current; } // current node cannot be recycled in any way current.die(); // noop if detaching // attempt to reuse the new one if (isStateTreeNode(newValue) && this.isAssignableFrom(getType(newValue))) { // newValue is a Node as well, move it here.. var newNode = getStateTreeNode(newValue); newNode.setParent(parent, subpath); return newNode; } // nothing to do, we have to create a new node return this.instantiate(parent, subpath, undefined, newValue); }; ComplexType.prototype.getSubTypes = function () { return null; }; __decorate([ action ], ComplexType.prototype, "create", null); return ComplexType; }(BaseType)); /** * @internal * @hidden */ var SimpleType = /** @class */ (function (_super) { __extends(SimpleType, _super); function SimpleType() { return _super !== null && _super.apply(this, arguments) || this; } SimpleType.prototype.createNewInstance = function (snapshot) { return snapshot; }; SimpleType.prototype.getValue = function (node) { // if we ever find a case where scalar nodes can be accessed without iterating through its parent // uncomment this to make sure the parent chain is created when this is accessed // if (node.parent) { // node.parent.createObservableInstanceIfNeeded() // } return node.storedValue; }; SimpleType.prototype.getSnapshot = function (node) { return node.storedValue; }; SimpleType.prototype.reconcile = function (current, newValue, parent, subpath) { // reconcile only if type and value are still the same, and only if the node is not detaching if (!current.isDetaching && current.type === this && current.storedValue === newValue) { return current; } var res = this.instantiate(parent, subpath, undefined, newValue); current.die(); // noop if detaching return res; }; SimpleType.prototype.getSubTypes = function () { return null; }; return SimpleType; }(BaseType)); /** * Returns if a given value represents a type. * * @param value Value to check. * @returns `true` if the value is a type. */ function isType(value) { return typeof value === "object" && value && value.isType === true; } /** * @internal * @hidden */ function assertIsType(type, argNumber) { assertArg(type, isType, "mobx-state-tree type", argNumber); } var runningActions = new Map(); /** * Note: Consider migrating to `createActionTrackingMiddleware2`, it is easier to use. * * Convenience utility to create action based middleware that supports async processes more easily. * All hooks are called for both synchronous and asynchronous actions. Except that either `onSuccess` or `onFail` is called * * The create middleware tracks the process of an action (assuming it passes the `filter`). * `onResume` can return any value, which will be passed as second argument to any other hook. This makes it possible to keep state during a process. * * See the `atomic` middleware for an example * * @param hooks * @returns */ function createActionTrackingMiddleware(hooks) { return function actionTrackingMiddleware(call, next, abort) { switch (call.type) { case "action": { if (!hooks.filter || hooks.filter(call) === true) { var context = hooks.onStart(call); hooks.onResume(call, context); runningActions.set(call.id, { call: call, context: context, async: false }); try { var res = next(call); hooks.onSuspend(call, context); if (runningActions.get(call.id).async === false) { runningActions.delete(call.id); hooks.onSuccess(call, context, res); } return res; } catch (e) { runningActions.delete(call.id); hooks.onFail(call, context, e); throw e; } } else { return next(call); } } case "flow_spawn": { var root = runningActions.get(call.rootId); root.async = true; return next(call); } case "flow_resume": case "flow_resume_error": { var root = runningActions.get(call.rootId); hooks.onResume(call, root.context); try { return next(call); } finally { hooks.onSuspend(call, root.context); } } case "flow_throw": { var root = runningActions.get(call.rootId); runningActions.delete(call.rootId); hooks.onFail(call, root.context, call.args[0]); return next(call); } case "flow_return": { var root = runningActions.get(call.rootId); runningActions.delete(call.rootId); hooks.onSuccess(call, root.context, call.args[0]); return next(call); } } }; } var RunningAction = /** @class */ (function () { function RunningAction(hooks, call) { this.hooks = hooks; this.call = call; this.flowsPending = 0; this.running = true; if (hooks) { hooks.onStart(call); } } RunningAction.prototype.finish = function (error) { if (this.running) { this.running = false; if (this.hooks) { this.hooks.onFinish(this.call, error); } } }; RunningAction.prototype.incFlowsPending = function () { this.flowsPending++; }; RunningAction.prototype.decFlowsPending = function () { this.flowsPending--; }; Object.defineProperty(RunningAction.prototype, "hasFlowsPending", { get: function () { return this.flowsPending > 0; }, enumerable: true, configurable: true }); return RunningAction; }()); /** * Convenience utility to create action based middleware that supports async processes more easily. * The flow is like this: * - for each action: if filter passes -> `onStart` -> (inner actions recursively) -> `onFinish` * * Example: if we had an action `a` that called inside an action `b1`, then `b2` the flow would be: * - `filter(a)` * - `onStart(a)` * - `filter(b1)` * - `onStart(b1)` * - `onFinish(b1)` * - `filter(b2)` * - `onStart(b2)` * - `onFinish(b2)` * - `onFinish(a)` * * The flow is the same no matter if the actions are sync or async. * * See the `atomic` middleware for an example * * @param hooks * @returns */ function createActionTrackingMiddleware2(middlewareHooks) { var runningActions = new WeakMap(); return function actionTrackingMiddleware(call, next) { // find parentRunningAction var parentRunningAction = call.parentActionEvent ? runningActions.get(call.parentActionEvent) : undefined; if (call.type === "action") { var newCall = __assign(__assign({}, call), { // make a shallow copy of the parent action env env: parentRunningAction && parentRunningAction.call.env, parentCall: parentRunningAction && parentRunningAction.call }); var passesFilter = !middlewareHooks.filter || middlewareHooks.filter(newCall); var hooks = passesFilter ? middlewareHooks : undefined; var runningAction = new RunningAction(hooks, newCall); runningActions.set(call, runningAction); var res = void 0; try { res = next(call); } catch (e) { runningAction.finish(e); throw e; } if (!runningAction.hasFlowsPending) { // sync action finished runningAction.finish(); } return res; } else { if (!parentRunningAction) { return next(call); } switch (call.type) { case "flow_spawn": { parentRunningAction.incFlowsPending(); return next(call); } case "flow_resume": case "flow_resume_error": { return next(call); } case "flow_throw": { var error = call.args[0]; try { return next(call); } finally { parentRunningAction.decFlowsPending(); if (!parentRunningAction.hasFlowsPending) { parentRunningAction.finish(error); } } } case "flow_return": { try { return next(call); } finally { parentRunningAction.decFlowsPending(); if (!parentRunningAction.hasFlowsPending) { parentRunningAction.finish(); } } } } } }; } function serializeArgument(node, actionName, index, arg) { if (arg instanceof Date) return { $MST_DATE: arg.getTime() }; if (isPrimitive(arg)) return arg; // We should not serialize MST nodes, even if we can, because we don't know if the receiving party can handle a raw snapshot instead of an // MST type instance. So if one wants to serialize a MST node that was pass in, either explitly pass: 1: an id, 2: a (relative) path, 3: a snapshot if (isStateTreeNode(arg)) return serializeTheUnserializable("[MSTNode: " + getType(arg).name + "]"); if (typeof arg === "function") return serializeTheUnserializable("[function]"); if (typeof arg === "object" && !isPlainObject(arg) && !isArray(arg)) return serializeTheUnserializable("[object " + ((arg && arg.constructor && arg.constructor.name) || "Complex Object") + "]"); try { // Check if serializable, cycle free etc... // MWE: there must be a better way.... JSON.stringify(arg); // or throws return arg; } catch (e) { return serializeTheUnserializable("" + e); } } function deserializeArgument(adm, value) { if (value && typeof value === "object" && "$MST_DATE" in value) return new Date(value["$MST_DATE"]); return value; } function serializeTheUnserializable(baseType) { return { $MST_UNSERIALIZABLE: true, type: baseType }; } /** * Applies an action or a series of actions in a single MobX transaction. * Does not return any value * Takes an action description as produced by the `onAction` middleware. * * @param target * @param actions */ function applyAction(target, actions) { // check all arguments assertIsStateTreeNode(target, 1); assertArg(actions, function (a) { return typeof a === "object"; }, "object or array", 2); runInAction(function () { asArray(actions).forEach(function (action) { return baseApplyAction(target, action); }); }); } function baseApplyAction(target, action) { var resolvedTarget = tryResolve(target, action.path || ""); if (!resolvedTarget) throw fail$1("Invalid action path: " + (action.path || "")); var node = getStateTreeNode(resolvedTarget); // Reserved functions if (action.name === "@APPLY_PATCHES") { return applyPatch.call(null, resolvedTarget, action.args[0]); } if (action.name === "@APPLY_SNAPSHOT") { return applySnapshot.call(null, resolvedTarget, action.args[0]); } if (!(typeof resolvedTarget[action.name] === "function")) throw fail$1("Action '" + action.name + "' does not exist in '" + node.path + "'"); return resolvedTarget[action.name].apply(resolvedTarget, action.args ? action.args.map(function (v) { return deserializeArgument(node, v); }) : []); } /** * Small abstraction around `onAction` and `applyAction`, attaches an action listener to a tree and records all the actions emitted. * Returns an recorder object with the following signature: * * Example: * ```ts * export interface IActionRecorder { * // the recorded actions * actions: ISerializedActionCall[] * // true if currently recording * recording: boolean * // stop recording actions * stop(): void * // resume recording actions * resume(): void * // apply all the recorded actions on the given object * replay(target: IAnyStateTreeNode): void * } * ``` * * The optional filter function allows to skip recording certain actions. * * @param subject * @returns */ function recordActions(subject, filter) { // check all arguments assertIsStateTreeNode(subject, 1); var actions = []; var listener = function (call) { var recordThis = filter ? filter(call, getRunningActionContext()) : true; if (recordThis) { actions.push(call); } }; var disposer; var recorder = { actions: actions, get recording() { return !!disposer; }, stop: function () { if (disposer) { disposer(); disposer = undefined; } }, resume: function () { if (disposer) return; disposer = onAction(subject, listener); }, replay: function (target) { applyAction(target, actions); } }; recorder.resume(); return recorder; } /** * Registers a function that will be invoked for each action that is called on the provided model instance, or to any of its children. * See [actions](https://github.com/mobxjs/mobx-state-tree#actions) for more details. onAction events are emitted only for the outermost called action in the stack. * Action can also be intercepted by middleware using addMiddleware to change the function call before it will be run. * * Not all action arguments might be serializable. For unserializable arguments, a struct like `{ $MST_UNSERIALIZABLE: true, type: "someType" }` will be generated. * MST Nodes are considered non-serializable as well (they could be serialized as there snapshot, but it is uncertain whether an replaying party will be able to handle such a non-instantiated snapshot). * Rather, when using `onAction` middleware, one should consider in passing arguments which are 1: an id, 2: a (relative) path, or 3: a snapshot. Instead of a real MST node. * * Example: * ```ts * const Todo = types.model({ * task: types.string * }) * * const TodoStore = types.model({ * todos: types.array(Todo) * }).actions(self => ({ * add(todo) { * self.todos.push(todo); * } * })) * * const s = TodoStore.create({ todos: [] }) * * let disposer = onAction(s, (call) => { * console.log(call); * }) * * s.add({ task: "Grab a coffee" }) * // Logs: { name: "add", path: "", args: [{ task: "Grab a coffee" }] } * ``` * * @param target * @param listener * @param attachAfter (default false) fires the listener *after* the action has executed instead of before. * @returns */ function onAction(target, listener, attachAfter) { if (attachAfter === void 0) { attachAfter = false; } // check all arguments assertIsStateTreeNode(target, 1); if (devMode()) { if (!isRoot(target)) warnError("Warning: Attaching onAction listeners to non root nodes is dangerous: No events will be emitted for actions initiated higher up in the tree."); if (!isProtected(target)) warnError("Warning: Attaching onAction listeners to non protected nodes is dangerous: No events will be emitted for direct modifications without action."); } return addMiddleware(target, function handler(rawCall, next) { if (rawCall.type === "action" && rawCall.id === rawCall.rootId) { var sourceNode_1 = getStateTreeNode(rawCall.context); var info = { name: rawCall.name, path: getRelativePathBetweenNodes(getStateTreeNode(target), sourceNode_1), args: rawCall.args.map(function (arg, index) { return serializeArgument(sourceNode_1, rawCall.name, index, arg); }) }; if (attachAfter) { var res = next(rawCall); listener(info); return res; } else { listener(info); return next(rawCall); } } else { return next(rawCall); } }); } var nextActionId = 1; var currentActionContext; /** * @internal * @hidden */ function getCurrentActionContext() { return currentActionContext; } /** * @internal * @hidden */ function getNextActionId() { return nextActionId++; } // TODO: optimize away entire action context if there is no middleware in tree? /** * @internal * @hidden */ function runWithActionContext(context, fn) { var node = getStateTreeNode(context.context); if (context.type === "action") { node.assertAlive({ actionContext: context }); } var baseIsRunningAction = node._isRunningAction; node._isRunningAction = true; var previousContext = currentActionContext; currentActionContext = context; try { return runMiddleWares(node, context, fn); } finally { currentActionContext = previousContext; node._isRunningAction = baseIsRunningAction; } } /** * @internal * @hidden */ function getParentActionContext(parentContext) { if (!parentContext) return undefined; if (parentContext.type === "action") return parentContext; return parentContext.parentActionEvent; } /** * @internal * @hidden */ function createActionInvoker(target, name, fn) { var res = function () { var id = getNextActionId(); var parentContext = currentActionContext; var parentActionContext = getParentActionContext(parentContext); return runWithActionContext({ type: "action", name: name, id: id, args: argsToArray(arguments), context: target, tree: getRoot(target), rootId: parentContext ? parentContext.rootId : id, parentId: parentContext ? parentContext.id : 0, allParentIds: parentContext ? __spread(parentContext.allParentIds, [parentContext.id]) : [], parentEvent: parentContext, parentActionEvent: parentActionContext }, fn); }; res._isMSTAction = true; return res; } /** * Middleware can be used to intercept any action is invoked on the subtree where it is attached. * If a tree is protected (by default), this means that any mutation of the tree will pass through your middleware. * * For more details, see the [middleware docs](../middleware.md) * * @param target Node to apply the middleware to. * @param middleware Middleware to apply. * @returns A callable function to dispose the middleware. */ function addMiddleware(target, handler, includeHooks) { if (includeHooks === void 0) { includeHooks = true; } var node = getStateTreeNode(target); if (devMode()) { if (!node.isProtectionEnabled) { warnError("It is recommended to protect the state tree before attaching action middleware, as otherwise it cannot be guaranteed that all changes are passed through middleware. See `protect`"); } } return node.addMiddleWare(handler, includeHooks); } /** * Binds middleware to a specific action. * * Example: * ```ts * type.actions(self => { * function takeA____() { * self.toilet.donate() * self.wipe() * self.wipe() * self.toilet.flush() * } * return { * takeA____: decorate(atomic, takeA____) * } * }) * ``` * * @param handler * @param fn * @param includeHooks * @returns The original function */ function decorate(handler, fn, includeHooks) { if (includeHooks === void 0) { includeHooks = true; } var middleware = { handler: handler, includeHooks: includeHooks }; fn.$mst_middleware = fn.$mst_middleware || []; fn.$mst_middleware.push(middleware); return fn; } var CollectedMiddlewares = /** @class */ (function () { function CollectedMiddlewares(node, fn) { this.arrayIndex = 0; this.inArrayIndex = 0; this.middlewares = []; // we just push middleware arrays into an array of arrays to avoid making copies if (fn.$mst_middleware) { this.middlewares.push(fn.$mst_middleware); } var n = node; // Find all middlewares. Optimization: cache this? while (n) { if (n.middlewares) this.middlewares.push(n.middlewares); n = n.parent; } } Object.defineProperty(CollectedMiddlewares.prototype, "isEmpty", { get: function () { return this.middlewares.length <= 0; }, enumerable: true, configurable: true }); CollectedMiddlewares.prototype.getNextMiddleware = function () { var array = this.middlewares[this.arrayIndex]; if (!array) return undefined; var item = array[this.inArrayIndex++]; if (!item) { this.arrayIndex++; this.inArrayIndex = 0; return this.getNextMiddleware(); } return item; }; return CollectedMiddlewares; }()); function runMiddleWares(node, baseCall, originalFn) { var middlewares = new CollectedMiddlewares(node, originalFn); // Short circuit if (middlewares.isEmpty) return action(originalFn).apply(null, baseCall.args); var result = null; function runNextMiddleware(call) { var middleware = middlewares.getNextMiddleware(); var handler = middleware && middleware.handler; if (!handler) { return action(originalFn).apply(null, call.args); } // skip hooks if asked to if (!middleware.includeHooks && Hook[call.name]) { return runNextMiddleware(call); } var nextInvoked = false; function next(call2, callback) { nextInvoked = true; // the result can contain // - the non manipulated return value from an action // - the non manipulated abort value // - one of the above but manipulated through the callback function result = runNextMiddleware(call2); if (callback) { result = callback(result); } } var abortInvoked = false; function abort(value) { abortInvoked = true; // overwrite the result // can be manipulated through middlewares earlier in the queue using the callback fn result = value; } handler(call, next, abort); if (devMode()) { if (!nextInvoked && !abortInvoked) { var node2 = getStateTreeNode(call.tree); throw fail$1("Neither the next() nor the abort() callback within the middleware " + handler.name + " for the action: \"" + call.name + "\" on the node: " + node2.type.name + " was invoked."); } else if (nextInvoked && abortInvoked) { var node2 = getStateTreeNode(call.tree); throw fail$1("The next() and abort() callback within the middleware " + handler.name + " for the action: \"" + call.name + "\" on the node: " + node2.type.name + " were invoked."); } } return result; } return runNextMiddleware(baseCall); } /** * Returns the currently executing MST action context, or undefined if none. */ function getRunningActionContext() { var current = getCurrentActionContext(); while (current && current.type !== "action") { current = current.parentActionEvent; } return current; } function _isActionContextThisOrChildOf(actionContext, sameOrParent, includeSame) { var parentId = typeof sameOrParent === "number" ? sameOrParent : sameOrParent.id; var current = includeSame ? actionContext : actionContext.parentActionEvent; while (current) { if (current.id === parentId) { return true; } current = current.parentActionEvent; } return false; } /** * Returns if the given action context is a parent of this action context. */ function isActionContextChildOf(actionContext, parent) { return _isActionContextThisOrChildOf(actionContext, parent, false); } /** * Returns if the given action context is this or a parent of this action context. */ function isActionContextThisOrChildOf(actionContext, parentOrThis) { return _isActionContextThisOrChildOf(actionContext, parentOrThis, true); } function safeStringify(value) { try { return JSON.stringify(value); } catch (e) { // istanbul ignore next return ""; } } /** * @internal * @hidden */ function prettyPrintValue(value) { return typeof value === "function" ? "" : isStateTreeNode(value) ? "<" + value + ">" : "`" + safeStringify(value) + "`"; } function shortenPrintValue(valueInString) { return valueInString.length < 280 ? valueInString : valueInString.substring(0, 272) + "......" + valueInString.substring(valueInString.length - 8); } function toErrorString(error) { var value = error.value; var type = error.context[error.context.length - 1].type; var fullPath = error.context .map(function (_a) { var path = _a.path; return path; }) .filter(function (path) { return path.length > 0; }) .join("/"); var pathPrefix = fullPath.length > 0 ? "at path \"/" + fullPath + "\" " : ""; var currentTypename = isStateTreeNode(value) ? "value of type " + getStateTreeNode(value).type.name + ":" : isPrimitive(value) ? "value" : "snapshot"; var isSnapshotCompatible = type && isStateTreeNode(value) && type.is(getStateTreeNode(value).snapshot); return ("" + pathPrefix + currentTypename + " " + prettyPrintValue(value) + " is not assignable " + (type ? "to type: `" + type.name + "`" : "") + (error.message ? " (" + error.message + ")" : "") + (type ? isPrimitiveType(type) || isPrimitive(value) ? "." : ", expected an instance of `" + type.name + "` or a snapshot like `" + type.describe() + "` instead." + (isSnapshotCompatible ? " (Note that a snapshot of the provided value is compatible with the targeted type)" : "") : ".")); } /** * @internal * @hidden */ function getContextForPath(context, path, type) { return context.concat([{ path: path, type: type }]); } /** * @internal * @hidden */ function typeCheckSuccess() { return EMPTY_ARRAY; } /** * @internal * @hidden */ function typeCheckFailure(context, value, message) { return [{ context: context, value: value, message: message }]; } /** * @internal * @hidden */ function flattenTypeErrors(errors) { return errors.reduce(function (a, i) { return a.concat(i); }, []); } // TODO; doublecheck: typecheck should only needed to be invoked from: type.create and array / map / value.property will change /** * @internal * @hidden */ function typecheckInternal(type, value) { // runs typeChecking if it is in dev-mode or through a process.env.ENABLE_TYPE_CHECK flag if (isTypeCheckingEnabled()) { typecheck(type, value); } } /** * Run's the typechecker for the given type on the given value, which can be a snapshot or an instance. * Throws if the given value is not according the provided type specification. * Use this if you need typechecks even in a production build (by default all automatic runtime type checks will be skipped in production builds) * * @param type Type to check against. * @param value Value to be checked, either a snapshot or an instance. */ function typecheck(type, value) { var errors = type.validate(value, [{ path: "", type: type }]); if (errors.length > 0) { throw fail$1(validationErrorsToString(type, value, errors)); } } function validationErrorsToString(type, value, errors) { if (errors.length === 0) { return undefined; } return ("Error while converting " + shortenPrintValue(prettyPrintValue(value)) + " to `" + type.name + "`:\n\n " + errors.map(toErrorString).join("\n ")); } var identifierCacheId = 0; /** * @internal * @hidden */ var IdentifierCache = /** @class */ (function () { function IdentifierCache() { this.cacheId = identifierCacheId++; // n.b. in cache all identifiers are normalized to strings this.cache = observable.map(); // last time the cache (array) for a given time changed // n.b. it is not really the time, but just an integer that gets increased after each modification to the array this.lastCacheModificationPerId = observable.map(); } IdentifierCache.prototype.updateLastCacheModificationPerId = function (identifier) { var lcm = this.lastCacheModificationPerId.get(identifier); // we start at 1 since 0 means no update since cache creation this.lastCacheModificationPerId.set(identifier, lcm === undefined ? 1 : lcm + 1); }; IdentifierCache.prototype.getLastCacheModificationPerId = function (identifier) { var modificationId = this.lastCacheModificationPerId.get(identifier) || 0; return this.cacheId + "-" + modificationId; }; IdentifierCache.prototype.addNodeToCache = function (node, lastCacheUpdate) { if (lastCacheUpdate === void 0) { lastCacheUpdate = true; } if (node.identifierAttribute) { var identifier = node.identifier; if (!this.cache.has(identifier)) { this.cache.set(identifier, observable.array([], mobxShallow)); } var set = this.cache.get(identifier); if (set.indexOf(node) !== -1) throw fail$1("Already registered"); set.push(node); if (lastCacheUpdate) { this.updateLastCacheModificationPerId(identifier); } } }; IdentifierCache.prototype.mergeCache = function (node) { var _this = this; values(node.identifierCache.cache).forEach(function (nodes) { return nodes.forEach(function (child) { _this.addNodeToCache(child); }); }); }; IdentifierCache.prototype.notifyDied = function (node) { if (node.identifierAttribute) { var id = node.identifier; var set = this.cache.get(id); if (set) { set.remove(node); // remove empty sets from cache if (!set.length) { this.cache.delete(id); } this.updateLastCacheModificationPerId(node.identifier); } } }; IdentifierCache.prototype.splitCache = function (node) { var _this = this; var res = new IdentifierCache(); var basePath = node.path; entries(this.cache).forEach(function (_a) { var _b = __read(_a, 2), id = _b[0], nodes = _b[1]; var modified = false; for (var i = nodes.length - 1; i >= 0; i--) { if (nodes[i].path.indexOf(basePath) === 0) { res.addNodeToCache(nodes[i], false); // no need to update lastUpdated since it is a whole new cache nodes.splice(i, 1); modified = true; } } if (modified) { _this.updateLastCacheModificationPerId(id); } }); return res; }; IdentifierCache.prototype.has = function (type, identifier) { var set = this.cache.get(identifier); if (!set) return false; return set.some(function (candidate) { return type.isAssignableFrom(candidate.type); }); }; IdentifierCache.prototype.resolve = function (type, identifier) { var set = this.cache.get(identifier); if (!set) return null; var matches = set.filter(function (candidate) { return type.isAssignableFrom(candidate.type); }); switch (matches.length) { case 0: return null; case 1: return matches[0]; default: throw fail$1("Cannot resolve a reference to type '" + type.name + "' with id: '" + identifier + "' unambigously, there are multiple candidates: " + matches .map(function (n) { return n.path; }) .join(", ")); } }; return IdentifierCache; }()); /** * @internal * @hidden */ function createObjectNode(type, parent, subpath, environment, initialValue) { var existingNode = getStateTreeNodeSafe(initialValue); if (existingNode) { if (existingNode.parent) { // istanbul ignore next throw fail$1("Cannot add an object to a state tree if it is already part of the same or another state tree. Tried to assign an object to '" + (parent ? parent.path : "") + "/" + subpath + "', but it lives already at '" + existingNode.path + "'"); } if (parent) { existingNode.setParent(parent, subpath); } // else it already has no parent since it is a pre-requisite return existingNode; } // not a node, a snapshot return new ObjectNode(type, parent, subpath, environment, initialValue); } /** * @internal * @hidden */ function createScalarNode(type, parent, subpath, environment, initialValue) { return new ScalarNode(type, parent, subpath, environment, initialValue); } /** * @internal * @hidden */ function isNode(value) { return value instanceof ScalarNode || value instanceof ObjectNode; } /** * @internal * @hidden */ var NodeLifeCycle; (function (NodeLifeCycle) { NodeLifeCycle[NodeLifeCycle["INITIALIZING"] = 0] = "INITIALIZING"; NodeLifeCycle[NodeLifeCycle["CREATED"] = 1] = "CREATED"; NodeLifeCycle[NodeLifeCycle["FINALIZED"] = 2] = "FINALIZED"; NodeLifeCycle[NodeLifeCycle["DETACHING"] = 3] = "DETACHING"; NodeLifeCycle[NodeLifeCycle["DEAD"] = 4] = "DEAD"; // no coming back from this one })(NodeLifeCycle || (NodeLifeCycle = {})); /** * Returns true if the given value is a node in a state tree. * More precisely, that is, if the value is an instance of a * `types.model`, `types.array` or `types.map`. * * @param value * @returns true if the value is a state tree node. */ function isStateTreeNode(value) { return !!(value && value.$treenode); } /** * @internal * @hidden */ function assertIsStateTreeNode(value, argNumber) { assertArg(value, isStateTreeNode, "mobx-state-tree node", argNumber); } /** * @internal * @hidden */ function getStateTreeNode(value) { if (!isStateTreeNode(value)) { // istanbul ignore next throw fail$1("Value " + value + " is no MST Node"); } return value.$treenode; } /** * @internal * @hidden */ function getStateTreeNodeSafe(value) { return (value && value.$treenode) || null; } /** * @internal * @hidden */ function toJSON() { return getStateTreeNode(this).snapshot; } var doubleDot = function (_) { return ".."; }; /** * @internal * @hidden */ function getRelativePathBetweenNodes(base, target) { // PRE condition target is (a child of) base! if (base.root !== target.root) { throw fail$1("Cannot calculate relative path: objects '" + base + "' and '" + target + "' are not part of the same object tree"); } var baseParts = splitJsonPath(base.path); var targetParts = splitJsonPath(target.path); var common = 0; for (; common < baseParts.length; common++) { if (baseParts[common] !== targetParts[common]) break; } // TODO: assert that no targetParts paths are "..", "." or ""! return (baseParts .slice(common) .map(doubleDot) .join("/") + joinJsonPath(targetParts.slice(common))); } /** * @internal * @hidden */ function resolveNodeByPath(base, path, failIfResolveFails) { if (failIfResolveFails === void 0) { failIfResolveFails = true; } return resolveNodeByPathParts(base, splitJsonPath(path), failIfResolveFails); } /** * @internal * @hidden */ function resolveNodeByPathParts(base, pathParts, failIfResolveFails) { if (failIfResolveFails === void 0) { failIfResolveFails = true; } var current = base; for (var i = 0; i < pathParts.length; i++) { var part = pathParts[i]; if (part === "..") { current = current.parent; if (current) continue; // not everything has a parent } else if (part === ".") { continue; } else if (current) { if (current instanceof ScalarNode) { // check if the value of a scalar resolves to a state tree node (e.g. references) // then we can continue resolving... try { var value = current.value; if (isStateTreeNode(value)) { current = getStateTreeNode(value); // fall through } } catch (e) { if (!failIfResolveFails) { return undefined; } throw e; } } if (current instanceof ObjectNode) { var subType = current.getChildType(part); if (subType) { current = current.getChildNode(part); if (current) continue; } } } if (failIfResolveFails) throw fail$1("Could not resolve '" + part + "' in path '" + (joinJsonPath(pathParts.slice(0, i)) || "/") + "' while resolving '" + joinJsonPath(pathParts) + "'"); else return undefined; } return current; } /** * @internal * @hidden */ function convertChildNodesToArray(childNodes) { if (!childNodes) return EMPTY_ARRAY; var keys = Object.keys(childNodes); if (!keys.length) return EMPTY_ARRAY; var result = new Array(keys.length); keys.forEach(function (key, index) { result[index] = childNodes[key]; }); return result; } // based on: https://github.com/mobxjs/mobx-utils/blob/master/src/async-action.ts /* All contents of this file are deprecated. The term `process` has been replaced with `flow` to avoid conflicts with the global `process` object. Refer to `flow.ts` for any further changes to this implementation. */ var DEPRECATION_MESSAGE = "See https://github.com/mobxjs/mobx-state-tree/issues/399 for more information. " + "Note that the middleware event types starting with `process` now start with `flow`."; /** * @hidden * * @deprecated has been renamed to `flow()`. * See https://github.com/mobxjs/mobx-state-tree/issues/399 for more information. * Note that the middleware event types starting with `process` now start with `flow`. * * @returns {Promise} */ function process$1(asyncAction) { deprecated("process", "`process()` has been renamed to `flow()`. " + DEPRECATION_MESSAGE); return flow(asyncAction); } /** * @internal * @hidden */ var EMPTY_ARRAY = Object.freeze([]); /** * @internal * @hidden */ var EMPTY_OBJECT = Object.freeze({}); /** * @internal * @hidden */ var mobxShallow = typeof $mobx === "string" ? { deep: false } : { deep: false, proxy: false }; Object.freeze(mobxShallow); /** * @internal * @hidden */ function fail$1(message) { if (message === void 0) { message = "Illegal state"; } return new Error("[mobx-state-tree] " + message); } /** * @internal * @hidden */ function identity(_) { return _; } /** * pollyfill (for IE) suggested in MDN: * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger * @internal * @hidden */ var isInteger = Number.isInteger || function (value) { return typeof value === "number" && isFinite(value) && Math.floor(value) === value; }; /** * @internal * @hidden */ function isArray(val) { return Array.isArray(val) || isObservableArray(val); } /** * @internal * @hidden */ function asArray(val) { if (!val) return EMPTY_ARRAY; if (isArray(val)) return val; return [val]; } /** * @internal * @hidden */ function extend(a) { var b = []; for (var _i = 1; _i < arguments.length; _i++) { b[_i - 1] = arguments[_i]; } for (var i = 0; i < b.length; i++) { var current = b[i]; for (var key in current) a[key] = current[key]; } return a; } /** * @internal * @hidden */ function isPlainObject(value) { if (value === null || typeof value !== "object") return false; var proto = Object.getPrototypeOf(value); return proto === Object.prototype || proto === null; } /** * @internal * @hidden */ function isMutable(value) { return (value !== null && typeof value === "object" && !(value instanceof Date) && !(value instanceof RegExp)); } /** * @internal * @hidden */ function isPrimitive(value, includeDate) { if (includeDate === void 0) { includeDate = true; } if (value === null || value === undefined) return true; if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || (includeDate && value instanceof Date)) return true; return false; } /** * @internal * @hidden * Freeze a value and return it (if not in production) */ function freeze(value) { if (!devMode()) return value; return isPrimitive(value) || isObservableArray(value) ? value : Object.freeze(value); } /** * @internal * @hidden * Recursively freeze a value (if not in production) */ function deepFreeze(value) { if (!devMode()) return value; freeze(value); if (isPlainObject(value)) { Object.keys(value).forEach(function (propKey) { if (!isPrimitive(value[propKey]) && !Object.isFrozen(value[propKey])) { deepFreeze(value[propKey]); } }); } return value; } /** * @internal * @hidden */ function isSerializable(value) { return typeof value !== "function"; } /** * @internal * @hidden */ function addHiddenFinalProp(object, propName, value) { Object.defineProperty(object, propName, { enumerable: false, writable: false, configurable: true, value: value }); } /** * @internal * @hidden */ function addHiddenWritableProp(object, propName, value) { Object.defineProperty(object, propName, { enumerable: false, writable: true, configurable: true, value: value }); } /** * @internal * @hidden */ var EventHandler = /** @class */ (function () { function EventHandler() { this.handlers = []; } Object.defineProperty(EventHandler.prototype, "hasSubscribers", { get: function () { return this.handlers.length > 0; }, enumerable: true, configurable: true }); EventHandler.prototype.register = function (fn, atTheBeginning) { var _this = this; if (atTheBeginning === void 0) { atTheBeginning = false; } if (atTheBeginning) { this.handlers.unshift(fn); } else { this.handlers.push(fn); } return function () { _this.unregister(fn); }; }; EventHandler.prototype.has = function (fn) { return this.handlers.indexOf(fn) >= 0; }; EventHandler.prototype.unregister = function (fn) { var index = this.handlers.indexOf(fn); if (index >= 0) { this.handlers.splice(index, 1); } }; EventHandler.prototype.clear = function () { this.handlers.length = 0; }; EventHandler.prototype.emit = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } // make a copy just in case it changes var handlers = this.handlers.slice(); handlers.forEach(function (f) { return f.apply(void 0, __spread(args)); }); }; return EventHandler; }()); /** * @internal * @hidden */ var EventHandlers = /** @class */ (function () { function EventHandlers() { } EventHandlers.prototype.hasSubscribers = function (event) { var handler = this.eventHandlers && this.eventHandlers[event]; return !!handler && handler.hasSubscribers; }; EventHandlers.prototype.register = function (event, fn, atTheBeginning) { if (atTheBeginning === void 0) { atTheBeginning = false; } if (!this.eventHandlers) { this.eventHandlers = {}; } var handler = this.eventHandlers[event]; if (!handler) { handler = this.eventHandlers[event] = new EventHandler(); } return handler.register(fn, atTheBeginning); }; EventHandlers.prototype.has = function (event, fn) { var handler = this.eventHandlers && this.eventHandlers[event]; return !!handler && handler.has(fn); }; EventHandlers.prototype.unregister = function (event, fn) { var handler = this.eventHandlers && this.eventHandlers[event]; if (handler) { handler.unregister(fn); } }; EventHandlers.prototype.clear = function (event) { if (this.eventHandlers) { delete this.eventHandlers[event]; } }; EventHandlers.prototype.clearAll = function () { this.eventHandlers = undefined; }; EventHandlers.prototype.emit = function (event) { var _a; var args = []; for (var _i = 1; _i < arguments.length; _i++) { args[_i - 1] = arguments[_i]; } var handler = this.eventHandlers && this.eventHandlers[event]; if (handler) { (_a = handler).emit.apply(_a, __spread(args)); } }; return EventHandlers; }()); /** * @internal * @hidden */ function argsToArray(args) { var res = new Array(args.length); for (var i = 0; i < args.length; i++) res[i] = args[i]; return res; } /** * @internal * @hidden */ function invalidateComputed(target, propName) { var atom = getAtom(target, propName); atom.trackAndCompute(); } /** * @internal * @hidden */ function stringStartsWith(str, beginning) { return str.indexOf(beginning) === 0; } /** * @internal * @hidden */ var deprecated = function (id, message) { // skip if running production if (!devMode()) return; // warn if hasn't been warned before if (deprecated.ids && !deprecated.ids.hasOwnProperty(id)) { warnError("Deprecation warning: " + message); } // mark as warned to avoid duplicate warn message if (deprecated.ids) deprecated.ids[id] = true; }; deprecated.ids = {}; /** * @internal * @hidden */ function warnError(msg) { console.warn(new Error("[mobx-state-tree] " + msg)); } /** * @internal * @hidden */ function isTypeCheckingEnabled() { return (devMode() || (typeof process !== "undefined" && process.env && process.env.ENABLE_TYPE_CHECK === "true")); } /** * @internal * @hidden */ function devMode() { return process.env.NODE_ENV !== "production"; } /** * @internal * @hidden */ function assertArg(value, fn, typeName, argNumber) { if (devMode()) { if (!fn(value)) { // istanbul ignore next throw fail$1("expected " + typeName + " as argument " + asArray(argNumber).join(" or ") + ", got " + value + " instead"); } } } /** * @internal * @hidden */ function assertIsFunction(value, argNumber) { assertArg(value, function (fn) { return typeof fn === "function"; }, "function", argNumber); } /** * @internal * @hidden */ function assertIsNumber(value, argNumber, min, max) { assertArg(value, function (n) { return typeof n === "number"; }, "number", argNumber); if (min !== undefined) { assertArg(value, function (n) { return n >= min; }, "number greater than " + min, argNumber); } if (max !== undefined) { assertArg(value, function (n) { return n <= max; }, "number lesser than " + max, argNumber); } } /** * @internal * @hidden */ function assertIsString(value, argNumber, canBeEmpty) { if (canBeEmpty === void 0) { canBeEmpty = true; } assertArg(value, function (s) { return typeof s === "string"; }, "string", argNumber); if (!canBeEmpty) { assertArg(value, function (s) { return s !== ""; }, "not empty string", argNumber); } } /** * See [asynchronous actions](https://github.com/mobxjs/mobx-state-tree/blob/master/docs/async-actions.md). * * @returns The flow as a promise. */ function flow(generator) { return createFlowSpawner(generator.name, generator); } /** * @deprecated Not needed since TS3.6. * Used for TypeScript to make flows that return a promise return the actual promise result. * * @param val * @returns */ function castFlowReturn(val) { return val; } /** * @internal * @hidden */ function createFlowSpawner(name, generator) { var spawner = function flowSpawner() { // Implementation based on https://github.com/tj/co/blob/master/index.js var runId = getNextActionId(); var parentContext = getCurrentActionContext(); if (!parentContext) { throw fail$1("a mst flow must always have a parent context"); } var parentActionContext = getParentActionContext(parentContext); if (!parentActionContext) { throw fail$1("a mst flow must always have a parent action context"); } var contextBase = { name: name, id: runId, tree: parentContext.tree, context: parentContext.context, parentId: parentContext.id, allParentIds: __spread(parentContext.allParentIds, [parentContext.id]), rootId: parentContext.rootId, parentEvent: parentContext, parentActionEvent: parentActionContext }; var args = arguments; function wrap(fn, type, arg) { fn.$mst_middleware = spawner.$mst_middleware; // pick up any middleware attached to the flow runWithActionContext(__assign(__assign({}, contextBase), { type: type, args: [arg] }), fn); } return new Promise(function (resolve, reject) { var gen; var init = function asyncActionInit() { gen = generator.apply(null, arguments); onFulfilled(undefined); // kick off the flow }; init.$mst_middleware = spawner.$mst_middleware; runWithActionContext(__assign(__assign({}, contextBase), { type: "flow_spawn", args: argsToArray(args) }), init); function onFulfilled(res) { var ret; try { // prettier-ignore wrap(function (r) { ret = gen.next(r); }, "flow_resume", res); } catch (e) { // prettier-ignore setImmediate(function () { wrap(function (r) { reject(e); }, "flow_throw", e); }); return; } next(ret); return; } function onRejected(err) { var ret; try { // prettier-ignore wrap(function (r) { ret = gen.throw(r); }, "flow_resume_error", err); // or yieldError? } catch (e) { // prettier-ignore setImmediate(function () { wrap(function (r) { reject(e); }, "flow_throw", e); }); return; } next(ret); } function next(ret) { if (ret.done) { // prettier-ignore setImmediate(function () { wrap(function (r) { resolve(r); }, "flow_return", ret.value); }); return; } // TODO: support more type of values? See https://github.com/tj/co/blob/249bbdc72da24ae44076afd716349d2089b31c4c/index.js#L100 if (!ret.value || typeof ret.value.then !== "function") { // istanbul ignore next throw fail$1("Only promises can be yielded to `async`, got: " + ret); } return ret.value.then(onFulfilled, onRejected); } }); }; return spawner; } /** * @internal * @hidden */ function splitPatch(patch) { if (!("oldValue" in patch)) throw fail$1("Patches without `oldValue` field cannot be inversed"); return [stripPatch(patch), invertPatch(patch)]; } /** * @internal * @hidden */ function stripPatch(patch) { // strips `oldvalue` information from the patch, so that it becomes a patch conform the json-patch spec // this removes the ability to undo the patch switch (patch.op) { case "add": return { op: "add", path: patch.path, value: patch.value }; case "remove": return { op: "remove", path: patch.path }; case "replace": return { op: "replace", path: patch.path, value: patch.value }; } } function invertPatch(patch) { switch (patch.op) { case "add": return { op: "remove", path: patch.path }; case "remove": return { op: "add", path: patch.path, value: patch.oldValue }; case "replace": return { op: "replace", path: patch.path, value: patch.oldValue }; } } /** * Simple simple check to check it is a number. */ function isNumber(x) { return typeof x === "number"; } /** * Escape slashes and backslashes. * * http://tools.ietf.org/html/rfc6901 */ function escapeJsonPath(path) { if (isNumber(path) === true) { return "" + path; } if (path.indexOf("/") === -1 && path.indexOf("~") === -1) return path; return path.replace(/~/g, "~0").replace(/\//g, "~1"); } /** * Unescape slashes and backslashes. */ function unescapeJsonPath(path) { return path.replace(/~1/g, "/").replace(/~0/g, "~"); } /** * Generates a json-path compliant json path from path parts. * * @param path * @returns */ function joinJsonPath(path) { // `/` refers to property with an empty name, while `` refers to root itself! if (path.length === 0) return ""; var getPathStr = function (p) { return p.map(escapeJsonPath).join("/"); }; if (path[0] === "." || path[0] === "..") { // relative return getPathStr(path); } else { // absolute return "/" + getPathStr(path); } } /** * Splits and decodes a json path into several parts. * * @param path * @returns */ function splitJsonPath(path) { // `/` refers to property with an empty name, while `` refers to root itself! var parts = path.split("/").map(unescapeJsonPath); var valid = path === "" || path === "." || path === ".." || stringStartsWith(path, "/") || stringStartsWith(path, "./") || stringStartsWith(path, "../"); if (!valid) { throw fail$1("a json path must be either rooted, empty or relative, but got '" + path + "'"); } // '/a/b/c' -> ["a", "b", "c"] // '../../b/c' -> ["..", "..", "b", "c"] // '' -> [] // '/' -> [''] // './a' -> [".", "a"] // /./a' -> [".", "a"] equivalent to './a' if (parts[0] === "") { parts.shift(); } return parts; } var SnapshotProcessor = /** @class */ (function (_super) { __extends(SnapshotProcessor, _super); function SnapshotProcessor(_subtype, _processors, name) { var _this = _super.call(this, name || _subtype.name) || this; _this._subtype = _subtype; _this._processors = _processors; return _this; } Object.defineProperty(SnapshotProcessor.prototype, "flags", { get: function () { return this._subtype.flags | TypeFlags.SnapshotProcessor; }, enumerable: true, configurable: true }); SnapshotProcessor.prototype.describe = function () { return "snapshotProcessor(" + this._subtype.describe() + ")"; }; SnapshotProcessor.prototype.preProcessSnapshot = function (sn) { if (this._processors.preProcessor) { return this._processors.preProcessor.call(null, sn); } return sn; }; SnapshotProcessor.prototype.postProcessSnapshot = function (sn) { if (this._processors.postProcessor) { return this._processors.postProcessor.call(null, sn); } return sn; }; SnapshotProcessor.prototype._fixNode = function (node) { var _this = this; // the node has to use these methods rather than the original type ones proxyNodeTypeMethods(node.type, this, "isAssignableFrom", "create"); var oldGetSnapshot = node.getSnapshot; node.getSnapshot = function () { return _this.postProcessSnapshot(oldGetSnapshot.call(node)); }; }; SnapshotProcessor.prototype.instantiate = function (parent, subpath, environment, initialValue) { var processedInitialValue = isStateTreeNode(initialValue) ? initialValue : this.preProcessSnapshot(initialValue); var node = this._subtype.instantiate(parent, subpath, environment, processedInitialValue); this._fixNode(node); return node; }; SnapshotProcessor.prototype.reconcile = function (current, newValue, parent, subpath) { var node = this._subtype.reconcile(current, isStateTreeNode(newValue) ? newValue : this.preProcessSnapshot(newValue), parent, subpath); if (node !== current) { this._fixNode(node); } return node; }; SnapshotProcessor.prototype.getSnapshot = function (node, applyPostProcess) { if (applyPostProcess === void 0) { applyPostProcess = true; } var sn = this._subtype.getSnapshot(node); return applyPostProcess ? this.postProcessSnapshot(sn) : sn; }; SnapshotProcessor.prototype.isValidSnapshot = function (value, context) { var processedSn = this.preProcessSnapshot(value); return this._subtype.validate(processedSn, context); }; SnapshotProcessor.prototype.getSubTypes = function () { return this._subtype; }; SnapshotProcessor.prototype.is = function (thing) { return (this._subtype.validate(isType(thing) ? this._subtype : this.preProcessSnapshot(thing), [ { path: "", type: this._subtype } ]).length === 0); }; return SnapshotProcessor; }(BaseType)); function proxyNodeTypeMethods(nodeType, snapshotProcessorType) { var e_1, _a; var methods = []; for (var _i = 2; _i < arguments.length; _i++) { methods[_i - 2] = arguments[_i]; } try { for (var methods_1 = __values(methods), methods_1_1 = methods_1.next(); !methods_1_1.done; methods_1_1 = methods_1.next()) { var method = methods_1_1.value; nodeType[method] = snapshotProcessorType[method].bind(snapshotProcessorType); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (methods_1_1 && !methods_1_1.done && (_a = methods_1.return)) _a.call(methods_1); } finally { if (e_1) throw e_1.error; } } } /** * `types.snapshotProcessor` - Runs a pre/post snapshot processor before/after serializing a given type. * * Example: * ```ts * const Todo1 = types.model({ text: types.string }) * // in the backend the text type must be null when empty * interface BackendTodo { * text: string | null * } * const Todo2 = types.snapshotProcessor(Todo1, { * // from snapshot to instance * preProcessor(sn: BackendTodo) { * return { * text: sn.text || ""; * } * }, * // from instance to snapshot * postProcessor(sn): BackendTodo { * return { * text: !sn.text ? null : sn.text * } * } * }) * ``` * * @param type Type to run the processors over. * @param processors Processors to run. * @param name Type name, or undefined to inherit the inner type one. * @returns */ function snapshotProcessor(type, processors, name) { assertIsType(type, 1); if (devMode()) { if (processors.postProcessor && typeof processors.postProcessor !== "function") { // istanbul ignore next throw fail("postSnapshotProcessor must be a function"); } if (processors.preProcessor && typeof processors.preProcessor !== "function") { // istanbul ignore next throw fail("preSnapshotProcessor must be a function"); } } return new SnapshotProcessor(type, processors, name); } var needsIdentifierError = "Map.put can only be used to store complex values that have an identifier type attribute"; function tryCollectModelTypes(type, modelTypes) { var e_1, _a; var subtypes = type.getSubTypes(); if (subtypes === cannotDetermineSubtype) { return false; } if (subtypes) { var subtypesArray = asArray(subtypes); try { for (var subtypesArray_1 = __values(subtypesArray), subtypesArray_1_1 = subtypesArray_1.next(); !subtypesArray_1_1.done; subtypesArray_1_1 = subtypesArray_1.next()) { var subtype = subtypesArray_1_1.value; if (!tryCollectModelTypes(subtype, modelTypes)) return false; } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (subtypesArray_1_1 && !subtypesArray_1_1.done && (_a = subtypesArray_1.return)) _a.call(subtypesArray_1); } finally { if (e_1) throw e_1.error; } } } if (type instanceof ModelType) { modelTypes.push(type); } return true; } /** * @internal * @hidden */ var MapIdentifierMode; (function (MapIdentifierMode) { MapIdentifierMode[MapIdentifierMode["UNKNOWN"] = 0] = "UNKNOWN"; MapIdentifierMode[MapIdentifierMode["YES"] = 1] = "YES"; MapIdentifierMode[MapIdentifierMode["NO"] = 2] = "NO"; })(MapIdentifierMode || (MapIdentifierMode = {})); var MSTMap = /** @class */ (function (_super) { __extends(MSTMap, _super); function MSTMap(initialData) { return _super.call(this, initialData, observable.ref.enhancer) || this; } MSTMap.prototype.get = function (key) { // maybe this is over-enthousiastic? normalize numeric keys to strings return _super.prototype.get.call(this, "" + key); }; MSTMap.prototype.has = function (key) { return _super.prototype.has.call(this, "" + key); }; MSTMap.prototype.delete = function (key) { return _super.prototype.delete.call(this, "" + key); }; MSTMap.prototype.set = function (key, value) { return _super.prototype.set.call(this, "" + key, value); }; MSTMap.prototype.put = function (value) { if (!value) throw fail$1("Map.put cannot be used to set empty values"); if (isStateTreeNode(value)) { var node = getStateTreeNode(value); if (devMode()) { if (!node.identifierAttribute) { throw fail$1(needsIdentifierError); } } if (node.identifier === null) { throw fail$1(needsIdentifierError); } this.set(node.identifier, value); return value; } else if (!isMutable(value)) { throw fail$1("Map.put can only be used to store complex values"); } else { var mapNode = getStateTreeNode(this); var mapType = mapNode.type; if (mapType.identifierMode !== MapIdentifierMode.YES) { throw fail$1(needsIdentifierError); } var idAttr = mapType.mapIdentifierAttribute; var id = value[idAttr]; if (!isValidIdentifier(id)) { // try again but this time after creating a node for the value // since it might be an optional identifier var newNode = this.put(mapType.getChildType().create(value, mapNode.environment)); return this.put(getSnapshot(newNode)); } var key = normalizeIdentifier(id); this.set(key, value); return this.get(key); } }; return MSTMap; }(ObservableMap)); /** * @internal * @hidden */ var MapType = /** @class */ (function (_super) { __extends(MapType, _super); function MapType(name, _subType, hookInitializers) { if (hookInitializers === void 0) { hookInitializers = []; } var _this = _super.call(this, name) || this; _this._subType = _subType; _this.identifierMode = MapIdentifierMode.UNKNOWN; _this.mapIdentifierAttribute = undefined; _this.flags = TypeFlags.Map; _this.hookInitializers = []; _this._determineIdentifierMode(); _this.hookInitializers = hookInitializers; return _this; } MapType.prototype.hooks = function (hooks) { var hookInitializers = this.hookInitializers.length > 0 ? this.hookInitializers.concat(hooks) : [hooks]; return new MapType(this.name, this._subType, hookInitializers); }; MapType.prototype.instantiate = function (parent, subpath, environment, initialValue) { this._determineIdentifierMode(); return createObjectNode(this, parent, subpath, environment, initialValue); }; MapType.prototype._determineIdentifierMode = function () { if (this.identifierMode !== MapIdentifierMode.UNKNOWN) { return; } var modelTypes = []; if (tryCollectModelTypes(this._subType, modelTypes)) { var identifierAttribute_1 = undefined; modelTypes.forEach(function (type) { if (type.identifierAttribute) { if (identifierAttribute_1 && identifierAttribute_1 !== type.identifierAttribute) { throw fail$1("The objects in a map should all have the same identifier attribute, expected '" + identifierAttribute_1 + "', but child of type '" + type.name + "' declared attribute '" + type.identifierAttribute + "' as identifier"); } identifierAttribute_1 = type.identifierAttribute; } }); if (identifierAttribute_1) { this.identifierMode = MapIdentifierMode.YES; this.mapIdentifierAttribute = identifierAttribute_1; } else { this.identifierMode = MapIdentifierMode.NO; } } }; MapType.prototype.initializeChildNodes = function (objNode, initialSnapshot) { if (initialSnapshot === void 0) { initialSnapshot = {}; } var subType = objNode.type._subType; var result = {}; Object.keys(initialSnapshot).forEach(function (name) { result[name] = subType.instantiate(objNode, name, undefined, initialSnapshot[name]); }); return result; }; MapType.prototype.createNewInstance = function (childNodes) { return new MSTMap(childNodes); }; MapType.prototype.finalizeNewInstance = function (node, instance) { _interceptReads(instance, node.unbox); var type = node.type; type.hookInitializers.forEach(function (initializer) { var hooks = initializer(instance); Object.keys(hooks).forEach(function (name) { var hook = hooks[name]; var actionInvoker = createActionInvoker(instance, name, hook); (!devMode() ? addHiddenFinalProp : addHiddenWritableProp)(instance, name, actionInvoker); }); }); intercept(instance, this.willChange); observe(instance, this.didChange); }; MapType.prototype.describe = function () { return "Map"; }; MapType.prototype.getChildren = function (node) { // return (node.storedValue as ObservableMap).values() return values(node.storedValue); }; MapType.prototype.getChildNode = function (node, key) { var childNode = node.storedValue.get("" + key); if (!childNode) throw fail$1("Not a child " + key); return childNode; }; MapType.prototype.willChange = function (change) { var node = getStateTreeNode(change.object); var key = change.name; node.assertWritable({ subpath: key }); var mapType = node.type; var subType = mapType._subType; switch (change.type) { case "update": { var newValue = change.newValue; var oldValue = change.object.get(key); if (newValue === oldValue) return null; typecheckInternal(subType, newValue); change.newValue = subType.reconcile(node.getChildNode(key), change.newValue, node, key); mapType.processIdentifier(key, change.newValue); } break; case "add": { typecheckInternal(subType, change.newValue); change.newValue = subType.instantiate(node, key, undefined, change.newValue); mapType.processIdentifier(key, change.newValue); } break; } return change; }; MapType.prototype.processIdentifier = function (expected, node) { if (this.identifierMode === MapIdentifierMode.YES && node instanceof ObjectNode) { var identifier = node.identifier; if (identifier !== expected) throw fail$1("A map of objects containing an identifier should always store the object under their own identifier. Trying to store key '" + identifier + "', but expected: '" + expected + "'"); } }; MapType.prototype.getSnapshot = function (node) { var res = {}; node.getChildren().forEach(function (childNode) { res[childNode.subpath] = childNode.snapshot; }); return res; }; MapType.prototype.processInitialSnapshot = function (childNodes) { var processed = {}; Object.keys(childNodes).forEach(function (key) { processed[key] = childNodes[key].getSnapshot(); }); return processed; }; MapType.prototype.didChange = function (change) { var node = getStateTreeNode(change.object); switch (change.type) { case "update": return void node.emitPatch({ op: "replace", path: escapeJsonPath(change.name), value: change.newValue.snapshot, oldValue: change.oldValue ? change.oldValue.snapshot : undefined }, node); case "add": return void node.emitPatch({ op: "add", path: escapeJsonPath(change.name), value: change.newValue.snapshot, oldValue: undefined }, node); case "delete": // a node got deleted, get the old snapshot and make the node die var oldSnapshot = change.oldValue.snapshot; change.oldValue.die(); // emit the patch return void node.emitPatch({ op: "remove", path: escapeJsonPath(change.name), oldValue: oldSnapshot }, node); } }; MapType.prototype.applyPatchLocally = function (node, subpath, patch) { var target = node.storedValue; switch (patch.op) { case "add": case "replace": target.set(subpath, patch.value); break; case "remove": target.delete(subpath); break; } }; MapType.prototype.applySnapshot = function (node, snapshot) { typecheckInternal(this, snapshot); var target = node.storedValue; var currentKeys = {}; Array.from(target.keys()).forEach(function (key) { currentKeys[key] = false; }); if (snapshot) { // Don't use target.replace, as it will throw away all existing items first for (var key in snapshot) { target.set(key, snapshot[key]); currentKeys["" + key] = true; } } Object.keys(currentKeys).forEach(function (key) { if (currentKeys[key] === false) target.delete(key); }); }; MapType.prototype.getChildType = function () { return this._subType; }; MapType.prototype.isValidSnapshot = function (value, context) { var _this = this; if (!isPlainObject(value)) { return typeCheckFailure(context, value, "Value is not a plain object"); } return flattenTypeErrors(Object.keys(value).map(function (path) { return _this._subType.validate(value[path], getContextForPath(context, path, _this._subType)); })); }; MapType.prototype.getDefaultSnapshot = function () { return EMPTY_OBJECT; }; MapType.prototype.removeChild = function (node, subpath) { node.storedValue.delete(subpath); }; __decorate([ action ], MapType.prototype, "applySnapshot", null); return MapType; }(ComplexType)); /** * `types.map` - Creates a key based collection type who's children are all of a uniform declared type. * If the type stored in a map has an identifier, it is mandatory to store the child under that identifier in the map. * * This type will always produce [observable maps](https://mobx.js.org/refguide/map.html) * * Example: * ```ts * const Todo = types.model({ * id: types.identifier, * task: types.string * }) * * const TodoStore = types.model({ * todos: types.map(Todo) * }) * * const s = TodoStore.create({ todos: {} }) * unprotect(s) * s.todos.set(17, { task: "Grab coffee", id: 17 }) * s.todos.put({ task: "Grab cookie", id: 18 }) // put will infer key from the identifier * console.log(s.todos.get(17).task) // prints: "Grab coffee" * ``` * * @param subtype * @returns */ function map(subtype) { return new MapType("map", subtype); } /** * Returns if a given value represents a map type. * * @param type * @returns `true` if it is a map type. */ function isMapType(type) { return isType(type) && (type.flags & TypeFlags.Map) > 0; } /** * @internal * @hidden */ var ArrayType = /** @class */ (function (_super) { __extends(ArrayType, _super); function ArrayType(name, _subType, hookInitializers) { if (hookInitializers === void 0) { hookInitializers = []; } var _this = _super.call(this, name) || this; _this._subType = _subType; _this.flags = TypeFlags.Array; _this.hookInitializers = []; _this.hookInitializers = hookInitializers; return _this; } ArrayType.prototype.hooks = function (hooks) { var hookInitializers = this.hookInitializers.length > 0 ? this.hookInitializers.concat(hooks) : [hooks]; return new ArrayType(this.name, this._subType, hookInitializers); }; ArrayType.prototype.instantiate = function (parent, subpath, environment, initialValue) { return createObjectNode(this, parent, subpath, environment, initialValue); }; ArrayType.prototype.initializeChildNodes = function (objNode, snapshot) { if (snapshot === void 0) { snapshot = []; } var subType = objNode.type._subType; var result = {}; snapshot.forEach(function (item, index) { var subpath = "" + index; result[subpath] = subType.instantiate(objNode, subpath, undefined, item); }); return result; }; ArrayType.prototype.createNewInstance = function (childNodes) { return observable.array(convertChildNodesToArray(childNodes), mobxShallow); }; ArrayType.prototype.finalizeNewInstance = function (node, instance) { _getAdministration(instance).dehancer = node.unbox; var type = node.type; type.hookInitializers.forEach(function (initializer) { var hooks = initializer(instance); Object.keys(hooks).forEach(function (name) { var hook = hooks[name]; var actionInvoker = createActionInvoker(instance, name, hook); (!devMode() ? addHiddenFinalProp : addHiddenWritableProp)(instance, name, actionInvoker); }); }); intercept(instance, this.willChange); observe(instance, this.didChange); }; ArrayType.prototype.describe = function () { return this._subType.describe() + "[]"; }; ArrayType.prototype.getChildren = function (node) { return node.storedValue.slice(); }; ArrayType.prototype.getChildNode = function (node, key) { var index = Number(key); if (index < node.storedValue.length) return node.storedValue[index]; throw fail$1("Not a child: " + key); }; ArrayType.prototype.willChange = function (change) { var node = getStateTreeNode(change.object); node.assertWritable({ subpath: "" + change.index }); var subType = node.type._subType; var childNodes = node.getChildren(); switch (change.type) { case "update": { if (change.newValue === change.object[change.index]) return null; var updatedNodes = reconcileArrayChildren(node, subType, [childNodes[change.index]], [change.newValue], [change.index]); if (!updatedNodes) { return null; } change.newValue = updatedNodes[0]; } break; case "splice": { var index_1 = change.index, removedCount = change.removedCount, added = change.added; var addedNodes = reconcileArrayChildren(node, subType, childNodes.slice(index_1, index_1 + removedCount), added, added.map(function (_, i) { return index_1 + i; })); if (!addedNodes) { return null; } change.added = addedNodes; // update paths of remaining items for (var i = index_1 + removedCount; i < childNodes.length; i++) { childNodes[i].setParent(node, "" + (i + added.length - removedCount)); } } break; } return change; }; ArrayType.prototype.getSnapshot = function (node) { return node.getChildren().map(function (childNode) { return childNode.snapshot; }); }; ArrayType.prototype.processInitialSnapshot = function (childNodes) { var processed = []; Object.keys(childNodes).forEach(function (key) { processed.push(childNodes[key].getSnapshot()); }); return processed; }; ArrayType.prototype.didChange = function (change) { var node = getStateTreeNode(change.object); switch (change.type) { case "update": return void node.emitPatch({ op: "replace", path: "" + change.index, value: change.newValue.snapshot, oldValue: change.oldValue ? change.oldValue.snapshot : undefined }, node); case "splice": for (var i = change.removedCount - 1; i >= 0; i--) node.emitPatch({ op: "remove", path: "" + (change.index + i), oldValue: change.removed[i].snapshot }, node); for (var i = 0; i < change.addedCount; i++) node.emitPatch({ op: "add", path: "" + (change.index + i), value: node.getChildNode("" + (change.index + i)).snapshot, oldValue: undefined }, node); return; } }; ArrayType.prototype.applyPatchLocally = function (node, subpath, patch) { var target = node.storedValue; var index = subpath === "-" ? target.length : Number(subpath); switch (patch.op) { case "replace": target[index] = patch.value; break; case "add": target.splice(index, 0, patch.value); break; case "remove": target.splice(index, 1); break; } }; ArrayType.prototype.applySnapshot = function (node, snapshot) { typecheckInternal(this, snapshot); var target = node.storedValue; target.replace(snapshot); }; ArrayType.prototype.getChildType = function () { return this._subType; }; ArrayType.prototype.isValidSnapshot = function (value, context) { var _this = this; if (!isArray(value)) { return typeCheckFailure(context, value, "Value is not an array"); } return flattenTypeErrors(value.map(function (item, index) { return _this._subType.validate(item, getContextForPath(context, "" + index, _this._subType)); })); }; ArrayType.prototype.getDefaultSnapshot = function () { return EMPTY_ARRAY; }; ArrayType.prototype.removeChild = function (node, subpath) { node.storedValue.splice(Number(subpath), 1); }; __decorate([ action ], ArrayType.prototype, "applySnapshot", null); return ArrayType; }(ComplexType)); /** * `types.array` - Creates an index based collection type who's children are all of a uniform declared type. * * This type will always produce [observable arrays](https://mobx.js.org/refguide/array.html) * * Example: * ```ts * const Todo = types.model({ * task: types.string * }) * * const TodoStore = types.model({ * todos: types.array(Todo) * }) * * const s = TodoStore.create({ todos: [] }) * unprotect(s) // needed to allow modifying outside of an action * s.todos.push({ task: "Grab coffee" }) * console.log(s.todos[0]) // prints: "Grab coffee" * ``` * * @param subtype * @returns */ function array(subtype) { assertIsType(subtype, 1); return new ArrayType(subtype.name + "[]", subtype); } function reconcileArrayChildren(parent, childType, oldNodes, newValues, newPaths) { var nothingChanged = true; for (var i = 0;; i++) { var hasNewNode = i <= newValues.length - 1; var oldNode = oldNodes[i]; var newValue = hasNewNode ? newValues[i] : undefined; var newPath = "" + newPaths[i]; // for some reason, instead of newValue we got a node, fallback to the storedValue // TODO: https://github.com/mobxjs/mobx-state-tree/issues/340#issuecomment-325581681 if (isNode(newValue)) newValue = newValue.storedValue; if (!oldNode && !hasNewNode) { // both are empty, end break; } else if (!hasNewNode) { // new one does not exists nothingChanged = false; oldNodes.splice(i, 1); if (oldNode instanceof ObjectNode) { // since it is going to be returned by pop/splice/shift better create it before killing it // so it doesn't end up in an undead state oldNode.createObservableInstanceIfNeeded(); } oldNode.die(); i--; } else if (!oldNode) { // there is no old node, create it // check if already belongs to the same parent. if so, avoid pushing item in. only swapping can occur. if (isStateTreeNode(newValue) && getStateTreeNode(newValue).parent === parent) { // this node is owned by this parent, but not in the reconcilable set, so it must be double throw fail$1("Cannot add an object to a state tree if it is already part of the same or another state tree. Tried to assign an object to '" + parent.path + "/" + newPath + "', but it lives already at '" + getStateTreeNode(newValue).path + "'"); } nothingChanged = false; var newNode = valueAsNode(childType, parent, newPath, newValue); oldNodes.splice(i, 0, newNode); } else if (areSame(oldNode, newValue)) { // both are the same, reconcile oldNodes[i] = valueAsNode(childType, parent, newPath, newValue, oldNode); } else { // nothing to do, try to reorder var oldMatch = undefined; // find a possible candidate to reuse for (var j = i; j < oldNodes.length; j++) { if (areSame(oldNodes[j], newValue)) { oldMatch = oldNodes.splice(j, 1)[0]; break; } } nothingChanged = false; var newNode = valueAsNode(childType, parent, newPath, newValue, oldMatch); oldNodes.splice(i, 0, newNode); } } return nothingChanged ? null : oldNodes; } /** * Convert a value to a node at given parent and subpath. Attempts to reuse old node if possible and given. */ function valueAsNode(childType, parent, subpath, newValue, oldNode) { // ensure the value is valid-ish typecheckInternal(childType, newValue); function getNewNode() { // the new value has a MST node if (isStateTreeNode(newValue)) { var childNode = getStateTreeNode(newValue); childNode.assertAlive(EMPTY_OBJECT); // the node lives here if (childNode.parent !== null && childNode.parent === parent) { childNode.setParent(parent, subpath); return childNode; } } // there is old node and new one is a value/snapshot if (oldNode) { return childType.reconcile(oldNode, newValue, parent, subpath); } // nothing to do, create from scratch return childType.instantiate(parent, subpath, undefined, newValue); } var newNode = getNewNode(); if (oldNode && oldNode !== newNode) { if (oldNode instanceof ObjectNode) { // since it is going to be returned by pop/splice/shift better create it before killing it // so it doesn't end up in an undead state oldNode.createObservableInstanceIfNeeded(); } oldNode.die(); } return newNode; } /** * Check if a node holds a value. */ function areSame(oldNode, newValue) { // never consider dead old nodes for reconciliation if (!oldNode.isAlive) { return false; } // the new value has the same node if (isStateTreeNode(newValue)) { var newNode = getStateTreeNode(newValue); return newNode.isAlive && newNode === oldNode; } // the provided value is the snapshot of the old node if (oldNode.snapshot === newValue) { return true; } // new value is a snapshot with the correct identifier return (oldNode instanceof ObjectNode && oldNode.identifier !== null && oldNode.identifierAttribute && isPlainObject(newValue) && oldNode.identifier === normalizeIdentifier(newValue[oldNode.identifierAttribute]) && oldNode.type.is(newValue)); } /** * Returns if a given value represents an array type. * * @param type * @returns `true` if the type is an array type. */ function isArrayType(type) { return isType(type) && (type.flags & TypeFlags.Array) > 0; } var PRE_PROCESS_SNAPSHOT = "preProcessSnapshot"; var POST_PROCESS_SNAPSHOT = "postProcessSnapshot"; function objectTypeToString() { return getStateTreeNode(this).toString(); } var defaultObjectOptions = { name: "AnonymousModel", properties: {}, initializers: EMPTY_ARRAY }; function toPropertiesObject(declaredProps) { // loop through properties and ensures that all items are types return Object.keys(declaredProps).reduce(function (props, key) { var _a, _b, _c; // warn if user intended a HOOK if (key in Hook) throw fail$1("Hook '" + key + "' was defined as property. Hooks should be defined as part of the actions"); // the user intended to use a view var descriptor = Object.getOwnPropertyDescriptor(props, key); if ("get" in descriptor) { throw fail$1("Getters are not supported as properties. Please use views instead"); } // undefined and null are not valid var value = descriptor.value; if (value === null || value === undefined) { throw fail$1("The default value of an attribute cannot be null or undefined as the type cannot be inferred. Did you mean `types.maybe(someType)`?"); // its a primitive, convert to its type } else if (isPrimitive(value)) { return Object.assign({}, props, (_a = {}, _a[key] = optional(getPrimitiveFactoryFromValue(value), value), _a)); // map defaults to empty object automatically for models } else if (value instanceof MapType) { return Object.assign({}, props, (_b = {}, _b[key] = optional(value, {}), _b)); } else if (value instanceof ArrayType) { return Object.assign({}, props, (_c = {}, _c[key] = optional(value, []), _c)); // its already a type } else if (isType(value)) { return props; // its a function, maybe the user wanted a view? } else if (devMode() && typeof value === "function") { throw fail$1("Invalid type definition for property '" + key + "', it looks like you passed a function. Did you forget to invoke it, or did you intend to declare a view / action?"); // no other complex values } else if (devMode() && typeof value === "object") { throw fail$1("Invalid type definition for property '" + key + "', it looks like you passed an object. Try passing another model type or a types.frozen."); // WTF did you pass in mate? } else { throw fail$1("Invalid type definition for property '" + key + "', cannot infer a type from a value like '" + value + "' (" + typeof value + ")"); } }, declaredProps); } /** * @internal * @hidden */ var ModelType = /** @class */ (function (_super) { __extends(ModelType, _super); function ModelType(opts) { var _this = _super.call(this, opts.name || defaultObjectOptions.name) || this; _this.flags = TypeFlags.Object; _this.named = function (name) { return _this.cloneAndEnhance({ name: name }); }; _this.props = function (properties) { return _this.cloneAndEnhance({ properties: properties }); }; _this.preProcessSnapshot = function (preProcessor) { var currentPreprocessor = _this.preProcessor; if (!currentPreprocessor) return _this.cloneAndEnhance({ preProcessor: preProcessor }); else return _this.cloneAndEnhance({ preProcessor: function (snapshot) { return currentPreprocessor(preProcessor(snapshot)); } }); }; _this.postProcessSnapshot = function (postProcessor) { var currentPostprocessor = _this.postProcessor; if (!currentPostprocessor) return _this.cloneAndEnhance({ postProcessor: postProcessor }); else return _this.cloneAndEnhance({ postProcessor: function (snapshot) { return postProcessor(currentPostprocessor(snapshot)); } }); }; Object.assign(_this, defaultObjectOptions, opts); // ensures that any default value gets converted to its related type _this.properties = toPropertiesObject(_this.properties); freeze(_this.properties); // make sure nobody messes with it _this.propertyNames = Object.keys(_this.properties); _this.identifierAttribute = _this._getIdentifierAttribute(); return _this; } ModelType.prototype._getIdentifierAttribute = function () { var identifierAttribute = undefined; this.forAllProps(function (propName, propType) { if (propType.flags & TypeFlags.Identifier) { if (identifierAttribute) throw fail$1("Cannot define property '" + propName + "' as object identifier, property '" + identifierAttribute + "' is already defined as identifier property"); identifierAttribute = propName; } }); return identifierAttribute; }; ModelType.prototype.cloneAndEnhance = function (opts) { return new ModelType({ name: opts.name || this.name, properties: Object.assign({}, this.properties, opts.properties), initializers: this.initializers.concat(opts.initializers || []), preProcessor: opts.preProcessor || this.preProcessor, postProcessor: opts.postProcessor || this.postProcessor }); }; ModelType.prototype.actions = function (fn) { var _this = this; var actionInitializer = function (self) { _this.instantiateActions(self, fn(self)); return self; }; return this.cloneAndEnhance({ initializers: [actionInitializer] }); }; ModelType.prototype.instantiateActions = function (self, actions) { // check if return is correct if (!isPlainObject(actions)) throw fail$1("actions initializer should return a plain object containing actions"); // bind actions to the object created Object.keys(actions).forEach(function (name) { // warn if preprocessor was given if (name === PRE_PROCESS_SNAPSHOT) throw fail$1("Cannot define action '" + PRE_PROCESS_SNAPSHOT + "', it should be defined using 'type.preProcessSnapshot(fn)' instead"); // warn if postprocessor was given if (name === POST_PROCESS_SNAPSHOT) throw fail$1("Cannot define action '" + POST_PROCESS_SNAPSHOT + "', it should be defined using 'type.postProcessSnapshot(fn)' instead"); var action2 = actions[name]; // apply hook composition var baseAction = self[name]; if (name in Hook && baseAction) { var specializedAction_1 = action2; action2 = function () { baseAction.apply(null, arguments); specializedAction_1.apply(null, arguments); }; } // the goal of this is to make sure actions using "this" can call themselves, // while still allowing the middlewares to register them var middlewares = action2.$mst_middleware; // make sure middlewares are not lost var boundAction = action2.bind(actions); boundAction.$mst_middleware = middlewares; var actionInvoker = createActionInvoker(self, name, boundAction); actions[name] = actionInvoker; (!devMode() ? addHiddenFinalProp : addHiddenWritableProp)(self, name, actionInvoker); }); }; ModelType.prototype.volatile = function (fn) { var _this = this; var stateInitializer = function (self) { _this.instantiateVolatileState(self, fn(self)); return self; }; return this.cloneAndEnhance({ initializers: [stateInitializer] }); }; ModelType.prototype.instantiateVolatileState = function (self, state) { // check views return if (!isPlainObject(state)) throw fail$1("volatile state initializer should return a plain object containing state"); set(self, state); }; ModelType.prototype.extend = function (fn) { var _this = this; var initializer = function (self) { var _a = fn(self), actions = _a.actions, views = _a.views, state = _a.state, rest = __rest(_a, ["actions", "views", "state"]); for (var key in rest) throw fail$1("The `extend` function should return an object with a subset of the fields 'actions', 'views' and 'state'. Found invalid key '" + key + "'"); if (state) _this.instantiateVolatileState(self, state); if (views) _this.instantiateViews(self, views); if (actions) _this.instantiateActions(self, actions); return self; }; return this.cloneAndEnhance({ initializers: [initializer] }); }; ModelType.prototype.views = function (fn) { var _this = this; var viewInitializer = function (self) { _this.instantiateViews(self, fn(self)); return self; }; return this.cloneAndEnhance({ initializers: [viewInitializer] }); }; ModelType.prototype.instantiateViews = function (self, views) { // check views return if (!isPlainObject(views)) throw fail$1("views initializer should return a plain object containing views"); Object.keys(views).forEach(function (key) { // is this a computed property? var descriptor = Object.getOwnPropertyDescriptor(views, key); if ("get" in descriptor) { if (isComputedProp(self, key)) { var computedValue = _getAdministration(self, key); // TODO: mobx currently does not allow redefining computes yet, pending #1121 // FIXME: this binds to the internals of mobx! computedValue.derivation = descriptor.get; computedValue.scope = self; if (descriptor.set) computedValue.setter = action(computedValue.name + "-setter", descriptor.set); } else { computed(self, key, descriptor, true); } } else if (typeof descriptor.value === "function") { (!devMode() ? addHiddenFinalProp : addHiddenWritableProp)(self, key, descriptor.value); } else { throw fail$1("A view member should either be a function or getter based property"); } }); }; ModelType.prototype.instantiate = function (parent, subpath, environment, initialValue) { var value = isStateTreeNode(initialValue) ? initialValue : this.applySnapshotPreProcessor(initialValue); return createObjectNode(this, parent, subpath, environment, value); // Optimization: record all prop- view- and action names after first construction, and generate an optimal base class // that pre-reserves all these fields for fast object-member lookups }; ModelType.prototype.initializeChildNodes = function (objNode, initialSnapshot) { if (initialSnapshot === void 0) { initialSnapshot = {}; } var type = objNode.type; var result = {}; type.forAllProps(function (name, childType) { result[name] = childType.instantiate(objNode, name, undefined, initialSnapshot[name]); }); return result; }; ModelType.prototype.createNewInstance = function (childNodes) { return observable.object(childNodes, EMPTY_OBJECT, mobxShallow); }; ModelType.prototype.finalizeNewInstance = function (node, instance) { addHiddenFinalProp(instance, "toString", objectTypeToString); this.forAllProps(function (name) { _interceptReads(instance, name, node.unbox); }); this.initializers.reduce(function (self, fn) { return fn(self); }, instance); intercept(instance, this.willChange); observe(instance, this.didChange); }; ModelType.prototype.willChange = function (chg) { // TODO: mobx typings don't seem to take into account that newValue can be set even when removing a prop var change = chg; var node = getStateTreeNode(change.object); var subpath = change.name; node.assertWritable({ subpath: subpath }); var childType = node.type.properties[subpath]; // only properties are typed, state are stored as-is references if (childType) { typecheckInternal(childType, change.newValue); change.newValue = childType.reconcile(node.getChildNode(subpath), change.newValue, node, subpath); } return change; }; ModelType.prototype.didChange = function (chg) { // TODO: mobx typings don't seem to take into account that newValue can be set even when removing a prop var change = chg; var childNode = getStateTreeNode(change.object); var childType = childNode.type.properties[change.name]; if (!childType) { // don't emit patches for volatile state return; } var oldChildValue = change.oldValue ? change.oldValue.snapshot : undefined; childNode.emitPatch({ op: "replace", path: escapeJsonPath(change.name), value: change.newValue.snapshot, oldValue: oldChildValue }, childNode); }; ModelType.prototype.getChildren = function (node) { var _this = this; var res = []; this.forAllProps(function (name) { res.push(_this.getChildNode(node, name)); }); return res; }; ModelType.prototype.getChildNode = function (node, key) { if (!(key in this.properties)) throw fail$1("Not a value property: " + key); var childNode = _getAdministration(node.storedValue, key).value; // TODO: blegh! if (!childNode) throw fail$1("Node not available for property " + key); return childNode; }; ModelType.prototype.getSnapshot = function (node, applyPostProcess) { var _this = this; if (applyPostProcess === void 0) { applyPostProcess = true; } var res = {}; this.forAllProps(function (name, type) { getAtom(node.storedValue, name).reportObserved(); res[name] = _this.getChildNode(node, name).snapshot; }); if (applyPostProcess) { return this.applySnapshotPostProcessor(res); } return res; }; ModelType.prototype.processInitialSnapshot = function (childNodes) { var processed = {}; Object.keys(childNodes).forEach(function (key) { processed[key] = childNodes[key].getSnapshot(); }); return this.applySnapshotPostProcessor(processed); }; ModelType.prototype.applyPatchLocally = function (node, subpath, patch) { if (!(patch.op === "replace" || patch.op === "add")) { throw fail$1("object does not support operation " + patch.op); } node.storedValue[subpath] = patch.value; }; ModelType.prototype.applySnapshot = function (node, snapshot) { var preProcessedSnapshot = this.applySnapshotPreProcessor(snapshot); typecheckInternal(this, preProcessedSnapshot); this.forAllProps(function (name) { node.storedValue[name] = preProcessedSnapshot[name]; }); }; ModelType.prototype.applySnapshotPreProcessor = function (snapshot) { var processor = this.preProcessor; return processor ? processor.call(null, snapshot) : snapshot; }; ModelType.prototype.applySnapshotPostProcessor = function (snapshot) { var postProcessor = this.postProcessor; if (postProcessor) return postProcessor.call(null, snapshot); return snapshot; }; ModelType.prototype.getChildType = function (propertyName) { assertIsString(propertyName, 1); return this.properties[propertyName]; }; ModelType.prototype.isValidSnapshot = function (value, context) { var _this = this; var snapshot = this.applySnapshotPreProcessor(value); if (!isPlainObject(snapshot)) { return typeCheckFailure(context, snapshot, "Value is not a plain object"); } return flattenTypeErrors(this.propertyNames.map(function (key) { return _this.properties[key].validate(snapshot[key], getContextForPath(context, key, _this.properties[key])); })); }; ModelType.prototype.forAllProps = function (fn) { var _this = this; this.propertyNames.forEach(function (key) { return fn(key, _this.properties[key]); }); }; ModelType.prototype.describe = function () { var _this = this; // optimization: cache return ("{ " + this.propertyNames.map(function (key) { return key + ": " + _this.properties[key].describe(); }).join("; ") + " }"); }; ModelType.prototype.getDefaultSnapshot = function () { return EMPTY_OBJECT; }; ModelType.prototype.removeChild = function (node, subpath) { node.storedValue[subpath] = undefined; }; __decorate([ action ], ModelType.prototype, "applySnapshot", null); return ModelType; }(ComplexType)); /** * `types.model` - Creates a new model type by providing a name, properties, volatile state and actions. * * See the [model type](https://github.com/mobxjs/mobx-state-tree#creating-models) description or the [getting started](https://github.com/mobxjs/mobx-state-tree/blob/master/docs/getting-started.md#getting-started-1) tutorial. */ function model() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var name = typeof args[0] === "string" ? args.shift() : "AnonymousModel"; var properties = args.shift() || {}; return new ModelType({ name: name, properties: properties }); } /** * `types.compose` - Composes a new model from one or more existing model types. * This method can be invoked in two forms: * Given 2 or more model types, the types are composed into a new Type. * Given first parameter as a string and 2 or more model types, * the types are composed into a new Type with the given name */ function compose() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } // TODO: just join the base type names if no name is provided var hasTypename = typeof args[0] === "string"; var typeName = hasTypename ? args[0] : "AnonymousModel"; if (hasTypename) { args.shift(); } // check all parameters if (devMode()) { args.forEach(function (type, i) { assertArg(type, isModelType, "mobx-state-tree model type", hasTypename ? i + 2 : i + 1); }); } return args .reduce(function (prev, cur) { return prev.cloneAndEnhance({ name: prev.name + "_" + cur.name, properties: cur.properties, initializers: cur.initializers, preProcessor: function (snapshot) { return cur.applySnapshotPreProcessor(prev.applySnapshotPreProcessor(snapshot)); }, postProcessor: function (snapshot) { return cur.applySnapshotPostProcessor(prev.applySnapshotPostProcessor(snapshot)); } }); }) .named(typeName); } /** * Returns if a given value represents a model type. * * @param type * @returns */ function isModelType(type) { return isType(type) && (type.flags & TypeFlags.Object) > 0; } // TODO: implement CoreType using types.custom ? /** * @internal * @hidden */ var CoreType = /** @class */ (function (_super) { __extends(CoreType, _super); function CoreType(name, flags, checker, initializer) { if (initializer === void 0) { initializer = identity; } var _this = _super.call(this, name) || this; _this.flags = flags; _this.checker = checker; _this.initializer = initializer; _this.flags = flags; return _this; } CoreType.prototype.describe = function () { return this.name; }; CoreType.prototype.instantiate = function (parent, subpath, environment, initialValue) { return createScalarNode(this, parent, subpath, environment, initialValue); }; CoreType.prototype.createNewInstance = function (snapshot) { return this.initializer(snapshot); }; CoreType.prototype.isValidSnapshot = function (value, context) { if (isPrimitive(value) && this.checker(value)) { return typeCheckSuccess(); } var typeName = this.name === "Date" ? "Date or a unix milliseconds timestamp" : this.name; return typeCheckFailure(context, value, "Value is not a " + typeName); }; return CoreType; }(SimpleType)); /** * `types.string` - Creates a type that can only contain a string value. * This type is used for string values by default * * Example: * ```ts * const Person = types.model({ * firstName: types.string, * lastName: "Doe" * }) * ``` */ // tslint:disable-next-line:variable-name var string = new CoreType("string", TypeFlags.String, function (v) { return typeof v === "string"; }); /** * `types.number` - Creates a type that can only contain a numeric value. * This type is used for numeric values by default * * Example: * ```ts * const Vector = types.model({ * x: types.number, * y: 1.5 * }) * ``` */ // tslint:disable-next-line:variable-name var number = new CoreType("number", TypeFlags.Number, function (v) { return typeof v === "number"; }); /** * `types.integer` - Creates a type that can only contain an integer value. * This type is used for integer values by default * * Example: * ```ts * const Size = types.model({ * width: types.integer, * height: 10 * }) * ``` */ // tslint:disable-next-line:variable-name var integer = new CoreType("integer", TypeFlags.Integer, function (v) { return isInteger(v); }); /** * `types.boolean` - Creates a type that can only contain a boolean value. * This type is used for boolean values by default * * Example: * ```ts * const Thing = types.model({ * isCool: types.boolean, * isAwesome: false * }) * ``` */ // tslint:disable-next-line:variable-name var boolean = new CoreType("boolean", TypeFlags.Boolean, function (v) { return typeof v === "boolean"; }); /** * `types.null` - The type of the value `null` */ var nullType = new CoreType("null", TypeFlags.Null, function (v) { return v === null; }); /** * `types.undefined` - The type of the value `undefined` */ var undefinedType = new CoreType("undefined", TypeFlags.Undefined, function (v) { return v === undefined; }); var _DatePrimitive = new CoreType("Date", TypeFlags.Date, function (v) { return typeof v === "number" || v instanceof Date; }, function (v) { return (v instanceof Date ? v : new Date(v)); }); _DatePrimitive.getSnapshot = function (node) { return node.storedValue.getTime(); }; /** * `types.Date` - Creates a type that can only contain a javascript Date value. * * Example: * ```ts * const LogLine = types.model({ * timestamp: types.Date, * }) * * LogLine.create({ timestamp: new Date() }) * ``` */ var DatePrimitive = _DatePrimitive; /** * @internal * @hidden */ function getPrimitiveFactoryFromValue(value) { switch (typeof value) { case "string": return string; case "number": return number; // In the future, isInteger(value) ? integer : number would be interesting, but would be too breaking for now case "boolean": return boolean; case "object": if (value instanceof Date) return DatePrimitive; } throw fail$1("Cannot determine primitive type from value " + value); } /** * Returns if a given value represents a primitive type. * * @param type * @returns */ function isPrimitiveType(type) { return (isType(type) && (type.flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.Integer | TypeFlags.Boolean | TypeFlags.Date)) > 0); } /** * @internal * @hidden */ var Literal = /** @class */ (function (_super) { __extends(Literal, _super); function Literal(value) { var _this = _super.call(this, JSON.stringify(value)) || this; _this.flags = TypeFlags.Literal; _this.value = value; return _this; } Literal.prototype.instantiate = function (parent, subpath, environment, initialValue) { return createScalarNode(this, parent, subpath, environment, initialValue); }; Literal.prototype.describe = function () { return JSON.stringify(this.value); }; Literal.prototype.isValidSnapshot = function (value, context) { if (isPrimitive(value) && value === this.value) { return typeCheckSuccess(); } return typeCheckFailure(context, value, "Value is not a literal " + JSON.stringify(this.value)); }; return Literal; }(SimpleType)); /** * `types.literal` - The literal type will return a type that will match only the exact given type. * The given value must be a primitive, in order to be serialized to a snapshot correctly. * You can use literal to match exact strings for example the exact male or female string. * * Example: * ```ts * const Person = types.model({ * name: types.string, * gender: types.union(types.literal('male'), types.literal('female')) * }) * ``` * * @param value The value to use in the strict equal check * @returns */ function literal(value) { // check that the given value is a primitive assertArg(value, isPrimitive, "primitive", 1); return new Literal(value); } /** * Returns if a given value represents a literal type. * * @param type * @returns */ function isLiteralType(type) { return isType(type) && (type.flags & TypeFlags.Literal) > 0; } var Refinement = /** @class */ (function (_super) { __extends(Refinement, _super); function Refinement(name, _subtype, _predicate, _message) { var _this = _super.call(this, name) || this; _this._subtype = _subtype; _this._predicate = _predicate; _this._message = _message; return _this; } Object.defineProperty(Refinement.prototype, "flags", { get: function () { return this._subtype.flags | TypeFlags.Refinement; }, enumerable: true, configurable: true }); Refinement.prototype.describe = function () { return this.name; }; Refinement.prototype.instantiate = function (parent, subpath, environment, initialValue) { // create the child type return this._subtype.instantiate(parent, subpath, environment, initialValue); }; Refinement.prototype.isAssignableFrom = function (type) { return this._subtype.isAssignableFrom(type); }; Refinement.prototype.isValidSnapshot = function (value, context) { var subtypeErrors = this._subtype.validate(value, context); if (subtypeErrors.length > 0) return subtypeErrors; var snapshot = isStateTreeNode(value) ? getStateTreeNode(value).snapshot : value; if (!this._predicate(snapshot)) { return typeCheckFailure(context, value, this._message(value)); } return typeCheckSuccess(); }; Refinement.prototype.reconcile = function (current, newValue, parent, subpath) { return this._subtype.reconcile(current, newValue, parent, subpath); }; Refinement.prototype.getSubTypes = function () { return this._subtype; }; return Refinement; }(BaseType)); /** * `types.refinement` - Creates a type that is more specific than the base type, e.g. `types.refinement(types.string, value => value.length > 5)` to create a type of strings that can only be longer then 5. * * @param name * @param type * @param predicate * @returns */ function refinement() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var name = typeof args[0] === "string" ? args.shift() : isType(args[0]) ? args[0].name : null; var type = args[0]; var predicate = args[1]; var message = args[2] ? args[2] : function (v) { return "Value does not respect the refinement predicate"; }; // ensures all parameters are correct assertIsType(type, [1, 2]); assertIsString(name, 1); assertIsFunction(predicate, [2, 3]); assertIsFunction(message, [3, 4]); return new Refinement(name, type, predicate, message); } /** * Returns if a given value is a refinement type. * * @param type * @returns */ function isRefinementType(type) { return (type.flags & TypeFlags.Refinement) > 0; } /** * `types.enumeration` - Can be used to create an string based enumeration. * (note: this methods is just sugar for a union of string literals) * * Example: * ```ts * const TrafficLight = types.model({ * color: types.enumeration("Color", ["Red", "Orange", "Green"]) * }) * ``` * * @param name descriptive name of the enumeration (optional) * @param options possible values this enumeration can have * @returns */ function enumeration(name, options) { var realOptions = typeof name === "string" ? options : name; // check all options if (devMode()) { realOptions.forEach(function (option, i) { assertIsString(option, i + 1); }); } var type = union.apply(void 0, __spread(realOptions.map(function (option) { return literal("" + option); }))); if (typeof name === "string") type.name = name; return type; } /** * @internal * @hidden */ var Union = /** @class */ (function (_super) { __extends(Union, _super); function Union(name, _types, options) { var _this = _super.call(this, name) || this; _this._types = _types; _this._eager = true; options = __assign({ eager: true, dispatcher: undefined }, options); _this._dispatcher = options.dispatcher; if (!options.eager) _this._eager = false; return _this; } Object.defineProperty(Union.prototype, "flags", { get: function () { var result = TypeFlags.Union; this._types.forEach(function (type) { result |= type.flags; }); return result; }, enumerable: true, configurable: true }); Union.prototype.isAssignableFrom = function (type) { return this._types.some(function (subType) { return subType.isAssignableFrom(type); }); }; Union.prototype.describe = function () { return "(" + this._types.map(function (factory) { return factory.describe(); }).join(" | ") + ")"; }; Union.prototype.instantiate = function (parent, subpath, environment, initialValue) { var type = this.determineType(initialValue, undefined); if (!type) throw fail$1("No matching type for union " + this.describe()); // can happen in prod builds return type.instantiate(parent, subpath, environment, initialValue); }; Union.prototype.reconcile = function (current, newValue, parent, subpath) { var type = this.determineType(newValue, current.type); if (!type) throw fail$1("No matching type for union " + this.describe()); // can happen in prod builds return type.reconcile(current, newValue, parent, subpath); }; Union.prototype.determineType = function (value, reconcileCurrentType) { // try the dispatcher, if defined if (this._dispatcher) { return this._dispatcher(value); } // find the most accomodating type // if we are using reconciliation try the current node type first (fix for #1045) if (reconcileCurrentType) { if (reconcileCurrentType.is(value)) { return reconcileCurrentType; } return this._types.filter(function (t) { return t !== reconcileCurrentType; }).find(function (type) { return type.is(value); }); } else { return this._types.find(function (type) { return type.is(value); }); } }; Union.prototype.isValidSnapshot = function (value, context) { if (this._dispatcher) { return this._dispatcher(value).validate(value, context); } var allErrors = []; var applicableTypes = 0; for (var i = 0; i < this._types.length; i++) { var type = this._types[i]; var errors = type.validate(value, context); if (errors.length === 0) { if (this._eager) return typeCheckSuccess(); else applicableTypes++; } else { allErrors.push(errors); } } if (applicableTypes === 1) return typeCheckSuccess(); return typeCheckFailure(context, value, "No type is applicable for the union").concat(flattenTypeErrors(allErrors)); }; Union.prototype.getSubTypes = function () { return this._types; }; return Union; }(BaseType)); /** * `types.union` - Create a union of multiple types. If the correct type cannot be inferred unambiguously from a snapshot, provide a dispatcher function of the form `(snapshot) => Type`. * * @param optionsOrType * @param otherTypes * @returns */ function union(optionsOrType) { var otherTypes = []; for (var _i = 1; _i < arguments.length; _i++) { otherTypes[_i - 1] = arguments[_i]; } var options = isType(optionsOrType) ? undefined : optionsOrType; var types = isType(optionsOrType) ? __spread([optionsOrType], otherTypes) : otherTypes; var name = "(" + types.map(function (type) { return type.name; }).join(" | ") + ")"; // check all options if (devMode()) { if (options) { assertArg(options, function (o) { return isPlainObject(o); }, "object { eager?: boolean, dispatcher?: Function }", 1); } types.forEach(function (type, i) { assertIsType(type, options ? i + 2 : i + 1); }); } return new Union(name, types, options); } /** * Returns if a given value represents a union type. * * @param type * @returns */ function isUnionType(type) { return (type.flags & TypeFlags.Union) > 0; } /** * @hidden * @internal */ var OptionalValue = /** @class */ (function (_super) { __extends(OptionalValue, _super); function OptionalValue(_subtype, _defaultValue, optionalValues) { var _this = _super.call(this, _subtype.name) || this; _this._subtype = _subtype; _this._defaultValue = _defaultValue; _this.optionalValues = optionalValues; return _this; } Object.defineProperty(OptionalValue.prototype, "flags", { get: function () { return this._subtype.flags | TypeFlags.Optional; }, enumerable: true, configurable: true }); OptionalValue.prototype.describe = function () { return this._subtype.describe() + "?"; }; OptionalValue.prototype.instantiate = function (parent, subpath, environment, initialValue) { if (this.optionalValues.indexOf(initialValue) >= 0) { var defaultInstanceOrSnapshot = this.getDefaultInstanceOrSnapshot(); return this._subtype.instantiate(parent, subpath, environment, defaultInstanceOrSnapshot); } return this._subtype.instantiate(parent, subpath, environment, initialValue); }; OptionalValue.prototype.reconcile = function (current, newValue, parent, subpath) { return this._subtype.reconcile(current, this.optionalValues.indexOf(newValue) < 0 && this._subtype.is(newValue) ? newValue : this.getDefaultInstanceOrSnapshot(), parent, subpath); }; OptionalValue.prototype.getDefaultInstanceOrSnapshot = function () { var defaultInstanceOrSnapshot = typeof this._defaultValue === "function" ? this._defaultValue() : this._defaultValue; // while static values are already snapshots and checked on types.optional // generator functions must always be rechecked just in case if (typeof this._defaultValue === "function") { typecheckInternal(this, defaultInstanceOrSnapshot); } return defaultInstanceOrSnapshot; }; OptionalValue.prototype.isValidSnapshot = function (value, context) { // defaulted values can be skipped if (this.optionalValues.indexOf(value) >= 0) { return typeCheckSuccess(); } // bounce validation to the sub-type return this._subtype.validate(value, context); }; OptionalValue.prototype.isAssignableFrom = function (type) { return this._subtype.isAssignableFrom(type); }; OptionalValue.prototype.getSubTypes = function () { return this._subtype; }; return OptionalValue; }(BaseType)); function checkOptionalPreconditions(type, defaultValueOrFunction) { // make sure we never pass direct instances if (typeof defaultValueOrFunction !== "function" && isStateTreeNode(defaultValueOrFunction)) { throw fail$1("default value cannot be an instance, pass a snapshot or a function that creates an instance/snapshot instead"); } assertIsType(type, 1); if (devMode()) { // we only check default values if they are passed directly // if they are generator functions they will be checked once they are generated // we don't check generator function results here to avoid generating a node just for type-checking purposes // which might generate side-effects if (typeof defaultValueOrFunction !== "function") { typecheckInternal(type, defaultValueOrFunction); } } } /** * `types.optional` - Can be used to create a property with a default value. * * Depending on the third argument (`optionalValues`) there are two ways of operation: * - If the argument is not provided, then if a value is not provided in the snapshot (`undefined` or missing), * it will default to the provided `defaultValue` * - If the argument is provided, then if the value in the snapshot matches one of the optional values inside the array then it will * default to the provided `defaultValue`. Additionally, if one of the optional values inside the array is `undefined` then a missing * property is also valid. * * Note that it is also possible to include values of the same type as the intended subtype as optional values, * in this case the optional value will be transformed into the `defaultValue` (e.g. `types.optional(types.string, "unnamed", [undefined, ""])` * will transform the snapshot values `undefined` (and therefore missing) and empty strings into the string `"unnamed"` when it gets * instantiated). * * If `defaultValue` is a function, the function will be invoked for every new instance. * Applying a snapshot in which the optional value is one of the optional values (or `undefined`/_not_ present if none are provided) causes the * value to be reset. * * Example: * ```ts * const Todo = types.model({ * title: types.string, * subtitle1: types.optional(types.string, "", [null]), * subtitle2: types.optional(types.string, "", [null, undefined]), * done: types.optional(types.boolean, false), * created: types.optional(types.Date, () => new Date()), * }) * * // if done is missing / undefined it will become false * // if created is missing / undefined it will get a freshly generated timestamp * // if subtitle1 is null it will default to "", but it cannot be missing or undefined * // if subtitle2 is null or undefined it will default to ""; since it can be undefined it can also be missing * const todo = Todo.create({ title: "Get coffee", subtitle1: null }) * ``` * * @param type * @param defaultValueOrFunction * @param optionalValues an optional array with zero or more primitive values (string, number, boolean, null or undefined) * that will be converted into the default. `[ undefined ]` is assumed when none is provided * @returns */ function optional(type, defaultValueOrFunction, optionalValues) { checkOptionalPreconditions(type, defaultValueOrFunction); return new OptionalValue(type, defaultValueOrFunction, optionalValues ? optionalValues : undefinedAsOptionalValues); } var undefinedAsOptionalValues = [undefined]; /** * Returns if a value represents an optional type. * * @template IT * @param type * @returns */ function isOptionalType(type) { return isType(type) && (type.flags & TypeFlags.Optional) > 0; } var optionalUndefinedType = optional(undefinedType, undefined); var optionalNullType = optional(nullType, null); /** * `types.maybe` - Maybe will make a type nullable, and also optional. * The value `undefined` will be used to represent nullability. * * @param type * @returns */ function maybe(type) { assertIsType(type, 1); return union(type, optionalUndefinedType); } /** * `types.maybeNull` - Maybe will make a type nullable, and also optional. * The value `null` will be used to represent no value. * * @param type * @returns */ function maybeNull(type) { assertIsType(type, 1); return union(type, optionalNullType); } var Late = /** @class */ (function (_super) { __extends(Late, _super); function Late(name, _definition) { var _this = _super.call(this, name) || this; _this._definition = _definition; return _this; } Object.defineProperty(Late.prototype, "flags", { get: function () { return (this._subType ? this._subType.flags : 0) | TypeFlags.Late; }, enumerable: true, configurable: true }); Late.prototype.getSubType = function (mustSucceed) { if (!this._subType) { var t = undefined; try { t = this._definition(); } catch (e) { if (e instanceof ReferenceError) // can happen in strict ES5 code when a definition is self refering t = undefined; else throw e; } if (mustSucceed && t === undefined) throw fail$1("Late type seems to be used too early, the definition (still) returns undefined"); if (t) { if (devMode() && !isType(t)) throw fail$1("Failed to determine subtype, make sure types.late returns a type definition."); this._subType = t; } } return this._subType; }; Late.prototype.instantiate = function (parent, subpath, environment, initialValue) { return this.getSubType(true).instantiate(parent, subpath, environment, initialValue); }; Late.prototype.reconcile = function (current, newValue, parent, subpath) { return this.getSubType(true).reconcile(current, newValue, parent, subpath); }; Late.prototype.describe = function () { var t = this.getSubType(false); return t ? t.name : ""; }; Late.prototype.isValidSnapshot = function (value, context) { var t = this.getSubType(false); if (!t) { // See #916; the variable the definition closure is pointing to wasn't defined yet, so can't be evaluted yet here return typeCheckSuccess(); } return t.validate(value, context); }; Late.prototype.isAssignableFrom = function (type) { var t = this.getSubType(false); return t ? t.isAssignableFrom(type) : false; }; Late.prototype.getSubTypes = function () { var subtype = this.getSubType(false); return subtype ? subtype : cannotDetermineSubtype; }; return Late; }(BaseType)); /** * `types.late` - Defines a type that gets implemented later. This is useful when you have to deal with circular dependencies. * Please notice that when defining circular dependencies TypeScript isn't smart enough to inference them. * * Example: * ```ts * // TypeScript isn't smart enough to infer self referencing types. * const Node = types.model({ * children: types.array(types.late((): IAnyModelType => Node)) // then typecast each array element to Instance * }) * ``` * * @param name The name to use for the type that will be returned. * @param type A function that returns the type that will be defined. * @returns */ function late(nameOrType, maybeType) { var name = typeof nameOrType === "string" ? nameOrType : "late(" + nameOrType.toString() + ")"; var type = typeof nameOrType === "string" ? maybeType : nameOrType; // checks that the type is actually a late type if (devMode()) { if (!(typeof type === "function" && type.length === 0)) throw fail$1("Invalid late type, expected a function with zero arguments that returns a type, got: " + type); } return new Late(name, type); } /** * Returns if a given value represents a late type. * * @param type * @returns */ function isLateType(type) { return isType(type) && (type.flags & TypeFlags.Late) > 0; } /** * @internal * @hidden */ var Frozen = /** @class */ (function (_super) { __extends(Frozen, _super); function Frozen(subType) { var _this = _super.call(this, subType ? "frozen(" + subType.name + ")" : "frozen") || this; _this.subType = subType; _this.flags = TypeFlags.Frozen; return _this; } Frozen.prototype.describe = function () { return ""; }; Frozen.prototype.instantiate = function (parent, subpath, environment, value) { // create the node return createScalarNode(this, parent, subpath, environment, deepFreeze(value)); }; Frozen.prototype.isValidSnapshot = function (value, context) { if (!isSerializable(value)) { return typeCheckFailure(context, value, "Value is not serializable and cannot be frozen"); } if (this.subType) return this.subType.validate(value, context); return typeCheckSuccess(); }; return Frozen; }(SimpleType)); var untypedFrozenInstance = new Frozen(); /** * `types.frozen` - Frozen can be used to store any value that is serializable in itself (that is valid JSON). * Frozen values need to be immutable or treated as if immutable. They need be serializable as well. * Values stored in frozen will snapshotted as-is by MST, and internal changes will not be tracked. * * This is useful to store complex, but immutable values like vectors etc. It can form a powerful bridge to parts of your application that should be immutable, or that assume data to be immutable. * * Note: if you want to store free-form state that is mutable, or not serializeable, consider using volatile state instead. * * Frozen properties can be defined in three different ways * 1. `types.frozen(SubType)` - provide a valid MST type and frozen will check if the provided data conforms the snapshot for that type * 2. `types.frozen({ someDefaultValue: true})` - provide a primitive value, object or array, and MST will infer the type from that object, and also make it the default value for the field * 3. `types.frozen()` - provide a typescript type, to help in strongly typing the field (design time only) * * Example: * ```ts * const GameCharacter = types.model({ * name: string, * location: types.frozen({ x: 0, y: 0}) * }) * * const hero = GameCharacter.create({ * name: "Mario", * location: { x: 7, y: 4 } * }) * * hero.location = { x: 10, y: 2 } // OK * hero.location.x = 7 // Not ok! * ``` * * ```ts * type Point = { x: number, y: number } * const Mouse = types.model({ * loc: types.frozen() * }) * ``` * * @param defaultValueOrType * @returns */ function frozen(arg) { if (arguments.length === 0) return untypedFrozenInstance; else if (isType(arg)) return new Frozen(arg); else return optional(untypedFrozenInstance, arg); } /** * Returns if a given value represents a frozen type. * * @param type * @returns */ function isFrozenType(type) { return isType(type) && (type.flags & TypeFlags.Frozen) > 0; } function getInvalidationCause(hook) { switch (hook) { case Hook.beforeDestroy: return "destroy"; case Hook.beforeDetach: return "detach"; default: return undefined; } } var StoredReference = /** @class */ (function () { function StoredReference(value, targetType) { this.targetType = targetType; if (isValidIdentifier(value)) { this.identifier = value; } else if (isStateTreeNode(value)) { var targetNode = getStateTreeNode(value); if (!targetNode.identifierAttribute) throw fail$1("Can only store references with a defined identifier attribute."); var id = targetNode.unnormalizedIdentifier; if (id === null || id === undefined) { throw fail$1("Can only store references to tree nodes with a defined identifier."); } this.identifier = id; } else { throw fail$1("Can only store references to tree nodes or identifiers, got: '" + value + "'"); } } StoredReference.prototype.updateResolvedReference = function (node) { var normalizedId = normalizeIdentifier(this.identifier); var root = node.root; var lastCacheModification = root.identifierCache.getLastCacheModificationPerId(normalizedId); if (!this.resolvedReference || this.resolvedReference.lastCacheModification !== lastCacheModification) { var targetType = this.targetType; // reference was initialized with the identifier of the target var target = root.identifierCache.resolve(targetType, normalizedId); if (!target) { throw new InvalidReferenceError("[mobx-state-tree] Failed to resolve reference '" + this.identifier + "' to type '" + this.targetType.name + "' (from node: " + node.path + ")"); } this.resolvedReference = { node: target, lastCacheModification: lastCacheModification }; } }; Object.defineProperty(StoredReference.prototype, "resolvedValue", { get: function () { this.updateResolvedReference(this.node); return this.resolvedReference.node.value; }, enumerable: true, configurable: true }); return StoredReference; }()); /** * @internal * @hidden */ var InvalidReferenceError = /** @class */ (function (_super) { __extends(InvalidReferenceError, _super); function InvalidReferenceError(m) { var _this = _super.call(this, m) || this; Object.setPrototypeOf(_this, InvalidReferenceError.prototype); return _this; } return InvalidReferenceError; }(Error)); /** * @internal * @hidden */ var BaseReferenceType = /** @class */ (function (_super) { __extends(BaseReferenceType, _super); function BaseReferenceType(targetType, onInvalidated) { var _this = _super.call(this, "reference(" + targetType.name + ")") || this; _this.targetType = targetType; _this.onInvalidated = onInvalidated; _this.flags = TypeFlags.Reference; return _this; } BaseReferenceType.prototype.describe = function () { return this.name; }; BaseReferenceType.prototype.isAssignableFrom = function (type) { return this.targetType.isAssignableFrom(type); }; BaseReferenceType.prototype.isValidSnapshot = function (value, context) { return isValidIdentifier(value) ? typeCheckSuccess() : typeCheckFailure(context, value, "Value is not a valid identifier, which is a string or a number"); }; BaseReferenceType.prototype.fireInvalidated = function (cause, storedRefNode, referenceId, refTargetNode) { // to actually invalidate a reference we need an alive parent, // since it is a scalar value (immutable-ish) and we need to change it // from the parent var storedRefParentNode = storedRefNode.parent; if (!storedRefParentNode || !storedRefParentNode.isAlive) { return; } var storedRefParentValue = storedRefParentNode.storedValue; if (!storedRefParentValue) { return; } this.onInvalidated({ cause: cause, parent: storedRefParentValue, invalidTarget: refTargetNode ? refTargetNode.storedValue : undefined, invalidId: referenceId, replaceRef: function (newRef) { applyPatch(storedRefNode.root.storedValue, { op: "replace", value: newRef, path: storedRefNode.path }); }, removeRef: function () { if (isModelType(storedRefParentNode.type)) { this.replaceRef(undefined); } else { applyPatch(storedRefNode.root.storedValue, { op: "remove", path: storedRefNode.path }); } } }); }; BaseReferenceType.prototype.addTargetNodeWatcher = function (storedRefNode, referenceId) { var _this = this; // this will make sure the target node becomes created var refTargetValue = this.getValue(storedRefNode); if (!refTargetValue) { return undefined; } var refTargetNode = getStateTreeNode(refTargetValue); var hookHandler = function (_, refTargetNodeHook) { var cause = getInvalidationCause(refTargetNodeHook); if (!cause) { return; } _this.fireInvalidated(cause, storedRefNode, referenceId, refTargetNode); }; var refTargetDetachHookDisposer = refTargetNode.registerHook(Hook.beforeDetach, hookHandler); var refTargetDestroyHookDisposer = refTargetNode.registerHook(Hook.beforeDestroy, hookHandler); return function () { refTargetDetachHookDisposer(); refTargetDestroyHookDisposer(); }; }; BaseReferenceType.prototype.watchTargetNodeForInvalidations = function (storedRefNode, identifier, customGetSet) { var _this = this; if (!this.onInvalidated) { return; } var onRefTargetDestroyedHookDisposer; // get rid of the watcher hook when the stored ref node is destroyed // detached is ignored since scalar nodes (where the reference resides) cannot be detached storedRefNode.registerHook(Hook.beforeDestroy, function () { if (onRefTargetDestroyedHookDisposer) { onRefTargetDestroyedHookDisposer(); } }); var startWatching = function (sync) { // re-create hook in case the stored ref gets reattached if (onRefTargetDestroyedHookDisposer) { onRefTargetDestroyedHookDisposer(); } // make sure the target node is actually there and initialized var storedRefParentNode = storedRefNode.parent; var storedRefParentValue = storedRefParentNode && storedRefParentNode.storedValue; if (storedRefParentNode && storedRefParentNode.isAlive && storedRefParentValue) { var refTargetNodeExists = void 0; if (customGetSet) { refTargetNodeExists = !!customGetSet.get(identifier, storedRefParentValue); } else { refTargetNodeExists = storedRefNode.root.identifierCache.has(_this.targetType, normalizeIdentifier(identifier)); } if (!refTargetNodeExists) { // we cannot change the reference in sync mode // since we are in the middle of a reconciliation/instantiation and the change would be overwritten // for those cases just let the wrong reference be assigned and fail upon usage // (like current references do) // this means that effectively this code will only run when it is created from a snapshot if (!sync) { _this.fireInvalidated("invalidSnapshotReference", storedRefNode, identifier, null); } } else { onRefTargetDestroyedHookDisposer = _this.addTargetNodeWatcher(storedRefNode, identifier); } } }; if (storedRefNode.state === NodeLifeCycle.FINALIZED) { // already attached, so the whole tree is ready startWatching(true); } else { if (!storedRefNode.isRoot) { // start watching once the whole tree is ready storedRefNode.root.registerHook(Hook.afterCreationFinalization, function () { // make sure to attach it so it can start listening if (storedRefNode.parent) { storedRefNode.parent.createObservableInstanceIfNeeded(); } }); } // start watching once the node is attached somewhere / parent changes storedRefNode.registerHook(Hook.afterAttach, function () { startWatching(false); }); } }; return BaseReferenceType; }(SimpleType)); /** * @internal * @hidden */ var IdentifierReferenceType = /** @class */ (function (_super) { __extends(IdentifierReferenceType, _super); function IdentifierReferenceType(targetType, onInvalidated) { return _super.call(this, targetType, onInvalidated) || this; } IdentifierReferenceType.prototype.getValue = function (storedRefNode) { if (!storedRefNode.isAlive) return undefined; var storedRef = storedRefNode.storedValue; return storedRef.resolvedValue; }; IdentifierReferenceType.prototype.getSnapshot = function (storedRefNode) { var ref = storedRefNode.storedValue; return ref.identifier; }; IdentifierReferenceType.prototype.instantiate = function (parent, subpath, environment, initialValue) { var identifier = isStateTreeNode(initialValue) ? getIdentifier(initialValue) : initialValue; var storedRef = new StoredReference(initialValue, this.targetType); var storedRefNode = createScalarNode(this, parent, subpath, environment, storedRef); storedRef.node = storedRefNode; this.watchTargetNodeForInvalidations(storedRefNode, identifier, undefined); return storedRefNode; }; IdentifierReferenceType.prototype.reconcile = function (current, newValue, parent, subpath) { if (!current.isDetaching && current.type === this) { var compareByValue = isStateTreeNode(newValue); var ref = current.storedValue; if ((!compareByValue && ref.identifier === newValue) || (compareByValue && ref.resolvedValue === newValue)) { current.setParent(parent, subpath); return current; } } var newNode = this.instantiate(parent, subpath, undefined, newValue); current.die(); // noop if detaching return newNode; }; return IdentifierReferenceType; }(BaseReferenceType)); /** * @internal * @hidden */ var CustomReferenceType = /** @class */ (function (_super) { __extends(CustomReferenceType, _super); function CustomReferenceType(targetType, options, onInvalidated) { var _this = _super.call(this, targetType, onInvalidated) || this; _this.options = options; return _this; } CustomReferenceType.prototype.getValue = function (storedRefNode) { if (!storedRefNode.isAlive) return undefined; var referencedNode = this.options.get(storedRefNode.storedValue, storedRefNode.parent ? storedRefNode.parent.storedValue : null); return referencedNode; }; CustomReferenceType.prototype.getSnapshot = function (storedRefNode) { return storedRefNode.storedValue; }; CustomReferenceType.prototype.instantiate = function (parent, subpath, environment, newValue) { var identifier = isStateTreeNode(newValue) ? this.options.set(newValue, parent ? parent.storedValue : null) : newValue; var storedRefNode = createScalarNode(this, parent, subpath, environment, identifier); this.watchTargetNodeForInvalidations(storedRefNode, identifier, this.options); return storedRefNode; }; CustomReferenceType.prototype.reconcile = function (current, newValue, parent, subpath) { var newIdentifier = isStateTreeNode(newValue) ? this.options.set(newValue, current ? current.storedValue : null) : newValue; if (!current.isDetaching && current.type === this && current.storedValue === newIdentifier) { current.setParent(parent, subpath); return current; } var newNode = this.instantiate(parent, subpath, undefined, newIdentifier); current.die(); // noop if detaching return newNode; }; return CustomReferenceType; }(BaseReferenceType)); /** * `types.reference` - Creates a reference to another type, which should have defined an identifier. * See also the [reference and identifiers](https://github.com/mobxjs/mobx-state-tree#references-and-identifiers) section. */ function reference(subType, options) { assertIsType(subType, 1); if (devMode()) { if (arguments.length === 2 && typeof arguments[1] === "string") { // istanbul ignore next throw fail$1("References with base path are no longer supported. Please remove the base path."); } } var getSetOptions = options ? options : undefined; var onInvalidated = options ? options.onInvalidated : undefined; if (getSetOptions && (getSetOptions.get || getSetOptions.set)) { if (devMode()) { if (!getSetOptions.get || !getSetOptions.set) { throw fail$1("reference options must either contain both a 'get' and a 'set' method or none of them"); } } return new CustomReferenceType(subType, { get: getSetOptions.get, set: getSetOptions.set }, onInvalidated); } else { return new IdentifierReferenceType(subType, onInvalidated); } } /** * Returns if a given value represents a reference type. * * @param type * @returns */ function isReferenceType(type) { return (type.flags & TypeFlags.Reference) > 0; } /** * `types.safeReference` - A safe reference is like a standard reference, except that it accepts the undefined value by default * and automatically sets itself to undefined (when the parent is a model) / removes itself from arrays and maps * when the reference it is pointing to gets detached/destroyed. * * The optional options parameter object accepts a parameter named `acceptsUndefined`, which is set to true by default, so it is suitable * for model properties. * When used inside collections (arrays/maps), it is recommended to set this option to false so it can't take undefined as value, * which is usually the desired in those cases. * * Strictly speaking it is a `types.maybe(types.reference(X))` (when `acceptsUndefined` is set to true, the default) and * `types.reference(X)` (when `acceptsUndefined` is set to false), both of them with a customized `onInvalidated` option. * * @param subType * @param options * @returns */ function safeReference(subType, options) { var refType = reference(subType, __assign(__assign({}, options), { onInvalidated: function (ev) { ev.removeRef(); } })); if (options && options.acceptsUndefined === false) { return refType; } else { return maybe(refType); } } var BaseIdentifierType = /** @class */ (function (_super) { __extends(BaseIdentifierType, _super); function BaseIdentifierType(name, validType) { var _this = _super.call(this, name) || this; _this.validType = validType; _this.flags = TypeFlags.Identifier; return _this; } BaseIdentifierType.prototype.instantiate = function (parent, subpath, environment, initialValue) { if (!parent || !(parent.type instanceof ModelType)) throw fail$1("Identifier types can only be instantiated as direct child of a model type"); return createScalarNode(this, parent, subpath, environment, initialValue); }; BaseIdentifierType.prototype.reconcile = function (current, newValue, parent, subpath) { // we don't consider detaching here since identifier are scalar nodes, and scalar nodes cannot be detached if (current.storedValue !== newValue) throw fail$1("Tried to change identifier from '" + current.storedValue + "' to '" + newValue + "'. Changing identifiers is not allowed."); current.setParent(parent, subpath); return current; }; BaseIdentifierType.prototype.isValidSnapshot = function (value, context) { if (typeof value !== this.validType) { return typeCheckFailure(context, value, "Value is not a valid " + this.describe() + ", expected a " + this.validType); } return typeCheckSuccess(); }; return BaseIdentifierType; }(SimpleType)); /** * @internal * @hidden */ var IdentifierType = /** @class */ (function (_super) { __extends(IdentifierType, _super); function IdentifierType() { var _this = _super.call(this, "identifier", "string") || this; _this.flags = TypeFlags.Identifier; return _this; } IdentifierType.prototype.describe = function () { return "identifier"; }; return IdentifierType; }(BaseIdentifierType)); /** * @internal * @hidden */ var IdentifierNumberType = /** @class */ (function (_super) { __extends(IdentifierNumberType, _super); function IdentifierNumberType() { return _super.call(this, "identifierNumber", "number") || this; } IdentifierNumberType.prototype.getSnapshot = function (node) { return node.storedValue; }; IdentifierNumberType.prototype.describe = function () { return "identifierNumber"; }; return IdentifierNumberType; }(BaseIdentifierType)); /** * `types.identifier` - Identifiers are used to make references, lifecycle events and reconciling works. * Inside a state tree, for each type can exist only one instance for each given identifier. * For example there couldn't be 2 instances of user with id 1. If you need more, consider using references. * Identifier can be used only as type property of a model. * This type accepts as parameter the value type of the identifier field that can be either string or number. * * Example: * ```ts * const Todo = types.model("Todo", { * id: types.identifier, * title: types.string * }) * ``` * * @returns */ var identifier = new IdentifierType(); /** * `types.identifierNumber` - Similar to `types.identifier`. This one will serialize from / to a number when applying snapshots * * Example: * ```ts * const Todo = types.model("Todo", { * id: types.identifierNumber, * title: types.string * }) * ``` * * @returns */ var identifierNumber = new IdentifierNumberType(); /** * Returns if a given value represents an identifier type. * * @param type * @returns */ function isIdentifierType(type) { return isType(type) && (type.flags & TypeFlags.Identifier) > 0; } /** * @internal * @hidden */ function normalizeIdentifier(id) { return "" + id; } /** * @internal * @hidden */ function isValidIdentifier(id) { return typeof id === "string" || typeof id === "number"; } /** * @internal * @hidden */ function assertIsValidIdentifier(id, argNumber) { assertArg(id, isValidIdentifier, "string or number (identifier)", argNumber); } /** * `types.custom` - Creates a custom type. Custom types can be used for arbitrary immutable values, that have a serializable representation. For example, to create your own Date representation, Decimal type etc. * * The signature of the options is: * ```ts * export interface CustomTypeOptions { * // Friendly name * name: string * // given a serialized value, how to turn it into the target type * fromSnapshot(snapshot: S): T * // return the serialization of the current value * toSnapshot(value: T): S * // if true, this is a converted value, if false, it's a snapshot * isTargetType(value: T | S): value is T * // a non empty string is assumed to be a validation error * getValidationMessage?(snapshot: S): string * } * ``` * * Example: * ```ts * const DecimalPrimitive = types.custom({ * name: "Decimal", * fromSnapshot(value: string) { * return new Decimal(value) * }, * toSnapshot(value: Decimal) { * return value.toString() * }, * isTargetType(value: string | Decimal): boolean { * return value instanceof Decimal * }, * getValidationMessage(value: string): string { * if (/^-?\d+\.\d+$/.test(value)) return "" // OK * return `'${value}' doesn't look like a valid decimal number` * } * }) * * const Wallet = types.model({ * balance: DecimalPrimitive * }) * ``` * * @param options * @returns */ function custom(options) { return new CustomType(options); } /** * @internal * @hidden */ var CustomType = /** @class */ (function (_super) { __extends(CustomType, _super); function CustomType(options) { var _this = _super.call(this, options.name) || this; _this.options = options; _this.flags = TypeFlags.Custom; return _this; } CustomType.prototype.describe = function () { return this.name; }; CustomType.prototype.isValidSnapshot = function (value, context) { if (this.options.isTargetType(value)) return typeCheckSuccess(); var typeError = this.options.getValidationMessage(value); if (typeError) { return typeCheckFailure(context, value, "Invalid value for type '" + this.name + "': " + typeError); } return typeCheckSuccess(); }; CustomType.prototype.getSnapshot = function (node) { return this.options.toSnapshot(node.storedValue); }; CustomType.prototype.instantiate = function (parent, subpath, environment, initialValue) { var valueToStore = this.options.isTargetType(initialValue) ? initialValue : this.options.fromSnapshot(initialValue); return createScalarNode(this, parent, subpath, environment, valueToStore); }; CustomType.prototype.reconcile = function (current, value, parent, subpath) { var isSnapshot = !this.options.isTargetType(value); // in theory customs use scalar nodes which cannot be detached, but still... if (!current.isDetaching) { var unchanged = current.type === this && (isSnapshot ? value === current.snapshot : value === current.storedValue); if (unchanged) { current.setParent(parent, subpath); return current; } } var valueToStore = isSnapshot ? this.options.fromSnapshot(value) : value; var newNode = this.instantiate(parent, subpath, undefined, valueToStore); current.die(); // noop if detaching return newNode; }; return CustomType; }(SimpleType)); // we import the types to re-export them inside types. var types = { enumeration: enumeration, model: model, compose: compose, custom: custom, reference: reference, safeReference: safeReference, union: union, optional: optional, literal: literal, maybe: maybe, maybeNull: maybeNull, refinement: refinement, string: string, boolean: boolean, number: number, integer: integer, Date: DatePrimitive, map: map, array: array, frozen: frozen, identifier: identifier, identifierNumber: identifierNumber, late: late, undefined: undefinedType, null: nullType, snapshotProcessor: snapshotProcessor }; export { addDisposer, addMiddleware, applyAction, applyPatch, applySnapshot, cast, castFlowReturn, castToReferenceSnapshot, castToSnapshot, clone, createActionTrackingMiddleware, createActionTrackingMiddleware2, decorate, destroy, detach, escapeJsonPath, flow, getChildType, getEnv, getIdentifier, getLivelinessChecking, getMembers, getNodeId, getParent, getParentOfType, getPath, getPathParts, getPropertyMembers, getRelativePath, getRoot, getRunningActionContext, getSnapshot, getType, hasParent, hasParentOfType, isActionContextChildOf, isActionContextThisOrChildOf, isAlive, isArrayType, isFrozenType, isIdentifierType, isLateType, isLiteralType, isMapType, isModelType, isOptionalType, isPrimitiveType, isProtected, isReferenceType, isRefinementType, isRoot, isStateTreeNode, isType, isUnionType, isValidReference, joinJsonPath, onAction, onPatch, onSnapshot, process$1 as process, protect, recordActions, recordPatches, resolveIdentifier, resolvePath, setLivelinessChecking, setLivelynessChecking, splitJsonPath, tryReference, tryResolve, typecheck, types, unescapeJsonPath, unprotect, walk };