UNPKG

metaapi.cloud-sdk

Version:

SDK for MetaApi, a professional cloud forex API which includes MetaTrader REST API and MetaTrader websocket API. Supports both MetaTrader 5 (MT5) and MetaTrader 4 (MT4). CopyFactory copy trading API included. (https://metaapi.cloud)

358 lines (357 loc) 41.1 kB
'use strict'; function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _async_to_generator(fn) { return function() { var self = this, args = arguments; return new Promise(function(resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } import HistoryStorage from './historyStorage'; import HistoryDatabase from './historyDatabase/index'; import { AVLTree } from '../binary-search-tree/avltree'; import LoggerManager from '../logger'; let MemoryHistoryStorage = class MemoryHistoryStorage extends HistoryStorage { /** * Initializes the storage and loads required data from a persistent storage * @param {string} accountId account id * @param {string} [application] application. Default is MetaApi */ initialize(accountId, application = 'MetaApi') { var _this = this, _superprop_get_initialize = ()=>super.initialize; return _async_to_generator(function*() { yield _superprop_get_initialize().call(_this, accountId, application); let { deals, historyOrders } = yield _this._historyDatabase.loadHistory(accountId, application); for (let deal of deals){ yield _this._addDeal(deal, true); } for (let historyOrder of historyOrders){ yield _this._addHistoryOrder(historyOrder, true); } })(); } /** * Resets the storage. Intended for use in tests * @returns {Promise} promise when the history is removed */ clear() { var _this = this; return _async_to_generator(function*() { _this._reset(); yield _this._historyDatabase.clear(_this._accountId, _this._application); })(); } /** * Returns the time of the last history order record stored in the history storage * @param {Number} [instanceNumber] index of an account instance connected * @returns {Date} the time of the last history order record stored in the history storage */ lastHistoryOrderTime(instanceNumber) { return this._maxHistoryOrderTime; } /** * Returns the time of the last history deal record stored in the history storage * @param {Number} [instanceNumber] index of an account instance connected * @returns {Date} the time of the last history deal record stored in the history storage */ lastDealTime(instanceNumber) { return this._maxDealTime; } /** * Invoked when a new MetaTrader history order is added * @param {String} instanceIndex index of an account instance connected * @param {MetatraderOrder} historyOrder new MetaTrader history order */ onHistoryOrderAdded(instanceIndex, historyOrder) { var _this = this; return _async_to_generator(function*() { yield _this._addHistoryOrder(historyOrder); })(); } /** * Invoked when a new MetaTrader history deal is added * @param {String} instanceIndex index of an account instance connected * @param {MetatraderDeal} deal new MetaTrader history deal */ onDealAdded(instanceIndex, deal) { var _this = this; return _async_to_generator(function*() { yield _this._addDeal(deal); })(); } /** * Returns all deals * @returns {Array<MetatraderDeal>} all deals */ get deals() { return this.getDealsByTimeRange(new Date(0), new Date(8640000000000000)); } /** * Returns deals by ticket id * @param {string} id ticket id * @returns {Array<MetatraderDeal>} deals found */ getDealsByTicket(id) { let deals = Object.values(this._dealsByTicket[id] || {}); deals.sort(this._dealsComparator); return deals; } /** * Returns deals by position id * @param {string} positionId position id * @returns {Array<MetatraderDeal>} deals found */ getDealsByPosition(positionId) { let deals = Object.values(this._dealsByPosition[positionId] || {}); deals.sort(this._dealsComparator); return deals; } /** * Returns deals by time range * @param startTime start time, inclusive * @param endTime end time, inclusive * @returns {Array<MetatraderDeal>} deals found */ getDealsByTimeRange(startTime, endTime) { let deals = this._dealsByTime.betweenBounds({ $gte: { time: startTime, id: 0, entryType: '' }, $lte: { time: endTime, id: Number.MAX_VALUE, entryType: '' } }); return deals; } /** * Returns all history orders * @returns {Array<MetatraderOrder>} all history orders */ get historyOrders() { return this.getHistoryOrdersByTimeRange(new Date(0), new Date(8640000000000000)); } /** * Returns history orders by ticket id * @param {string} id ticket id * @returns {Array<MetatraderOrder>} history orders found */ getHistoryOrdersByTicket(id) { let historyOrders = Object.values(this._historyOrdersByTicket[id] || {}); historyOrders.sort(this._historyOrdersComparator); return historyOrders; } /** * Returns history orders by position id * @param {string} positionId position id * @returns {Array<MetatraderOrder>} history orders found */ getHistoryOrdersByPosition(positionId) { let historyOrders = Object.values(this._historyOrdersByPosition[positionId] || {}); historyOrders.sort(this._historyOrdersComparator); return historyOrders; } /** * Returns history orders by time range * @param startTime start time, inclusive * @param endTime end time, inclusive * @returns {Array<MetatraderOrder>} hisotry orders found */ getHistoryOrdersByTimeRange(startTime, endTime) { let historyOrders = this._historyOrdersByTime.betweenBounds({ $gte: { doneTime: startTime, id: 0, type: '', state: '' }, $lte: { doneTime: endTime, id: Number.MAX_VALUE, type: '', state: '' } }); return historyOrders; } /** * Invoked when a synchronization of history deals on a MetaTrader account have finished to indicate progress of an * initial terminal state synchronization * @param {String} instanceIndex index of an account instance connected * @param {String} synchronizationId synchronization request id * @return {Promise} promise which resolves when the asynchronous event is processed */ onDealsSynchronized(instanceIndex, synchronizationId) { var _this = this, _superprop_get_onDealsSynchronized = ()=>super.onDealsSynchronized; return _async_to_generator(function*() { yield _this._flushDatabase(); yield _superprop_get_onDealsSynchronized().call(_this, instanceIndex, synchronizationId); })(); } _reset() { this._orderSynchronizationFinished = {}; this._dealSynchronizationFinished = {}; this._dealsByTicket = {}; this._dealsByPosition = {}; this._historyOrdersByTicket = {}; this._historyOrdersByPosition = {}; // eslint-disable-next-line complexity this._historyOrdersComparator = (o1, o2)=>{ let timeDiff = (o1.doneTime || new Date(0)).getTime() - (o2.doneTime || new Date(0)).getTime(); if (timeDiff === 0) { let idDiff = o1.id - o2.id; if (idDiff === 0) { if (o1.type > o2.type) { return 1; } else if (o1.type < o2.type) { return -1; } else { if (o1.state > o2.state) { return 1; } else if (o1.state < o2.state) { return -1; } else { return 0; } } } else { return idDiff; } } else { return timeDiff; } }; this._historyOrdersByTime = new AVLTree({ compareKeys: this._historyOrdersComparator }); this._dealsComparator = (d1, d2)=>{ let timeDiff = (d1.time || new Date(0)).getTime() - (d2.time || new Date(0)).getTime(); if (timeDiff === 0) { let idDiff = d1.id - d2.id; if (idDiff === 0) { if (d1.entryType > d2.entryType) { return 1; } else if (d1.entryType < d2.entryType) { return -1; } else { return 0; } } else { return idDiff; } } else { return timeDiff; } }; this._dealsByTime = new AVLTree({ compareKeys: this._dealsComparator }); this._maxHistoryOrderTime = new Date(0); this._maxDealTime = new Date(0); this._newHistoryOrders = []; this._newDeals = []; clearTimeout(this._flushTimeout); delete this._flushTimeout; } // eslint-disable-next-line complexity _addDeal(deal, existing) { var _this = this; return _async_to_generator(function*() { let key = _this._getDealKey(deal); _this._dealsByTicket[deal.id] = _this._dealsByTicket[deal.id] || {}; let newDeal = !existing && !_this._dealsByTicket[deal.id][key]; _this._dealsByTicket[deal.id][key] = deal; if (deal.positionId) { _this._dealsByPosition[deal.positionId] = _this._dealsByPosition[deal.positionId] || {}; _this._dealsByPosition[deal.positionId][key] = deal; } _this._dealsByTime.delete(deal); _this._dealsByTime.insert(deal, deal); if (deal.time && (!_this._maxDealTime || _this._maxDealTime.getTime() < deal.time.getTime())) { _this._maxDealTime = deal.time; } if (newDeal) { _this._newDeals.push(deal); clearTimeout(_this._flushTimeout); _this._flushTimeout = setTimeout(_this._flushDatabase.bind(_this), 5000); } })(); } _getDealKey(deal) { return (deal.time || new Date(0)).toISOString() + ':' + deal.id + ':' + deal.entryType; } // eslint-disable-next-line complexity _addHistoryOrder(historyOrder, existing) { var _this = this; return _async_to_generator(function*() { let key = _this._getHistoryOrderKey(historyOrder); _this._historyOrdersByTicket[historyOrder.id] = _this._historyOrdersByTicket[historyOrder.id] || {}; let newHistoryOrder = !existing && !_this._historyOrdersByTicket[historyOrder.id][key]; _this._historyOrdersByTicket[historyOrder.id][key] = historyOrder; if (historyOrder.positionId) { _this._historyOrdersByPosition[historyOrder.positionId] = _this._historyOrdersByPosition[historyOrder.positionId] || {}; _this._historyOrdersByPosition[historyOrder.positionId][key] = historyOrder; } _this._historyOrdersByTime.delete(historyOrder); _this._historyOrdersByTime.insert(historyOrder, historyOrder); if (historyOrder.doneTime && (!_this._maxHistoryOrderTime || _this._maxHistoryOrderTime.getTime() < historyOrder.doneTime.getTime())) { _this._maxHistoryOrderTime = historyOrder.doneTime; } if (newHistoryOrder) { _this._newHistoryOrders.push(historyOrder); clearTimeout(_this._flushTimeout); _this._flushTimeout = setTimeout(_this._flushDatabase.bind(_this), 5000); } })(); } _getHistoryOrderKey(historyOrder) { return (historyOrder.doneTime || new Date(0)).toISOString() + ':' + historyOrder.id + ':' + historyOrder.type + ':' + historyOrder.state; } _flushDatabase() { var _this = this; return _async_to_generator(function*() { if (_this._flushPromise) { yield _this._flushPromise; } if (_this._flushRunning) { return; } _this._flushRunning = true; let resolve; _this._flushPromise = new Promise((res)=>resolve = res); try { yield _this._historyDatabase.flush(_this._accountId, _this._application, _this._newHistoryOrders, _this._newDeals); _this._newHistoryOrders = []; _this._newDeals = []; _this._logger.debug(`${_this._accountId}: flushed history db`); } catch (err) { _this._logger.warn(`${_this._accountId}: error flushing history db`, err); _this._flushTimeout = setTimeout(_this._flushDatabase.bind(_this), 15000); } finally{ resolve(); _this._flushRunning = false; } })(); } /** * Constructs the in-memory history store instance */ constructor(){ super(); this._historyDatabase = HistoryDatabase.getInstance(); this._reset(); this._logger = LoggerManager.getLogger('MemoryHistoryStorage'); } }; /** * History storage which stores MetaTrader history in RAM */ export { MemoryHistoryStorage as default }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCBIaXN0b3J5U3RvcmFnZSBmcm9tICcuL2hpc3RvcnlTdG9yYWdlJztcbmltcG9ydCBIaXN0b3J5RGF0YWJhc2UgZnJvbSAnLi9oaXN0b3J5RGF0YWJhc2UvaW5kZXgnO1xuaW1wb3J0IHsgQVZMVHJlZSB9IGZyb20gJy4uL2JpbmFyeS1zZWFyY2gtdHJlZS9hdmx0cmVlJ1xuaW1wb3J0IExvZ2dlck1hbmFnZXIgZnJvbSAnLi4vbG9nZ2VyJztcblxuLyoqXG4gKiBIaXN0b3J5IHN0b3JhZ2Ugd2hpY2ggc3RvcmVzIE1ldGFUcmFkZXIgaGlzdG9yeSBpbiBSQU1cbiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgTWVtb3J5SGlzdG9yeVN0b3JhZ2UgZXh0ZW5kcyBIaXN0b3J5U3RvcmFnZSB7XG5cbiAgLyoqXG4gICAqIENvbnN0cnVjdHMgdGhlIGluLW1lbW9yeSBoaXN0b3J5IHN0b3JlIGluc3RhbmNlXG4gICAqL1xuICBjb25zdHJ1Y3RvcigpIHtcbiAgICBzdXBlcigpO1xuICAgIHRoaXMuX2hpc3RvcnlEYXRhYmFzZSA9IEhpc3RvcnlEYXRhYmFzZS5nZXRJbnN0YW5jZSgpO1xuICAgIHRoaXMuX3Jlc2V0KCk7XG4gICAgdGhpcy5fbG9nZ2VyID0gTG9nZ2VyTWFuYWdlci5nZXRMb2dnZXIoJ01lbW9yeUhpc3RvcnlTdG9yYWdlJyk7XG4gIH1cblxuICAvKipcbiAgICogSW5pdGlhbGl6ZXMgdGhlIHN0b3JhZ2UgYW5kIGxvYWRzIHJlcXVpcmVkIGRhdGEgZnJvbSBhIHBlcnNpc3RlbnQgc3RvcmFnZVxuICAgKiBAcGFyYW0ge3N0cmluZ30gYWNjb3VudElkIGFjY291bnQgaWRcbiAgICogQHBhcmFtIHtzdHJpbmd9IFthcHBsaWNhdGlvbl0gYXBwbGljYXRpb24uIERlZmF1bHQgaXMgTWV0YUFwaVxuICAgKi9cbiAgYXN5bmMgaW5pdGlhbGl6ZShhY2NvdW50SWQsIGFwcGxpY2F0aW9uID0gJ01ldGFBcGknKSB7XG4gICAgYXdhaXQgc3VwZXIuaW5pdGlhbGl6ZShhY2NvdW50SWQsIGFwcGxpY2F0aW9uKTtcbiAgICBsZXQge2RlYWxzLCBoaXN0b3J5T3JkZXJzfSA9IGF3YWl0IHRoaXMuX2hpc3RvcnlEYXRhYmFzZS5sb2FkSGlzdG9yeShhY2NvdW50SWQsIGFwcGxpY2F0aW9uKTtcbiAgICBmb3IgKGxldCBkZWFsIG9mIGRlYWxzKSB7XG4gICAgICBhd2FpdCB0aGlzLl9hZGREZWFsKGRlYWwsIHRydWUpO1xuICAgIH1cbiAgICBmb3IgKGxldCBoaXN0b3J5T3JkZXIgb2YgaGlzdG9yeU9yZGVycykge1xuICAgICAgYXdhaXQgdGhpcy5fYWRkSGlzdG9yeU9yZGVyKGhpc3RvcnlPcmRlciwgdHJ1ZSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJlc2V0cyB0aGUgc3RvcmFnZS4gSW50ZW5kZWQgZm9yIHVzZSBpbiB0ZXN0c1xuICAgKiBAcmV0dXJucyB7UHJvbWlzZX0gcHJvbWlzZSB3aGVuIHRoZSBoaXN0b3J5IGlzIHJlbW92ZWRcbiAgICovXG4gIGFzeW5jIGNsZWFyKCkge1xuICAgIHRoaXMuX3Jlc2V0KCk7XG4gICAgYXdhaXQgdGhpcy5faGlzdG9yeURhdGFiYXNlLmNsZWFyKHRoaXMuX2FjY291bnRJZCwgdGhpcy5fYXBwbGljYXRpb24pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIHRpbWUgb2YgdGhlIGxhc3QgaGlzdG9yeSBvcmRlciByZWNvcmQgc3RvcmVkIGluIHRoZSBoaXN0b3J5IHN0b3JhZ2VcbiAgICogQHBhcmFtIHtOdW1iZXJ9IFtpbnN0YW5jZU51bWJlcl0gaW5kZXggb2YgYW4gYWNjb3VudCBpbnN0YW5jZSBjb25uZWN0ZWRcbiAgICogQHJldHVybnMge0RhdGV9IHRoZSB0aW1lIG9mIHRoZSBsYXN0IGhpc3Rvcnkgb3JkZXIgcmVjb3JkIHN0b3JlZCBpbiB0aGUgaGlzdG9yeSBzdG9yYWdlXG4gICAqL1xuICBsYXN0SGlzdG9yeU9yZGVyVGltZShpbnN0YW5jZU51bWJlcikge1xuICAgIHJldHVybiB0aGlzLl9tYXhIaXN0b3J5T3JkZXJUaW1lO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIHRpbWUgb2YgdGhlIGxhc3QgaGlzdG9yeSBkZWFsIHJlY29yZCBzdG9yZWQgaW4gdGhlIGhpc3Rvcnkgc3RvcmFnZVxuICAgKiBAcGFyYW0ge051bWJlcn0gW2luc3RhbmNlTnVtYmVyXSBpbmRleCBvZiBhbiBhY2NvdW50IGluc3RhbmNlIGNvbm5lY3RlZFxuICAgKiBAcmV0dXJucyB7RGF0ZX0gdGhlIHRpbWUgb2YgdGhlIGxhc3QgaGlzdG9yeSBkZWFsIHJlY29yZCBzdG9yZWQgaW4gdGhlIGhpc3Rvcnkgc3RvcmFnZVxuICAgKi9cbiAgbGFzdERlYWxUaW1lKGluc3RhbmNlTnVtYmVyKSB7XG4gICAgcmV0dXJuIHRoaXMuX21heERlYWxUaW1lO1xuICB9XG5cbiAgLyoqXG4gICAqIEludm9rZWQgd2hlbiBhIG5ldyBNZXRhVHJhZGVyIGhpc3Rvcnkgb3JkZXIgaXMgYWRkZWRcbiAgICogQHBhcmFtIHtTdHJpbmd9IGluc3RhbmNlSW5kZXggaW5kZXggb2YgYW4gYWNjb3VudCBpbnN0YW5jZSBjb25uZWN0ZWRcbiAgICogQHBhcmFtIHtNZXRhdHJhZGVyT3JkZXJ9IGhpc3RvcnlPcmRlciBuZXcgTWV0YVRyYWRlciBoaXN0b3J5IG9yZGVyXG4gICAqL1xuICBhc3luYyBvbkhpc3RvcnlPcmRlckFkZGVkKGluc3RhbmNlSW5kZXgsIGhpc3RvcnlPcmRlcikge1xuICAgIGF3YWl0IHRoaXMuX2FkZEhpc3RvcnlPcmRlcihoaXN0b3J5T3JkZXIpO1xuICB9XG5cbiAgLyoqXG4gICAqIEludm9rZWQgd2hlbiBhIG5ldyBNZXRhVHJhZGVyIGhpc3RvcnkgZGVhbCBpcyBhZGRlZFxuICAgKiBAcGFyYW0ge1N0cmluZ30gaW5zdGFuY2VJbmRleCBpbmRleCBvZiBhbiBhY2NvdW50IGluc3RhbmNlIGNvbm5lY3RlZFxuICAgKiBAcGFyYW0ge01ldGF0cmFkZXJEZWFsfSBkZWFsIG5ldyBNZXRhVHJhZGVyIGhpc3RvcnkgZGVhbFxuICAgKi9cbiAgYXN5bmMgb25EZWFsQWRkZWQoaW5zdGFuY2VJbmRleCwgZGVhbCkge1xuICAgIGF3YWl0IHRoaXMuX2FkZERlYWwoZGVhbCk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBhbGwgZGVhbHNcbiAgICogQHJldHVybnMge0FycmF5PE1ldGF0cmFkZXJEZWFsPn0gYWxsIGRlYWxzXG4gICAqL1xuICBnZXQgZGVhbHMoKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0RGVhbHNCeVRpbWVSYW5nZShuZXcgRGF0ZSgwKSwgbmV3IERhdGUoODY0MDAwMDAwMDAwMDAwMCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgZGVhbHMgYnkgdGlja2V0IGlkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpZCB0aWNrZXQgaWRcbiAgICogQHJldHVybnMge0FycmF5PE1ldGF0cmFkZXJEZWFsPn0gZGVhbHMgZm91bmRcbiAgICovXG4gIGdldERlYWxzQnlUaWNrZXQoaWQpIHtcbiAgICBsZXQgZGVhbHMgPSBPYmplY3QudmFsdWVzKHRoaXMuX2RlYWxzQnlUaWNrZXRbaWRdIHx8IHt9KTtcbiAgICBkZWFscy5zb3J0KHRoaXMuX2RlYWxzQ29tcGFyYXRvcik7XG4gICAgcmV0dXJuIGRlYWxzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgZGVhbHMgYnkgcG9zaXRpb24gaWRcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBvc2l0aW9uSWQgcG9zaXRpb24gaWRcbiAgICogQHJldHVybnMge0FycmF5PE1ldGF0cmFkZXJEZWFsPn0gZGVhbHMgZm91bmRcbiAgICovXG4gIGdldERlYWxzQnlQb3NpdGlvbihwb3NpdGlvbklkKSB7XG4gICAgbGV0IGRlYWxzID0gT2JqZWN0LnZhbHVlcyh0aGlzLl9kZWFsc0J5UG9zaXRpb25bcG9zaXRpb25JZF0gfHwge30pO1xuICAgIGRlYWxzLnNvcnQodGhpcy5fZGVhbHNDb21wYXJhdG9yKTtcbiAgICByZXR1cm4gZGVhbHM7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBkZWFscyBieSB0aW1lIHJhbmdlXG4gICAqIEBwYXJhbSBzdGFydFRpbWUgc3RhcnQgdGltZSwgaW5jbHVzaXZlXG4gICAqIEBwYXJhbSBlbmRUaW1lIGVuZCB0aW1lLCBpbmNsdXNpdmVcbiAgICogQHJldHVybnMge0FycmF5PE1ldGF0cmFkZXJEZWFsPn0gZGVhbHMgZm91bmRcbiAgICovXG4gIGdldERlYWxzQnlUaW1lUmFuZ2Uoc3RhcnRUaW1lLCBlbmRUaW1lKSB7XG4gICAgbGV0IGRlYWxzID0gdGhpcy5fZGVhbHNCeVRpbWUuYmV0d2VlbkJvdW5kcyh7XG4gICAgICAkZ3RlOiB7dGltZTogc3RhcnRUaW1lLCBpZDogMCwgZW50cnlUeXBlOiAnJ30sXG4gICAgICAkbHRlOiB7dGltZTogZW5kVGltZSwgaWQ6IE51bWJlci5NQVhfVkFMVUUsIGVudHJ5VHlwZTogJyd9XG4gICAgfSk7XG4gICAgcmV0dXJuIGRlYWxzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYWxsIGhpc3Rvcnkgb3JkZXJzXG4gICAqIEByZXR1cm5zIHtBcnJheTxNZXRhdHJhZGVyT3JkZXI+fSBhbGwgaGlzdG9yeSBvcmRlcnNcbiAgICovXG4gIGdldCBoaXN0b3J5T3JkZXJzKCkge1xuICAgIHJldHVybiB0aGlzLmdldEhpc3RvcnlPcmRlcnNCeVRpbWVSYW5nZShuZXcgRGF0ZSgwKSwgbmV3IERhdGUoODY0MDAwMDAwMDAwMDAwMCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgaGlzdG9yeSBvcmRlcnMgYnkgdGlja2V0IGlkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpZCB0aWNrZXQgaWRcbiAgICogQHJldHVybnMge0FycmF5PE1ldGF0cmFkZXJPcmRlcj59IGhpc3Rvcnkgb3JkZXJzIGZvdW5kXG4gICAqL1xuICBnZXRIaXN0b3J5T3JkZXJzQnlUaWNrZXQoaWQpIHtcbiAgICBsZXQgaGlzdG9yeU9yZGVycyA9IE9iamVjdC52YWx1ZXModGhpcy5faGlzdG9yeU9yZGVyc0J5VGlja2V0W2lkXSB8fCB7fSk7XG4gICAgaGlzdG9yeU9yZGVycy5zb3J0KHRoaXMuX2hpc3RvcnlPcmRlcnNDb21wYXJhdG9yKTtcbiAgICByZXR1cm4gaGlzdG9yeU9yZGVycztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGhpc3Rvcnkgb3JkZXJzIGJ5IHBvc2l0aW9uIGlkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwb3NpdGlvbklkIHBvc2l0aW9uIGlkXG4gICAqIEByZXR1cm5zIHtBcnJheTxNZXRhdHJhZGVyT3JkZXI+fSBoaXN0b3J5IG9yZGVycyBmb3VuZFxuICAgKi9cbiAgZ2V0SGlzdG9yeU9yZGVyc0J5UG9zaXRpb24ocG9zaXRpb25JZCkge1xuICAgIGxldCBoaXN0b3J5T3JkZXJzID0gT2JqZWN0LnZhbHVlcyh0aGlzLl9oaXN0b3J5T3JkZXJzQnlQb3NpdGlvbltwb3NpdGlvbklkXSB8fCB7fSk7XG4gICAgaGlzdG9yeU9yZGVycy5zb3J0KHRoaXMuX2hpc3RvcnlPcmRlcnNDb21wYXJhdG9yKTtcbiAgICByZXR1cm4gaGlzdG9yeU9yZGVycztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGhpc3Rvcnkgb3JkZXJzIGJ5IHRpbWUgcmFuZ2VcbiAgICogQHBhcmFtIHN0YXJ0VGltZSBzdGFydCB0aW1lLCBpbmNsdXNpdmVcbiAgICogQHBhcmFtIGVuZFRpbWUgZW5kIHRpbWUsIGluY2x1c2l2ZVxuICAgKiBAcmV0dXJucyB7QXJyYXk8TWV0YXRyYWRlck9yZGVyPn0gaGlzb3RyeSBvcmRlcnMgZm91bmRcbiAgICovXG4gIGdldEhpc3RvcnlPcmRlcnNCeVRpbWVSYW5nZShzdGFydFRpbWUsIGVuZFRpbWUpIHtcbiAgICBsZXQgaGlzdG9yeU9yZGVycyA9IHRoaXMuX2hpc3RvcnlPcmRlcnNCeVRpbWUuYmV0d2VlbkJvdW5kcyh7XG4gICAgICAkZ3RlOiB7ZG9uZVRpbWU6IHN0YXJ0VGltZSwgaWQ6IDAsIHR5cGU6ICcnLCBzdGF0ZTogJyd9LFxuICAgICAgJGx0ZToge2RvbmVUaW1lOiBlbmRUaW1lLCBpZDogTnVtYmVyLk1BWF9WQUxVRSwgdHlwZTogJycsIHN0YXRlOiAnJ31cbiAgICB9KTtcbiAgICByZXR1cm4gaGlzdG9yeU9yZGVycztcbiAgfVxuXG4gIC8qKlxuICAgKiBJbnZva2VkIHdoZW4gYSBzeW5jaHJvbml6YXRpb24gb2YgaGlzdG9yeSBkZWFscyBvbiBhIE1ldGFUcmFkZXIgYWNjb3VudCBoYXZlIGZpbmlzaGVkIHRvIGluZGljYXRlIHByb2dyZXNzIG9mIGFuXG4gICAqIGluaXRpYWwgdGVybWluYWwgc3RhdGUgc3luY2hyb25pemF0aW9uXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBpbnN0YW5jZUluZGV4IGluZGV4IG9mIGFuIGFjY291bnQgaW5zdGFuY2UgY29ubmVjdGVkXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBzeW5jaHJvbml6YXRpb25JZCBzeW5jaHJvbml6YXRpb24gcmVxdWVzdCBpZFxuICAgKiBAcmV0dXJuIHtQcm9taXNlfSBwcm9taXNlIHdoaWNoIHJlc29sdmVzIHdoZW4gdGhlIGFzeW5jaHJvbm91cyBldmVudCBpcyBwcm9jZXNzZWRcbiAgICovXG4gIGFzeW5jIG9uRGVhbHNTeW5jaHJvbml6ZWQoaW5zdGFuY2VJbmRleCwgc3luY2hyb25pemF0aW9uSWQpIHtcbiAgICBhd2FpdCB0aGlzLl9mbHVzaERhdGFiYXNlKCk7XG4gICAgYXdhaXQgc3VwZXIub25EZWFsc1N5bmNocm9uaXplZChpbnN0YW5jZUluZGV4LCBzeW5jaHJvbml6YXRpb25JZCk7XG4gIH1cblxuICBfcmVzZXQoKSB7XG4gICAgdGhpcy5fb3JkZXJTeW5jaHJvbml6YXRpb25GaW5pc2hlZCA9IHt9O1xuICAgIHRoaXMuX2RlYWxTeW5jaHJvbml6YXRpb25GaW5pc2hlZCA9IHt9O1xuICAgIHRoaXMuX2RlYWxzQnlUaWNrZXQgPSB7fTtcbiAgICB0aGlzLl9kZWFsc0J5UG9zaXRpb24gPSB7fTtcbiAgICB0aGlzLl9oaXN0b3J5T3JkZXJzQnlUaWNrZXQgPSB7fTtcbiAgICB0aGlzLl9oaXN0b3J5T3JkZXJzQnlQb3NpdGlvbiA9IHt9O1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjb21wbGV4aXR5XG4gICAgdGhpcy5faGlzdG9yeU9yZGVyc0NvbXBhcmF0b3IgPSAobzEsIG8yKSA9PiB7XG4gICAgICBsZXQgdGltZURpZmYgPSAobzEuZG9uZVRpbWUgfHwgbmV3IERhdGUoMCkpLmdldFRpbWUoKSAtIChvMi5kb25lVGltZSB8fCBuZXcgRGF0ZSgwKSkuZ2V0VGltZSgpO1xuICAgICAgaWYgKHRpbWVEaWZmID09PSAwKSB7XG4gICAgICAgIGxldCBpZERpZmYgPSBvMS5pZCAtIG8yLmlkO1xuICAgICAgICBpZiAoaWREaWZmID09PSAwKSB7XG4gICAgICAgICAgaWYgKG8xLnR5cGUgPiBvMi50eXBlKSB7XG4gICAgICAgICAgICByZXR1cm4gMTtcbiAgICAgICAgICB9IGVsc2UgaWYgKG8xLnR5cGUgPCBvMi50eXBlKSB7XG4gICAgICAgICAgICByZXR1cm4gLTE7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmIChvMS5zdGF0ZSA+IG8yLnN0YXRlKSB7XG4gICAgICAgICAgICAgIHJldHVybiAxO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChvMS5zdGF0ZSA8IG8yLnN0YXRlKSB7XG4gICAgICAgICAgICAgIHJldHVybiAtMTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIHJldHVybiAwO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXR1cm4gaWREaWZmO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gdGltZURpZmY7XG4gICAgICB9XG4gICAgfTtcbiAgICB0aGlzLl9oaXN0b3J5T3JkZXJzQnlUaW1lID0gbmV3IEFWTFRyZWUoe2NvbXBhcmVLZXlzOiB0aGlzLl9oaXN0b3J5T3JkZXJzQ29tcGFyYXRvcn0pO1xuICAgIHRoaXMuX2RlYWxzQ29tcGFyYXRvciA9IChkMSwgZDIpID0+IHtcbiAgICAgIGxldCB0aW1lRGlmZiA9IChkMS50aW1lIHx8IG5ldyBEYXRlKDApKS5nZXRUaW1lKCkgLSAoZDIudGltZSB8fCBuZXcgRGF0ZSgwKSkuZ2V0VGltZSgpO1xuICAgICAgaWYgKHRpbWVEaWZmID09PSAwKSB7XG4gICAgICAgIGxldCBpZERpZmYgPSBkMS5pZCAtIGQyLmlkO1xuICAgICAgICBpZiAoaWREaWZmID09PSAwKSB7XG4gICAgICAgICAgaWYgKGQxLmVudHJ5VHlwZSA+IGQyLmVudHJ5VHlwZSkge1xuICAgICAgICAgICAgcmV0dXJuIDE7XG4gICAgICAgICAgfSBlbHNlIGlmIChkMS5lbnRyeVR5cGUgPCBkMi5lbnRyeVR5cGUpIHtcbiAgICAgICAgICAgIHJldHVybiAtMTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIDA7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiBpZERpZmY7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiB0aW1lRGlmZjtcbiAgICAgIH1cbiAgICB9O1xuICAgIHRoaXMuX2RlYWxzQnlUaW1lID0gbmV3IEFWTFRyZWUoe2NvbXBhcmVLZXlzOiB0aGlzLl9kZWFsc0NvbXBhcmF0b3J9KTtcbiAgICB0aGlzLl9tYXhIaXN0b3J5T3JkZXJUaW1lID0gbmV3IERhdGUoMCk7XG4gICAgdGhpcy5fbWF4RGVhbFRpbWUgPSBuZXcgRGF0ZSgwKTtcbiAgICB0aGlzLl9uZXdIaXN0b3J5T3JkZXJzID0gW107XG4gICAgdGhpcy5fbmV3RGVhbHMgPSBbXTtcbiAgICBjbGVhclRpbWVvdXQodGhpcy5fZmx1c2hUaW1lb3V0KTtcbiAgICBkZWxldGUgdGhpcy5fZmx1c2hUaW1lb3V0O1xuICB9XG5cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGNvbXBsZXhpdHlcbiAgYXN5bmMgX2FkZERlYWwoZGVhbCwgZXhpc3RpbmcpIHtcbiAgICBsZXQga2V5ID0gdGhpcy5fZ2V0RGVhbEtleShkZWFsKTtcbiAgICB0aGlzLl9kZWFsc0J5VGlja2V0W2RlYWwuaWRdID0gdGhpcy5fZGVhbHNCeVRpY2tldFtkZWFsLmlkXSB8fCB7fTtcbiAgICBsZXQgbmV3RGVhbCA9ICFleGlzdGluZyAmJiAhdGhpcy5fZGVhbHNCeVRpY2tldFtkZWFsLmlkXVtrZXldO1xuICAgIHRoaXMuX2RlYWxzQnlUaWNrZXRbZGVhbC5pZF1ba2V5XSA9IGRlYWw7XG4gICAgaWYgKGRlYWwucG9zaXRpb25JZCkge1xuICAgICAgdGhpcy5fZGVhbHNCeVBvc2l0aW9uW2RlYWwucG9zaXRpb25JZF0gPSB0aGlzLl9kZWFsc0J5UG9zaXRpb25bZGVhbC5wb3NpdGlvbklkXSB8fCB7fTtcbiAgICAgIHRoaXMuX2RlYWxzQnlQb3NpdGlvbltkZWFsLnBvc2l0aW9uSWRdW2tleV0gPSBkZWFsO1xuICAgIH1cbiAgICB0aGlzLl9kZWFsc0J5VGltZS5kZWxldGUoZGVhbCk7XG4gICAgdGhpcy5fZGVhbHNCeVRpbWUuaW5zZXJ0KGRlYWwsIGRlYWwpO1xuICAgIGlmIChkZWFsLnRpbWUgJiYgKCF0aGlzLl9tYXhEZWFsVGltZSB8fCB0aGlzLl9tYXhEZWFsVGltZS5nZXRUaW1lKCkgPCBkZWFsLnRpbWUuZ2V0VGltZSgpKSkge1xuICAgICAgdGhpcy5fbWF4RGVhbFRpbWUgPSBkZWFsLnRpbWU7XG4gICAgfVxuICAgIGlmIChuZXdEZWFsKSB7XG4gICAgICB0aGlzLl9uZXdEZWFscy5wdXNoKGRlYWwpO1xuICAgICAgY2xlYXJUaW1lb3V0KHRoaXMuX2ZsdXNoVGltZW91dCk7XG4gICAgICB0aGlzLl9mbHVzaFRpbWVvdXQgPSBzZXRUaW1lb3V0KHRoaXMuX2ZsdXNoRGF0YWJhc2UuYmluZCh0aGlzKSwgNTAwMCk7XG4gICAgfVxuICB9XG5cbiAgX2dldERlYWxLZXkoZGVhbCkge1xuICAgIHJldHVybiAoZGVhbC50aW1lIHx8IG5ldyBEYXRlKDApKS50b0lTT1N0cmluZygpICsgJzonICsgZGVhbC5pZCArICc6JyArIGRlYWwuZW50cnlUeXBlO1xuICB9XG5cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGNvbXBsZXhpdHlcbiAgYXN5bmMgX2FkZEhpc3RvcnlPcmRlcihoaXN0b3J5T3JkZXIsIGV4aXN0aW5nKSB7XG4gICAgbGV0IGtleSA9IHRoaXMuX2dldEhpc3RvcnlPcmRlcktleShoaXN0b3J5T3JkZXIpO1xuICAgIHRoaXMuX2hpc3RvcnlPcmRlcnNCeVRpY2tldFtoaXN0b3J5T3JkZXIuaWRdID0gdGhpcy5faGlzdG9yeU9yZGVyc0J5VGlja2V0W2hpc3RvcnlPcmRlci5pZF0gfHwge307XG4gICAgbGV0IG5ld0hpc3RvcnlPcmRlciA9ICFleGlzdGluZyAmJiAhdGhpcy5faGlzdG9yeU9yZGVyc0J5VGlja2V0W2hpc3RvcnlPcmRlci5pZF1ba2V5XTtcbiAgICB0aGlzLl9oaXN0b3J5T3JkZXJzQnlUaWNrZXRbaGlzdG9yeU9yZGVyLmlkXVtrZXldID0gaGlzdG9yeU9yZGVyO1xuICAgIGlmIChoaXN0b3J5T3JkZXIucG9zaXRpb25JZCkge1xuICAgICAgdGhpcy5faGlzdG9yeU9yZGVyc0J5UG9zaXRpb25baGlzdG9yeU9yZGVyLnBvc2l0aW9uSWRdID0gdGhpcy5faGlzdG9yeU9yZGVyc0J5UG9zaXRpb25baGlzdG9yeU9yZGVyLnBvc2l0aW9uSWRdIHx8XG4gICAgICAgIHt9O1xuICAgICAgdGhpcy5faGlzdG9yeU9yZGVyc0J5UG9zaXRpb25baGlzdG9yeU9yZGVyLnBvc2l0aW9uSWRdW2tleV0gPSBoaXN0b3J5T3JkZXI7XG4gICAgfVxuICAgIHRoaXMuX2hpc3RvcnlPcmRlcnNCeVRpbWUuZGVsZXRlKGhpc3RvcnlPcmRlcik7XG4gICAgdGhpcy5faGlzdG9yeU9yZGVyc0J5VGltZS5pbnNlcnQoaGlzdG9yeU9yZGVyLCBoaXN0b3J5T3JkZXIpO1xuICAgIGlmIChoaXN0b3J5T3JkZXIuZG9uZVRpbWUgJiYgKCF0aGlzLl9tYXhIaXN0b3J5T3JkZXJUaW1lIHx8XG4gICAgICAgIHRoaXMuX21heEhpc3RvcnlPcmRlclRpbWUuZ2V0VGltZSgpIDwgaGlzdG9yeU9yZGVyLmRvbmVUaW1lLmdldFRpbWUoKSkpIHtcbiAgICAgIHRoaXMuX21heEhpc3RvcnlPcmRlclRpbWUgPSBoaXN0b3J5T3JkZXIuZG9uZVRpbWU7XG4gICAgfVxuICAgIGlmIChuZXdIaXN0b3J5T3JkZXIpIHtcbiAgICAgIHRoaXMuX25ld0hpc3RvcnlPcmRlcnMucHVzaChoaXN0b3J5T3JkZXIpO1xuICAgICAgY2xlYXJUaW1lb3V0KHRoaXMuX2ZsdXNoVGltZW91dCk7XG4gICAgICB0aGlzLl9mbHVzaFRpbWVvdXQgPSBzZXRUaW1lb3V0KHRoaXMuX2ZsdXNoRGF0YWJhc2UuYmluZCh0aGlzKSwgNTAwMCk7XG4gICAgfVxuICB9XG5cbiAgX2dldEhpc3RvcnlPcmRlcktleShoaXN0b3J5T3JkZXIpIHtcbiAgICByZXR1cm4gKGhpc3RvcnlPcmRlci5kb25lVGltZSB8fCBuZXcgRGF0ZSgwKSkudG9JU09TdHJpbmcoKSArICc6JyArIGhpc3RvcnlPcmRlci5pZCArICc6JyArXG4gICAgICBoaXN0b3J5T3JkZXIudHlwZSArICc6JyArIGhpc3RvcnlPcmRlci5zdGF0ZTtcbiAgfVxuXG4gIGFzeW5jIF9mbHVzaERhdGFiYXNlKCkge1xuICAgIGlmICh0aGlzLl9mbHVzaFByb21pc2UpIHtcbiAgICAgIGF3YWl0IHRoaXMuX2ZsdXNoUHJvbWlzZTtcbiAgICB9XG4gICAgaWYgKHRoaXMuX2ZsdXNoUnVubmluZykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLl9mbHVzaFJ1bm5pbmcgPSB0cnVlO1xuICAgIGxldCByZXNvbHZlO1xuICAgIHRoaXMuX2ZsdXNoUHJvbWlzZSA9IG5ldyBQcm9taXNlKHJlcyA9PiByZXNvbHZlID0gcmVzKTtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5faGlzdG9yeURhdGFiYXNlLmZsdXNoKHRoaXMuX2FjY291bnRJZCwgdGhpcy5fYXBwbGljYXRpb24sIHRoaXMuX25ld0hpc3RvcnlPcmRlcnMsIHRoaXMuX25ld0RlYWxzKTtcbiAgICAgIHRoaXMuX25ld0hpc3RvcnlPcmRlcnMgPSBbXTtcbiAgICAgIHRoaXMuX25ld0RlYWxzID0gW107XG4gICAgICB0aGlzLl9sb2dnZXIuZGVidWcoYCR7dGhpcy5fYWNjb3VudElkfTogZmx1c2hlZCBoaXN0b3J5IGRiYCk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICB0aGlzLl9sb2dnZXIud2FybihgJHt0aGlzLl9hY2NvdW50SWR9OiBlcnJvciBmbHVzaGluZyBoaXN0b3J5IGRiYCwgZXJyKTtcbiAgICAgIHRoaXMuX2ZsdXNoVGltZW91dCA9IHNldFRpbWVvdXQodGhpcy5fZmx1c2hEYXRhYmFzZS5iaW5kKHRoaXMpLCAxNTAwMCk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIHJlc29sdmUoKTtcbiAgICAgIHRoaXMuX2ZsdXNoUnVubmluZyA9IGZhbHNlO1xuICAgIH1cbiAgfVxuXG59XG4iXSwibmFtZXMiOlsiSGlzdG9yeVN0b3JhZ2UiLCJIaXN0b3J5RGF0YWJhc2UiLCJBVkxUcmVlIiwiTG9nZ2VyTWFuYWdlciIsIk1lbW9yeUhpc3RvcnlTdG9yYWdlIiwiaW5pdGlhbGl6ZSIsImFjY291bnRJZCIsImFwcGxpY2F0aW9uIiwiZGVhbHMiLCJoaXN0b3J5T3JkZXJzIiwiX2hpc3RvcnlEYXRhYmFzZSIsImxvYWRIaXN0b3J5IiwiZGVhbCIsIl9hZGREZWFsIiwiaGlzdG9yeU9yZGVyIiwiX2FkZEhpc3RvcnlPcmRlciIsImNsZWFyIiwiX3Jlc2V0IiwiX2FjY291bnRJZCIsIl9hcHBsaWNhdGlvbiIsImxhc3RIaXN0b3J5T3JkZXJUaW1lIiwiaW5zdGFuY2VOdW1iZXIiLCJfbWF4SGlzdG9yeU9yZGVyVGltZSIsImxhc3REZWFsVGltZSIsIl9tYXhEZWFsVGltZSIsIm9uSGlzdG9yeU9yZGVyQWRkZWQiLCJpbnN0YW5jZUluZGV4Iiwib25EZWFsQWRkZWQiLCJnZXREZWFsc0J5VGltZVJhbmdlIiwiRGF0ZSIsImdldERlYWxzQnlUaWNrZXQiLCJpZCIsIk9iamVjdCIsInZhbHVlcyIsIl9kZWFsc0J5VGlja2V0Iiwic29ydCIsIl9kZWFsc0NvbXBhcmF0b3IiLCJnZXREZWFsc0J5UG9zaXRpb24iLCJwb3NpdGlvbklkIiwiX2RlYWxzQnlQb3NpdGlvbiIsInN0YXJ0VGltZSIsImVuZFRpbWUiLCJfZGVhbHNCeVRpbWUiLCJiZXR3ZWVuQm91bmRzIiwiJGd0ZSIsInRpbWUiLCJlbnRyeVR5cGUiLCIkbHRlIiwiTnVtYmVyIiwiTUFYX1ZBTFVFIiwiZ2V0SGlzdG9yeU9yZGVyc0J5VGltZVJhbmdlIiwiZ2V0SGlzdG9yeU9yZGVyc0J5VGlja2V0IiwiX2hpc3RvcnlPcmRlcnNCeVRpY2tldCIsIl9oaXN0b3J5T3JkZXJzQ29tcGFyYXRvciIsImdldEhpc3RvcnlPcmRlcnNCeVBvc2l0aW9uIiwiX2hpc3RvcnlPcmRlcnNCeVBvc2l0aW9uIiwiX2hpc3RvcnlPcmRlcnNCeVRpbWUiLCJkb25lVGltZSIsInR5cGUiLCJzdGF0ZSIsIm9uRGVhbHNTeW5jaHJvbml6ZWQiLCJzeW5jaHJvbml6YXRpb25JZCIsIl9mbHVzaERhdGFiYXNlIiwiX29yZGVyU3luY2hyb25pemF0aW9uRmluaXNoZWQiLCJfZGVhbFN5bmNocm9uaXphdGlvbkZpbmlzaGVkIiwibzEiLCJvMiIsInRpbWVEaWZmIiwiZ2V0VGltZSIsImlkRGlmZiIsImNvbXBhcmVLZXlzIiwiZDEiLCJkMiIsIl9uZXdIaXN0b3J5T3JkZXJzIiwiX25ld0RlYWxzIiwiY2xlYXJUaW1lb3V0IiwiX2ZsdXNoVGltZW91dCIsImV4aXN0aW5nIiwia2V5IiwiX2dldERlYWxLZXkiLCJuZXdEZWFsIiwiZGVsZXRlIiwiaW5zZXJ0IiwicHVzaCIsInNldFRpbWVvdXQiLCJiaW5kIiwidG9JU09TdHJpbmciLCJfZ2V0SGlzdG9yeU9yZGVyS2V5IiwibmV3SGlzdG9yeU9yZGVyIiwiX2ZsdXNoUHJvbWlzZSIsIl9mbHVzaFJ1bm5pbmciLCJyZXNvbHZlIiwiUHJvbWlzZSIsInJlcyIsImZsdXNoIiwiX2xvZ2dlciIsImRlYnVnIiwiZXJyIiwid2FybiIsImNvbnN0cnVjdG9yIiwiZ2V0SW5zdGFuY2UiLCJnZXRMb2dnZXIiXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFFQSxPQUFPQSxvQkFBb0IsbUJBQW1CO0FBQzlDLE9BQU9DLHFCQUFxQiwwQkFBMEI7QUFDdEQsU0FBU0MsT0FBTyxRQUFRLGdDQUErQjtBQUN2RCxPQUFPQyxtQkFBbUIsWUFBWTtBQUt2QixJQUFBLEFBQU1DLHVCQUFOLE1BQU1BLDZCQUE2Qko7SUFZaEQ7Ozs7R0FJQyxHQUNELEFBQU1LLFdBQVdDLFNBQVMsRUFBRUMsY0FBYyxTQUFTOztlQUFuRCxvQkFBQTtZQUNFLE1BQU0sNEJBQUEsWUFBaUJELFdBQVdDO1lBQ2xDLElBQUksRUFBQ0MsS0FBSyxFQUFFQyxhQUFhLEVBQUMsR0FBRyxNQUFNLE1BQUtDLGdCQUFnQixDQUFDQyxXQUFXLENBQUNMLFdBQVdDO1lBQ2hGLEtBQUssSUFBSUssUUFBUUosTUFBTztnQkFDdEIsTUFBTSxNQUFLSyxRQUFRLENBQUNELE1BQU07WUFDNUI7WUFDQSxLQUFLLElBQUlFLGdCQUFnQkwsY0FBZTtnQkFDdEMsTUFBTSxNQUFLTSxnQkFBZ0IsQ0FBQ0QsY0FBYztZQUM1QztRQUNGOztJQUVBOzs7R0FHQyxHQUNELEFBQU1FOztlQUFOLG9CQUFBO1lBQ0UsTUFBS0MsTUFBTTtZQUNYLE1BQU0sTUFBS1AsZ0JBQWdCLENBQUNNLEtBQUssQ0FBQyxNQUFLRSxVQUFVLEVBQUUsTUFBS0MsWUFBWTtRQUN0RTs7SUFFQTs7OztHQUlDLEdBQ0RDLHFCQUFxQkMsY0FBYyxFQUFFO1FBQ25DLE9BQU8sSUFBSSxDQUFDQyxvQkFBb0I7SUFDbEM7SUFF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