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)
248 lines (247 loc) • 33 kB
JavaScript
;
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _async_to_generator(fn) {
return function() {
var self = this, args = arguments;
return new Promise(function(resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
import randomstring from 'randomstring';
import SynchronizationListener from '../../../clients/metaApi/synchronizationListener';
import LoggerManager from '../../../logger';
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
addEquityBalanceListener(listener, accountId) {
var _this = this;
return _async_to_generator(function*() {
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 = function() {
var _ref = _async_to_generator(function*(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
});
});
}
}
}
});
return function processEquityBalanceEvent(equity, balance) {
return _ref.apply(this, arguments);
};
}();
let EquityBalanceStreamListener = class EquityBalanceStreamListener extends SynchronizationListener {
onDealsSynchronized(instanceIndex, synchronizationId) {
var _this = this;
return _async_to_generator(function*() {
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);
}
})();
}
onDisconnected(instanceIndex) {
var _this = this;
return _async_to_generator(function*() {
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
onSymbolPriceUpdated(instanceIndex, price) {
var _this = this;
return _async_to_generator(function*() {
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
yield processEquityBalanceEvent(price.equity);
})();
}
onAccountInformationUpdated(instanceIndex, accountInformation) {
return _async_to_generator(function*() {
yield processEquityBalanceEvent(accountInformation.equity, accountInformation.balance);
})();
}
};
const listenerId = randomstring.generate(10);
const accountListeners = _this.getAccountListeners(accountId);
accountListeners[listenerId] = listener;
_this._accountsByListenerId[listenerId] = accountId;
let isDeployed = false;
const account = yield _this._metaApi.metatraderAccountApi.getAccount(accountId);
while(!isDeployed){
try {
yield account.waitDeployed();
isDeployed = true;
} catch (err) {
listener.onError(err);
_this._logger.error(`Error wait for account ${accountId} to deploy, retrying`, err);
yield 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 {
yield connection.connect();
yield connection.waitSynchronized();
isSynchronized = true;
} catch (err) {
listener.onError(err);
_this._logger.error('Error configuring equity balance stream listener ' + `for account ${accountId}, retrying`, err);
yield 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);
yield 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 = LoggerManager.getLogger('EquityBalanceStreamManager');
}
};
/**
* Manager for handling equity balance event listeners
*/ export { EquityBalanceStreamManager as default };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCByYW5kb21zdHJpbmcgZnJvbSAncmFuZG9tc3RyaW5nJztcbmltcG9ydCBTeW5jaHJvbml6YXRpb25MaXN0ZW5lciBmcm9tICcuLi8uLi8uLi9jbGllbnRzL21ldGFBcGkvc3luY2hyb25pemF0aW9uTGlzdGVuZXInO1xuaW1wb3J0IExvZ2dlck1hbmFnZXIgZnJvbSAnLi4vLi4vLi4vbG9nZ2VyJztcblxuLyoqXG4gKiBNYW5hZ2VyIGZvciBoYW5kbGluZyBlcXVpdHkgYmFsYW5jZSBldmVudCBsaXN0ZW5lcnNcbiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgRXF1aXR5QmFsYW5jZVN0cmVhbU1hbmFnZXIge1xuXG4gIC8qKlxuICAgKiBDb25zdHJ1Y3RzIGVxdWl0eSBiYWxhbmNlIGV2ZW50IGxpc3RlbmVyIG1hbmFnZXIgaW5zdGFuY2VcbiAgICogQHBhcmFtIHtEb21haW5DbGllbnR9IGRvbWFpbkNsaWVudCBkb21haW4gY2xpZW50XG4gICAqIEBwYXJhbSB7TWV0YUFwaX0gbWV0YUFwaSBtZXRhQXBpIFNESyBpbnN0YW5jZVxuICAgKi9cbiAgY29uc3RydWN0b3IoZG9tYWluQ2xpZW50LCBtZXRhQXBpKSB7XG4gICAgdGhpcy5fZG9tYWluQ2xpZW50ID0gZG9tYWluQ2xpZW50O1xuICAgIHRoaXMuX21ldGFBcGkgPSBtZXRhQXBpO1xuICAgIHRoaXMuX2VxdWl0eUJhbGFuY2VMaXN0ZW5lcnMgPSB7fTtcbiAgICB0aGlzLl9hY2NvdW50c0J5TGlzdGVuZXJJZCA9IHt9O1xuICAgIHRoaXMuX2VxdWl0eUJhbGFuY2VDb25uZWN0aW9ucyA9IHt9O1xuICAgIHRoaXMuX2VxdWl0eUJhbGFuY2VDYWNoZXMgPSB7fTtcbiAgICB0aGlzLl9hY2NvdW50U3luY2hyb25pemF0aW9uRmxhZ3MgPSB7fTtcbiAgICB0aGlzLl9wZW5kaW5nSW5pdGFsaXphdGlvblJlc29sdmVzID0ge307XG4gICAgdGhpcy5fcmV0cnlJbnRlcnZhbEluU2Vjb25kcyA9IDE7XG4gICAgdGhpcy5fbG9nZ2VyID0gTG9nZ2VyTWFuYWdlci5nZXRMb2dnZXIoJ0VxdWl0eUJhbGFuY2VTdHJlYW1NYW5hZ2VyJyk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBsaXN0ZW5lcnMgZm9yIGFjY291bnRcbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBhY2NvdW50IGlkIHRvIHJldHVybiBsaXN0ZW5lcnMgZm9yXG4gICAqIEByZXR1cm5zIHt7W2xpc3RlbmVySWQ6IHN0cmluZ106IEVxdWl0eUJhbGFuY2VMaXN0ZW5lcn19IGRpY3Rpb25hcnkgb2YgYWNjb3VudCBlcXVpdHkgYmFsYW5jZSBldmVudCBsaXN0ZW5lcnNcbiAgICovXG4gIGdldEFjY291bnRMaXN0ZW5lcnMoYWNjb3VudElkKSB7XG4gICAgaWYoIXRoaXMuX2VxdWl0eUJhbGFuY2VMaXN0ZW5lcnNbYWNjb3VudElkXSkge1xuICAgICAgdGhpcy5fZXF1aXR5QmFsYW5jZUxpc3RlbmVyc1thY2NvdW50SWRdID0ge307XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9lcXVpdHlCYWxhbmNlTGlzdGVuZXJzW2FjY291bnRJZF07XG4gIH1cblxuICAvKipcbiAgICogQWRkcyBhbiBlcXVpdHkgYmFsYW5jZSBldmVudCBsaXN0ZW5lclxuICAgKiBAcGFyYW0ge0VxdWl0eUJhbGFuY2VMaXN0ZW5lcn0gbGlzdGVuZXIgZXF1aXR5IGJhbGFuY2UgZXZlbnQgbGlzdGVuZXJcbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBhY2NvdW50IGlkXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IGxpc3RlbmVyIGlkXG4gICAqL1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbWF4LXN0YXRlbWVudHMsIGNvbXBsZXhpdHlcbiAgYXN5bmMgYWRkRXF1aXR5QmFsYW5jZUxpc3RlbmVyKGxpc3RlbmVyLCBhY2NvdW50SWQpIHtcbiAgICBpZighdGhpcy5fZXF1aXR5QmFsYW5jZUNhY2hlc1thY2NvdW50SWRdKSB7XG4gICAgICB0aGlzLl9lcXVpdHlCYWxhbmNlQ2FjaGVzW2FjY291bnRJZF0gPSB7XG4gICAgICAgIGJhbGFuY2U6IG51bGwsXG4gICAgICAgIGVxdWl0eTogbnVsbCxcbiAgICAgICAgcGVuZGluZ0luaXRhbGl6YXRpb25SZXNvbHZlczogW11cbiAgICAgIH07XG4gICAgfVxuICAgIGNvbnN0IGNhY2hlID0gdGhpcy5fZXF1aXR5QmFsYW5jZUNhY2hlc1thY2NvdW50SWRdO1xuICAgIGxldCBjb25uZWN0aW9uID0gbnVsbDtcbiAgICBsZXQgcmV0cnlJbnRlcnZhbEluU2Vjb25kcyA9IHRoaXMuX3JldHJ5SW50ZXJ2YWxJblNlY29uZHM7XG4gICAgY29uc3QgZ2V0QWNjb3VudExpc3RlbmVycyA9ICgpID0+IHRoaXMuZ2V0QWNjb3VudExpc3RlbmVycyhhY2NvdW50SWQpO1xuICAgIGNvbnN0IHBlbmRpbmdJbml0YWxpemF0aW9uUmVzb2x2ZXMgPSB0aGlzLl9wZW5kaW5nSW5pdGFsaXphdGlvblJlc29sdmVzO1xuICAgIGNvbnN0IHN5bmNocm9uaXphdGlvbkZsYWdzID0gdGhpcy5fYWNjb3VudFN5bmNocm9uaXphdGlvbkZsYWdzO1xuXG4gICAgY29uc3QgcHJvY2Vzc0VxdWl0eUJhbGFuY2VFdmVudCA9IGFzeW5jIChlcXVpdHksIGJhbGFuY2UpID0+IHtcbiAgICAgIGlmKHRoaXMuX2VxdWl0eUJhbGFuY2VDYWNoZXNbYWNjb3VudElkXSkge1xuICAgICAgICBpZihlcXVpdHkgIT09IGNhY2hlLmVxdWl0eSB8fCAoYmFsYW5jZSAmJiBiYWxhbmNlICE9PSBjYWNoZS5iYWxhbmNlKSkge1xuICAgICAgICAgIGNhY2hlLmVxdWl0eSA9IGVxdWl0eTtcbiAgICAgICAgICBpZihiYWxhbmNlKSB7XG4gICAgICAgICAgICBjYWNoZS5iYWxhbmNlID0gYmFsYW5jZTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYoY2FjaGUuZXF1aXR5ICE9PSBudWxsICYmIGNhY2hlLmJhbGFuY2UgIT09IG51bGwpIHtcbiAgICAgICAgICAgIE9iamVjdC52YWx1ZXMoZ2V0QWNjb3VudExpc3RlbmVycygpKS5mb3JFYWNoKGFjY291bnRMaXN0ZW5lciA9PiB7XG4gICAgICAgICAgICAgIGFjY291bnRMaXN0ZW5lci5vbkVxdWl0eU9yQmFsYW5jZVVwZGF0ZWQoe1xuICAgICAgICAgICAgICAgIGVxdWl0eTogY2FjaGUuZXF1aXR5LFxuICAgICAgICAgICAgICAgIGJhbGFuY2U6IGNhY2hlLmJhbGFuY2VcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9O1xuXG4gICAgY2xhc3MgRXF1aXR5QmFsYW5jZVN0cmVhbUxpc3RlbmVyIGV4dGVuZHMgU3luY2hyb25pemF0aW9uTGlzdGVuZXIge1xuXG4gICAgICBhc3luYyBvbkRlYWxzU3luY2hyb25pemVkKGluc3RhbmNlSW5kZXgsIHN5bmNocm9uaXphdGlvbklkKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgaWYoIXN5bmNocm9uaXphdGlvbkZsYWdzW2FjY291bnRJZF0pIHtcbiAgICAgICAgICAgIHN5bmNocm9uaXphdGlvbkZsYWdzW2FjY291bnRJZF0gPSB0cnVlO1xuICAgICAgICAgICAgT2JqZWN0LnZhbHVlcyhnZXRBY2NvdW50TGlzdGVuZXJzKCkpLmZvckVhY2goYWNjb3VudExpc3RlbmVyID0+IHtcbiAgICAgICAgICAgICAgYWNjb3VudExpc3RlbmVyLm9uQ29ubmVjdGVkKCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYocGVuZGluZ0luaXRhbGl6YXRpb25SZXNvbHZlc1thY2NvdW50SWRdKSB7XG4gICAgICAgICAgICBwZW5kaW5nSW5pdGFsaXphdGlvblJlc29sdmVzW2FjY291bnRJZF0uZm9yRWFjaChyZXNvbHZlID0+IHJlc29sdmUoKSk7XG4gICAgICAgICAgICBkZWxldGUgcGVuZGluZ0luaXRhbGl6YXRpb25SZXNvbHZlc1thY2NvdW50SWRdO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgT2JqZWN0LnZhbHVlcyhnZXRBY2NvdW50TGlzdGVuZXJzKCkpLmZvckVhY2goYWNjb3VudExpc3RlbmVyID0+IHtcbiAgICAgICAgICAgIGFjY291bnRMaXN0ZW5lci5vbkVycm9yKGVycik7XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgdGhpcy5fbG9nZ2VyLmVycm9yKCdFcnJvciBwcm9jZXNzaW5nIG9uRGVhbHNTeW5jaHJvbml6ZWQgZXZlbnQgZm9yICcgK1xuICAgICAgICAgIGBlcXVpdHkgYmFsYW5jZSBsaXN0ZW5lciBmb3IgYWNjb3VudCAke2FjY291bnRJZH1gLCBlcnIpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGFzeW5jIG9uRGlzY29ubmVjdGVkKGluc3RhbmNlSW5kZXgpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBpZihzeW5jaHJvbml6YXRpb25GbGFnc1thY2NvdW50SWRdICYmICFjb25uZWN0aW9uLmhlYWx0aE1vbml0b3IuaGVhbHRoU3RhdHVzLnN5bmNocm9uaXplZCkge1xuICAgICAgICAgICAgc3luY2hyb25pemF0aW9uRmxhZ3NbYWNjb3VudElkXSA9IGZhbHNlO1xuICAgICAgICAgICAgT2JqZWN0LnZhbHVlcyhnZXRBY2NvdW50TGlzdGVuZXJzKCkpLmZvckVhY2goYWNjb3VudExpc3RlbmVyID0+IHtcbiAgICAgICAgICAgICAgYWNjb3VudExpc3RlbmVyLm9uRGlzY29ubmVjdGVkKCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgIE9iamVjdC52YWx1ZXMoZ2V0QWNjb3VudExpc3RlbmVycygpKS5mb3JFYWNoKGFjY291bnRMaXN0ZW5lciA9PiB7XG4gICAgICAgICAgICBhY2NvdW50TGlzdGVuZXIub25FcnJvcihlcnIpO1xuICAgICAgICAgIH0pO1xuICAgICAgICAgIHRoaXMuX2xvZ2dlci5lcnJvcignRXJyb3IgcHJvY2Vzc2luZyBvbkRpc2Nvbm5lY3RlZCBldmVudCBmb3IgJyArXG4gICAgICAgIGBlcXVpdHkgYmFsYW5jZSBsaXN0ZW5lciBmb3IgYWNjb3VudCAke2FjY291bnRJZH1gLCBlcnIpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjb21wbGV4aXR5LCBtYXgtc3RhdGVtZW50c1xuICAgICAgYXN5bmMgb25TeW1ib2xQcmljZVVwZGF0ZWQoaW5zdGFuY2VJbmRleCwgcHJpY2UpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBpZihwZW5kaW5nSW5pdGFsaXphdGlvblJlc29sdmVzW2FjY291bnRJZF0pIHtcbiAgICAgICAgICAgIHBlbmRpbmdJbml0YWxpemF0aW9uUmVzb2x2ZXNbYWNjb3VudElkXS5mb3JFYWNoKHJlc29sdmUgPT4gcmVzb2x2ZSgpKTtcbiAgICAgICAgICAgIGRlbGV0ZSBwZW5kaW5nSW5pdGFsaXphdGlvblJlc29sdmVzW2FjY291bnRJZF07XG4gICAgICAgICAgfVxuICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICBPYmplY3QudmFsdWVzKGdldEFjY291bnRMaXN0ZW5lcnMoKSkuZm9yRWFjaChhY2NvdW50TGlzdGVuZXIgPT4ge1xuICAgICAgICAgICAgYWNjb3VudExpc3RlbmVyLm9uRXJyb3IoZXJyKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgICB0aGlzLl9sb2dnZXIuZXJyb3IoJ0Vycm9yIHByb2Nlc3Npbmcgb25TeW1ib2xQcmljZVVwZGF0ZWQgZXZlbnQgZm9yICcgK1xuICAgICAgICAgICAgYGVxdWl0eSBiYWxhbmNlIGxpc3RlbmVyIGZvciBhY2NvdW50ICR7YWNjb3VudElkfWAsIGVycik7XG4gICAgICAgIH1cbiAgICAgICAgLy8gcHJpY2UgZGF0YSBvbmx5IGNvbnRhaW5zIGVxdWl0eVxuICAgICAgICBhd2FpdCBwcm9jZXNzRXF1aXR5QmFsYW5jZUV2ZW50KHByaWNlLmVxdWl0eSk7XG4gICAgICB9XG4gICAgXG4gICAgICBhc3luYyBvbkFjY291bnRJbmZvcm1hdGlvblVwZGF0ZWQoaW5zdGFuY2VJbmRleCwgYWNjb3VudEluZm9ybWF0aW9uKSB7XG4gICAgICAgIGF3YWl0IHByb2Nlc3NFcXVpdHlCYWxhbmNlRXZlbnQoYWNjb3VudEluZm9ybWF0aW9uLmVxdWl0eSwgYWNjb3VudEluZm9ybWF0aW9uLmJhbGFuY2UpO1xuICAgICAgfVxuXG4gICAgfVxuXG4gICAgY29uc3QgbGlzdGVuZXJJZCA9IHJhbmRvbXN0cmluZy5nZW5lcmF0ZSgxMCk7XG4gICAgY29uc3QgYWNjb3VudExpc3RlbmVycyA9IHRoaXMuZ2V0QWNjb3VudExpc3RlbmVycyhhY2NvdW50SWQpO1xuICAgIGFjY291bnRMaXN0ZW5lcnNbbGlzdGVuZXJJZF0gPSBsaXN0ZW5lcjtcbiAgICB0aGlzLl9hY2NvdW50c0J5TGlzdGVuZXJJZFtsaXN0ZW5lcklkXSA9IGFjY291bnRJZDtcbiAgICBsZXQgaXNEZXBsb3llZCA9IGZhbHNlO1xuICAgIGNvbnN0IGFjY291bnQgPSBhd2FpdCB0aGlzLl9tZXRhQXBpLm1ldGF0cmFkZXJBY2NvdW50QXBpLmdldEFjY291bnQoYWNjb3VudElkKTtcbiAgICB3aGlsZSghaXNEZXBsb3llZCkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgYWNjb3VudC53YWl0RGVwbG95ZWQoKTtcbiAgICAgICAgaXNEZXBsb3llZCA9IHRydWU7ICBcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBsaXN0ZW5lci5vbkVycm9yKGVycik7XG4gICAgICAgIHRoaXMuX2xvZ2dlci5lcnJvcihgRXJyb3Igd2FpdCBmb3IgYWNjb3VudCAke2FjY291bnRJZH0gdG8gZGVwbG95LCByZXRyeWluZ2AsIGVycik7XG4gICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKHJlcyA9PiBzZXRUaW1lb3V0KHJlcywgcmV0cnlJbnRlcnZhbEluU2Vjb25kcyAqIDEwMDApKTsgXG4gICAgICAgIHJldHJ5SW50ZXJ2YWxJblNlY29uZHMgPSBNYXRoLm1pbihyZXRyeUludGVydmFsSW5TZWNvbmRzICogMiwgMzAwKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYoIXRoaXMuX2VxdWl0eUJhbGFuY2VDb25uZWN0aW9uc1thY2NvdW50SWRdKSB7XG4gICAgICByZXRyeUludGVydmFsSW5TZWNvbmRzID0gdGhpcy5fcmV0cnlJbnRlcnZhbEluU2Vjb25kcztcbiAgICAgIGNvbm5lY3Rpb24gPSBhY2NvdW50LmdldFN0cmVhbWluZ0Nvbm5lY3Rpb24oKTtcbiAgICAgIHRoaXMuX2VxdWl0eUJhbGFuY2VDb25uZWN0aW9uc1thY2NvdW50SWRdID0gY29ubmVjdGlvbjtcbiAgICAgIGNvbnN0IHN5bmNMaXN0ZW5lciA9IG5ldyBFcXVpdHlCYWxhbmNlU3RyZWFtTGlzdGVuZXIoKTtcbiAgICAgIGNvbm5lY3Rpb24uYWRkU3luY2hyb25pemF0aW9uTGlzdGVuZXIoc3luY0xpc3RlbmVyKTtcbiAgICAgIFxuICAgICAgbGV0IGlzU3luY2hyb25pemVkID0gZmFsc2U7XG4gICAgICB3aGlsZSghaXNTeW5jaHJvbml6ZWQpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBhd2FpdCBjb25uZWN0aW9uLmNvbm5lY3QoKTtcbiAgICAgICAgICBhd2FpdCBjb25uZWN0aW9uLndhaXRTeW5jaHJvbml6ZWQoKTtcbiAgICAgICAgICBpc1N5bmNocm9uaXplZCA9IHRydWU7XG4gICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgIGxpc3RlbmVyLm9uRXJyb3IoZXJyKTtcbiAgICAgICAgICB0aGlzLl9sb2dnZXIuZXJyb3IoJ0Vycm9yIGNvbmZpZ3VyaW5nIGVxdWl0eSBiYWxhbmNlIHN0cmVhbSBsaXN0ZW5lciAnICtcbiAgICAgICAgICAgIGBmb3IgYWNjb3VudCAke2FjY291bnRJZH0sIHJldHJ5aW5nYCwgZXJyKTtcbiAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXMgPT4gc2V0VGltZW91dChyZXMsIHJldHJ5SW50ZXJ2YWxJblNlY29uZHMgKiAxMDAwKSk7IFxuICAgICAgICAgIHJldHJ5SW50ZXJ2YWxJblNlY29uZHMgPSBNYXRoLm1pbihyZXRyeUludGVydmFsSW5TZWNvbmRzICogMiwgMzAwKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0cnlJbnRlcnZhbEluU2Vjb25kcyA9IHRoaXMuX3JldHJ5SW50ZXJ2YWxJblNlY29uZHM7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbm5lY3Rpb24gPSB0aGlzLl9lcXVpdHlCYWxhbmNlQ29ubmVjdGlvbnNbYWNjb3VudElkXTtcbiAgICAgIGlmKCFjb25uZWN0aW9uLmhlYWx0aE1vbml0b3IuaGVhbHRoU3RhdHVzLnN5bmNocm9uaXplZCkge1xuICAgICAgICBpZighdGhpcy5fcGVuZGluZ0luaXRhbGl6YXRpb25SZXNvbHZlc1thY2NvdW50SWRdKSB7XG4gICAgICAgICAgdGhpcy5fcGVuZGluZ0luaXRhbGl6YXRpb25SZXNvbHZlc1thY2NvdW50SWRdID0gW107XG4gICAgICAgIH1cbiAgICAgICAgbGV0IHJlc29sdmVJbml0aWFsaXplO1xuICAgICAgICBsZXQgaW5pdGlhbGl6ZVByb21pc2UgPSBuZXcgUHJvbWlzZSgocmVzLCByZWopID0+IHtcbiAgICAgICAgICByZXNvbHZlSW5pdGlhbGl6ZSA9IHJlcztcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuX3BlbmRpbmdJbml0YWxpemF0aW9uUmVzb2x2ZXNbYWNjb3VudElkXS5wdXNoKHJlc29sdmVJbml0aWFsaXplKTtcbiAgICAgICAgYXdhaXQgaW5pdGlhbGl6ZVByb21pc2U7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBsaXN0ZW5lcklkO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZXMgZXF1aXR5IGJhbGFuY2UgZXZlbnQgbGlzdGVuZXIgYnkgaWRcbiAgICogQHBhcmFtIHtTdHJpbmd9IGxpc3RlbmVySWQgbGlzdGVuZXIgaWRcbiAgICovXG4gIHJlbW92ZUVxdWl0eUJhbGFuY2VMaXN0ZW5lcihsaXN0ZW5lcklkKSB7XG4gICAgaWYodGhpcy5fYWNjb3VudHNCeUxpc3RlbmVySWRbbGlzdGVuZXJJZF0pIHtcbiAgICAgIGNvbnN0IGFjY291bnRJZCA9IHRoaXMuX2FjY291bnRzQnlMaXN0ZW5lcklkW2xpc3RlbmVySWRdO1xuICAgICAgZGVsZXRlIHRoaXMuX2FjY291bnRTeW5jaHJvbml6YXRpb25GbGFnc1thY2NvdW50SWRdO1xuICAgICAgZGVsZXRlIHRoaXMuX2FjY291bnRzQnlMaXN0ZW5lcklkW2xpc3RlbmVySWRdO1xuICAgICAgaWYodGhpcy5fZXF1aXR5QmFsYW5jZUxpc3RlbmVyc1thY2NvdW50SWRdKSB7XG4gICAgICAgIGRlbGV0ZSB0aGlzLl9lcXVpdHlCYWxhbmNlTGlzdGVuZXJzW2FjY291bnRJZF1bbGlzdGVuZXJJZF07XG4gICAgICB9XG4gICAgICBpZih0aGlzLl9lcXVpdHlCYWxhbmNlQ29ubmVjdGlvbnNbYWNjb3VudElkXSAmJiBcbiAgICAgICAgIU9iamVjdC5rZXlzKHRoaXMuX2VxdWl0eUJhbGFuY2VMaXN0ZW5lcnNbYWNjb3VudElkXSkubGVuZ3RoKSB7XG4gICAgICAgIHRoaXMuX2VxdWl0eUJhbGFuY2VDb25uZWN0aW9uc1thY2NvdW50SWRdLmNsb3NlKCk7XG4gICAgICAgIGRlbGV0ZSB0aGlzLl9lcXVpdHlCYWxhbmNlQ29ubmVjdGlvbnNbYWNjb3VudElkXTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxufVxuIl0sIm5hbWVzIjpbInJhbmRvbXN0cmluZyIsIlN5bmNocm9uaXphdGlvbkxpc3RlbmVyIiwiTG9nZ2VyTWFuYWdlciIsIkVxdWl0eUJhbGFuY2VTdHJlYW1NYW5hZ2VyIiwiZ2V0QWNjb3VudExpc3RlbmVycyIsImFjY291bnRJZCIsIl9lcXVpdHlCYWxhbmNlTGlzdGVuZXJzIiwiYWRkRXF1aXR5QmFsYW5jZUxpc3RlbmVyIiwibGlzdGVuZXIiLCJfZXF1aXR5QmFsYW5jZUNhY2hlcyIsImJhbGFuY2UiLCJlcXVpdHkiLCJwZW5kaW5nSW5pdGFsaXphdGlvblJlc29sdmVzIiwiY2FjaGUiLCJjb25uZWN0aW9uIiwicmV0cnlJbnRlcnZhbEluU2Vjb25kcyIsIl9yZXRyeUludGVydmFsSW5TZWNvbmRzIiwiX3BlbmRpbmdJbml0YWxpemF0aW9uUmVzb2x2ZXMiLCJzeW5jaHJvbml6YXRpb25GbGFncyIsIl9hY2NvdW50U3luY2hyb25pemF0aW9uRmxhZ3MiLCJwcm9jZXNzRXF1aXR5QmFsYW5jZUV2ZW50IiwiT2JqZWN0IiwidmFsdWVzIiwiZm9yRWFjaCIsImFjY291bnRMaXN0ZW5lciIsIm9uRXF1aXR5T3JCYWxhbmNlVXBkYXRlZCIsIkVxdWl0eUJhbGFuY2VTdHJlYW1MaXN0ZW5lciIsIm9uRGVhbHNTeW5jaHJvbml6ZWQiLCJpbnN0YW5jZUluZGV4Iiwic3luY2hyb25pemF0aW9uSWQiLCJvbkNvbm5lY3RlZCIsInJlc29sdmUiLCJlcnIiLCJvbkVycm9yIiwiX2xvZ2dlciIsImVycm9yIiwib25EaXNjb25uZWN0ZWQiLCJoZWFsdGhNb25pdG9yIiwiaGVhbHRoU3RhdHVzIiwic3luY2hyb25pemVkIiwib25TeW1ib2xQcmljZVVwZGF0ZWQiLCJwcmljZSIsIm9uQWNjb3VudEluZm9ybWF0aW9uVXBkYXRlZCIsImFjY291bnRJbmZvcm1hdGlvbiIsImxpc3RlbmVySWQiLCJnZW5lcmF0ZSIsImFjY291bnRMaXN0ZW5lcnMiLCJfYWNjb3VudHNCeUxpc3RlbmVySWQiLCJpc0RlcGxveWVkIiwiYWNjb3VudCIsIl9tZXRhQXBpIiwibWV0YXRyYWRlckFjY291bnRBcGkiLCJnZXRBY2NvdW50Iiwid2FpdERlcGxveWVkIiwiUHJvbWlzZSIsInJlcyIsInNldFRpbWVvdXQiLCJNYXRoIiwibWluIiwiX2VxdWl0eUJhbGFuY2VDb25uZWN0aW9ucyIsImdldFN0cmVhbWluZ0Nvbm5lY3Rpb24iLCJzeW5jTGlzdGVuZXIiLCJhZGRTeW5jaHJvbml6YXRpb25MaXN0ZW5lciIsImlzU3luY2hyb25pemVkIiwiY29ubmVjdCIsIndhaXRTeW5jaHJvbml6ZWQiLCJyZXNvbHZlSW5pdGlhbGl6ZSIsImluaXRpYWxpemVQcm9taXNlIiwicmVqIiwicHVzaCIsInJlbW92ZUVxdWl0eUJhbGFuY2VMaXN0ZW5lciIsImtleXMiLCJsZW5ndGgiLCJjbG9zZSIsImNvbnN0cnVjdG9yIiwiZG9tYWluQ2xpZW50IiwibWV0YUFwaSIsIl9kb21haW5DbGllbnQiLCJnZXRMb2dnZXIiXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFFQSxPQUFPQSxrQkFBa0IsZUFBZTtBQUN4QyxPQUFPQyw2QkFBNkIsbURBQW1EO0FBQ3ZGLE9BQU9DLG1CQUFtQixrQkFBa0I7QUFLN0IsSUFBQSxBQUFNQyw2QkFBTixNQUFNQTtJQW9CbkI7Ozs7R0FJQyxHQUNEQyxvQkFBb0JDLFNBQVMsRUFBRTtRQUM3QixJQUFHLENBQUMsSUFBSSxDQUFDQyx1QkFBdUIsQ0FBQ0QsVUFBVSxFQUFFO1lBQzNDLElBQUksQ0FBQ0MsdUJBQXVCLENBQUNELFVBQVUsR0FBRyxDQUFDO1FBQzdDO1FBQ0EsT0FBTyxJQUFJLENBQUNDLHVCQUF1QixDQUFDRCxVQUFVO0lBQ2hEO0lBRUE7Ozs7O0dBS0MsR0FDRCxzREFBc0Q7SUFDaERFLHlCQUF5QkMsUUFBUSxFQUFFSCxTQUFTOztlQUFsRCxvQkFBQTtZQUNFLElBQUcsQ0FBQyxNQUFLSSxvQkFBb0IsQ0FBQ0osVUFBVSxFQUFFO2dCQUN4QyxNQUFLSSxvQkFBb0IsQ0FBQ0osVUFBVSxHQUFHO29CQUNyQ0ssU0FBUztvQkFDVEMsUUFBUTtvQkFDUkMsOEJBQThCLEVBQUU7Z0JBQ2xDO1lBQ0Y7WUFDQSxNQUFNQyxRQUFRLE1BQUtKLG9CQUFvQixDQUFDSixVQUFVO1lBQ2xELElBQUlTLGFBQWE7WUFDakIsSUFBSUMs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