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)
211 lines (210 loc) • 30.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "default", {
enumerable: true,
get: function() {
return EquityBalanceStreamManager;
}
});
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 EquityBalanceStreamManager = class EquityBalanceStreamManager {
/**
* Returns listeners for account
* @param {String} accountId account id to return listeners for
* @returns {{[listenerId: string]: EquityBalanceListener}} dictionary of account equity balance event listeners
*/ getAccountListeners(accountId) {
if (!this._equityBalanceListeners[accountId]) {
this._equityBalanceListeners[accountId] = {};
}
return this._equityBalanceListeners[accountId];
}
/**
* Adds an equity balance event listener
* @param {EquityBalanceListener} listener equity balance event listener
* @param {String} accountId account id
* @returns {Promise<string>} listener id
*/ // eslint-disable-next-line max-statements, complexity
async addEquityBalanceListener(listener, accountId) {
if (!this._equityBalanceCaches[accountId]) {
this._equityBalanceCaches[accountId] = {
balance: null,
equity: null,
pendingInitalizationResolves: []
};
}
const cache = this._equityBalanceCaches[accountId];
let connection = null;
let retryIntervalInSeconds = this._retryIntervalInSeconds;
const getAccountListeners = ()=>this.getAccountListeners(accountId);
const pendingInitalizationResolves = this._pendingInitalizationResolves;
const synchronizationFlags = this._accountSynchronizationFlags;
const processEquityBalanceEvent = async (equity, balance)=>{
if (this._equityBalanceCaches[accountId]) {
if (equity !== cache.equity || balance && balance !== cache.balance) {
cache.equity = equity;
if (balance) {
cache.balance = balance;
}
if (cache.equity !== null && cache.balance !== null) {
Object.values(getAccountListeners()).forEach((accountListener)=>{
accountListener.onEquityOrBalanceUpdated({
equity: cache.equity,
balance: cache.balance
});
});
}
}
}
};
let EquityBalanceStreamListener = class EquityBalanceStreamListener 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 balance 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 balance 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];
}
} catch (err) {
Object.values(getAccountListeners()).forEach((accountListener)=>{
accountListener.onError(err);
});
this._logger.error("Error processing onSymbolPriceUpdated event for " + `equity balance listener for account ${accountId}`, err);
}
// price data only contains equity
await processEquityBalanceEvent(price.equity);
}
async onAccountInformationUpdated(instanceIndex, accountInformation) {
await processEquityBalanceEvent(accountInformation.equity, accountInformation.balance);
}
};
const listenerId = _randomstring.default.generate(10);
const accountListeners = this.getAccountListeners(accountId);
accountListeners[listenerId] = listener;
this._accountsByListenerId[listenerId] = accountId;
let isDeployed = false;
const account = await this._metaApi.metatraderAccountApi.getAccount(accountId);
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._equityBalanceConnections[accountId]) {
retryIntervalInSeconds = this._retryIntervalInSeconds;
connection = account.getStreamingConnection();
this._equityBalanceConnections[accountId] = connection;
const syncListener = new EquityBalanceStreamListener();
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 balance 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._equityBalanceConnections[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;
}
}
return listenerId;
}
/**
* Removes equity balance event listener by id
* @param {String} listenerId listener id
*/ removeEquityBalanceListener(listenerId) {
if (this._accountsByListenerId[listenerId]) {
const accountId = this._accountsByListenerId[listenerId];
delete this._accountSynchronizationFlags[accountId];
delete this._accountsByListenerId[listenerId];
if (this._equityBalanceListeners[accountId]) {
delete this._equityBalanceListeners[accountId][listenerId];
}
if (this._equityBalanceConnections[accountId] && !Object.keys(this._equityBalanceListeners[accountId]).length) {
this._equityBalanceConnections[accountId].close();
delete this._equityBalanceConnections[accountId];
}
}
}
/**
* Constructs equity balance event listener manager instance
* @param {DomainClient} domainClient domain client
* @param {MetaApi} metaApi metaApi SDK instance
*/ constructor(domainClient, metaApi){
this._domainClient = domainClient;
this._metaApi = metaApi;
this._equityBalanceListeners = {};
this._accountsByListenerId = {};
this._equityBalanceConnections = {};
this._equityBalanceCaches = {};
this._accountSynchronizationFlags = {};
this._pendingInitalizationResolves = {};
this._retryIntervalInSeconds = 1;
this._logger = _logger.default.getLogger("EquityBalanceStreamManager");
}
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCByYW5kb21zdHJpbmcgZnJvbSAncmFuZG9tc3RyaW5nJztcbmltcG9ydCBTeW5jaHJvbml6YXRpb25MaXN0ZW5lciBmcm9tICcuLi8uLi8uLi9jbGllbnRzL21ldGFBcGkvc3luY2hyb25pemF0aW9uTGlzdGVuZXInO1xuaW1wb3J0IExvZ2dlck1hbmFnZXIgZnJvbSAnLi4vLi4vLi4vbG9nZ2VyJztcblxuLyoqXG4gKiBNYW5hZ2VyIGZvciBoYW5kbGluZyBlcXVpdHkgYmFsYW5jZSBldmVudCBsaXN0ZW5lcnNcbiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgRXF1aXR5QmFsYW5jZVN0cmVhbU1hbmFnZXIge1xuXG4gIC8qKlxuICAgKiBDb25zdHJ1Y3RzIGVxdWl0eSBiYWxhbmNlIGV2ZW50IGxpc3RlbmVyIG1hbmFnZXIgaW5zdGFuY2VcbiAgICogQHBhcmFtIHtEb21haW5DbGllbnR9IGRvbWFpbkNsaWVudCBkb21haW4gY2xpZW50XG4gICAqIEBwYXJhbSB7TWV0YUFwaX0gbWV0YUFwaSBtZXRhQXBpIFNESyBpbnN0YW5jZVxuICAgKi9cbiAgY29uc3RydWN0b3IoZG9tYWluQ2xpZW50LCBtZXRhQXBpKSB7XG4gICAgdGhpcy5fZG9tYWluQ2xpZW50ID0gZG9tYWluQ2xpZW50O1xuICAgIHRoaXMuX21ldGFBcGkgPSBtZXRhQXBpO1xuICAgIHRoaXMuX2VxdWl0eUJhbGFuY2VMaXN0ZW5lcnMgPSB7fTtcbiAgICB0aGlzLl9hY2NvdW50c0J5TGlzdGVuZXJJZCA9IHt9O1xuICAgIHRoaXMuX2VxdWl0eUJhbGFuY2VDb25uZWN0aW9ucyA9IHt9O1xuICAgIHRoaXMuX2VxdWl0eUJhbGFuY2VDYWNoZXMgPSB7fTtcbiAgICB0aGlzLl9hY2NvdW50U3luY2hyb25pemF0aW9uRmxhZ3MgPSB7fTtcbiAgICB0aGlzLl9wZW5kaW5nSW5pdGFsaXphdGlvblJlc29sdmVzID0ge307XG4gICAgdGhpcy5fcmV0cnlJbnRlcnZhbEluU2Vjb25kcyA9IDE7XG4gICAgdGhpcy5fbG9nZ2VyID0gTG9nZ2VyTWFuYWdlci5nZXRMb2dnZXIoJ0VxdWl0eUJhbGFuY2VTdHJlYW1NYW5hZ2VyJyk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBsaXN0ZW5lcnMgZm9yIGFjY291bnRcbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBhY2NvdW50IGlkIHRvIHJldHVybiBsaXN0ZW5lcnMgZm9yXG4gICAqIEByZXR1cm5zIHt7W2xpc3RlbmVySWQ6IHN0cmluZ106IEVxdWl0eUJhbGFuY2VMaXN0ZW5lcn19IGRpY3Rpb25hcnkgb2YgYWNjb3VudCBlcXVpdHkgYmFsYW5jZSBldmVudCBsaXN0ZW5lcnNcbiAgICovXG4gIGdldEFjY291bnRMaXN0ZW5lcnMoYWNjb3VudElkKSB7XG4gICAgaWYoIXRoaXMuX2VxdWl0eUJhbGFuY2VMaXN0ZW5lcnNbYWNjb3VudElkXSkge1xuICAgICAgdGhpcy5fZXF1aXR5QmFsYW5jZUxpc3RlbmVyc1thY2NvdW50SWRdID0ge307XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9lcXVpdHlCYWxhbmNlTGlzdGVuZXJzW2FjY291bnRJZF07XG4gIH1cblxuICAvKipcbiAgICogQWRkcyBhbiBlcXVpdHkgYmFsYW5jZSBldmVudCBsaXN0ZW5lclxuICAgKiBAcGFyYW0ge0VxdWl0eUJhbGFuY2VMaXN0ZW5lcn0gbGlzdGVuZXIgZXF1aXR5IGJhbGFuY2UgZXZlbnQgbGlzdGVuZXJcbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBhY2NvdW50IGlkXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IGxpc3RlbmVyIGlkXG4gICAqL1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbWF4LXN0YXRlbWVudHMsIGNvbXBsZXhpdHlcbiAgYXN5bmMgYWRkRXF1aXR5QmFsYW5jZUxpc3RlbmVyKGxpc3RlbmVyLCBhY2NvdW50SWQpIHtcbiAgICBpZighdGhpcy5fZXF1aXR5QmFsYW5jZUNhY2hlc1thY2NvdW50SWRdKSB7XG4gICAgICB0aGlzLl9lcXVpdHlCYWxhbmNlQ2FjaGVzW2FjY291bnRJZF0gPSB7XG4gICAgICAgIGJhbGFuY2U6IG51bGwsXG4gICAgICAgIGVxdWl0eTogbnVsbCxcbiAgICAgICAgcGVuZGluZ0luaXRhbGl6YXRpb25SZXNvbHZlczogW11cbiAgICAgIH07XG4gICAgfVxuICAgIGNvbnN0IGNhY2hlID0gdGhpcy5fZXF1aXR5QmFsYW5jZUNhY2hlc1thY2NvdW50SWRdO1xuICAgIGxldCBjb25uZWN0aW9uID0gbnVsbDtcbiAgICBsZXQgcmV0cnlJbnRlcnZhbEluU2Vjb25kcyA9IHRoaXMuX3JldHJ5SW50ZXJ2YWxJblNlY29uZHM7XG4gICAgY29uc3QgZ2V0QWNjb3VudExpc3RlbmVycyA9ICgpID0+IHRoaXMuZ2V0QWNjb3VudExpc3RlbmVycyhhY2NvdW50SWQpO1xuICAgIGNvbnN0IHBlbmRpbmdJbml0YWxpemF0aW9uUmVzb2x2ZXMgPSB0aGlzLl9wZW5kaW5nSW5pdGFsaXphdGlvblJlc29sdmVzO1xuICAgIGNvbnN0IHN5bmNocm9uaXphdGlvbkZsYWdzID0gdGhpcy5fYWNjb3VudFN5bmNocm9uaXphdGlvbkZsYWdzO1xuXG4gICAgY29uc3QgcHJvY2Vzc0VxdWl0eUJhbGFuY2VFdmVudCA9IGFzeW5jIChlcXVpdHksIGJhbGFuY2UpID0+IHtcbiAgICAgIGlmKHRoaXMuX2VxdWl0eUJhbGFuY2VDYWNoZXNbYWNjb3VudElkXSkge1xuICAgICAgICBpZihlcXVpdHkgIT09IGNhY2hlLmVxdWl0eSB8fCAoYmFsYW5jZSAmJiBiYWxhbmNlICE9PSBjYWNoZS5iYWxhbmNlKSkge1xuICAgICAgICAgIGNhY2hlLmVxdWl0eSA9IGVxdWl0eTtcbiAgICAgICAgICBpZihiYWxhbmNlKSB7XG4gICAgICAgICAgICBjYWNoZS5iYWxhbmNlID0gYmFsYW5jZTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYoY2FjaGUuZXF1aXR5ICE9PSBudWxsICYmIGNhY2hlLmJhbGFuY2UgIT09IG51bGwpIHtcbiAgICAgICAgICAgIE9iamVjdC52YWx1ZXMoZ2V0QWNjb3VudExpc3RlbmVycygpKS5mb3JFYWNoKGFjY291bnRMaXN0ZW5lciA9PiB7XG4gICAgICAgICAgICAgIGFjY291bnRMaXN0ZW5lci5vbkVxdWl0eU9yQmFsYW5jZVVwZGF0ZWQoe1xuICAgICAgICAgICAgICAgIGVxdWl0eTogY2FjaGUuZXF1aXR5LFxuICAgICAgICAgICAgICAgIGJhbGFuY2U6IGNhY2hlLmJhbGFuY2VcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9O1xuXG4gICAgY2xhc3MgRXF1aXR5QmFsYW5jZVN0cmVhbUxpc3RlbmVyIGV4dGVuZHMgU3luY2hyb25pemF0aW9uTGlzdGVuZXIge1xuXG4gICAgICBhc3luYyBvbkRlYWxzU3luY2hyb25pemVkKGluc3RhbmNlSW5kZXgsIHN5bmNocm9uaXphdGlvbklkKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgaWYoIXN5bmNocm9uaXphdGlvbkZsYWdzW2FjY291bnRJZF0pIHtcbiAgICAgICAgICAgIHN5bmNocm9uaXphdGlvbkZsYWdzW2FjY291bnRJZF0gPSB0cnVlO1xuICAgICAgICAgICAgT2JqZWN0LnZhbHVlcyhnZXRBY2NvdW50TGlzdGVuZXJzKCkpLmZvckVhY2goYWNjb3VudExpc3RlbmVyID0+IHtcbiAgICAgICAgICAgICAgYWNjb3VudExpc3RlbmVyLm9uQ29ubmVjdGVkKCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYocGVuZGluZ0luaXRhbGl6YXRpb25SZXNvbHZlc1thY2NvdW50SWRdKSB7XG4gICAgICAgICAgICBwZW5kaW5nSW5pdGFsaXphdGlvblJlc29sdmVzW2FjY291bnRJZF0uZm9yRWFjaChyZXNvbHZlID0+IHJlc29sdmUoKSk7XG4gICAgICAgICAgICBkZWxldGUgcGVuZGluZ0luaXRhbGl6YXRpb25SZXNvbHZlc1thY2NvdW50SWRdO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgT2JqZWN0LnZhbHVlcyhnZXRBY2NvdW50TGlzdGVuZXJzKCkpLmZvckVhY2goYWNjb3VudExpc3RlbmVyID0+IHtcbiAgICAgICAgICAgIGFjY291bnRMaXN0ZW5lci5vbkVycm9yKGVycik7XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgdGhpcy5fbG9nZ2VyLmVycm9yKCdFcnJvciBwcm9jZXNzaW5nIG9uRGVhbHNTeW5jaHJvbml6ZWQgZXZlbnQgZm9yICcgK1xuICAgICAgICAgIGBlcXVpdHkgYmFsYW5jZSBsaXN0ZW5lciBmb3IgYWNjb3VudCAke2FjY291bnRJZH1gLCBlcnIpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGFzeW5jIG9uRGlzY29ubmVjdGVkKGluc3RhbmNlSW5kZXgpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBpZihzeW5jaHJvbml6YXRpb25GbGFnc1thY2NvdW50SWRdICYmICFjb25uZWN0aW9uLmhlYWx0aE1vbml0b3IuaGVhbHRoU3RhdHVzLnN5bmNocm9uaXplZCkge1xuICAgICAgICAgICAgc3luY2hyb25pemF0aW9uRmxhZ3NbYWNjb3VudElkXSA9IGZhbHNlO1xuICAgICAgICAgICAgT2JqZWN0LnZhbHVlcyhnZXRBY2NvdW50TGlzdGVuZXJzKCkpLmZvckVhY2goYWNjb3VudExpc3RlbmVyID0+IHtcbiAgICAgICAgICAgICAgYWNjb3VudExpc3RlbmVyLm9uRGlzY29ubmVjdGVkKCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgIE9iamVjdC52YWx1ZXMoZ2V0QWNjb3VudExpc3RlbmVycygpKS5mb3JFYWNoKGFjY291bnRMaXN0ZW5lciA9PiB7XG4gICAgICAgICAgICBhY2NvdW50TGlzdGVuZXIub25FcnJvcihlcnIpO1xuICAgICAgICAgIH0pO1xuICAgICAgICAgIHRoaXMuX2xvZ2dlci5lcnJvcignRXJyb3IgcHJvY2Vzc2luZyBvbkRpc2Nvbm5lY3RlZCBldmVudCBmb3IgJyArXG4gICAgICAgIGBlcXVpdHkgYmFsYW5jZSBsaXN0ZW5lciBmb3IgYWNjb3VudCAke2FjY291bnRJZH1gLCBlcnIpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjb21wbGV4aXR5LCBtYXgtc3RhdGVtZW50c1xuICAgICAgYXN5bmMgb25TeW1ib2xQcmljZVVwZGF0ZWQoaW5zdGFuY2VJbmRleCwgcHJpY2UpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBpZihwZW5kaW5nSW5pdGFsaXphdGlvblJlc29sdmVzW2FjY291bnRJZF0pIHtcbiAgICAgICAgICAgIHBlbmRpbmdJbml0YWxpemF0aW9uUmVzb2x2ZXNbYWNjb3VudElkXS5mb3JFYWNoKHJlc29sdmUgPT4gcmVzb2x2ZSgpKTtcbiAgICAgICAgICAgIGRlbGV0ZSBwZW5kaW5nSW5pdGFsaXphdGlvblJlc29sdmVzW2FjY291bnRJZF07XG4gICAgICAgICAgfVxuICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICBPYmplY3QudmFsdWVzKGdldEFjY291bnRMaXN0ZW5lcnMoKSkuZm9yRWFjaChhY2NvdW50TGlzdGVuZXIgPT4ge1xuICAgICAgICAgICAgYWNjb3VudExpc3RlbmVyLm9uRXJyb3IoZXJyKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgICB0aGlzLl9sb2dnZXIuZXJyb3IoJ0Vycm9yIHByb2Nlc3Npbmcgb25TeW1ib2xQcmljZVVwZGF0ZWQgZXZlbnQgZm9yICcgK1xuICAgICAgICAgICAgYGVxdWl0eSBiYWxhbmNlIGxpc3RlbmVyIGZvciBhY2NvdW50ICR7YWNjb3VudElkfWAsIGVycik7XG4gICAgICAgIH1cbiAgICAgICAgLy8gcHJpY2UgZGF0YSBvbmx5IGNvbnRhaW5zIGVxdWl0eVxuICAgICAgICBhd2FpdCBwcm9jZXNzRXF1aXR5QmFsYW5jZUV2ZW50KHByaWNlLmVxdWl0eSk7XG4gICAgICB9XG4gICAgXG4gICAgICBhc3luYyBvbkFjY291bnRJbmZvcm1hdGlvblVwZGF0ZWQoaW5zdGFuY2VJbmRleCwgYWNjb3VudEluZm9ybWF0aW9uKSB7XG4gICAgICAgIGF3YWl0IHByb2Nlc3NFcXVpdHlCYWxhbmNlRXZlbnQoYWNjb3VudEluZm9ybWF0aW9uLmVxdWl0eSwgYWNjb3VudEluZm9ybWF0aW9uLmJhbGFuY2UpO1xuICAgICAgfVxuXG4gICAgfVxuXG4gICAgY29uc3QgbGlzdGVuZXJJZCA9IHJhbmRvbXN0cmluZy5nZW5lcmF0ZSgxMCk7XG4gICAgY29uc3QgYWNjb3VudExpc3RlbmVycyA9IHRoaXMuZ2V0QWNjb3VudExpc3RlbmVycyhhY2NvdW50SWQpO1xuICAgIGFjY291bnRMaXN0ZW5lcnNbbGlzdGVuZXJJZF0gPSBsaXN0ZW5lcjtcbiAgICB0aGlzLl9hY2NvdW50c0J5TGlzdGVuZXJJZFtsaXN0ZW5lcklkXSA9IGFjY291bnRJZDtcbiAgICBsZXQgaXNEZXBsb3llZCA9IGZhbHNlO1xuICAgIGNvbnN0IGFjY291bnQgPSBhd2FpdCB0aGlzLl9tZXRhQXBpLm1ldGF0cmFkZXJBY2NvdW50QXBpLmdldEFjY291bnQoYWNjb3VudElkKTtcbiAgICB3aGlsZSghaXNEZXBsb3llZCkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgYWNjb3VudC53YWl0RGVwbG95ZWQoKTtcbiAgICAgICAgaXNEZXBsb3llZCA9IHRydWU7ICBcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBsaXN0ZW5lci5vbkVycm9yKGVycik7XG4gICAgICAgIHRoaXMuX2xvZ2dlci5lcnJvcihgRXJyb3Igd2FpdCBmb3IgYWNjb3VudCAke2FjY291bnRJZH0gdG8gZGVwbG95LCByZXRyeWluZ2AsIGVycik7XG4gICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKHJlcyA9PiBzZXRUaW1lb3V0KHJlcywgcmV0cnlJbnRlcnZhbEluU2Vjb25kcyAqIDEwMDApKTsgXG4gICAgICAgIHJldHJ5SW50ZXJ2YWxJblNlY29uZHMgPSBNYXRoLm1pbihyZXRyeUludGVydmFsSW5TZWNvbmRzICogMiwgMzAwKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYoIXRoaXMuX2VxdWl0eUJhbGFuY2VDb25uZWN0aW9uc1thY2NvdW50SWRdKSB7XG4gICAgICByZXRyeUludGVydmFsSW5TZWNvbmRzID0gdGhpcy5fcmV0cnlJbnRlcnZhbEluU2Vjb25kcztcbiAgICAgIGNvbm5lY3Rpb24gPSBhY2NvdW50LmdldFN0cmVhbWluZ0Nvbm5lY3Rpb24oKTtcbiAgICAgIHRoaXMuX2VxdWl0eUJhbGFuY2VDb25uZWN0aW9uc1thY2NvdW50SWRdID0gY29ubmVjdGlvbjtcbiAgICAgIGNvbnN0IHN5bmNMaXN0ZW5lciA9IG5ldyBFcXVpdHlCYWxhbmNlU3RyZWFtTGlzdGVuZXIoKTtcbiAgICAgIGNvbm5lY3Rpb24uYWRkU3luY2hyb25pemF0aW9uTGlzdGVuZXIoc3luY0xpc3RlbmVyKTtcbiAgICAgIFxuICAgICAgbGV0IGlzU3luY2hyb25pemVkID0gZmFsc2U7XG4gICAgICB3aGlsZSghaXNTeW5jaHJvbml6ZWQpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBhd2FpdCBjb25uZWN0aW9uLmNvbm5lY3QoKTtcbiAgICAgICAgICBhd2FpdCBjb25uZWN0aW9uLndhaXRTeW5jaHJvbml6ZWQoKTtcbiAgICAgICAgICBpc1N5bmNocm9uaXplZCA9IHRydWU7XG4gICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgIGxpc3RlbmVyLm9uRXJyb3IoZXJyKTtcbiAgICAgICAgICB0aGlzLl9sb2dnZXIuZXJyb3IoJ0Vycm9yIGNvbmZpZ3VyaW5nIGVxdWl0eSBiYWxhbmNlIHN0cmVhbSBsaXN0ZW5lciAnICtcbiAgICAgICAgICAgIGBmb3IgYWNjb3VudCAke2FjY291bnRJZH0sIHJldHJ5aW5nYCwgZXJyKTtcbiAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXMgPT4gc2V0VGltZW91dChyZXMsIHJldHJ5SW50ZXJ2YWxJblNlY29uZHMgKiAxMDAwKSk7IFxuICAgICAgICAgIHJldHJ5SW50ZXJ2YWxJblNlY29uZHMgPSBNYXRoLm1pbihyZXRyeUludGVydmFsSW5TZWNvbmRzICogMiwgMzAwKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0cnlJbnRlcnZhbEluU2Vjb25kcyA9IHRoaXMuX3JldHJ5SW50ZXJ2YWxJblNlY29uZHM7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbm5lY3Rpb24gPSB0aGlzLl9lcXVpdHlCYWxhbmNlQ29ubmVjdGlvbnNbYWNjb3VudElkXTtcbiAgICAgIGlmKCFjb25uZWN0aW9uLmhlYWx0aE1vbml0b3IuaGVhbHRoU3RhdHVzLnN5bmNocm9uaXplZCkge1xuICAgICAgICBpZighdGhpcy5fcGVuZGluZ0luaXRhbGl6YXRpb25SZXNvbHZlc1thY2NvdW50SWRdKSB7XG4gICAgICAgICAgdGhpcy5fcGVuZGluZ0luaXRhbGl6YXRpb25SZXNvbHZlc1thY2NvdW50SWRdID0gW107XG4gICAgICAgIH1cbiAgICAgICAgbGV0IHJlc29sdmVJbml0aWFsaXplO1xuICAgICAgICBsZXQgaW5pdGlhbGl6ZVByb21pc2UgPSBuZXcgUHJvbWlzZSgocmVzLCByZWopID0+IHtcbiAgICAgICAgICByZXNvbHZlSW5pdGlhbGl6ZSA9IHJlcztcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuX3BlbmRpbmdJbml0YWxpemF0aW9uUmVzb2x2ZXNbYWNjb3VudElkXS5wdXNoKHJlc29sdmVJbml0aWFsaXplKTtcbiAgICAgICAgYXdhaXQgaW5pdGlhbGl6ZVByb21pc2U7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBsaXN0ZW5lcklkO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZXMgZXF1aXR5IGJhbGFuY2UgZXZlbnQgbGlzdGVuZXIgYnkgaWRcbiAgICogQHBhcmFtIHtTdHJpbmd9IGxpc3RlbmVySWQgbGlzdGVuZXIgaWRcbiAgICovXG4gIHJlbW92ZUVxdWl0eUJhbGFuY2VMaXN0ZW5lcihsaXN0ZW5lcklkKSB7XG4gICAgaWYodGhpcy5fYWNjb3VudHNCeUxpc3RlbmVySWRbbGlzdGVuZXJJZF0pIHtcbiAgICAgIGNvbnN0IGFjY291bnRJZCA9IHRoaXMuX2FjY291bnRzQnlMaXN0ZW5lcklkW2xpc3RlbmVySWRdO1xuICAgICAgZGVsZXRlIHRoaXMuX2FjY291bnRTeW5jaHJvbml6YXRpb25GbGFnc1thY2NvdW50SWRdO1xuICAgICAgZGVsZXRlIHRoaXMuX2FjY291bnRzQnlMaXN0ZW5lcklkW2xpc3RlbmVySWRdO1xuICAgICAgaWYodGhpcy5fZXF1aXR5QmFsYW5jZUxpc3RlbmVyc1thY2NvdW50SWRdKSB7XG4gICAgICAgIGRlbGV0ZSB0aGlzLl9lcXVpdHlCYWxhbmNlTGlzdGVuZXJzW2FjY291bnRJZF1bbGlzdGVuZXJJZF07XG4gICAgICB9XG4gICAgICBpZih0aGlzLl9lcXVpdHlCYWxhbmNlQ29ubmVjdGlvbnNbYWNjb3VudElkXSAmJiBcbiAgICAgICAgIU9iamVjdC5rZXlzKHRoaXMuX2VxdWl0eUJhbGFuY2VMaXN0ZW5lcnNbYWNjb3VudElkXSkubGVuZ3RoKSB7XG4gICAgICAgIHRoaXMuX2VxdWl0eUJhbGFuY2VDb25uZWN0aW9uc1thY2NvdW50SWRdLmNsb3NlKCk7XG4gICAgICAgIGRlbGV0ZSB0aGlzLl9lcXVpdHlCYWxhbmNlQ29ubmVjdGlvbnNbYWNjb3VudElkXTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxufVxuIl0sIm5hbWVzIjpbIkVxdWl0eUJhbGFuY2VTdHJlYW1NYW5hZ2VyIiwiZ2V0QWNjb3VudExpc3RlbmVycyIsImFjY291bnRJZCIsIl9lcXVpdHlCYWxhbmNlTGlzdGVuZXJzIiwiYWRkRXF1aXR5QmFsYW5jZUxpc3RlbmVyIiwibGlzdGVuZXIiLCJfZXF1aXR5QmFsYW5jZUNhY2hlcyIsImJhbGFuY2UiLCJlcXVpdHkiLCJwZW5kaW5nSW5pdGFsaXphdGlvblJlc29sdmVzIiwiY2FjaGUiLCJjb25uZWN0aW9uIiwicmV0cnlJbnRlcnZhbEluU2Vjb25kcyIsIl9yZXRyeUludGVydmFsSW5TZWNvbmRzIiwiX3BlbmRpbmdJbml0YWxpemF0aW9uUmVzb2x2ZXMiLCJzeW5jaHJvbml6YXRpb25GbGFncyIsIl9hY2NvdW50U3luY2hyb25pemF0aW9uRmxhZ3MiLCJwcm9jZXNzRXF1aXR5QmFsYW5jZUV2ZW50IiwiT2JqZWN0IiwidmFsdWVzIiwiZm9yRWFjaCIsImFjY291bnRMaXN0ZW5lciIsIm9uRXF1aXR5T3JCYWxhbmNlVXBkYXRlZCIsIkVxdWl0eUJhbGFuY2VTdHJlYW1MaXN0ZW5lciIsIlN5bmNocm9uaXphdGlvbkxpc3RlbmVyIiwib25EZWFsc1N5bmNocm9uaXplZCIsImluc3RhbmNlSW5kZXgiLCJzeW5jaHJvbml6YXRpb25JZCIsIm9uQ29ubmVjdGVkIiwicmVzb2x2ZSIsImVyciIsIm9uRXJyb3IiLCJfbG9nZ2VyIiwiZXJyb3IiLCJvbkRpc2Nvbm5lY3RlZCIsImhlYWx0aE1vbml0b3IiLCJoZWFsdGhTdGF0dXMiLCJzeW5jaHJvbml6ZWQiLCJvblN5bWJvbFByaWNlVXBkYXRlZCIsInByaWNlIiwib25BY2NvdW50SW5mb3JtYXRpb25VcGRhdGVkIiwiYWNjb3VudEluZm9ybWF0aW9uIiwibGlzdGVuZXJJZCIsInJhbmRvbXN0cmluZyIsImdlbmVyYXRlIiwiYWNjb3VudExpc3RlbmVycyIsIl9hY2NvdW50c0J5TGlzdGVuZXJJZCIsImlzRGVwbG95ZWQiLCJhY2NvdW50IiwiX21ldGFBcGkiLCJtZXRhdHJhZGVyQWNjb3VudEFwaSIsImdldEFjY291bnQiLCJ3YWl0RGVwbG95ZWQiLCJQcm9taXNlIiwicmVzIiwic2V0VGltZW91dCIsIk1hdGgiLCJtaW4iLCJfZXF1aXR5QmFsYW5jZUNvbm5lY3Rpb25zIiwiZ2V0U3RyZWFtaW5nQ29ubmVjdGlvbiIsInN5bmNMaXN0ZW5lciIsImFkZFN5bmNocm9uaXphdGlvbkxpc3RlbmVyIiwiaXNTeW5jaHJvbml6ZWQiLCJjb25uZWN0Iiwid2FpdFN5bmNocm9uaXplZCIsInJlc29sdmVJbml0aWFsaXplIiwiaW5pdGlhbGl6ZVByb21pc2UiLCJyZWoiLCJwdXNoIiwicmVtb3ZlRXF1aXR5QmFsYW5jZUxpc3RlbmVyIiwia2V5cyIsImxlbmd0aCIsImNsb3NlIiwiY29uc3RydWN0b3IiLCJkb21haW5DbGllbnQiLCJtZXRhQXBpIiwiX2RvbWFpbkNsaWVudCIsIkxvZ2dlck1hbmFnZXIiLCJnZXRMb2dnZXIiXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7O2VBU3FCQTs7O3FFQVBJO2dGQUNXOytEQUNWOzs7Ozs7QUFLWCxJQUFBLEFBQU1BLDZCQUFOLE1BQU1BO0lBb0JuQjs7OztHQUlDLEdBQ0RDLG9CQUFvQkMsU0FBUyxFQUFFO1FBQzdCLElBQUcsQ0FBQyxJQUFJLENBQUNDLHVCQUF1QixDQUFDRCxVQUFVLEVBQUU7WUFDM0MsSUFBSSxDQUFDQyx1QkFBdUIsQ0FBQ0QsVUFBVSxHQUFHLENBQUM7UUFDN0M7UUFDQSxPQUFPLElBQUksQ0FBQ0MsdUJBQXVCLENBQUNELFVBQVU7SUFDaEQ7SUFFQTs7Ozs7R0FLQyxHQUNELHNEQUFzRDtJQUN0RCxNQUFNRSx5QkFBeUJDLFFBQVEsRUFBRUgsU0FBUyxFQUFFO1FBQ2xELElBQUcsQ0FBQyxJQUFJLENBQUNJLG9CQUFvQixDQUFDSixVQUFVLEVBQUU7WUFDeEMsSUFBSSxDQUFDSSxvQkFBb0IsQ0FBQ0osVUFBVSxHQUFHO2dCQUNyQ0ssU0FBUztnQkFDVEMsUUFBUTtnQkFDUkMsOEJBQThCLEVBQUU7WUFDbEM7UUFDRjtRQUNBLE1BQU1DLFFBQVEsSUFBSSxDQUFDSixvQkFBb0IsQ0FBQ0osVUFBVTtRQUNsRCxJQUFJUyxhQUFhO1FBQ2pCLElBQUlDLHlCQUF5QixJQUFJLENBQUNDLHVCQUF1QjtRQUN6RCxNQUFNWixzQkFBc0IsSUFBTSxJQUFJLENB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