UNPKG

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