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)

211 lines (210 loc) 30.8 kB
"use strict"; 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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