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)
193 lines (192 loc) • 28.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "default", {
enumerable: true,
get: function() {
return LatencyMonitor;
}
});
const _latencyListener = /*#__PURE__*/ _interop_require_default(require("../clients/metaApi/latencyListener"));
const _statisticalReservoir = /*#__PURE__*/ _interop_require_default(require("./reservoir/statisticalReservoir"));
const _reservoir = /*#__PURE__*/ _interop_require_default(require("./reservoir/reservoir"));
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
let LatencyMonitor = class LatencyMonitor extends _latencyListener.default {
/**
* Invoked with latency information when application receives a response to RPC request
* @param {string} accountId account id
* @param {string} type request type
* @param {ResponseTimestamps} timestamps request timestamps object containing latency information
*/ onResponse(accountId, type, timestamps) {
if (!this._requestReservoirs[type]) {
this._requestReservoirs[type] = {
branch: true,
clientLatency: this._initializeReservoirs(),
serverLatency: this._initializeReservoirs()
};
}
if (timestamps.serverProcessingStarted && timestamps.serverProcessingFinished) {
let serverLatency = timestamps.serverProcessingFinished.getTime() - timestamps.serverProcessingStarted.getTime();
this._saveMeasurement(this._requestReservoirs[type].serverLatency, serverLatency);
}
if (timestamps.clientProcessingStarted && timestamps.clientProcessingFinished && timestamps.serverProcessingStarted && timestamps.serverProcessingFinished) {
let serverLatency = timestamps.serverProcessingFinished.getTime() - timestamps.serverProcessingStarted.getTime();
let clientLatency = timestamps.clientProcessingFinished.getTime() - timestamps.clientProcessingStarted.getTime() - serverLatency;
this._saveMeasurement(this._requestReservoirs[type].clientLatency, clientLatency);
}
}
/**
* Returns request processing latencies
* @returns {Object} request processing latencies
*/ get requestLatencies() {
return this._constructLatenciesRecursively(this._requestReservoirs);
}
/**
* Invoked with latency information when application receives symbol price update event
* @param {string} accountId account id
* @param {string} symbol price symbol
* @param {SymbolPriceTimestamps} timestamps timestamps object containing latency information about price streaming
*/ onSymbolPrice(accountId, symbol, timestamps) {
if (timestamps.eventGenerated && timestamps.serverProcessingStarted) {
let brokerLatency = timestamps.serverProcessingStarted.getTime() - timestamps.eventGenerated.getTime();
this._saveMeasurement(this._priceReservoirs.brokerLatency, brokerLatency);
}
if (timestamps.serverProcessingStarted && timestamps.serverProcessingFinished) {
let serverLatency = timestamps.serverProcessingFinished.getTime() - timestamps.serverProcessingStarted.getTime();
this._saveMeasurement(this._priceReservoirs.serverLatency, serverLatency);
}
if (timestamps.serverProcessingFinished && timestamps.clientProcessingFinished) {
let clientLatency = timestamps.clientProcessingFinished.getTime() - timestamps.serverProcessingFinished.getTime();
this._saveMeasurement(this._priceReservoirs.clientLatency, clientLatency);
}
}
/**
* Returns price streaming latencies
* @returns {Object} price streaming latencies
*/ get priceLatencies() {
return this._constructLatenciesRecursively(this._priceReservoirs);
}
/**
* Invoked with latency information when application receives update event
* @param {string} accountId account id
* @param {UpdateTimestamps} timestamps timestamps object containing latency information about update streaming
*/ onUpdate(accountId, timestamps) {
if (timestamps.eventGenerated && timestamps.serverProcessingStarted) {
let brokerLatency = timestamps.serverProcessingStarted.getTime() - timestamps.eventGenerated.getTime();
this._saveMeasurement(this._updateReservoirs.brokerLatency, brokerLatency);
}
if (timestamps.serverProcessingStarted && timestamps.serverProcessingFinished) {
let serverLatency = timestamps.serverProcessingFinished.getTime() - timestamps.serverProcessingStarted.getTime();
this._saveMeasurement(this._updateReservoirs.serverLatency, serverLatency);
}
if (timestamps.serverProcessingFinished && timestamps.clientProcessingFinished) {
let clientLatency = timestamps.clientProcessingFinished.getTime() - timestamps.serverProcessingFinished.getTime();
this._saveMeasurement(this._updateReservoirs.clientLatency, clientLatency);
}
}
/**
* Returns update streaming latencies
* @returns {Object} update streaming latencies
*/ get updateLatencies() {
return this._constructLatenciesRecursively(this._updateReservoirs);
}
/**
* Invoked with latency information when application receives trade response
* @param {string} accountId account id
* @param {TradeTimestamps} timestamps timestamps object containing latency information about a trade
*/ onTrade(accountId, timestamps) {
if (timestamps.clientProcessingStarted && timestamps.serverProcessingStarted) {
let clientLatency = timestamps.serverProcessingStarted.getTime() - timestamps.clientProcessingStarted.getTime();
this._saveMeasurement(this._tradeReservoirs.clientLatency, clientLatency);
}
if (timestamps.serverProcessingStarted && timestamps.tradeStarted) {
let serverLatency = timestamps.tradeStarted.getTime() - timestamps.serverProcessingStarted.getTime();
this._saveMeasurement(this._tradeReservoirs.serverLatency, serverLatency);
}
if (timestamps.tradeStarted && timestamps.tradeExecuted) {
let brokerLatency = timestamps.tradeExecuted.getTime() - timestamps.tradeStarted.getTime();
this._saveMeasurement(this._tradeReservoirs.brokerLatency, brokerLatency);
}
}
/**
* Returns trade latencies
* @returns {Object} trade latencies
*/ get tradeLatencies() {
return this._constructLatenciesRecursively(this._tradeReservoirs);
}
_saveMeasurement(reservoirs, clientLatency) {
for (let e of Object.entries(reservoirs)){
if (e[0] === "branch") {
continue;
}
e[1].percentiles.pushMeasurement(clientLatency);
e[1].reservoir.pushMeasurement(clientLatency);
}
}
_constructLatenciesRecursively(reservoirs) {
let result = {};
for (let e of Object.entries(reservoirs)){
if (e[0] === "branch") {
continue;
}
result[e[0]] = e[1].branch ? this._constructLatenciesRecursively(e[1]) : {
p50: e[1].percentiles.getPercentile(50),
p75: e[1].percentiles.getPercentile(75),
p90: e[1].percentiles.getPercentile(90),
p95: e[1].percentiles.getPercentile(95),
p98: e[1].percentiles.getPercentile(98),
avg: e[1].reservoir.getStatistics().average,
count: e[1].reservoir.getStatistics().count,
min: e[1].reservoir.getStatistics().min,
max: e[1].reservoir.getStatistics().max
};
}
return result;
}
_initializeReservoirs() {
return {
branch: true,
"1h": {
percentiles: new _statisticalReservoir.default(1000, 60 * 60 * 1000),
reservoir: new _reservoir.default(60, 60 * 60 * 1000)
},
"1d": {
percentiles: new _statisticalReservoir.default(1000, 24 * 60 * 60 * 1000),
reservoir: new _reservoir.default(60, 24 * 60 * 60 * 1000)
},
"1w": {
percentiles: new _statisticalReservoir.default(1000, 7 * 24 * 60 * 60 * 1000),
reservoir: new _reservoir.default(60, 7 * 24 * 60 * 60 * 1000)
}
};
}
/**
* Constructs latency monitor instance
*/ constructor(){
super();
this._tradeReservoirs = {
clientLatency: this._initializeReservoirs(),
serverLatency: this._initializeReservoirs(),
brokerLatency: this._initializeReservoirs()
};
this._updateReservoirs = {
clientLatency: this._initializeReservoirs(),
serverLatency: this._initializeReservoirs(),
brokerLatency: this._initializeReservoirs()
};
this._priceReservoirs = {
clientLatency: this._initializeReservoirs(),
serverLatency: this._initializeReservoirs(),
brokerLatency: this._initializeReservoirs()
};
this._requestReservoirs = {
branch: true
};
}
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCBMYXRlbmN5TGlzdGVuZXIgZnJvbSAnLi4vY2xpZW50cy9tZXRhQXBpL2xhdGVuY3lMaXN0ZW5lcic7XG5pbXBvcnQgU3RhdGlzdGljYWxSZXNlcnZvaXIgZnJvbSAnLi9yZXNlcnZvaXIvc3RhdGlzdGljYWxSZXNlcnZvaXInO1xuaW1wb3J0IFJlc2Vydm9pciBmcm9tICcuL3Jlc2Vydm9pci9yZXNlcnZvaXInO1xuXG4vKipcbiAqIFJlc3BvbnNpYmxlIGZvciBtb25pdG9yaW5nIE1ldGFBcGkgYXBwbGljYXRpb24gbGF0ZW5jaWVzXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIExhdGVuY3lNb25pdG9yIGV4dGVuZHMgTGF0ZW5jeUxpc3RlbmVyIHtcblxuICAvKipcbiAgICogQ29uc3RydWN0cyBsYXRlbmN5IG1vbml0b3IgaW5zdGFuY2VcbiAgICovXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHN1cGVyKCk7XG4gICAgdGhpcy5fdHJhZGVSZXNlcnZvaXJzID0ge1xuICAgICAgY2xpZW50TGF0ZW5jeTogdGhpcy5faW5pdGlhbGl6ZVJlc2Vydm9pcnMoKSxcbiAgICAgIHNlcnZlckxhdGVuY3k6IHRoaXMuX2luaXRpYWxpemVSZXNlcnZvaXJzKCksXG4gICAgICBicm9rZXJMYXRlbmN5OiB0aGlzLl9pbml0aWFsaXplUmVzZXJ2b2lycygpXG4gICAgfTtcbiAgICB0aGlzLl91cGRhdGVSZXNlcnZvaXJzID0ge1xuICAgICAgY2xpZW50TGF0ZW5jeTogdGhpcy5faW5pdGlhbGl6ZVJlc2Vydm9pcnMoKSxcbiAgICAgIHNlcnZlckxhdGVuY3k6IHRoaXMuX2luaXRpYWxpemVSZXNlcnZvaXJzKCksXG4gICAgICBicm9rZXJMYXRlbmN5OiB0aGlzLl9pbml0aWFsaXplUmVzZXJ2b2lycygpXG4gICAgfTtcbiAgICB0aGlzLl9wcmljZVJlc2Vydm9pcnMgPSB7XG4gICAgICBjbGllbnRMYXRlbmN5OiB0aGlzLl9pbml0aWFsaXplUmVzZXJ2b2lycygpLFxuICAgICAgc2VydmVyTGF0ZW5jeTogdGhpcy5faW5pdGlhbGl6ZVJlc2Vydm9pcnMoKSxcbiAgICAgIGJyb2tlckxhdGVuY3k6IHRoaXMuX2luaXRpYWxpemVSZXNlcnZvaXJzKClcbiAgICB9O1xuICAgIHRoaXMuX3JlcXVlc3RSZXNlcnZvaXJzID0ge1xuICAgICAgYnJhbmNoOiB0cnVlXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbnZva2VkIHdpdGggbGF0ZW5jeSBpbmZvcm1hdGlvbiB3aGVuIGFwcGxpY2F0aW9uIHJlY2VpdmVzIGEgcmVzcG9uc2UgdG8gUlBDIHJlcXVlc3RcbiAgICogQHBhcmFtIHtzdHJpbmd9IGFjY291bnRJZCBhY2NvdW50IGlkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIHJlcXVlc3QgdHlwZVxuICAgKiBAcGFyYW0ge1Jlc3BvbnNlVGltZXN0YW1wc30gdGltZXN0YW1wcyByZXF1ZXN0IHRpbWVzdGFtcHMgb2JqZWN0IGNvbnRhaW5pbmcgbGF0ZW5jeSBpbmZvcm1hdGlvblxuICAgKi9cbiAgb25SZXNwb25zZShhY2NvdW50SWQsIHR5cGUsIHRpbWVzdGFtcHMpIHtcbiAgICBpZiAoIXRoaXMuX3JlcXVlc3RSZXNlcnZvaXJzW3R5cGVdKSB7XG4gICAgICB0aGlzLl9yZXF1ZXN0UmVzZXJ2b2lyc1t0eXBlXSA9IHtcbiAgICAgICAgYnJhbmNoOiB0cnVlLFxuICAgICAgICBjbGllbnRMYXRlbmN5OiB0aGlzLl9pbml0aWFsaXplUmVzZXJ2b2lycygpLFxuICAgICAgICBzZXJ2ZXJMYXRlbmN5OiB0aGlzLl9pbml0aWFsaXplUmVzZXJ2b2lycygpXG4gICAgICB9O1xuICAgIH1cbiAgICBpZiAodGltZXN0YW1wcy5zZXJ2ZXJQcm9jZXNzaW5nU3RhcnRlZCAmJiB0aW1lc3RhbXBzLnNlcnZlclByb2Nlc3NpbmdGaW5pc2hlZCkge1xuICAgICAgbGV0IHNlcnZlckxhdGVuY3kgPSB0aW1lc3RhbXBzLnNlcnZlclByb2Nlc3NpbmdGaW5pc2hlZC5nZXRUaW1lKCkgLSB0aW1lc3RhbXBzLnNlcnZlclByb2Nlc3NpbmdTdGFydGVkLmdldFRpbWUoKTtcbiAgICAgIHRoaXMuX3NhdmVNZWFzdXJlbWVudCh0aGlzLl9yZXF1ZXN0UmVzZXJ2b2lyc1t0eXBlXS5zZXJ2ZXJMYXRlbmN5LCBzZXJ2ZXJMYXRlbmN5KTtcbiAgICB9XG4gICAgaWYgKHRpbWVzdGFtcHMuY2xpZW50UHJvY2Vzc2luZ1N0YXJ0ZWQgJiYgdGltZXN0YW1wcy5jbGllbnRQcm9jZXNzaW5nRmluaXNoZWQgJiZcbiAgICAgIHRpbWVzdGFtcHMuc2VydmVyUHJvY2Vzc2luZ1N0YXJ0ZWQgJiYgdGltZXN0YW1wcy5zZXJ2ZXJQcm9jZXNzaW5nRmluaXNoZWQpIHtcbiAgICAgIGxldCBzZXJ2ZXJMYXRlbmN5ID0gdGltZXN0YW1wcy5zZXJ2ZXJQcm9jZXNzaW5nRmluaXNoZWQuZ2V0VGltZSgpIC0gdGltZXN0YW1wcy5zZXJ2ZXJQcm9jZXNzaW5nU3RhcnRlZC5nZXRUaW1lKCk7XG4gICAgICBsZXQgY2xpZW50TGF0ZW5jeSA9IHRpbWVzdGFtcHMuY2xpZW50UHJvY2Vzc2luZ0ZpbmlzaGVkLmdldFRpbWUoKSAtIHRpbWVzdGFtcHMuY2xpZW50UHJvY2Vzc2luZ1N0YXJ0ZWQuZ2V0VGltZSgpIC1cbiAgICAgICAgc2VydmVyTGF0ZW5jeTtcbiAgICAgIHRoaXMuX3NhdmVNZWFzdXJlbWVudCh0aGlzLl9yZXF1ZXN0UmVzZXJ2b2lyc1t0eXBlXS5jbGllbnRMYXRlbmN5LCBjbGllbnRMYXRlbmN5KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyByZXF1ZXN0IHByb2Nlc3NpbmcgbGF0ZW5jaWVzXG4gICAqIEByZXR1cm5zIHtPYmplY3R9IHJlcXVlc3QgcHJvY2Vzc2luZyBsYXRlbmNpZXNcbiAgICovXG4gIGdldCByZXF1ZXN0TGF0ZW5jaWVzKCkge1xuICAgIHJldHVybiB0aGlzLl9jb25zdHJ1Y3RMYXRlbmNpZXNSZWN1cnNpdmVseSh0aGlzLl9yZXF1ZXN0UmVzZXJ2b2lycyk7XG4gIH1cblxuICAvKipcbiAgICogSW52b2tlZCB3aXRoIGxhdGVuY3kgaW5mb3JtYXRpb24gd2hlbiBhcHBsaWNhdGlvbiByZWNlaXZlcyBzeW1ib2wgcHJpY2UgdXBkYXRlIGV2ZW50XG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhY2NvdW50SWQgYWNjb3VudCBpZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gc3ltYm9sIHByaWNlIHN5bWJvbFxuICAgKiBAcGFyYW0ge1N5bWJvbFByaWNlVGltZXN0YW1wc30gdGltZXN0YW1wcyB0aW1lc3RhbXBzIG9iamVjdCBjb250YWluaW5nIGxhdGVuY3kgaW5mb3JtYXRpb24gYWJvdXQgcHJpY2Ugc3RyZWFtaW5nXG4gICAqL1xuICBvblN5bWJvbFByaWNlKGFjY291bnRJZCwgc3ltYm9sLCB0aW1lc3RhbXBzKSB7XG4gICAgaWYgKHRpbWVzdGFtcHMuZXZlbnRHZW5lcmF0ZWQgJiYgdGltZXN0YW1wcy5zZXJ2ZXJQcm9jZXNzaW5nU3RhcnRlZCkge1xuICAgICAgbGV0IGJyb2tlckxhdGVuY3kgPSB0aW1lc3RhbXBzLnNlcnZlclByb2Nlc3NpbmdTdGFydGVkLmdldFRpbWUoKSAtIHRpbWVzdGFtcHMuZXZlbnRHZW5lcmF0ZWQuZ2V0VGltZSgpO1xuICAgICAgdGhpcy5fc2F2ZU1lYXN1cmVtZW50KHRoaXMuX3ByaWNlUmVzZXJ2b2lycy5icm9rZXJMYXRlbmN5LCBicm9rZXJMYXRlbmN5KTtcbiAgICB9XG4gICAgaWYgKHRpbWVzdGFtcHMuc2VydmVyUHJvY2Vzc2luZ1N0YXJ0ZWQgJiYgdGltZXN0YW1wcy5zZXJ2ZXJQcm9jZXNzaW5nRmluaXNoZWQpIHtcbiAgICAgIGxldCBzZXJ2ZXJMYXRlbmN5ID0gdGltZXN0YW1wcy5zZXJ2ZXJQcm9jZXNzaW5nRmluaXNoZWQuZ2V0VGltZSgpIC0gdGltZXN0YW1wcy5zZXJ2ZXJQcm9jZXNzaW5nU3RhcnRlZC5nZXRUaW1lKCk7XG4gICAgICB0aGlzLl9zYXZlTWVhc3VyZW1lbnQodGhpcy5fcHJpY2VSZXNlcnZvaXJzLnNlcnZlckxhdGVuY3ksIHNlcnZlckxhdGVuY3kpO1xuICAgIH1cbiAgICBpZiAodGltZXN0YW1wcy5zZXJ2ZXJQcm9jZXNzaW5nRmluaXNoZWQgJiYgdGltZXN0YW1wcy5jbGllbnRQcm9jZXNzaW5nRmluaXNoZWQpIHtcbiAgICAgIGxldCBjbGllbnRMYXRlbmN5ID0gdGltZXN0YW1wcy5jbGllbnRQcm9jZXNzaW5nRmluaXNoZWQuZ2V0VGltZSgpIC0gdGltZXN0YW1wcy5zZXJ2ZXJQcm9jZXNzaW5nRmluaXNoZWQuZ2V0VGltZSgpO1xuICAgICAgdGhpcy5fc2F2ZU1lYXN1cmVtZW50KHRoaXMuX3ByaWNlUmVzZXJ2b2lycy5jbGllbnRMYXRlbmN5LCBjbGllbnRMYXRlbmN5KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBwcmljZSBzdHJlYW1pbmcgbGF0ZW5jaWVzXG4gICAqIEByZXR1cm5zIHtPYmplY3R9IHByaWNlIHN0cmVhbWluZyBsYXRlbmNpZXNcbiAgICovXG4gIGdldCBwcmljZUxhdGVuY2llcygpIHtcbiAgICByZXR1cm4gdGhpcy5fY29uc3RydWN0TGF0ZW5jaWVzUmVjdXJzaXZlbHkodGhpcy5fcHJpY2VSZXNlcnZvaXJzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbnZva2VkIHdpdGggbGF0ZW5jeSBpbmZvcm1hdGlvbiB3aGVuIGFwcGxpY2F0aW9uIHJlY2VpdmVzIHVwZGF0ZSBldmVudFxuICAgKiBAcGFyYW0ge3N0cmluZ30gYWNjb3VudElkIGFjY291bnQgaWRcbiAgICogQHBhcmFtIHtVcGRhdGVUaW1lc3RhbXBzfSB0aW1lc3RhbXBzIHRpbWVzdGFtcHMgb2JqZWN0IGNvbnRhaW5pbmcgbGF0ZW5jeSBpbmZvcm1hdGlvbiBhYm91dCB1cGRhdGUgc3RyZWFtaW5nXG4gICAqL1xuICBvblVwZGF0ZShhY2NvdW50SWQsIHRpbWVzdGFtcHMpIHtcbiAgICBpZiAodGltZXN0YW1wcy5ldmVudEdlbmVyYXRlZCAmJiB0aW1lc3RhbXBzLnNlcnZlclByb2Nlc3NpbmdTdGFydGVkKSB7XG4gICAgICBsZXQgYnJva2VyTGF0ZW5jeSA9IHRpbWVzdGFtcHMuc2VydmVyUHJvY2Vzc2luZ1N0YXJ0ZWQuZ2V0VGltZSgpIC0gdGltZXN0YW1wcy5ldmVudEdlbmVyYXRlZC5nZXRUaW1lKCk7XG4gICAgICB0aGlzLl9zYXZlTWVhc3VyZW1lbnQodGhpcy5fdXBkYXRlUmVzZXJ2b2lycy5icm9rZXJMYXRlbmN5LCBicm9rZXJMYXRlbmN5KTtcbiAgICB9XG4gICAgaWYgKHRpbWVzdGFtcHMuc2VydmVyUHJvY2Vzc2luZ1N0YXJ0ZWQgJiYgdGltZXN0YW1wcy5zZXJ2ZXJQcm9jZXNzaW5nRmluaXNoZWQpIHtcbiAgICAgIGxldCBzZXJ2ZXJMYXRlbmN5ID0gdGltZXN0YW1wcy5zZXJ2ZXJQcm9jZXNzaW5nRmluaXNoZWQuZ2V0VGltZSgpIC0gdGltZXN0YW1wcy5zZXJ2ZXJQcm9jZXNzaW5nU3RhcnRlZC5nZXRUaW1lKCk7XG4gICAgICB0aGlzLl9zYXZlTWVhc3VyZW1lbnQodGhpcy5fdXBkYXRlUmVzZXJ2b2lycy5zZXJ2ZXJMYXRlbmN5LCBzZXJ2ZXJMYXRlbmN5KTtcbiAgICB9XG4gICAgaWYgKHRpbWVzdGFtcHMuc2VydmVyUHJvY2Vzc2luZ0ZpbmlzaGVkICYmIHRpbWVzdGFtcHMuY2xpZW50UHJvY2Vzc2luZ0ZpbmlzaGVkKSB7XG4gICAgICBsZXQgY2xpZW50TGF0ZW5jeSA9IHRpbWVzdGFtcHMuY2xpZW50UHJvY2Vzc2luZ0ZpbmlzaGVkLmdldFRpbWUoKSAtIHRpbWVzdGFtcHMuc2VydmVyUHJvY2Vzc2luZ0ZpbmlzaGVkLmdldFRpbWUoKTtcbiAgICAgIHRoaXMuX3NhdmVNZWFzdXJlbWVudCh0aGlzLl91cGRhdGVSZXNlcnZvaXJzLmNsaWVudExhdGVuY3ksIGNsaWVudExhdGVuY3kpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHVwZGF0ZSBzdHJlYW1pbmcgbGF0ZW5jaWVzXG4gICAqIEByZXR1cm5zIHtPYmplY3R9IHVwZGF0ZSBzdHJlYW1pbmcgbGF0ZW5jaWVzXG4gICAqL1xuICBnZXQgdXBkYXRlTGF0ZW5jaWVzKCkge1xuICAgIHJldHVybiB0aGlzLl9jb25zdHJ1Y3RMYXRlbmNpZXNSZWN1cnNpdmVseSh0aGlzLl91cGRhdGVSZXNlcnZvaXJzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbnZva2VkIHdpdGggbGF0ZW5jeSBpbmZvcm1hdGlvbiB3aGVuIGFwcGxpY2F0aW9uIHJlY2VpdmVzIHRyYWRlIHJlc3BvbnNlXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhY2NvdW50SWQgYWNjb3VudCBpZFxuICAgKiBAcGFyYW0ge1RyYWRlVGltZXN0YW1wc30gdGltZXN0YW1wcyB0aW1lc3RhbXBzIG9iamVjdCBjb250YWluaW5nIGxhdGVuY3kgaW5mb3JtYXRpb24gYWJvdXQgYSB0cmFkZVxuICAgKi9cbiAgb25UcmFkZShhY2NvdW50SWQsIHRpbWVzdGFtcHMpIHtcbiAgICBpZiAodGltZXN0YW1wcy5jbGllbnRQcm9jZXNzaW5nU3RhcnRlZCAmJiB0aW1lc3RhbXBzLnNlcnZlclByb2Nlc3NpbmdTdGFydGVkKSB7XG4gICAgICBsZXQgY2xpZW50TGF0ZW5jeSA9IHRpbWVzdGFtcHMuc2VydmVyUHJvY2Vzc2luZ1N0YXJ0ZWQuZ2V0VGltZSgpIC0gdGltZXN0YW1wcy5jbGllbnRQcm9jZXNzaW5nU3RhcnRlZC5nZXRUaW1lKCk7XG4gICAgICB0aGlzLl9zYXZlTWVhc3VyZW1lbnQodGhpcy5fdHJhZGVSZXNlcnZvaXJzLmNsaWVudExhdGVuY3ksIGNsaWVudExhdGVuY3kpO1xuICAgIH1cbiAgICBpZiAodGltZXN0YW1wcy5zZXJ2ZXJQcm9jZXNzaW5nU3RhcnRlZCAmJiB0aW1lc3RhbXBzLnRyYWRlU3RhcnRlZCkge1xuICAgICAgbGV0IHNlcnZlckxhdGVuY3kgPSB0aW1lc3RhbXBzLnRyYWRlU3RhcnRlZC5nZXRUaW1lKCkgLSB0aW1lc3RhbXBzLnNlcnZlclByb2Nlc3NpbmdTdGFydGVkLmdldFRpbWUoKTtcbiAgICAgIHRoaXMuX3NhdmVNZWFzdXJlbWVudCh0aGlzLl90cmFkZVJlc2Vydm9pcnMuc2VydmVyTGF0ZW5jeSwgc2VydmVyTGF0ZW5jeSk7XG4gICAgfVxuICAgIGlmICh0aW1lc3RhbXBzLnRyYWRlU3RhcnRlZCAmJiB0aW1lc3RhbXBzLnRyYWRlRXhlY3V0ZWQpIHtcbiAgICAgIGxldCBicm9rZXJMYXRlbmN5ID0gdGltZXN0YW1wcy50cmFkZUV4ZWN1dGVkLmdldFRpbWUoKSAtIHRpbWVzdGFtcHMudHJhZGVTdGFydGVkLmdldFRpbWUoKTtcbiAgICAgIHRoaXMuX3NhdmVNZWFzdXJlbWVudCh0aGlzLl90cmFkZVJlc2Vydm9pcnMuYnJva2VyTGF0ZW5jeSwgYnJva2VyTGF0ZW5jeSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdHJhZGUgbGF0ZW5jaWVzXG4gICAqIEByZXR1cm5zIHtPYmplY3R9IHRyYWRlIGxhdGVuY2llc1xuICAgKi9cbiAgZ2V0IHRyYWRlTGF0ZW5jaWVzKCkge1xuICAgIHJldHVybiB0aGlzLl9jb25zdHJ1Y3RMYXRlbmNpZXNSZWN1cnNpdmVseSh0aGlzLl90cmFkZVJlc2Vydm9pcnMpO1xuICB9XG5cbiAgX3NhdmVNZWFzdXJlbWVudChyZXNlcnZvaXJzLCBjbGllbnRMYXRlbmN5KSB7XG4gICAgZm9yIChsZXQgZSBvZiBPYmplY3QuZW50cmllcyhyZXNlcnZvaXJzKSkge1xuICAgICAgaWYgKGVbMF0gPT09ICdicmFuY2gnKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgZVsxXS5wZXJjZW50aWxlcy5wdXNoTWVhc3VyZW1lbnQoY2xpZW50TGF0ZW5jeSk7XG4gICAgICBlWzFdLnJlc2Vydm9pci5wdXNoTWVhc3VyZW1lbnQoY2xpZW50TGF0ZW5jeSk7XG4gICAgfVxuICB9XG5cbiAgX2NvbnN0cnVjdExhdGVuY2llc1JlY3Vyc2l2ZWx5KHJlc2Vydm9pcnMpIHtcbiAgICBsZXQgcmVzdWx0ID0ge307XG4gICAgZm9yIChsZXQgZSBvZiBPYmplY3QuZW50cmllcyhyZXNlcnZvaXJzKSkge1xuICAgICAgaWYgKGVbMF0gPT09ICdicmFuY2gnKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgcmVzdWx0W2VbMF1dID0gZVsxXS5icmFuY2ggPyB0aGlzLl9jb25zdHJ1Y3RMYXRlbmNpZXNSZWN1cnNpdmVseShlWzFdKSA6IHtcbiAgICAgICAgcDUwOiBlWzFdLnBlcmNlbnRpbGVzLmdldFBlcmNlbnRpbGUoNTApLFxuICAgICAgICBwNzU6IGVbMV0ucGVyY2VudGlsZXMuZ2V0UGVyY2VudGlsZSg3NSksXG4gICAgICAgIHA5MDogZVsxXS5wZXJjZW50aWxlcy5nZXRQZXJjZW50aWxlKDkwKSxcbiAgICAgICAgcDk1OiBlWzFdLnBlcmNlbnRpbGVzLmdldFBlcmNlbnRpbGUoOTUpLFxuICAgICAgICBwOTg6IGVbMV0ucGVyY2VudGlsZXMuZ2V0UGVyY2VudGlsZSg5OCksXG4gICAgICAgIGF2ZzogZVsxXS5yZXNlcnZvaXIuZ2V0U3RhdGlzdGljcygpLmF2ZXJhZ2UsXG4gICAgICAgIGNvdW50OiBlWzFdLnJlc2Vydm9pci5nZXRTdGF0aXN0aWNzKCkuY291bnQsXG4gICAgICAgIG1pbjogZVsxXS5yZXNlcnZvaXIuZ2V0U3RhdGlzdGljcygpLm1pbixcbiAgICAgICAgbWF4OiBlWzFdLnJlc2Vydm9pci5nZXRTdGF0aXN0aWNzKCkubWF4XG4gICAgICB9O1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgX2luaXRpYWxpemVSZXNlcnZvaXJzKCkge1xuICAgIHJldHVybiB7XG4gICAgICBicmFuY2g6IHRydWUsXG4gICAgICAnMWgnOiB7XG4gICAgICAgIHBlcmNlbnRpbGVzOiBuZXcgU3RhdGlzdGljYWxSZXNlcnZvaXIoMTAwMCwgNjAgKiA2MCAqIDEwMDApLFxuICAgICAgICByZXNlcnZvaXI6IG5ldyBSZXNlcnZvaXIoNjAsIDYwICogNjAgKiAxMDAwKVxuICAgICAgfSxcbiAgICAgICcxZCc6IHtcbiAgICAgICAgcGVyY2VudGlsZXM6IG5ldyBTdGF0aXN0aWNhbFJlc2Vydm9pcigxMDAwLCAyNCAqIDYwICogNjAgKiAxMDAwKSxcbiAgICAgICAgcmVzZXJ2b2lyOiBuZXcgUmVzZXJ2b2lyKDYwLCAyNCAqIDYwICogNjAgKiAxMDAwKVxuICAgICAgfSxcbiAgICAgICcxdyc6IHtcbiAgICAgICAgcGVyY2VudGlsZXM6IG5ldyBTdGF0aXN0aWNhbFJlc2Vydm9pcigxMDAwLCA3ICogMjQgKiA2MCAqIDYwICogMTAwMCksXG4gICAgICAgIHJlc2Vydm9pcjogbmV3IFJlc2Vydm9pcig2MCwgNyAqIDI0ICogNjAgKiA2MCAqIDEwMDApXG4gICAgICB9XG4gICAgfTtcbiAgfVxuXG59XG4iXSwibmFtZXMiOlsiTGF0ZW5jeU1vbml0b3IiLCJMYXRlbmN5TGlzdGVuZXIiLCJvblJlc3BvbnNlIiwiYWNjb3VudElkIiwidHlwZSIsInRpbWVzdGFtcHMiLCJfcmVxdWVzdFJlc2Vydm9pcnMiLCJicmFuY2giLCJjbGllbnRMYXRlbmN5IiwiX2luaXRpYWxpemVSZXNlcnZvaXJzIiwic2VydmVyTGF0ZW5jeSIsInNlcnZlclByb2Nlc3NpbmdTdGFydGVkIiwic2VydmVyUHJvY2Vzc2luZ0ZpbmlzaGVkIiwiZ2V0VGltZSIsIl9zYXZlTWVhc3VyZW1lbnQiLCJjbGllbnRQcm9jZXNzaW5nU3RhcnRlZCIsImNsaWVudFByb2Nlc3NpbmdGaW5pc2hlZCIsInJlcXVlc3RMYXRlbmNpZXMiLCJfY29uc3RydWN0TGF0ZW5jaWVzUmVjdXJzaXZlbHkiLCJvblN5bWJvbFByaWNlIiwic3ltYm9sIiwiZXZlbnRHZW5lcmF0ZWQiLCJicm9rZXJMYXRlbmN5IiwiX3ByaWNlUmVzZXJ2b2lycyIsInByaWNlTGF0ZW5jaWVzIiwib25VcGRhdGUiLCJfdXBkYXRlUmVzZXJ2b2lycyIsInVwZGF0ZUxhdGVuY2llcyIsIm9uVHJhZGUiLCJfdHJhZGVSZXNlcnZvaXJzIiwidHJhZGVTdGFydGVkIiwidHJhZGVFeGVjdXRlZCIsInRyYWRlTGF0ZW5jaWVzIiwicmVzZXJ2b2lycyIsImUiLCJPYmplY3QiLCJlbnRyaWVzIiwicGVyY2VudGlsZXMiLCJwdXNoTWVhc3VyZW1lbnQiLCJyZXNlcnZvaXIiLCJyZXN1bHQiLCJwNTAiLCJnZXRQZXJjZW50aWxlIiwicDc1IiwicDkwIiwicDk1IiwicDk4IiwiYXZnIiwiZ2V0U3RhdGlzdGljcyIsImF2ZXJhZ2UiLCJjb3VudCIsIm1pbiIsIm1heCIsIlN0YXRpc3RpY2FsUmVzZXJ2b2lyIiwiUmVzZXJ2b2lyIiwiY29uc3RydWN0b3IiXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7O2VBU3FCQTs7O3dFQVBPOzZFQUNLO2tFQUNYOzs7Ozs7QUFLUCxJQUFBLEFBQU1BLGlCQUFOLE1BQU1BLHVCQUF1QkMsd0JBQWU7SUEyQnpEOzs7OztHQUtDLEdBQ0RDLFdBQVdDLFNBQVMsRUFBRUMsSUFBSSxFQUFFQyxVQUFVLEVBQUU7UUFDdEMsSUFBSSxDQUFDLElBQUksQ0FBQ0Msa0JBQWtCLENBQUNGLEtBQUssRUFBRTtZQUNsQyxJQUFJLENBQUNFLGtCQUFrQixDQUFDRixLQUFLLEdBQUc7Z0JBQzlCRyxRQUFRO2dCQUNSQyxlQUFlLElBQUksQ0FBQ0MscUJBQXFCO2dCQUN6Q0MsZUFBZSxJQUFJLENBQUNELHFCQUFxQjtZQUMzQztRQUNGO1FBQ0EsSUFBSUosV0FBV00sdUJBQXVCLElBQUlOLFdBQVdPLHdCQUF3QixFQUFFO1lBQzdFLElBQUlGLGdCQUFnQkwsV0FBV08sd0JBQXdCLENBQUNDLE9BQU8sS0FBS1IsV0FBV00sdUJBQXVCLENBQUNFLE9BQU87WUFDOUcsSUFBSSxDQUFDQyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUNSLGtCQUFrQixDQUFDRixLQUFLLENBQUNNLGFBQWEsRUFBRUE7UUFDckU7UUFDQSxJQUFJTCxXQUFXVSx1QkFBdUIsSUFBSVYsV0FBV1csd0JBQXdCLElBQzNFWCxXQUFXTSx1QkFBdUIs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