UNPKG

mobx-utils

Version:

Utility functions and common patterns for MobX

1,791 lines • 77.7 kB
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('mobx')) :
    typeof define === 'function' && define.amd ? define(['exports', 'mobx'], factory) :
    (global = global || self, factory(global.mobxUtils = {}, global.mobx));
}(this, (function (exports, mobx) { 'use strict';

    var NOOP = function () { };
    var IDENTITY = function (_) { return _; };
    function fail(message) {
        throw new Error("[mobx-utils] " + message);
    }
    function invariant(cond, message) {
        if (message === void 0) { message = "Illegal state"; }
        if (!cond)
            fail(message);
    }
    function addHiddenProp(object, propName, value) {
        Object.defineProperty(object, propName, {
            enumerable: false,
            writable: true,
            configurable: true,
            value: value,
        });
    }
    var deepFields = function (x) {
        return (x &&
            x !== Object.prototype &&
            Object.getOwnPropertyNames(x).concat(deepFields(Object.getPrototypeOf(x)) || []));
    };
    var distinctDeepFields = function (x) {
        var deepFieldsIndistinct = deepFields(x);
        var deepFieldsDistinct = deepFieldsIndistinct.filter(function (item, index) { return deepFieldsIndistinct.indexOf(item) === index; });
        return deepFieldsDistinct;
    };
    var getAllMethodsAndProperties = function (x) {
        return distinctDeepFields(x).filter(function (name) { return name !== "constructor" && !~name.indexOf("__"); });
    };

    var PENDING = "pending";
    var FULFILLED = "fulfilled";
    var REJECTED = "rejected";
    function caseImpl(handlers) {
        switch (this.state) {
            case PENDING:
                return handlers.pending && handlers.pending(this.value);
            case REJECTED:
                return handlers.rejected && handlers.rejected(this.value);
            case FULFILLED:
                return handlers.fulfilled ? handlers.fulfilled(this.value) : this.value;
        }
    }
    /**
     * `fromPromise` takes a Promise, extends it with 2 observable properties that track
     * the status of the promise and returns it. The returned object has the following observable properties:
     *  - `value`: either the initial value, the value the Promise resolved to, or the value the Promise was rejected with. use `.state` if you need to be able to tell the difference.
     *  - `state`: one of `"pending"`, `"fulfilled"` or `"rejected"`
     *
     * And the following methods:
     * - `case({fulfilled, rejected, pending})`: maps over the result using the provided handlers, or returns `undefined` if a handler isn't available for the current promise state.
     *
     * The returned object implements `PromiseLike<TValue>`, so you can chain additional `Promise` handlers using `then`. You may also use it with `await` in `async` functions.
     *
     * Note that the status strings are available as constants:
     * `mobxUtils.PENDING`, `mobxUtils.REJECTED`, `mobxUtil.FULFILLED`
     *
     * fromPromise takes an optional second argument, a previously created `fromPromise` based observable.
     * This is useful to replace one promise based observable with another, without going back to an intermediate
     * "pending" promise state while fetching data. For example:
     *
     * @example
     * \@observer
     * class SearchResults extends React.Component {
     *   \@observable.ref searchResults
     *
     *   componentDidUpdate(nextProps) {
     *     if (nextProps.query !== this.props.query)
     *       this.searchResults = fromPromise(
     *         window.fetch("/search?q=" + nextProps.query),
     *         // by passing, we won't render a pending state if we had a successful search query before
     *         // rather, we will keep showing the previous search results, until the new promise resolves (or rejects)
     *         this.searchResults
     *       )
     *   }
     *
     *   render() {
     *     return this.searchResults.case({
     *        pending: (staleValue) => {
     *          return staleValue || "searching" // <- value might set to previous results while the promise is still pending
     *        },
     *        fulfilled: (value) => {
     *          return value // the fresh results
     *        },
     *        rejected: (error) => {
     *          return "Oops: " + error
     *        }
     *     })
     *   }
     * }
     *
     * Observable promises can be created immediately in a certain state using
     * `fromPromise.reject(reason)` or `fromPromise.resolve(value?)`.
     * The main advantage of `fromPromise.resolve(value)` over `fromPromise(Promise.resolve(value))` is that the first _synchronously_ starts in the desired state.
     *
     * It is possible to directly create a promise using a resolve, reject function:
     * `fromPromise((resolve, reject) => setTimeout(() => resolve(true), 1000))`
     *
     * @example
     * const fetchResult = fromPromise(fetch("http://someurl"))
     *
     * // combine with when..
     * when(
     *   () => fetchResult.state !== "pending",
     *   () => {
     *     console.log("Got ", fetchResult.value)
     *   }
     * )
     *
     * // or a mobx-react component..
     * const myComponent = observer(({ fetchResult }) => {
     *   switch(fetchResult.state) {
     *      case "pending": return <div>Loading...</div>
     *      case "rejected": return <div>Ooops... {fetchResult.value}</div>
     *      case "fulfilled": return <div>Gotcha: {fetchResult.value}</div>
     *   }
     * })
     *
     * // or using the case method instead of switch:
     *
     * const myComponent = observer(({ fetchResult }) =>
     *   fetchResult.case({
     *     pending:   () => <div>Loading...</div>,
     *     rejected:  error => <div>Ooops.. {error}</div>,
     *     fulfilled: value => <div>Gotcha: {value}</div>,
     *   }))
     *
     * // chain additional handler(s) to the resolve/reject:
     *
     * fetchResult.then(
     *   (result) =>  doSomeTransformation(result),
     *   (rejectReason) => console.error('fetchResult was rejected, reason: ' + rejectReason)
     * ).then(
     *   (transformedResult) => console.log('transformed fetchResult: ' + transformedResult)
     * )
     *
     * @param origPromise The promise which will be observed
     * @param oldPromise The previously observed promise
     * @returns origPromise with added properties and methods described above.
     */
    function fromPromise(origPromise, oldPromise) {
        invariant(arguments.length <= 2, "fromPromise expects up to two arguments");
        invariant(typeof origPromise === "function" ||
            (typeof origPromise === "object" &&
                origPromise &&
                typeof origPromise.then === "function"), "Please pass a promise or function to fromPromise");
        if (origPromise.isPromiseBasedObservable === true)
            return origPromise;
        if (typeof origPromise === "function") {
            // If it is a (reject, resolve function, wrap it)
            origPromise = new Promise(origPromise);
        }
        var promise = origPromise;
        origPromise.then(mobx.action("observableFromPromise-resolve", function (value) {
            promise.value = value;
            promise.state = FULFILLED;
        }), mobx.action("observableFromPromise-reject", function (reason) {
            promise.value = reason;
            promise.state = REJECTED;
        }));
        promise.isPromiseBasedObservable = true;
        promise.case = caseImpl;
        var oldData = oldPromise && (oldPromise.state === FULFILLED || oldPromise.state === PENDING)
            ? oldPromise.value
            : undefined;
        mobx.extendObservable(promise, {
            value: oldData,
            state: PENDING,
        }, {}, { deep: false });
        return promise;
    }
    (function (fromPromise) {
        fromPromise.reject = mobx.action("fromPromise.reject", function (reason) {
            var p = fromPromise(Promise.reject(reason));
            p.state = REJECTED;
            p.value = reason;
            return p;
        });
        function resolveBase(value) {
            if (value === void 0) { value = undefined; }
            var p = fromPromise(Promise.resolve(value));
            p.state = FULFILLED;
            p.value = value;
            return p;
        }
        fromPromise.resolve = mobx.action("fromPromise.resolve", resolveBase);
    })(fromPromise || (fromPromise = {}));
    /**
     * Returns true if the provided value is a promise-based observable.
     * @param value any
     * @returns {boolean}
     */
    function isPromiseBasedObservable(value) {
        return value && value.isPromiseBasedObservable === true;
    }

    var __spreadArrays = (undefined && undefined.__spreadArrays) || function () {
        for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
        for (var r = Array(s), k = 0, i = 0; i < il; i++)
            for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
                r[k] = a[j];
        return r;
    };
    /**
     * Moves an item from one position to another, checking that the indexes given are within bounds.
     *
     * @example
     * const source = observable([1, 2, 3])
     * moveItem(source, 0, 1)
     * console.log(source.map(x => x)) // [2, 1, 3]
     *
     * @export
     * @param {ObservableArray<T>} target
     * @param {number} fromIndex
     * @param {number} toIndex
     * @returns {ObservableArray<T>}
     */
    function moveItem(target, fromIndex, toIndex) {
        checkIndex(target, fromIndex);
        checkIndex(target, toIndex);
        if (fromIndex === toIndex) {
            return;
        }
        var oldItems = target.slice();
        var newItems;
        if (fromIndex < toIndex) {
            newItems = __spreadArrays(oldItems.slice(0, fromIndex), oldItems.slice(fromIndex + 1, toIndex + 1), [
                oldItems[fromIndex]
            ], oldItems.slice(toIndex + 1));
        }
        else {
            // toIndex < fromIndex
            newItems = __spreadArrays(oldItems.slice(0, toIndex), [
                oldItems[fromIndex]
            ], oldItems.slice(toIndex, fromIndex), oldItems.slice(fromIndex + 1));
        }
        target.replace(newItems);
        return target;
    }
    /**
     * Checks whether the specified index is within bounds. Throws if not.
     *
     * @private
     * @param {ObservableArray<any>} target
     * @param {number }index
     */
    function checkIndex(target, index) {
        if (index < 0) {
            throw new Error("[mobx.array] Index out of bounds: " + index + " is negative");
        }
        var length = target.length;
        if (index >= length) {
            throw new Error("[mobx.array] Index out of bounds: " + index + " is not smaller than " + length);
        }
    }

    /**
     * `lazyObservable` creates an observable around a `fetch` method that will not be invoked
     * until the observable is needed the first time.
     * The fetch method receives a `sink` callback which can be used to replace the
     * current value of the lazyObservable. It is allowed to call `sink` multiple times
     * to keep the lazyObservable up to date with some external resource.
     *
     * Note that it is the `current()` call itself which is being tracked by MobX,
     * so make sure that you don't dereference to early.
     *
     * @example
     * const userProfile = lazyObservable(
     *   sink => fetch("/myprofile").then(profile => sink(profile))
     * )
     *
     * // use the userProfile in a React component:
     * const Profile = observer(({ userProfile }) =>
     *   userProfile.current() === undefined
     *   ? <div>Loading user profile...</div>
     *   : <div>{userProfile.current().displayName}</div>
     * )
     *
     * // triggers refresh the userProfile
     * userProfile.refresh()
     *
     * @param {(sink: (newValue: T) => void) => void} fetch method that will be called the first time the value of this observable is accessed. The provided sink can be used to produce a new value, synchronously or asynchronously
     * @param {T} [initialValue=undefined] optional initialValue that will be returned from `current` as long as the `sink` has not been called at least once
     * @returns {{
     *     current(): T,
     *     refresh(): T,
     *     reset(): T
     *     pending: boolean
     * }}
     */
    function lazyObservable(fetch, initialValue) {
        if (initialValue === void 0) { initialValue = undefined; }
        var started = false;
        var value = mobx.observable.box(initialValue, { deep: false });
        var pending = mobx.observable.box(false);
        var currentFnc = function () {
            if (!started) {
                started = true;
                mobx._allowStateChanges(true, function () {
                    pending.set(true);
                });
                fetch(function (newValue) {
                    mobx._allowStateChanges(true, function () {
                        value.set(newValue);
                        pending.set(false);
                    });
                });
            }
            return value.get();
        };
        var resetFnc = mobx.action("lazyObservable-reset", function () {
            started = false;
            value.set(initialValue);
            return value.get();
        });
        return {
            current: currentFnc,
            refresh: function () {
                if (started) {
                    started = false;
                    return currentFnc();
                }
                else {
                    return value.get();
                }
            },
            reset: function () {
                return resetFnc();
            },
            get pending() {
                return pending.get();
            },
        };
    }

    /**
     * `fromResource` creates an observable whose current state can be inspected using `.current()`,
     * and which can be kept in sync with some external datasource that can be subscribed to.
     *
     * The created observable will only subscribe to the datasource if it is in use somewhere,
     * (un)subscribing when needed. To enable `fromResource` to do that two callbacks need to be provided,
     * one to subscribe, and one to unsubscribe. The subscribe callback itself will receive a `sink` callback, which can be used
     * to update the current state of the observable, allowing observes to react.
     *
     * Whatever is passed to `sink` will be returned by `current()`. The values passed to the sink will not be converted to
     * observables automatically, but feel free to do so.
     * It is the `current()` call itself which is being tracked,
     * so make sure that you don't dereference to early.
     *
     * For inspiration, an example integration with the apollo-client on [github](https://github.com/apollostack/apollo-client/issues/503#issuecomment-241101379),
     * or the [implementation](https://github.com/mobxjs/mobx-utils/blob/1d17cf7f7f5200937f68cc0b5e7ec7f3f71dccba/src/now.ts#L43-L57) of `mobxUtils.now`
     *
     * The following example code creates an observable that connects to a `dbUserRecord`,
     * which comes from an imaginary database and notifies when it has changed.
     *
     * @example
     * function createObservableUser(dbUserRecord) {
     *   let currentSubscription;
     *   return fromResource(
     *     (sink) => {
     *       // sink the current state
     *       sink(dbUserRecord.fields)
     *       // subscribe to the record, invoke the sink callback whenever new data arrives
     *       currentSubscription = dbUserRecord.onUpdated(() => {
     *         sink(dbUserRecord.fields)
     *       })
     *     },
     *     () => {
     *       // the user observable is not in use at the moment, unsubscribe (for now)
     *       dbUserRecord.unsubscribe(currentSubscription)
     *     }
     *   )
     * }
     *
     * // usage:
     * const myUserObservable = createObservableUser(myDatabaseConnector.query("name = 'Michel'"))
     *
     * // use the observable in autorun
     * autorun(() => {
     *   // printed everytime the database updates its records
     *   console.log(myUserObservable.current().displayName)
     * })
     *
     * // ... or a component
     * const userComponent = observer(({ user }) =>
     *   <div>{user.current().displayName}</div>
     * )
     *
     * @export
     * @template T
     * @param {(sink: (newValue: T) => void) => void} subscriber
     * @param {IDisposer} [unsubscriber=NOOP]
     * @param {T} [initialValue=undefined] the data that will be returned by `get()` until the `sink` has emitted its first data
     * @returns {{
     *     current(): T;
     *     dispose(): void;
     *     isAlive(): boolean;
     * }}
     */
    function fromResource(subscriber, unsubscriber, initialValue) {
        if (unsubscriber === void 0) { unsubscriber = NOOP; }
        if (initialValue === void 0) { initialValue = undefined; }
        var isActive = false;
        var isDisposed = false;
        var value = initialValue;
        var suspender = function () {
            if (isActive) {
                isActive = false;
                unsubscriber();
            }
        };
        var atom = mobx.createAtom("ResourceBasedObservable", function () {
            invariant(!isActive && !isDisposed);
            isActive = true;
            subscriber(function (newValue) {
                mobx._allowStateChanges(true, function () {
                    value = newValue;
                    atom.reportChanged();
                });
            });
        }, suspender);
        return {
            current: function () {
                invariant(!isDisposed, "subscribingObservable has already been disposed");
                var isBeingTracked = atom.reportObserved();
                if (!isBeingTracked && !isActive)
                    console.warn("Called `get` of a subscribingObservable outside a reaction. Current value will be returned but no new subscription has started");
                return value;
            },
            dispose: function () {
                isDisposed = true;
                suspender();
            },
            isAlive: function () { return isActive; },
        };
    }

    var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
        var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
        if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
        else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
        return c > 3 && r && Object.defineProperty(target, key, r), r;
    };
    function observableSymbol() {
        return (typeof Symbol === "function" && Symbol.observable) || "@@observable";
    }
    /**
     * Converts an expression to an observable stream (a.k.a. TC 39 Observable / RxJS observable).
     * The provided expression is tracked by mobx as long as there are subscribers, automatically
     * emitting when new values become available. The expressions respect (trans)actions.
     *
     * @example
     *
     * const user = observable({
     *   firstName: "C.S",
     *   lastName: "Lewis"
     * })
     *
     * Rx.Observable
     *   .from(mobxUtils.toStream(() => user.firstname + user.lastName))
     *   .scan(nameChanges => nameChanges + 1, 0)
     *   .subscribe(nameChanges => console.log("Changed name ", nameChanges, "times"))
     *
     * @export
     * @template T
     * @param {() => T} expression
     * @param {boolean} fireImmediately (by default false)
     * @returns {IObservableStream<T>}
     */
    function toStream(expression, fireImmediately) {
        var _a;
        if (fireImmediately === void 0) { fireImmediately = false; }
        var computedValue = mobx.computed(expression);
        return _a = {
                subscribe: function (observer) {
                    if ("function" === typeof observer) {
                        return {
                            unsubscribe: mobx.observe(computedValue, function (_a) {
                                var newValue = _a.newValue;
                                return observer(newValue);
                            }, fireImmediately),
                        };
                    }
                    if (observer && "object" === typeof observer && observer.next) {
                        return {
                            unsubscribe: mobx.observe(computedValue, function (_a) {
                                var newValue = _a.newValue;
                                return observer.next(newValue);
                            }, fireImmediately),
                        };
                    }
                    return {
                        unsubscribe: function () { },
                    };
                }
            },
            _a[observableSymbol()] = function () {
                return this;
            },
            _a;
    }
    var StreamListener = /** @class */ (function () {
        function StreamListener(observable, initialValue) {
            var _this = this;
            Object.defineProperty(this, "current", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: void 0
            });
            Object.defineProperty(this, "subscription", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: void 0
            });
            mobx.makeObservable(this);
            mobx.runInAction(function () {
                _this.current = initialValue;
                _this.subscription = observable.subscribe(_this);
            });
        }
        Object.defineProperty(StreamListener.prototype, "dispose", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function () {
                if (this.subscription) {
                    this.subscription.unsubscribe();
                }
            }
        });
        Object.defineProperty(StreamListener.prototype, "next", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function (value) {
                this.current = value;
            }
        });
        Object.defineProperty(StreamListener.prototype, "complete", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function () {
                this.dispose();
            }
        });
        Object.defineProperty(StreamListener.prototype, "error", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function (value) {
                this.current = value;
                this.dispose();
            }
        });
        __decorate([
            mobx.observable.ref
        ], StreamListener.prototype, "current", void 0);
        __decorate([
            mobx.action.bound
        ], StreamListener.prototype, "next", null);
        __decorate([
            mobx.action.bound
        ], StreamListener.prototype, "complete", null);
        __decorate([
            mobx.action.bound
        ], StreamListener.prototype, "error", null);
        return StreamListener;
    }());
    function fromStream(observable, initialValue) {
        if (initialValue === void 0) { initialValue = undefined; }
        return new StreamListener(observable, initialValue);
    }

    var __assign = (undefined && undefined.__assign) || function () {
        __assign = Object.assign || function(t) {
            for (var s, i = 1, n = arguments.length; i < n; i++) {
                s = arguments[i];
                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                    t[p] = s[p];
            }
            return t;
        };
        return __assign.apply(this, arguments);
    };
    var __decorate$1 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
        var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
        if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
        else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
        return c > 3 && r && Object.defineProperty(target, key, r), r;
    };
    var RESERVED_NAMES = ["model", "reset", "submit", "isDirty", "isPropertyDirty", "resetProperty"];
    var ViewModel = /** @class */ (function () {
        function ViewModel(model) {
            var _this = this;
            Object.defineProperty(this, "model", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: model
            });
            Object.defineProperty(this, "localValues", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: mobx.observable.map({})
            });
            Object.defineProperty(this, "localComputedValues", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: mobx.observable.map({})
            });
            Object.defineProperty(this, "isPropertyDirty", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: function (key) {
                    return _this.localValues.has(key);
                }
            });
            mobx.makeObservable(this);
            invariant(mobx.isObservableObject(model), "createViewModel expects an observable object");
            var ownMethodsAndProperties = getAllMethodsAndProperties(this);
            // use this helper as Object.getOwnPropertyNames doesn't return getters
            getAllMethodsAndProperties(model).forEach(function (key) {
                var _a;
                if (ownMethodsAndProperties.includes(key)) {
                    return;
                }
                if (key === mobx.$mobx || key === "__mobxDidRunLazyInitializers") {
                    return;
                }
                invariant(RESERVED_NAMES.indexOf(key) === -1, "The propertyname " + key + " is reserved and cannot be used with viewModels");
                if (mobx.isComputedProp(model, key)) {
                    var computedBox = mobx._getAdministration(model, key); // Fixme: there is no clear api to get the derivation
                    var get = computedBox.derivation.bind(_this);
                    var set = (_a = computedBox.setter_) === null || _a === void 0 ? void 0 : _a.bind(_this);
                    _this.localComputedValues.set(key, mobx.computed(get, { set: set }));
                }
                var descriptor = Object.getOwnPropertyDescriptor(model, key);
                var additionalDescriptor = descriptor ? { enumerable: descriptor.enumerable } : {};
                Object.defineProperty(_this, key, __assign(__assign({}, additionalDescriptor), { configurable: true, get: function () {
                        if (mobx.isComputedProp(model, key))
                            return _this.localComputedValues.get(key).get();
                        if (_this.isPropertyDirty(key))
                            return _this.localValues.get(key);
                        else
                            return _this.model[key];
                    }, set: mobx.action(function (value) {
                        if (mobx.isComputedProp(model, key)) {
                            _this.localComputedValues.get(key).set(value);
                        }
                        else if (value !== _this.model[key]) {
                            _this.localValues.set(key, value);
                        }
                        else {
                            _this.localValues.delete(key);
                        }
                    }) }));
            });
        }
        Object.defineProperty(ViewModel.prototype, "isDirty", {
            get: function () {
                return this.localValues.size > 0;
            },
            enumerable: false,
            configurable: true
        });
        Object.defineProperty(ViewModel.prototype, "changedValues", {
            get: function () {
                return new Map(this.localValues);
            },
            enumerable: false,
            configurable: true
        });
        Object.defineProperty(ViewModel.prototype, "submit", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function () {
                var _this = this;
                mobx.keys(this.localValues).forEach(function (key) {
                    var source = _this.localValues.get(key);
                    var destination = _this.model[key];
                    if (mobx.isObservableArray(destination)) {
                        destination.replace(source);
                    }
                    else if (mobx.isObservableMap(destination)) {
                        destination.clear();
                        destination.merge(source);
                    }
                    else if (!mobx.isComputed(source)) {
                        _this.model[key] = source;
                    }
                });
                this.localValues.clear();
            }
        });
        Object.defineProperty(ViewModel.prototype, "reset", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function () {
                this.localValues.clear();
            }
        });
        Object.defineProperty(ViewModel.prototype, "resetProperty", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function (key) {
                this.localValues.delete(key);
            }
        });
        __decorate$1([
            mobx.computed
        ], ViewModel.prototype, "isDirty", null);
        __decorate$1([
            mobx.computed
        ], ViewModel.prototype, "changedValues", null);
        __decorate$1([
            mobx.action.bound
        ], ViewModel.prototype, "submit", null);
        __decorate$1([
            mobx.action.bound
        ], ViewModel.prototype, "reset", null);
        __decorate$1([
            mobx.action.bound
        ], ViewModel.prototype, "resetProperty", null);
        return ViewModel;
    }());
    /**
     * `createViewModel` takes an object with observable properties (model)
     * and wraps a viewmodel around it. The viewmodel proxies all enumerable properties of the original model with the following behavior:
     *  - as long as no new value has been assigned to the viewmodel property, the original property will be returned.
     *  - any future change in the model will be visible in the viewmodel as well unless the viewmodel property was dirty at the time of the attempted change.
     *  - once a new value has been assigned to a property of the viewmodel, that value will be returned during a read of that property in the future. However, the original model remain untouched until `submit()` is called.
     *
     * The viewmodel exposes the following additional methods, besides all the enumerable properties of the model:
     * - `submit()`: copies all the values of the viewmodel to the model and resets the state
     * - `reset()`: resets the state of the viewmodel, abandoning all local modifications
     * - `resetProperty(propName)`: resets the specified property of the viewmodel
     * - `isDirty`: observable property indicating if the viewModel contains any modifications
     * - `isPropertyDirty(propName)`: returns true if the specified property is dirty
     * - `changedValues`: returns a key / value map with the properties that have been changed in the model so far
     * - `model`: The original model object for which this viewModel was created
     *
     * You may use observable arrays, maps and objects with `createViewModel` but keep in mind to assign fresh instances of those to the viewmodel's properties, otherwise you would end up modifying the properties of the original model.
     * Note that if you read a non-dirty property, viewmodel only proxies the read to the model. You therefore need to assign a fresh instance not only the first time you make the assignment but also after calling `reset()` or `submit()`.
     *
     * @example
     * class Todo {
     *   \@observable title = "Test"
     * }
     *
     * const model = new Todo()
     * const viewModel = createViewModel(model);
     *
     * autorun(() => console.log(viewModel.model.title, ",", viewModel.title))
     * // prints "Test, Test"
     * model.title = "Get coffee"
     * // prints "Get coffee, Get coffee", viewModel just proxies to model
     * viewModel.title = "Get tea"
     * // prints "Get coffee, Get tea", viewModel's title is now dirty, and the local value will be printed
     * viewModel.submit()
     * // prints "Get tea, Get tea", changes submitted from the viewModel to the model, viewModel is proxying again
     * viewModel.title = "Get cookie"
     * // prints "Get tea, Get cookie" // viewModel has diverged again
     * viewModel.reset()
     * // prints "Get tea, Get tea", changes of the viewModel have been abandoned
     *
     * @param {T} model
     * @returns {(T & IViewModel<T>)}
     * ```
     */
    function createViewModel(model) {
        return new ViewModel(model);
    }

    /**
     * MobX normally suspends any computed value that is not in use by any reaction,
     * and lazily re-evaluates the expression if needed outside a reaction while not in use.
     * `keepAlive` marks a computed value as always in use, meaning that it will always fresh, but never disposed automatically.
     *
     * @example
     * const obj = observable({
     *   number: 3,
     *   doubler: function() { return this.number * 2 }
     * })
     * const stop = keepAlive(obj, "doubler")
     *
     * @param {Object} target an object that has a computed property, created by `@computed` or `extendObservable`
     * @param {string} property the name of the property to keep alive
     * @returns {IDisposer} stops this keep alive so that the computed value goes back to normal behavior
     */
    /**
     * @example
     * const number = observable(3)
     * const doubler = computed(() => number.get() * 2)
     * const stop = keepAlive(doubler)
     * // doubler will now stay in sync reactively even when there are no further observers
     * stop()
     * // normal behavior, doubler results will be recomputed if not observed but needed, but lazily
     *
     * @param {IComputedValue<any>} computedValue created using the `computed` function
     * @returns {IDisposer} stops this keep alive so that the computed value goes back to normal behavior
     */
    function keepAlive(_1, _2) {
        var computed = mobx.getAtom(_1, _2);
        if (!computed)
            throw new Error("No computed provided, please provide an object created with `computed(() => expr)` or an object + property name");
        return mobx.observe(computed, function () { });
    }

    /**
     * `queueProcessor` takes an observable array, observes it and calls `processor`
     * once for each item added to the observable array, optionally debouncing the action
     *
     * @example
     * const pendingNotifications = observable([])
     * const stop = queueProcessor(pendingNotifications, msg => {
     *   // show Desktop notification
     *   new Notification(msg);
     * })
     *
     * // usage:
     * pendingNotifications.push("test!")
     *
     * @param {T[]} observableArray observable array instance to track
     * @param {(item: T) => void} processor action to call per item
     * @param {number} [debounce=0] optional debounce time in ms. With debounce 0 the processor will run synchronously
     * @returns {IDisposer} stops the processor
     */
    function queueProcessor(observableArray, processor, debounce) {
        if (debounce === void 0) { debounce = 0; }
        if (!mobx.isObservableArray(observableArray))
            throw new Error("Expected observable array as first argument");
        if (!mobx.isAction(processor))
            processor = mobx.action("queueProcessor", processor);
        var runner = function () {
            // construct a final set
            var items = observableArray.slice(0);
            // clear the queue for next iteration
            mobx.runInAction(function () { return observableArray.splice(0); });
            // fire processor
            items.forEach(processor);
        };
        if (debounce > 0)
            return mobx.autorun(runner, { delay: debounce });
        else
            return mobx.autorun(runner);
    }

    /**
     * `chunkProcessor` takes an observable array, observes it and calls `processor`
     * once for a chunk of items added to the observable array, optionally deboucing the action.
     * The maximum chunk size can be limited by number.
     * This allows both, splitting larger into smaller chunks or (when debounced) combining smaller
     * chunks and/or single items into reasonable chunks of work.
     *
     * @example
     * const trackedActions = observable([])
     * const stop = chunkProcessor(trackedActions, chunkOfMax10Items => {
     *   sendTrackedActionsToServer(chunkOfMax10Items);
     * }, 100, 10)
     *
     * // usage:
     * trackedActions.push("scrolled")
     * trackedActions.push("hoveredButton")
     * // when both pushes happen within 100ms, there will be only one call to server
     *
     * @param {T[]} observableArray observable array instance to track
     * @param {(item: T[]) => void} processor action to call per item
     * @param {number} [debounce=0] optional debounce time in ms. With debounce 0 the processor will run synchronously
     * @param {number} [maxChunkSize=0] optionally do not call on full array but smaller chunks. With 0 it will process the full array.
     * @returns {IDisposer} stops the processor
     */
    function chunkProcessor(observableArray, processor, debounce, maxChunkSize) {
        if (debounce === void 0) { debounce = 0; }
        if (maxChunkSize === void 0) { maxChunkSize = 0; }
        if (!mobx.isObservableArray(observableArray))
            throw new Error("Expected observable array as first argument");
        if (!mobx.isAction(processor))
            processor = mobx.action("chunkProcessor", processor);
        var runner = function () {
            var _loop_1 = function () {
                var chunkSize = maxChunkSize === 0
                    ? observableArray.length
                    : Math.min(observableArray.length, maxChunkSize);
                // construct a final set
                var items = observableArray.slice(0, chunkSize);
                // clear the slice for next iteration
                mobx.runInAction(function () { return observableArray.splice(0, chunkSize); });
                // fire processor
                processor(items);
            };
            while (observableArray.length > 0) {
                _loop_1();
            }
        };
        if (debounce > 0)
            return mobx.autorun(runner, { delay: debounce });
        else
            return mobx.autorun(runner);
    }

    var tickers = {};
    /**
     * Disposes of all the internal Observables created by invocations of `now()`.
     *
     * The use case for this is to ensure that unit tests can run independent of each other.
     * You should not call this in regular application code.
     *
     * @example
     * afterEach(() => {
     *     utils.resetNowInternalState()
     * })
     */
    function resetNowInternalState() {
        for (var _i = 0, _a = Object.getOwnPropertyNames(tickers); _i < _a.length; _i++) {
            var key = _a[_i];
            tickers[key].dispose();
            delete tickers[key];
        }
    }
    /**
     * Returns the current date time as epoch number.
     * The date time is read from an observable which is updated automatically after the given interval.
     * So basically it treats time as an observable.
     *
     * The function takes an interval as parameter, which indicates how often `now()` will return a new value.
     * If no interval is given, it will update each second. If "frame" is specified, it will update each time a
     * `requestAnimationFrame` is available.
     *
     * Multiple clocks with the same interval will automatically be synchronized.
     *
     * Countdown example: https://jsfiddle.net/mweststrate/na0qdmkw/
     *
     * @example
     *
     * const start = Date.now()
     *
     * autorun(() => {
     *   console.log("Seconds elapsed: ", (mobxUtils.now() - start) / 1000)
     * })
     *
     *
     * @export
     * @param {(number | "frame")} [interval=1000] interval in milliseconds about how often the interval should update
     * @returns
     */
    function now(interval) {
        if (interval === void 0) { interval = 1000; }
        if (!mobx._isComputingDerivation()) {
            // See #40
            return Date.now();
        }
        if (!tickers[interval]) {
            if (typeof interval === "number")
                tickers[interval] = createIntervalTicker(interval);
            else
                tickers[interval] = createAnimationFrameTicker();
        }
        return tickers[interval].current();
    }
    function createIntervalTicker(interval) {
        var subscriptionHandle;
        return fromResource(function (sink) {
            sink(Date.now());
            subscriptionHandle = setInterval(function () { return sink(Date.now()); }, interval);
        }, function () {
            clearInterval(subscriptionHandle);
        }, Date.now());
    }
    function createAnimationFrameTicker() {
        var frameBasedTicker = fromResource(function (sink) {
            sink(Date.now());
            function scheduleTick() {
                window.requestAnimationFrame(function () {
                    sink(Date.now());
                    if (frameBasedTicker.isAlive())
                        scheduleTick();
                });
            }
            scheduleTick();
        }, function () { }, Date.now());
        return frameBasedTicker;
    }

    /**
     *`expr` can be used to create temporary computed values inside computed values.
     * Nesting computed values is useful to create cheap computations in order to prevent expensive computations from needing to run.
     * In the following example the expression prevents that a component is rerender _each time_ the selection changes;
     * instead it will only rerenders when the current todo is (de)selected.
     *
     * `expr(func)` is an alias for `computed(func).get()`.
     * Please note that the function given to `expr` is evaluated _twice_ in the scenario that the overall expression value changes.
     * It is evaluated the first time when any observables it depends on change.
     * It is evaluated a second time when a change in its value triggers the outer computed or reaction to evaluate, which recreates and reevaluates the expression.
     *
     * In the following example, the expression prevents the `TodoView` component from being re-rendered if the selection changes elsewhere.
     * Instead, the component will only re-render when the relevant todo is (de)selected, which happens much less frequently.
     *
     * @example
     * const Todo = observer((props) => {
     *     const todo = props.todo
     *     const isSelected = mobxUtils.expr(() => props.viewState.selection === todo)
     * const TodoView = observer(({ todo, editorState }) => {
     *     const isSelected = mobxUtils.expr(() => editorState.selection === todo)
     *     return <div className={isSelected ? "todo todo-selected" : "todo"}>{todo.title}</div>
     * })
     */
    function expr(expr) {
        if (!mobx._isComputingDerivation())
            console.warn("'expr' should only be used inside other reactive functions.");
        // optimization: would be more efficient if the expr itself wouldn't be evaluated first on the next change, but just a 'changed' signal would be fired
        return mobx.computed(expr).get();
    }

    var __assign$1 = (undefined && undefined.__assign) || function () {
        __assign$1 = Object.assign || function(t) {
            for (var s, i = 1, n = arguments.length; i < n; i++) {
                s = arguments[i];
                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                    t[p] = s[p];
            }
            return t;
        };
        return __assign$1.apply(this, arguments);
    };
    var memoizationId = 0;
    /**
     * Creates a function that maps an object to a view.
     * The mapping is memoized.
     *
     * See the [transformer](#createtransformer-in-detail) section for more details.
     *
     * @param transformer
     * @param onCleanup
     */
    function createTransformer(transformer, arg2) {
        invariant(typeof transformer === "function" && transformer.length < 2, "createTransformer expects a function that accepts one argument");
        // Memoizes: object id -> reactive view that applies transformer to the object
        var views = {};
        var onCleanup = undefined;
        var keepAlive = false;
        var debugNameGenerator = undefined;
        if (typeof arg2 === "object") {
            onCleanup = arg2.onCleanup;
            keepAlive = arg2.keepAlive !== undefined ? arg2.keepAlive : false;
            debugNameGenerator = arg2.debugNameGenerator;
        }
        else if (typeof arg2 === "function") {
            onCleanup = arg2;
        }
        function createView(sourceIdentifier, sourceObject) {
            var latestValue;
            var computedValueOptions = {};
            if (typeof arg2 === "object") {
                onCleanup = arg2.onCleanup;
                debugNameGenerator = arg2.debugNameGenerator;
                computedValueOptions = arg2;
            }
            else if (typeof arg2 === "function") {
                onCleanup = arg2;
            }
            else {
                onCleanup = undefined;
                debugNameGenerator = undefined;
            }
            var prettifiedName = debugNameGenerator
                ? debugNameGenerator(sourceObject)
                : "Transformer-" + transformer.name + "-" + sourceIdentifier;
            var expr = mobx.computed(function () {
                return (latestValue = transformer(sourceObject));
            }, __assign$1(__assign$1({}, computedValueOptions), { name: prettifiedName }));
            if (!keepAlive) {
                var disposer_1 = mobx.onBecomeUnobserved(expr, function () {
                    delete views[sourceIdentifier];
                    disposer_1();
                    if (onCleanup)
                        onCleanup(latestValue, sourceObject);
                });
            }
            return expr;
        }
        var memoWarned = false;
        return function (object) {
            var identifier = getMemoizationId(object);
            var reactiveView = views[identifier];
            if (reactiveView)
                return reactiveView.get();
            if (!keepAlive && !mobx._isComputingDerivation()) {
                if (!memoWarned) {
                    console.warn("invoking a transformer from outside a reactive context won't memorized " +
                        "and is cleaned up immediately, unless keepAlive is set");
                    memoWarned = true;
                }
                var value = transformer(object);
                if (onCleanup)
                    onCleanup(value, object);
                return value;
            }
            // Not in cache; create a reactive view
            reactiveView = views[identifier] = createView(identifier, object);
            return reactiveView.get();
        };
    }
    function getMemoizationId(object) {
        var objectType = typeof object;
        if (objectType === "string")
            return "string:" + object;
        if (objectType === "number")
            return "number:" + object;
        if (object === null || (objectType !== "object" && objectType !== "function"))
            throw new Error("[mobx-utils] transform expected an object, function, string or number, got: " + String(object));
        var tid = object.$transformId;
        if (tid === undefined) {
            tid = "memoizationId:" + ++memoizationId;
            addHiddenProp(object, "$transformId", tid);
        }
        return tid;
    }

    function buildPath(entry) {
        if (!entry)
            return "ROOT";
        var res = [];
        while (entry.parent) {
            res.push(entry.path);
            entry = entry.parent;
        }
        return res.reverse().join("/");
    }
    function isRecursivelyObservable(thing) {
        return mobx.isObservableObject(thing) || mobx.isObservableArray(thing) || mobx.isObservableMap(thing);
    }
    /**
     * Given an object, deeply observes the given object.
     * It is like `observe` from mobx, but applied recursively, including all future children.
     *
     * Note that the given object cannot ever contain cycles and should be a tree.
     *
     * As benefit: path and root will be provided in the callback, so the signature of the listener is
     * (change, path, root) => void
     *
     * The returned disposer can be invoked to clean up the listener
     *
     * deepObserve cannot be used on computed values.
     *
     * @example
     * const disposer = deepObserve(target, (change, path) => {
     *    console.dir(change)
     * })
     */
    function deepObserve(target, listener) {
        var entrySet = new WeakMap();
        function genericListener(change) {
            var entry = entrySet.get(change.object);
            processChange(change, entry);
            listener(change, buildPath(entry), target);
        }
        function processChange(change, parent) {
            switch (change.type) {
                // Object changes
                case "add": // also for map
                    observeRecursively(change.newValue, parent, change.name);
                    break;
                case "update": // also for array and map
                    unobserveRecursively(change.oldValue);
                    observeRecursively(change.newValue, parent, change.name || "" + change.index);
                    break;
                case "remove": // object
                case "delete": // map
                    unobserveRecursively(change.oldValue);
                    break;
                // Array changes
                case "splice":
                    change.removed.map(unobserveRecursively);
                    change.added.forEach(function (value, idx) {
                        return observeRecursively(value, parent, "" + (change.index + idx));
                    });
                    // update paths
                    for (var i = change.index + change.addedCount; i < change.object.length; i++) {
                        if (isRecursivelyObservable(change.object[i])) {
                            var entry = entrySet.get(change.object[i]);
                            if (entry)
                                entry.path = "" + i;
                        }
                    }
                    break;
            }
        }
        function observeRecursively(thing, parent, path) {
            if (isRecursivelyObservable(thing)) {
                var entry = entrySet.get(thing);
                if (entry) {
                    if (entry.parent !== parent || entry.path !== path)
                        // MWE: this constraint is artificial, and this tool could be made to work with cycles,
                        // but it increases administration complexity, has tricky edge cases and the meaning of 'path'
                        // would become less clear. So doesn't seem to be needed for now
                        throw new Error("The same observable object cannot appear twice in the same tree," +
                            (" trying to assign it to '" + buildPath(parent) + "/" + path + "',") +
                            (" but it already exists at '" + buildPath(entry.parent) + "/" + entry.path + "'"));
                }
                else {
                    var entry_1 = {
                        parent: parent,
                        path: path,
                        dispose: mobx.observe(thing, genericListener),
                    };
                    entrySet.set(thing, entry_1);
                    mobx.entries(thing).forEach(function (_a) {
                        var key = _a[0], value = _a[1];
                        return observeRecursively(value, entry_1, key);
                    });
                }
            }
        }
        function unobserveRecursively(thing) {
            if (isRecursivelyObservable(thing)) {
                var entry = entrySet.get(thing);
                if (!entry)
                    return;
                entrySet.delete(thing);
                entry.dispose();
                mobx.values(thing).forEach(unobserveRecursively);
            }
        }
        observeRecursively(target, undefined, "");
        return function () {
            unobserveRecursively(target);
        };
    }

    var __extends = (undefined && undefined.__extends) || (function () {
        var extendStatics = function (d, b) {
            extendStatics = Object.setPrototypeOf ||
                ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
                function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
            return extendStatics(d, b);
        };
        return function (d, b) {
            extendStatics(d, b);
            function __() { this.constructor = d; }
            d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
        };
    })();
    /**
     * Reactively sorts a base observable array into multiple observable arrays based on the value of a
     * `groupBy: (item: T) => G` function.
     *
     * This observes the individual computed groupBy values and only updates the source and dest arrays
     * when there is an actual change, so this is far more efficient than, for example
     * `base.filter(i => groupBy(i) === 'we')`. Call #dispose() to stop tracking.
     *
     * No guarantees are made about the order of items in the grouped arrays.
     *
     * The resulting map of arrays is read-only. clear(), set(), delete() are not supported and
     * modifying the group arrays will lead to undefined behavior.
     *
     * NB: ObservableGroupMap relies on `Symbol`s. If you are targeting a platform which doesn't
     * support these natively, you will need to provide a polyfill.
     *
     * @param {array} base The array to sort into groups.
     * @param {function} groupBy The function used for grouping.
     * @param options Object with properties:
     *  `name`: Debug name of this ObservableGroupMap.
     *  `keyToName`: Function to create the debug names of the observable group arrays.
     *
     * @example
     * const slices = observable([
     *     { day: "mo", hours: 12 },
     *     { day: "tu", hours: 2 },
     * ])
     * const slicesByDay = new ObservableGroupMap(slices, (slice) => slice.day)
     * autorun(() => console.log(
     *     slicesByDay.get("mo")?.length ?? 0,
     *     slicesByDay.get("we"))) // outputs 1, undefined
     * slices[0].day = "we" // outputs 0, [{ day: "we", hours: 12 }]
     */
    var ObservableGroupMap = /** @class */ (function (_super) {
        __extends(ObservableGroupMap, _super);
        function ObservableGroupMap(base, groupBy, _a) {
            var _b = _a === void 0 ? {} : _a, _c = _b.name, name = _c === void 0 ? "ogm" + ((Math.random() * 1000) | 0) : _c, _d = _b.keyToName, keyToName = _d === void 0 ? function (x) { return "" + x; } : _d;
            var _this = _super.call(this) || this;
            /**
             * Base observable array which is being sorted into groups.
             */
            Object.defineProperty(_this, "_base", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: void 0
            });
            /**
             * The ObservableGroupMap needs to track some state per-item. This is the name/symbol of the
             * property used to attach the state.
             */
            Object.defineProperty(_this, "_ogmInfoKey", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: void 0
            });
            /**
             * The function used to group the items.
             */
            Object.defineProperty(_this, "_groupBy", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: void 0
            });
            /**
             * This function is used to generate the mobx debug names of the observable group arrays.
             */
            Object.defineProperty(_this, "_keyToName", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: void 0
            });
            Object.defineProperty(_this, "_disposeBaseObserver", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: void 0
            });
            _this._keyToName = keyToName;
            _this._groupBy = groupBy;
            _this._ogmInfoKey = Symbol("ogmInfo" + name);
            _this._base = base;
            for (var i = 0; i < base.length; i++) {
                _this._addItem(base[i]);
            }
            _this._disposeBaseObserver = mobx.observe(_this._base, function (change) {
                if ("splice" === change.type) {
                    mobx.transaction(function () {
                        for (var _i = 0, _a = change.removed; _i < _a.length; _i++) {
                            var removed = _a[_i];
                            _this._removeItem(removed);
                        }
                        for (var _b = 0, _c = change.added; _b < _c.length; _b++) {
                            var added = _c[_b];
                            _this._addItem(added);
                        }
                    });
                }
                else if ("update" === change.type) {
                    mobx.transaction(function () {
                        _this._removeItem(change.oldValue);
                        _this._addItem(change.newValue);
                    });
                }
                else {
                    throw new Error("illegal state");
                }
            });
            return _this;
        }
        Object.defineProperty(ObservableGroupMap.prototype, "clear", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function () {
                throw new Error("not supported");
            }
        });
        Object.defineProperty(ObservableGroupMap.prototype, "delete", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function (_key) {
                throw new Error("not supported");
            }
        });
        Object.defineProperty(ObservableGroupMap.prototype, "set", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function (_key, _value) {
                throw new Error("not supported");
            }
        });
        /**
         * Disposes all observers created during construction and removes state added to base array
         * items.
         */
        Object.defineProperty(ObservableGroupMap.prototype, "dispose", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function () {
                this._disposeBaseObserver();
                for (var i = 0; i < this._base.length; i++) {
                    var item = this._base[i];
                    var grouperItemInfo = item[this._ogmInfoKey];
                    grouperItemInfo.reaction();
                    delete item[this._ogmInfoKey];
                }
            }
        });
        Object.defineProperty(ObservableGroupMap.prototype, "_getGroupArr", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function (key) {
                var result = _super.prototype.get.call(this, key);
                if (undefined === result) {
                    result = mobx.observable([], { name: "GroupArray[" + this._keyToName(key) + "]", deep: false });
                    _super.prototype.set.call(this, key, result);
                }
                return result;
            }
        });
        Object.defineProperty(ObservableGroupMap.prototype, "_removeFromGroupArr", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function (key, itemIndex) {
                var arr = _super.prototype.get.call(this, key);
                if (1 === arr.length) {
                    _super.prototype.delete.call(this, key);
                }
                else if (itemIndex === arr.length - 1) {
                    // last position in array
                    arr.length--;
                }
                else {
                    arr[itemIndex] = arr[arr.length - 1];
                    arr[itemIndex][this._ogmInfoKey].groupArrIndex = itemIndex;
                    arr.length--;
                }
            }
        });
        Object.defineProperty(ObservableGroupMap.prototype, "_addItem", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function (item) {
                var _this = this;
                var groupByValue = this._groupBy(item);
                var groupArr = this._getGroupArr(groupByValue);
                var value = {
                    groupByValue: groupByValue,
                    groupArrIndex: groupArr.length,
                    reaction: mobx.reaction(function () { return _this._groupBy(item); }, function (newGroupByValue, _r) {
                        var grouperItemInfo = item[_this._ogmInfoKey];
                        _this._removeFromGroupArr(grouperItemInfo.groupByValue, grouperItemInfo.groupArrIndex);
                        var newGroupArr = _this._getGroupArr(newGroupByValue);
                        var newGroupArrIndex = newGroupArr.length;
                        newGroupArr.push(item);
                        grouperItemInfo.groupByValue = newGroupByValue;
                        grouperItemInfo.groupArrIndex = newGroupArrIndex;
                    }),
                };
                Object.defineProperty(item, this._ogmInfoKey, {
                    configurable: true,
                    enumerable: false,
                    value: value,
                });
                groupArr.push(item);
            }
        });
        Object.defineProperty(ObservableGroupMap.prototype, "_removeItem", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function (item) {
                var grouperItemInfo = item[this._ogmInfoKey];
                this._removeFromGroupArr(grouperItemInfo.groupByValue, grouperItemInfo.groupArrIndex);
                grouperItemInfo.reaction();
                delete item[this._ogmInfoKey];
            }
        });
        return ObservableGroupMap;
    }(mobx.ObservableMap));

    /**
     * @private
     */
    var DeepMapEntry = /** @class */ (function () {
        function DeepMapEntry(base, args) {
            Object.defineProperty(this, "base", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: base
            });
            Object.defineProperty(this, "args", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: args
            });
            Object.defineProperty(this, "root", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: void 0
            });
            Object.defineProperty(this, "closest", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: void 0
            });
            Object.defineProperty(this, "closestIdx", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: 0
            });
            Object.defineProperty(this, "isDisposed", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: false
            });
            var current = (this.closest = this.root = base);
            var i = 0;
            for (; i < this.args.length - 1; i++) {
                current = current.get(args[i]);
                if (current)
                    this.closest = current;
                else
                    break;
            }
            this.closestIdx = i;
        }
        Object.defineProperty(DeepMapEntry.prototype, "exists", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function () {
                this.assertNotDisposed();
                var l = this.args.length;
                return this.closestIdx >= l - 1 && this.closest.has(this.args[l - 1]);
            }
        });
        Object.defineProperty(DeepMapEntry.prototype, "get", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function () {
                this.assertNotDisposed();
                if (!this.exists())
                    throw new Error("Entry doesn't exist");
                return this.closest.get(this.args[this.args.length - 1]);
            }
        });
        Object.defineProperty(DeepMapEntry.prototype, "set", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function (value) {
                this.assertNotDisposed();
                var l = this.args.length;
                var current = this.closest;
                // create remaining maps
                for (var i = this.closestIdx; i < l - 1; i++) {
                    var m = new Map();
                    current.set(this.args[i], m);
                    current = m;
                }
                this.closestIdx = l - 1;
                this.closest = current;
                current.set(this.args[l - 1], value);
            }
        });
        Object.defineProperty(DeepMapEntry.prototype, "delete", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function () {
                this.assertNotDisposed();
                if (!this.exists())
                    throw new Error("Entry doesn't exist");
                var l = this.args.length;
                this.closest.delete(this.args[l - 1]);
                // clean up remaining maps if needed (reconstruct stack first)
                var c = this.root;
                var maps = [c];
                for (var i = 0; i < l - 1; i++) {
                    c = c.get(this.args[i]);
                    maps.push(c);
                }
                for (var i = maps.length - 1; i > 0; i--) {
                    if (maps[i].size === 0)
                        maps[i - 1].delete(this.args[i - 1]);
                }
                this.isDisposed = true;
            }
        });
        Object.defineProperty(DeepMapEntry.prototype, "assertNotDisposed", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function () {
                // TODO: once this becomes annoying, we should introduce a reset method to re-run the constructor logic
                if (this.isDisposed)
                    throw new Error("Concurrent modification exception");
            }
        });
        return DeepMapEntry;
    }());
    /**
     * @private
     */
    var DeepMap = /** @class */ (function () {
        function DeepMap() {
            Object.defineProperty(this, "store", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: new Map()
            });
            Object.defineProperty(this, "argsLength", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: -1
            });
            Object.defineProperty(this, "last", {
                enumerable: true,
                configurable: true,
                writable: true,
                value: void 0
            });
        }
        Object.defineProperty(DeepMap.prototype, "entry", {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function (args) {
                if (this.argsLength === -1)
                    this.argsLength = args.length;
                else if (this.argsLength !== args.length)
                    throw new Error("DeepMap should be used with functions with a consistent length, expected: " + this.argsLength + ", got: " + args.length);
                if (this.last)
                    this.last.isDisposed = true;
                return (this.last = new DeepMapEntry(this.store, args));
            }
        });
        return DeepMap;
    }());

    var __assign$2 = (undefined && undefined.__assign) || function () {
        __assign$2 = Object.assign || function(t) {
            for (var s, i = 1, n = arguments.length; i < n; i++) {
                s = arguments[i];
                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                    t[p] = s[p];
            }
            return t;
        };
        return __assign$2.apply(this, arguments);
    };
    var __spreadArrays$1 = (undefined && undefined.__spreadArrays) || function () {
        for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
        for (var r = Array(s), k = 0, i = 0; i < il; i++)
            for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
                r[k] = a[j];
        return r;
    };
    /**
     * computedFn takes a function with an arbitrary amount of arguments,
     * and memoizes the output of the function based on the arguments passed in.
     *
     * computedFn(fn) returns a function with the very same signature. There is no limit on the amount of arguments
     * that is accepted. However, the amount of arguments must be constant and default arguments are not supported.
     *
     * By default the output of a function call will only be memoized as long as the
     * output is being observed.
     *
     * The function passes into `computedFn` should be pure, not be an action and only be relying on
     * observables.
     *
     * Setting `keepAlive` to `true` will cause the output to be forcefully cached forever.
     * Note that this might introduce memory leaks!
     *
     * @example
     * const store = observable({
        a: 1,
        b: 2,
        c: 3,
        m: computedFn(function(x) {
          return this.a * this.b * x
        })
      })

      const d = autorun(() => {
        // store.m(3) will be cached as long as this autorun is running
        console.log(store.m(3) * store.c)
      })
     *
     * @param fn
     * @param keepAliveOrOptions
     */
    function computedFn(fn, keepAliveOrOptions) {
        if (keepAliveOrOptions === void 0) { keepAliveOrOptions = false; }
        if (mobx.isAction(fn))
            throw new Error("computedFn shouldn't be used on actions");
        var memoWarned = false;
        var i = 0;
        var opts = typeof keepAliveOrOptions === "boolean"
            ? { keepAlive: keepAliveOrOptions }
            : keepAliveOrOptions;
        var d = new DeepMap();
        return function () {
            var _this = this;
            var args = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                args[_i] = arguments[_i];
            }
            var entry = d.entry(args);
            // cache hit, return
            if (entry.exists())
                return entry.get().get();
            // if function is invoked, and its a cache miss without reactive, there is no point in caching...
            if (!opts.keepAlive && !mobx._isComputingDerivation()) {
                if (!memoWarned && mobx._getGlobalState().computedRequiresReaction) {
                    console.warn("invoking a computedFn from outside an reactive context won't be memoized, unless keepAlive is set");
                    memoWarned = true;
                }
                return fn.apply(this, args);
            }
            // create new entry
            var latestValue;
            var c = mobx.computed(function () {
                return (latestValue = fn.apply(_this, args));
            }, __assign$2(__assign$2({}, opts), { name: "computedFn(" + (opts.name || fn.name) + "#" + ++i + ")" }));
            entry.set(c);
            // clean up if no longer observed
            if (!opts.keepAlive)
                mobx.onBecomeUnobserved(c, function () {
                    d.entry(args).delete();
                    if (opts.onCleanup)
                        opts.onCleanup.apply(opts, __spreadArrays$1([latestValue], args));
                    latestValue = undefined;
                });
            // return current val
            return c.get();
        };
    }

    exports.FULFILLED = FULFILLED;
    exports.IDENTITY = IDENTITY;
    exports.NOOP = NOOP;
    exports.ObservableGroupMap = ObservableGroupMap;
    exports.PENDING = PENDING;
    exports.REJECTED = REJECTED;
    exports.ViewModel = ViewModel;
    exports.addHiddenProp = addHiddenProp;
    exports.chunkProcessor = chunkProcessor;
    exports.computedFn = computedFn;
    exports.createTransformer = createTransformer;
    exports.createViewModel = createViewModel;
    exports.deepObserve = deepObserve;
    exports.expr = expr;
    exports.fail = fail;
    exports.fromPromise = fromPromise;
    exports.fromResource = fromResource;
    exports.fromStream = fromStream;
    exports.getAllMethodsAndProperties = getAllMethodsAndProperties;
    exports.invariant = invariant;
    exports.isPromiseBasedObservable = isPromiseBasedObservable;
    exports.keepAlive = keepAlive;
    exports.lazyObservable = lazyObservable;
    exports.moveItem = moveItem;
    exports.now = now;
    exports.queueProcessor = queueProcessor;
    exports.resetNowInternalState = resetNowInternalState;
    exports.toStream = toStream;

    Object.defineProperty(exports, '__esModule', { value: true });

})));