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)
316 lines (315 loc) • 39.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "default", {
enumerable: true,
get: function() {
return MemoryHistoryStorage;
}
});
const _historyStorage = /*#__PURE__*/ _interop_require_default(require("./historyStorage"));
const _index = /*#__PURE__*/ _interop_require_default(require("./historyDatabase/index"));
const _binarysearchtree = require("binary-search-tree");
const _logger = /*#__PURE__*/ _interop_require_default(require("../logger"));
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
let MemoryHistoryStorage = class MemoryHistoryStorage extends _historyStorage.default {
/**
* Initializes the storage and loads required data from a persistent storage
* @param {string} accountId account id
* @param {string} [application] application. Default is MetaApi
*/ async initialize(accountId, application = "MetaApi") {
await super.initialize(accountId, application);
let { deals, historyOrders } = await this._historyDatabase.loadHistory(accountId, application);
for (let deal of deals){
await this._addDeal(deal, true);
}
for (let historyOrder of historyOrders){
await this._addHistoryOrder(historyOrder, true);
}
}
/**
* Resets the storage. Intended for use in tests
* @returns {Promise} promise when the history is removed
*/ async clear() {
this._reset();
await 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
*/ async onHistoryOrderAdded(instanceIndex, historyOrder) {
await 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
*/ async onDealAdded(instanceIndex, deal) {
await 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
*/ async onDealsSynchronized(instanceIndex, synchronizationId) {
await this._flushDatabase();
await super.onDealsSynchronized(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 _binarysearchtree.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 _binarysearchtree.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
async _addDeal(deal, existing) {
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
async _addHistoryOrder(historyOrder, existing) {
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;
}
async _flushDatabase() {
if (this._flushPromise) {
await this._flushPromise;
}
if (this._flushRunning) {
return;
}
this._flushRunning = true;
let resolve;
this._flushPromise = new Promise((res)=>resolve = res);
try {
await 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 = _index.default.getInstance();
this._reset();
this._logger = _logger.default.getLogger("MemoryHistoryStorage");
}
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCBIaXN0b3J5U3RvcmFnZSBmcm9tICcuL2hpc3RvcnlTdG9yYWdlJztcbmltcG9ydCBIaXN0b3J5RGF0YWJhc2UgZnJvbSAnLi9oaXN0b3J5RGF0YWJhc2UvaW5kZXgnO1xuaW1wb3J0IHsgQVZMVHJlZSB9IGZyb20gJ2JpbmFyeS1zZWFyY2gtdHJlZSc7XG5pbXBvcnQgTG9nZ2VyTWFuYWdlciBmcm9tICcuLi9sb2dnZXInO1xuXG4vKipcbiAqIEhpc3Rvcnkgc3RvcmFnZSB3aGljaCBzdG9yZXMgTWV0YVRyYWRlciBoaXN0b3J5IGluIFJBTVxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBNZW1vcnlIaXN0b3J5U3RvcmFnZSBleHRlbmRzIEhpc3RvcnlTdG9yYWdlIHtcblxuICAvKipcbiAgICogQ29uc3RydWN0cyB0aGUgaW4tbWVtb3J5IGhpc3Rvcnkgc3RvcmUgaW5zdGFuY2VcbiAgICovXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHN1cGVyKCk7XG4gICAgdGhpcy5faGlzdG9yeURhdGFiYXNlID0gSGlzdG9yeURhdGFiYXNlLmdldEluc3RhbmNlKCk7XG4gICAgdGhpcy5fcmVzZXQoKTtcbiAgICB0aGlzLl9sb2dnZXIgPSBMb2dnZXJNYW5hZ2VyLmdldExvZ2dlcignTWVtb3J5SGlzdG9yeVN0b3JhZ2UnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbml0aWFsaXplcyB0aGUgc3RvcmFnZSBhbmQgbG9hZHMgcmVxdWlyZWQgZGF0YSBmcm9tIGEgcGVyc2lzdGVudCBzdG9yYWdlXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhY2NvdW50SWQgYWNjb3VudCBpZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gW2FwcGxpY2F0aW9uXSBhcHBsaWNhdGlvbi4gRGVmYXVsdCBpcyBNZXRhQXBpXG4gICAqL1xuICBhc3luYyBpbml0aWFsaXplKGFjY291bnRJZCwgYXBwbGljYXRpb24gPSAnTWV0YUFwaScpIHtcbiAgICBhd2FpdCBzdXBlci5pbml0aWFsaXplKGFjY291bnRJZCwgYXBwbGljYXRpb24pO1xuICAgIGxldCB7ZGVhbHMsIGhpc3RvcnlPcmRlcnN9ID0gYXdhaXQgdGhpcy5faGlzdG9yeURhdGFiYXNlLmxvYWRIaXN0b3J5KGFjY291bnRJZCwgYXBwbGljYXRpb24pO1xuICAgIGZvciAobGV0IGRlYWwgb2YgZGVhbHMpIHtcbiAgICAgIGF3YWl0IHRoaXMuX2FkZERlYWwoZGVhbCwgdHJ1ZSk7XG4gICAgfVxuICAgIGZvciAobGV0IGhpc3RvcnlPcmRlciBvZiBoaXN0b3J5T3JkZXJzKSB7XG4gICAgICBhd2FpdCB0aGlzLl9hZGRIaXN0b3J5T3JkZXIoaGlzdG9yeU9yZGVyLCB0cnVlKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVzZXRzIHRoZSBzdG9yYWdlLiBJbnRlbmRlZCBmb3IgdXNlIGluIHRlc3RzXG4gICAqIEByZXR1cm5zIHtQcm9taXNlfSBwcm9taXNlIHdoZW4gdGhlIGhpc3RvcnkgaXMgcmVtb3ZlZFxuICAgKi9cbiAgYXN5bmMgY2xlYXIoKSB7XG4gICAgdGhpcy5fcmVzZXQoKTtcbiAgICBhd2FpdCB0aGlzLl9oaXN0b3J5RGF0YWJhc2UuY2xlYXIodGhpcy5fYWNjb3VudElkLCB0aGlzLl9hcHBsaWNhdGlvbik7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgdGltZSBvZiB0aGUgbGFzdCBoaXN0b3J5IG9yZGVyIHJlY29yZCBzdG9yZWQgaW4gdGhlIGhpc3Rvcnkgc3RvcmFnZVxuICAgKiBAcGFyYW0ge051bWJlcn0gW2luc3RhbmNlTnVtYmVyXSBpbmRleCBvZiBhbiBhY2NvdW50IGluc3RhbmNlIGNvbm5lY3RlZFxuICAgKiBAcmV0dXJucyB7RGF0ZX0gdGhlIHRpbWUgb2YgdGhlIGxhc3QgaGlzdG9yeSBvcmRlciByZWNvcmQgc3RvcmVkIGluIHRoZSBoaXN0b3J5IHN0b3JhZ2VcbiAgICovXG4gIGxhc3RIaXN0b3J5T3JkZXJUaW1lKGluc3RhbmNlTnVtYmVyKSB7XG4gICAgcmV0dXJuIHRoaXMuX21heEhpc3RvcnlPcmRlclRpbWU7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgdGltZSBvZiB0aGUgbGFzdCBoaXN0b3J5IGRlYWwgcmVjb3JkIHN0b3JlZCBpbiB0aGUgaGlzdG9yeSBzdG9yYWdlXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBbaW5zdGFuY2VOdW1iZXJdIGluZGV4IG9mIGFuIGFjY291bnQgaW5zdGFuY2UgY29ubmVjdGVkXG4gICAqIEByZXR1cm5zIHtEYXRlfSB0aGUgdGltZSBvZiB0aGUgbGFzdCBoaXN0b3J5IGRlYWwgcmVjb3JkIHN0b3JlZCBpbiB0aGUgaGlzdG9yeSBzdG9yYWdlXG4gICAqL1xuICBsYXN0RGVhbFRpbWUoaW5zdGFuY2VOdW1iZXIpIHtcbiAgICByZXR1cm4gdGhpcy5fbWF4RGVhbFRpbWU7XG4gIH1cblxuICAvKipcbiAgICogSW52b2tlZCB3aGVuIGEgbmV3IE1ldGFUcmFkZXIgaGlzdG9yeSBvcmRlciBpcyBhZGRlZFxuICAgKiBAcGFyYW0ge1N0cmluZ30gaW5zdGFuY2VJbmRleCBpbmRleCBvZiBhbiBhY2NvdW50IGluc3RhbmNlIGNvbm5lY3RlZFxuICAgKiBAcGFyYW0ge01ldGF0cmFkZXJPcmRlcn0gaGlzdG9yeU9yZGVyIG5ldyBNZXRhVHJhZGVyIGhpc3Rvcnkgb3JkZXJcbiAgICovXG4gIGFzeW5jIG9uSGlzdG9yeU9yZGVyQWRkZWQoaW5zdGFuY2VJbmRleCwgaGlzdG9yeU9yZGVyKSB7XG4gICAgYXdhaXQgdGhpcy5fYWRkSGlzdG9yeU9yZGVyKGhpc3RvcnlPcmRlcik7XG4gIH1cblxuICAvKipcbiAgICogSW52b2tlZCB3aGVuIGEgbmV3IE1ldGFUcmFkZXIgaGlzdG9yeSBkZWFsIGlzIGFkZGVkXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBpbnN0YW5jZUluZGV4IGluZGV4IG9mIGFuIGFjY291bnQgaW5zdGFuY2UgY29ubmVjdGVkXG4gICAqIEBwYXJhbSB7TWV0YXRyYWRlckRlYWx9IGRlYWwgbmV3IE1ldGFUcmFkZXIgaGlzdG9yeSBkZWFsXG4gICAqL1xuICBhc3luYyBvbkRlYWxBZGRlZChpbnN0YW5jZUluZGV4LCBkZWFsKSB7XG4gICAgYXdhaXQgdGhpcy5fYWRkRGVhbChkZWFsKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGFsbCBkZWFsc1xuICAgKiBAcmV0dXJucyB7QXJyYXk8TWV0YXRyYWRlckRlYWw+fSBhbGwgZGVhbHNcbiAgICovXG4gIGdldCBkZWFscygpIHtcbiAgICByZXR1cm4gdGhpcy5nZXREZWFsc0J5VGltZVJhbmdlKG5ldyBEYXRlKDApLCBuZXcgRGF0ZSg4NjQwMDAwMDAwMDAwMDAwKSk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBkZWFscyBieSB0aWNrZXQgaWRcbiAgICogQHBhcmFtIHtzdHJpbmd9IGlkIHRpY2tldCBpZFxuICAgKiBAcmV0dXJucyB7QXJyYXk8TWV0YXRyYWRlckRlYWw+fSBkZWFscyBmb3VuZFxuICAgKi9cbiAgZ2V0RGVhbHNCeVRpY2tldChpZCkge1xuICAgIGxldCBkZWFscyA9IE9iamVjdC52YWx1ZXModGhpcy5fZGVhbHNCeVRpY2tldFtpZF0gfHwge30pO1xuICAgIGRlYWxzLnNvcnQodGhpcy5fZGVhbHNDb21wYXJhdG9yKTtcbiAgICByZXR1cm4gZGVhbHM7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBkZWFscyBieSBwb3NpdGlvbiBpZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gcG9zaXRpb25JZCBwb3NpdGlvbiBpZFxuICAgKiBAcmV0dXJucyB7QXJyYXk8TWV0YXRyYWRlckRlYWw+fSBkZWFscyBmb3VuZFxuICAgKi9cbiAgZ2V0RGVhbHNCeVBvc2l0aW9uKHBvc2l0aW9uSWQpIHtcbiAgICBsZXQgZGVhbHMgPSBPYmplY3QudmFsdWVzKHRoaXMuX2RlYWxzQnlQb3NpdGlvbltwb3NpdGlvbklkXSB8fCB7fSk7XG4gICAgZGVhbHMuc29ydCh0aGlzLl9kZWFsc0NvbXBhcmF0b3IpO1xuICAgIHJldHVybiBkZWFscztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGRlYWxzIGJ5IHRpbWUgcmFuZ2VcbiAgICogQHBhcmFtIHN0YXJ0VGltZSBzdGFydCB0aW1lLCBpbmNsdXNpdmVcbiAgICogQHBhcmFtIGVuZFRpbWUgZW5kIHRpbWUsIGluY2x1c2l2ZVxuICAgKiBAcmV0dXJucyB7QXJyYXk8TWV0YXRyYWRlckRlYWw+fSBkZWFscyBmb3VuZFxuICAgKi9cbiAgZ2V0RGVhbHNCeVRpbWVSYW5nZShzdGFydFRpbWUsIGVuZFRpbWUpIHtcbiAgICBsZXQgZGVhbHMgPSB0aGlzLl9kZWFsc0J5VGltZS5iZXR3ZWVuQm91bmRzKHtcbiAgICAgICRndGU6IHt0aW1lOiBzdGFydFRpbWUsIGlkOiAwLCBlbnRyeVR5cGU6ICcnfSxcbiAgICAgICRsdGU6IHt0aW1lOiBlbmRUaW1lLCBpZDogTnVtYmVyLk1BWF9WQUxVRSwgZW50cnlUeXBlOiAnJ31cbiAgICB9KTtcbiAgICByZXR1cm4gZGVhbHM7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBhbGwgaGlzdG9yeSBvcmRlcnNcbiAgICogQHJldHVybnMge0FycmF5PE1ldGF0cmFkZXJPcmRlcj59IGFsbCBoaXN0b3J5IG9yZGVyc1xuICAgKi9cbiAgZ2V0IGhpc3RvcnlPcmRlcnMoKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0SGlzdG9yeU9yZGVyc0J5VGltZVJhbmdlKG5ldyBEYXRlKDApLCBuZXcgRGF0ZSg4NjQwMDAwMDAwMDAwMDAwKSk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBoaXN0b3J5IG9yZGVycyBieSB0aWNrZXQgaWRcbiAgICogQHBhcmFtIHtzdHJpbmd9IGlkIHRpY2tldCBpZFxuICAgKiBAcmV0dXJucyB7QXJyYXk8TWV0YXRyYWRlck9yZGVyPn0gaGlzdG9yeSBvcmRlcnMgZm91bmRcbiAgICovXG4gIGdldEhpc3RvcnlPcmRlcnNCeVRpY2tldChpZCkge1xuICAgIGxldCBoaXN0b3J5T3JkZXJzID0gT2JqZWN0LnZhbHVlcyh0aGlzLl9oaXN0b3J5T3JkZXJzQnlUaWNrZXRbaWRdIHx8IHt9KTtcbiAgICBoaXN0b3J5T3JkZXJzLnNvcnQodGhpcy5faGlzdG9yeU9yZGVyc0NvbXBhcmF0b3IpO1xuICAgIHJldHVybiBoaXN0b3J5T3JkZXJzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgaGlzdG9yeSBvcmRlcnMgYnkgcG9zaXRpb24gaWRcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBvc2l0aW9uSWQgcG9zaXRpb24gaWRcbiAgICogQHJldHVybnMge0FycmF5PE1ldGF0cmFkZXJPcmRlcj59IGhpc3Rvcnkgb3JkZXJzIGZvdW5kXG4gICAqL1xuICBnZXRIaXN0b3J5T3JkZXJzQnlQb3NpdGlvbihwb3NpdGlvbklkKSB7XG4gICAgbGV0IGhpc3RvcnlPcmRlcnMgPSBPYmplY3QudmFsdWVzKHRoaXMuX2hpc3RvcnlPcmRlcnNCeVBvc2l0aW9uW3Bvc2l0aW9uSWRdIHx8IHt9KTtcbiAgICBoaXN0b3J5T3JkZXJzLnNvcnQodGhpcy5faGlzdG9yeU9yZGVyc0NvbXBhcmF0b3IpO1xuICAgIHJldHVybiBoaXN0b3J5T3JkZXJzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgaGlzdG9yeSBvcmRlcnMgYnkgdGltZSByYW5nZVxuICAgKiBAcGFyYW0gc3RhcnRUaW1lIHN0YXJ0IHRpbWUsIGluY2x1c2l2ZVxuICAgKiBAcGFyYW0gZW5kVGltZSBlbmQgdGltZSwgaW5jbHVzaXZlXG4gICAqIEByZXR1cm5zIHtBcnJheTxNZXRhdHJhZGVyT3JkZXI+fSBoaXNvdHJ5IG9yZGVycyBmb3VuZFxuICAgKi9cbiAgZ2V0SGlzdG9yeU9yZGVyc0J5VGltZVJhbmdlKHN0YXJ0VGltZSwgZW5kVGltZSkge1xuICAgIGxldCBoaXN0b3J5T3JkZXJzID0gdGhpcy5faGlzdG9yeU9yZGVyc0J5VGltZS5iZXR3ZWVuQm91bmRzKHtcbiAgICAgICRndGU6IHtkb25lVGltZTogc3RhcnRUaW1lLCBpZDogMCwgdHlwZTogJycsIHN0YXRlOiAnJ30sXG4gICAgICAkbHRlOiB7ZG9uZVRpbWU6IGVuZFRpbWUsIGlkOiBOdW1iZXIuTUFYX1ZBTFVFLCB0eXBlOiAnJywgc3RhdGU6ICcnfVxuICAgIH0pO1xuICAgIHJldHVybiBoaXN0b3J5T3JkZXJzO1xuICB9XG5cbiAgLyoqXG4gICAqIEludm9rZWQgd2hlbiBhIHN5bmNocm9uaXphdGlvbiBvZiBoaXN0b3J5IGRlYWxzIG9uIGEgTWV0YVRyYWRlciBhY2NvdW50IGhhdmUgZmluaXNoZWQgdG8gaW5kaWNhdGUgcHJvZ3Jlc3Mgb2YgYW5cbiAgICogaW5pdGlhbCB0ZXJtaW5hbCBzdGF0ZSBzeW5jaHJvbml6YXRpb25cbiAgICogQHBhcmFtIHtTdHJpbmd9IGluc3RhbmNlSW5kZXggaW5kZXggb2YgYW4gYWNjb3VudCBpbnN0YW5jZSBjb25uZWN0ZWRcbiAgICogQHBhcmFtIHtTdHJpbmd9IHN5bmNocm9uaXphdGlvbklkIHN5bmNocm9uaXphdGlvbiByZXF1ZXN0IGlkXG4gICAqIEByZXR1cm4ge1Byb21pc2V9IHByb21pc2Ugd2hpY2ggcmVzb2x2ZXMgd2hlbiB0aGUgYXN5bmNocm9ub3VzIGV2ZW50IGlzIHByb2Nlc3NlZFxuICAgKi9cbiAgYXN5bmMgb25EZWFsc1N5bmNocm9uaXplZChpbnN0YW5jZUluZGV4LCBzeW5jaHJvbml6YXRpb25JZCkge1xuICAgIGF3YWl0IHRoaXMuX2ZsdXNoRGF0YWJhc2UoKTtcbiAgICBhd2FpdCBzdXBlci5vbkRlYWxzU3luY2hyb25pemVkKGluc3RhbmNlSW5kZXgsIHN5bmNocm9uaXphdGlvbklkKTtcbiAgfVxuXG4gIF9yZXNldCgpIHtcbiAgICB0aGlzLl9vcmRlclN5bmNocm9uaXphdGlvbkZpbmlzaGVkID0ge307XG4gICAgdGhpcy5fZGVhbFN5bmNocm9uaXphdGlvbkZpbmlzaGVkID0ge307XG4gICAgdGhpcy5fZGVhbHNCeVRpY2tldCA9IHt9O1xuICAgIHRoaXMuX2RlYWxzQnlQb3NpdGlvbiA9IHt9O1xuICAgIHRoaXMuX2hpc3RvcnlPcmRlcnNCeVRpY2tldCA9IHt9O1xuICAgIHRoaXMuX2hpc3RvcnlPcmRlcnNCeVBvc2l0aW9uID0ge307XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGNvbXBsZXhpdHlcbiAgICB0aGlzLl9oaXN0b3J5T3JkZXJzQ29tcGFyYXRvciA9IChvMSwgbzIpID0+IHtcbiAgICAgIGxldCB0aW1lRGlmZiA9IChvMS5kb25lVGltZSB8fCBuZXcgRGF0ZSgwKSkuZ2V0VGltZSgpIC0gKG8yLmRvbmVUaW1lIHx8IG5ldyBEYXRlKDApKS5nZXRUaW1lKCk7XG4gICAgICBpZiAodGltZURpZmYgPT09IDApIHtcbiAgICAgICAgbGV0IGlkRGlmZiA9IG8xLmlkIC0gbzIuaWQ7XG4gICAgICAgIGlmIChpZERpZmYgPT09IDApIHtcbiAgICAgICAgICBpZiAobzEudHlwZSA+IG8yLnR5cGUpIHtcbiAgICAgICAgICAgIHJldHVybiAxO1xuICAgICAgICAgIH0gZWxzZSBpZiAobzEudHlwZSA8IG8yLnR5cGUpIHtcbiAgICAgICAgICAgIHJldHVybiAtMTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKG8xLnN0YXRlID4gbzIuc3RhdGUpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIDE7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKG8xLnN0YXRlIDwgbzIuc3RhdGUpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIC0xO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgcmV0dXJuIDA7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiBpZERpZmY7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiB0aW1lRGlmZjtcbiAgICAgIH1cbiAgICB9O1xuICAgIHRoaXMuX2hpc3RvcnlPcmRlcnNCeVRpbWUgPSBuZXcgQVZMVHJlZSh7Y29tcGFyZUtleXM6IHRoaXMuX2hpc3RvcnlPcmRlcnNDb21wYXJhdG9yfSk7XG4gICAgdGhpcy5fZGVhbHNDb21wYXJhdG9yID0gKGQxLCBkMikgPT4ge1xuICAgICAgbGV0IHRpbWVEaWZmID0gKGQxLnRpbWUgfHwgbmV3IERhdGUoMCkpLmdldFRpbWUoKSAtIChkMi50aW1lIHx8IG5ldyBEYXRlKDApKS5nZXRUaW1lKCk7XG4gICAgICBpZiAodGltZURpZmYgPT09IDApIHtcbiAgICAgICAgbGV0IGlkRGlmZiA9IGQxLmlkIC0gZDIuaWQ7XG4gICAgICAgIGlmIChpZERpZmYgPT09IDApIHtcbiAgICAgICAgICBpZiAoZDEuZW50cnlUeXBlID4gZDIuZW50cnlUeXBlKSB7XG4gICAgICAgICAgICByZXR1cm4gMTtcbiAgICAgICAgICB9IGVsc2UgaWYgKGQxLmVudHJ5VHlwZSA8IGQyLmVudHJ5VHlwZSkge1xuICAgICAgICAgICAgcmV0dXJuIC0xO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gMDtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJuIGlkRGlmZjtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIHRpbWVEaWZmO1xuICAgICAgfVxuICAgIH07XG4gICAgdGhpcy5fZGVhbHNCeVRpbWUgPSBuZXcgQVZMVHJlZSh7Y29tcGFyZUtleXM6IHRoaXMuX2RlYWxzQ29tcGFyYXRvcn0pO1xuICAgIHRoaXMuX21heEhpc3RvcnlPcmRlclRpbWUgPSBuZXcgRGF0ZSgwKTtcbiAgICB0aGlzLl9tYXhEZWFsVGltZSA9IG5ldyBEYXRlKDApO1xuICAgIHRoaXMuX25ld0hpc3RvcnlPcmRlcnMgPSBbXTtcbiAgICB0aGlzLl9uZXdEZWFscyA9IFtdO1xuICAgIGNsZWFyVGltZW91dCh0aGlzLl9mbHVzaFRpbWVvdXQpO1xuICAgIGRlbGV0ZSB0aGlzLl9mbHVzaFRpbWVvdXQ7XG4gIH1cblxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY29tcGxleGl0eVxuICBhc3luYyBfYWRkRGVhbChkZWFsLCBleGlzdGluZykge1xuICAgIGxldCBrZXkgPSB0aGlzLl9nZXREZWFsS2V5KGRlYWwpO1xuICAgIHRoaXMuX2RlYWxzQnlUaWNrZXRbZGVhbC5pZF0gPSB0aGlzLl9kZWFsc0J5VGlja2V0W2RlYWwuaWRdIHx8IHt9O1xuICAgIGxldCBuZXdEZWFsID0gIWV4aXN0aW5nICYmICF0aGlzLl9kZWFsc0J5VGlja2V0W2RlYWwuaWRdW2tleV07XG4gICAgdGhpcy5fZGVhbHNCeVRpY2tldFtkZWFsLmlkXVtrZXldID0gZGVhbDtcbiAgICBpZiAoZGVhbC5wb3NpdGlvbklkKSB7XG4gICAgICB0aGlzLl9kZWFsc0J5UG9zaXRpb25bZGVhbC5wb3NpdGlvbklkXSA9IHRoaXMuX2RlYWxzQnlQb3NpdGlvbltkZWFsLnBvc2l0aW9uSWRdIHx8IHt9O1xuICAgICAgdGhpcy5fZGVhbHNCeVBvc2l0aW9uW2RlYWwucG9zaXRpb25JZF1ba2V5XSA9IGRlYWw7XG4gICAgfVxuICAgIHRoaXMuX2RlYWxzQnlUaW1lLmRlbGV0ZShkZWFsKTtcbiAgICB0aGlzLl9kZWFsc0J5VGltZS5pbnNlcnQoZGVhbCwgZGVhbCk7XG4gICAgaWYgKGRlYWwudGltZSAmJiAoIXRoaXMuX21heERlYWxUaW1lIHx8IHRoaXMuX21heERlYWxUaW1lLmdldFRpbWUoKSA8IGRlYWwudGltZS5nZXRUaW1lKCkpKSB7XG4gICAgICB0aGlzLl9tYXhEZWFsVGltZSA9IGRlYWwudGltZTtcbiAgICB9XG4gICAgaWYgKG5ld0RlYWwpIHtcbiAgICAgIHRoaXMuX25ld0RlYWxzLnB1c2goZGVhbCk7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5fZmx1c2hUaW1lb3V0KTtcbiAgICAgIHRoaXMuX2ZsdXNoVGltZW91dCA9IHNldFRpbWVvdXQodGhpcy5fZmx1c2hEYXRhYmFzZS5iaW5kKHRoaXMpLCA1MDAwKTtcbiAgICB9XG4gIH1cblxuICBfZ2V0RGVhbEtleShkZWFsKSB7XG4gICAgcmV0dXJuIChkZWFsLnRpbWUgfHwgbmV3IERhdGUoMCkpLnRvSVNPU3RyaW5nKCkgKyAnOicgKyBkZWFsLmlkICsgJzonICsgZGVhbC5lbnRyeVR5cGU7XG4gIH1cblxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY29tcGxleGl0eVxuICBhc3luYyBfYWRkSGlzdG9yeU9yZGVyKGhpc3RvcnlPcmRlciwgZXhpc3RpbmcpIHtcbiAgICBsZXQga2V5ID0gdGhpcy5fZ2V0SGlzdG9yeU9yZGVyS2V5KGhpc3RvcnlPcmRlcik7XG4gICAgdGhpcy5faGlzdG9yeU9yZGVyc0J5VGlja2V0W2hpc3RvcnlPcmRlci5pZF0gPSB0aGlzLl9oaXN0b3J5T3JkZXJzQnlUaWNrZXRbaGlzdG9yeU9yZGVyLmlkXSB8fCB7fTtcbiAgICBsZXQgbmV3SGlzdG9yeU9yZGVyID0gIWV4aXN0aW5nICYmICF0aGlzLl9oaXN0b3J5T3JkZXJzQnlUaWNrZXRbaGlzdG9yeU9yZGVyLmlkXVtrZXldO1xuICAgIHRoaXMuX2hpc3RvcnlPcmRlcnNCeVRpY2tldFtoaXN0b3J5T3JkZXIuaWRdW2tleV0gPSBoaXN0b3J5T3JkZXI7XG4gICAgaWYgKGhpc3RvcnlPcmRlci5wb3NpdGlvbklkKSB7XG4gICAgICB0aGlzLl9oaXN0b3J5T3JkZXJzQnlQb3NpdGlvbltoaXN0b3J5T3JkZXIucG9zaXRpb25JZF0gPSB0aGlzLl9oaXN0b3J5T3JkZXJzQnlQb3NpdGlvbltoaXN0b3J5T3JkZXIucG9zaXRpb25JZF0gfHxcbiAgICAgICAge307XG4gICAgICB0aGlzLl9oaXN0b3J5T3JkZXJzQnlQb3NpdGlvbltoaXN0b3J5T3JkZXIucG9zaXRpb25JZF1ba2V5XSA9IGhpc3RvcnlPcmRlcjtcbiAgICB9XG4gICAgdGhpcy5faGlzdG9yeU9yZGVyc0J5VGltZS5kZWxldGUoaGlzdG9yeU9yZGVyKTtcbiAgICB0aGlzLl9oaXN0b3J5T3JkZXJzQnlUaW1lLmluc2VydChoaXN0b3J5T3JkZXIsIGhpc3RvcnlPcmRlcik7XG4gICAgaWYgKGhpc3RvcnlPcmRlci5kb25lVGltZSAmJiAoIXRoaXMuX21heEhpc3RvcnlPcmRlclRpbWUgfHxcbiAgICAgICAgdGhpcy5fbWF4SGlzdG9yeU9yZGVyVGltZS5nZXRUaW1lKCkgPCBoaXN0b3J5T3JkZXIuZG9uZVRpbWUuZ2V0VGltZSgpKSkge1xuICAgICAgdGhpcy5fbWF4SGlzdG9yeU9yZGVyVGltZSA9IGhpc3RvcnlPcmRlci5kb25lVGltZTtcbiAgICB9XG4gICAgaWYgKG5ld0hpc3RvcnlPcmRlcikge1xuICAgICAgdGhpcy5fbmV3SGlzdG9yeU9yZGVycy5wdXNoKGhpc3RvcnlPcmRlcik7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5fZmx1c2hUaW1lb3V0KTtcbiAgICAgIHRoaXMuX2ZsdXNoVGltZW91dCA9IHNldFRpbWVvdXQodGhpcy5fZmx1c2hEYXRhYmFzZS5iaW5kKHRoaXMpLCA1MDAwKTtcbiAgICB9XG4gIH1cblxuICBfZ2V0SGlzdG9yeU9yZGVyS2V5KGhpc3RvcnlPcmRlcikge1xuICAgIHJldHVybiAoaGlzdG9yeU9yZGVyLmRvbmVUaW1lIHx8IG5ldyBEYXRlKDApKS50b0lTT1N0cmluZygpICsgJzonICsgaGlzdG9yeU9yZGVyLmlkICsgJzonICtcbiAgICAgIGhpc3RvcnlPcmRlci50eXBlICsgJzonICsgaGlzdG9yeU9yZGVyLnN0YXRlO1xuICB9XG5cbiAgYXN5bmMgX2ZsdXNoRGF0YWJhc2UoKSB7XG4gICAgaWYgKHRoaXMuX2ZsdXNoUHJvbWlzZSkge1xuICAgICAgYXdhaXQgdGhpcy5fZmx1c2hQcm9taXNlO1xuICAgIH1cbiAgICBpZiAodGhpcy5fZmx1c2hSdW5uaW5nKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuX2ZsdXNoUnVubmluZyA9IHRydWU7XG4gICAgbGV0IHJlc29sdmU7XG4gICAgdGhpcy5fZmx1c2hQcm9taXNlID0gbmV3IFByb21pc2UocmVzID0+IHJlc29sdmUgPSByZXMpO1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0aGlzLl9oaXN0b3J5RGF0YWJhc2UuZmx1c2godGhpcy5fYWNjb3VudElkLCB0aGlzLl9hcHBsaWNhdGlvbiwgdGhpcy5fbmV3SGlzdG9yeU9yZGVycywgdGhpcy5fbmV3RGVhbHMpO1xuICAgICAgdGhpcy5fbmV3SGlzdG9yeU9yZGVycyA9IFtdO1xuICAgICAgdGhpcy5fbmV3RGVhbHMgPSBbXTtcbiAgICAgIHRoaXMuX2xvZ2dlci5kZWJ1ZyhgJHt0aGlzLl9hY2NvdW50SWR9OiBmbHVzaGVkIGhpc3RvcnkgZGJgKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIHRoaXMuX2xvZ2dlci53YXJuKGAke3RoaXMuX2FjY291bnRJZH06IGVycm9yIGZsdXNoaW5nIGhpc3RvcnkgZGJgLCBlcnIpO1xuICAgICAgdGhpcy5fZmx1c2hUaW1lb3V0ID0gc2V0VGltZW91dCh0aGlzLl9mbHVzaERhdGFiYXNlLmJpbmQodGhpcyksIDE1MDAwKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgcmVzb2x2ZSgpO1xuICAgICAgdGhpcy5fZmx1c2hSdW5uaW5nID0gZmFsc2U7XG4gICAgfVxuICB9XG5cbn1cbiJdLCJuYW1lcyI6WyJNZW1vcnlIaXN0b3J5U3RvcmFnZSIsIkhpc3RvcnlTdG9yYWdlIiwiaW5pdGlhbGl6ZSIsImFjY291bnRJZCIsImFwcGxpY2F0aW9uIiwiZGVhbHMiLCJoaXN0b3J5T3JkZXJzIiwiX2hpc3RvcnlEYXRhYmFzZSIsImxvYWRIaXN0b3J5IiwiZGVhbCIsIl9hZGREZWFsIiwiaGlzdG9yeU9yZGVyIiwiX2FkZEhpc3RvcnlPcmRlciIsImNsZWFyIiwiX3Jlc2V0IiwiX2FjY291bnRJZCIsIl9hcHBsaWNhdGlvbiIsImxhc3RIaXN0b3J5T3JkZXJUaW1lIiwiaW5zdGFuY2VOdW1iZXIiLCJfbWF4SGlzdG9yeU9yZGVyVGltZSIsImxhc3REZWFsVGltZSIsIl9tYXhEZWFsVGltZSIsIm9uSGlzdG9yeU9yZGVyQWRkZWQiLCJpbnN0YW5jZUluZGV4Iiwib25EZWFsQWRkZWQiLCJnZXREZWFsc0J5VGltZVJhbmdlIiwiRGF0ZSIsImdldERlYWxzQnlUaWNrZXQiLCJpZCIsIk9iamVjdCIsInZhbHVlcyIsIl9kZWFsc0J5VGlja2V0Iiwic29ydCIsIl9kZWFsc0NvbXBhcmF0b3IiLCJnZXREZWFsc0J5UG9zaXRpb24iLCJwb3NpdGlvbklkIiwiX2RlYWxzQnlQb3NpdGlvbiIsInN0YXJ0VGltZSIsImVuZFRpbWUiLCJfZGVhbHNCeVRpbWUiLCJiZXR3ZWVuQm91bmRzIiwiJGd0ZSIsInRpbWUiLCJlbnRyeVR5cGUiLCIkbHRlIiwiTnVtYmVyIiwiTUFYX1ZBTFVFIiwiZ2V0SGlzdG9yeU9yZGVyc0J5VGltZVJhbmdlIiwiZ2V0SGlzdG9yeU9yZGVyc0J5VGlja2V0IiwiX2hpc3RvcnlPcmRlcnNCeVRpY2tldCIsIl9oaXN0b3J5T3JkZXJzQ29tcGFyYXRvciIsImdldEhpc3RvcnlPcmRlcnNCeVBvc2l0aW9uIiwiX2hpc3RvcnlPcmRlcnNCeVBvc2l0aW9uIiwiX2hpc3RvcnlPcmRlcnNCeVRpbWUiLCJkb25lVGltZSIsInR5cGUiLCJzdGF0ZSIsIm9uRGVhbHNTeW5jaHJvbml6ZWQiLCJzeW5jaHJvbml6YXRpb25JZCIsIl9mbHVzaERhdGFiYXNlIiwiX29yZGVyU3luY2hyb25pemF0aW9uRmluaXNoZWQiLCJfZGVhbFN5bmNocm9uaXphdGlvbkZpbmlzaGVkIiwibzEiLCJvMiIsInRpbWVEaWZmIiwiZ2V0VGltZSIsImlkRGlmZiIsIkFWTFRyZWUiLCJjb21wYXJlS2V5cyIsImQxIiwiZDIiLCJfbmV3SGlzdG9yeU9yZGVycyIsIl9uZXdEZWFscyIsImNsZWFyVGltZW91dCIsIl9mbHVzaFRpbWVvdXQiLCJleGlzdGluZyIsImtleSIsIl9nZXREZWFsS2V5IiwibmV3RGVhbCIsImRlbGV0ZSIsImluc2VydCIsInB1c2giLCJzZXRUaW1lb3V0IiwiYmluZCIsInRvSVNPU3RyaW5nIiwiX2dldEhpc3RvcnlPcmRlcktleSIsIm5ld0hpc3RvcnlPcmRlciIsIl9mbHVzaFByb21pc2UiLCJfZmx1c2hSdW5uaW5nIiwicmVzb2x2ZSIsIlByb21pc2UiLCJyZXMiLCJmbHVzaCIsIl9sb2dnZXIiLCJkZWJ1ZyIsImVyciIsIndhcm4iLCJjb25zdHJ1Y3RvciIsIkhpc3RvcnlEYXRhYmFzZSIsImdldEluc3RhbmNlIiwiTG9nZ2VyTWFuYWdlciIsImdldExvZ2dlciJdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7ZUFVcUJBOzs7dUVBUk07OERBQ0M7a0NBQ0o7K0RBQ0U7Ozs7OztBQUtYLElBQUEsQUFBTUEsdUJBQU4sTUFBTUEsNkJBQTZCQyx1QkFBYztJQVk5RDs7OztHQUlDLEdBQ0QsTUFBTUMsV0FBV0MsU0FBUyxFQUFFQyxjQUFjLFNBQVMsRUFBRTtRQUNuRCxNQUFNLEtBQUssQ0FBQ0YsV0FBV0MsV0FBV0M7UUFDbEMsSUFBSSxFQUFDQyxLQUFLLEVBQUVDLGFBQWEsRUFBQyxHQUFHLE1BQU0sSUFBSSxDQUFDQyxnQkFBZ0IsQ0FBQ0MsV0FBVyxDQUFDTCxXQUFXQztRQUNoRixLQUFLLElBQUlLLFFBQVFKLE1BQU87WUFDdEIsTUFBTSxJQUFJLENBQUNLLFFBQVEsQ0FBQ0QsTUFBTTtRQUM1QjtRQUNBLEtBQUssSUFBSUUsZ0JBQWdCTCxjQUFlO1lBQ3RDLE1BQU0sSUFBSSxDQUFDTSxnQkFBZ0IsQ0FBQ0QsY0FBYztRQUM1QztJQUNGO0lBRUE7OztHQUdDLEdBQ0QsTUFBTUUsUUFBUTtRQUNaLElBQUksQ0FBQ0MsTUFBTTtRQUNYLE1BQU0sSUFBSSxDQUFDUCxnQkFBZ0IsQ0FBQ00sS0FBSyxDQUFDLElBQUksQ0FBQ0UsVUFBVSxFQUFFLElBQUksQ0FBQ0MsWUFBWTtJQUN0RTtJQUVBOzs7O0dBSUMsR0FDREMscUJBQXFCQyxjQUFjLEVBQUU7UUFDbkMsT0FBTyxJQUFJLENBQUNDLG9CQUFvQjtJQUNsQztJQUVBOzs7O0dBSUMsR0FDREMsYUFBYUYsY0FBYyxFQUFFO1FBQzNCLE9BQU8sSUFBSSxDQUFDRyxZQUFZO0lBQzFCO0lBRUE7Ozs7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