UNPKG

139 kBJavaScriptView Raw
1import { action, computed, extendShallowObservable, extras, intercept, isComputed, isObservableArray, observable, observe, reaction, runInAction } from 'mobx';
2
3/*! *****************************************************************************
4Copyright (c) Microsoft Corporation. All rights reserved.
5Licensed under the Apache License, Version 2.0 (the "License"); you may not use
6this file except in compliance with the License. You may obtain a copy of the
7License at http://www.apache.org/licenses/LICENSE-2.0
8
9THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
10KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
11WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
12MERCHANTABLITY OR NON-INFRINGEMENT.
13
14See the Apache Version 2.0 License for specific language governing permissions
15and limitations under the License.
16***************************************************************************** */
17/* global Reflect, Promise */
18
19var extendStatics = Object.setPrototypeOf ||
20 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
21 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
22
23function __extends(d, b) {
24 extendStatics(d, b);
25 function __() { this.constructor = d; }
26 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
27}
28
29
30
31
32
33function __decorate(decorators, target, key, desc) {
34 var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
35 if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
36 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;
37 return c > 3 && r && Object.defineProperty(target, key, r), r;
38}
39
40var EMPTY_ARRAY = Object.freeze([]);
41var EMPTY_OBJECT = Object.freeze({});
42function fail(message) {
43 if (message === void 0) { message = "Illegal state"; }
44 throw new Error("[mobx-state-tree] " + message);
45}
46function identity(_) {
47 return _;
48}
49
50function noop() { }
51function isArray(val) {
52 return !!(Array.isArray(val) || isObservableArray(val));
53}
54function asArray(val) {
55 if (!val)
56 return EMPTY_ARRAY;
57 if (isArray(val))
58 return val;
59 return [val];
60}
61function extend(a) {
62 var b = [];
63 for (var _i = 1; _i < arguments.length; _i++) {
64 b[_i - 1] = arguments[_i];
65 }
66 for (var i = 0; i < b.length; i++) {
67 var current = b[i];
68 for (var key in current)
69 a[key] = current[key];
70 }
71 return a;
72}
73
74function isPlainObject(value) {
75 if (value === null || typeof value !== "object")
76 return false;
77 var proto = Object.getPrototypeOf(value);
78 return proto === Object.prototype || proto === null;
79}
80function isMutable(value) {
81 return (value !== null &&
82 typeof value === "object" &&
83 !(value instanceof Date) &&
84 !(value instanceof RegExp));
85}
86function isPrimitive(value) {
87 if (value === null || value === undefined)
88 return true;
89 if (typeof value === "string" ||
90 typeof value === "number" ||
91 typeof value === "boolean" ||
92 value instanceof Date)
93 return true;
94 return false;
95}
96function freeze(value) {
97 return isPrimitive(value) ? value : Object.freeze(value);
98}
99function deepFreeze(value) {
100 freeze(value);
101 if (isPlainObject(value)) {
102 Object.keys(value).forEach(function (propKey) {
103 if (!isPrimitive(value[propKey]) &&
104 !Object.isFrozen(value[propKey])) {
105 deepFreeze(value[propKey]);
106 }
107 });
108 }
109 return value;
110}
111function isSerializable(value) {
112 return typeof value !== "function";
113}
114function addHiddenFinalProp(object, propName, value) {
115 Object.defineProperty(object, propName, {
116 enumerable: false,
117 writable: false,
118 configurable: true,
119 value: value
120 });
121}
122
123function addReadOnlyProp(object, propName, value) {
124 Object.defineProperty(object, propName, {
125 enumerable: true,
126 writable: false,
127 configurable: true,
128 value: value
129 });
130}
131function remove(collection, item) {
132 var idx = collection.indexOf(item);
133 if (idx !== -1)
134 collection.splice(idx, 1);
135}
136function registerEventHandler(handlers, handler) {
137 handlers.push(handler);
138 return function () {
139 remove(handlers, handler);
140 };
141}
142
143function argsToArray(args) {
144 var res = new Array(args.length);
145 for (var i = 0; i < args.length; i++)
146 res[i] = args[i];
147 return res;
148}
149
150// https://tools.ietf.org/html/rfc6902
151// http://jsonpatch.com/
152function splitPatch(patch) {
153 if (!("oldValue" in patch))
154 fail("Patches without `oldValue` field cannot be inversed");
155 return [stripPatch(patch), invertPatch(patch)];
156}
157function stripPatch(patch) {
158 // strips `oldvalue` information from the patch, so that it becomes a patch conform the json-patch spec
159 // this removes the ability to undo the patch
160 switch (patch.op) {
161 case "add":
162 return { op: "add", path: patch.path, value: patch.value };
163 case "remove":
164 return { op: "remove", path: patch.path };
165 case "replace":
166 return { op: "replace", path: patch.path, value: patch.value };
167 }
168}
169function invertPatch(patch) {
170 switch (patch.op) {
171 case "add":
172 return {
173 op: "remove",
174 path: patch.path
175 };
176 case "remove":
177 return {
178 op: "add",
179 path: patch.path,
180 value: patch.oldValue
181 };
182 case "replace":
183 return {
184 op: "replace",
185 path: patch.path,
186 value: patch.oldValue
187 };
188 }
189}
190/**
191 * escape slashes and backslashes
192 * http://tools.ietf.org/html/rfc6901
193 */
194function escapeJsonPath(str) {
195 return str.replace(/~/g, "~1").replace(/\//g, "~0");
196}
197/**
198 * unescape slashes and backslashes
199 */
200function unescapeJsonPath(str) {
201 return str.replace(/~0/g, "/").replace(/~1/g, "~");
202}
203function joinJsonPath(path) {
204 // `/` refers to property with an empty name, while `` refers to root itself!
205 if (path.length === 0)
206 return "";
207 return "/" + path.map(escapeJsonPath).join("/");
208}
209function splitJsonPath(path) {
210 // `/` refers to property with an empty name, while `` refers to root itself!
211 var parts = path.split("/").map(unescapeJsonPath);
212 // path '/a/b/c' -> a b c
213 // path '../../b/c -> .. .. b c
214 return parts[0] === "" ? parts.slice(1) : parts;
215}
216
217var TypeFlags;
218(function (TypeFlags) {
219 TypeFlags[TypeFlags["String"] = 1] = "String";
220 TypeFlags[TypeFlags["Number"] = 2] = "Number";
221 TypeFlags[TypeFlags["Boolean"] = 4] = "Boolean";
222 TypeFlags[TypeFlags["Date"] = 8] = "Date";
223 TypeFlags[TypeFlags["Literal"] = 16] = "Literal";
224 TypeFlags[TypeFlags["Array"] = 32] = "Array";
225 TypeFlags[TypeFlags["Map"] = 64] = "Map";
226 TypeFlags[TypeFlags["Object"] = 128] = "Object";
227 TypeFlags[TypeFlags["Frozen"] = 256] = "Frozen";
228 TypeFlags[TypeFlags["Optional"] = 512] = "Optional";
229 TypeFlags[TypeFlags["Reference"] = 1024] = "Reference";
230 TypeFlags[TypeFlags["Identifier"] = 2048] = "Identifier";
231 TypeFlags[TypeFlags["Late"] = 4096] = "Late";
232 TypeFlags[TypeFlags["Refinement"] = 8192] = "Refinement";
233 TypeFlags[TypeFlags["Union"] = 16384] = "Union";
234 TypeFlags[TypeFlags["Null"] = 32768] = "Null";
235 TypeFlags[TypeFlags["Undefined"] = 65536] = "Undefined";
236})(TypeFlags || (TypeFlags = {}));
237function isType(value) {
238 return typeof value === "object" && value && value.isType === true;
239}
240function isPrimitiveType(type) {
241 return (isType(type) &&
242 (type.flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.Boolean | TypeFlags.Date)) >
243 0);
244}
245
246
247
248
249
250
251
252
253function isReferenceType(type) {
254 return (type.flags & TypeFlags.Reference) > 0;
255}
256
257/**
258 * Returns the _actual_ type of the given tree node. (Or throws)
259 *
260 * @export
261 * @param {IStateTreeNode} object
262 * @returns {IType<S, T>}
263 */
264function getType(object) {
265 return getStateTreeNode(object).type;
266}
267/**
268 * Returns the _declared_ type of the given sub property of an object, array or map.
269 *
270 * @example
271 * const Box = types.model({ x: 0, y: 0 })
272 * const box = Box.create()
273 *
274 * console.log(getChildType(box, "x").name) // 'number'
275 *
276 * @export
277 * @param {IStateTreeNode} object
278 * @param {string} child
279 * @returns {IType<any, any>}
280 */
281function getChildType(object, child) {
282 return getStateTreeNode(object).getChildType(child);
283}
284/**
285 * Registers a function that will be invoked for each mutation that is applied to the provided model instance, or to any of its children.
286 * 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.
287 * Patches can be used to deep observe a model tree.
288 *
289 * @export
290 * @param {Object} target the model instance from which to receive patches
291 * @param {(patch: IJsonPatch, reversePatch) => void} callback the callback that is invoked for each patch. The reversePatch is a patch that would actually undo the emitted patch
292 * @param {includeOldValue} boolean if oldValue is included in the patches, they can be inverted. However patches will become much bigger and might not be suitable for efficient transport
293 * @returns {IDisposer} function to remove the listener
294 */
295function onPatch(target, callback) {
296 // check all arguments
297 if (process.env.NODE_ENV !== "production") {
298 if (!isStateTreeNode(target))
299 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
300 if (typeof callback !== "function")
301 fail("expected second argument to be a function, got " + callback + " instead");
302 }
303 return getStateTreeNode(target).onPatch(callback);
304}
305/**
306 * Registers a function that is invoked whenever a new snapshot for the given model instance is available.
307 * The listener will only be fire at the and of the current MobX (trans)action.
308 * See [snapshots](https://github.com/mobxjs/mobx-state-tree#snapshots) for more details.
309 *
310 * @export
311 * @param {Object} target
312 * @param {(snapshot: any) => void} callback
313 * @returns {IDisposer}
314 */
315function onSnapshot(target, callback) {
316 // check all arguments
317 if (process.env.NODE_ENV !== "production") {
318 if (!isStateTreeNode(target))
319 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
320 if (typeof callback !== "function")
321 fail("expected second argument to be a function, got " + callback + " instead");
322 }
323 return getStateTreeNode(target).onSnapshot(callback);
324}
325/**
326 * Applies a JSON-patch to the given model instance or bails out if the patch couldn't be applied
327 * See [patches](https://github.com/mobxjs/mobx-state-tree#patches) for more details.
328 *
329 * Can apply a single past, or an array of patches.
330 *
331 * @export
332 * @param {Object} target
333 * @param {IJsonPatch} patch
334 * @returns
335 */
336function applyPatch(target, patch) {
337 // check all arguments
338 if (process.env.NODE_ENV !== "production") {
339 if (!isStateTreeNode(target))
340 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
341 if (typeof patch !== "object")
342 fail("expected second argument to be an object or array, got " + patch + " instead");
343 }
344 getStateTreeNode(target).applyPatches(asArray(patch));
345}
346/**
347 * Small abstraction around `onPatch` and `applyPatch`, attaches a patch listener to a tree and records all the patches.
348 * Returns an recorder object with the following signature:
349 *
350 * @example
351 * export interface IPatchRecorder {
352 * // the recorded patches
353 * patches: IJsonPatch[]
354 * // the inverse of the recorded patches
355 * inversePatches: IJsonPatch[]
356 * // stop recording patches
357 * stop(target?: IStateTreeNode): any
358 * // resume recording patches
359 * resume()
360 * // apply all the recorded patches on the given target (the original subject if omitted)
361 * replay(target?: IStateTreeNode): any
362 * // reverse apply the recorded patches on the given target (the original subject if omitted)
363 * // stops the recorder if not already stopped
364 * undo(): void
365 * }
366 *
367 * @export
368 * @param {IStateTreeNode} subject
369 * @returns {IPatchRecorder}
370 */
371function recordPatches(subject) {
372 // check all arguments
373 if (process.env.NODE_ENV !== "production") {
374 if (!isStateTreeNode(subject))
375 fail("expected first argument to be a mobx-state-tree node, got " + subject + " instead");
376 }
377 var disposer = null;
378 function resume() {
379 if (disposer)
380 return;
381 disposer = onPatch(subject, function (patch, inversePatch) {
382 recorder.rawPatches.push([patch, inversePatch]);
383 });
384 }
385 var recorder = {
386 rawPatches: [],
387 get patches() {
388 return this.rawPatches.map(function (_a) {
389 var a = _a[0];
390 return a;
391 });
392 },
393 get inversePatches() {
394 return this.rawPatches.map(function (_a) {
395 var _ = _a[0], b = _a[1];
396 return b;
397 });
398 },
399 stop: function () {
400 if (disposer)
401 disposer();
402 disposer = null;
403 },
404 resume: resume,
405 replay: function (target) {
406 applyPatch(target || subject, recorder.patches);
407 },
408 undo: function (target) {
409 applyPatch(target || subject, recorder.inversePatches.slice().reverse());
410 }
411 };
412 resume();
413 return recorder;
414}
415/**
416 * The inverse of `unprotect`
417 *
418 * @export
419 * @param {IStateTreeNode} target
420 *
421 */
422function protect(target) {
423 // check all arguments
424 if (process.env.NODE_ENV !== "production") {
425 if (!isStateTreeNode(target))
426 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
427 }
428 var node = getStateTreeNode(target);
429 if (!node.isRoot)
430 fail("`protect` can only be invoked on root nodes");
431 node.isProtectionEnabled = true;
432}
433/**
434 * By default it is not allowed to directly modify a model. Models can only be modified through actions.
435 * However, in some cases you don't care about the advantages (like replayability, traceability, etc) this yields.
436 * For example because you are building a PoC or don't have any middleware attached to your tree.
437 *
438 * In that case you can disable this protection by calling `unprotect` on the root of your tree.
439 *
440 * @example
441 * const Todo = types.model({
442 * done: false,
443 * toggle() {
444 * this.done = !this.done
445 * }
446 * })
447 *
448 * const todo = new Todo()
449 * todo.done = true // OK
450 * protect(todo)
451 * todo.done = false // throws!
452 * todo.toggle() // OK
453 */
454function unprotect(target) {
455 // check all arguments
456 if (process.env.NODE_ENV !== "production") {
457 if (!isStateTreeNode(target))
458 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
459 }
460 var node = getStateTreeNode(target);
461 if (!node.isRoot)
462 fail("`unprotect` can only be invoked on root nodes");
463 node.isProtectionEnabled = false;
464}
465/**
466 * Returns true if the object is in protected mode, @see protect
467 */
468function isProtected(target) {
469 return getStateTreeNode(target).isProtected;
470}
471/**
472 * Applies a snapshot to a given model instances. Patch and snapshot listeners will be invoked as usual.
473 *
474 * @export
475 * @param {Object} target
476 * @param {Object} snapshot
477 * @returns
478 */
479function applySnapshot(target, snapshot) {
480 // check all arguments
481 if (process.env.NODE_ENV !== "production") {
482 if (!isStateTreeNode(target))
483 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
484 }
485 return getStateTreeNode(target).applySnapshot(snapshot);
486}
487/**
488 * Calculates a snapshot from the given model instance. The snapshot will always reflect the latest state but use
489 * structural sharing where possible. Doesn't require MobX transactions to be completed.
490 *
491 * @export
492 * @param {Object} target
493 * @returns {*}
494 */
495function getSnapshot(target) {
496 // check all arguments
497 if (process.env.NODE_ENV !== "production") {
498 if (!isStateTreeNode(target))
499 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
500 }
501 return getStateTreeNode(target).snapshot;
502}
503/**
504 * Given a model instance, returns `true` if the object has a parent, that is, is part of another object, map or array
505 *
506 * @export
507 * @param {Object} target
508 * @param {number} depth = 1, how far should we look upward?
509 * @returns {boolean}
510 */
511function hasParent(target, depth) {
512 if (depth === void 0) { depth = 1; }
513 // check all arguments
514 if (process.env.NODE_ENV !== "production") {
515 if (!isStateTreeNode(target))
516 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
517 if (typeof depth !== "number")
518 fail("expected second argument to be a number, got " + depth + " instead");
519 if (depth < 0)
520 fail("Invalid depth: " + depth + ", should be >= 1");
521 }
522 var parent = getStateTreeNode(target).parent;
523 while (parent) {
524 if (--depth === 0)
525 return true;
526 parent = parent.parent;
527 }
528 return false;
529}
530/**
531 * Returns the immediate parent of this object, or null.
532 *
533 * Note that the immediate parent can be either an object, map or array, and
534 * doesn't necessarily refer to the parent model
535 *
536 * @export
537 * @param {Object} target
538 * @param {number} depth = 1, how far should we look upward?
539 * @returns {*}
540 */
541function getParent(target, depth) {
542 if (depth === void 0) { depth = 1; }
543 // check all arguments
544 if (process.env.NODE_ENV !== "production") {
545 if (!isStateTreeNode(target))
546 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
547 if (typeof depth !== "number")
548 fail("expected second argument to be a number, got " + depth + " instead");
549 if (depth < 0)
550 fail("Invalid depth: " + depth + ", should be >= 1");
551 }
552 var d = depth;
553 var parent = getStateTreeNode(target).parent;
554 while (parent) {
555 if (--d === 0)
556 return parent.storedValue;
557 parent = parent.parent;
558 }
559 return fail("Failed to find the parent of " + getStateTreeNode(target) + " at depth " + depth);
560}
561/**
562 * Given an object in a model tree, returns the root object of that tree
563 *
564 * @export
565 * @param {Object} target
566 * @returns {*}
567 */
568function getRoot(target) {
569 // check all arguments
570 if (process.env.NODE_ENV !== "production") {
571 if (!isStateTreeNode(target))
572 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
573 }
574 return getStateTreeNode(target).root.storedValue;
575}
576/**
577 * Returns the path of the given object in the model tree
578 *
579 * @export
580 * @param {Object} target
581 * @returns {string}
582 */
583function getPath(target) {
584 // check all arguments
585 if (process.env.NODE_ENV !== "production") {
586 if (!isStateTreeNode(target))
587 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
588 }
589 return getStateTreeNode(target).path;
590}
591/**
592 * Returns the path of the given object as unescaped string array
593 *
594 * @export
595 * @param {Object} target
596 * @returns {string[]}
597 */
598function getPathParts(target) {
599 // check all arguments
600 if (process.env.NODE_ENV !== "production") {
601 if (!isStateTreeNode(target))
602 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
603 }
604 return splitJsonPath(getStateTreeNode(target).path);
605}
606/**
607 * Returns true if the given object is the root of a model tree
608 *
609 * @export
610 * @param {Object} target
611 * @returns {boolean}
612 */
613function isRoot(target) {
614 // check all arguments
615 if (process.env.NODE_ENV !== "production") {
616 if (!isStateTreeNode(target))
617 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
618 }
619 return getStateTreeNode(target).isRoot;
620}
621/**
622 * Resolves a path relatively to a given object.
623 * Returns undefined if no value can be found.
624 *
625 * @export
626 * @param {Object} target
627 * @param {string} path - escaped json path
628 * @returns {*}
629 */
630function resolvePath(target, path) {
631 // check all arguments
632 if (process.env.NODE_ENV !== "production") {
633 if (!isStateTreeNode(target))
634 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
635 if (typeof path !== "string")
636 fail("expected second argument to be a number, got " + path + " instead");
637 }
638 var node = getStateTreeNode(target).resolve(path);
639 return node ? node.value : undefined;
640}
641/**
642 * Resolves a model instance given a root target, the type and the identifier you are searching for.
643 * Returns undefined if no value can be found.
644 *
645 * @export
646 * @param {IType<any, any>} type
647 * @param {IStateTreeNode} target
648 * @param {(string | number)} identifier
649 * @returns {*}
650 */
651function resolveIdentifier(type, target, identifier) {
652 // check all arguments
653 if (process.env.NODE_ENV !== "production") {
654 if (!isType(type))
655 fail("expected first argument to be a mobx-state-tree type, got " + type + " instead");
656 if (!isStateTreeNode(target))
657 fail("expected second argument to be a mobx-state-tree node, got " + target + " instead");
658 if (!(typeof identifier === "string" || typeof identifier === "number"))
659 fail("expected third argument to be a string or number, got " + identifier + " instead");
660 }
661 var node = getStateTreeNode(target).root.identifierCache.resolve(type, "" + identifier);
662 return node ? node.value : undefined;
663}
664/**
665 *
666 *
667 * @export
668 * @param {Object} target
669 * @param {string} path
670 * @returns {*}
671 */
672function tryResolve(target, path) {
673 // check all arguments
674 if (process.env.NODE_ENV !== "production") {
675 if (!isStateTreeNode(target))
676 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
677 if (typeof path !== "string")
678 fail("expected second argument to be a string, got " + path + " instead");
679 }
680 var node = getStateTreeNode(target).resolve(path, false);
681 if (node === undefined)
682 return undefined;
683 return node ? node.value : undefined;
684}
685/**
686 * Given two state tree nodes that are part of the same tree,
687 * returns the shortest jsonpath needed to navigate from the one to the other
688 *
689 * @export
690 * @param {IStateTreeNode} base
691 * @param {IStateTreeNode} target
692 * @returns {string}
693 */
694function getRelativePath(base, target) {
695 // check all arguments
696 if (process.env.NODE_ENV !== "production") {
697 if (!isStateTreeNode(target))
698 fail("expected second argument to be a mobx-state-tree node, got " + target + " instead");
699 if (!isStateTreeNode(base))
700 fail("expected first argument to be a mobx-state-tree node, got " + base + " instead");
701 }
702 return getStateTreeNode(base).getRelativePathTo(getStateTreeNode(target));
703}
704/**
705 * Returns a deep copy of the given state tree node as new tree.
706 * Short hand for `snapshot(x) = getType(x).create(getSnapshot(x))`
707 *
708 * _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_
709 *
710 * @export
711 * @template T
712 * @param {T} source
713 * @param {boolean | any} 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.
714 * @returns {T}
715 */
716function clone(source, keepEnvironment) {
717 if (keepEnvironment === void 0) { keepEnvironment = true; }
718 // check all arguments
719 if (process.env.NODE_ENV !== "production") {
720 if (!isStateTreeNode(source))
721 fail("expected first argument to be a mobx-state-tree node, got " + source + " instead");
722 }
723 var node = getStateTreeNode(source);
724 return node.type.create(node.snapshot, keepEnvironment === true
725 ? node.root._environment
726 : keepEnvironment === false ? undefined : keepEnvironment); // it's an object or something else
727}
728/**
729 * Removes a model element from the state tree, and let it live on as a new state tree
730 */
731function detach(target) {
732 // check all arguments
733 if (process.env.NODE_ENV !== "production") {
734 if (!isStateTreeNode(target))
735 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
736 }
737 getStateTreeNode(target).detach();
738 return target;
739}
740/**
741 * Removes a model element from the state tree, and mark it as end-of-life; the element should not be used anymore
742 */
743function destroy(target) {
744 // check all arguments
745 if (process.env.NODE_ENV !== "production") {
746 if (!isStateTreeNode(target))
747 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
748 }
749 var node = getStateTreeNode(target);
750 if (node.isRoot)
751 node.die();
752 else
753 node.parent.removeChild(node.subpath);
754}
755/**
756 * Returns true if the given state tree node is not killed yet.
757 * This means that the node is still a part of a tree, and that `destroy`
758 * has not been called. If a node is not alive anymore, the only thing one can do with it
759 * is requesting it's last path and snapshot
760 *
761 * @export
762 * @param {IStateTreeNode} target
763 * @returns {boolean}
764 */
765function isAlive(target) {
766 // check all arguments
767 if (process.env.NODE_ENV !== "production") {
768 if (!isStateTreeNode(target))
769 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
770 }
771 return getStateTreeNode(target).isAlive;
772}
773/**
774 * Use this utility to register a function that should be called whenever the
775 * targeted state tree node is destroyed. This is a useful alternative to managing
776 * cleanup methods yourself using the `beforeDestroy` hook.
777 *
778 * @example
779 * const Todo = types.model({
780 * title: types.string
781 * }, {
782 * afterCreate() {
783 * const autoSaveDisposer = reaction(
784 * () => getSnapshot(this),
785 * snapshot => sendSnapshotToServerSomehow(snapshot)
786 * )
787 * // stop sending updates to server if this
788 * // instance is destroyed
789 * addDisposer(this, autoSaveDisposer)
790 * }
791 * })
792 *
793 * @export
794 * @param {IStateTreeNode} target
795 * @param {() => void} disposer
796 */
797function addDisposer(target, disposer) {
798 // check all arguments
799 if (process.env.NODE_ENV !== "production") {
800 if (!isStateTreeNode(target))
801 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
802 if (typeof disposer !== "function")
803 fail("expected second argument to be a function, got " + disposer + " instead");
804 }
805 getStateTreeNode(target).addDisposer(disposer);
806}
807/**
808 * Returns the environment of the current state tree. For more info on environments,
809 * see [Dependency injection](https://github.com/mobxjs/mobx-state-tree#dependency-injection)
810 *
811 * Returns an empty environment if the tree wasn't initialized with an environment
812 *
813 * @export
814 * @param {IStateTreeNode} target
815 * @returns {*}
816 */
817function getEnv(target) {
818 // check all arguments
819 if (process.env.NODE_ENV !== "production") {
820 if (!isStateTreeNode(target))
821 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
822 }
823 var node = getStateTreeNode(target);
824 var env = node.root._environment;
825 if (!!!env)
826 return EMPTY_OBJECT;
827 return env;
828}
829/**
830 * Performs a depth first walk through a tree
831 */
832function walk(target, processor) {
833 // check all arguments
834 if (process.env.NODE_ENV !== "production") {
835 if (!isStateTreeNode(target))
836 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
837 if (typeof processor !== "function")
838 fail("expected second argument to be a function, got " + processor + " instead");
839 }
840 var node = getStateTreeNode(target);
841 // tslint:disable-next-line:no_unused-variable
842 node.getChildren().forEach(function (child) {
843 if (isStateTreeNode(child.storedValue))
844 walk(child.storedValue, processor);
845 });
846 processor(node.storedValue);
847}
848
849var nextActionId = 1;
850var currentActionContext = null;
851function getNextActionId() {
852 return nextActionId++;
853}
854function runWithActionContext(context, fn) {
855 var node = getStateTreeNode(context.context);
856 var baseIsRunningAction = node._isRunningAction;
857 var prevContext = currentActionContext;
858 node.assertAlive();
859 node._isRunningAction = true;
860 currentActionContext = context;
861 try {
862 return runMiddleWares(node, context, fn);
863 }
864 finally {
865 currentActionContext = prevContext;
866 node._isRunningAction = baseIsRunningAction;
867 }
868}
869function getActionContext() {
870 if (!currentActionContext)
871 return fail("Not running an action!");
872 return currentActionContext;
873}
874function createActionInvoker(target, name, fn) {
875 var wrappedFn = action(name, fn);
876 wrappedFn.$mst_middleware = fn.$mst_middleware;
877 return function () {
878 var id = getNextActionId();
879 return runWithActionContext({
880 type: "action",
881 name: name,
882 id: id,
883 args: argsToArray(arguments),
884 context: target,
885 tree: getRoot(target),
886 rootId: currentActionContext ? currentActionContext.rootId : id,
887 parentId: currentActionContext ? currentActionContext.id : 0
888 }, wrappedFn);
889 };
890}
891/**
892 * Middleware can be used to intercept any action is invoked on the subtree where it is attached.
893 * If a tree is protected (by default), this means that any mutation of the tree will pass through your middleware.
894 *
895 * For more details, see the [middleware docs](docs/middleware.md)
896 *
897 * @export
898 * @param {IStateTreeNode} target
899 * @param {(action: IRawActionCall, next: (call: IRawActionCall) => any) => any} middleware
900 * @returns {IDisposer}
901 */
902function addMiddleware(target, middleware) {
903 var node = getStateTreeNode(target);
904 if (process.env.NODE_ENV !== "production") {
905 if (!node.isProtectionEnabled)
906 console.warn("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`");
907 }
908 return node.addMiddleWare(middleware);
909}
910/**
911 * Binds middleware to a specific action
912 *
913 * @example
914 * type.actions(self => {
915 * function takeA____() {
916 * self.toilet.donate()
917 * self.wipe()
918 * self.wipe()
919 * self.toilet.flush()
920 * }
921 * return {
922 * takeA____: decorate(atomic, takeA____)
923 * }
924 * })
925 *
926 * @export
927 * @template T
928 * @param {IMiddlewareHandler} middleware
929 * @param Function} fn
930 * @returns the original function
931 */
932function decorate(middleware, fn) {
933 if (fn.$mst_middleware)
934 fn.$mst_middleware.push(middleware);
935 else
936 fn.$mst_middleware = [middleware];
937 return fn;
938}
939function collectMiddlewareHandlers(node, baseCall, fn) {
940 var handlers = fn.$mst_middleware || [];
941 var n = node;
942 // Find all middlewares. Optimization: cache this?
943 while (n) {
944 handlers = handlers.concat(n.middlewares);
945 n = n.parent;
946 }
947 return handlers;
948}
949function runMiddleWares(node, baseCall, originalFn) {
950 var handlers = collectMiddlewareHandlers(node, baseCall, originalFn);
951 // Short circuit
952 if (!handlers.length)
953 return originalFn.apply(null, baseCall.args);
954 function runNextMiddleware(call) {
955 var handler = handlers.shift(); // Optimization: counter instead of shift is probably faster
956 if (handler)
957 return handler(call, runNextMiddleware);
958 else
959 return originalFn.apply(null, baseCall.args);
960 }
961 return runNextMiddleware(baseCall);
962}
963
964var IdentifierCache = /** @class */ (function () {
965 function IdentifierCache() {
966 this.cache = observable.map();
967 }
968 IdentifierCache.prototype.addNodeToCache = function (node) {
969 if (node.identifierAttribute) {
970 var identifier = node.identifier;
971 if (!this.cache.has(identifier)) {
972 this.cache.set(identifier, observable.shallowArray());
973 }
974 var set = this.cache.get(identifier);
975 if (set.indexOf(node) !== -1)
976 fail("Already registered");
977 set.push(node);
978 }
979 return this;
980 };
981 IdentifierCache.prototype.mergeCache = function (node) {
982 var _this = this;
983 node.identifierCache.cache.values().forEach(function (nodes) {
984 return nodes.forEach(function (child) {
985 _this.addNodeToCache(child);
986 });
987 });
988 };
989 IdentifierCache.prototype.notifyDied = function (node) {
990 if (node.identifierAttribute) {
991 var set = this.cache.get(node.identifier);
992 if (set)
993 set.remove(node);
994 }
995 };
996 IdentifierCache.prototype.splitCache = function (node) {
997 var res = new IdentifierCache();
998 var basePath = node.path;
999 this.cache.values().forEach(function (nodes) {
1000 for (var i = nodes.length - 1; i >= 0; i--) {
1001 if (nodes[i].path.indexOf(basePath) === 0) {
1002 res.addNodeToCache(nodes[i]);
1003 nodes.splice(i, 1);
1004 }
1005 }
1006 });
1007 return res;
1008 };
1009 IdentifierCache.prototype.resolve = function (type, identifier) {
1010 var set = this.cache.get(identifier);
1011 if (!set)
1012 return null;
1013 var matches = set.filter(function (candidate) { return type.isAssignableFrom(candidate.type); });
1014 switch (matches.length) {
1015 case 0:
1016 return null;
1017 case 1:
1018 return matches[0];
1019 default:
1020 return fail("Cannot resolve a reference to type '" + type.name + "' with id: '" + identifier + "' unambigously, there are multiple candidates: " + matches
1021 .map(function (n) { return n.path; })
1022 .join(", "));
1023 }
1024 };
1025 return IdentifierCache;
1026}());
1027
1028var nextNodeId = 1;
1029var Node = /** @class */ (function () {
1030 function Node(type, parent, subpath, environment, initialValue, createNewInstance, finalizeNewInstance) {
1031 if (createNewInstance === void 0) { createNewInstance = identity; }
1032 if (finalizeNewInstance === void 0) { finalizeNewInstance = noop; }
1033 var _this = this;
1034 // optimization: these fields make MST memory expensive for primitives. Most can be initialized lazily, or with EMPTY_ARRAY on prototype
1035 this.nodeId = ++nextNodeId;
1036 this._parent = null;
1037 this.subpath = "";
1038 this.isProtectionEnabled = true;
1039 this.identifierAttribute = undefined; // not to be modified directly, only through model initialization
1040 this._environment = undefined;
1041 this._isRunningAction = false; // only relevant for root
1042 this._autoUnbox = true; // unboxing is disabled when reading child nodes
1043 this._isAlive = true; // optimization: use binary flags for all these switches
1044 this._isDetaching = false;
1045 this.middlewares = [];
1046 this.snapshotSubscribers = [];
1047 // TODO: split patches in two; patch and reversePatch
1048 this.patchSubscribers = [];
1049 this.disposers = [];
1050 this.type = type;
1051 this._parent = parent;
1052 this.subpath = subpath;
1053 this._environment = environment;
1054 this.unbox = this.unbox.bind(this);
1055 this.storedValue = createNewInstance(initialValue);
1056 var canAttachTreeNode = canAttachNode(this.storedValue);
1057 // Optimization: this does not need to be done per instance
1058 // if some pieces from createActionInvoker are extracted
1059 this.applyPatches = createActionInvoker(this.storedValue, "@APPLY_PATCHES", function (patches) {
1060 patches.forEach(function (patch) {
1061 var parts = splitJsonPath(patch.path);
1062 var node = _this.resolvePath(parts.slice(0, -1));
1063 node.applyPatchLocally(parts[parts.length - 1], patch);
1064 });
1065 }).bind(this.storedValue);
1066 this.applySnapshot = createActionInvoker(this.storedValue, "@APPLY_SNAPSHOT", function (snapshot) {
1067 // if the snapshot is the same as the current one, avoid performing a reconcile
1068 if (snapshot === _this.snapshot)
1069 return;
1070 // else, apply it by calling the type logic
1071 return _this.type.applySnapshot(_this, snapshot);
1072 }).bind(this.storedValue);
1073 if (!parent)
1074 this.identifierCache = new IdentifierCache();
1075 if (canAttachTreeNode)
1076 addHiddenFinalProp(this.storedValue, "$treenode", this);
1077 var sawException = true;
1078 try {
1079 if (canAttachTreeNode)
1080 addHiddenFinalProp(this.storedValue, "toJSON", toJSON);
1081 this._isRunningAction = true;
1082 finalizeNewInstance(this, initialValue);
1083 this._isRunningAction = false;
1084 if (parent)
1085 parent.root.identifierCache.addNodeToCache(this);
1086 else
1087 this.identifierCache.addNodeToCache(this);
1088 this.fireHook("afterCreate");
1089 if (parent)
1090 this.fireHook("afterAttach");
1091 sawException = false;
1092 }
1093 finally {
1094 if (sawException) {
1095 // short-cut to die the instance, to avoid the snapshot computed starting to throw...
1096 this._isAlive = false;
1097 }
1098 }
1099 // optimization: don't keep the snapshot by default alive with a reaction by default
1100 // in prod mode. This saves lot of GC overhead (important for e.g. React Native)
1101 // if the feature is not actively used
1102 // downside; no structural sharing if getSnapshot is called incidently
1103 var snapshotDisposer = reaction(function () { return _this.snapshot; }, function (snapshot) {
1104 _this.emitSnapshot(snapshot);
1105 });
1106 snapshotDisposer.onError(function (e) {
1107 throw e;
1108 });
1109 this.addDisposer(snapshotDisposer);
1110 }
1111 Object.defineProperty(Node.prototype, "identifier", {
1112 get: function () {
1113 return this.identifierAttribute ? this.storedValue[this.identifierAttribute] : null;
1114 },
1115 enumerable: true,
1116 configurable: true
1117 });
1118 Object.defineProperty(Node.prototype, "path", {
1119 /*
1120 * Returnes (escaped) path representation as string
1121 */
1122 get: function () {
1123 if (!this.parent)
1124 return "";
1125 return this.parent.path + "/" + escapeJsonPath(this.subpath);
1126 },
1127 enumerable: true,
1128 configurable: true
1129 });
1130 Object.defineProperty(Node.prototype, "isRoot", {
1131 get: function () {
1132 return this.parent === null;
1133 },
1134 enumerable: true,
1135 configurable: true
1136 });
1137 Object.defineProperty(Node.prototype, "parent", {
1138 get: function () {
1139 return this._parent;
1140 },
1141 enumerable: true,
1142 configurable: true
1143 });
1144 Object.defineProperty(Node.prototype, "root", {
1145 // TODO: make computed
1146 get: function () {
1147 // future optimization: store root ref in the node and maintain it
1148 var p, r = this;
1149 while ((p = r.parent))
1150 r = p;
1151 return r;
1152 },
1153 enumerable: true,
1154 configurable: true
1155 });
1156 // TODO: lift logic outside this file
1157 Node.prototype.getRelativePathTo = function (target) {
1158 // PRE condition target is (a child of) base!
1159 if (this.root !== target.root)
1160 fail("Cannot calculate relative path: objects '" + this + "' and '" + target + "' are not part of the same object tree");
1161 var baseParts = splitJsonPath(this.path);
1162 var targetParts = splitJsonPath(target.path);
1163 var common = 0;
1164 for (; common < baseParts.length; common++) {
1165 if (baseParts[common] !== targetParts[common])
1166 break;
1167 }
1168 // TODO: assert that no targetParts paths are "..", "." or ""!
1169 return (baseParts.slice(common).map(function (_) { return ".."; }).join("/") +
1170 joinJsonPath(targetParts.slice(common)));
1171 };
1172 Node.prototype.resolve = function (path, failIfResolveFails) {
1173 if (failIfResolveFails === void 0) { failIfResolveFails = true; }
1174 return this.resolvePath(splitJsonPath(path), failIfResolveFails);
1175 };
1176 Node.prototype.resolvePath = function (pathParts, failIfResolveFails) {
1177 if (failIfResolveFails === void 0) { failIfResolveFails = true; }
1178 // counter part of getRelativePath
1179 // note that `../` is not part of the JSON pointer spec, which is actually a prefix format
1180 // in json pointer: "" = current, "/a", attribute a, "/" is attribute "" etc...
1181 // so we treat leading ../ apart...
1182 var current = this;
1183 for (var i = 0; i < pathParts.length; i++) {
1184 if (pathParts[i] === "")
1185 current = current.root;
1186 else if (pathParts[i] === ".." // '/bla' or 'a//b' splits to empty strings
1187 )
1188 current = current.parent;
1189 else if (pathParts[i] === "." || pathParts[i] === "")
1190 continue;
1191 else if (current) {
1192 current = current.getChildNode(pathParts[i]);
1193 continue;
1194 }
1195 if (!current) {
1196 if (failIfResolveFails)
1197 return fail("Could not resolve '" + pathParts[i] + "' in '" + joinJsonPath(pathParts.slice(0, i - 1)) + "', path of the patch does not resolve");
1198 else
1199 return undefined;
1200 }
1201 }
1202 return current;
1203 };
1204 Object.defineProperty(Node.prototype, "value", {
1205 get: function () {
1206 if (!this._isAlive)
1207 return undefined;
1208 return this.type.getValue(this);
1209 },
1210 enumerable: true,
1211 configurable: true
1212 });
1213 Object.defineProperty(Node.prototype, "isAlive", {
1214 get: function () {
1215 return this._isAlive;
1216 },
1217 enumerable: true,
1218 configurable: true
1219 });
1220 Node.prototype.die = function () {
1221 if (this._isDetaching)
1222 return;
1223 if (isStateTreeNode(this.storedValue)) {
1224 walk(this.storedValue, function (child) { return getStateTreeNode(child).aboutToDie(); });
1225 walk(this.storedValue, function (child) { return getStateTreeNode(child).finalizeDeath(); });
1226 }
1227 };
1228 Node.prototype.aboutToDie = function () {
1229 this.disposers.splice(0).forEach(function (f) { return f(); });
1230 this.fireHook("beforeDestroy");
1231 };
1232 Node.prototype.finalizeDeath = function () {
1233 // invariant: not called directly but from "die"
1234 this.root.identifierCache.notifyDied(this);
1235 var self = this;
1236 var oldPath = this.path;
1237 addReadOnlyProp(this, "snapshot", this.snapshot); // kill the computed prop and just store the last snapshot
1238 this.patchSubscribers.splice(0);
1239 this.snapshotSubscribers.splice(0);
1240 this.patchSubscribers.splice(0);
1241 this._isAlive = false;
1242 this._parent = null;
1243 this.subpath = "";
1244 // This is quite a hack, once interceptable objects / arrays / maps are extracted from mobx,
1245 // we could express this in a much nicer way
1246 // TODO: should be possible to obtain id's still...
1247 Object.defineProperty(this.storedValue, "$mobx", {
1248 get: function () {
1249 fail("This object has died and is no longer part of a state tree. It cannot be used anymore. The object (of type '" + self
1250 .type
1251 .name + "') used to live at '" + oldPath + "'. It is possible to access the last snapshot of this object using 'getSnapshot', or to create a fresh copy using 'clone'. If you want to remove an object from the tree without killing it, use 'detach' instead.");
1252 }
1253 });
1254 };
1255 Node.prototype.assertAlive = function () {
1256 if (!this._isAlive)
1257 fail(this + " cannot be used anymore as it has died; it has been removed from a state tree. If you want to remove an element from a tree and let it live on, use 'detach' or 'clone' the value");
1258 };
1259 Object.defineProperty(Node.prototype, "snapshot", {
1260 get: function () {
1261 if (!this._isAlive)
1262 return undefined;
1263 // advantage of using computed for a snapshot is that nicely respects transactions etc.
1264 // Optimization: only freeze on dev builds
1265 return freeze(this.type.getSnapshot(this));
1266 },
1267 enumerable: true,
1268 configurable: true
1269 });
1270 Node.prototype.onSnapshot = function (onChange) {
1271 return registerEventHandler(this.snapshotSubscribers, onChange);
1272 };
1273 Node.prototype.emitSnapshot = function (snapshot) {
1274 this.snapshotSubscribers.forEach(function (f) { return f(snapshot); });
1275 };
1276 Node.prototype.applyPatchLocally = function (subpath, patch) {
1277 this.assertWritable();
1278 this.type.applyPatchLocally(this, subpath, patch);
1279 };
1280 Node.prototype.onPatch = function (handler) {
1281 return registerEventHandler(this.patchSubscribers, handler);
1282 };
1283 Node.prototype.emitPatch = function (basePatch, source) {
1284 if (this.patchSubscribers.length) {
1285 var localizedPatch = extend({}, basePatch, {
1286 path: source.path.substr(this.path.length) + "/" + basePatch.path // calculate the relative path of the patch
1287 });
1288 var _a = splitPatch(localizedPatch), patch_1 = _a[0], reversePatch_1 = _a[1];
1289 this.patchSubscribers.forEach(function (f) { return f(patch_1, reversePatch_1); });
1290 }
1291 if (this.parent)
1292 this.parent.emitPatch(basePatch, source);
1293 };
1294 Node.prototype.setParent = function (newParent, subpath) {
1295 if (subpath === void 0) { subpath = null; }
1296 if (this.parent === newParent && this.subpath === subpath)
1297 return;
1298 if (newParent) {
1299 if (this._parent && newParent !== this._parent) {
1300 fail("A node cannot exists twice in the state tree. Failed to add " + this + " to path '" + newParent.path + "/" + subpath + "'.");
1301 }
1302 if (!this._parent && newParent.root === this) {
1303 fail("A state tree is not allowed to contain itself. Cannot assign " + this + " to path '" + newParent.path + "/" + subpath + "'");
1304 }
1305 if (!this._parent &&
1306 !!this._environment &&
1307 this._environment !== newParent._environment) {
1308 fail("A state tree cannot be made part of another state tree as long as their environments are different.");
1309 }
1310 }
1311 if (this.parent && !newParent) {
1312 this.die();
1313 }
1314 else {
1315 this.subpath = subpath || "";
1316 if (newParent && newParent !== this._parent) {
1317 newParent.root.identifierCache.mergeCache(this);
1318 this._parent = newParent;
1319 this.fireHook("afterAttach");
1320 }
1321 }
1322 };
1323 Node.prototype.addDisposer = function (disposer) {
1324 this.disposers.unshift(disposer);
1325 };
1326 Node.prototype.isRunningAction = function () {
1327 if (this._isRunningAction)
1328 return true;
1329 if (this.isRoot)
1330 return false;
1331 return this.parent.isRunningAction();
1332 };
1333 Node.prototype.addMiddleWare = function (handler) {
1334 return registerEventHandler(this.middlewares, handler);
1335 };
1336 Node.prototype.getChildNode = function (subpath) {
1337 this.assertAlive();
1338 this._autoUnbox = false;
1339 var res = this.type.getChildNode(this, subpath);
1340 this._autoUnbox = true;
1341 return res;
1342 };
1343 Node.prototype.getChildren = function () {
1344 this.assertAlive();
1345 this._autoUnbox = false;
1346 var res = this.type.getChildren(this);
1347 this._autoUnbox = true;
1348 return res;
1349 };
1350 Node.prototype.getChildType = function (key) {
1351 return this.type.getChildType(key);
1352 };
1353 Object.defineProperty(Node.prototype, "isProtected", {
1354 get: function () {
1355 return this.root.isProtectionEnabled;
1356 },
1357 enumerable: true,
1358 configurable: true
1359 });
1360 Node.prototype.assertWritable = function () {
1361 this.assertAlive();
1362 if (!this.isRunningAction() && this.isProtected) {
1363 fail("Cannot modify '" + this + "', the object is protected and can only be modified by using an action.");
1364 }
1365 };
1366 Node.prototype.removeChild = function (subpath) {
1367 this.type.removeChild(this, subpath);
1368 };
1369 Node.prototype.detach = function () {
1370 if (!this._isAlive)
1371 fail("Error while detaching, node is not alive.");
1372 if (this.isRoot)
1373 return;
1374 else {
1375 this.fireHook("beforeDetach");
1376 this._environment = this.root._environment; // make backup of environment
1377 this._isDetaching = true;
1378 this.identifierCache = this.root.identifierCache.splitCache(this);
1379 this.parent.removeChild(this.subpath);
1380 this._parent = null;
1381 this.subpath = "";
1382 this._isDetaching = false;
1383 }
1384 };
1385 Node.prototype.unbox = function (childNode) {
1386 if (childNode && this._autoUnbox === true)
1387 return childNode.value;
1388 return childNode;
1389 };
1390 Node.prototype.fireHook = function (name) {
1391 var fn = this.storedValue && typeof this.storedValue === "object" && this.storedValue[name];
1392 if (typeof fn === "function")
1393 fn.apply(this.storedValue);
1394 };
1395 Node.prototype.toString = function () {
1396 var identifier = this.identifier ? "(id: " + this.identifier + ")" : "";
1397 return this.type.name + "@" + (this.path || "<root>") + identifier + (this.isAlive
1398 ? ""
1399 : "[dead]");
1400 };
1401 __decorate([
1402 observable
1403 ], Node.prototype, "_parent", void 0);
1404 __decorate([
1405 observable
1406 ], Node.prototype, "subpath", void 0);
1407 __decorate([
1408 computed
1409 ], Node.prototype, "path", null);
1410 __decorate([
1411 computed
1412 ], Node.prototype, "value", null);
1413 __decorate([
1414 computed
1415 ], Node.prototype, "snapshot", null);
1416 return Node;
1417}());
1418/**
1419 * Returns true if the given value is a node in a state tree.
1420 * More precisely, that is, if the value is an instance of a
1421 * `types.model`, `types.array` or `types.map`.
1422 *
1423 * @export
1424 * @param {*} value
1425 * @returns {value is IStateTreeNode}
1426 */
1427function isStateTreeNode(value) {
1428 return !!(value && value.$treenode);
1429}
1430function getStateTreeNode(value) {
1431 if (isStateTreeNode(value))
1432 return value.$treenode;
1433 else
1434 return fail("Value " + value + " is no MST Node");
1435}
1436function canAttachNode(value) {
1437 return (value &&
1438 typeof value === "object" &&
1439 !(value instanceof Date) &&
1440 !isStateTreeNode(value) &&
1441 !Object.isFrozen(value));
1442}
1443function toJSON() {
1444 return getStateTreeNode(this).snapshot;
1445}
1446function createNode(type, parent, subpath, environment, initialValue, createNewInstance, finalizeNewInstance) {
1447 if (createNewInstance === void 0) { createNewInstance = identity; }
1448 if (finalizeNewInstance === void 0) { finalizeNewInstance = noop; }
1449 if (isStateTreeNode(initialValue)) {
1450 var targetNode = getStateTreeNode(initialValue);
1451 if (!targetNode.isRoot)
1452 fail("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
1453 ? parent.path
1454 : "") + "/" + subpath + "', but it lives already at '" + targetNode.path + "'");
1455 targetNode.setParent(parent, subpath);
1456 return targetNode;
1457 }
1458 // tslint:disable-next-line:no_unused-variable
1459 return new Node(type, parent, subpath, environment, initialValue, createNewInstance, finalizeNewInstance);
1460}
1461
1462function safeStringify(value) {
1463 try {
1464 return JSON.stringify(value);
1465 }
1466 catch (e) {
1467 return "<Unserializable: " + e + ">";
1468 }
1469}
1470function prettyPrintValue(value) {
1471 return typeof value === "function"
1472 ? "<function" + (value.name ? " " + value.name : "") + ">"
1473 : isStateTreeNode(value) ? "<" + value + ">" : "`" + safeStringify(value) + "`";
1474}
1475function toErrorString(error) {
1476 var value = error.value;
1477 var type = error.context[error.context.length - 1].type;
1478 var fullPath = error.context.map(function (_a) {
1479 var path = _a.path;
1480 return path;
1481 }).filter(function (path) { return path.length > 0; }).join("/");
1482 var pathPrefix = fullPath.length > 0 ? "at path \"/" + fullPath + "\" " : "";
1483 var currentTypename = isStateTreeNode(value)
1484 ? "value of type " + getStateTreeNode(value).type.name + ":"
1485 : isPrimitive(value) ? "value" : "snapshot";
1486 var isSnapshotCompatible = type && isStateTreeNode(value) && type.is(getStateTreeNode(value).snapshot);
1487 return ("" + pathPrefix + currentTypename + " " + prettyPrintValue(value) + " is not assignable " + (type
1488 ? "to type: `" + type.name + "`"
1489 : "") +
1490 (error.message ? " (" + error.message + ")" : "") +
1491 (type
1492 ? isPrimitiveType(type)
1493 ? "."
1494 : ", expected an instance of `" + type.name + "` or a snapshot like `" + type.describe() + "` instead." +
1495 (isSnapshotCompatible
1496 ? " (Note that a snapshot of the provided value is compatible with the targeted type)"
1497 : "")
1498 : "."));
1499}
1500
1501function getContextForPath(context, path, type) {
1502 return context.concat([{ path: path, type: type }]);
1503}
1504function typeCheckSuccess() {
1505 return EMPTY_ARRAY;
1506}
1507function typeCheckFailure(context, value, message) {
1508 return [{ context: context, value: value, message: message }];
1509}
1510function flattenTypeErrors(errors) {
1511 return errors.reduce(function (a, i) { return a.concat(i); }, []);
1512}
1513// TODO; doublecheck: typecheck should only needed to be invoked from: type.create and array / map / value.property will change
1514function typecheck(type, value) {
1515 // if not in dev-mode, do not even try to run typecheck. Everything is developer fault!
1516 if (process.env.NODE_ENV === "production")
1517 return;
1518 typecheckPublic(type, value);
1519}
1520/**
1521 * Run's the typechecker on the given type.
1522 * Throws if the given value is not according the provided type specification.
1523 * Use this if you need typechecks even in a production build (by default all automatic runtime type checks will be skipped in production builds)
1524 *
1525 * @alias typecheck
1526 * @export
1527 * @param {IType<any, any>} type
1528 * @param {*} value
1529 */
1530function typecheckPublic(type, value) {
1531 var errors = type.validate(value, [{ path: "", type: type }]);
1532 if (errors.length > 0) {
1533 fail("Error while converting " + prettyPrintValue(value) + " to `" + type.name + "`:\n" +
1534 errors.map(toErrorString).join("\n"));
1535 }
1536}
1537
1538/*
1539 * A complex type produces a MST node (Node in the state tree)
1540 */
1541var ComplexType = /** @class */ (function () {
1542 function ComplexType(name) {
1543 this.isType = true;
1544 this.name = name;
1545 }
1546 ComplexType.prototype.create = function (snapshot, environment) {
1547 if (snapshot === void 0) { snapshot = this.getDefaultSnapshot(); }
1548 typecheck(this, snapshot);
1549 return this.instantiate(null, "", environment, snapshot).value;
1550 };
1551 ComplexType.prototype.isAssignableFrom = function (type) {
1552 return type === this;
1553 };
1554 ComplexType.prototype.validate = function (value, context) {
1555 if (isStateTreeNode(value)) {
1556 return getType(value) === this || this.isAssignableFrom(getType(value))
1557 ? typeCheckSuccess()
1558 : typeCheckFailure(context, value);
1559 // it is tempting to compare snapshots, but in that case we should always clone on assignments...
1560 }
1561 return this.isValidSnapshot(value, context);
1562 };
1563 ComplexType.prototype.is = function (value) {
1564 return this.validate(value, [{ path: "", type: this }]).length === 0;
1565 };
1566 ComplexType.prototype.reconcile = function (current, newValue) {
1567 if (current.snapshot === newValue)
1568 // newValue is the current snapshot of the node, noop
1569 return current;
1570 if (isStateTreeNode(newValue) && getStateTreeNode(newValue) === current)
1571 // the current node is the same as the new one
1572 return current;
1573 if (current.type === this &&
1574 isMutable(newValue) &&
1575 !isStateTreeNode(newValue) &&
1576 (!current.identifierAttribute ||
1577 current.identifier === newValue[current.identifierAttribute])) {
1578 // the newValue has no node, so can be treated like a snapshot
1579 // we can reconcile
1580 current.applySnapshot(newValue);
1581 return current;
1582 }
1583 // current node cannot be recycled in any way
1584 var parent = current.parent, subpath = current.subpath;
1585 current.die();
1586 // attempt to reuse the new one
1587 if (isStateTreeNode(newValue) && this.isAssignableFrom(getType(newValue))) {
1588 // newValue is a Node as well, move it here..
1589 var newNode = getStateTreeNode(newValue);
1590 newNode.setParent(parent, subpath);
1591 return newNode;
1592 }
1593 // nothing to do, we have to create a new node
1594 return this.instantiate(parent, subpath, current._environment, newValue);
1595 };
1596 Object.defineProperty(ComplexType.prototype, "Type", {
1597 get: function () {
1598 return fail("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`");
1599 },
1600 enumerable: true,
1601 configurable: true
1602 });
1603 Object.defineProperty(ComplexType.prototype, "SnapshotType", {
1604 get: function () {
1605 return fail("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`");
1606 },
1607 enumerable: true,
1608 configurable: true
1609 });
1610 __decorate([
1611 action
1612 ], ComplexType.prototype, "create", null);
1613 return ComplexType;
1614}());
1615var Type = /** @class */ (function (_super) {
1616 __extends(Type, _super);
1617 function Type(name) {
1618 return _super.call(this, name) || this;
1619 }
1620 Type.prototype.getValue = function (node) {
1621 return node.storedValue;
1622 };
1623 Type.prototype.getSnapshot = function (node) {
1624 return node.storedValue;
1625 };
1626 Type.prototype.getDefaultSnapshot = function () {
1627 return undefined;
1628 };
1629 Type.prototype.applySnapshot = function (node, snapshot) {
1630 fail("Immutable types do not support applying snapshots");
1631 };
1632 Type.prototype.applyPatchLocally = function (node, subpath, patch) {
1633 fail("Immutable types do not support applying patches");
1634 };
1635 Type.prototype.getChildren = function (node) {
1636 return EMPTY_ARRAY;
1637 };
1638 Type.prototype.getChildNode = function (node, key) {
1639 return fail("No child '" + key + "' available in type: " + this.name);
1640 };
1641 Type.prototype.getChildType = function (key) {
1642 return fail("No child '" + key + "' available in type: " + this.name);
1643 };
1644 Type.prototype.reconcile = function (current, newValue) {
1645 // reconcile only if type and value are still the same
1646 if (current.type === this && current.storedValue === newValue)
1647 return current;
1648 var res = this.instantiate(current.parent, current.subpath, current._environment, newValue);
1649 current.die();
1650 return res;
1651 };
1652 Type.prototype.removeChild = function (node, subpath) {
1653 return fail("No child '" + subpath + "' available in type: " + this.name);
1654 };
1655 return Type;
1656}(ComplexType));
1657
1658function mapToString() {
1659 return getStateTreeNode(this) + "(" + this.size + " items)";
1660}
1661function put(value) {
1662 if (!!!value)
1663 fail("Map.put cannot be used to set empty values");
1664 var node;
1665 if (isStateTreeNode(value)) {
1666 node = getStateTreeNode(value);
1667 }
1668 else if (isMutable(value)) {
1669 var targetType = getStateTreeNode(this).type
1670 .subType;
1671 node = getStateTreeNode(targetType.create(value));
1672 }
1673 else {
1674 return fail("Map.put can only be used to store complex values");
1675 }
1676 if (!node.identifierAttribute)
1677 fail("Map.put can only be used to store complex values that have an identifier type attribute");
1678 this.set(node.identifier, node.value);
1679 return this;
1680}
1681var MapType = /** @class */ (function (_super) {
1682 __extends(MapType, _super);
1683 function MapType(name, subType) {
1684 var _this = _super.call(this, name) || this;
1685 _this.shouldAttachNode = true;
1686 _this.flags = TypeFlags.Map;
1687 _this.createNewInstance = function () {
1688 // const identifierAttr = getIdentifierAttribute(this.subType)
1689 var map = observable.shallowMap();
1690 addHiddenFinalProp(map, "put", put);
1691 addHiddenFinalProp(map, "toString", mapToString);
1692 return map;
1693 };
1694 _this.finalizeNewInstance = function (node, snapshot) {
1695 var instance = node.storedValue;
1696 extras.interceptReads(instance, node.unbox);
1697 intercept(instance, function (c) { return _this.willChange(c); });
1698 node.applySnapshot(snapshot);
1699 observe(instance, _this.didChange);
1700 };
1701 _this.subType = subType;
1702 return _this;
1703 }
1704 MapType.prototype.instantiate = function (parent, subpath, environment, snapshot) {
1705 return createNode(this, parent, subpath, environment, snapshot, this.createNewInstance, this.finalizeNewInstance);
1706 };
1707 MapType.prototype.describe = function () {
1708 return "Map<string, " + this.subType.describe() + ">";
1709 };
1710 MapType.prototype.getChildren = function (node) {
1711 return node.storedValue.values();
1712 };
1713 MapType.prototype.getChildNode = function (node, key) {
1714 var childNode = node.storedValue.get(key);
1715 if (!childNode)
1716 fail("Not a child " + key);
1717 return childNode;
1718 };
1719 MapType.prototype.willChange = function (change) {
1720 var node = getStateTreeNode(change.object);
1721 node.assertWritable();
1722 switch (change.type) {
1723 case "update":
1724 {
1725 var newValue = change.newValue;
1726 var oldValue = change.object.get(change.name);
1727 if (newValue === oldValue)
1728 return null;
1729 typecheck(this.subType, newValue);
1730 change.newValue = this.subType.reconcile(node.getChildNode(change.name), change.newValue);
1731 this.verifyIdentifier(change.name, change.newValue);
1732 }
1733 break;
1734 case "add":
1735 {
1736 typecheck(this.subType, change.newValue);
1737 change.newValue = this.subType.instantiate(node, change.name, undefined, change.newValue);
1738 this.verifyIdentifier(change.name, change.newValue);
1739 }
1740 break;
1741 }
1742 return change;
1743 };
1744 MapType.prototype.verifyIdentifier = function (expected, node) {
1745 var identifier = node.identifier;
1746 if (identifier !== null && "" + identifier !== "" + expected)
1747 fail("A map of objects containing an identifier should always store the object under their own identifier. Trying to store key '" + identifier + "', but expected: '" + expected + "'");
1748 };
1749 MapType.prototype.getValue = function (node) {
1750 return node.storedValue;
1751 };
1752 MapType.prototype.getSnapshot = function (node) {
1753 var res = {};
1754 node.getChildren().forEach(function (childNode) {
1755 res[childNode.subpath] = childNode.snapshot;
1756 });
1757 return res;
1758 };
1759 MapType.prototype.didChange = function (change) {
1760 var node = getStateTreeNode(change.object);
1761 switch (change.type) {
1762 case "update":
1763 return void node.emitPatch({
1764 op: "replace",
1765 path: escapeJsonPath(change.name),
1766 value: change.newValue.snapshot,
1767 oldValue: change.oldValue ? change.oldValue.snapshot : undefined
1768 }, node);
1769 case "add":
1770 return void node.emitPatch({
1771 op: "add",
1772 path: escapeJsonPath(change.name),
1773 value: change.newValue.snapshot,
1774 oldValue: undefined
1775 }, node);
1776 case "delete":
1777 // a node got deleted, get the old snapshot and make the node die
1778 var oldSnapshot = change.oldValue.snapshot;
1779 change.oldValue.die();
1780 // emit the patch
1781 return void node.emitPatch({
1782 op: "remove",
1783 path: escapeJsonPath(change.name),
1784 oldValue: oldSnapshot
1785 }, node);
1786 }
1787 };
1788 MapType.prototype.applyPatchLocally = function (node, subpath, patch) {
1789 var target = node.storedValue;
1790 switch (patch.op) {
1791 case "add":
1792 case "replace":
1793 target.set(subpath, patch.value);
1794 break;
1795 case "remove":
1796 target.delete(subpath);
1797 break;
1798 }
1799 };
1800 MapType.prototype.applySnapshot = function (node, snapshot) {
1801 typecheck(this, snapshot);
1802 var target = node.storedValue;
1803 var currentKeys = {};
1804 target.keys().forEach(function (key) {
1805 currentKeys[key] = false;
1806 });
1807 // Don't use target.replace, as it will throw all existing items first
1808 Object.keys(snapshot).forEach(function (key) {
1809 target.set(key, snapshot[key]);
1810 currentKeys[key] = true;
1811 });
1812 Object.keys(currentKeys).forEach(function (key) {
1813 if (currentKeys[key] === false)
1814 target.delete(key);
1815 });
1816 };
1817 MapType.prototype.getChildType = function (key) {
1818 return this.subType;
1819 };
1820 MapType.prototype.isValidSnapshot = function (value, context) {
1821 var _this = this;
1822 if (!isPlainObject(value)) {
1823 return typeCheckFailure(context, value, "Value is not a plain object");
1824 }
1825 return flattenTypeErrors(Object.keys(value).map(function (path) {
1826 return _this.subType.validate(value[path], getContextForPath(context, path, _this.subType));
1827 }));
1828 };
1829 MapType.prototype.getDefaultSnapshot = function () {
1830 return {};
1831 };
1832 MapType.prototype.removeChild = function (node, subpath) {
1833
1834 node.storedValue.delete(subpath);
1835 };
1836 __decorate([
1837 action
1838 ], MapType.prototype, "applySnapshot", null);
1839 return MapType;
1840}(ComplexType));
1841/**
1842 * Creates a key based collection type who's children are all of a uniform declared type.
1843 * If the type stored in a map has an identifier, it is mandatory to store the child under that identifier in the map.
1844 *
1845 * This type will always produce [observable maps](https://mobx.js.org/refguide/map.html)
1846 *
1847 * @example
1848 * const Todo = types.model({
1849 * id: types.identifier(types.number),
1850 * task: types.string
1851 * })
1852 *
1853 * const TodoStore = types.model({
1854 * todos: types.map(Todo)
1855 * })
1856 *
1857 * const s = TodoStore.create({ todos: {} })
1858 * unprotect(s)
1859 * s.todos.set(17, { task: "Grab coffee", id: 17 })
1860 * s.todos.put({ task: "Grab cookie", id: 18 }) // put will infer key from the identifier
1861 * console.log(s.todos.get(17).task) // prints: "Grab coffee"
1862 *
1863 * @export
1864 * @alias types.map
1865 * @param {IType<S, T>} subtype
1866 * @returns {IComplexType<S[], IObservableArray<T>>}
1867 */
1868function map(subtype) {
1869 return new MapType("map<string, " + subtype.name + ">", subtype);
1870}
1871
1872function arrayToString() {
1873 return getStateTreeNode(this) + "(" + this.length + " items)";
1874}
1875var ArrayType = /** @class */ (function (_super) {
1876 __extends(ArrayType, _super);
1877 function ArrayType(name, subType) {
1878 var _this = _super.call(this, name) || this;
1879 _this.shouldAttachNode = true;
1880 _this.flags = TypeFlags.Array;
1881 _this.createNewInstance = function () {
1882 var array = observable.shallowArray();
1883 addHiddenFinalProp(array, "toString", arrayToString);
1884 return array;
1885 };
1886 _this.finalizeNewInstance = function (node, snapshot) {
1887 var instance = node.storedValue;
1888 extras.getAdministration(instance).dehancer = node.unbox;
1889 intercept(instance, function (change) { return _this.willChange(change); });
1890 node.applySnapshot(snapshot);
1891 observe(instance, _this.didChange);
1892 };
1893 _this.subType = subType;
1894 return _this;
1895 }
1896 ArrayType.prototype.describe = function () {
1897 return this.subType.describe() + "[]";
1898 };
1899 ArrayType.prototype.instantiate = function (parent, subpath, environment, snapshot) {
1900 return createNode(this, parent, subpath, environment, snapshot, this.createNewInstance, this.finalizeNewInstance);
1901 };
1902 ArrayType.prototype.getChildren = function (node) {
1903 return node.storedValue.peek();
1904 };
1905 ArrayType.prototype.getChildNode = function (node, key) {
1906 var index = parseInt(key, 10);
1907 if (index < node.storedValue.length)
1908 return node.storedValue[index];
1909 return fail("Not a child: " + key);
1910 };
1911 ArrayType.prototype.willChange = function (change) {
1912 var node = getStateTreeNode(change.object);
1913 node.assertWritable();
1914 var childNodes = node.getChildren();
1915 switch (change.type) {
1916 case "update":
1917 if (change.newValue === change.object[change.index])
1918 return null;
1919 change.newValue = reconcileArrayChildren(node, this.subType, [childNodes[change.index]], [change.newValue], [change.index])[0];
1920 break;
1921 case "splice":
1922 var index_1 = change.index, removedCount = change.removedCount, added = change.added;
1923 change.added = reconcileArrayChildren(node, this.subType, childNodes.slice(index_1, index_1 + removedCount), added, added.map(function (_, i) { return index_1 + i; }));
1924 // update paths of remaining items
1925 for (var i = index_1 + removedCount; i < childNodes.length; i++) {
1926 childNodes[i].setParent(node, "" + (i + added.length - removedCount));
1927 }
1928 break;
1929 }
1930 return change;
1931 };
1932 ArrayType.prototype.getValue = function (node) {
1933 return node.storedValue;
1934 };
1935 ArrayType.prototype.getSnapshot = function (node) {
1936 return node.getChildren().map(function (childNode) { return childNode.snapshot; });
1937 };
1938 ArrayType.prototype.didChange = function (change) {
1939 var node = getStateTreeNode(change.object);
1940 switch (change.type) {
1941 case "update":
1942 return void node.emitPatch({
1943 op: "replace",
1944 path: "" + change.index,
1945 value: change.newValue.snapshot,
1946 oldValue: change.oldValue ? change.oldValue.snapshot : undefined
1947 }, node);
1948 case "splice":
1949 for (var i = change.removedCount - 1; i >= 0; i--)
1950 node.emitPatch({
1951 op: "remove",
1952 path: "" + (change.index + i),
1953 oldValue: change.removed[i].snapshot
1954 }, node);
1955 for (var i = 0; i < change.addedCount; i++)
1956 node.emitPatch({
1957 op: "add",
1958 path: "" + (change.index + i),
1959 value: node.getChildNode("" + (change.index + i)).snapshot,
1960 oldValue: undefined
1961 }, node);
1962 return;
1963 }
1964 };
1965 ArrayType.prototype.applyPatchLocally = function (node, subpath, patch) {
1966 var target = node.storedValue;
1967 var index = subpath === "-" ? target.length : parseInt(subpath);
1968 switch (patch.op) {
1969 case "replace":
1970 target[index] = patch.value;
1971 break;
1972 case "add":
1973 target.splice(index, 0, patch.value);
1974 break;
1975 case "remove":
1976 target.splice(index, 1);
1977 break;
1978 }
1979 };
1980 ArrayType.prototype.applySnapshot = function (node, snapshot) {
1981 typecheck(this, snapshot);
1982 var target = node.storedValue;
1983 target.replace(snapshot);
1984 };
1985 ArrayType.prototype.getChildType = function (key) {
1986 return this.subType;
1987 };
1988 ArrayType.prototype.isValidSnapshot = function (value, context) {
1989 var _this = this;
1990 if (!isArray(value)) {
1991 return typeCheckFailure(context, value, "Value is not an array");
1992 }
1993 return flattenTypeErrors(value.map(function (item, index) {
1994 return _this.subType.validate(item, getContextForPath(context, "" + index, _this.subType));
1995 }));
1996 };
1997 ArrayType.prototype.getDefaultSnapshot = function () {
1998 return [];
1999 };
2000 ArrayType.prototype.removeChild = function (node, subpath) {
2001 node.storedValue.splice(parseInt(subpath, 10), 1);
2002 };
2003 __decorate([
2004 action
2005 ], ArrayType.prototype, "applySnapshot", null);
2006 return ArrayType;
2007}(ComplexType));
2008/**
2009 * Creates an index based collection type who's children are all of a uniform declared type.
2010 *
2011 * This type will always produce [observable arrays](https://mobx.js.org/refguide/array.html)
2012 *
2013 * @example
2014 * const Todo = types.model({
2015 * task: types.string
2016 * })
2017 *
2018 * const TodoStore = types.model({
2019 * todos: types.array(Todo)
2020 * })
2021 *
2022 * const s = TodoStore.create({ todos: [] })
2023 * s.todos.push({ task: "Grab coffee" })
2024 * console.log(s.todos[0]) // prints: "Grab coffee"
2025 *
2026 * @export
2027 * @alias types.array
2028 * @param {IType<S, T>} subtype
2029 * @returns {IComplexType<S[], IObservableArray<T>>}
2030 */
2031function array(subtype) {
2032 if (process.env.NODE_ENV !== "production") {
2033 if (!isType(subtype))
2034 fail("expected a mobx-state-tree type as first argument, got " + subtype + " instead");
2035 }
2036 return new ArrayType(subtype.name + "[]", subtype);
2037}
2038function reconcileArrayChildren(parent, childType, oldNodes, newValues, newPaths) {
2039 var oldNode, newValue, hasNewNode = false, oldMatch = undefined;
2040 for (var i = 0;; i++) {
2041 oldNode = oldNodes[i];
2042 newValue = newValues[i];
2043 // for some reason, instead of newValue we got a node, fallback to the storedValue
2044 // TODO: https://github.com/mobxjs/mobx-state-tree/issues/340#issuecomment-325581681
2045 if (newValue instanceof Node)
2046 newValue = newValue.storedValue;
2047 hasNewNode = i <= newValues.length - 1;
2048 // both are empty, end
2049 if (!oldNode && !hasNewNode) {
2050 break;
2051 // new one does not exists, old one dies
2052 }
2053 else if (!hasNewNode) {
2054 oldNode.die();
2055 oldNodes.splice(i, 1);
2056 i--;
2057 // there is no old node, create it
2058 }
2059 else if (!oldNode) {
2060 // check if already belongs to the same parent. if so, avoid pushing item in. only swapping can occur.
2061 if (isStateTreeNode(newValue) && getStateTreeNode(newValue).parent === parent) {
2062 // this node is owned by this parent, but not in the reconcilable set, so it must be double
2063 fail("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 + "/" + newPaths[i] + "', but it lives already at '" + getStateTreeNode(newValue).path + "'");
2064 }
2065 oldNodes.splice(i, 0, valueAsNode(childType, parent, "" + newPaths[i], newValue));
2066 // both are the same, reconcile
2067 }
2068 else if (areSame(oldNode, newValue)) {
2069 oldNodes[i] = valueAsNode(childType, parent, "" + newPaths[i], newValue, oldNode);
2070 // nothing to do, try to reorder
2071 }
2072 else {
2073 oldMatch = undefined;
2074 // find a possible candidate to reuse
2075 for (var j = i; j < oldNodes.length; j++) {
2076 if (areSame(oldNodes[j], newValue)) {
2077 oldMatch = oldNodes.splice(j, 1)[0];
2078 break;
2079 }
2080 }
2081 oldNodes.splice(i, 0, valueAsNode(childType, parent, "" + newPaths[i], newValue, oldMatch));
2082 }
2083 }
2084 return oldNodes;
2085}
2086// convert a value to a node at given parent and subpath. attempts to reuse old node if possible and given
2087function valueAsNode(childType, parent, subpath, newValue, oldNode) {
2088 // ensure the value is valid-ish
2089 typecheck(childType, newValue);
2090 // the new value has a MST node
2091 if (isStateTreeNode(newValue)) {
2092 var childNode_1 = getStateTreeNode(newValue);
2093 childNode_1.assertAlive();
2094 // the node lives here
2095 if (childNode_1.parent !== null && childNode_1.parent === parent) {
2096 childNode_1.setParent(parent, subpath);
2097 if (oldNode && oldNode !== childNode_1)
2098 oldNode.die();
2099 return childNode_1;
2100 }
2101 }
2102 // there is old node and new one is a value/snapshot
2103 if (oldNode) {
2104 var childNode_2 = childType.reconcile(oldNode, newValue);
2105 childNode_2.setParent(parent, subpath);
2106 return childNode_2;
2107 }
2108 // nothing to do, create from scratch
2109 var childNode = childType.instantiate(parent, subpath, parent._environment, newValue);
2110 return childNode;
2111}
2112// given a value
2113function areSame(oldNode, newValue) {
2114 // the new value has the same node
2115 if (isStateTreeNode(newValue)) {
2116 return getStateTreeNode(newValue) === oldNode;
2117 }
2118 // the provided value is the snapshot of the old node
2119 if (isMutable(newValue) && oldNode.snapshot === newValue)
2120 return true;
2121 // new value is a snapshot with the correct identifier
2122 if (oldNode.identifier !== null &&
2123 oldNode.identifierAttribute &&
2124 isPlainObject(newValue) &&
2125 newValue[oldNode.identifierAttribute] === oldNode.identifier)
2126 return true;
2127 return false;
2128}
2129
2130var CoreType = /** @class */ (function (_super) {
2131 __extends(CoreType, _super);
2132 function CoreType(name, flags, checker, initializer) {
2133 if (initializer === void 0) { initializer = identity; }
2134 var _this = _super.call(this, name) || this;
2135 _this.flags = flags;
2136 _this.checker = checker;
2137 _this.initializer = initializer;
2138 return _this;
2139 }
2140 CoreType.prototype.describe = function () {
2141 return this.name;
2142 };
2143 CoreType.prototype.instantiate = function (parent, subpath, environment, snapshot) {
2144 return createNode(this, parent, subpath, environment, snapshot, this.initializer);
2145 };
2146 CoreType.prototype.isValidSnapshot = function (value, context) {
2147 if (isPrimitive(value) && this.checker(value)) {
2148 return typeCheckSuccess();
2149 }
2150 var typeName = this.name === "Date" ? "Date or a unix milliseconds timestamp" : this.name;
2151 return typeCheckFailure(context, value, "Value is not a " + typeName);
2152 };
2153 return CoreType;
2154}(Type));
2155/**
2156 * Creates a type that can only contain a string value.
2157 * This type is used for string values by default
2158 *
2159 * @export
2160 * @alias types.string
2161 * @example
2162 * const Person = types.model({
2163 * firstName: types.string,
2164 * lastName: "Doe"
2165 * })
2166 */
2167// tslint:disable-next-line:variable-name
2168var string = new CoreType("string", TypeFlags.String, function (v) { return typeof v === "string"; });
2169/**
2170 * Creates a type that can only contain a numeric value.
2171 * This type is used for numeric values by default
2172 *
2173 * @export
2174 * @alias types.number
2175 * @example
2176 * const Vector = types.model({
2177 * x: types.number,
2178 * y: 0
2179 * })
2180 */
2181// tslint:disable-next-line:variable-name
2182var number = new CoreType("number", TypeFlags.Number, function (v) { return typeof v === "number"; });
2183/**
2184 * Creates a type that can only contain a boolean value.
2185 * This type is used for boolean values by default
2186 *
2187 * @export
2188 * @alias types.boolean
2189 * @example
2190 * const Thing = types.model({
2191 * isCool: types.boolean,
2192 * isAwesome: false
2193 * })
2194 */
2195// tslint:disable-next-line:variable-name
2196var boolean = new CoreType("boolean", TypeFlags.Boolean, function (v) { return typeof v === "boolean"; });
2197/**
2198 * The type of the value `null`
2199 *
2200 * @export
2201 * @alias types.null
2202 */
2203var nullType = new CoreType("null", TypeFlags.Null, function (v) { return v === null; });
2204/**
2205 * The type of the value `undefined`
2206 *
2207 * @export
2208 * @alias types.undefined
2209 */
2210var undefinedType = new CoreType("undefined", TypeFlags.Undefined, function (v) { return v === undefined; });
2211/**
2212 * Creates a type that can only contain a javascript Date value.
2213 *
2214 * @export
2215 * @alias types.Date
2216 * @example
2217 * const LogLine = types.model({
2218 * timestamp: types.Date,
2219 * })
2220 *
2221 * LogLine.create({ timestamp: new Date() })
2222 */
2223// tslint:disable-next-line:variable-name
2224var 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)); });
2225DatePrimitive.getSnapshot = function (node) {
2226 return node.storedValue.getTime();
2227};
2228function getPrimitiveFactoryFromValue(value) {
2229 switch (typeof value) {
2230 case "string":
2231 return string;
2232 case "number":
2233 return number;
2234 case "boolean":
2235 return boolean;
2236 case "object":
2237 if (value instanceof Date)
2238 return DatePrimitive;
2239 }
2240 return fail("Cannot determine primtive type from value " + value);
2241}
2242
2243var IdentifierType = /** @class */ (function (_super) {
2244 __extends(IdentifierType, _super);
2245 function IdentifierType(identifierType) {
2246 var _this = _super.call(this, "identifier(" + identifierType.name + ")") || this;
2247 _this.identifierType = identifierType;
2248 _this.flags = TypeFlags.Identifier;
2249 return _this;
2250 }
2251 IdentifierType.prototype.instantiate = function (parent, subpath, environment, snapshot) {
2252 if (!parent || !isStateTreeNode(parent.storedValue))
2253 return fail("Identifier types can only be instantiated as direct child of a model type");
2254 if (parent.identifierAttribute)
2255 fail("Cannot define property '" + subpath + "' as object identifier, property '" + parent.identifierAttribute + "' is already defined as identifier property");
2256 parent.identifierAttribute = subpath;
2257 return createNode(this, parent, subpath, environment, snapshot);
2258 };
2259 IdentifierType.prototype.reconcile = function (current, newValue) {
2260 if (current.storedValue !== newValue)
2261 return fail("Tried to change identifier from '" + current.storedValue + "' to '" + newValue + "'. Changing identifiers is not allowed.");
2262 return current;
2263 };
2264 IdentifierType.prototype.describe = function () {
2265 return "identifier(" + this.identifierType.describe() + ")";
2266 };
2267 IdentifierType.prototype.isValidSnapshot = function (value, context) {
2268 if (value === undefined ||
2269 value === null ||
2270 typeof value === "string" ||
2271 typeof value === "number")
2272 return this.identifierType.validate(value, context);
2273 return typeCheckFailure(context, value, "Value is not a valid identifier, which is a string or a number");
2274 };
2275 return IdentifierType;
2276}(Type));
2277/**
2278 * Identifiers are used to make references, lifecycle events and reconciling works.
2279 * Inside a state tree, for each type can exist only one instance for each given identifier.
2280 * For example there couldn't be 2 instances of user with id 1. If you need more, consider using references.
2281 * Identifier can be used only as type property of a model.
2282 * This type accepts as parameter the value type of the identifier field that can be either string or number.
2283 *
2284 * @example
2285 * const Todo = types.model("Todo", {
2286 * id: types.identifier(types.string),
2287 * title: types.string
2288 * })
2289 *
2290 * @export
2291 * @alias types.identifier
2292 * @template T
2293 * @param {IType<T, T>} baseType
2294 * @returns {IType<T, T>}
2295 */
2296function identifier(baseType) {
2297 if (baseType === void 0) { baseType = string; }
2298 if (process.env.NODE_ENV !== "production") {
2299 if (!isType(baseType))
2300 fail("expected a mobx-state-tree type as first argument, got " + baseType + " instead");
2301 }
2302 return new IdentifierType(baseType);
2303}
2304
2305var OptionalValue = /** @class */ (function (_super) {
2306 __extends(OptionalValue, _super);
2307 function OptionalValue(type, defaultValue) {
2308 var _this = _super.call(this, type.name) || this;
2309 _this.type = type;
2310 _this.defaultValue = defaultValue;
2311 return _this;
2312 }
2313 Object.defineProperty(OptionalValue.prototype, "flags", {
2314 get: function () {
2315 return this.type.flags | TypeFlags.Optional;
2316 },
2317 enumerable: true,
2318 configurable: true
2319 });
2320 OptionalValue.prototype.describe = function () {
2321 return this.type.describe() + "?";
2322 };
2323 OptionalValue.prototype.instantiate = function (parent, subpath, environment, value) {
2324 if (typeof value === "undefined") {
2325 var defaultValue = this.getDefaultValue();
2326 var defaultSnapshot = isStateTreeNode(defaultValue)
2327 ? getStateTreeNode(defaultValue).snapshot
2328 : defaultValue;
2329 return this.type.instantiate(parent, subpath, environment, defaultSnapshot);
2330 }
2331 return this.type.instantiate(parent, subpath, environment, value);
2332 };
2333 OptionalValue.prototype.reconcile = function (current, newValue) {
2334 return this.type.reconcile(current, this.type.is(newValue) ? newValue : this.getDefaultValue());
2335 };
2336 OptionalValue.prototype.getDefaultValue = function () {
2337 var defaultValue = typeof this.defaultValue === "function" ? this.defaultValue() : this.defaultValue;
2338 if (typeof this.defaultValue === "function")
2339 typecheck(this, defaultValue);
2340 return defaultValue;
2341 };
2342 OptionalValue.prototype.isValidSnapshot = function (value, context) {
2343 // defaulted values can be skipped
2344 if (value === undefined) {
2345 return typeCheckSuccess();
2346 }
2347 // bounce validation to the sub-type
2348 return this.type.validate(value, context);
2349 };
2350 OptionalValue.prototype.isAssignableFrom = function (type) {
2351 return this.type.isAssignableFrom(type);
2352 };
2353 return OptionalValue;
2354}(Type));
2355/**
2356 * `types.optional` can be used to create a property with a default value.
2357 * If the given value is not provided in the snapshot, it will default to the provided `defaultValue`.
2358 * If `defaultValue` is a function, the function will be invoked for every new instance.
2359 * Applying a snapshot in which the optional value is _not_ present, causes the value to be reset
2360 *
2361 * @example
2362 * const Todo = types.model({
2363 * title: types.optional(types.string, "Test"),
2364 * done: types.optional(types.boolean, false),
2365 * created: types.optional(types.Date, () => new Date())
2366 * })
2367 *
2368 * // it is now okay to omit 'created' and 'done'. created will get a freshly generated timestamp
2369 * const todo = Todo.create({ title: "Get coffee "})
2370 *
2371 * @export
2372 * @alias types.optional
2373 */
2374function optional(type, defaultValueOrFunction) {
2375 if (process.env.NODE_ENV !== "production") {
2376 if (!isType(type))
2377 fail("expected a mobx-state-tree type as first argument, got " + type + " instead");
2378 var defaultValue = typeof defaultValueOrFunction === "function"
2379 ? defaultValueOrFunction()
2380 : defaultValueOrFunction;
2381 var defaultSnapshot = isStateTreeNode(defaultValue)
2382 ? getStateTreeNode(defaultValue).snapshot
2383 : defaultValue;
2384 typecheck(type, defaultSnapshot);
2385 }
2386 return new OptionalValue(type, defaultValueOrFunction);
2387}
2388
2389var PRE_PROCESS_SNAPSHOT = "preProcessSnapshot";
2390var HOOK_NAMES = {
2391 afterCreate: "afterCreate",
2392 afterAttach: "afterAttach",
2393 postProcessSnapshot: "postProcessSnapshot",
2394 beforeDetach: "beforeDetach",
2395 beforeDestroy: "beforeDestroy"
2396};
2397function objectTypeToString() {
2398 return getStateTreeNode(this).toString();
2399}
2400var defaultObjectOptions = {
2401 name: "AnonymousModel",
2402 properties: {},
2403 initializers: EMPTY_ARRAY
2404};
2405function toPropertiesObject(properties) {
2406 // loop through properties and ensures that all items are types
2407 return Object.keys(properties).reduce(function (properties, key) {
2408 // warn if user intended a HOOK
2409 if (key in HOOK_NAMES)
2410 return fail("Hook '" + key + "' was defined as property. Hooks should be defined as part of the actions");
2411 // the user intended to use a view
2412 var descriptor = Object.getOwnPropertyDescriptor(properties, key);
2413 if ("get" in descriptor) {
2414 fail("Getters are not supported as properties. Please use views instead");
2415 }
2416 // undefined and null are not valid
2417 var value = descriptor.value;
2418 if (value === null || undefined) {
2419 fail("The default value of an attribute cannot be null or undefined as the type cannot be inferred. Did you mean `types.maybe(someType)`?");
2420 // its a primitive, convert to its type
2421 }
2422 else if (isPrimitive(value)) {
2423 return Object.assign({}, properties, (_a = {},
2424 _a[key] = optional(getPrimitiveFactoryFromValue(value), value),
2425 _a));
2426 // its already a type
2427 }
2428 else if (isType(value)) {
2429 return properties;
2430 // its a function, maybe the user wanted a view?
2431 }
2432 else if (typeof value === "function") {
2433 fail("Functions are not supported as properties, use views instead");
2434 // no other complex values
2435 }
2436 else if (typeof value === "object") {
2437 fail("In property '" + key + "': base model's should not contain complex values: '" + value + "'");
2438 // WTF did you passed in mate?
2439 }
2440 else {
2441 fail("Unexpected value for property '" + key + "'");
2442 }
2443 var _a;
2444 }, properties);
2445}
2446// TODO: rename to Model
2447var ObjectType = /** @class */ (function (_super) {
2448 __extends(ObjectType, _super);
2449 function ObjectType(opts) {
2450 var _this = _super.call(this, opts.name || defaultObjectOptions.name) || this;
2451 _this.flags = TypeFlags.Object;
2452 _this.createNewInstance = function () {
2453 var instance = observable.shallowObject(EMPTY_OBJECT);
2454 addHiddenFinalProp(instance, "toString", objectTypeToString);
2455 return instance;
2456 };
2457 _this.finalizeNewInstance = function (node, snapshot) {
2458 var instance = node.storedValue;
2459 _this.forAllProps(function (name, type) {
2460 extendShallowObservable(instance, (_a = {},
2461 _a[name] = observable.ref(type.instantiate(node, name, node._environment, snapshot[name])),
2462 _a));
2463 extras.interceptReads(node.storedValue, name, node.unbox);
2464 var _a;
2465 });
2466 _this.initializers.reduce(function (self, fn) { return fn(self); }, instance);
2467 intercept(instance, function (change) { return _this.willChange(change); });
2468 observe(instance, _this.didChange);
2469 };
2470 _this.didChange = function (change) {
2471 var node = getStateTreeNode(change.object);
2472 node.emitPatch({
2473 op: "replace",
2474 path: escapeJsonPath(change.name),
2475 value: change.newValue.snapshot,
2476 oldValue: change.oldValue ? change.oldValue.snapshot : undefined
2477 }, node);
2478 };
2479 var name = opts.name || defaultObjectOptions.name;
2480 // TODO: this test still needed?
2481 if (!/^\w[\w\d_]*$/.test(name))
2482 fail("Typename should be a valid identifier: " + name);
2483 Object.assign(_this, defaultObjectOptions, opts);
2484 // ensures that any default value gets converted to its related type
2485 _this.properties = toPropertiesObject(_this.properties);
2486 _this.propertiesNames = Object.keys(_this.properties);
2487 Object.freeze(_this.properties); // make sure nobody messes with it
2488 return _this;
2489 }
2490 ObjectType.prototype.extend = function (opts) {
2491 return new ObjectType({
2492 name: opts.name || this.name,
2493 properties: Object.assign({}, this.properties, opts.properties),
2494 initializers: this.initializers.concat(opts.initializers || []),
2495 preProcessor: opts.preProcessor || this.preProcessor
2496 });
2497 };
2498 ObjectType.prototype.actions = function (fn) {
2499 var actionInitializer = function (self) {
2500 var actions = fn(self);
2501 // check if return is correct
2502 if (!isPlainObject(actions))
2503 fail("actions initializer should return a plain object containing actions");
2504 // bind actions to the object created
2505 Object.keys(actions).forEach(function (name) {
2506 // warn if preprocessor was given
2507 if (name === PRE_PROCESS_SNAPSHOT)
2508 return fail("Cannot define action '" + PRE_PROCESS_SNAPSHOT + "', it should be defined using 'type.preProcessSnapshot(fn)' instead");
2509 // apply hook composition
2510 var action$$1 = actions[name];
2511 var baseAction = self[name];
2512 if (name in HOOK_NAMES && baseAction) {
2513 var specializedAction_1 = action$$1;
2514 if (name === HOOK_NAMES.postProcessSnapshot)
2515 action$$1 = function (snapshot) { return specializedAction_1(baseAction(snapshot)); };
2516 else
2517 action$$1 = function () {
2518 baseAction.apply(null, arguments);
2519 specializedAction_1.apply(null, arguments);
2520 };
2521 }
2522 addHiddenFinalProp(self, name, createActionInvoker(self, name, action$$1));
2523 });
2524 return self;
2525 };
2526 return this.extend({ initializers: [actionInitializer] });
2527 };
2528 ObjectType.prototype.named = function (name) {
2529 return this.extend({ name: name });
2530 };
2531 ObjectType.prototype.props = function (properties) {
2532 return this.extend({ properties: properties });
2533 };
2534 ObjectType.prototype.views = function (fn) {
2535 var viewInitializer = function (self) {
2536 var views = fn(self);
2537 // check views return
2538 if (!isPlainObject(views))
2539 fail("views initializer should return a plain object containing views");
2540 Object.keys(views).forEach(function (key) {
2541 // is this a computed property?
2542 var descriptor = Object.getOwnPropertyDescriptor(views, key);
2543 var value = descriptor.value;
2544 if ("get" in descriptor) {
2545 // TODO: mobx currently does not allow redefining computes yet, pending #1121
2546 if (isComputed(self.$mobx.values[key])) {
2547 // TODO: use `isComputed(self, key)`, pending mobx #1120
2548
2549 self.$mobx.values[key] = computed(descriptor.get, {
2550 name: key,
2551 setter: descriptor.set,
2552 context: self
2553 });
2554 }
2555 else {
2556 var tmp = {};
2557 Object.defineProperty(tmp, key, {
2558 get: descriptor.get,
2559 set: descriptor.set,
2560 enumerable: true
2561 });
2562 extendShallowObservable(self, tmp);
2563 }
2564 }
2565 else if (typeof value === "function") {
2566 // this is a view function, merge as is!
2567 addHiddenFinalProp(self, key, value);
2568 }
2569 else {
2570 fail("A view member should either be a function or getter based property");
2571 }
2572 });
2573 return self;
2574 };
2575 return this.extend({ initializers: [viewInitializer] });
2576 };
2577 ObjectType.prototype.preProcessSnapshot = function (preProcessor) {
2578 var currentPreprocessor = this.preProcessor;
2579 if (!currentPreprocessor)
2580 return this.extend({ preProcessor: preProcessor });
2581 else
2582 return this.extend({
2583 preProcessor: function (snapshot) { return currentPreprocessor(preProcessor(snapshot)); }
2584 });
2585 };
2586 ObjectType.prototype.instantiate = function (parent, subpath, environment, snapshot) {
2587 return createNode(this, parent, subpath, environment, this.applySnapshotPreProcessor(snapshot), this.createNewInstance, this.finalizeNewInstance);
2588 // Optimization: record all prop- view- and action names after first construction, and generate an optimal base class
2589 // that pre-reserves all these fields for fast object-member lookups
2590 };
2591 ObjectType.prototype.willChange = function (change) {
2592 var node = getStateTreeNode(change.object);
2593 var type = this.properties[change.name];
2594 node.assertWritable();
2595 typecheck(type, change.newValue);
2596 change.newValue = type.reconcile(node.getChildNode(change.name), change.newValue);
2597 return change;
2598 };
2599 ObjectType.prototype.getChildren = function (node) {
2600 var _this = this;
2601 var res = [];
2602 this.forAllProps(function (name, type) {
2603 res.push(_this.getChildNode(node, name));
2604 });
2605 return res;
2606 };
2607 ObjectType.prototype.getChildNode = function (node, key) {
2608 if (!(key in this.properties))
2609 return fail("Not a value property: " + key);
2610 var childNode = node.storedValue.$mobx.values[key].value; // TODO: blegh!
2611 if (!childNode)
2612 return fail("Node not available for property " + key);
2613 return childNode;
2614 };
2615 ObjectType.prototype.getValue = function (node) {
2616 return node.storedValue;
2617 };
2618 ObjectType.prototype.getSnapshot = function (node) {
2619 var _this = this;
2620 var res = {};
2621 this.forAllProps(function (name, type) {
2622 // TODO: FIXME, make sure the observable ref is used!
2623
2624 extras.getAtom(node.storedValue, name).reportObserved();
2625 res[name] = _this.getChildNode(node, name).snapshot;
2626 });
2627 if (typeof node.storedValue.postProcessSnapshot === "function")
2628 return node.storedValue.postProcessSnapshot.call(null, res);
2629 return res;
2630 };
2631 ObjectType.prototype.applyPatchLocally = function (node, subpath, patch) {
2632 if (!(patch.op === "replace" || patch.op === "add"))
2633 fail("object does not support operation " + patch.op);
2634 node.storedValue[subpath] = patch.value;
2635 };
2636 ObjectType.prototype.applySnapshot = function (node, snapshot) {
2637 var s = this.applySnapshotPreProcessor(snapshot);
2638 typecheck(this, s);
2639 this.forAllProps(function (name, type) {
2640 node.storedValue[name] = s[name];
2641 });
2642 };
2643 ObjectType.prototype.applySnapshotPreProcessor = function (snapshot) {
2644 if (this.preProcessor)
2645 return this.preProcessor.call(null, snapshot);
2646 return snapshot;
2647 };
2648 ObjectType.prototype.getChildType = function (key) {
2649 return this.properties[key];
2650 };
2651 ObjectType.prototype.isValidSnapshot = function (value, context) {
2652 var _this = this;
2653 var snapshot = this.applySnapshotPreProcessor(value);
2654 if (!isPlainObject(snapshot)) {
2655 return typeCheckFailure(context, snapshot, "Value is not a plain object");
2656 }
2657 return flattenTypeErrors(this.propertiesNames.map(function (key) {
2658 return _this.properties[key].validate(snapshot[key], getContextForPath(context, key, _this.properties[key]));
2659 }));
2660 };
2661 ObjectType.prototype.forAllProps = function (fn) {
2662 var _this = this;
2663 this.propertiesNames.forEach(function (key) { return fn(key, _this.properties[key]); });
2664 };
2665 ObjectType.prototype.describe = function () {
2666 var _this = this;
2667 // optimization: cache
2668 return ("{ " +
2669 this.propertiesNames
2670 .map(function (key) { return key + ": " + _this.properties[key].describe(); })
2671 .join("; ") +
2672 " }");
2673 };
2674 ObjectType.prototype.getDefaultSnapshot = function () {
2675 return {};
2676 };
2677 ObjectType.prototype.removeChild = function (node, subpath) {
2678 node.storedValue[subpath] = null;
2679 };
2680 __decorate([
2681 action
2682 ], ObjectType.prototype, "applySnapshot", null);
2683 return ObjectType;
2684}(ComplexType));
2685/**
2686 * Creates a new model type by providing a name, properties, volatile state and actions.
2687 *
2688 * 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.
2689 *
2690 * @export
2691 * @alias types.model
2692 */
2693function model() {
2694 var args = [];
2695 for (var _i = 0; _i < arguments.length; _i++) {
2696 args[_i] = arguments[_i];
2697 }
2698 var name = typeof args[0] === "string" ? args.shift() : "AnonymousModel";
2699 var properties = args.shift() || {};
2700 return new ObjectType({ name: name, properties: properties });
2701}
2702/**
2703 * Composes a new model from one or more existing model types.
2704 * This method can be invoked in two forms:
2705 * Given 2 or more model types, the types are composed into a new Type.
2706 *
2707 * @export
2708 * @alias types.compose
2709 */
2710function compose() {
2711 var args = [];
2712 for (var _i = 0; _i < arguments.length; _i++) {
2713 args[_i] = arguments[_i];
2714 }
2715 // TODO: just join the base type names if no name is provided
2716 var typeName = typeof args[0] === "string" ? args.shift() : "AnonymousModel";
2717 // check all parameters
2718 if (process.env.NODE_ENV !== "production") {
2719 args.forEach(function (type) {
2720 if (!isType(type))
2721 fail("expected a mobx-state-tree type, got " + type + " instead");
2722 });
2723 }
2724 return args
2725 .reduce(function (prev, cur) {
2726 return prev.extend({
2727 name: prev.name + "_" + cur.name,
2728 properties: cur.properties,
2729 initializers: cur.initializers
2730 });
2731 })
2732 .named(typeName);
2733}
2734
2735var StoredReference = /** @class */ (function () {
2736 function StoredReference(mode, value) {
2737 this.mode = mode;
2738 this.value = value;
2739 if (mode === "object") {
2740 if (!isStateTreeNode(value))
2741 return fail("Can only store references to tree nodes, got: '" + value + "'");
2742 var targetNode = getStateTreeNode(value);
2743 if (!targetNode.identifierAttribute)
2744 return fail("Can only store references with a defined identifier attribute.");
2745 }
2746 }
2747 return StoredReference;
2748}());
2749var ReferenceType = /** @class */ (function (_super) {
2750 __extends(ReferenceType, _super);
2751 function ReferenceType(targetType) {
2752 var _this = _super.call(this, "reference(" + targetType.name + ")") || this;
2753 _this.targetType = targetType;
2754 _this.flags = TypeFlags.Reference;
2755 return _this;
2756 }
2757 ReferenceType.prototype.describe = function () {
2758 return this.name;
2759 };
2760 ReferenceType.prototype.getValue = function (node) {
2761 var ref = node.storedValue;
2762 if (ref.mode === "object")
2763 return ref.value;
2764 if (!node.isAlive)
2765 return undefined;
2766 // reference was initialized with the identifier of the target
2767 var target = node.root.identifierCache.resolve(this.targetType, ref.value);
2768 if (!target)
2769 return fail("Failed to resolve reference of type " + this.targetType
2770 .name + ": '" + ref.value + "' (in: " + node.path + ")");
2771 return target.value;
2772 };
2773 ReferenceType.prototype.getSnapshot = function (node) {
2774 var ref = node.storedValue;
2775 switch (ref.mode) {
2776 case "identifier":
2777 return ref.value;
2778 case "object":
2779 return getStateTreeNode(ref.value).identifier;
2780 }
2781 };
2782 ReferenceType.prototype.instantiate = function (parent, subpath, environment, snapshot) {
2783 var isComplex = isStateTreeNode(snapshot);
2784 return createNode(this, parent, subpath, environment, new StoredReference(isComplex ? "object" : "identifier", snapshot));
2785 };
2786 ReferenceType.prototype.reconcile = function (current, newValue) {
2787 var targetMode = isStateTreeNode(newValue) ? "object" : "identifier";
2788 if (isReferenceType(current.type)) {
2789 var ref = current.storedValue;
2790 if (targetMode === ref.mode && ref.value === newValue)
2791 return current;
2792 }
2793 var newNode = this.instantiate(current.parent, current.subpath, current._environment, newValue);
2794 current.die();
2795 return newNode;
2796 };
2797 ReferenceType.prototype.isAssignableFrom = function (type) {
2798 return this.targetType.isAssignableFrom(type);
2799 };
2800 ReferenceType.prototype.isValidSnapshot = function (value, context) {
2801 return typeof value === "string" || typeof value === "number"
2802 ? typeCheckSuccess()
2803 : typeCheckFailure(context, value, "Value is not a valid identifier, which is a string or a number");
2804 };
2805 return ReferenceType;
2806}(Type));
2807/**
2808 * Creates a reference to another type, which should have defined an identifier.
2809 * See also the [reference and identifiers](https://github.com/mobxjs/mobx-state-tree#references-and-identifiers) section.
2810 *
2811 * @export
2812 * @alias types.reference
2813 */
2814function reference(subType) {
2815 // check that a type is given
2816 if (process.env.NODE_ENV !== "production") {
2817 if (!isType(subType))
2818 fail("expected a mobx-state-tree type as first argument, got " + subType + " instead");
2819 if (arguments.length === 2 && typeof arguments[1] === "string")
2820 fail("References with base path are no longer supported. Please remove the base path.");
2821 }
2822 return new ReferenceType(subType);
2823}
2824
2825var Union = /** @class */ (function (_super) {
2826 __extends(Union, _super);
2827 function Union(name, types, dispatcher) {
2828 var _this = _super.call(this, name) || this;
2829 _this.dispatcher = null;
2830 _this.dispatcher = dispatcher;
2831 _this.types = types;
2832 return _this;
2833 }
2834 Object.defineProperty(Union.prototype, "flags", {
2835 get: function () {
2836 var result = TypeFlags.Union;
2837 this.types.forEach(function (type) {
2838 result |= type.flags;
2839 });
2840 return result;
2841 },
2842 enumerable: true,
2843 configurable: true
2844 });
2845 Union.prototype.isAssignableFrom = function (type) {
2846 return this.types.some(function (subType) { return subType.isAssignableFrom(type); });
2847 };
2848 Union.prototype.describe = function () {
2849 return "(" + this.types.map(function (factory) { return factory.describe(); }).join(" | ") + ")";
2850 };
2851 Union.prototype.instantiate = function (parent, subpath, environment, value) {
2852 return this.determineType(value).instantiate(parent, subpath, environment, value);
2853 };
2854 Union.prototype.reconcile = function (current, newValue) {
2855 return this.determineType(newValue).reconcile(current, newValue);
2856 };
2857 Union.prototype.determineType = function (value) {
2858 // try the dispatcher, if defined
2859 if (this.dispatcher !== null) {
2860 return this.dispatcher(value);
2861 }
2862 // find the most accomodating type
2863 var applicableTypes = this.types.filter(function (type) { return type.is(value); });
2864 if (applicableTypes.length > 1)
2865 return fail("Ambiguos snapshot " + JSON.stringify(value) + " for union " + this
2866 .name + ". Please provide a dispatch in the union declaration.");
2867 return applicableTypes[0];
2868 };
2869 Union.prototype.isValidSnapshot = function (value, context) {
2870 if (this.dispatcher !== null) {
2871 return this.dispatcher(value).validate(value, context);
2872 }
2873 var errors = this.types.map(function (type) { return type.validate(value, context); });
2874 var applicableTypes = errors.filter(function (errorArray) { return errorArray.length === 0; });
2875 if (applicableTypes.length > 1) {
2876 return typeCheckFailure(context, value, "Multiple types are applicable for the union (hint: provide a dispatch function)");
2877 }
2878 else if (applicableTypes.length === 0) {
2879 return typeCheckFailure(context, value, "No type is applicable for the union").concat(flattenTypeErrors(errors));
2880 }
2881 return typeCheckSuccess();
2882 };
2883 return Union;
2884}(Type));
2885/**
2886 * types.union(dispatcher?, types...) 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.
2887 *
2888 * @export
2889 * @alias types.union
2890 * @param {(ITypeDispatcher | IType<any, any>)} dispatchOrType
2891 * @param {...IType<any, any>[]} otherTypes
2892 * @returns {IType<any, any>}
2893 */
2894function union(dispatchOrType) {
2895 var otherTypes = [];
2896 for (var _i = 1; _i < arguments.length; _i++) {
2897 otherTypes[_i - 1] = arguments[_i];
2898 }
2899 var dispatcher = isType(dispatchOrType) ? null : dispatchOrType;
2900 var types = isType(dispatchOrType) ? otherTypes.concat(dispatchOrType) : otherTypes;
2901 var name = "(" + types.map(function (type) { return type.name; }).join(" | ") + ")";
2902 // check all options
2903 if (process.env.NODE_ENV !== "production") {
2904 types.forEach(function (type) {
2905 if (!isType(type))
2906 fail("expected all possible types to be a mobx-state-tree type, got " +
2907 type +
2908 " instead");
2909 });
2910 }
2911 return new Union(name, types, dispatcher);
2912}
2913
2914var Literal = /** @class */ (function (_super) {
2915 __extends(Literal, _super);
2916 function Literal(value) {
2917 var _this = _super.call(this, JSON.stringify(value)) || this;
2918 _this.flags = TypeFlags.Literal;
2919 _this.value = value;
2920 return _this;
2921 }
2922 Literal.prototype.instantiate = function (parent, subpath, environment, snapshot) {
2923 return createNode(this, parent, subpath, environment, snapshot);
2924 };
2925 Literal.prototype.describe = function () {
2926 return JSON.stringify(this.value);
2927 };
2928 Literal.prototype.isValidSnapshot = function (value, context) {
2929 if (isPrimitive(value) && value === this.value) {
2930 return typeCheckSuccess();
2931 }
2932 return typeCheckFailure(context, value, "Value is not a literal " + JSON.stringify(this.value));
2933 };
2934 return Literal;
2935}(Type));
2936/**
2937 * The literal type will return a type that will match only the exact given type.
2938 * The given value must be a primitive, in order to be serialized to a snapshot correctly.
2939 * You can use literal to match exact strings for example the exact male or female string.
2940 *
2941 * @example
2942 * const Person = types.model({
2943 * name: types.string,
2944 * gender: types.union(types.literal('male'), types.literal('female'))
2945 * })
2946 *
2947 * @export
2948 * @alias types.literal
2949 * @template S
2950 * @param {S} value The value to use in the strict equal check
2951 * @returns {ISimpleType<S>}
2952 */
2953function literal(value) {
2954 // check that the given value is a primitive
2955 if (process.env.NODE_ENV !== "production") {
2956 if (!isPrimitive(value))
2957 fail("Literal types can be built only on top of primitives");
2958 }
2959 return new Literal(value);
2960}
2961
2962var Frozen = /** @class */ (function (_super) {
2963 __extends(Frozen, _super);
2964 function Frozen() {
2965 var _this = _super.call(this, "frozen") || this;
2966 _this.flags = TypeFlags.Frozen;
2967 return _this;
2968 }
2969 Frozen.prototype.describe = function () {
2970 return "<any immutable value>";
2971 };
2972 Frozen.prototype.instantiate = function (parent, subpath, environment, value) {
2973 // deep freeze the object/array
2974 return createNode(this, parent, subpath, environment, deepFreeze(value));
2975 };
2976 Frozen.prototype.isValidSnapshot = function (value, context) {
2977 if (!isSerializable(value)) {
2978 return typeCheckFailure(context, value, "Value is not serializable and cannot be frozen");
2979 }
2980 return typeCheckSuccess();
2981 };
2982 return Frozen;
2983}(Type));
2984/**
2985 * Frozen can be used to story any value that is serializable in itself (that is valid JSON).
2986 * Frozen values need to be immutable or treated as if immutable.
2987 * Values stored in frozen will snapshotted as-is by MST, and internal changes will not be tracked.
2988 *
2989 * 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.
2990 *
2991 * @example
2992 * const GameCharacter = types.model({
2993 * name: string,
2994 * location: types.frozen
2995 * })
2996 *
2997 * const hero = new GameCharacter({
2998 * name: "Mario",
2999 * location: { x: 7, y: 4 }
3000 * })
3001 *
3002 * hero.location = { x: 10, y: 2 } // OK
3003 * hero.location.x = 7 // Not ok!
3004 *
3005 * @alias types.frozen
3006 */
3007var frozen = new Frozen();
3008
3009var optionalNullType = optional(nullType, null);
3010/**
3011 * Maybe will make a type nullable, and also null by default.
3012 *
3013 * @export
3014 * @alias types.maybe
3015 * @template S
3016 * @template T
3017 * @param {IType<S, T>} type The type to make nullable
3018 * @returns {(IType<S | null | undefined, T | null>)}
3019 */
3020function maybe(type) {
3021 if (process.env.NODE_ENV !== "production") {
3022 if (!isType(type))
3023 fail("expected a mobx-state-tree type as first argument, got " + type + " instead");
3024 if (type === frozen) {
3025 fail("Unable to declare `types.maybe(types.frozen)`. Frozen already accepts `null`. Consider using `types.optional(types.frozen, null)` instead.");
3026 }
3027 }
3028 return union(optionalNullType, type);
3029}
3030
3031var Refinement = /** @class */ (function (_super) {
3032 __extends(Refinement, _super);
3033 function Refinement(name, type, predicate, message) {
3034 var _this = _super.call(this, name) || this;
3035 _this.type = type;
3036 _this.predicate = predicate;
3037 _this.message = message;
3038 return _this;
3039 }
3040 Object.defineProperty(Refinement.prototype, "flags", {
3041 get: function () {
3042 return this.type.flags | TypeFlags.Refinement;
3043 },
3044 enumerable: true,
3045 configurable: true
3046 });
3047 Refinement.prototype.describe = function () {
3048 return this.name;
3049 };
3050 Refinement.prototype.instantiate = function (parent, subpath, environment, value) {
3051 // create the child type
3052 var inst = this.type.instantiate(parent, subpath, environment, value);
3053 return inst;
3054 };
3055 Refinement.prototype.isAssignableFrom = function (type) {
3056 return this.type.isAssignableFrom(type);
3057 };
3058 Refinement.prototype.isValidSnapshot = function (value, context) {
3059 var subtypeErrors = this.type.validate(value, context);
3060 if (subtypeErrors.length > 0)
3061 return subtypeErrors;
3062 var snapshot = isStateTreeNode(value) ? getStateTreeNode(value).snapshot : value;
3063 if (!this.predicate(snapshot)) {
3064 return typeCheckFailure(context, value, this.message(value));
3065 }
3066 return typeCheckSuccess();
3067 };
3068 return Refinement;
3069}(Type));
3070/**
3071 * `types.refinement(baseType, (snapshot) => boolean)` 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.
3072 *
3073 * @export
3074 * @alias types.refinement
3075 * @template T
3076 * @param {string} name
3077 * @param {IType<T, T>} type
3078 * @param {(snapshot: T) => boolean} predicate
3079 * @returns {IType<T, T>}
3080 */
3081function refinement() {
3082 var args = [];
3083 for (var _i = 0; _i < arguments.length; _i++) {
3084 args[_i] = arguments[_i];
3085 }
3086 var name = typeof args[0] === "string" ? args.shift() : isType(args[0]) ? args[0].name : null;
3087 var type = args[0];
3088 var predicate = args[1];
3089 var message = args[2]
3090 ? args[2]
3091 : function (v) { return "Value does not respect the refinement predicate"; };
3092 // ensures all parameters are correct
3093 if (process.env.NODE_ENV !== "production") {
3094 if (typeof name !== "string")
3095 fail("expected a string as first argument, got " + name + " instead");
3096 if (!isType(type))
3097 fail("expected a mobx-state-tree type as first or second argument, got " +
3098 type +
3099 " instead");
3100 if (typeof predicate !== "function")
3101 fail("expected a function as third argument, got " + predicate + " instead");
3102 if (typeof message !== "function")
3103 fail("expected a function as fourth argument, got " + message + " instead");
3104 }
3105 return new Refinement(name, type, predicate, message);
3106}
3107
3108var Late = /** @class */ (function (_super) {
3109 __extends(Late, _super);
3110 function Late(name, definition) {
3111 var _this = _super.call(this, name) || this;
3112 _this._subType = null;
3113 _this.definition = definition;
3114 return _this;
3115 }
3116 Object.defineProperty(Late.prototype, "flags", {
3117 get: function () {
3118 return this.subType.flags | TypeFlags.Late;
3119 },
3120 enumerable: true,
3121 configurable: true
3122 });
3123 Object.defineProperty(Late.prototype, "subType", {
3124 get: function () {
3125 if (this._subType === null) {
3126 this._subType = this.definition();
3127 }
3128 return this._subType;
3129 },
3130 enumerable: true,
3131 configurable: true
3132 });
3133 Late.prototype.instantiate = function (parent, subpath, environment, snapshot) {
3134 return this.subType.instantiate(parent, subpath, environment, snapshot);
3135 };
3136 Late.prototype.reconcile = function (current, newValue) {
3137 return this.subType.reconcile(current, newValue);
3138 };
3139 Late.prototype.describe = function () {
3140 return this.subType.name;
3141 };
3142 Late.prototype.isValidSnapshot = function (value, context) {
3143 return this.subType.validate(value, context);
3144 };
3145 Late.prototype.isAssignableFrom = function (type) {
3146 return this.subType.isAssignableFrom(type);
3147 };
3148 return Late;
3149}(Type));
3150/**
3151 * Defines a type that gets implemented later. This is useful when you have to deal with circular dependencies.
3152 * Please notice that when defining circular dependencies TypeScript isn't smart enough to inference them.
3153 * You need to declare an interface to explicit the return type of the late parameter function.
3154 *
3155 * @example
3156 * interface INode {
3157 * childs: INode[]
3158 * }
3159 *
3160 * // TypeScript is'nt smart enough to infer self referencing types.
3161 * const Node = types.model({
3162 * childs: types.optional(types.array(types.late<any, INode>(() => Node)), [])
3163 * })
3164 *
3165 * @export
3166 * @alias types.late
3167 * @template S
3168 * @template T
3169 * @param {string} [name] The name to use for the type that will be returned.
3170 * @param {ILateType<S, T>} type A function that returns the type that will be defined.
3171 * @returns {IType<S, T>}
3172 */
3173function late(nameOrType, maybeType) {
3174 var name = typeof nameOrType === "string" ? nameOrType : "late(" + nameOrType.toString() + ")";
3175 var type = typeof nameOrType === "string" ? maybeType : nameOrType;
3176 // checks that the type is actually a late type
3177 if (process.env.NODE_ENV !== "production") {
3178 if (!(typeof type === "function" && type.length === 0))
3179 fail("Invalid late type, expected a function with zero arguments that returns a type, got: " +
3180 type);
3181 }
3182 return new Late(name, type);
3183}
3184
3185/**
3186 * Can be used to create an string based enumeration.
3187 * (note: this methods is just sugar for a union of string literals)
3188 *
3189 * @example
3190 * const TrafficLight = types.model({
3191 * color: types.enumeration("Color", ["Red", "Orange", "Green"])
3192 * })
3193 *
3194 * @export
3195 * @alias types.enumeration
3196 * @param {string} name descriptive name of the enumeration (optional)
3197 * @param {string[]} options possible values this enumeration can have
3198 * @returns {ISimpleType<string>}
3199 */
3200function enumeration(name, options) {
3201 var realOptions = typeof name === "string" ? options : name;
3202 // check all options
3203 if (process.env.NODE_ENV !== "production") {
3204 realOptions.forEach(function (option) {
3205 if (typeof option !== "string")
3206 fail("expected all options to be string, got " + type + " instead");
3207 });
3208 }
3209 var type = union.apply(void 0, realOptions.map(function (option) { return literal("" + option); }));
3210 if (typeof name === "string")
3211 type.name = name;
3212 return type;
3213}
3214
3215// tslint:disable-next-line:no_unused-variable
3216// tslint:disable-next-line:no_unused-variable
3217// tslint:disable-next-line:no_unused-variable
3218var types = {
3219 enumeration: enumeration,
3220 model: model,
3221 compose: compose,
3222 reference: reference,
3223 union: union,
3224 optional: optional,
3225 literal: literal,
3226 maybe: maybe,
3227 refinement: refinement,
3228 string: string,
3229 boolean: boolean,
3230 number: number,
3231 Date: DatePrimitive,
3232 map: map,
3233 array: array,
3234 frozen: frozen,
3235 identifier: identifier,
3236 late: late,
3237 undefined: undefinedType,
3238 null: nullType
3239};
3240
3241// based on: https://github.com/mobxjs/mobx-utils/blob/master/src/async-action.ts
3242/**
3243 * See [asynchronous actions](https://github.com/mobxjs/mobx-state-tree/blob/master/docs/async-actions.md).
3244 *
3245 * @export
3246 * @alias process
3247 * @returns {Promise}
3248 */
3249function process$1(asyncAction) {
3250 return createProcessSpawner(asyncAction.name, asyncAction);
3251}
3252function createProcessSpawner(name, generator) {
3253 var spawner = function processSpawner() {
3254 // Implementation based on https://github.com/tj/co/blob/master/index.js
3255 var runId = getNextActionId();
3256 var baseContext = getActionContext();
3257 var args = arguments;
3258 function wrap(fn, type, arg) {
3259 fn.$mst_middleware = spawner.$mst_middleware; // pick up any middleware attached to the process
3260 runWithActionContext({
3261 name: name,
3262 type: type,
3263 id: runId,
3264 args: [arg],
3265 tree: baseContext.tree,
3266 context: baseContext.context,
3267 parentId: baseContext.id,
3268 rootId: baseContext.rootId
3269 }, fn);
3270 }
3271 return new Promise(function (resolve, reject) {
3272 var gen;
3273 var init = function asyncActionInit() {
3274 gen = generator.apply(null, arguments);
3275 onFulfilled(undefined); // kick off the process
3276 };
3277 init.$mst_middleware = spawner.$mst_middleware;
3278 runWithActionContext({
3279 name: name,
3280 type: "process_spawn",
3281 id: runId,
3282 args: argsToArray(args),
3283 tree: baseContext.tree,
3284 context: baseContext.context,
3285 parentId: baseContext.id,
3286 rootId: baseContext.rootId
3287 }, init);
3288 function onFulfilled(res) {
3289 var ret;
3290 try {
3291 // prettier-ignore
3292 wrap(function (r) { ret = gen.next(r); }, "process_resume", res);
3293 }
3294 catch (e) {
3295 // prettier-ignore
3296 setImmediate(function () {
3297 wrap(function (r) { reject(e); }, "process_throw", e);
3298 });
3299 return;
3300 }
3301 next(ret);
3302 return;
3303 }
3304 function onRejected(err) {
3305 var ret;
3306 try {
3307 // prettier-ignore
3308 wrap(function (r) { ret = gen.throw(r); }, "process_resume_error", err); // or yieldError?
3309 }
3310 catch (e) {
3311 // prettier-ignore
3312 setImmediate(function () {
3313 wrap(function (r) { reject(e); }, "process_throw", e);
3314 });
3315 return;
3316 }
3317 next(ret);
3318 }
3319 function next(ret) {
3320 if (ret.done) {
3321 // prettier-ignore
3322 setImmediate(function () {
3323 wrap(function (r) { resolve(r); }, "process_return", ret.value);
3324 });
3325 return;
3326 }
3327 // TODO: support more type of values? See https://github.com/tj/co/blob/249bbdc72da24ae44076afd716349d2089b31c4c/index.js#L100
3328 if (!ret.value || typeof ret.value.then !== "function")
3329 fail("Only promises can be yielded to `async`, got: " + ret);
3330 return ret.value.then(onFulfilled, onRejected);
3331 }
3332 });
3333 };
3334 return spawner;
3335}
3336
3337function serializeArgument(node, actionName, index, arg) {
3338 if (arg instanceof Date)
3339 return { $MST_DATE: arg.getTime() };
3340 if (isPrimitive(arg))
3341 return arg;
3342 // 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
3343 // 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
3344 if (isStateTreeNode(arg))
3345 return serializeTheUnserializable("[MSTNode: " + getType(arg).name + "]");
3346 if (typeof arg === "function")
3347 return serializeTheUnserializable("[function]");
3348 if (typeof arg === "object" && !isPlainObject(arg) && !isArray(arg))
3349 return serializeTheUnserializable("[object " + ((arg && arg.constructor && arg.constructor.name) || "Complex Object") + "]");
3350 try {
3351 // Check if serializable, cycle free etc...
3352 // MWE: there must be a better way....
3353 JSON.stringify(arg); // or throws
3354 return arg;
3355 }
3356 catch (e) {
3357 return serializeTheUnserializable("" + e);
3358 }
3359}
3360function deserializeArgument(adm, value) {
3361 if (value && typeof value === "object" && "$MST_DATE" in value)
3362 return new Date(value["$MST_DATE"]);
3363 return value;
3364}
3365function serializeTheUnserializable(baseType) {
3366 return {
3367 $MST_UNSERIALIZABLE: true,
3368 type: baseType
3369 };
3370}
3371/**
3372 * Applies an action or a series of actions in a single MobX transaction.
3373 * Does not return any value
3374 * Takes an action description as produced by the `onAction` middleware.
3375 *
3376 * @export
3377 * @param {Object} target
3378 * @param {IActionCall[]} actions
3379 * @param {IActionCallOptions} [options]
3380 */
3381function applyAction(target, actions) {
3382 // check all arguments
3383 if (process.env.NODE_ENV !== "production") {
3384 if (!isStateTreeNode(target))
3385 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
3386 if (typeof actions !== "object")
3387 fail("expected second argument to be an object or array, got " + actions + " instead");
3388 }
3389 runInAction(function () {
3390 asArray(actions).forEach(function (action$$1) { return baseApplyAction(target, action$$1); });
3391 });
3392}
3393function baseApplyAction(target, action$$1) {
3394 var resolvedTarget = tryResolve(target, action$$1.path || "");
3395 if (!resolvedTarget)
3396 return fail("Invalid action path: " + (action$$1.path || ""));
3397 var node = getStateTreeNode(resolvedTarget);
3398 // Reserved functions
3399 if (action$$1.name === "@APPLY_PATCHES") {
3400 return applyPatch.call(null, resolvedTarget, action$$1.args[0]);
3401 }
3402 if (action$$1.name === "@APPLY_SNAPSHOT") {
3403 return applySnapshot.call(null, resolvedTarget, action$$1.args[0]);
3404 }
3405 if (!(typeof resolvedTarget[action$$1.name] === "function"))
3406 fail("Action '" + action$$1.name + "' does not exist in '" + node.path + "'");
3407 return resolvedTarget[action$$1.name].apply(resolvedTarget, action$$1.args ? action$$1.args.map(function (v) { return deserializeArgument(node, v); }) : []);
3408}
3409/**
3410 * Small abstraction around `onAction` and `applyAction`, attaches an action listener to a tree and records all the actions emitted.
3411 * Returns an recorder object with the following signature:
3412 *
3413 * @example
3414 * export interface IActionRecorder {
3415 * // the recorded actions
3416 * actions: ISerializedActionCall[]
3417 * // stop recording actions
3418 * stop(): any
3419 * // apply all the recorded actions on the given object
3420 * replay(target: IStateTreeNode): any
3421 * }
3422 *
3423 * @export
3424 * @param {IStateTreeNode} subject
3425 * @returns {IPatchRecorder}
3426 */
3427function recordActions(subject) {
3428 // check all arguments
3429 if (process.env.NODE_ENV !== "production") {
3430 if (!isStateTreeNode(subject))
3431 fail("expected first argument to be a mobx-state-tree node, got " + subject + " instead");
3432 }
3433 var recorder = {
3434 actions: [],
3435 stop: function () { return disposer(); },
3436 replay: function (target) {
3437 applyAction(target, recorder.actions);
3438 }
3439 };
3440 var disposer = onAction(subject, recorder.actions.push.bind(recorder.actions));
3441 return recorder;
3442}
3443/**
3444 * Registers a function that will be invoked for each action that is called on the provided model instance, or to any of its children.
3445 * 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.
3446 * Action can also be intercepted by middleware using addMiddleware to change the function call before it will be run.
3447 *
3448 * Not all action arguments might be serializable. For unserializable arguments, a struct like `{ $MST_UNSERIALIZABLE: true, type: "someType" }` will be generated.
3449 * 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).
3450 * 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.
3451 *
3452 * @export
3453 * @param {IStateTreeNode} target
3454 * @param {(call: ISerializedActionCall) => void} listener
3455 * @param attachAfter {boolean} (default false) fires the listener *after* the action has executed instead of before.
3456 * @returns {IDisposer}
3457 */
3458function onAction(target, listener, attachAfter) {
3459 if (attachAfter === void 0) { attachAfter = false; }
3460 // check all arguments
3461 if (process.env.NODE_ENV !== "production") {
3462 if (!isStateTreeNode(target))
3463 fail("expected first argument to be a mobx-state-tree node, got " + target + " instead");
3464 if (!isRoot(target))
3465 console.warn("[mobx-state-tree] Warning: Attaching onAction listeners to non root nodes is dangerous: No events will be emitted for actions initiated higher up in the tree.");
3466 if (!isProtected(target))
3467 console.warn("[mobx-state-tree] Warning: Attaching onAction listeners to non protected nodes is dangerous: No events will be emitted for direct modifications without action.");
3468 }
3469 function fireListener(rawCall) {
3470 if (rawCall.type === "action" && rawCall.id === rawCall.rootId) {
3471 var sourceNode_1 = getStateTreeNode(rawCall.context);
3472 listener({
3473 name: rawCall.name,
3474 path: getStateTreeNode(target).getRelativePathTo(sourceNode_1),
3475 args: rawCall.args.map(function (arg, index) {
3476 return serializeArgument(sourceNode_1, rawCall.name, index, arg);
3477 })
3478 });
3479 }
3480 }
3481 return addMiddleware(target, attachAfter
3482 ? function onActionMiddleware(rawCall, next) {
3483 var res = next(rawCall);
3484 fireListener(rawCall);
3485 return res;
3486 }
3487 : function onActionMiddleware(rawCall, next) {
3488 fireListener(rawCall);
3489 return next(rawCall);
3490 });
3491}
3492
3493/**
3494 * Convenience utility to create action based middleware that supports async processes more easily.
3495 * All hooks are called for both synchronous and asynchronous actions. Except that either `onSuccess` or `onFail` is called
3496 *
3497 * The create middleware tracks the process of an action (assuming it passes the `filter`).
3498 * `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.
3499 *
3500 * See the `atomic` middleware for an example
3501 *
3502 * @export
3503 * @template T
3504 * @template any
3505 * @param {{
3506 * filter?: (call: IMiddlewareEvent) => boolean
3507 * onStart: (call: IMiddlewareEvent) => T
3508 * onResume: (call: IMiddlewareEvent, context: T) => void
3509 * onSuspend: (call: IMiddlewareEvent, context: T) => void
3510 * onSuccess: (call: IMiddlewareEvent, context: T, result: any) => void
3511 * onFail: (call: IMiddlewareEvent, context: T, error: any) => void
3512 * }} hooks
3513 * @returns {IMiddlewareHandler}
3514 */
3515function createActionTrackingMiddleware(hooks) {
3516 var runningActions = new Map();
3517 return function actionTrackingMiddleware(call, next) {
3518 switch (call.type) {
3519 case "action": {
3520 if (!hooks.filter || hooks.filter(call) === true) {
3521 var context = hooks.onStart(call);
3522 hooks.onResume(call, context);
3523 runningActions.set(call.id, {
3524 call: call,
3525 context: context,
3526 async: false
3527 });
3528 try {
3529 var res = next(call);
3530 hooks.onSuspend(call, context);
3531 if (runningActions.get(call.id).async === false) {
3532 hooks.onSuccess(call, context, res);
3533 }
3534 return res;
3535 }
3536 catch (e) {
3537 hooks.onFail(call, context, e);
3538 throw e;
3539 }
3540 }
3541 else {
3542 return next(call);
3543 }
3544 }
3545 case "process_spawn": {
3546 var root = runningActions.get(call.rootId);
3547 root.async = true;
3548 return next(call);
3549 }
3550 case "process_resume":
3551 case "process_resume_error": {
3552 var root = runningActions.get(call.rootId);
3553 hooks.onResume(call, root.context);
3554 try {
3555 return next(call);
3556 }
3557 finally {
3558 hooks.onSuspend(call, root.context);
3559 }
3560 }
3561 case "process_throw": {
3562 var root = runningActions.get(call.rootId);
3563 runningActions.delete(call.id);
3564 hooks.onFail(call, root.context, call.args[0]);
3565 return next(call);
3566 }
3567 case "process_return": {
3568 var root = runningActions.get(call.rootId);
3569 runningActions.delete(call.id);
3570 hooks.onSuccess(call, root.context, call.args[0]);
3571 return next(call);
3572 }
3573 }
3574 };
3575}
3576
3577// Fix some circular deps:
3578
3579export { types, typecheckPublic as typecheck, escapeJsonPath, unescapeJsonPath, decorate, addMiddleware, process$1 as process, isStateTreeNode, applyAction, onAction, recordActions, createActionTrackingMiddleware, getType, getChildType, onPatch, onSnapshot, applyPatch, recordPatches, protect, unprotect, isProtected, applySnapshot, getSnapshot, hasParent, getParent, getRoot, getPath, getPathParts, isRoot, resolvePath, resolveIdentifier, tryResolve, getRelativePath, clone, detach, destroy, isAlive, addDisposer, getEnv, walk };