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
JavaScript
'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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