UNPKG

@ortoo/angular-resource

Version:

Magical CRUD resource management for angular

392 lines 54.4 kB
// tslint:disable only-arrow-functions
import { values } from 'lodash';
import { EventEmitter } from './emitter';
import * as utils from './utils';
const MAX_STORAGE_SIZE = 3 * 1024 * 1024; // 3MB - should fit without problems in any browser
export function createLocalResourceFactory(ServerResourceFactory, QueryFactory, CollectionFactory, ResourceDBFactory, localforage) {
    let totalStorageSize = 0;
    let totalDesiredSize = 0;
    let persistMode = 'FULL';
    let persistModeEmitter = new EventEmitter();
    function LocalResourceFactory(url, rootKey, rootKeyPlural) {
        let db = ResourceDBFactory();
        // Create the server resource and query
        let ServerResource = ServerResourceFactory(url, rootKey, rootKeyPlural);
        let QueryList = QueryFactory(url, rootKey, rootKeyPlural, db);
        let Collection = CollectionFactory();
        // In memory resource store
        let _resources = {};
        let _internalRes = {};
        let ourStorageSize = 0;
        let ourDesiredSize = 0;
        let persistModeWatching = false;
        let _lastreqs = {};
        let _reqs = {};
        let pStorageKey = utils.persistentStorageKey(url);
        let persistProm = null;
        function toObject() {
            return utils.toObject(this);
        }
        function idToModel(id) {
            let loc = _internalRes[id];
            return loc.$mod;
        }
        function serverTransform(serv, id, transformer) {
            let res = _internalRes[serv.$id];
            // If we don't have a resource then create it
            if (!res) {
                res = new LocalResource(serv, true);
            }
            if (id) {
                _resources[id] = res;
            }
            return transformer(res, id);
        }
        function get(ids, force, transform) {
            // We've requested a bunch of ids
            // We should persist the change (to offline storage) if this is the first request
            // for this (these) objects
            let now = new Date().getTime();
            let isSomeFirst = false;
            if (Array.isArray(ids)) {
                ids.forEach(function (id) {
                    if (!_reqs[id]) {
                        isSomeFirst = true;
                    }
                    _lastreqs[id] = now;
                    _reqs[id] = true;
                });
            }
            else {
                if (!_reqs[ids]) {
                    isSomeFirst = true;
                }
                _lastreqs[ids] = now;
                _reqs[ids] = true;
            }
            // If our persistMode is MIN we'll want to persist to storage here
            if (persistMode === 'MIN' && isSomeFirst) {
                persistChange();
            }
            return ServerResource.get(ids, force, function (serv, id) {
                return serverTransform(serv, id, transform);
            });
        }
        function collect(Resource, seeds, relation) {
            return new Collection(Resource, seeds, relation);
        }
        function query(qry, limit, Resource) {
            return QueryList(qry, limit, Resource, idToModel);
        }
        function remove(skipServer) {
            if (this._id) {
                delete _resources[this._id];
            }
            this.$deleted = true;
            // Kick the database
            syncToStorage(this);
            // If we have been created then notify the server
            if (this.$created && !skipServer) {
                return this.$serv.$remove();
            }
            return Promise.resolve(true);
        }
        // Once we are synced with the server resource we will resolve the promise
        function save(vals) {
            let res = this;
            res.$created = true;
            // Only trigger the server sync once per 'tick' (so calling save() multiple times
            // has no effect)
            if (!res.$saveprom) {
                let oldData = this.$toObject();
                res.$saveprom = new Promise(resolve => {
                    setTimeout(function () {
                        resolve();
                    });
                }).then(function () {
                    res.$saveprom = null;
                    // Save us to the db
                    syncToStorage(res);
                    let patch = utils.diff(oldData, res.$toObject());
                    return syncToServer(res, patch).then(function () {
                        return res;
                    });
                }).then(function () {
                    if (!res.$resolved) {
                        // Wait for the db to sync before resolving (if the sync is outstanding)
                        let dbprom = res.$dbsync ? res.$dbsync : Promise.resolve();
                        dbprom.then(function () {
                            res.$deferred.resolve(res);
                            res.$resolved = true;
                        });
                    }
                    return res;
                });
            }
            utils.removeResValues(res);
            utils.setResValues(res, vals);
            return res.$saveprom;
        }
        function performServerSync(res, patch) {
            // We are about to sync. Unset the resync flag
            res.$resync = [];
            // Wait for the server to finish saving. Then check if we need to resync. This promise
            // won't resolve until there are no more resyncs to do
            return res.$serv.$save(patch).then(function () {
                if (res.$resync.length > 0) {
                    return performServerSync(res, res.$resync);
                }
                return res;
            });
        }
        function syncToServer(res, patch) {
            if (res.$sync) {
                res.$resync.push.apply(res.$resync, patch);
            }
            else {
                let prom = performServerSync(res, patch)['finally'](function () {
                    // Once we've synced remove the $sync promise
                    delete res.$sync;
                });
                res.$sync = prom;
            }
            return res.$sync;
        }
        function updatedServer(res, newVal, oldVal) {
            // We could have been deleted (existing oldVal, null newval)
            if (oldVal && !newVal) {
                // We have been deleted. Cleanup ourselves and pass it up the chain
                res.$emitter.emit('update', null, res.$toObject());
                res.$remove(true);
            }
            else {
                res.$created = true;
                if (oldVal._id && (newVal._id !== oldVal._id)) {
                    throw new Error('Not allowed to change id');
                }
                // Merge the objects together using the oldVal as a base, and using *our* version
                // to resolve any conflicts. We may need to put in some conflict resolution logic
                // somewhere on a case by case basis
                let preexist = res.$toObject();
                let merge = utils.mergeObjects(res.$toObject(), oldVal, newVal);
                // Now put back in the merge values
                utils.removeResValues(res);
                utils.setResValues(res, merge);
                // Make sure we are stored
                _resources[res._id] = res;
                // If we've only just been given an id then store of the created time as the
                // time we were last requested (this is because the object must have just been
                // created on the server)
                if (res._id && !oldVal._id) {
                    _lastreqs[res._id] = res.$createdAt;
                    _reqs[res._id] = true;
                }
                // Notify that we have changed
                res.$emitter.emit('update', res.$toObject(), preexist);
                // Kick the db
                let syncprom = syncToStorage(res) || Promise.resolve();
                syncprom.then(function () {
                    // We might have synced for the first time
                    return res.$serv.$promise.then(function () {
                        if (!res.$resolved) {
                            res.$deferred.resolve(res);
                            res.$resolved = true;
                        }
                    });
                });
            }
        }
        function performDbSync(res) {
            res.$dbresync = false;
            return db.update(res).then(function () {
                if (res.$dbresync) {
                    return performDbSync(res);
                }
                return;
            });
        }
        function watchForPersistModeChanges() {
            if (persistModeWatching) {
                return;
            }
            persistModeEmitter.on('change', function (_url) {
                // If we have called this then ignore
                if (_url === url) {
                    return;
                }
                // Redo the persist
                doPersist();
            });
        }
        function doPersist() {
            // If we're already doing a persist or we dont have advanced storage options then
            // just return
            if (persistMode === 'NONE' || !utils.advancedStorage(localforage)) {
                return Promise.resolve();
            }
            // If this is the first time through then stick a listener on for changes
            if (!persistModeWatching) {
                watchForPersistModeChanges();
            }
            let data = [];
            switch (persistMode) {
                case 'FULL':
                    data = values(_resources).map(function (res) {
                        return {
                            lastreq: _lastreqs[res._id],
                            obj: res.$toObject(),
                        };
                    });
                    break;
                case 'MIN':
                    values(_resources).forEach(function (res) {
                        if (_reqs[res._id]) {
                            data.push({
                                lastreq: _lastreqs[res._id],
                                obj: res.$toObject(),
                            });
                        }
                    });
                    break;
                default:
                    return Promise.resolve();
            }
            // We need to manually manage storage
            let dataStr = JSON.stringify(data);
            let newStorageSize = dataStr.length;
            let expectedSize = totalDesiredSize - ourDesiredSize + newStorageSize;
            // Do we expect to bust the max size? If so we need to change persist mode
            // and emit
            if (expectedSize > MAX_STORAGE_SIZE) {
                if (persistMode === 'FULL') {
                    persistMode = 'MIN';
                }
                else if (persistMode === 'MIN') {
                    persistMode = 'NONE';
                }
                else {
                    // Don't know how we could get here but return just in case
                    return Promise.resolve();
                }
                persistModeEmitter.emit('change', url);
                // Schedule this later
                return new Promise(resolve => setTimeout(resolve)).then(doPersist);
            }
            // MODE HAS NOT CHANGED
            // Store our expected size
            totalDesiredSize = expectedSize;
            ourDesiredSize = newStorageSize;
            return localforage.setItem(pStorageKey, data).then(function () {
                totalStorageSize = totalStorageSize - ourStorageSize + newStorageSize;
                ourStorageSize = newStorageSize;
            });
        }
        function persistChange() {
            // If we're already doing a persist or we dont have advanced storage options then
            // just return
            if (persistProm || persistMode === 'NONE' || !utils.advancedStorage(localforage)) {
                return;
            }
            persistProm = new Promise(r => setTimeout(r, 500)).then(function () {
                return doPersist().then(function () {
                    // Finished the persist
                    persistProm = null;
                });
            });
        }
        function syncToStorage(res) {
            persistChange();
            if (res.$dbsync) {
                res.$dbresync = true;
            }
            else {
                let prom = performDbSync(res);
                prom.then(finishedSync, finishedSync);
                res.$dbsync = prom;
            }
            return res.$dbsync;
            function finishedSync() {
                // Whatever happens remove the $sync promise and refresh all the queries
                delete res.$dbsync;
                res.$dbresync = false;
                QueryList.refresh();
            }
        }
        function updateServer(val) {
            this.$serv.$updateVal(val);
        }
        function refresh() {
            this.$serv.$refresh();
        }
        function LocalResourceConstructor(val, fromServ, mod, lastRequested) {
            let res = this;
            this.$emitter = new EventEmitter();
            this.$deferred = new utils.Deferred();
            this.$promise = this.$deferred.promise; // An initial promise for our initial
            // fetch or create of data
            this.$resolved = false; // Have we had an initial resolution of the promise
            this.$created = false; // Have we pushed any values down to the server yet?
            this.$createdAt = new Date().getTime();
            this.$resync = [];
            this.$saveprom = null;
            this.$deleted = false;
            // Store off the model so we can reference it later
            this.$mod = mod;
            // Used to correlate to the db objects when we don't have an _id (creating)
            this.$id = fromServ ? val.$id : utils.uuid();
            _internalRes[this.$id] = this;
            // If we've been given values put them on
            if (val) {
                let props = fromServ ? val.$toObject() : val;
                for (let key of Object.keys(props)) {
                    res[key] = props[key];
                }
            }
            if (val && fromServ) {
                this.$serv = val;
            }
            else if (this._id) {
                this.$serv = new ServerResource(val, this.$id);
            }
            else {
                // Don't add in the values until we save
                this.$serv = new ServerResource(null, this.$id);
            }
            // If we have an id then add us to the store
            if (this._id) {
                _resources[this._id] = this;
            }
            // If we have an id and we've been passed in a last requested time then store off
            // the last requested time
            if (this._id && lastRequested) {
                // If the last requested already exists use the max
                let existing = _lastreqs[this._id] || 0;
                _lastreqs[this._id] = Math.max(lastRequested, existing);
            }
            // Listen for changes on the server
            this.$serv.$emitter.on('update', function (newVal, oldVal) {
                updatedServer(res, newVal, oldVal);
            });
            // Update us in the db
            this.$dbresync = false;
            // If it's from the server don't create it yet. Wait for the update to come (along
            // with hopefully all the data)
            if (!fromServ && val) {
                syncToStorage(this);
            }
        }
        const LocalResource = LocalResourceConstructor;
        LocalResource.get = get;
        LocalResource.query = query;
        LocalResource.collect = collect;
        LocalResource.prototype.$save = save;
        LocalResource.prototype.$remove = remove;
        LocalResource.prototype.$delete = remove;
        LocalResource.prototype.$toObject = toObject;
        LocalResource.prototype.$updateServer = updateServer;
        LocalResource.prototype.$refresh = refresh;
        return LocalResource;
    }
    return LocalResourceFactory;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9jYWwuanMiLCJzb3VyY2VSb290IjoiL0BvcnRvby9hbmd1bGFyLXJlc291cmNlLyIsInNvdXJjZXMiOlsibG9jYWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsc0NBQXNDO0FBQ3RDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFFaEMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLFdBQVcsQ0FBQztBQWN6QyxPQUFPLEtBQUssS0FBSyxNQUFNLFNBQVMsQ0FBQztBQVFqQyxNQUFNLGdCQUFnQixHQUFHLENBQUMsR0FBRyxJQUFJLEdBQUcsSUFBSSxDQUFDLENBQUMsbURBQW1EO0FBRTdGLE1BQU0scUNBQXFDLHFCQUE0QyxFQUM1QyxZQUEwQixFQUMxQixpQkFBMkMsRUFDM0MsaUJBQWtDLEVBQ2xDLFdBQXdCO0lBQ2pFLElBQUksZ0JBQWdCLEdBQUcsQ0FBQyxDQUFDO0lBQ3pCLElBQUksZ0JBQWdCLEdBQUcsQ0FBQyxDQUFDO0lBQ3pCLElBQUksV0FBVyxHQUFHLE1BQU0sQ0FBQztJQUN6QixJQUFJLGtCQUFrQixHQUFHLElBQUksWUFBWSxFQUFFLENBQUM7SUFFNUMsOEJBQThCLEdBQVcsRUFBRSxPQUFlLEVBQUUsYUFBcUI7UUFFL0UsSUFBSSxFQUFFLEdBQUcsaUJBQWlCLEVBQUUsQ0FBQztRQUU3Qix1Q0FBdUM7UUFDdkMsSUFBSSxjQUFjLEdBQUcscUJBQXFCLENBQUMsR0FBRyxFQUFFLE9BQU8sRUFBRSxhQUFhLENBQUMsQ0FBQztRQUN4RSxJQUFJLFNBQVMsR0FBRyxZQUFZLENBQUMsR0FBRyxFQUFFLE9BQU8sRUFBRSxhQUFhLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDOUQsSUFBSSxVQUFVLEdBQUcsaUJBQWlCLEVBQUUsQ0FBQztRQUVyQywyQkFBMkI7UUFDM0IsSUFBSSxVQUFVLEdBQWdCLEVBQUUsQ0FBQztRQUNqQyxJQUFJLFlBQVksR0FBZ0IsRUFBRSxDQUFDO1FBRW5DLElBQUksY0FBYyxHQUFHLENB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QWEsQ0FBQyxTQUFTLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQztRQUN6QyxhQUFhLENBQUMsU0FBUyxDQUFDLFNBQVMsR0FBRyxRQUFRLENBQUM7UUFDN0MsYUFBYSxDQUFDLFNBQVMsQ0FBQyxhQUFhLEdBQUcsWUFBWSxDQUFDO1FBQ3JELGFBQWEsQ0FBQyxTQUFTLENBQUMsUUFBUSxHQUFHLE9BQU8sQ0FBQztRQUUzQyxNQUFNLENBQUMsYUFBYSxDQUFDO0lBQ3ZCLENBQUM7SUFFRCxNQUFNLENBQUMsb0JBQW9CLENBQUM7QUFDOUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIHRzbGludDpkaXNhYmxlIG9ubHktYXJyb3ctZnVuY3Rpb25zXG5pbXBvcnQgeyB2YWx1ZXMgfSBmcm9tICdsb2Rhc2gnO1xuXG5pbXBvcnQgeyBFdmVudEVtaXR0ZXIgfSBmcm9tICcuL2VtaXR0ZXInO1xuaW1wb3J0IHsgQ29sbGVjdGlvbkZhY3RvcnksXG4gICAgICAgICBDb2xsZWN0aW9uU2VhcmNoZXIsXG4gICAgICAgICBEYXRhYmFzZUZhY3RvcnksXG4gICAgICAgICBKU09OUGF0Y2gsXG4gICAgICAgICBMb2NhbEZvcmFnZSxcbiAgICAgICAgIExvY2FsTW9kZWwsXG4gICAgICAgICBMb2NhbFJlc291cmNlIGFzIElMb2NhbFJlc291cmNlLFxuICAgICAgICAgTW9kZWwsXG4gICAgICAgICBNb2RlbEFycmF5LFxuICAgICAgICAgUXVlcnlGYWN0b3J5LFxuICAgICAgICAgUmVzb3VyY2UsXG4gICAgICAgICBTZXJ2ZXJNb2RlbCxcbiAgICAgICAgIFNlcnZlclJlc291cmNlRmFjdG9yeSB9IGZyb20gJy4vdHlwZXMnO1xuaW1wb3J0ICogYXMgdXRpbHMgZnJvbSAnLi91dGlscyc7XG5cbmludGVyZmFjZSBSZXNvdXJjZU1hcCB7XG4gIFtpZDogc3RyaW5nXTogTG9jYWxNb2RlbDtcbn1cblxudHlwZSBMYXN0UmVxdWVzdCA9IChEYXRlIHwgbnVtYmVyKTtcblxuY29uc3QgTUFYX1NUT1JBR0VfU0laRSA9IDMgKiAxMDI0ICogMTAyNDsgLy8gM01CIC0gc2hvdWxkIGZpdCB3aXRob3V0IHByb2JsZW1zIGluIGFueSBicm93c2VyXG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVMb2NhbFJlc291cmNlRmFjdG9yeShTZXJ2ZXJSZXNvdXJjZUZhY3Rvcnk6IFNlcnZlclJlc291cmNlRmFjdG9yeSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBRdWVyeUZhY3Rvcnk6IFF1ZXJ5RmFjdG9yeSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDb2xsZWN0aW9uRmFjdG9yeTogQ29sbGVjdGlvbkZhY3Rvcnk8TW9kZWw+LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJlc291cmNlREJGYWN0b3J5OiBEYXRhYmFzZUZhY3RvcnksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9jYWxmb3JhZ2U6IExvY2FsRm9yYWdlKSB7XG4gIGxldCB0b3RhbFN0b3JhZ2VTaXplID0gMDtcbiAgbGV0IHRvdGFsRGVzaXJlZFNpemUgPSAwO1xuICBsZXQgcGVyc2lzdE1vZGUgPSAnRlVMTCc7XG4gIGxldCBwZXJzaXN0TW9kZUVtaXR0ZXIgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG5cbiAgZnVuY3Rpb24gTG9jYWxSZXNvdXJjZUZhY3RvcnkodXJsOiBzdHJpbmcsIHJvb3RLZXk6IHN0cmluZywgcm9vdEtleVBsdXJhbDogc3RyaW5nKSB7XG5cbiAgICBsZXQgZGIgPSBSZXNvdXJjZURCRmFjdG9yeSgpO1xuXG4gICAgLy8gQ3JlYXRlIHRoZSBzZXJ2ZXIgcmVzb3VyY2UgYW5kIHF1ZXJ5XG4gICAgbGV0IFNlcnZlclJlc291cmNlID0gU2VydmVyUmVzb3VyY2VGYWN0b3J5KHVybCwgcm9vdEtleSwgcm9vdEtleVBsdXJhbCk7XG4gICAgbGV0IFF1ZXJ5TGlzdCA9IFF1ZXJ5RmFjdG9yeSh1cmwsIHJvb3RLZXksIHJvb3RLZXlQbHVyYWwsIGRiKTtcbiAgICBsZXQgQ29sbGVjdGlvbiA9IENvbGxlY3Rpb25GYWN0b3J5KCk7XG5cbiAgICAvLyBJbiBtZW1vcnkgcmVzb3VyY2Ugc3RvcmVcbiAgICBsZXQgX3Jlc291cmNlczogUmVzb3VyY2VNYXAgPSB7fTtcbiAgICBsZXQgX2ludGVybmFsUmVzOiBSZXNvdXJjZU1hcCA9IHt9O1xuXG4gICAgbGV0IG91clN0b3JhZ2VTaXplID0gMDtcbiAgICBsZXQgb3VyRGVzaXJlZFNpemUgPSAwO1xuICAgIGxldCBwZXJzaXN0TW9kZVdhdGNoaW5nID0gZmFsc2U7XG5cbiAgICBsZXQgX2xhc3RyZXFzOiB7W2lkOiBzdHJpbmddOiBMYXN0UmVxdWVzdH0gPSB7fTtcbiAgICBsZXQgX3JlcXMgPSB7fTtcblxuICAgIGxldCBwU3RvcmFnZUtleSA9IHV0aWxzLnBlcnNpc3RlbnRTdG9yYWdlS2V5KHVybCk7XG4gICAgbGV0IHBlcnNpc3RQcm9tOiBQcm9taXNlPGFueT4gPSBudWxsO1xuXG4gICAgZnVuY3Rpb24gdG9PYmplY3QoKSB7XG4gICAgICByZXR1cm4gdXRpbHMudG9PYmplY3QodGhpcyk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gaWRUb01vZGVsKGlkOiBzdHJpbmcpIHtcbiAgICAgIGxldCBsb2MgPSBfaW50ZXJuYWxSZXNbaWRdO1xuICAgICAgcmV0dXJuIGxvYy4kbW9kO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIHNlcnZlclRyYW5zZm9ybShzZXJ2OiBTZXJ2ZXJNb2RlbCwgaWQ6IHN0cmluZywgdHJhbnNmb3JtZXI6IEZ1bmN0aW9uKSB7XG4gICAgICBsZXQgcmVzOiBMb2NhbE1vZGVsID0gX2ludGVybmFsUmVzW3NlcnYuJGlkXTtcblxuICAgICAgLy8gSWYgd2UgZG9uJ3QgaGF2ZSBhIHJlc291cmNlIHRoZW4gY3JlYXRlIGl0XG4gICAgICBpZiAoIXJlcykge1xuICAgICAgICByZXMgPSBuZXcgKDxhbnk+IExvY2FsUmVzb3VyY2UpKHNlcnYsIHRydWUpO1xuICAgICAgfVxuXG4gICAgICBpZiAoaWQpIHtcbiAgICAgICAgX3Jlc291cmNlc1tpZF0gPSByZXM7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB0cmFuc2Zvcm1lcihyZXMsIGlkKTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBnZXQoaWRzOiAoc3RyaW5nIHwgc3RyaW5nW10pLCBmb3JjZT86IGJvb2xlYW4sIHRyYW5zZm9ybT86IEZ1bmN0aW9uKSB7XG4gICAgICAvLyBXZSd2ZSByZXF1ZXN0ZWQgYSBidW5jaCBvZiBpZHNcbiAgICAgIC8vIFdlIHNob3VsZCBwZXJzaXN0IHRoZSBjaGFuZ2UgKHRvIG9mZmxpbmUgc3RvcmFnZSkgaWYgdGhpcyBpcyB0aGUgZmlyc3QgcmVxdWVzdFxuICAgICAgLy8gZm9yIHRoaXMgKHRoZXNlKSBvYmplY3RzXG4gICAgICBsZXQgbm93ID0gbmV3IERhdGUoKS5nZXRUaW1lKCk7XG4gICAgICBsZXQgaXNTb21lRmlyc3QgPSBmYWxzZTtcbiAgICAgIGlmIChBcnJheS5pc0FycmF5KGlkcykpIHtcbiAgICAgICAgaWRzLmZvckVhY2goZnVuY3Rpb24oaWQpIHtcbiAgICAgICAgICBpZiAoIV9yZXFzW2lkXSkge1xuICAgICAgICAgICAgaXNTb21lRmlyc3QgPSB0cnVlO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIF9sYXN0cmVxc1tpZF0gPSBub3c7XG4gICAgICAgICAgX3JlcXNbaWRdID0gdHJ1ZTtcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpZiAoIV9yZXFzW2lkc10pIHtcbiAgICAgICAgICBpc1NvbWVGaXJzdCA9IHRydWU7XG4gICAgICAgIH1cblxuICAgICAgICBfbGFzdHJlcXNbaWRzXSA9IG5vdztcbiAgICAgICAgX3JlcXNbaWRzXSA9IHRydWU7XG4gICAgICB9XG5cbiAgICAgIC8vIElmIG91ciBwZXJzaXN0TW9kZSBpcyBNSU4gd2UnbGwgd2FudCB0byBwZXJzaXN0IHRvIHN0b3JhZ2UgaGVyZVxuICAgICAgaWYgKHBlcnNpc3RNb2RlID09PSAnTUlOJyAmJiBpc1NvbWVGaXJzdCkge1xuICAgICAgICBwZXJzaXN0Q2hhbmdlKCk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBTZXJ2ZXJSZXNvdXJjZS5nZXQoaWRzLCBmb3JjZSwgZnVuY3Rpb24oc2VydjogU2VydmVyTW9kZWwsIGlkOiBzdHJpbmcpIHtcbiAgICAgICAgcmV0dXJuIHNlcnZlclRyYW5zZm9ybShzZXJ2LCBpZCwgdHJhbnNmb3JtKTtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGNvbGxlY3QoUmVzb3VyY2U6IFJlc291cmNlPE1vZGVsPiwgc2VlZHM6IE1vZGVsIHwgTW9kZWxBcnJheTxNb2RlbD4sIHJlbGF0aW9uOiBDb2xsZWN0aW9uU2VhcmNoZXIpIHtcbiAgICAgIHJldHVybiBuZXcgQ29sbGVjdGlvbihSZXNvdXJjZSwgc2VlZHMsIHJlbGF0aW9uKTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBxdWVyeShxcnk6IGFueSwgbGltaXQ6IG51bWJlciwgUmVzb3VyY2U6IFJlc291cmNlPE1vZGVsPikge1xuICAgICAgcmV0dXJuIFF1ZXJ5TGlzdChxcnksIGxpbWl0LCBSZXNvdXJjZSwgaWRUb01vZGVsKTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiByZW1vdmUodGhpczogTG9jYWxNb2RlbCwgc2tpcFNlcnZlcj86IGJvb2xlYW4pIHtcbiAgICAgIGlmICh0aGlzLl9pZCkge1xuICAgICAgICBkZWxldGUgX3Jlc291cmNlc1t0aGlzLl9pZF07XG4gICAgICB9XG5cbiAgICAgIHRoaXMuJGRlbGV0ZWQgPSB0cnVlO1xuXG4gICAgICAvLyBLaWNrIHRoZSBkYXRhYmFzZVxuICAgICAgc3luY1RvU3RvcmFnZSh0aGlzKTtcblxuICAgICAgLy8gSWYgd2UgaGF2ZSBiZWVuIGNyZWF0ZWQgdGhlbiBub3RpZnkgdGhlIHNlcnZlclxuXG4gICAgICBpZiAodGhpcy4kY3JlYXRlZCAmJiAhc2tpcFNlcnZlcikge1xuICAgICAgICByZXR1cm4gdGhpcy4kc2Vydi4kcmVtb3ZlKCk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUodHJ1ZSk7XG4gICAgfVxuXG4gICAgLy8gT25jZSB3ZSBhcmUgc3luY2VkIHdpdGggdGhlIHNlcnZlciByZXNvdXJjZSB3ZSB3aWxsIHJlc29sdmUgdGhlIHByb21pc2VcbiAgICBmdW5jdGlvbiBzYXZlKHRoaXM6IExvY2FsTW9kZWwsIHZhbHM6IGFueSkge1xuICAgICAgbGV0IHJlcyA9IHRoaXM7XG4gICAgICByZXMuJGNyZWF0ZWQgPSB0cnVlO1xuXG4gICAgICAvLyBPbmx5IHRyaWdnZXIgdGhlIHNlcnZlciBzeW5jIG9uY2UgcGVyICd0aWNrJyAoc28gY2FsbGluZyBzYXZlKCkgbXVsdGlwbGUgdGltZXNcbiAgICAgIC8vIGhhcyBubyBlZmZlY3QpXG4gICAgICBpZiAoIXJlcy4kc2F2ZXByb20pIHtcbiAgICAgICAgbGV0IG9sZERhdGEgPSB0aGlzLiR0b09iamVjdCgpO1xuICAgICAgICByZXMuJHNhdmVwcm9tID0gbmV3IFByb21pc2UocmVzb2x2ZSA9PiB7XG4gICAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSkudGhlbihmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgcmVzLiRzYXZlcHJvbSA9IG51bGw7XG5cbiAgICAgICAgICAvLyBTYXZlIHVzIHRvIHRoZSBkYlxuICAgICAgICAgIHN5bmNUb1N0b3JhZ2UocmVzKTtcblxuICAgICAgICAgIGxldCBwYXRjaCA9IHV0aWxzLmRpZmYob2xkRGF0YSwgcmVzLiR0b09iamVjdCgpKTtcbiAgICAgICAgICByZXR1cm4gc3luY1RvU2VydmVyKHJlcywgcGF0Y2gpLnRoZW4oZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICByZXR1cm4gcmVzO1xuICAgICAgICAgIH0pO1xuICAgICAgICB9KS50aGVuKGZ1bmN0aW9uKCkge1xuICAgICAgICAgIGlmICghcmVzLiRyZXNvbHZlZCkge1xuICAgICAgICAgICAgLy8gV2FpdCBmb3IgdGhlIGRiIHRvIHN5bmMgYmVmb3JlIHJlc29sdmluZyAoaWYgdGhlIHN5bmMgaXMgb3V0c3RhbmRpbmcpXG4gICAgICAgICAgICBsZXQgZGJwcm9tID0gcmVzLiRkYnN5bmMgPyByZXMuJGRic3luYyA6IFByb21pc2UucmVzb2x2ZSgpO1xuICAgICAgICAgICAgZGJwcm9tLnRoZW4oZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgIHJlcy4kZGVmZXJyZWQucmVzb2x2ZShyZXMpO1xuICAgICAgICAgICAgICByZXMuJHJlc29sdmVkID0gdHJ1ZTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gcmVzO1xuICAgICAgICB9KTtcbiAgICAgIH1cblxuICAgICAgdXRpbHMucmVtb3ZlUmVzVmFsdWVzKHJlcyk7XG4gICAgICB1dGlscy5zZXRSZXNWYWx1ZXMocmVzLCB2YWxzKTtcblxuICAgICAgcmV0dXJuIHJlcy4kc2F2ZXByb207XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gcGVyZm9ybVNlcnZlclN5bmMocmVzOiBhbnksIHBhdGNoOiBKU09OUGF0Y2gpIHtcbiAgICAgIC8vIFdlIGFyZSBhYm91dCB0byBzeW5jLiBVbnNldCB0aGUgcmVzeW5jIGZsYWdcbiAgICAgIHJlcy4kcmVzeW5jID0gW107XG5cbiAgICAgIC8vIFdhaXQgZm9yIHRoZSBzZXJ2ZXIgdG8gZmluaXNoIHNhdmluZy4gVGhlbiBjaGVjayBpZiB3ZSBuZWVkIHRvIHJlc3luYy4gVGhpcyBwcm9taXNlXG4gICAgICAvLyB3b24ndCByZXNvbHZlIHVudGlsIHRoZXJlIGFyZSBubyBtb3JlIHJlc3luY3MgdG8gZG9cbiAgICAgIHJldHVybiByZXMuJHNlcnYuJHNhdmUocGF0Y2gpLnRoZW4oZnVuY3Rpb24oKSB7XG4gICAgICAgIGlmIChyZXMuJHJlc3luYy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgcmV0dXJuIHBlcmZvcm1TZXJ2ZXJTeW5jKHJlcywgcmVzLiRyZXN5bmMpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHJlcztcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIHN5bmNUb1NlcnZlcihyZXM6IGFueSwgcGF0Y2g6IEpTT05QYXRjaCkge1xuICAgICAgaWYgKHJlcy4kc3luYykge1xuICAgICAgICByZXMuJHJlc3luYy5wdXNoLmFwcGx5KHJlcy4kcmVzeW5jLCBwYXRjaCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBsZXQgcHJvbSA9IHBlcmZvcm1TZXJ2ZXJTeW5jKHJlcywgcGF0Y2gpWydmaW5hbGx5J10oZnVuY3Rpb24oKSB7XG4gICAgICAgICAgLy8gT25jZSB3ZSd2ZSBzeW5jZWQgcmVtb3ZlIHRoZSAkc3luYyBwcm9taXNlXG4gICAgICAgICAgZGVsZXRlIHJlcy4kc3luYztcbiAgICAgICAgfSk7XG5cbiAgICAgICAgcmVzLiRzeW5jID0gcHJvbTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHJlcy4kc3luYztcbiAgICB9XG5cbiAgICBmdW5jdGlvbiB1cGRhdGVkU2VydmVyKHJlczogTG9jYWxNb2RlbCwgbmV3VmFsOiBhbnksIG9sZFZhbDogYW55KSB7XG5cbiAgICAgIC8vIFdlIGNvdWxkIGhhdmUgYmVlbiBkZWxldGVkIChleGlzdGluZyBvbGRWYWwsIG51bGwgbmV3dmFsKVxuICAgICAgaWYgKG9sZFZhbCAmJiAhbmV3VmFsKSB7XG4gICAgICAgIC8vIFdlIGhhdmUgYmVlbiBkZWxldGVkLiBDbGVhbnVwIG91cnNlbHZlcyBhbmQgcGFzcyBpdCB1cCB0aGUgY2hhaW5cbiAgICAgICAgcmVzLiRlbWl0dGVyLmVtaXQoJ3VwZGF0ZScsIG51bGwsIHJlcy4kdG9PYmplY3QoKSk7XG4gICAgICAgIHJlcy4kcmVtb3ZlKHRydWUpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmVzLiRjcmVhdGVkID0gdHJ1ZTtcblxuICAgICAgICBpZiAob2xkVmFsLl9pZCAmJiAobmV3VmFsLl9pZCAhPT0gb2xkVmFsLl9pZCkpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vdCBhbGxvd2VkIHRvIGNoYW5nZSBpZCcpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gTWVyZ2UgdGhlIG9iamVjdHMgdG9nZXRoZXIgdXNpbmcgdGhlIG9sZFZhbCBhcyBhIGJhc2UsIGFuZCB1c2luZyAqb3VyKiB2ZXJzaW9uXG4gICAgICAgIC8vIHRvIHJlc29sdmUgYW55IGNvbmZsaWN0cy4gV2UgbWF5IG5lZWQgdG8gcHV0IGluIHNvbWUgY29uZmxpY3QgcmVzb2x1dGlvbiBsb2dpY1xuICAgICAgICAvLyBzb21ld2hlcmUgb24gYSBjYXNlIGJ5IGNhc2UgYmFzaXNcbiAgICAgICAgbGV0IHByZWV4aXN0ID0gcmVzLiR0b09iamVjdCgpO1xuICAgICAgICBsZXQgbWVyZ2UgPSB1dGlscy5tZXJnZU9iamVjdHMocmVzLiR0b09iamVjdCgpLCBvbGRWYWwsIG5ld1ZhbCk7XG5cbiAgICAgICAgLy8gTm93IHB1dCBiYWNrIGluIHRoZSBtZXJnZSB2YWx1ZXNcbiAgICAgICAgdXRpbHMucmVtb3ZlUmVzVmFsdWVzKHJlcyk7XG4gICAgICAgIHV0aWxzLnNldFJlc1ZhbHVlcyhyZXMsIG1lcmdlKTtcblxuICAgICAgICAvLyBNYWtlIHN1cmUgd2UgYXJlIHN0b3JlZFxuICAgICAgICBfcmVzb3VyY2VzW3Jlcy5faWRdID0gcmVzO1xuXG4gICAgICAgIC8vIElmIHdlJ3ZlIG9ubHkganVzdCBiZWVuIGdpdmVuIGFuIGlkIHRoZW4gc3RvcmUgb2YgdGhlIGNyZWF0ZWQgdGltZSBhcyB0aGVcbiAgICAgICAgLy8gdGltZSB3ZSB3ZXJlIGxhc3QgcmVxdWVzdGVkICh0aGlzIGlzIGJlY2F1c2UgdGhlIG9iamVjdCBtdXN0IGhhdmUganVzdCBiZWVuXG4gICAgICAgIC8vIGNyZWF0ZWQgb24gdGhlIHNlcnZlcilcbiAgICAgICAgaWYgKHJlcy5faWQgJiYgIW9sZFZhbC5faWQpIHtcbiAgICAgICAgICBfbGFzdHJlcXNbcmVzLl9pZF0gPSByZXMuJGNyZWF0ZWRBdDtcbiAgICAgICAgICBfcmVxc1tyZXMuX2lkXSA9IHRydWU7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBOb3RpZnkgdGhhdCB3ZSBoYXZlIGNoYW5nZWRcbiAgICAgICAgcmVzLiRlbWl0dGVyLmVtaXQoJ3VwZGF0ZScsIHJlcy4kdG9PYmplY3QoKSwgcHJlZXhpc3QpO1xuICAgICAgICAvLyBLaWNrIHRoZSBkYlxuICAgICAgICBsZXQgc3luY3Byb20gPSBzeW5jVG9TdG9yYWdlKHJlcykgfHwgUHJvbWlzZS5yZXNvbHZlKCk7XG5cbiAgICAgICAgc3luY3Byb20udGhlbihmdW5jdGlvbigpIHtcbiAgICAgICAgICAvLyBXZSBtaWdodCBoYXZlIHN5bmNlZCBmb3IgdGhlIGZpcnN0IHRpbWVcbiAgICAgICAgICByZXR1cm4gcmVzLiRzZXJ2LiRwcm9taXNlLnRoZW4oZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICBpZiAoIXJlcy4kcmVzb2x2ZWQpIHtcbiAgICAgICAgICAgICAgcmVzLiRkZWZlcnJlZC5yZXNvbHZlKHJlcyk7XG4gICAgICAgICAgICAgIHJlcy4kcmVzb2x2ZWQgPSB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBmdW5jdGlvbiBwZXJmb3JtRGJTeW5jKHJlczogTG9jYWxNb2RlbCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgcmVzLiRkYnJlc3luYyA9IGZhbHNlO1xuICAgICAgcmV0dXJuIGRiLnVwZGF0ZShyZXMpLnRoZW4oZnVuY3Rpb24oKSB7XG4gICAgICAgIGlmIChyZXMuJGRicmVzeW5jKSB7XG4gICAgICAgICAgcmV0dXJuIHBlcmZvcm1EYlN5bmMocmVzKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybjtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIHdhdGNoRm9yUGVyc2lzdE1vZGVDaGFuZ2VzKCkge1xuICAgICAgaWYgKHBlcnNpc3RNb2RlV2F0Y2hpbmcpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBwZXJzaXN0TW9kZUVtaXR0ZXIub24oJ2NoYW5nZScsIGZ1bmN0aW9uKF91cmwpIHtcbiAgICAgICAgLy8gSWYgd2UgaGF2ZSBjYWxsZWQgdGhpcyB0aGVuIGlnbm9yZVxuICAgICAgICBpZiAoX3VybCA9PT0gdXJsKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gUmVkbyB0aGUgcGVyc2lzdFxuICAgICAgICBkb1BlcnNpc3QoKTtcbiAgICAgIH0pO1xuXG4gICAgfVxuXG4gICAgZnVuY3Rpb24gZG9QZXJzaXN0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgLy8gSWYgd2UncmUgYWxyZWFkeSBkb2luZyBhIHBlcnNpc3Qgb3Igd2UgZG9udCBoYXZlIGFkdmFuY2VkIHN0b3JhZ2Ugb3B0aW9ucyB0aGVuXG4gICAgICAvLyBqdXN0IHJldHVyblxuICAgICAgaWYgKHBlcnNpc3RNb2RlID09PSAnTk9ORScgfHwgIXV0aWxzLmFkdmFuY2VkU3RvcmFnZShsb2NhbGZvcmFnZSkpIHtcbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xuICAgICAgfVxuXG4gICAgICAvLyBJZiB0aGlzIGlzIHRoZSBmaXJzdCB0aW1lIHRocm91Z2ggdGhlbiBzdGljayBhIGxpc3RlbmVyIG9uIGZvciBjaGFuZ2VzXG4gICAgICBpZiAoIXBlcnNpc3RNb2RlV2F0Y2hpbmcpIHtcbiAgICAgICAgd2F0Y2hGb3JQZXJzaXN0TW9kZUNoYW5nZXMoKTtcbiAgICAgIH1cblxuICAgICAgbGV0IGRhdGE6IEFycmF5PHtsYXN0cmVxOiBMYXN0UmVxdWVzdCwgb2JqOiBhbnl9PiA9IFtdO1xuXG4gICAgICBzd2l0Y2ggKHBlcnNpc3RNb2RlKSB7XG4gICAgICAgIGNhc2UgJ0ZVTEwnOlxuICAgICAgICAgIGRhdGEgPSB2YWx1ZXMoX3Jlc291cmNlcykubWFwKGZ1bmN0aW9uKHJlcykge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgbGFzdHJlcTogX2xhc3RyZXFzW3Jlcy5faWRdLFxuICAgICAgICAgICAgICBvYmo6IHJlcy4kdG9PYmplY3QoKSxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgY2FzZSAnTUlOJzpcbiAgICAgICAgICB2YWx1ZXMoX3Jlc291cmNlcykuZm9yRWFjaChmdW5jdGlvbihyZXMpIHtcbiAgICAgICAgICAgIGlmIChfcmVxc1tyZXMuX2lkXSkge1xuICAgICAgICAgICAgICBkYXRhLnB1c2goe1xuICAgICAgICAgICAgICAgIGxhc3RyZXE6IF9sYXN0cmVxc1tyZXMuX2lkXSxcbiAgICAgICAgICAgICAgICBvYmo6IHJlcy4kdG9PYmplY3QoKSxcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xuICAgICAgfVxuXG4gICAgICAvLyBXZSBuZWVkIHRvIG1hbnVhbGx5IG1hbmFnZSBzdG9yYWdlXG4gICAgICBsZXQgZGF0YVN0ciA9IEpTT04uc3RyaW5naWZ5KGRhdGEpO1xuXG4gICAgICBsZXQgbmV3U3RvcmFnZVNpemUgPSBkYXRhU3RyLmxlbmd0aDtcbiAgICAgIGxldCBleHBlY3RlZFNpemUgPSB0b3RhbERlc2lyZWRTaXplIC0gb3VyRGVzaXJlZFNpemUgKyBuZXdTdG9yYWdlU2l6ZTtcblxuICAgICAgLy8gRG8gd2UgZXhwZWN0IHRvIGJ1c3QgdGhlIG1heCBzaXplPyBJZiBzbyB3ZSBuZWVkIHRvIGNoYW5nZSBwZXJzaXN0IG1vZGVcbiAgICAgIC8vIGFuZCBlbWl0XG4gICAgICBpZiAoZXhwZWN0ZWRTaXplID4gTUFYX1NUT1JBR0VfU0laRSkge1xuICAgICAgICBpZiAocGVyc2lzdE1vZGUgPT09ICdGVUxMJykge1xuICAgICAgICAgIHBlcnNpc3RNb2RlID0gJ01JTic7XG4gICAgICAgIH0gZWxzZSBpZiAocGVyc2lzdE1vZGUgPT09ICdNSU4nKSB7XG4gICAgICAgICAgcGVyc2lzdE1vZGUgPSAnTk9ORSc7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gRG9uJ3Qga25vdyBob3cgd2UgY291bGQgZ2V0IGhlcmUgYnV0IHJldHVybiBqdXN0IGluIGNhc2VcbiAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgICAgIH1cblxuICAgICAgICBwZXJzaXN0TW9kZUVtaXR0ZXIuZW1pdCgnY2hhbmdlJywgdXJsKTtcblxuICAgICAgICAvLyBTY2hlZHVsZSB0aGlzIGxhdGVyXG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZTx2b2lkPihyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSkpLnRoZW4oZG9QZXJzaXN0KTtcbiAgICAgIH1cblxuICAgICAgLy8gTU9ERSBIQVMgTk9UIENIQU5HRURcblxuICAgICAgLy8gU3RvcmUgb3VyIGV4cGVjdGVkIHNpemVcbiAgICAgIHRvdGFsRGVzaXJlZFNpemUgPSBleHBlY3RlZFNpemU7XG4gICAgICBvdXJEZXNpcmVkU2l6ZSA9IG5ld1N0b3JhZ2VTaXplO1xuXG4gICAgICByZXR1cm4gbG9jYWxmb3JhZ2Uuc2V0SXRlbShwU3RvcmFnZUtleSwgZGF0YSkudGhlbihmdW5jdGlvbigpIHtcbiAgICAgICAgdG90YWxTdG9yYWdlU2l6ZSA9IHRvdGFsU3RvcmFnZVNpemUgLSBvdXJTdG9yYWdlU2l6ZSArIG5ld1N0b3JhZ2VTaXplO1xuICAgICAgICBvdXJTdG9yYWdlU2l6ZSA9IG5ld1N0b3JhZ2VTaXplO1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gcGVyc2lzdENoYW5nZSgpIHtcbiAgICAgIC8vIElmIHdlJ3JlIGFscmVhZHkgZG9pbmcgYSBwZXJzaXN0IG9yIHdlIGRvbnQgaGF2ZSBhZHZhbmNlZCBzdG9yYWdlIG9wdGlvbnMgdGhlblxuICAgICAgLy8ganVzdCByZXR1cm5cbiAgICAgIGlmIChwZXJzaXN0UHJvbSB8fCBwZXJzaXN0TW9kZSA9PT0gJ05PTkUnIHx8ICF1dGlscy5hZHZhbmNlZFN0b3JhZ2UobG9jYWxmb3JhZ2UpKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgcGVyc2lzdFByb20gPSBuZXcgUHJvbWlzZShyID0+IHNldFRpbWVvdXQociwgNTAwKSkudGhlbihmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIGRvUGVyc2lzdCgpLnRoZW4oZnVuY3Rpb24oKSB7XG4gICAgICAgICAgLy8gRmluaXNoZWQgdGhlIHBlcnNpc3RcbiAgICAgICAgICBwZXJzaXN0UHJvbSA9IG51bGw7XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gc3luY1RvU3RvcmFnZShyZXM6IExvY2FsTW9kZWwpOiBQcm9taXNlPHZvaWQ+IHtcblxuICAgICAgcGVyc2lzdENoYW5nZSgpO1xuXG4gICAgICBpZiAocmVzLiRkYnN5bmMpIHtcbiAgICAgICAgcmVzLiRkYnJlc3luYyA9IHRydWU7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBsZXQgcHJvbSA9IHBlcmZvcm1EYlN5bmMocmVzKTtcblxuICAgICAgICBwcm9tLnRoZW4oZmluaXNoZWRTeW5jLCBmaW5pc2hlZFN5bmMpO1xuXG4gICAgICAgIHJlcy4kZGJzeW5jID0gcHJvbTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHJlcy4kZGJzeW5jO1xuXG4gICAgICBmdW5jdGlvbiBmaW5pc2hlZFN5bmMoKSB7XG4gICAgICAgIC8vIFdoYXRldmVyIGhhcHBlbnMgcmVtb3ZlIHRoZSAkc3luYyBwcm9taXNlIGFuZCByZWZyZXNoIGFsbCB0aGUgcXVlcmllc1xuICAgICAgICBkZWxldGUgcmVzLiRkYnN5bmM7XG4gICAgICAgIHJlcy4kZGJyZXN5bmMgPSBmYWxzZTtcbiAgICAgICAgUXVlcnlMaXN0LnJlZnJlc2goKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBmdW5jdGlvbiB1cGRhdGVTZXJ2ZXIodGhpczogTG9jYWxNb2RlbCwgdmFsOiBhbnkpIHtcbiAgICAgIHRoaXMuJHNlcnYuJHVwZGF0ZVZhbCh2YWwpO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIHJlZnJlc2godGhpczogTG9jYWxNb2RlbCkge1xuICAgICAgdGhpcy4kc2Vydi4kcmVmcmVzaCgpO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIExvY2FsUmVzb3VyY2VDb25zdHJ1Y3Rvcih2YWw6IGFueSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnJvbVNlcnY/OiBib29sZWFuLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2Q/OiBhbnksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3RSZXF1ZXN0ZWQ/OiAoRGF0ZSB8IG51bWJlcikpIHtcbiAgICAgIGxldCByZXMgPSB0aGlzO1xuICAgICAgdGhpcy4kZW1pdHRlciA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcblxuICAgICAgdGhpcy4kZGVmZXJyZWQgPSBuZXcgdXRpbHMuRGVmZXJyZWQoKTtcbiAgICAgIHRoaXMuJHByb21pc2UgPSB0aGlzLiRkZWZlcnJlZC5wcm9taXNlOyAvLyBBbiBpbml0aWFsIHByb21pc2UgZm9yIG91ciBpbml0aWFsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gZmV0Y2ggb3IgY3JlYXRlIG9mIGRhdGFcbiAgICAgIHRoaXMuJHJlc29sdmVkID0gZmFsc2U7IC8vIEhhdmUgd2UgaGFkIGFuIGluaXRpYWwgcmVzb2x1dGlvbiBvZiB0aGUgcHJvbWlzZVxuICAgICAgdGhpcy4kY3JlYXRlZCA9IGZhbHNlOyAvLyBIYXZlIHdlIHB1c2hlZCBhbnkgdmFsdWVzIGRvd24gdG8gdGhlIHNlcnZlciB5ZXQ/XG5cbiAgICAgIHRoaXMuJGNyZWF0ZWRBdCA9IG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xuXG4gICAgICB0aGlzLiRyZXN5bmMgPSBbXTtcbiAgICAgIHRoaXMuJHNhdmVwcm9tID0gbnVsbDtcbiAgICAgIHRoaXMuJGRlbGV0ZWQgPSBmYWxzZTtcblxuICAgICAgLy8gU3RvcmUgb2ZmIHRoZSBtb2RlbCBzbyB3ZSBjYW4gcmVmZXJlbmNlIGl0IGxhdGVyXG4gICAgICB0aGlzLiRtb2QgPSBtb2Q7XG5cbiAgICAgIC8vIFVzZWQgdG8gY29ycmVsYXRlIHRvIHRoZSBkYiBvYmplY3RzIHdoZW4gd2UgZG9uJ3QgaGF2ZSBhbiBfaWQgKGNyZWF0aW5nKVxuICAgICAgdGhpcy4kaWQgPSBmcm9tU2VydiA/IHZhbC4kaWQgOiB1dGlscy51dWlkKCk7XG4gICAgICBfaW50ZXJuYWxSZXNbdGhpcy4kaWRdID0gdGhpcztcblxuICAgICAgLy8gSWYgd2UndmUgYmVlbiBnaXZlbiB2YWx1ZXMgcHV0IHRoZW0gb25cbiAgICAgIGlmICh2YWwpIHtcbiAgICAgICAgbGV0IHByb3BzID0gZnJvbVNlcnYgPyB2YWwuJHRvT2JqZWN0KCkgOiB2YWw7XG5cbiAgICAgICAgZm9yIChsZXQga2V5IG9mIE9iamVjdC5rZXlzKHByb3BzKSkge1xuICAgICAgICAgIHJlc1trZXldID0gcHJvcHNba2V5XTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAodmFsICYmIGZyb21TZXJ2KSB7XG4gICAgICAgIHRoaXMuJHNlcnYgPSB2YWw7XG4gICAgICB9IGVsc2UgaWYgKHRoaXMuX2lkKSB7XG4gICAgICAgIHRoaXMuJHNlcnYgPSBuZXcgKDxhbnk+IFNlcnZlclJlc291cmNlKSh2YWwsIHRoaXMuJGlkKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIERvbid0IGFkZCBpbiB0aGUgdmFsdWVzIHVudGlsIHdlIHNhdmVcbiAgICAgICAgdGhpcy4kc2VydiA9IG5ldyAoPGFueT4gU2VydmVyUmVzb3VyY2UpKG51bGwsIHRoaXMuJGlkKTtcbiAgICAgIH1cblxuICAgICAgLy8gSWYgd2UgaGF2ZSBhbiBpZCB0aGVuIGFkZCB1cyB0byB0aGUgc3RvcmVcbiAgICAgIGlmICh0aGlzLl9pZCkge1xuICAgICAgICBfcmVzb3VyY2VzW3RoaXMuX2lkXSA9IHRoaXM7XG4gICAgICB9XG5cbiAgICAgIC8vIElmIHdlIGhhdmUgYW4gaWQgYW5kIHdlJ3ZlIGJlZW4gcGFzc2VkIGluIGEgbGFzdCByZXF1ZXN0ZWQgdGltZSB0aGVuIHN0b3JlIG9mZlxuICAgICAgLy8gdGhlIGxhc3QgcmVxdWVzdGVkIHRpbWVcbiAgICAgIGlmICh0aGlzLl9pZCAmJiBsYXN0UmVxdWVzdGVkKSB7XG4gICAgICAgIC8vIElmIHRoZSBsYXN0IHJlcXVlc3RlZCBhbHJlYWR5IGV4aXN0cyB1c2UgdGhlIG1heFxuICAgICAgICBsZXQgZXhpc3RpbmcgPSBfbGFzdHJlcXNbdGhpcy5faWRdIHx8IDA7XG4gICAgICAgIF9sYXN0cmVxc1t0aGlzLl9pZF0gPSBNYXRoLm1heChsYXN0UmVxdWVzdGVkIGFzIG51bWJlciwgZXhpc3RpbmcgYXMgbnVtYmVyKTtcbiAgICAgIH1cblxuICAgICAgLy8gTGlzdGVuIGZvciBjaGFuZ2VzIG9uIHRoZSBzZXJ2ZXJcbiAgICAgIHRoaXMuJHNlcnYuJGVtaXR0ZXIub24oJ3VwZGF0ZScsIGZ1bmN0aW9uKG5ld1ZhbDogYW55LCBvbGRWYWw6IGFueSkge1xuICAgICAgICB1cGRhdGVkU2VydmVyKHJlcywgbmV3VmFsLCBvbGRWYWwpO1xuICAgICAgfSk7XG5cbiAgICAgIC8vIFVwZGF0ZSB1cyBpbiB0aGUgZGJcbiAgICAgIHRoaXMuJGRicmVzeW5jID0gZmFsc2U7XG5cbiAgICAgIC8vIElmIGl0J3MgZnJvbSB0aGUgc2VydmVyIGRvbid0IGNyZWF0ZSBpdCB5ZXQuIFdhaXQgZm9yIHRoZSB1cGRhdGUgdG8gY29tZSAoYWxvbmdcbiAgICAgIC8vIHdpdGggaG9wZWZ1bGx5IGFsbCB0aGUgZGF0YSlcbiAgICAgIGlmICghZnJvbVNlcnYgJiYgdmFsKSB7XG4gICAgICAgIHN5bmNUb1N0b3JhZ2UodGhpcyk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3QgTG9jYWxSZXNvdXJjZTogSUxvY2FsUmVzb3VyY2UgPSBMb2NhbFJlc291cmNlQ29uc3RydWN0b3I7XG4gICAgTG9jYWxSZXNvdXJjZS5nZXQgPSBnZXQ7XG4gICAgTG9jYWxSZXNvdXJjZS5xdWVyeSA9IHF1ZXJ5O1xuICAgIExvY2FsUmVzb3VyY2UuY29sbGVjdCA9IGNvbGxlY3Q7XG5cbiAgICBMb2NhbFJlc291cmNlLnByb3RvdHlwZS4kc2F2ZSA9IHNhdmU7XG4gICAgTG9jYWxSZXNvdXJjZS5wcm90b3R5cGUuJHJlbW92ZSA9IHJlbW92ZTtcbiAgICBMb2NhbFJlc291cmNlLnByb3RvdHlwZS4kZGVsZXRlID0gcmVtb3ZlO1xuICAgIExvY2FsUmVzb3VyY2UucHJvdG90eXBlLiR0b09iamVjdCA9IHRvT2JqZWN0O1xuICAgIExvY2FsUmVzb3VyY2UucHJvdG90eXBlLiR1cGRhdGVTZXJ2ZXIgPSB1cGRhdGVTZXJ2ZXI7XG4gICAgTG9jYWxSZXNvdXJjZS5wcm90b3R5cGUuJHJlZnJlc2ggPSByZWZyZXNoO1xuXG4gICAgcmV0dXJuIExvY2FsUmVzb3VyY2U7XG4gIH1cblxuICByZXR1cm4gTG9jYWxSZXNvdXJjZUZhY3Rvcnk7XG59XG4iXX0=