UNPKG

@ortoo/angular-resource

Version:

Magical CRUD resource management for angular

549 lines 72.7 kB
import { cloneDeep } from 'lodash';
import { clone } from 'lodash';
import { map } from 'lodash';
import { union } from 'lodash';
import { isFunction } from 'lodash';
import { isEmpty } from 'lodash';
import { isUndefined } from 'lodash';
import { ArrayEmitter } from './array-emitter.js';
import { Chain } from './chain';
import { convertToResolvedSubject, Deferred } from './utils';
export class Query extends ArrayEmitter {
}
export class LocalQuery extends Query {
}
export class ServerQuery extends Query {
}
function normalizeQuery(qry) {
    if (!qry) {
        return;
    }
    if (qry._q) {
        return qry._q;
    }
    else if (qry.find) {
        return qry;
    }
    else {
        return { find: qry };
    }
}
function extendQuery(qry1, qry2) {
    // Calc the new query that we want
    const _qry = cloneDeep(qry1);
    ['limit', 'skip', 'sort'].forEach(prop => {
        if (qry2[prop]) {
            _qry[prop] = qry2[prop];
        }
    });
    if (!isEmpty(qry2.find)) {
        // Want to or together - but is the toplevel already an or? (and only an or)
        let existingOr = false;
        if (_qry.find.$or) {
            let valid = true;
            for (const key in _qry.find) {
                if (key !== '$or') {
                    valid = false;
                    break;
                }
            }
            existingOr = valid;
        }
        if (existingOr) {
            _qry.find = clone(_qry.find);
        }
        else {
            _qry.find = { $or: [_qry.find] };
        }
        _qry.find.$or.push(qry2.find);
    }
    return _qry;
}
export function createQueryFactory(socket) {
    function QueryFactory(url, rootKey, rootKeyPlural, db) {
        const sock = socket(url, rootKey, rootKeyPlural);
        // Object holding queries (keys are the query ids, values are arrays of arrays)
        const _localQueries = [];
        /**
         * Local Query List
         * @param {[type]} qry      [description]
         * @param {[type]} limit    [description]
         * @param {[type]} Resource [description]
         * @param {[type]} toRes    [description]
         *
         * Strategy - we run the query on the server. However if we can deal with it locally (via
         * nedb) then do so. If the query at any time becomes more complicated we just fall through
         * to the server version
         */
        const LocalQueryList = (qry, limit, Resource, toRes) => {
            // Allow qry to be a promise, and we return the raw (non _q bit)
            qry = Promise.resolve(qry).then(normalizeQuery).then(_qry => {
                if (limit) {
                    _qry.limit = limit;
                }
                return _qry;
            });
            // Generate the ServerQuery. We do this so we have something to fall back on
            // and also so that we will (or should!) get notified of changes from the server if
            // someone else creates or removes something that should go in these results
            const serverResults = ServerQueryList(qry, limit, Resource);
            const results = new LocalQuery();
            _localQueries.push(results);
            let currentLimit = 0;
            let currentSkip = 0;
            let lastBatchSize = 0;
            let fallback = false;
            results.loading = true;
            // When the server results are updated we want to check paging options and apply
            // them to our results
            serverResults.$emitter.on('update', syncFromServer);
            function syncFromServer() {
                results.hasNext = serverResults.hasNext;
                results.hasPrev = serverResults.hasPrev;
                // If we are falling back then set up and copy our letious properties across
                if (fallback) {
                    results.$hasNext = false;
                    results.$hasPrev = false;
                    results.length = 0;
                    serverResults.forEach(res => {
                        results.push(res);
                    });
                    currentLimit = results.length;
                    currentSkip = results.$skip;
                    lastBatchSize = lastBatchSize || results.length;
                }
            }
            function get() {
                return Resource.get.apply(Resource, arguments);
            }
            function chain(Model, qryFn) {
                return Chain(results, Model, qryFn);
            }
            function query(_qry) {
                // Store off the qry making sure its a promise
                qry = Promise.resolve(_qry);
                fallback = !db.qryIsSimple(_qry);
                // If we are fallingback then just resolve with our results. The server should
                // do the rest.
                if (fallback) {
                    // We want to return the server's promise here so that people will be notified
                    // when the results are actually loaded
                    let prom;
                    if (!serverResults.$resolved) {
                        prom = serverResults.$promise.then(() => {
                            return results;
                        });
                    }
                    else {
                        prom = Promise.resolve(results);
                    }
                    return prom;
                }
                return db.query(_qry).then((ids) => {
                    // We can set the hasNext and hasPrev values to true here if we know there
                    // are some. However only the server has the definitive ability to say
                    // there arent any
                    results.$hasNext = false;
                    results.$hasPrev = false;
                    if (limit && ids.length > limit) {
                        // We have more results to fetch
                        results.hasNext = true;
                        results.$hasNext = true;
                        // Trim the results down to size
                        ids.length -= 1;
                    }
                    const skip = _qry.skip;
                    if (skip) {
                        results.hasPrev = true;
                        results.$hasPrev = true;
                    }
                    // Calculate paging options
                    currentLimit = ids.length;
                    currentSkip = skip || 0;
                    lastBatchSize = lastBatchSize || ids.length;
                    // Go to resource types
                    const tmpResults = ids.map(toRes);
                    // Do we need to do a transform?
                    let rprom;
                    if (isFunction(results.transform)) {
                        rprom = Promise.resolve(results.transform(tmpResults));
                    }
                    else {
                        rprom = Promise.resolve(tmpResults);
                    }
                    return rprom.then(transformed => {
                        // Put the resources into the list
                        const old = results.slice();
                        results.length = 0;
                        transformed.forEach((res) => {
                            results.push(res);
                        });
                        results.$emitter.emit('update', results, old);
                        return results;
                    });
                });
            }
            function refreshQuery(forceServer) {
                // Perform our query
                let prom = qry.then(query).then((res) => {
                    // If we don't have any results then maybe wait for the server to return
                    if ((res.length === 0 && !serverResults.$resolved)) {
                        return serverResults.$promise.then(() => {
                            return refreshQuery();
                        });
                    }
                    else {
                        return res;
                    }
                });
                if (forceServer) {
                    prom = serverResults.$refresh(true);
                }
                return prom.then((res) => {
                    results.loading = false;
                    return res;
                });
            }
            function extendResults(obj, noSanitize, retServer) {
                // Sanitize the object
                if (!noSanitize) {
                    if (obj._q) {
                        obj = obj._q;
                    }
                    else {
                        obj = { find: obj };
                    }
                }
                return qry.then((resolved) => {
                    const _qry = extendQuery(resolved, obj);
                    return replace(_qry, retServer);
                });
            }
            function replace(_qry, retServer) {
                // Sync down to the serverquery
                const serverProm = serverResults.replace(_qry);
                // Do the query but replace the existing query
                const localProm = Promise.resolve(_qry).then(normalizeQuery).then(normQry => {
                    // We allow a query to resolve to something falsy - in which case we just
                    // drop it
                    if (!normQry) {
                        return;
                    }
                    return qry.then((oldqry) => {
                        if (JSON.stringify(oldqry) !== JSON.stringify(normQry)) {
                            // query is different - continue
                            return query(normQry);
                        }
                    });
                });
                return retServer ? serverProm : localProm;
            }
            function next(num) {
                let promise;
                let extendSize;
                if (results.hasNext) {
                    lastBatchSize = extendSize = num || lastBatchSize;
                    const extendObj = {
                        limit: currentLimit + extendSize,
                    };
                    promise = extendResults(extendObj, true, !results.$hasNext);
                }
                else {
                    const deferred = new Deferred();
                    deferred.resolve();
                    promise = deferred.promise;
                }
                return promise;
            }
            function prev(num) {
                let promise;
                let extendSize;
                if (results.hasPrev) {
                    lastBatchSize = extendSize = num || lastBatchSize;
                    const extendObj = {
                        limit: currentLimit + extendSize,
                        skip: Math.max(currentSkip - extendSize, 0),
                    };
                    promise = extendResults(extendObj, true, !results.$hasPrev);
                }
                else {
                    const deferred = new Deferred();
                    deferred.resolve();
                    promise = deferred.promise;
                }
                return promise;
            }
            results.$refresh = refreshQuery;
            results.extend = extendResults;
            results.replace = replace;
            results.get = get;
            results.next = next;
            results.prev = prev;
            results.$promise = refreshQuery();
            results.$resolvedSubject = convertToResolvedSubject(results.$subject, results.$promise);
            results.chain = chain;
            results.$Model = Resource;
            results.$serverResults = serverResults;
            // Put currentSkip and currentLimit onto the results
            Object.defineProperty(results, 'currentLimit', {
                enumerable: true,
                get: () => {
                    return currentLimit;
                },
            });
            Object.defineProperty(results, 'currentSkip', {
                enumerable: true,
                get: () => {
                    return currentSkip;
                },
            });
            return results;
        };
        let refreshTimer;
        LocalQueryList.refresh = () => {
            // If we have a timer outstanding then just return - a refresh will happen soon.
            if (!refreshTimer) {
                refreshTimer = setTimeout(doRefresh, 100);
            }
            return refreshTimer;
            function doRefresh() {
                _localQueries.forEach(res => {
                    res.$refresh();
                });
                refreshTimer = null;
            }
        };
        /**
         * Server query list. The server makes sure we keep in sync
         * @param {[type]} qry      [description]
         * @param {[type]} limit    [description]
         * @param {[type]} Resource [description]
         */
        function ServerQueryList(qry, limit, Resource) {
            // Allow qry to be a promise
            qry = Promise.resolve(qry).then(normalizeQuery).then(_qry => {
                if (limit) {
                    _qry.limit = limit;
                }
                return _qry;
            });
            let emitPromise = null;
            const results = new ServerQuery();
            let currentLimit = 0;
            let currentSkip = 0;
            let lastBatchSize = 0;
            let _pagingOpts = {};
            results.loading = true;
            function query(data) {
                // We only want to do one emit at a time (otherwise we could get into a bad state)
                if (isFunction(data)) {
                    data = data();
                }
                // Store off the query - make sure its a promise
                qry = Promise.resolve(data);
                return qry.then((_data) => {
                    // If we have an existing emitPromise then wait for it to resolve before we run
                    let promise;
                    if (emitPromise) {
                        promise = emitPromise.then(() => {
                            return sock.query(_data);
                        });
                    }
                    else {
                        emitPromise = promise = sock.query(_data);
                    }
                    return promise;
                });
            }
            function newData(_qryId, response, force) {
                if (!response) {
                    return;
                }
                const deferred = new Deferred();
                const ids = response.ids;
                const pagingOpts = response.pagingOpts;
                // So far we've only got the ids of the qry result - go and fetch the actual objects.
                // This mechanism saves bandwidth by only getting the object data once then listening
                // for changes to it
                //
                // If we are forcing we want to get both the old and new ids to check for any changes
                // deletions etc..
                let getIds = ids;
                if (force) {
                    const oldIds = map(results, '_id');
                    getIds = union(ids, oldIds);
                }
                Resource.get(getIds, force).$promise.then((ress) => {
                    const ressmap = {};
                    ress.forEach(res => {
                        ressmap[res._id] = res;
                    });
                    // We don't allow repeated ids so just iterate over the results
                    const tmpResults = [];
                    ids.forEach(id => {
                        tmpResults.push(ressmap[id]);
                    });
                    // Do we need to do a transform?
                    let rprom;
                    if (isFunction(results.transform)) {
                        rprom = Promise.resolve(results.transform(tmpResults));
                    }
                    else {
                        rprom = Promise.resolve(tmpResults);
                    }
                    return rprom.then(transformed => {
                        results.length = 0;
                        transformed.forEach((res) => {
                            results.push(res);
                        });
                        // Since we now have data in our array store off limit data
                        currentLimit = tmpResults.length;
                        results.$skip = currentSkip = (qry && qry.skip) ? qry.skip : 0;
                        lastBatchSize = lastBatchSize || tmpResults.length;
                        _pagingOpts = pagingOpts;
                        results.hasNext = (pagingOpts.next != null);
                        results.hasPrev = (pagingOpts.prev != null);
                        // Data has come back
                        results.loading = false;
                        results.$emitter.emit('update', results);
                        deferred.resolve(results);
                    });
                });
                return deferred.promise;
            }
            function refreshQuery(force) {
                const req = query(qry);
                const promise = req.then((res) => {
                    // If we get no response (the app could be offline) then just resolve with
                    // the existing results
                    if (isUndefined(res)) {
                        results.loading = false;
                        return results;
                    }
                    const _qryId = res.qryId;
                    const response = res.data;
                    return newData(_qryId, response, force);
                });
                return promise;
            }
            function replace(_qry) {
                // Do the query but replace the existing query
                return Promise.resolve(_qry).then(normalizeQuery).then(normQry => {
                    // We allow a query to resolve to something falsy - in which case we just
                    // drop it
                    if (!normQry) {
                        return;
                    }
                    return qry.then((oldqry) => {
                        if (JSON.stringify(oldqry) !== JSON.stringify(normQry)) {
                            // query is different - continue
                            const req = query(normQry);
                            const promise = req.then((res) => {
                                // If we get no response (the app could be offline) then just resolve with
                                // the existing results
                                if (isUndefined(res)) {
                                    results.loading = false;
                                    return results;
                                }
                                // We do have a response. Continue
                                const _qryId = res.qryId;
                                const data = res.data;
                                return newData(_qryId, data);
                            });
                            return promise;
                        }
                    });
                });
            }
            function extendResults(obj, noSanitize) {
                // Sanitize the object
                if (!noSanitize) {
                    if (obj._q) {
                        obj = obj._q;
                    }
                    else {
                        obj = { find: obj };
                    }
                }
                return qry.then((resolved) => {
                    const _qry = extendQuery(resolved, obj);
                    return replace(_qry);
                });
            }
            function next(num) {
                let promise;
                let extendSize;
                if (_pagingOpts.next) {
                    lastBatchSize = extendSize = num || lastBatchSize;
                    const extendObj = {
                        limit: currentLimit + extendSize,
                    };
                    promise = extendResults(extendObj, true);
                }
                else {
                    const deferred = new Deferred();
                    deferred.resolve();
                    promise = deferred.promise;
                }
                return promise;
            }
            function prev(num) {
                let promise;
                let extendSize;
                if (_pagingOpts.prev) {
                    lastBatchSize = extendSize = num || lastBatchSize;
                    const extendObj = {
                        limit: currentLimit + extendSize,
                        skip: Math.max(currentSkip - extendSize, 0),
                    };
                    promise = extendResults(extendObj, true);
                }
                else {
                    const deferred = new Deferred();
                    deferred.resolve();
                    promise = deferred.promise;
                }
                return promise;
            }
            function get() {
                return Resource.get.apply(Resource, arguments);
            }
            function chain(Model, qryFn) {
                return Chain(results, Model, qryFn);
            }
            results.$newData = newData;
            results.$refresh = refreshQuery;
            results.extend = extendResults;
            results.replace = replace;
            results.get = get;
            results.next = next;
            results.prev = prev;
            results.$promise = refreshQuery().then(res => {
                results.$resolved = true;
                return res;
            });
            results.$resolvedSubject = convertToResolvedSubject(results.$subject, results.$promise);
            results.$resolved = false;
            results.chain = chain;
            results.$Model = Resource;
            results.$skip = 0;
            // Put currentSkip and currentLimit onto the results
            Object.defineProperty(results, 'currentLimit', {
                enumerable: true,
                get: () => {
                    return currentLimit;
                },
            });
            Object.defineProperty(results, 'currentSkip', {
                enumerable: true,
                get: () => {
                    return currentSkip;
                },
            });
            return results;
        }
        return db ? LocalQueryList : ServerQueryList;
    }
    return QueryFactory;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicXVlcnkuanMiLCJzb3VyY2VSb290IjoiL0BvcnRvby9hbmd1bGFyLXJlc291cmNlLyIsInNvdXJjZXMiOlsicXVlcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBSUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUNuQyxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sUUFBUSxDQUFDO0FBQy9CLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFDN0IsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUMvQixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sUUFBUSxDQUFDO0FBQ3BDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFDakMsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUVyQyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDbEQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUVoQyxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsUUFBUSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBRTdELE1BQU0sWUFBZ0IsU0FBUSxZQUFlO0NBd0I1QztBQUVELE1BQU0saUJBQXFCLFNBQVEsS0FBUTtDQUFHO0FBRTlDLE1BQU0sa0JBQXNCLFNBQVEsS0FBUTtDQUUzQztBQUVELHdCQUF3QixHQUFRO0lBQzlCLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztRQUNULE1BQU0sQ0FBQztJQUNULENBQUM7SUFFRCxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNYLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO0lBQ2hCLENBQUM7SUFBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDcEIsTUFBTSxDQUFD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QUMsS0FBSyxHQUFHLEtBQUssQ0FBQztZQUN0QixPQUFPLENBQUMsTUFBTSxHQUFHLFFBQVEsQ0FBQztZQUMxQixPQUFPLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQztZQUVsQixvREFBb0Q7WUFDcEQsTUFBTSxDQUFDLGNBQWMsQ0FBQyxPQUFPLEVBQUUsY0FBYyxFQUFFO2dCQUM3QyxVQUFVLEVBQUUsSUFBSTtnQkFDaEIsR0FBRyxFQUFFO29CQUNILE1BQU0sQ0FBQyxZQUFZLENBQUM7Z0JBQ3RCLENBQUM7YUFDRixDQUFDLENBQUM7WUFDSCxNQUFNLENBQUMsY0FBYyxDQUFDLE9BQU8sRUFBRSxhQUFhLEVBQUU7Z0JBQzVDLFVBQVUsRUFBRSxJQUFJO2dCQUNoQixHQUFHLEVBQUU7b0JBQ0gsTUFBTSxDQUFDLFdBQVcsQ0FBQztnQkFDckIsQ0FBQzthQUNGLENBQUMsQ0FBQztZQUVILE1BQU0sQ0FBQyxPQUFPLENBQUM7UUFDakIsQ0FBQztRQUVELE1BQU0sQ0FBQyxFQUFFLEdBQUcsY0FBYyxHQUFHLGVBQWUsQ0FBQztJQUMvQyxDQUFDO0lBRUQsTUFBTSxDQUFDLFlBQVksQ0FBQztBQUN0QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gdHNsaW50OmRpc2FibGUgbWF4LWNsYXNzZXMtcGVyLWZpbGUgaW50ZXJmYWNlLW5hbWVcbmltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCB9IGZyb20gJ3J4anMvQmVoYXZpb3JTdWJqZWN0JztcbmltcG9ydCB7IFN1YmplY3QgfSBmcm9tICdyeGpzL1N1YmplY3QnO1xuXG5pbXBvcnQgeyBjbG9uZURlZXAgfSBmcm9tICdsb2Rhc2gnO1xuaW1wb3J0IHsgY2xvbmUgfSBmcm9tICdsb2Rhc2gnO1xuaW1wb3J0IHsgbWFwIH0gZnJvbSAnbG9kYXNoJztcbmltcG9ydCB7IHVuaW9uIH0gZnJvbSAnbG9kYXNoJztcbmltcG9ydCB7IGlzRnVuY3Rpb24gfSBmcm9tICdsb2Rhc2gnO1xuaW1wb3J0IHsgaXNFbXB0eSB9IGZyb20gJ2xvZGFzaCc7XG5pbXBvcnQgeyBpc1VuZGVmaW5lZCB9IGZyb20gJ2xvZGFzaCc7XG5cbmltcG9ydCB7IEFycmF5RW1pdHRlciB9IGZyb20gJy4vYXJyYXktZW1pdHRlci5qcyc7XG5pbXBvcnQgeyBDaGFpbiB9IGZyb20gJy4vY2hhaW4nO1xuaW1wb3J0IHsgRGF0YWJhc2UsIE1vZGVsLCBRdWVyeSBhcyBJUXVlcnksIFF1ZXJ5RnVuY3Rpb24sIFF1ZXJ5TGlzdCwgUmVzb3VyY2UsIFNvY2tldEZhY3RvcnkgfSBmcm9tICcuL3R5cGVzJztcbmltcG9ydCB7IGNvbnZlcnRUb1Jlc29sdmVkU3ViamVjdCwgRGVmZXJyZWQgfSBmcm9tICcuL3V0aWxzJztcblxuZXhwb3J0IGNsYXNzIFF1ZXJ5PFQ+IGV4dGVuZHMgQXJyYXlFbWl0dGVyPFQ+IGltcGxlbWVudHMgSVF1ZXJ5PFQ+IHtcbiAgcHVibGljICRyZWZyZXNoOiAoZm9yY2U/OiBib29sZWFuKSA9PiBQcm9taXNlPFF1ZXJ5PFQ+PjtcbiAgcHVibGljICRwcm9taXNlOiBQcm9taXNlPFF1ZXJ5PFQ+PjtcbiAgcHVibGljICRzdWJqZWN0OiBCZWhhdmlvclN1YmplY3Q8UXVlcnk8VD4+O1xuICBwdWJsaWMgJHJlc29sdmVkU3ViamVjdDogU3ViamVjdDxRdWVyeTxUPj47XG4gIHB1YmxpYyAkcmVzb2x2ZWQ6IGJvb2xlYW47XG4gIHB1YmxpYyAkTW9kZWw6IGFueTtcbiAgcHVibGljICRzZXJ2ZXJSZXN1bHRzPzogUXVlcnk8VD47XG4gIHB1YmxpYyAkc2tpcD86IG51bWJlcjtcbiAgcHVibGljICRoYXNOZXh0OiBib29sZWFuO1xuICBwdWJsaWMgJGhhc1ByZXY6IGJvb2xlYW47XG5cbiAgcHVibGljIGV4dGVuZDogKG9iajogYW55LCBub1Nhbml0aXplPzogYm9vbGVhbiwgcmV0U2VydmVyPzogYm9vbGVhbikgPT4gUHJvbWlzZTxRdWVyeTxUPj47XG4gIHB1YmxpYyByZXBsYWNlOiAocXJ5OiBhbnksIHJldFNlcnZlcj86IGJvb2xlYW4pID0+IFByb21pc2U8UXVlcnk8VD4+O1xuICBwdWJsaWMgZ2V0OiAoLi4uYXJnczogYW55W10pID0+IGFueTtcbiAgcHVibGljIG5leHQ6IChudW0/OiBudW1iZXIpID0+IFByb21pc2U8UXVlcnk8VD4+O1xuICBwdWJsaWMgcHJldjogKG51bT86IG51bWJlcikgPT4gUHJvbWlzZTxRdWVyeTxUPj47XG4gIHB1YmxpYyBjaGFpbjogKE1vZGVsOiBSZXNvdXJjZTxNb2RlbD4gfCBRdWVyeUZ1bmN0aW9uLCBxcnlGbj86IFF1ZXJ5RnVuY3Rpb24pID0+IElRdWVyeTxNb2RlbD47XG4gIHB1YmxpYyB0cmFuc2Zvcm06ICh0bXBSZXN1bHRzOiBhbnlbXSkgPT4gUHJvbWlzZTxhbnlbXT4gfCBhbnlbXTtcbiAgcHVibGljIGN1cnJlbnRMaW1pdDogbnVtYmVyO1xuICBwdWJsaWMgY3VycmVudFNraXA6IG51bWJlcjtcbiAgcHVibGljIGxvYWRpbmc6IGJvb2xlYW47XG4gIHB1YmxpYyBoYXNOZXh0OiBib29sZWFuO1xuICBwdWJsaWMgaGFzUHJldjogYm9vbGVhbjtcbn1cblxuZXhwb3J0IGNsYXNzIExvY2FsUXVlcnk8VD4gZXh0ZW5kcyBRdWVyeTxUPiB7fVxuXG5leHBvcnQgY2xhc3MgU2VydmVyUXVlcnk8VD4gZXh0ZW5kcyBRdWVyeTxUPiB7XG4gIHB1YmxpYyAkbmV3RGF0YT86IChxcnlJZDogc3RyaW5nLCByZXNwb25zZTogYW55LCBmb3JjZT86IGJvb2xlYW4pID0+IHZvaWQ7XG59XG5cbmZ1bmN0aW9uIG5vcm1hbGl6ZVF1ZXJ5KHFyeTogYW55KSB7XG4gIGlmICghcXJ5KSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgaWYgKHFyeS5fcSkge1xuICAgIHJldHVybiBxcnkuX3E7XG4gIH0gZWxzZSBpZiAocXJ5LmZpbmQpIHtcbiAgICByZXR1cm4gcXJ5O1xuICB9IGVsc2Uge1xuICAgIHJldHVybiB7ZmluZDogcXJ5fTtcbiAgfVxufVxuXG5mdW5jdGlvbiBleHRlbmRRdWVyeShxcnkxOiBhbnksIHFyeTI6IGFueSkge1xuICAvLyBDYWxjIHRoZSBuZXcgcXVlcnkgdGhhdCB3ZSB3YW50XG4gIGNvbnN0IF9xcnkgPSBjbG9uZURlZXAocXJ5MSk7XG4gIFsnbGltaXQnLCAnc2tpcCcsICdzb3J0J10uZm9yRWFjaChwcm9wID0+IHtcbiAgICBpZiAocXJ5Mltwcm9wXSkge1xuICAgICAgX3FyeVtwcm9wXSA9IHFyeTJbcHJvcF07XG4gICAgfVxuICB9KTtcblxuICBpZiAoIWlzRW1wdHkocXJ5Mi5maW5kKSkge1xuICAgIC8vIFdhbnQgdG8gb3IgdG9nZXRoZXIgLSBidXQgaXMgdGhlIHRvcGxldmVsIGFscmVhZHkgYW4gb3I/IChhbmQgb25seSBhbiBvcilcbiAgICBsZXQgZXhpc3RpbmdPciA9IGZhbHNlO1xuICAgIGlmIChfcXJ5LmZpbmQuJG9yKSB7XG4gICAgICBsZXQgdmFsaWQgPSB0cnVlO1xuICAgICAgZm9yIChjb25zdCBrZXkgaW4gX3FyeS5maW5kKSB7XG4gICAgICAgIGlmIChrZXkgIT09ICckb3InKSB7XG4gICAgICAgICAgdmFsaWQgPSBmYWxzZTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZXhpc3RpbmdPciA9IHZhbGlkO1xuICAgIH1cblxuICAgIGlmIChleGlzdGluZ09yKSB7XG4gICAgICBfcXJ5LmZpbmQgPSBjbG9uZShfcXJ5LmZpbmQpO1xuICAgIH0gZWxzZSB7XG4gICAgICBfcXJ5LmZpbmQgPSB7JG9yOiBbX3FyeS5maW5kXX07XG4gICAgfVxuXG4gICAgX3FyeS5maW5kLiRvci5wdXNoKHFyeTIuZmluZCk7XG4gIH1cblxuICByZXR1cm4gX3FyeTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVF1ZXJ5RmFjdG9yeShzb2NrZXQ6IFNvY2tldEZhY3RvcnkpIHtcblxuICBmdW5jdGlvbiBRdWVyeUZhY3RvcnkodXJsOiBzdHJpbmcsIHJvb3RLZXk6IHN0cmluZywgcm9vdEtleVBsdXJhbDogc3RyaW5nLCBkYj86IERhdGFiYXNlKTogUXVlcnlMaXN0IHtcbiAgICBjb25zdCBzb2NrID0gc29ja2V0KHVybCwgcm9vdEtleSwgcm9vdEtleVBsdXJhbCk7XG5cbiAgICAvLyBPYmplY3QgaG9sZGluZyBxdWVyaWVzIChrZXlzIGFyZSB0aGUgcXVlcnkgaWRzLCB2YWx1ZXMgYXJlIGFycmF5cyBvZiBhcnJheXMpXG4gICAgY29uc3QgX2xvY2FsUXVlcmllczogQXJyYXk8TG9jYWxRdWVyeTxNb2RlbD4+ID0gW107XG5cbiAgICAvKipcbiAgICAgKiBMb2NhbCBRdWVyeSBMaXN0XG4gICAgICogQHBhcmFtIHtbdHlwZV19IHFyeSAgICAgIFtkZXNjcmlwdGlvbl1cbiAgICAgKiBAcGFyYW0ge1t0eXBlXX0gbGltaXQgICAgW2Rlc2NyaXB0aW9uXVxuICAgICAqIEBwYXJhbSB7W3R5cGVdfSBSZXNvdXJjZSBbZGVzY3JpcHRpb25dXG4gICAgICogQHBhcmFtIHtbdHlwZV19IHRvUmVzICAgIFtkZXNjcmlwdGlvbl1cbiAgICAgKlxuICAgICAqIFN0cmF0ZWd5IC0gd2UgcnVuIHRoZSBxdWVyeSBvbiB0aGUgc2VydmVyLiBIb3dldmVyIGlmIHdlIGNhbiBkZWFsIHdpdGggaXQgbG9jYWxseSAodmlhXG4gICAgICogbmVkYikgdGhlbiBkbyBzby4gSWYgdGhlIHF1ZXJ5IGF0IGFueSB0aW1lIGJlY29tZXMgbW9yZSBjb21wbGljYXRlZCB3ZSBqdXN0IGZhbGwgdGhyb3VnaFxuICAgICAqIHRvIHRoZSBzZXJ2ZXIgdmVyc2lvblxuICAgICAqL1xuICAgIGNvbnN0IExvY2FsUXVlcnlMaXN0OiBRdWVyeUxpc3QgPSAocXJ5OiBhbnksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW1pdDogbnVtYmVyLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUmVzb3VyY2U6IFJlc291cmNlPE1vZGVsPixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvUmVzOiAoaWQ6IHN0cmluZykgPT4gTW9kZWwpOiBMb2NhbFF1ZXJ5PE1vZGVsPiA9PiB7XG4gICAgICAvLyBBbGxvdyBxcnkgdG8gYmUgYSBwcm9taXNlLCBhbmQgd2UgcmV0dXJuIHRoZSByYXcgKG5vbiBfcSBiaXQpXG4gICAgICBxcnkgPSBQcm9taXNlLnJlc29sdmUocXJ5KS50aGVuKG5vcm1hbGl6ZVF1ZXJ5KS50aGVuKF9xcnkgPT4ge1xuICAgICAgICBpZiAobGltaXQpIHtcbiAgICAgICAgICBfcXJ5LmxpbWl0ID0gbGltaXQ7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIF9xcnk7XG4gICAgICB9KTtcblxuICAgICAgLy8gR2VuZXJhdGUgdGhlIFNlcnZlclF1ZXJ5LiBXZSBkbyB0aGlzIHNvIHdlIGhhdmUgc29tZXRoaW5nIHRvIGZhbGwgYmFjayBvblxuICAgICAgLy8gYW5kIGFsc28gc28gdGhhdCB3ZSB3aWxsIChvciBzaG91bGQhKSBnZXQgbm90aWZpZWQgb2YgY2hhbmdlcyBmcm9tIHRoZSBzZXJ2ZXIgaWZcbiAgICAgIC8vIHNvbWVvbmUgZWxzZSBjcmVhdGVzIG9yIHJlbW92ZXMgc29tZXRoaW5nIHRoYXQgc2hvdWxkIGdvIGluIHRoZXNlIHJlc3VsdHNcbiAgICAgIGNvbnN0IHNlcnZlclJlc3VsdHMgPSBTZXJ2ZXJRdWVyeUxpc3QocXJ5LCBsaW1pdCwgUmVzb3VyY2UpO1xuXG4gICAgICBjb25zdCByZXN1bHRzID0gbmV3IExvY2FsUXVlcnk8TW9kZWw+KCk7XG4gICAgICBfbG9jYWxRdWVyaWVzLnB1c2gocmVzdWx0cyk7XG4gICAgICBsZXQgY3VycmVudExpbWl0ID0gMDtcbiAgICAgIGxldCBjdXJyZW50U2tpcCA9IDA7XG4gICAgICBsZXQgbGFzdEJhdGNoU2l6ZSA9IDA7XG4gICAgICBsZXQgZmFsbGJhY2sgPSBmYWxzZTtcblxuICAgICAgcmVzdWx0cy5sb2FkaW5nID0gdHJ1ZTtcblxuICAgICAgLy8gV2hlbiB0aGUgc2VydmVyIHJlc3VsdHMgYXJlIHVwZGF0ZWQgd2Ugd2FudCB0byBjaGVjayBwYWdpbmcgb3B0aW9ucyBhbmQgYXBwbHlcbiAgICAgIC8vIHRoZW0gdG8gb3VyIHJlc3VsdHNcbiAgICAgIHNlcnZlclJlc3VsdHMuJGVtaXR0ZXIub24oJ3VwZGF0ZScsIHN5bmNGcm9tU2VydmVyKTtcblxuICAgICAgZnVuY3Rpb24gc3luY0Zyb21TZXJ2ZXIoKSB7XG4gICAgICAgIHJlc3VsdHMuaGFzTmV4dCA9IHNlcnZlclJlc3VsdHMuaGFzTmV4dDtcbiAgICAgICAgcmVzdWx0cy5oYXNQcmV2ID0gc2VydmVyUmVzdWx0cy5oYXNQcmV2O1xuXG4gICAgICAgIC8vIElmIHdlIGFyZSBmYWxsaW5nIGJhY2sgdGhlbiBzZXQgdXAgYW5kIGNvcHkgb3VyIGxldGlvdXMgcHJvcGVydGllcyBhY3Jvc3NcbiAgICAgICAgaWYgKGZhbGxiYWNrKSB7XG4gICAgICAgICAgcmVzdWx0cy4kaGFzTmV4dCA9IGZhbHNlO1xuICAgICAgICAgIHJlc3VsdHMuJGhhc1ByZXYgPSBmYWxzZTtcbiAgICAgICAgICByZXN1bHRzLmxlbmd0aCA9IDA7XG4gICAgICAgICAgc2VydmVyUmVzdWx0cy5mb3JFYWNoKHJlcyA9PiB7XG4gICAgICAgICAgICByZXN1bHRzLnB1c2gocmVzKTtcbiAgICAgICAgICB9KTtcblxuICAgICAgICAgIGN1cnJlbnRMaW1pdCA9IHJlc3VsdHMubGVuZ3RoO1xuICAgICAgICAgIGN1cnJlbnRTa2lwID0gcmVzdWx0cy4kc2tpcDtcbiAgICAgICAgICBsYXN0QmF0Y2hTaXplID0gbGFzdEJhdGNoU2l6ZSB8fCByZXN1bHRzLmxlbmd0aDtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICAgIHJldHVybiBSZXNvdXJjZS5nZXQuYXBwbHkoUmVzb3VyY2UsIGFyZ3VtZW50cyk7XG4gICAgICB9XG5cbiAgICAgIGZ1bmN0aW9uIGNoYWluKE1vZGVsOiBSZXNvdXJjZTxNb2RlbD4gfCBRdWVyeUZ1bmN0aW9uLCBxcnlGbj86IFF1ZXJ5RnVuY3Rpb24pIHtcbiAgICAgICAgcmV0dXJuIENoYWluKHJlc3VsdHMsIE1vZGVsLCBxcnlGbik7XG4gICAgICB9XG5cbiAgICAgIGZ1bmN0aW9uIHF1ZXJ5KF9xcnk6IGFueSkge1xuXG4gICAgICAgIC8vIFN0b3JlIG9mZiB0aGUgcXJ5IG1ha2luZyBzdXJlIGl0cyBhIHByb21pc2VcbiAgICAgICAgcXJ5ID0gUHJvbWlzZS5yZXNvbHZlKF9xcnkpO1xuXG4gICAgICAgIGZhbGxiYWNrID0gIWRiLnFyeUlzU2ltcGxlKF9xcnkpO1xuXG4gICAgICAgIC8vIElmIHdlIGFyZSBmYWxsaW5nYmFjayB0aGVuIGp1c3QgcmVzb2x2ZSB3aXRoIG91ciByZXN1bHRzLiBUaGUgc2VydmVyIHNob3VsZFxuICAgICAgICAvLyBkbyB0aGUgcmVzdC5cbiAgICAgICAgaWYgKGZhbGxiYWNrKSB7XG4gICAgICAgICAgLy8gV2Ugd2FudCB0byByZXR1cm4gdGhlIHNlcnZlcidzIHByb21pc2UgaGVyZSBzbyB0aGF0IHBlb3BsZSB3aWxsIGJlIG5vdGlmaWVkXG4gICAgICAgICAgLy8gd2hlbiB0aGUgcmVzdWx0cyBhcmUgYWN0dWFsbHkgbG9hZGVkXG4gICAgICAgICAgbGV0IHByb207XG4gICAgICAgICAgaWYgKCFzZXJ2ZXJSZXN1bHRzLiRyZXNvbHZlZCkge1xuICAgICAgICAgICAgcHJvbSA9IHNlcnZlclJlc3VsdHMuJHByb21pc2UudGhlbigoKSA9PiB7XG4gICAgICAgICAgICAgIHJldHVybiByZXN1bHRzO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHByb20gPSBQcm9taXNlLnJlc29sdmUocmVzdWx0cyk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgcmV0dXJuIHByb207XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gZGIucXVlcnkoX3FyeSkudGhlbigoaWRzOiBzdHJpbmdbXSkgPT4ge1xuXG4gICAgICAgICAgLy8gV2UgY2FuIHNldCB0aGUgaGFzTmV4dCBhbmQgaGFzUHJldiB2YWx1ZXMgdG8gdHJ1ZSBoZXJlIGlmIHdlIGtub3cgdGhlcmVcbiAgICAgICAgICAvLyBhcmUgc29tZS4gSG93ZXZlciBvbmx5IHRoZSBzZXJ2ZXIgaGFzIHRoZSBkZWZpbml0aXZlIGFiaWxpdHkgdG8gc2F5XG4gICAgICAgICAgLy8gdGhlcmUgYXJlbnQgYW55XG4gICAgICAgICAgcmVzdWx0cy4kaGFzTmV4dCA9IGZhbHNlO1xuICAgICAgICAgIHJlc3VsdHMuJGhhc1ByZXYgPSBmYWxzZTtcbiAgICAgICAgICBpZiAobGltaXQgJiYgaWRzLmxlbmd0aCA+IGxpbWl0KSB7XG4gICAgICAgICAgICAvLyBXZSBoYXZlIG1vcmUgcmVzdWx0cyB0byBmZXRjaFxuICAgICAgICAgICAgcmVzdWx0cy5oYXNOZXh0ID0gdHJ1ZTtcbiAgICAgICAgICAgIHJlc3VsdHMuJGhhc05leHQgPSB0cnVlO1xuXG4gICAgICAgICAgICAvLyBUcmltIHRoZSByZXN1bHRzIGRvd24gdG8gc2l6ZVxuICAgICAgICAgICAgaWRzLmxlbmd0aCAtPSAxO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGNvbnN0IHNraXAgPSBfcXJ5LnNraXA7XG5cbiAgICAgICAgICBpZiAoc2tpcCkge1xuICAgICAgICAgICAgcmVzdWx0cy5oYXNQcmV2ID0gdHJ1ZTtcbiAgICAgICAgICAgIHJlc3VsdHMuJGhhc1ByZXYgPSB0cnVlO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIENhbGN1bGF0ZSBwYWdpbmcgb3B0aW9uc1xuICAgICAgICAgIGN1cnJlbnRMaW1pdCA9IGlkcy5sZW5ndGg7XG4gICAgICAgICAgY3VycmVudFNraXAgPSBza2lwIHx8IDA7XG4gICAgICAgICAgbGFzdEJhdGNoU2l6ZSA9IGxhc3RCYXRjaFNpemUgfHwgaWRzLmxlbmd0aDtcblxuICAgICAgICAgIC8vIEdvIHRvIHJlc291cmNlIHR5cGVzXG4gICAgICAgICAgY29uc3QgdG1wUmVzdWx0cyA9IGlkcy5tYXAodG9SZXMpO1xuXG4gICAgICAgICAgLy8gRG8gd2UgbmVlZCB0byBkbyBhIHRyYW5zZm9ybT9cbiAgICAgICAgICBsZXQgcnByb207XG4gICAgICAgICAgaWYgKGlzRnVuY3Rpb24ocmVzdWx0cy50cmFuc2Zvcm0pKSB7XG4gICAgICAgICAgICBycHJvbSA9IFByb21pc2UucmVzb2x2ZShyZXN1bHRzLnRyYW5zZm9ybSh0bXBSZXN1bHRzKSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJwcm9tID0gUHJvbWlzZS5yZXNvbHZlKHRtcFJlc3VsdHMpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHJldHVybiBycHJvbS50aGVuKHRyYW5zZm9ybWVkID0+IHtcbiAgICAgICAgICAgIC8vIFB1dCB0aGUgcmVzb3VyY2VzIGludG8gdGhlIGxpc3RcbiAgICAgICAgICAgIGNvbnN0IG9sZCA9IHJlc3VsdHMuc2xpY2UoKTtcbiAgICAgICAgICAgIHJlc3VsdHMubGVuZ3RoID0gMDtcbiAgICAgICAgICAgIHRyYW5zZm9ybWVkLmZvckVhY2goKHJlczogTW9kZWwpID0+IHtcbiAgICAgICAgICAgICAgcmVzdWx0cy5wdXNoKHJlcyk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgcmVzdWx0cy4kZW1pdHRlci5lbWl0KCd1cGRhdGUnLCByZXN1bHRzLCBvbGQpO1xuXG4gICAgICAgICAgICByZXR1cm4gcmVzdWx0cztcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG5cbiAgICAgIH1cblxuICAgICAgZnVuY3Rpb24gcmVmcmVzaFF1ZXJ5KGZvcmNlU2VydmVyPzogYm9vbGVhbik6IFByb21pc2U8TG9jYWxRdWVyeTxNb2RlbD4+IHtcbiAgICAgICAgLy8gUGVyZm9ybSBvdXIgcXVlcnlcbiAgICAgICAgbGV0IHByb20gPSBxcnkudGhlbihxdWVyeSkudGhlbigocmVzOiBhbnlbXSkgPT4ge1xuICAgICAgICAgIC8vIElmIHdlIGRvbid0IGhhdmUgYW55IHJlc3VsdHMgdGhlbiBtYXliZSB3YWl0IGZvciB0aGUgc2VydmVyIHRvIHJldHVyblxuICAgICAgICAgIGlmICgocmVzLmxlbmd0aCA9PT0gMCAmJiAhc2VydmVyUmVzdWx0cy4kcmVzb2x2ZWQpKSB7XG4gICAgICAgICAgICByZXR1cm4gc2VydmVyUmVzdWx0cy4kcHJvbWlzZS50aGVuKCgpID0+IHtcbiAgICAgICAgICAgICAgcmV0dXJuIHJlZnJlc2hRdWVyeSgpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiByZXM7XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICBpZiAoZm9yY2VTZXJ2ZXIpIHtcbiAgICAgICAgICBwcm9tID0gc2VydmVyUmVzdWx0cy4kcmVmcmVzaCh0cnVlKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBwcm9tLnRoZW4oKHJlczogYW55KSA9PiB7XG4gICAgICAgICAgcmVzdWx0cy5sb2FkaW5nID0gZmFsc2U7XG4gICAgICAgICAgcmV0dXJuIHJlcztcbiAgICAgICAgfSk7XG4gICAgICB9XG5cbiAgICAgIGZ1bmN0aW9uIGV4dGVuZFJlc3VsdHMob2JqOiBhbnksIG5vU2FuaXRpemU/OiBib29sZWFuLCByZXRTZXJ2ZXI/OiBib29sZWFuKSB7XG5cbiAgICAgICAgLy8gU2FuaXRpemUgdGhlIG9iamVjdFxuICAgICAgICBpZiAoIW5vU2FuaXRpemUpIHtcbiAgICAgICAgICBpZiAob2JqLl9xKSB7XG4gICAgICAgICAgICBvYmogPSBvYmouX3E7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIG9iaiA9IHtmaW5kOiBvYmp9O1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBxcnkudGhlbigocmVzb2x2ZWQ6IGFueSkgPT4ge1xuICAgICAgICAgIGNvbnN0IF9xcnkgPSBleHRlbmRRdWVyeShyZXNvbHZlZCwgb2JqKTtcbiAgICAgICAgICByZXR1cm4gcmVwbGFjZShfcXJ5LCByZXRTZXJ2ZXIpO1xuICAgICAgICB9KTtcbiAgICAgIH1cblxuICAgICAgZnVuY3Rpb24gcmVwbGFjZShfcXJ5OiBhbnksIHJldFNlcnZlcj86IGJvb2xlYW4pIHtcbiAgICAgICAgLy8gU3luYyBkb3duIHRvIHRoZSBzZXJ2ZXJxdWVyeVxuICAgICAgICBjb25zdCBzZXJ2ZXJQcm9tID0gc2VydmVyUmVzdWx0cy5yZXBsYWNlKF9xcnkpO1xuXG4gICAgICAgIC8vIERvIHRoZSBxdWVyeSBidXQgcmVwbGFjZSB0aGUgZXhpc3RpbmcgcXVlcnlcbiAgICAgICAgY29uc3QgbG9jYWxQcm9tID0gUHJvbWlzZS5yZXNvbHZlKF9xcnkpLnRoZW4obm9ybWFsaXplUXVlcnkpLnRoZW4obm9ybVFyeSA9PiB7XG5cbiAgICAgICAgICAvLyBXZSBhbGxvdyBhIHF1ZXJ5IHRvIHJlc29sdmUgdG8gc29tZXRoaW5nIGZhbHN5IC0gaW4gd2hpY2ggY2FzZSB3ZSBqdXN0XG4gICAgICAgICAgLy8gZHJvcCBpdFxuICAgICAgICAgIGlmICghbm9ybVFyeSkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHJldHVybiBxcnkudGhlbigob2xkcXJ5OiBhbnkpID0+IHtcbiAgICAgICAgICAgIGlmIChKU09OLnN0cmluZ2lmeShvbGRxcnkpICE9PSBKU09OLnN0cmluZ2lmeShub3JtUXJ5KSkge1xuICAgICAgICAgICAgICAvLyBxdWVyeSBpcyBkaWZmZXJlbnQgLSBjb250aW51ZVxuXG4gICAgICAgICAgICAgIHJldHVybiBxdWVyeShub3JtUXJ5KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgcmV0dXJuIHJldFNlcnZlciA/IHNlcnZlclByb20gOiBsb2NhbFByb207XG4gICAgICB9XG5cbiAgICAgIGZ1bmN0aW9uIG5leHQobnVtPzogbnVtYmVyKSB7XG4gICAgICAgIGxldCBwcm9taXNlO1xuICAgICAgICBsZXQgZXh0ZW5kU2l6ZTtcbiAgICAgICAgaWYgKHJlc3VsdHMuaGFzTmV4dCkge1xuICAgICAgICAgIGxhc3RCYXRjaFNpemUgPSBleHRlbmRTaXplID0gbnVtIHx8IGxhc3RCYXRjaFNpemU7XG4gICAgICAgICAgY29uc3QgZXh0ZW5kT2JqID0ge1xuICAgICAgICAgICAgbGltaXQ6IGN1cnJlbnRMaW1pdCArIGV4dGVuZFNpemUsXG4gICAgICAgICAgfTtcbiAgICAgICAgICBwcm9taXNlID0gZXh0ZW5kUmVzdWx0cyhleHRlbmRPYmosIHRydWUsICFyZXN1bHRzLiRoYXNOZXh0KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjb25zdCBkZWZlcnJlZCA9IG5ldyBEZWZlcnJlZCgpO1xuICAgICAgICAgIGRlZmVycmVkLnJlc29sdmUoKTtcbiAgICAgICAgICBwcm9taXNlID0gZGVmZXJyZWQucHJvbWlzZTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBwcm9taXNlO1xuICAgICAgfVxuXG4gICAgICBmdW5jdGlvbiBwcmV2KG51bT86IG51bWJlcikge1xuICAgICAgICBsZXQgcHJvbWlzZTtcbiAgICAgICAgbGV0IGV4dGVuZFNpemU7XG4gICAgICAgIGlmIChyZXN1bHRzLmhhc1ByZXYpIHtcbiAgICAgICAgICBsYXN0QmF0Y2hTaXplID0gZXh0ZW5kU2l6ZSA9IG51bSB8fCBsYXN0QmF0Y2hTaXplO1xuICAgICAgICAgIGNvbnN0IGV4dGVuZE9iaiA9IHtcbiAgICAgICAgICAgIGxpbWl0OiBjdXJyZW50TGltaXQgKyBleHRlbmRTaXplLFxuICAgICAgICAgICAgc2tpcDogTWF0aC5tYXgoY3VycmVudFNraXAgLSBleHRlbmRTaXplLCAwKSxcbiAgICAgICAgICB9O1xuICAgICAgICAgIHByb21pc2UgPSBleHRlbmRSZXN1bHRzKGV4dGVuZE9iaiwgdHJ1ZSwgIXJlc3VsdHMuJGhhc1ByZXYpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGNvbnN0IGRlZmVycmVkID0gbmV3IERlZmVycmVkKCk7XG4gICAgICAgICAgZGVmZXJyZWQucmVzb2x2ZSgpO1xuICAgICAgICAgIHByb21pc2UgPSBkZWZlcnJlZC5wcm9taXNlO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHByb21pc2U7XG4gICAgICB9XG5cbiAgICAgIHJlc3VsdHMuJHJlZnJlc2ggPSByZWZyZXNoUXVlcnk7XG4gICAgICByZXN1bHRzLmV4dGVuZCA9IGV4dGVuZFJlc3VsdHM7XG4gICAgICByZXN1bHRzLnJlcGxhY2UgPSByZXBsYWNlO1xuICAgICAgcmVzdWx0cy5nZXQgPSBnZXQ7XG4gICAgICByZXN1bHRzLm5leHQgPSBuZXh0O1xuICAgICAgcmVzdWx0cy5wcmV2ID0gcHJldjtcbiAgICAgIHJlc3VsdHMuJHByb21pc2UgPSByZWZyZXNoUXVlcnkoKTtcbiAgICAgIHJlc3VsdHMuJHJlc29sdmVkU3ViamVjdCA9IGNvbnZlcnRUb1Jlc29sdmVkU3ViamVjdChyZXN1bHRzLiRzdWJqZWN0LCByZXN1bHRzLiRwcm9taXNlKTtcbiAgICAgIHJlc3VsdHMuY2hhaW4gPSBjaGFpbjtcbiAgICAgIHJlc3VsdHMuJE1vZGVsID0gUmVzb3VyY2U7XG4gICAgICByZXN1bHRzLiRzZXJ2ZXJSZXN1bHRzID0gc2VydmVyUmVzdWx0cztcblxuICAgICAgLy8gUHV0IGN1cnJlbnRTa2lwIGFuZCBjdXJyZW50TGltaXQgb250byB0aGUgcmVzdWx0c1xuICAgICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHJlc3VsdHMsICdjdXJyZW50TGltaXQnLCB7XG4gICAgICAgIGVudW1lcmFibGU6IHRydWUsXG4gICAgICAgIGdldDogKCkgPT4ge1xuICAgICAgICAgIHJldHVybiBjdXJyZW50TGltaXQ7XG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShyZXN1bHRzLCAnY3VycmVudFNraXAnLCB7XG4gICAgICAgIGVudW1lcmFibGU6IHRydWUsXG4gICAgICAgIGdldDogKCkgPT4ge1xuICAgICAgICAgIHJldHVybiBjdXJyZW50U2tpcDtcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuXG4gICAgICByZXR1cm4gcmVzdWx0cztcbiAgICB9O1xuXG4gICAgbGV0IHJlZnJlc2hUaW1lcjogbnVtYmVyO1xuICAgIExvY2FsUXVlcnlMaXN0LnJlZnJlc2ggPSAoKSA9PiB7XG5cbiAgICAgIC8vIElmIHdlIGhhdmUgYSB0aW1lciBvdXRzdGFuZGluZyB0aGVuIGp1c3QgcmV0dXJuIC0gYSByZWZyZXNoIHdpbGwgaGFwcGVuIHNvb24uXG4gICAgICBpZiAoIXJlZnJlc2hUaW1lcikge1xuICAgICAgICByZWZyZXNoVGltZXIgPSBzZXRUaW1lb3V0KGRvUmVmcmVzaCwgMTAwKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHJlZnJlc2hUaW1lcjtcblxuICAgICAgZnVuY3Rpb24gZG9SZWZyZXNoKCkge1xuICAgICAgICBfbG9jYWxRdWVyaWVzLmZvckVhY2gocmVzID0+IHtcbiAgICAgICAgICByZXMuJHJlZnJlc2goKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJlZnJlc2hUaW1lciA9IG51bGw7XG4gICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIFNlcnZlciBxdWVyeSBsaXN0LiBUaGUgc2VydmVyIG1ha2VzIHN1cmUgd2Uga2VlcCBpbiBzeW5jXG4gICAgICogQHBhcmFtIHtbdHlwZV19IHFyeSAgICAgIFtkZXNjcmlwdGlvbl1cbiAgICAgKiBAcGFyYW0ge1t0eXBlXX0gbGltaXQgICAgW2Rlc2NyaXB0aW9uXVxuICAgICAqIEBwYXJhbSB7W3R5cGVdfSBSZXNvdXJjZSBbZGVzY3JpcHRpb25dXG4gICAgICovXG4gICAgZnVuY3Rpb24gU2VydmVyUXVlcnlMaXN0KHFyeTogYW55LCBsaW1pdDogbnVtYmVyLCBSZXNvdXJjZTogYW55KTogU2VydmVyUXVlcnk8TW9kZWw+IHtcbiAgICAgIC8vIEFsbG93IHFyeSB0byBiZSBhIHByb21pc2VcbiAgICAgIHFyeSA9IFByb21pc2UucmVzb2x2ZShxcnkpLnRoZW4obm9ybWFsaXplUXVlcnkpLnRoZW4oX3FyeSA9PiB7XG4gICAgICAgIGlmIChsaW1pdCkge1xuICAgICAgICAgIF9xcnkubGltaXQgPSBsaW1pdDtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gX3FyeTtcbiAgICAgIH0pO1xuXG4gICAgICBsZXQgZW1pdFByb21pc2U6IFByb21pc2U8YW55PiA9IG51bGw7XG4gICAgICBjb25zdCByZXN1bHRzID0gbmV3IFNlcnZlclF1ZXJ5PE1vZGVsPigpO1xuICAgICAgbGV0IGN1cnJlbnRMaW1pdCA9IDA7XG4gICAgICBsZXQgY3VycmVudFNraXAgPSAwO1xuICAgICAgbGV0IGxhc3RCYXRjaFNpemUgPSAwO1xuICAgICAgbGV0IF9wYWdpbmdPcHRzOiBhbnkgPSB7fTtcblxuICAgICAgcmVzdWx0cy5sb2FkaW5nID0gdHJ1ZTtcblxuICAgICAgZnVuY3Rpb24gcXVlcnkoZGF0YTogYW55KSB7XG4gICAgICAgIC8vIFdlIG9ubHkgd2FudCB0byBkbyBvbmUgZW1pdCBhdCBhIHRpbWUgKG90aGVyd2lzZSB3ZSBjb3VsZCBnZXQgaW50byBhIGJhZCBzdGF0ZSlcbiAgICAgICAgaWYgKGlzRnVuY3Rpb24oZGF0YSkpIHtcbiAgICAgICAgICBkYXRhID0gZGF0YSgpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gU3RvcmUgb2ZmIHRoZSBxdWVyeSAtIG1ha2Ugc3VyZSBpdHMgYSBwcm9taXNlXG4gICAgICAgIHFyeSA9IFByb21pc2UucmVzb2x2ZShkYXRhKTtcblxuICAgICAgICByZXR1cm4gcXJ5LnRoZW4oKF9kYXRhOiBhbnkpID0+IHtcbiAgICAgICAgICAvLyBJZiB3ZSBoYXZlIGFuIGV4aXN0aW5nIGVtaXRQcm9taXNlIHRoZW4gd2FpdCBmb3IgaXQgdG8gcmVzb2x2ZSBiZWZvcmUgd2UgcnVuXG4gICAgICAgICAgbGV0IHByb21pc2U7XG4gICAgICAgICAgaWYgKGVtaXRQcm9taXNlKSB7XG4gICAgICAgICAgICBwcm9taXNlID0gZW1pdFByb21pc2UudGhlbigoKSA9PiB7XG4gICAgICAgICAgICAgIHJldHVybiBzb2NrLnF1ZXJ5KF9kYXRhKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBlbWl0UHJvbWlzZSA9IHByb21pc2UgPSBzb2NrLnF1ZXJ5KF9kYXRhKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICByZXR1cm4gcHJvbWlzZTtcbiAgICAgICAgfSk7XG4gICAgICB9XG5cbiAgICAgIGZ1bmN0aW9uIG5ld0RhdGEoX3FyeUlkOiBzdHJpbmcsIHJlc3BvbnNlOiBhbnksIGZvcmNlPzogYm9vbGVhbikge1xuICAgICAgICBpZiAoIXJlc3BvbnNlKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgZGVmZXJyZWQgPSBuZXcgRGVmZXJyZWQoKTtcbiAgICAgICAgY29uc3QgaWRzOiBzdHJpbmdbXSA9IHJlc3BvbnNlLmlkcztcbiAgICAgICAgY29uc3QgcGFnaW5nT3B0cyA9IHJlc3BvbnNlLnBhZ2luZ09wdHM7XG5cbiAgICAgICAgLy8gU28gZmFyIHdlJ3ZlIG9ubHkgZ290IHRoZSBpZHMgb2YgdGhlIHFyeSByZXN1bHQgLSBnbyBhbmQgZmV0Y2ggdGhlIGFjdHVhbCBvYmplY3RzLlxuICAgICAgICAvLyBUaGlzIG1lY2hhbmlzbSBzYXZlcyBiYW5kd2lkdGggYnkgb25seSBnZXR0aW5nIHRoZSBvYmplY3QgZGF0YSBvbmNlIHRoZW4gbGlzdGVuaW5nXG4gICAgICAgIC8vIGZvciBjaGFuZ2VzIHRvIGl0XG4gICAgICAgIC8vXG4gICAgICAgIC8vIElmIHdlIGFyZSBmb3JjaW5nIHdlIHdhbnQgdG8gZ2V0IGJvdGggdGhlIG9sZCBhbmQgbmV3IGlkcyB0byBjaGVjayBmb3IgYW55IGNoYW5nZXNcbiAgICAgICAgLy8gZGVsZXRpb25zIGV0Yy4uXG4gICAgICAgIGxldCBnZXRJZHMgPSBpZHM7XG4gICAgICAgIGlmIChmb3JjZSkge1xuICAgICAgICAgIGNvbnN0IG9sZElkcyA9IG1hcChyZXN1bHRzLCAnX2lkJyk7XG4gICAgICAgICAgZ2V0SWRzID0gdW5pb24oaWRzLCBvbGRJZHMpO1xuICAgICAgICB9XG5cbiAgICAgICAgUmVzb3VyY2UuZ2V0KGdldElkcywgZm9yY2UpLiRwcm9taXNlLnRoZW4oKHJlc3M6IGFueVtdKSA9PiB7XG4gICAgICAgICAgY29uc3QgcmVzc21hcDogYW55ID0ge307XG4gICAgICAgICAgcmVzcy5mb3JFYWNoKHJlcyA9PiB7XG4gICAgICAgICAgICByZXNzbWFwW3Jlcy5faWRdID0gcmVzO1xuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgLy8gV2UgZG9uJ3QgYWxsb3cgcmVwZWF0ZWQgaWRzIHNvIGp1c3QgaXRlcmF0ZSBvdmVyIHRoZSByZXN1bHRzXG4gICAgICAgICAgY29uc3QgdG1wUmVzdWx0czogYW55W10gPSBbXTtcbiAgICAgICAgICBpZHMuZm9yRWFjaChpZCA9PiB7XG4gICAgICAgICAgICB0bXBSZXN1bHRzLnB1c2gocmVzc21hcFtpZF0pO1xuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgLy8gRG8gd2UgbmVlZCB0byBkbyBhIHRyYW5zZm9ybT9cbiAgICAgICAgICBsZXQgcnByb207XG4gICAgICAgICAgaWYgKGlzRnVuY3Rpb24ocmVzdWx0cy50cmFuc2Zvcm0pKSB7XG4gICAgICAgICAgICBycHJvbSA9IFByb21pc2UucmVzb2x2ZShyZXN1bHRzLnRyYW5zZm9ybSh0bXBSZXN1bHRzKSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJwcm9tID0gUHJvbWlzZS5yZXNvbHZlKHRtcFJlc3VsdHMpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHJldHVybiBycHJvbS50aGVuKHRyYW5zZm9ybWVkID0+IHtcblxuICAgICAgICAgICAgcmVzdWx0cy5sZW5ndGggPSAwO1xuICAgICAgICAgICAgdHJhbnNmb3JtZWQuZm9yRWFjaCgocmVzOiBNb2RlbCkgPT4ge1xuICAgICAgICAgICAgICByZXN1bHRzLnB1c2gocmVzKTtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAvLyBTaW5jZSB3ZSBub3cgaGF2ZSBkYXRhIGluIG91ciBhcnJheSBzdG9yZSBvZmYgbGltaXQgZGF0YVxuICAgICAgICAgICAgY3VycmVudExpbWl0ID0gdG1wUmVzdWx0cy5sZW5ndGg7XG4gICAgICAgICAgICByZXN1bHRzLiRza2lwID0gY3VycmVudFNraXAgPSAocXJ5ICYmIHFyeS5za2lwKSA/IHFyeS5za2lwIDogMDtcbiAgICAgICAgICAgIGxhc3RCYXRjaFNpemUgPSBsYXN0QmF0Y2hTaXplIHx8IHRtcFJlc3VsdHMubGVuZ3RoO1xuICAgICAgICAgICAgX3BhZ2luZ09wdHMgPSBwYWdpbmdPcHRzO1xuICAgICAgICAgICAgcmVzdWx0cy5oYXNOZXh0ID0gKHBhZ2luZ09wdHMubmV4dCAhPSBudWxsKTtcbiAgICAgICAgICAgIHJlc3VsdHMuaGFzUHJldiA9IChwYWdpbmdPcHRzLnByZXYgIT0gbnVsbCk7XG5cbiAgICAgICAgICAgIC8vIERhdGEgaGFzIGNvbWUgYmFja1xuICAgICAgICAgICAgcmVzdWx0cy5sb2FkaW5nID0gZmFsc2U7XG5cbiAgICAgICAgICAgIHJlc3VsdHMuJGVtaXR0ZXIuZW1pdCgndXBkYXRlJywgcmVzdWx0cyk7XG5cbiAgICAgICAgICAgIGRlZmVycmVkLnJlc29sdmUocmVzdWx0cyk7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHJldHVybiBkZWZlcnJlZC5wcm9taXNlO1xuICAgICAgfVxuXG4gICAgICBmdW5jdGlvbiByZWZyZXNoUXVlcnkoZm9yY2U/OiBib29sZWFuKTogUHJvbWlzZTxTZXJ2ZXJRdWVyeTxNb2RlbD4+IHtcbiAgICAgICAgY29uc3QgcmVxID0gcXVlcnkocXJ5KTtcbiAgICAgICAgY29uc3QgcHJvbWlzZSA9IHJlcS50aGVuKChyZXM6IGFueSkgPT4ge1xuXG4gICAgICAgICAgLy8gSWYgd2UgZ2V0IG5vIHJlc3BvbnNlICh0aGUgYXBwIGNvdWxkIGJlIG9mZmxpbmUpIHRoZW4ganVzdCByZXNvbHZlIHdpdGhcbiAgICAgICAgICAvLyB0aGUgZXhpc3RpbmcgcmVzdWx0c1xuICAgICAgICAgIGlmIChpc1VuZGVmaW5lZChyZXMpKSB7XG4gICAgICAgICAgICByZXN1bHRzLmxvYWRpbmcgPSBmYWxzZTtcbiAgICAgICAgICAgIHJldHVybiByZXN1bHRzO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGNvbnN0IF9xcnlJZCA9IHJlcy5xcnlJZDtcbiAgICAgICAgICBjb25zdCByZXNwb25zZSA9IHJlcy5kYXRhO1xuXG4gICAgICAgICAgcmV0dXJuIG5ld0RhdGEoX3FyeUlkLCByZXNwb25zZSwgZm9yY2UpO1xuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIHByb21pc2U7XG4gICAgICB9XG5cbiAgICAgIGZ1bmN0aW9uIHJlcGxhY2UoX3FyeTogYW55KSB7XG4gICAgICAgIC8vIERvIHRoZSBxdWVyeSBidXQgcmVwbGFjZSB0aGUgZXhpc3RpbmcgcXVlcnlcbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShfcXJ5KS50aGVuKG5vcm1hbGl6ZVF1ZXJ5KS50aGVuKG5vcm1RcnkgPT4ge1xuXG4gICAgICAgICAgLy8gV2UgYWxsb3cgYSBxdWVyeSB0byByZXNvbHZlIHRvIHNvbWV0aGluZyBmYWxzeSAtIGluIHdoaWNoIGNhc2Ugd2UganVzdFxuICAgICAgICAgIC8vIGRyb3AgaXRcbiAgICAgICAgICBpZiAoIW5vcm1RcnkpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICByZXR1cm4gcXJ5LnRoZW4oKG9sZHFyeTogYW55KSA9PiB7XG4gICAgICAgICAgICBpZiAoSlNPTi5zdHJpbmdpZnkob2xkcXJ5KSAhPT0gSlNPTi5zdHJpbmdpZnkobm9ybVFyeSkpIHtcbiAgICAgICAgICAgICAgLy8gcXVlcnkgaXMgZGlmZmVyZW50IC0gY29udGludWVcbiAgICAgICAgICAgICAgY29uc3QgcmVxID0gcXVlcnkobm9ybVFyeSk7XG5cbiAgICAgICAgICAgICAgY29uc3QgcHJvbWlzZSA9IHJlcS50aGVuKChyZXM6IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgIC8vIElmIHdlIGdldCBubyByZXNwb25zZSAodGhlIGFwcCBjb3VsZCBiZSBvZmZsaW5lKSB0aGVuIGp1c3QgcmVzb2x2ZSB3aXRoXG4gICAgICAgICAgICAgICAgLy8gdGhlIGV4aXN0aW5nIHJlc3VsdHNcbiAgICAgICAgICAgICAgICBpZiAoaXNVbmRlZmluZWQocmVzKSkge1xuICAgICAgICAgICAgICAgICAgcmVzdWx0cy5sb2FkaW5nID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgICByZXR1cm4gcmVzdWx0cztcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBXZSBkbyBoYXZlIGEgcmVzcG9uc2UuIENvbnRpbnVlXG4gICAgICAgICAgICAgICAgY29uc3QgX3FyeUlkID0gcmVzLnFyeUlkO1xuICAgICAgICAgICAgICAgIGNvbnN0IGRhdGEgPSByZXMuZGF0YTtcblxuICAgICAgICAgICAgICAgIHJldHVybiBuZXdEYXRhKF9xcnlJZCwgZGF0YSk7XG4gICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgIHJldHVybiBwcm9taXNlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcblxuICAgICAgfVxuXG4gICAgICBmdW5jdGlvbiBleHRlbmRSZXN1bHRzKG9iajogYW55LCBub1Nhbml0aXplPzogYm9vbGVhbikge1xuXG4gICAgICAgIC8vIFNhbml0aXplIHRoZSBvYmplY3RcbiAgICAgICAgaWYgKCFub1Nhbml0aXplKSB7XG4gICAgICAgICAgaWYgKG9iai5fcSkge1xuICAgICAgICAgICAgb2JqID0gb2JqLl9xO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBvYmogPSB7ZmluZDogb2JqfTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gcXJ5LnRoZW4oKHJlc29sdmVkOiBhbnkpID0+IHtcbiAgICAgICAgICBjb25zdCBfcXJ5ID0gZXh0ZW5kUXVlcnkocmVzb2x2ZWQsIG9iaik7XG4gICAgICAgICAgcmV0dXJuIHJlcGxhY2UoX3FyeSk7XG4gICAgICAgIH0pO1xuICAgICAgfVxuXG4gICAgICBmdW5jdGlvbiBuZXh0KG51bTogbnVtYmVyKSB7XG4gICAgICAgIGxldCBwcm9taXNlO1xuICAgICAgICBsZXQgZXh0ZW5kU2l6ZTtcbiAgICAgICAgaWYgKF9wYWdpbmdPcHRzLm5leHQpIHtcbiAgICAgICAgICBsYXN0QmF0Y2hTaXplID0gZXh0ZW5kU2l6ZSA9IG51bSB8fCBsYXN0QmF0Y2hTaXplO1xuICAgICAgICAgIGNvbnN0IGV4dGVuZE9iaiA9IHtcbiAgICAgICAgICAgIGxpbWl0OiBjdXJyZW50TGltaXQgKyBleHRlbmRTaXplLFxuICAgICAgICAgIH07XG4gICAgICAgICAgcHJvbWlzZSA9IGV4dGVuZFJlc3VsdHMoZXh0ZW5kT2JqLCB0cnVlKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjb25zdCBkZWZlcnJlZCA9IG5ldyBEZWZlcnJlZCgpO1xuICAgICAgICAgIGRlZmVycmVkLnJlc29sdmUoKTtcbiAgICAgICAgICBwcm9taXNlID0gZGVmZXJyZWQucHJvbWlzZTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBwcm9taXNlO1xuICAgICAgfVxuXG4gICAgICBmdW5jdGlvbiBwcmV2KG51bTogbnVtYmVyKSB7XG4gICAgICAgIGxldCBwcm9taXNlO1xuICAgICAgICBsZXQgZXh0ZW5kU2l6ZTtcbiAgICAgICAgaWYgKF9wYWdpbmdPcHRzLnByZXYpIHtcbiAgICAgICAgICBsYXN0QmF0Y2hTaXplID0gZXh0ZW5kU2l6ZSA9IG51bSB8fCBsYXN0QmF0Y2hTaXplO1xuICAgICAgICAgIGNvbnN0IGV4dGVuZE9iaiA9IHtcbiAgICAgICAgICAgIGxpbWl0OiBjdXJyZW50TGltaXQgKyBleHRlbmRTaXplLFxuICAgICAgICAgICAgc2tpcDogTWF0aC5tYXgoY3VycmVudFNraXAgLSBleHRlbmRTaXplLCAwKSxcbiAgICAgICAgICB9O1xuICAgICAgICAgIHByb21pc2UgPSBleHRlbmRSZXN1bHRzKGV4dGVuZE9iaiwgdHJ1ZSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY29uc3QgZGVmZXJyZWQgPSBuZXcgRGVmZXJyZWQoKTtcbiAgICAgICAgICBkZWZlcnJlZC5yZXNvbHZlKCk7XG4gICAgICAgICAgcHJvbWlzZSA9IGRlZmVycmVkLnByb21pc2U7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gcHJvbWlzZTtcbiAgICAgIH1cblxuICAgICAgZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgICByZXR1cm4gUmVzb3VyY2UuZ2V0LmFwcGx5KFJlc291cmNlLCBhcmd1bWVudHMpO1xuICAgICAgfVxuXG4gICAgICBmdW5jdGlvbiBjaGFpbihNb2RlbDogYW55LCBxcnlGbjogUXVlcnlGdW5jdGlvbikge1xuICAgICAgICByZXR1cm4gQ2hhaW4ocmVzdWx0cywgTW9kZWwsIHFyeUZuKTtcbiAgICAgIH1cblxuICAgICAgcmVzdWx0cy4kbmV3RGF0YSA9IG5ld0RhdGE7XG4gICAgICByZXN1bHRzLiRyZWZyZXNoID0gcmVmcmVzaFF1ZXJ5O1xuICAgICAgcmVzdWx0cy5leHRlbmQgPSBleHRlbmRSZXN1bHRzO1xuICAgICAgcmVzdWx0cy5yZXBsYWNlID0gcmVwbGFjZTtcbiAgICAgIHJlc3VsdHMuZ2V0ID0gZ2V0O1xuICAgICAgcmVzdWx0cy5uZXh0ID0gbmV4dDtcbiAgICAgIHJlc3VsdHMucHJldiA9IHByZXY7XG4gICAgICByZXN1bHRzLiRwcm9taXNlID0gcmVmcmVzaFF1ZXJ5KCkudGhlbihyZXMgPT4ge1xuICAgICAgICByZXN1bHRzLiRyZXNvbHZlZCA9IHRydWU7XG4gICAgICAgIHJldHVybiByZXM7XG4gICAgICB9KTtcbiAgICAgIHJlc3VsdHMuJHJlc29sdmVkU3ViamVjdCA9IGNvbnZlcnRUb1Jlc29sdmVkU3ViamVjdChyZXN1bHRzLiRzdWJqZWN0LCByZXN1bHRzLiRwcm9taXNlKTtcbiAgICAgIHJlc3VsdHMuJHJlc29sdmVkID0gZmFsc2U7XG4gICAgICByZXN1bHRzLmNoYWluID0gY2hhaW47XG4gICAgICByZXN1bHRzLiRNb2RlbCA9IFJlc291cmNlO1xuICAgICAgcmVzdWx0cy4kc2tpcCA9IDA7XG5cbiAgICAgIC8vIFB1dCBjdXJyZW50U2tpcCBhbmQgY3VycmVudExpbWl0IG9udG8gdGhlIHJlc3VsdHNcbiAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShyZXN1bHRzLCAnY3VycmVudExpbWl0Jywge1xuICAgICAgICBlbnVtZXJhYmxlOiB0cnVlLFxuICAgICAgICBnZXQ6ICgpID0+IHtcbiAgICAgICAgICByZXR1cm4gY3VycmVudExpbWl0O1xuICAgICAgICB9LFxuICAgICAgfSk7XG4gICAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkocmVzdWx0cywgJ2N1cnJlbnRTa2lwJywge1xuICAgICAgICBlbnVtZXJhYmxlOiB0cnVlLFxuICAgICAgICBnZXQ6ICgpID0+IHtcbiAgICAgICAgICByZXR1cm4gY3VycmVudFNraXA7XG4gICAgICAgIH0sXG4gICAgICB9KTtcblxuICAgICAgcmV0dXJuIHJlc3VsdHM7XG4gICAgfVxuXG4gICAgcmV0dXJuIGRiID8gTG9jYWxRdWVyeUxpc3QgOiBTZXJ2ZXJRdWVyeUxpc3Q7XG4gIH1cblxuICByZXR1cm4gUXVlcnlGYWN0b3J5O1xufVxuIl19