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)
322 lines (321 loc) • 53.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "default", {
enumerable: true,
get: function() {
return EquityChartStreamManager;
}
});
const _randomstring = /*#__PURE__*/ _interop_require_default(require("randomstring"));
const _synchronizationListener = /*#__PURE__*/ _interop_require_default(require("../../../clients/metaApi/synchronizationListener"));
const _logger = /*#__PURE__*/ _interop_require_default(require("../../../logger"));
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
let EquityChartStreamManager = class EquityChartStreamManager {
/**
* Returns listeners for account
* @param {String} accountId account id to return listeners for
* @returns {{[listenerId: string]: EquityChartListener}} dictionary of account equity chart event listeners
*/ getAccountListeners(accountId) {
if (!this._equityChartListeners[accountId]) {
this._equityChartListeners[accountId] = {};
}
return this._equityChartListeners[accountId];
}
/**
* Adds an equity chart event listener
* @param {EquityChartListener} listener equity chart event listener
* @param {String} accountId account id
* @param {Date} [startTime] date to start tracking from
* @returns {Promise<string>} listener id
*/ // eslint-disable-next-line max-statements, complexity
async addEquityChartListener(listener, accountId, startTime) {
if (!this._equityChartCaches[accountId]) {
this._equityChartCaches[accountId] = {
record: {},
lastPeriod: {},
pendingInitalizationResolves: []
};
}
const cache = this._equityChartCaches[accountId];
let connection = null;
let retryIntervalInSeconds = this._retryIntervalInSeconds;
const equityTrackingClient = this._equityTrackingClient;
const getAccountListeners = ()=>this.getAccountListeners(accountId);
const pendingInitalizationResolves = this._pendingInitalizationResolves;
const synchronizationFlags = this._accountSynchronizationFlags;
let EquityChartStreamListener = class EquityChartStreamListener extends _synchronizationListener.default {
async onDealsSynchronized(instanceIndex, synchronizationId) {
try {
if (!synchronizationFlags[accountId]) {
synchronizationFlags[accountId] = true;
Object.values(getAccountListeners()).forEach((accountListener)=>{
accountListener.onConnected();
});
}
if (pendingInitalizationResolves[accountId]) {
pendingInitalizationResolves[accountId].forEach((resolve)=>resolve());
delete pendingInitalizationResolves[accountId];
}
} catch (err) {
Object.values(getAccountListeners()).forEach((accountListener)=>{
accountListener.onError(err);
});
this._logger.error("Error processing onDealsSynchronized event for " + `equity chart listener for account ${accountId}`, err);
}
}
async onDisconnected(instanceIndex) {
try {
if (synchronizationFlags[accountId] && !connection.healthMonitor.healthStatus.synchronized) {
synchronizationFlags[accountId] = false;
Object.values(getAccountListeners()).forEach((accountListener)=>{
accountListener.onDisconnected();
});
}
} catch (err) {
Object.values(getAccountListeners()).forEach((accountListener)=>{
accountListener.onError(err);
});
this._logger.error("Error processing onDisconnected event for " + `equity chart listener for account ${accountId}`, err);
}
}
// eslint-disable-next-line complexity, max-statements
async onSymbolPriceUpdated(instanceIndex, price) {
try {
if (pendingInitalizationResolves[accountId]) {
pendingInitalizationResolves[accountId].forEach((resolve)=>resolve());
delete pendingInitalizationResolves[accountId];
}
const equity = price.equity;
const brokerTime = price.brokerTime;
if (!cache.lastPeriod) {
return;
}
if (brokerTime > cache.lastPeriod.endBrokerTime) {
Object.values(getAccountListeners()).forEach((accountListener)=>{
accountListener.onEquityRecordCompleted();
});
const startBrokerTime = cache.lastPeriod.startBrokerTime;
cache.lastPeriod = null;
// eslint-disable-next-line no-constant-condition
while(true){
let periods = await equityTrackingClient.getEquityChart(accountId, startBrokerTime, undefined, true);
if (periods.length < 2) {
await new Promise((res)=>setTimeout(res, 10000));
} else {
Object.values(getAccountListeners()).forEach((accountListener)=>{
accountListener.onEquityRecordUpdated(periods);
});
cache.lastPeriod = periods[1];
break;
}
}
} else {
const accountInformation = connection.terminalState.accountInformation;
if (accountInformation) {
const previousInfo = {
startBrokerTime: cache.lastPeriod.startBrokerTime,
endBrokerTime: cache.lastPeriod.endBrokerTime,
averageBalance: cache.record.averageBalance,
minBalance: cache.record.minBalance,
maxBalance: cache.record.maxBalance,
averageEquity: Math.floor(cache.record.averageEquity),
minEquity: cache.record.minEquity,
maxEquity: cache.record.maxEquity,
lastBalance: cache.lastPeriod.lastBalance,
lastEquity: cache.lastPeriod.lastEquity
};
let durationIncrement = new Date(brokerTime).getTime() - new Date(cache.lastPeriod.brokerTime).getTime();
cache.lastPeriod.equitySum += durationIncrement * (cache.lastPeriod.equity || accountInformation.equity);
cache.lastPeriod.balanceSum += durationIncrement * (cache.lastPeriod.balance || accountInformation.balance);
cache.lastPeriod.duration += durationIncrement;
cache.lastPeriod.equity = price.equity;
cache.lastPeriod.balance = accountInformation.balance;
cache.lastPeriod.brokerTime = price.brokerTime;
cache.record.duration = cache.lastPeriod.duration;
cache.record.balanceSum = cache.lastPeriod.balanceSum;
cache.record.equitySum = cache.lastPeriod.equitySum;
cache.record.averageEquity = cache.lastPeriod.duration ? cache.lastPeriod.equitySum / cache.lastPeriod.duration : equity;
cache.record.averageBalance = cache.lastPeriod.duration ? cache.lastPeriod.balanceSum / cache.lastPeriod.duration : accountInformation.balance;
cache.record.minEquity = Math.min(cache.record.minEquity, price.equity);
cache.record.maxEquity = Math.max(cache.record.maxEquity, price.equity);
cache.record.lastEquity = equity;
cache.record.minBalance = Math.min(cache.record.minBalance, accountInformation.balance);
cache.record.maxBalance = Math.max(cache.record.maxBalance, accountInformation.balance);
cache.record.lastBalance = accountInformation.balance;
/**
* due to calculation inaccuracy, averageEquity will never match the previous value
* therefore, floor before comparing
*/ if (cache.lastPeriod.startBrokerTime) {
const newInfo = {
startBrokerTime: cache.lastPeriod.startBrokerTime,
endBrokerTime: cache.lastPeriod.endBrokerTime,
averageBalance: cache.record.averageBalance,
minBalance: cache.record.minBalance,
maxBalance: cache.record.maxBalance,
averageEquity: Math.floor(cache.record.averageEquity),
minEquity: cache.record.minEquity,
maxEquity: cache.record.maxEquity,
lastBalance: cache.record.lastBalance,
lastEquity: cache.record.lastEquity
};
if (JSON.stringify(previousInfo) !== JSON.stringify(newInfo)) {
Object.values(getAccountListeners()).forEach((accountListener)=>{
accountListener.onEquityRecordUpdated([
newInfo
]);
});
}
}
}
}
} catch (err) {
Object.values(getAccountListeners()).forEach((accountListener)=>{
accountListener.onError(err);
});
this._logger.error("Error processing onSymbolPriceUpdated event for " + `equity chart listener for account ${accountId}`, err);
}
}
async onAccountInformationUpdated(instanceIndex, accountInformation) {
try {
const balance = accountInformation.balance;
cache.lastPeriod.balance = balance;
cache.lastPeriod.lastBalance = balance;
cache.record.lastBalance = balance;
cache.record.minBalance = Math.min(cache.record.minBalance, balance);
cache.record.maxBalance = Math.max(cache.record.minBalance, balance);
} catch (err) {
Object.values(getAccountListeners()).forEach((accountListener)=>{
accountListener.onError(err);
});
this._logger.error("Error processing onAccountInformationUpdated event for " + `equity chart listener for account ${accountId}`, err);
}
}
};
const listenerId = _randomstring.default.generate(10);
const accountListeners = this.getAccountListeners(accountId);
accountListeners[listenerId] = listener;
this._accountsByListenerId[listenerId] = accountId;
const account = await this._metaApi.metatraderAccountApi.getAccount(accountId);
let isDeployed = false;
while(!isDeployed){
try {
await account.waitDeployed();
isDeployed = true;
} catch (err) {
listener.onError(err);
this._logger.error(`Error wait for account ${accountId} to deploy, retrying`, err);
await new Promise((res)=>setTimeout(res, retryIntervalInSeconds * 1000));
retryIntervalInSeconds = Math.min(retryIntervalInSeconds * 2, 300);
}
}
if (!this._equityChartConnections[accountId]) {
retryIntervalInSeconds = this._retryIntervalInSeconds;
connection = account.getStreamingConnection();
this._equityChartConnections[accountId] = connection;
const syncListener = new EquityChartStreamListener();
connection.addSynchronizationListener(syncListener);
let isSynchronized = false;
while(!isSynchronized){
try {
await connection.connect();
await connection.waitSynchronized();
isSynchronized = true;
} catch (err) {
listener.onError(err);
this._logger.error(`Error configuring equity chart stream listener for account ${accountId}, retrying`, err);
await new Promise((res)=>setTimeout(res, retryIntervalInSeconds * 1000));
retryIntervalInSeconds = Math.min(retryIntervalInSeconds * 2, 300);
}
}
retryIntervalInSeconds = this._retryIntervalInSeconds;
} else {
connection = this._equityChartConnections[accountId];
if (!connection.healthMonitor.healthStatus.synchronized) {
if (!this._pendingInitalizationResolves[accountId]) {
this._pendingInitalizationResolves[accountId] = [];
}
let resolveInitialize;
let initializePromise = new Promise((res, rej)=>{
resolveInitialize = res;
});
this._pendingInitalizationResolves[accountId].push(resolveInitialize);
await initializePromise;
}
}
let initialData = [];
while(!initialData.length){
try {
initialData = await equityTrackingClient.getEquityChart(accountId, startTime, undefined, true);
if (initialData.length) {
const lastItem = initialData.slice(-1)[0];
listener.onEquityRecordUpdated(initialData);
cache.lastPeriod = {
duration: lastItem.duration,
equitySum: lastItem.equitySum,
balanceSum: lastItem.balanceSum,
startBrokerTime: lastItem.startBrokerTime,
endBrokerTime: lastItem.endBrokerTime,
brokerTime: lastItem.brokerTime,
averageEquity: Math.floor(lastItem.averageEquity),
minEquity: lastItem.minEquity,
maxEquity: lastItem.maxEquity,
averageBalance: lastItem.averageBalance,
minBalance: lastItem.minBalance,
maxBalance: lastItem.maxBalance,
lastBalance: lastItem.lastBalance,
lastEquity: lastItem.lastEquity
};
cache.record = cache.lastPeriod;
}
} catch (err) {
listener.onError(err);
this._logger.error(`Failed initialize equity chart data for account ${accountId}`, err);
await new Promise((res)=>setTimeout(res, retryIntervalInSeconds * 1000));
retryIntervalInSeconds = Math.min(retryIntervalInSeconds * 2, 300);
}
}
return listenerId;
}
/**
* Removes equity chart event listener by id
* @param {String} listenerId listener id
*/ removeEquityChartListener(listenerId) {
if (this._accountsByListenerId[listenerId]) {
const accountId = this._accountsByListenerId[listenerId];
delete this._accountSynchronizationFlags[accountId];
delete this._accountsByListenerId[listenerId];
if (this._equityChartListeners[accountId]) {
delete this._equityChartListeners[accountId][listenerId];
}
if (this._equityChartConnections[accountId] && !Object.keys(this._equityChartListeners[accountId]).length) {
this._equityChartConnections[accountId].close();
delete this._equityChartConnections[accountId];
}
}
}
/**
* Constructs equity chart event listener manager instance
* @param {DomainClient} domainClient domain client
* @param {EquityTrackingClient} equityTrackingClient equity tracking client
* @param {MetaApi} metaApi metaApi SDK instance
*/ constructor(domainClient, equityTrackingClient, metaApi){
this._domainClient = domainClient;
this._equityTrackingClient = equityTrackingClient;
this._metaApi = metaApi;
this._equityChartListeners = {};
this._accountsByListenerId = {};
this._equityChartConnections = {};
this._equityChartCaches = {};
this._accountSynchronizationFlags = {};
this._pendingInitalizationResolves = {};
this._retryIntervalInSeconds = 1;
this._logger = _logger.default.getLogger("EquityChartStreamManager");
}
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCByYW5kb21zdHJpbmcgZnJvbSAncmFuZG9tc3RyaW5nJztcbmltcG9ydCBTeW5jaHJvbml6YXRpb25MaXN0ZW5lciBmcm9tICcuLi8uLi8uLi9jbGllbnRzL21ldGFBcGkvc3luY2hyb25pemF0aW9uTGlzdGVuZXInO1xuaW1wb3J0IExvZ2dlck1hbmFnZXIgZnJvbSAnLi4vLi4vLi4vbG9nZ2VyJztcblxuLyoqXG4gKiBNYW5hZ2VyIGZvciBoYW5kbGluZyBlcXVpdHkgY2hhcnQgZXZlbnQgbGlzdGVuZXJzXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEVxdWl0eUNoYXJ0U3RyZWFtTWFuYWdlciB7XG5cbiAgLyoqXG4gICAqIENvbnN0cnVjdHMgZXF1aXR5IGNoYXJ0IGV2ZW50IGxpc3RlbmVyIG1hbmFnZXIgaW5zdGFuY2VcbiAgICogQHBhcmFtIHtEb21haW5DbGllbnR9IGRvbWFpbkNsaWVudCBkb21haW4gY2xpZW50XG4gICAqIEBwYXJhbSB7RXF1aXR5VHJhY2tpbmdDbGllbnR9IGVxdWl0eVRyYWNraW5nQ2xpZW50IGVxdWl0eSB0cmFja2luZyBjbGllbnRcbiAgICogQHBhcmFtIHtNZXRhQXBpfSBtZXRhQXBpIG1ldGFBcGkgU0RLIGluc3RhbmNlXG4gICAqL1xuICBjb25zdHJ1Y3Rvcihkb21haW5DbGllbnQsIGVxdWl0eVRyYWNraW5nQ2xpZW50LCBtZXRhQXBpKSB7XG4gICAgdGhpcy5fZG9tYWluQ2xpZW50ID0gZG9tYWluQ2xpZW50O1xuICAgIHRoaXMuX2VxdWl0eVRyYWNraW5nQ2xpZW50ID0gZXF1aXR5VHJhY2tpbmdDbGllbnQ7XG4gICAgdGhpcy5fbWV0YUFwaSA9IG1ldGFBcGk7XG4gICAgdGhpcy5fZXF1aXR5Q2hhcnRMaXN0ZW5lcnMgPSB7fTtcbiAgICB0aGlzLl9hY2NvdW50c0J5TGlzdGVuZXJJZCA9IHt9O1xuICAgIHRoaXMuX2VxdWl0eUNoYXJ0Q29ubmVjdGlvbnMgPSB7fTtcbiAgICB0aGlzLl9lcXVpdHlDaGFydENhY2hlcyA9IHt9O1xuICAgIHRoaXMuX2FjY291bnRTeW5jaHJvbml6YXRpb25GbGFncyA9IHt9O1xuICAgIHRoaXMuX3BlbmRpbmdJbml0YWxpemF0aW9uUmVzb2x2ZXMgPSB7fTtcbiAgICB0aGlzLl9yZXRyeUludGVydmFsSW5TZWNvbmRzID0gMTtcbiAgICB0aGlzLl9sb2dnZXIgPSBMb2dnZXJNYW5hZ2VyLmdldExvZ2dlcignRXF1aXR5Q2hhcnRTdHJlYW1NYW5hZ2VyJyk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBsaXN0ZW5lcnMgZm9yIGFjY291bnRcbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBhY2NvdW50IGlkIHRvIHJldHVybiBsaXN0ZW5lcnMgZm9yXG4gICAqIEByZXR1cm5zIHt7W2xpc3RlbmVySWQ6IHN0cmluZ106IEVxdWl0eUNoYXJ0TGlzdGVuZXJ9fSBkaWN0aW9uYXJ5IG9mIGFjY291bnQgZXF1aXR5IGNoYXJ0IGV2ZW50IGxpc3RlbmVyc1xuICAgKi9cbiAgZ2V0QWNjb3VudExpc3RlbmVycyhhY2NvdW50SWQpIHtcbiAgICBpZighdGhpcy5fZXF1aXR5Q2hhcnRMaXN0ZW5lcnNbYWNjb3VudElkXSkge1xuICAgICAgdGhpcy5fZXF1aXR5Q2hhcnRMaXN0ZW5lcnNbYWNjb3VudElkXSA9IHt9O1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fZXF1aXR5Q2hhcnRMaXN0ZW5lcnNbYWNjb3VudElkXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGRzIGFuIGVxdWl0eSBjaGFydCBldmVudCBsaXN0ZW5lclxuICAgKiBAcGFyYW0ge0VxdWl0eUNoYXJ0TGlzdGVuZXJ9IGxpc3RlbmVyIGVxdWl0eSBjaGFydCBldmVudCBsaXN0ZW5lclxuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIGFjY291bnQgaWRcbiAgICogQHBhcmFtIHtEYXRlfSBbc3RhcnRUaW1lXSBkYXRlIHRvIHN0YXJ0IHRyYWNraW5nIGZyb21cbiAgICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gbGlzdGVuZXIgaWRcbiAgICovXG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBtYXgtc3RhdGVtZW50cywgY29tcGxleGl0eVxuICBhc3luYyBhZGRFcXVpdHlDaGFydExpc3RlbmVyKGxpc3RlbmVyLCBhY2NvdW50SWQsIHN0YXJ0VGltZSkge1xuICAgIGlmKCF0aGlzLl9lcXVpdHlDaGFydENhY2hlc1thY2NvdW50SWRdKSB7XG4gICAgICB0aGlzLl9lcXVpdHlDaGFydENhY2hlc1thY2NvdW50SWRdID0ge1xuICAgICAgICByZWNvcmQ6IHt9LFxuICAgICAgICBsYXN0UGVyaW9kOiB7fSxcbiAgICAgICAgcGVuZGluZ0luaXRhbGl6YXRpb25SZXNvbHZlczogW11cbiAgICAgIH07XG4gICAgfVxuICAgIGNvbnN0IGNhY2hlID0gdGhpcy5fZXF1aXR5Q2hhcnRDYWNoZXNbYWNjb3VudElkXTtcbiAgICBsZXQgY29ubmVjdGlvbiA9IG51bGw7XG4gICAgbGV0IHJldHJ5SW50ZXJ2YWxJblNlY29uZHMgPSB0aGlzLl9yZXRyeUludGVydmFsSW5TZWNvbmRzO1xuICAgIGNvbnN0IGVxdWl0eVRyYWNraW5nQ2xpZW50ID0gdGhpcy5fZXF1aXR5VHJhY2tpbmdDbGllbnQ7XG4gICAgY29uc3QgZ2V0QWNjb3VudExpc3RlbmVycyA9ICgpID0+IHRoaXMuZ2V0QWNjb3VudExpc3RlbmVycyhhY2NvdW50SWQpO1xuICAgIGNvbnN0IHBlbmRpbmdJbml0YWxpemF0aW9uUmVzb2x2ZXMgPSB0aGlzLl9wZW5kaW5nSW5pdGFsaXphdGlvblJlc29sdmVzO1xuICAgIGNvbnN0IHN5bmNocm9uaXphdGlvbkZsYWdzID0gdGhpcy5fYWNjb3VudFN5bmNocm9uaXphdGlvbkZsYWdzO1xuXG4gICAgY2xhc3MgRXF1aXR5Q2hhcnRTdHJlYW1MaXN0ZW5lciBleHRlbmRzIFN5bmNocm9uaXphdGlvbkxpc3RlbmVyIHtcblxuICAgICAgYXN5bmMgb25EZWFsc1N5bmNocm9uaXplZChpbnN0YW5jZUluZGV4LCBzeW5jaHJvbml6YXRpb25JZCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGlmKCFzeW5jaHJvbml6YXRpb25GbGFnc1thY2NvdW50SWRdKSB7XG4gICAgICAgICAgICBzeW5jaHJvbml6YXRpb25GbGFnc1thY2NvdW50SWRdID0gdHJ1ZTtcbiAgICAgICAgICAgIE9iamVjdC52YWx1ZXMoZ2V0QWNjb3VudExpc3RlbmVycygpKS5mb3JFYWNoKGFjY291bnRMaXN0ZW5lciA9PiB7XG4gICAgICAgICAgICAgIGFjY291bnRMaXN0ZW5lci5vbkNvbm5lY3RlZCgpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmKHBlbmRpbmdJbml0YWxpemF0aW9uUmVzb2x2ZXNbYWNjb3VudElkXSkge1xuICAgICAgICAgICAgcGVuZGluZ0luaXRhbGl6YXRpb25SZXNvbHZlc1thY2NvdW50SWRdLmZvckVhY2gocmVzb2x2ZSA9PiByZXNvbHZlKCkpO1xuICAgICAgICAgICAgZGVsZXRlIHBlbmRpbmdJbml0YWxpemF0aW9uUmVzb2x2ZXNbYWNjb3VudElkXTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgIE9iamVjdC52YWx1ZXMoZ2V0QWNjb3VudExpc3RlbmVycygpKS5mb3JFYWNoKGFjY291bnRMaXN0ZW5lciA9PiB7XG4gICAgICAgICAgICBhY2NvdW50TGlzdGVuZXIub25FcnJvcihlcnIpO1xuICAgICAgICAgIH0pO1xuICAgICAgICAgIHRoaXMuX2xvZ2dlci5lcnJvcignRXJyb3IgcHJvY2Vzc2luZyBvbkRlYWxzU3luY2hyb25pemVkIGV2ZW50IGZvciAnICtcbiAgICAgICAgICBgZXF1aXR5IGNoYXJ0IGxpc3RlbmVyIGZvciBhY2NvdW50ICR7YWNjb3VudElkfWAsIGVycik7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgYXN5bmMgb25EaXNjb25uZWN0ZWQoaW5zdGFuY2VJbmRleCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGlmKHN5bmNocm9uaXphdGlvbkZsYWdzW2FjY291bnRJZF0gJiYgIWNvbm5lY3Rpb24uaGVhbHRoTW9uaXRvci5oZWFsdGhTdGF0dXMuc3luY2hyb25pemVkKSB7XG4gICAgICAgICAgICBzeW5jaHJvbml6YXRpb25GbGFnc1thY2NvdW50SWRdID0gZmFsc2U7XG4gICAgICAgICAgICBPYmplY3QudmFsdWVzKGdldEFjY291bnRMaXN0ZW5lcnMoKSkuZm9yRWFjaChhY2NvdW50TGlzdGVuZXIgPT4ge1xuICAgICAgICAgICAgICBhY2NvdW50TGlzdGVuZXIub25EaXNjb25uZWN0ZWQoKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgT2JqZWN0LnZhbHVlcyhnZXRBY2NvdW50TGlzdGVuZXJzKCkpLmZvckVhY2goYWNjb3VudExpc3RlbmVyID0+IHtcbiAgICAgICAgICAgIGFjY291bnRMaXN0ZW5lci5vbkVycm9yKGVycik7XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgdGhpcy5fbG9nZ2VyLmVycm9yKCdFcnJvciBwcm9jZXNzaW5nIG9uRGlzY29ubmVjdGVkIGV2ZW50IGZvciAnICtcbiAgICAgICAgICBgZXF1aXR5IGNoYXJ0IGxpc3RlbmVyIGZvciBhY2NvdW50ICR7YWNjb3VudElkfWAsIGVycik7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGNvbXBsZXhpdHksIG1heC1zdGF0ZW1lbnRzXG4gICAgICBhc3luYyBvblN5bWJvbFByaWNlVXBkYXRlZChpbnN0YW5jZUluZGV4LCBwcmljZSkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGlmKHBlbmRpbmdJbml0YWxpemF0aW9uUmVzb2x2ZXNbYWNjb3VudElkXSkge1xuICAgICAgICAgICAgcGVuZGluZ0luaXRhbGl6YXRpb25SZXNvbHZlc1thY2NvdW50SWRdLmZvckVhY2gocmVzb2x2ZSA9PiByZXNvbHZlKCkpO1xuICAgICAgICAgICAgZGVsZXRlIHBlbmRpbmdJbml0YWxpemF0aW9uUmVzb2x2ZXNbYWNjb3VudElkXTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBjb25zdCBlcXVpdHkgPSBwcmljZS5lcXVpdHk7XG4gICAgICAgICAgY29uc3QgYnJva2VyVGltZSA9IHByaWNlLmJyb2tlclRpbWU7XG4gICAgICAgICAgaWYoIWNhY2hlLmxhc3RQZXJpb2QpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYoYnJva2VyVGltZSA+IGNhY2hlLmxhc3RQZXJpb2QuZW5kQnJva2VyVGltZSkge1xuICAgICAgICAgICAgT2JqZWN0LnZhbHVlcyhnZXRBY2NvdW50TGlzdGVuZXJzKCkpLmZvckVhY2goYWNjb3VudExpc3RlbmVyID0+IHtcbiAgICAgICAgICAgICAgYWNjb3VudExpc3RlbmVyLm9uRXF1aXR5UmVjb3JkQ29tcGxldGVkKCk7ICBcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgY29uc3Qgc3RhcnRCcm9rZXJUaW1lID0gY2FjaGUubGFzdFBlcmlvZC5zdGFydEJyb2tlclRpbWU7XG4gICAgICAgICAgICBjYWNoZS5sYXN0UGVyaW9kID0gbnVsbDtcbiAgICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zdGFudC1jb25kaXRpb25cbiAgICAgICAgICAgIHdoaWxlKHRydWUpIHtcbiAgICAgICAgICAgICAgbGV0IHBlcmlvZHMgPSBhd2FpdCBlcXVpdHlUcmFja2luZ0NsaWVudC5nZXRFcXVpdHlDaGFydChhY2NvdW50SWQsIHN0YXJ0QnJva2VyVGltZSwgdW5kZWZpbmVkLCB0cnVlKTtcbiAgICAgICAgICAgICAgaWYocGVyaW9kcy5sZW5ndGggPCAyKSB7XG4gICAgICAgICAgICAgICAgYXdhaXQgbmV3IFByb21pc2UocmVzID0+IHNldFRpbWVvdXQocmVzLCAxMDAwMCkpO1xuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIE9iamVjdC52YWx1ZXMoZ2V0QWNjb3VudExpc3RlbmVycygpKS5mb3JFYWNoKGFjY291bnRMaXN0ZW5lciA9PiB7XG4gICAgICAgICAgICAgICAgICBhY2NvdW50TGlzdGVuZXIub25FcXVpdHlSZWNvcmRVcGRhdGVkKHBlcmlvZHMpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIGNhY2hlLmxhc3RQZXJpb2QgPSBwZXJpb2RzWzFdO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNvbnN0IGFjY291bnRJbmZvcm1hdGlvbiA9IGNvbm5lY3Rpb24udGVybWluYWxTdGF0ZS5hY2NvdW50SW5mb3JtYXRpb247XG4gICAgICAgICAgICBpZihhY2NvdW50SW5mb3JtYXRpb24pIHtcbiAgICAgICAgICAgICAgY29uc3QgcHJldmlvdXNJbmZvID0ge1xuICAgICAgICAgICAgICAgIHN0YXJ0QnJva2VyVGltZTogY2FjaGUubGFzdFBlcmlvZC5zdGFydEJyb2tlclRpbWUsXG4gICAgICAgICAgICAgICAgZW5kQnJva2VyVGltZTogY2FjaGUubGFzdFBlcmlvZC5lbmRCcm9rZXJUaW1lLFxuICAgICAgICAgICAgICAgIGF2ZXJhZ2VCYWxhbmNlOiBjYWNoZS5yZWNvcmQuYXZlcmFnZUJhbGFuY2UsXG4gICAgICAgICAgICAgICAgbWluQmFsYW5jZTogY2FjaGUucmVjb3JkLm1pbkJhbGFuY2UsXG4gICAgICAgICAgICAgICAgbWF4QmFsYW5jZTogY2FjaGUucmVjb3JkLm1heEJhbGFuY2UsXG4gICAgICAgICAgICAgICAgYXZlcmFnZUVxdWl0eTogTWF0aC5mbG9vcihjYWNoZS5yZWNvcmQuYXZlcmFnZUVxdWl0eSksXG4gICAgICAgICAgICAgICAgbWluRXF1aXR5OiBjYWNoZS5yZWNvcmQubWluRXF1aXR5LFxuICAgICAgICAgICAgICAgIG1heEVxdWl0eTogY2FjaGUucmVjb3JkLm1heEVxdWl0eSxcbiAgICAgICAgICAgICAgICBsYXN0QmFsYW5jZTogY2FjaGUubGFzdFBlcmlvZC5sYXN0QmFsYW5jZSxcbiAgICAgICAgICAgICAgICBsYXN0RXF1aXR5OiBjYWNoZS5sYXN0UGVyaW9kLmxhc3RFcXVpdHlcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgbGV0IGR1cmF0aW9uSW5jcmVtZW50ID0gbmV3IERhdGUoYnJva2VyVGltZSkuZ2V0VGltZSgpIC0gbmV3IERhdGUoY2FjaGUubGFzdFBlcmlvZC5icm9rZXJUaW1lKS5nZXRUaW1lKCk7XG4gICAgICAgICAgICAgIGNhY2hlLmxhc3RQZXJpb2QuZXF1aXR5U3VtICs9IGR1cmF0aW9uSW5jcmVtZW50ICogKGNhY2hlLmxhc3RQZXJpb2QuZXF1aXR5IHx8IGFjY291bnRJbmZvcm1hdGlvbi5lcXVpdHkpO1xuICAgICAgICAgICAgICBjYWNoZS5sYXN0UGVyaW9kLmJhbGFuY2VTdW0gKz0gZHVyYXRpb25JbmNyZW1lbnQgKiBcbiAgICAgICAgICAgICAgICAoY2FjaGUubGFzdFBlcmlvZC5iYWxhbmNlIHx8IGFjY291bnRJbmZvcm1hdGlvbi5iYWxhbmNlKTtcbiAgICAgICAgICAgICAgY2FjaGUubGFzdFBlcmlvZC5kdXJhdGlvbiArPSBkdXJhdGlvbkluY3JlbWVudDtcbiAgICAgICAgICAgICAgY2FjaGUubGFzdFBlcmlvZC5lcXVpdHkgPSBwcmljZS5lcXVpdHk7XG4gICAgICAgICAgICAgIGNhY2hlLmxhc3RQZXJpb2QuYmFsYW5jZSA9IGFjY291bnRJbmZvcm1hdGlvbi5iYWxhbmNlO1xuICAgICAgICAgICAgICBjYWNoZS5sYXN0UGVyaW9kLmJyb2tlclRpbWUgPSBwcmljZS5icm9rZXJUaW1lO1xuICAgICAgICAgICAgICBjYWNoZS5yZWNvcmQuZHVyYXRpb24gPSBjYWNoZS5sYXN0UGVyaW9kLmR1cmF0aW9uO1xuICAgICAgICAgICAgICBjYWNoZS5yZWNvcmQuYmFsYW5jZVN1bSA9IGNhY2hlLmxhc3RQZXJpb2QuYmFsYW5jZVN1bTtcbiAgICAgICAgICAgICAgY2FjaGUucmVjb3JkLmVxdWl0eVN1bSA9IGNhY2hlLmxhc3RQZXJpb2QuZXF1aXR5U3VtO1xuICAgICAgICAgICAgICBjYWNoZS5yZWNvcmQuYXZlcmFnZUVxdWl0eSA9IGNhY2hlLmxhc3RQZXJpb2QuZHVyYXRpb24gPyBcbiAgICAgICAgICAgICAgICBjYWNoZS5sYXN0UGVyaW9kLmVxdWl0eVN1bSAvIGNhY2hlLmxhc3RQZXJpb2QuZHVyYXRpb24gOiBlcXVpdHk7XG4gICAgICAgICAgICAgIGNhY2hlLnJlY29yZC5hdmVyYWdlQmFsYW5jZSA9IGNhY2hlLmxhc3RQZXJpb2QuZHVyYXRpb24gPyBcbiAgICAgICAgICAgICAgICBjYWNoZS5sYXN0UGVyaW9kLmJhbGFuY2VTdW0gLyBjYWNoZS5sYXN0UGVyaW9kLmR1cmF0aW9uIDogYWNjb3VudEluZm9ybWF0aW9uLmJhbGFuY2U7XG4gICAgICAgICAgICAgIGNhY2hlLnJlY29yZC5taW5FcXVpdHkgPSBNYXRoLm1pbihjYWNoZS5yZWNvcmQubWluRXF1aXR5LCBwcmljZS5lcXVpdHkpO1xuICAgICAgICAgICAgICBjYWNoZS5yZWNvcmQubWF4RXF1aXR5ID0gTWF0aC5tYXgoY2FjaGUucmVjb3JkLm1heEVxdWl0eSwgcHJpY2UuZXF1aXR5KTtcbiAgICAgICAgICAgICAgY2FjaGUucmVjb3JkLmxhc3RFcXVpdHkgPSBlcXVpdHk7XG4gICAgICAgICAgICAgIGNhY2hlLnJlY29yZC5taW5CYWxhbmNlID0gTWF0aC5taW4oY2FjaGUucmVjb3JkLm1pbkJhbGFuY2UsIGFjY291bnRJbmZvcm1hdGlvbi5iYWxhbmNlKTtcbiAgICAgICAgICAgICAgY2FjaGUucmVjb3JkLm1heEJhbGFuY2UgPSBNYXRoLm1heChjYWNoZS5yZWNvcmQubWF4QmFsYW5jZSwgYWNjb3VudEluZm9ybWF0aW9uLmJhbGFuY2UpO1xuICAgICAgICAgICAgICBjYWNoZS5yZWNvcmQubGFzdEJhbGFuY2UgPSBhY2NvdW50SW5mb3JtYXRpb24uYmFsYW5jZTtcbiAgICAgICAgICAgICAgLyoqXG4gICAgICAgICAgICAgKiBkdWUgdG8gY2FsY3VsYXRpb24gaW5hY2N1cmFjeSwgYXZlcmFnZUVxdWl0eSB3aWxsIG5ldmVyIG1hdGNoIHRoZSBwcmV2aW91cyB2YWx1ZVxuICAgICAgICAgICAgICogdGhlcmVmb3JlLCBmbG9vciBiZWZvcmUgY29tcGFyaW5nXG4gICAgICAgICAgICAgKi9cbiAgICAgICAgICAgICAgaWYoY2FjaGUubGFzdFBlcmlvZC5zdGFydEJyb2tlclRpbWUpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBuZXdJbmZvID0ge1xuICAgICAgICAgICAgICAgICAgc3RhcnRCcm9rZXJUaW1lOiBjYWNoZS5sYXN0UGVyaW9kLnN0YXJ0QnJva2VyVGltZSxcbiAgICAgICAgICAgICAgICAgIGVuZEJyb2tlclRpbWU6IGNhY2hlLmxhc3RQZXJpb2QuZW5kQnJva2VyVGltZSxcbiAgICAgICAgICAgICAgICAgIGF2ZXJhZ2VCYWxhbmNlOiBjYWNoZS5yZWNvcmQuYXZlcmFnZUJhbGFuY2UsXG4gICAgICAgICAgICAgICAgICBtaW5CYWxhbmNlOiBjYWNoZS5yZWNvcmQubWluQmFsYW5jZSxcbiAgICAgICAgICAgICAgICAgIG1heEJhbGFuY2U6IGNhY2hlLnJlY29yZC5tYXhCYWxhbmNlLFxuICAgICAgICAgICAgICAgICAgYXZlcmFnZUVxdWl0eTogTWF0aC5mbG9vcihjYWNoZS5yZWNvcmQuYXZlcmFnZUVxdWl0eSksXG4gICAgICAgICAgICAgICAgICBtaW5FcXVpdHk6IGNhY2hlLnJlY29yZC5taW5FcXVpdHksXG4gICAgICAgICAgICAgICAgICBtYXhFcXVpdHk6IGNhY2hlLnJlY29yZC5tYXhFcXVpdHksXG4gICAgICAgICAgICAgICAgICBsYXN0QmFsYW5jZTogY2FjaGUucmVjb3JkLmxhc3RCYWxhbmNlLFxuICAgICAgICAgICAgICAgICAgbGFzdEVxdWl0eTogY2FjaGUucmVjb3JkLmxhc3RFcXVpdHlcbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIGlmKEpTT04uc3RyaW5naWZ5KHByZXZpb3VzSW5mbykgIT09IEpTT04uc3RyaW5naWZ5KG5ld0luZm8pKSB7XG4gICAgICAgICAgICAgICAgICBPYmplY3QudmFsdWVzKGdldEFjY291bnRMaXN0ZW5lcnMoKSkuZm9yRWFjaChhY2NvdW50TGlzdGVuZXIgPT4ge1xuICAgICAgICAgICAgICAgICAgICBhY2NvdW50TGlzdGVuZXIub25FcXVpdHlSZWNvcmRVcGRhdGVkKFtuZXdJbmZvXSk7IFxuICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSAgICAgXG4gICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgIE9iamVjdC52YWx1ZXMoZ2V0QWNjb3VudExpc3RlbmVycygpKS5mb3JFYWNoKGFjY291bnRMaXN0ZW5lciA9PiB7XG4gICAgICAgICAgICBhY2NvdW50TGlzdGVuZXIub25FcnJvcihlcnIpO1xuICAgICAgICAgIH0pO1xuICAgICAgICAgIHRoaXMuX2xvZ2dlci5lcnJvcignRXJyb3IgcHJvY2Vzc2luZyBvblN5bWJvbFByaWNlVXBkYXRlZCBldmVudCBmb3IgJyArXG4gICAgICAgICAgYGVxdWl0eSBjaGFydCBsaXN0ZW5lciBmb3IgYWNjb3VudCAke2FjY291bnRJZH1gLCBlcnIpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgXG4gICAgICBhc3luYyBvbkFjY291bnRJbmZvcm1hdGlvblVwZGF0ZWQoaW5zdGFuY2VJbmRleCwgYWNjb3VudEluZm9ybWF0aW9uKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgYmFsYW5jZSA9IGFjY291bnRJbmZvcm1hdGlvbi5iYWxhbmNlO1xuICAgICAgICAgIGNhY2hlLmxhc3RQZXJpb2QuYmFsYW5jZSA9IGJhbGFuY2U7XG4gICAgICAgICAgY2FjaGUubGFzdFBlcmlvZC5sYXN0QmFsYW5jZSA9IGJhbGFuY2U7XG4gICAgICAgICAgY2FjaGUucmVjb3JkLmxhc3RCYWxhbmNlID0gYmFsYW5jZTtcbiAgICAgICAgICBjYWNoZS5yZWNvcmQubWluQmFsYW5jZSA9IE1hdGgubWluKGNhY2hlLnJlY29yZC5taW5CYWxhbmNlLCBiYWxhbmNlKTtcbiAgICAgICAgICBjYWNoZS5yZWNvcmQubWF4QmFsYW5jZSA9IE1hdGgubWF4KGNhY2hlLnJlY29yZC5taW5CYWxhbmNlLCBiYWxhbmNlKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgT2JqZWN0LnZhbHVlcyhnZXRBY2NvdW50TGlzdGVuZXJzKCkpLmZvckVhY2goYWNjb3VudExpc3RlbmVyID0+IHtcbiAgICAgICAgICAgIGFjY291bnRMaXN0ZW5lci5vbkVycm9yKGVycik7XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgdGhpcy5fbG9nZ2VyLmVycm9yKCdFcnJvciBwcm9jZXNzaW5nIG9uQWNjb3VudEluZm9ybWF0aW9uVXBkYXRlZCBldmVudCBmb3IgJyArXG4gICAgICAgICAgYGVxdWl0eSBjaGFydCBsaXN0ZW5lciBmb3IgYWNjb3VudCAke2FjY291bnRJZH1gLCBlcnIpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICB9XG5cbiAgICBjb25zdCBsaXN0ZW5lcklkID0gcmFuZG9tc3RyaW5nLmdlbmVyYXRlKDEwKTtcbiAgICBjb25zdCBhY2NvdW50TGlzdGVuZXJzID0gdGhpcy5nZXRBY2NvdW50TGlzdGVuZXJzKGFjY291bnRJZCk7XG4gICAgYWNjb3VudExpc3RlbmVyc1tsaXN0ZW5lcklkXSA9IGxpc3RlbmVyO1xuICAgIHRoaXMuX2FjY291bnRzQnlMaXN0ZW5lcklkW2xpc3RlbmVySWRdID0gYWNjb3VudElkO1xuICAgIGNvbnN0IGFjY291bnQgPSBhd2FpdCB0aGlzLl9tZXRhQXBpLm1ldGF0cmFkZXJBY2NvdW50QXBpLmdldEFjY291bnQoYWNjb3VudElkKTtcbiAgICBsZXQgaXNEZXBsb3llZCA9IGZhbHNlO1xuICAgIHdoaWxlKCFpc0RlcGxveWVkKSB7XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCBhY2NvdW50LndhaXREZXBsb3llZCgpO1xuICAgICAgICBpc0RlcGxveWVkID0gdHJ1ZTsgIFxuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGxpc3RlbmVyLm9uRXJyb3IoZXJyKTtcbiAgICAgICAgdGhpcy5fbG9nZ2VyLmVycm9yKGBFcnJvciB3YWl0IGZvciBhY2NvdW50ICR7YWNjb3VudElkfSB0byBkZXBsb3ksIHJldHJ5aW5nYCwgZXJyKTtcbiAgICAgICAgYXdhaXQgbmV3IFByb21pc2UocmVzID0+IHNldFRpbWVvdXQocmVzLCByZXRyeUludGVydmFsSW5TZWNvbmRzICogMTAwMCkpOyBcbiAgICAgICAgcmV0cnlJbnRlcnZhbEluU2Vjb25kcyA9IE1hdGgubWluKHJldHJ5SW50ZXJ2YWxJblNlY29uZHMgKiAyLCAzMDApO1xuICAgICAgfVxuICAgIH1cbiAgICBpZighdGhpcy5fZXF1aXR5Q2hhcnRDb25uZWN0aW9uc1thY2NvdW50SWRdKSB7XG4gICAgICByZXRyeUludGVydmFsSW5TZWNvbmRzID0gdGhpcy5fcmV0cnlJbnRlcnZhbEluU2Vjb25kcztcbiAgICAgIGNvbm5lY3Rpb24gPSBhY2NvdW50LmdldFN0cmVhbWluZ0Nvbm5lY3Rpb24oKTtcbiAgICAgIHRoaXMuX2VxdWl0eUNoYXJ0Q29ubmVjdGlvbnNbYWNjb3VudElkXSA9IGNvbm5lY3Rpb247XG4gICAgICBjb25zdCBzeW5jTGlzdGVuZXIgPSBuZXcgRXF1aXR5Q2hhcnRTdHJlYW1MaXN0ZW5lcigpO1xuICAgICAgY29ubmVjdGlvbi5hZGRTeW5jaHJvbml6YXRpb25MaXN0ZW5lcihzeW5jTGlzdGVuZXIpO1xuICAgICAgXG4gICAgICBsZXQgaXNTeW5jaHJvbml6ZWQgPSBmYWxzZTtcbiAgICAgIHdoaWxlKCFpc1N5bmNocm9uaXplZCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGF3YWl0IGNvbm5lY3Rpb24uY29ubmVjdCgpO1xuICAgICAgICAgIGF3YWl0IGNvbm5lY3Rpb24ud2FpdFN5bmNocm9uaXplZCgpO1xuICAgICAgICAgIGlzU3luY2hyb25pemVkID0gdHJ1ZTtcbiAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgbGlzdGVuZXIub25FcnJvcihlcnIpO1xuICAgICAgICAgIHRoaXMuX2xvZ2dlci5lcnJvcihgRXJyb3IgY29uZmlndXJpbmcgZXF1aXR5IGNoYXJ0IHN0cmVhbSBsaXN0ZW5lciBmb3IgYWNjb3VudCAke2FjY291bnRJZH0sIHJldHJ5aW5nYCwgZXJyKTtcbiAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXMgPT4gc2V0VGltZW91dChyZXMsIHJldHJ5SW50ZXJ2YWxJblNlY29uZHMgKiAxMDAwKSk7IFxuICAgICAgICAgIHJldHJ5SW50ZXJ2YWxJblNlY29uZHMgPSBNYXRoLm1pbihyZXRyeUludGVydmFsSW5TZWNvbmRzICogMiwgMzAwKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0cnlJbnRlcnZhbEluU2Vjb25kcyA9IHRoaXMuX3JldHJ5SW50ZXJ2YWxJblNlY29uZHM7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbm5lY3Rpb24gPSB0aGlzLl9lcXVpdHlDaGFydENvbm5lY3Rpb25zW2FjY291bnRJZF07XG4gICAgICBpZighY29ubmVjdGlvbi5oZWFsdGhNb25pdG9yLmhlYWx0aFN0YXR1cy5zeW5jaHJvbml6ZWQpIHtcbiAgICAgICAgaWYoIXRoaXMuX3BlbmRpbmdJbml0YWxpemF0aW9uUmVzb2x2ZXNbYWNjb3VudElkXSkge1xuICAgICAgICAgIHRoaXMuX3BlbmRpbmdJbml0YWxpemF0aW9uUmVzb2x2ZXNbYWNjb3VudElkXSA9IFtdO1xuICAgICAgICB9XG4gICAgICAgIGxldCByZXNvbHZlSW5pdGlhbGl6ZTtcbiAgICAgICAgbGV0IGluaXRpYWxpemVQcm9taXNlID0gbmV3IFByb21pc2UoKHJlcywgcmVqKSA9PiB7XG4gICAgICAgICAgcmVzb2x2ZUluaXRpYWxpemUgPSByZXM7XG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLl9wZW5kaW5nSW5pdGFsaXphdGlvblJlc29sdmVzW2FjY291bnRJZF0ucHVzaChyZXNvbHZlSW5pdGlhbGl6ZSk7XG4gICAgICAgIGF3YWl0IGluaXRpYWxpemVQcm9taXNlO1xuICAgICAgfVxuICAgIH1cblxuICAgIGxldCBpbml0aWFsRGF0YSA9IFtdO1xuICAgIHdoaWxlKCFpbml0aWFsRGF0YS5sZW5ndGgpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGluaXRpYWxEYXRhID0gYXdhaXQgZXF1aXR5VHJhY2tpbmdDbGllbnQuZ2V0RXF1aXR5Q2hhcnQoYWNjb3VudElkLCBzdGFydFRpbWUsIHVuZGVmaW5lZCwgdHJ1ZSk7XG4gICAgICAgIGlmKGluaXRpYWxEYXRhLmxlbmd0aCkge1xuICAgICAgICAgIGNvbnN0IGxhc3RJdGVtID0gaW5pdGlhbERhdGEuc2xpY2UoLTEpWzBdO1xuICAgICAgICAgIGxpc3RlbmVyLm9uRXF1aXR5UmVjb3JkVXBkYXRlZChpbml0aWFsRGF0YSk7XG4gICAgICAgICAgY2FjaGUubGFzdFBlcmlvZCA9IHtcbiAgICAgICAgICAgIGR1cmF0aW9uOiBsYXN0SXRlbS5kdXJhdGlvbixcbiAgICAgICAgICAgIGVxdWl0eVN1bTogbGFzdEl0ZW0uZXF1aXR5U3VtLFxuICAgICAgICAgICAgYmFsYW5jZVN1bTogbGFzdEl0ZW0uYmFsYW5jZVN1bSxcbiAgICAgICAgICAgIHN0YXJ0QnJva2VyVGltZTogbGFzdEl0ZW0uc3RhcnRCcm9rZXJUaW1lLFxuICAgICAgICAgICAgZW5kQnJva2VyVGltZTogbGFzdEl0ZW0uZW5kQnJva2VyVGltZSxcbiAgICAgICAgICAgIGJyb2tlclRpbWU6IGxhc3RJdGVtLmJyb2tlclRpbWUsXG4gICAgICAgICAgICBhdmVyYWdlRXF1aXR5OiBNYXRoLmZsb29yKGxhc3RJdGVtLmF2ZXJhZ2VFcXVpdHkpLFxuICAgICAgICAgICAgbWluRXF1aXR5OiBsYXN0SXRlbS5taW5FcXVpdHksXG4gICAgICAgICAgICBtYXhFcXVpdHk6IGxhc3RJdGVtLm1heEVxdWl0eSxcbiAgICAgICAgICAgIGF2ZXJhZ2VCYWxhbmNlOiBsYXN0SXRlbS5hdmVyYWdlQmFsYW5jZSxcbiAgICAgICAgICAgIG1pbkJhbGFuY2U6IGxhc3RJdGVtLm1pbkJhbGFuY2UsXG4gICAgICAgICAgICBtYXhCYWxhbmNlOiBsYXN0SXRlbS5tYXhCYWxhbmNlLFxuICAgICAgICAgICAgbGFzdEJhbGFuY2U6IGxhc3RJdGVtLmxhc3RCYWxhbmNlLFxuICAgICAgICAgICAgbGFzdEVxdWl0eTogbGFzdEl0ZW0ubGFzdEVxdWl0eVxuICAgICAgICAgIH07XG4gICAgICAgICAgY2FjaGUucmVjb3JkID0gY2FjaGUubGFzdFBlcmlvZDtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGxpc3RlbmVyLm9uRXJyb3IoZXJyKTtcbiAgICAgICAgdGhpcy5fbG9nZ2VyLmVycm9yKGBGYWlsZWQgaW5pdGlhbGl6ZSBlcXVpdHkgY2hhcnQgZGF0YSBmb3IgYWNjb3VudCAke2FjY291bnRJZH1gLCBlcnIpO1xuICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXMgPT4gc2V0VGltZW91dChyZXMsIHJldHJ5SW50ZXJ2YWxJblNlY29uZHMgKiAxMDAwKSk7IFxuICAgICAgICByZXRyeUludGVydmFsSW5TZWNvbmRzID0gTWF0aC5taW4ocmV0cnlJbnRlcnZhbEluU2Vjb25kcyAqIDIsIDMwMCk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBsaXN0ZW5lcklkO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZXMgZXF1aXR5IGNoYXJ0IGV2ZW50IGxpc3RlbmVyIGJ5IGlkXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBsaXN0ZW5lcklkIGxpc3RlbmVyIGlkXG4gICAqL1xuICByZW1vdmVFcXVpdHlDaGFydExpc3RlbmVyKGxpc3RlbmVySWQpIHtcbiAgICBpZih0aGlzLl9hY2NvdW50c0J5TGlzdGVuZXJJZFtsaXN0ZW5lcklkXSkge1xuICAgICAgY29uc3QgYWNjb3VudElkID0gdGhpcy5fYWNjb3VudHNCeUxpc3RlbmVySWRbbGlzdGVuZXJJZF07XG4gICAgICBkZWxldGUgdGhpcy5fYWNjb3VudFN5bmNocm9uaXphdGlvbkZsYWdzW2FjY291bnRJZF07XG4gICAgICBkZWxldGUgdGhpcy5fYWNjb3VudHNCeUxpc3RlbmVySWRbbGlzdGVuZXJJZF07XG4gICAgICBpZih0aGlzLl9lcXVpdHlDaGFydExpc3RlbmVyc1thY2NvdW50SWRdKSB7XG4gICAgICAgIGRlbGV0ZSB0aGlzLl9lcXVpdHlDaGFydExpc3RlbmVyc1thY2NvdW50SWRdW2xpc3RlbmVySWRdO1xuICAgICAgfVxuICAgICAgaWYodGhpcy5fZXF1aXR5Q2hhcnRDb25uZWN0aW9uc1thY2NvdW50SWRdICYmIFxuICAgICAgICAhT2JqZWN0LmtleXModGhpcy5fZXF1aXR5Q2hhcnRMaXN0ZW5lcnNbYWNjb3VudElkXSkubGVuZ3RoKSB7XG4gICAgICAgIHRoaXMuX2VxdWl0eUNoYXJ0Q29ubmVjdGlvbnNbYWNjb3VudElkXS5jbG9zZSgpO1xuICAgICAgICBkZWxldGUgdGhpcy5fZXF1aXR5Q2hhcnRDb25uZWN0aW9uc1thY2NvdW50SWRdO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG59XG4iXSwibmFtZXMiOlsiRXF1aXR5Q2hhcnRTdHJlYW1NYW5hZ2VyIiwiZ2V0QWNjb3VudExpc3RlbmVycyIsImFjY291bnRJZCIsIl9lcXVpdHlDaGFydExpc3RlbmVycyIsImFkZEVxdWl0eUNoYXJ0TGlzdGVuZXIiLCJsaXN0ZW5lciIsInN0YXJ0VGltZSIsIl9lcXVpdHlDaGFydENhY2hlcyIsInJlY29yZCIsImxhc3RQZXJpb2QiLCJwZW5kaW5nSW5pdGFsaXphdGlvblJlc29sdmVzIiwiY2FjaGUiLCJjb25uZWN0aW9uIiwicmV0cnlJbnRlcnZhbEluU2Vjb25kcyIsIl9yZXRyeUludGVydmFsSW5TZWNvbmRzIiwiZXF1aXR5VHJhY2tpbmdDbGllbnQiLCJfZXF1aXR5VHJhY2tpbmdDbGllbnQiLCJfcGVuZGluZ0luaXRhbGl6YXRpb25SZXNvbHZlcyIsInN5bmNocm9uaXphdGlvbkZsYWdzIiwiX2FjY291bnRTeW5jaHJvbml6YXRpb25GbGFncyIsIkVxdWl0eUNoYXJ0U3RyZWFtTGlzdGVuZXIiLCJTeW5jaHJvbml6YXRpb25MaXN0ZW5lciIsIm9uRGVhbHNTeW5jaHJvbml6ZWQiLCJpbnN0YW5jZUluZGV4Iiwic3luY2hyb25pemF0aW9uSWQiLCJPYmplY3QiLCJ2YWx1ZXMiLCJmb3JFYWNoIiwiYWNjb3VudExpc3RlbmVyIiwib25Db25uZWN0ZWQiLCJyZXNvbHZlIiwiZXJyIiwib25FcnJvciIsIl9sb2dnZXIiLCJlcnJvciIsIm9uRGlzY29ubmVjdGVkIiwiaGVhbHRoTW9uaXRvciIsImhlYWx0aFN0YXR1cyIsInN5bmNocm9uaXplZCIsIm9uU3ltYm9sUHJpY2VVcGRhdGVkIiwicHJpY2UiLCJlcXVpdHkiLCJicm9rZXJUaW1lIiwiZW5kQnJva2VyVGltZSIsIm9uRXF1aXR5UmVjb3JkQ29tcGxldGVkIiwic3RhcnRCcm9rZXJUaW1lIiwicGVyaW9kcyIsImdldEVxdWl0eUNoYXJ0IiwidW5kZWZpbmVkIiwibGVuZ3RoIiwiUHJvbWlzZSIsInJlcyIsInNldFRpbWVvdXQiLCJvbkVxdWl0eVJlY29yZFVwZGF0ZWQiLCJhY2NvdW50SW5mb3JtYXRpb24iLCJ0ZXJtaW5hbFN0YXRlIiwicHJldmlvdXNJbmZvIiwiYXZlcmFnZUJhbGFuY2UiLCJtaW5CYWxhbmNlIiwibWF4QmFsYW5jZSIsImF2ZXJhZ2VFcXVpdHkiLCJNYXRoIiwiZmxvb3IiLCJtaW5FcXVpdHkiLCJtYXhFcXVpdHkiLCJsYXN0QmFsYW5jZSIsImxhc3RFcXVpdHkiLCJkdXJhdGlvbkluY3JlbWVudCIsIkRhdGUiLCJnZXRUaW1lIiwiZXF1aXR5U3VtIiwiYmFsYW5jZVN1bSIsImJhbGFuY2UiLCJkdXJhdGlvbiIsIm1pbiIsIm1heCIsIm5ld0luZm8iLCJKU09OIiwic3RyaW5naWZ5Iiwib25BY2NvdW50SW5mb3JtYXRpb25VcGRhdGVkIiwibGlzdGVuZXJJZCIsInJhbmRvbXN0cmluZyIsImdlbmVyYXRlIiwiYWNjb3VudExpc3RlbmVycyIsIl9hY2NvdW50c0J5TGlzdGVuZXJJZCIsImFjY291bnQiLCJfbWV0YUFwaSIsIm1ldGF0cmFkZXJBY2NvdW50QXBpIiwiZ2V0QWNjb3VudCIsImlzRGVwbG95ZWQiLCJ3YWl0RGVwbG95ZWQiLCJfZXF1aXR5Q2hhcnRDb25uZWN0aW9ucyIsImdldFN0cmVhbWluZ0Nvbm5lY3Rpb24iLCJzeW5jTGlzdGVuZXIiLCJhZGRTeW5jaHJvbml6YXRpb25MaXN0ZW5lciIsImlzU3luY2hyb25pemVkIiwiY29ubmVjdCIsIndhaXRTeW5jaHJvbml6ZWQiLCJyZXNvbHZlSW5pdGlhbGl6ZSIsImluaXRpYWxpemVQcm9taXNlIiwicmVqIiwicHVzaCIsImluaXRpYWxEYXRhIiwibGFzdEl0ZW0iLCJzbGljZSIsInJlbW92ZUVxdWl0eUNoYXJ0TGlzdGVuZXIiLCJrZXlzIiwiY2xvc2UiLCJjb25zdHJ1Y3RvciIsImRvbWFpbkNsaWVudCIsIm1ldGFBcGkiLCJfZG9tYWluQ2xpZW50IiwiTG9nZ2VyTWFuYWdlciIsImdldExvZ2dlciJdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7ZUFTcUJBOzs7cUVBUEk7Z0ZBQ1c7K0RBQ1Y7Ozs7OztBQUtYLElBQUEsQUFBTUEsMkJBQU4sTUFBTUE7SUFzQm5COzs7O0dBSUMsR0FDREMsb0JBQW9CQyxTQUFTLEVBQUU7UUFDN0IsSUFBRyxDQUFDLElBQUksQ0FBQ0MscUJBQXFCLENBQUNELFVBQVUsRUFBRTtZQUN6QyxJQUFJLENBQUNDLHFCQUFxQixDQUFDRCxVQUFV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