UNPKG

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
'use strict'; 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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