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)

166 lines (165 loc) 22.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "default", { enumerable: true, get: function() { return RpcMetaApiConnection; } }); const _logger = /*#__PURE__*/ _interop_require_default(require("../logger")); const _metaApiConnection = /*#__PURE__*/ _interop_require_default(require("./metaApiConnection")); const _timeoutError = /*#__PURE__*/ _interop_require_default(require("../clients/timeoutError")); function _define_property(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } let RpcMetaApiConnection = class RpcMetaApiConnection extends _metaApiConnection.default { /** * Opens the connection. Can only be called the first time, next calls will be ignored. * @param {string} instanceId connection instance id * @return {Promise} promise resolving when the connection is opened */ async connect(instanceId) { if (!this._openedInstances.includes(instanceId)) { this._openedInstances.push(instanceId); } if (!this._opened) { this._opened = true; const accountRegions = this._account.accountRegions; this._websocketClient.addAccountCache(this._account.id, accountRegions); Object.keys(accountRegions).forEach((region)=>{ if (!this._options.region || this._options.region === region) { this._websocketClient.ensureSubscribe(accountRegions[region], 0); this._websocketClient.ensureSubscribe(accountRegions[region], 1); } }); } } /** * Closes the connection. The instance of the class should no longer be used after this method is invoked. * @param {string} instanceId connection instance id */ async close(instanceId) { if (this._opened) { this._openedInstances = this._openedInstances.filter((id)=>id !== instanceId); if (!this._openedInstances.length && !this._closed) { await this._connectionRegistry.removeRpc(this.account); this._websocketClient.removeSynchronizationListener(this.account.id, this); this._websocketClient.removeAccountCache(this.account.id); this._websocketClient.removeReconnectListener(this); this._closed = true; } } } /** * Invoked when connection to MetaTrader terminal established * @param {String} instanceIndex index of an account instance connected * @param {Number} replicas number of account replicas launched * @return {Promise} promise which resolves when the asynchronous event is processed */ async onConnected(instanceIndex, replicas) { const state = this._getState(instanceIndex); state.synchronized = true; const region = this.getRegion(instanceIndex); this.cancelRefresh(region); } /** * Invoked when connection to MetaTrader terminal terminated * @param {String} instanceIndex index of an account instance connected * @return {Promise} promise which resolves when the asynchronous event is processed */ async onDisconnected(instanceIndex) { const state = this._getState(instanceIndex); state.synchronized = false; this._logger.debug(`${this._account.id}:${instanceIndex}: disconnected from broker`); } /** * Invoked when a stream for an instance index is closed * @param {String} instanceIndex index of an account instance connected */ async onStreamClosed(instanceIndex) { delete this._stateByInstanceIndex[instanceIndex]; } /** * Returns flag indicating status of state synchronization with MetaTrader terminal * @returns {Boolean} a flag indicating status of state synchronization with MetaTrader terminal */ isSynchronized() { return Object.values(this._stateByInstanceIndex).map((instance)=>instance.synchronized).includes(true); } /** * Waits until synchronization to RPC application is completed * @param {Number} timeoutInSeconds synchronization timeout in seconds. Defaults to 5 minutes * @return {Promise} promise which resolves when synchronization to RPC application is completed * @throws {TimeoutError} if application failed to synchronize with the teminal within timeout allowed */ async waitSynchronized(timeoutInSeconds = 300) { this._checkIsConnectionActive(); const startTime = Date.now(); let synchronized = this.isSynchronized(); while(!synchronized && startTime + timeoutInSeconds * 1000 > Date.now()){ await new Promise((res)=>setTimeout(res, 1000)); synchronized = this.isSynchronized(); } if (!synchronized) { throw new _timeoutError.default("Timed out waiting for MetaApi to synchronize to MetaTrader account " + this._account.id); } // eslint-disable-next-line while(true){ try { await this._websocketClient.waitSynchronized(this._account.id, undefined, "RPC", 5, "RPC"); break; } catch (err) { if (Date.now() > startTime + timeoutInSeconds * 1000) { throw err; } } } } /** * Invoked when connection to MetaApi websocket API restored after a disconnect * @param {String} region reconnected region * @param {Number} instanceNumber reconnected instance number * @return {Promise} promise which resolves when connection to MetaApi websocket API restored after a disconnect */ async onReconnected(region, instanceNumber) { const instanceTemplate = `${region}:${instanceNumber}`; Object.keys(this._stateByInstanceIndex).filter((key)=>key.startsWith(`${instanceTemplate}:`)).forEach((key)=>{ delete this._stateByInstanceIndex[key]; }); } _getState(instanceIndex) { if (!this._stateByInstanceIndex[instanceIndex]) { this._stateByInstanceIndex[instanceIndex] = { instanceIndex, synchronized: false }; } return this._stateByInstanceIndex[instanceIndex]; } /** * Constructs MetaApi MetaTrader RPC Api connection * @param {MetaApiOpts} options MetaApi options * @param {MetaApiWebsocketClient} websocketClient MetaApi websocket client * @param {MetatraderAccount} account MetaTrader account id to connect to * @param {ConnectionRegistry} connectionRegistry metatrader account connection registry */ constructor(options, websocketClient, account, connectionRegistry){ super(options, websocketClient, account, "RPC"); _define_property(this, "_openedInstances", void 0); this._connectionRegistry = connectionRegistry; this._websocketClient.addSynchronizationListener(account.id, this); this._stateByInstanceIndex = {}; this._openedInstances = []; Object.values(account.accountRegions).forEach((replicaId)=>this._websocketClient.addReconnectListener(this, replicaId)); this._logger = _logger.default.getLogger("MetaApiConnection"); } }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCBMb2dnZXJNYW5hZ2VyIGZyb20gJy4uL2xvZ2dlcic7XG5pbXBvcnQgTWV0YUFwaUNvbm5lY3Rpb24gZnJvbSAnLi9tZXRhQXBpQ29ubmVjdGlvbic7XG5pbXBvcnQgVGltZW91dEVycm9yIGZyb20gJy4uL2NsaWVudHMvdGltZW91dEVycm9yJztcblxuLyoqXG4gKiBFeHBvc2VzIE1ldGFBcGkgTWV0YVRyYWRlciBSUEMgQVBJIGNvbm5lY3Rpb24gdG8gY29uc3VtZXJzXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFJwY01ldGFBcGlDb25uZWN0aW9uIGV4dGVuZHMgTWV0YUFwaUNvbm5lY3Rpb24ge1xuICBcbiAgcHJpdmF0ZSBfb3BlbmVkSW5zdGFuY2VzOiBhbnlbXTtcblxuICAvKipcbiAgICogQ29uc3RydWN0cyBNZXRhQXBpIE1ldGFUcmFkZXIgUlBDIEFwaSBjb25uZWN0aW9uXG4gICAqIEBwYXJhbSB7TWV0YUFwaU9wdHN9IG9wdGlvbnMgTWV0YUFwaSBvcHRpb25zXG4gICAqIEBwYXJhbSB7TWV0YUFwaVdlYnNvY2tldENsaWVudH0gd2Vic29ja2V0Q2xpZW50IE1ldGFBcGkgd2Vic29ja2V0IGNsaWVudFxuICAgKiBAcGFyYW0ge01ldGF0cmFkZXJBY2NvdW50fSBhY2NvdW50IE1ldGFUcmFkZXIgYWNjb3VudCBpZCB0byBjb25uZWN0IHRvXG4gICAqIEBwYXJhbSB7Q29ubmVjdGlvblJlZ2lzdHJ5fSBjb25uZWN0aW9uUmVnaXN0cnkgbWV0YXRyYWRlciBhY2NvdW50IGNvbm5lY3Rpb24gcmVnaXN0cnlcbiAgICovXG4gIGNvbnN0cnVjdG9yKG9wdGlvbnMsIHdlYnNvY2tldENsaWVudCwgYWNjb3VudCwgY29ubmVjdGlvblJlZ2lzdHJ5KSB7XG4gICAgc3VwZXIob3B0aW9ucywgd2Vic29ja2V0Q2xpZW50LCBhY2NvdW50LCAnUlBDJyk7XG4gICAgdGhpcy5fY29ubmVjdGlvblJlZ2lzdHJ5ID0gY29ubmVjdGlvblJlZ2lzdHJ5O1xuICAgIHRoaXMuX3dlYnNvY2tldENsaWVudC5hZGRTeW5jaHJvbml6YXRpb25MaXN0ZW5lcihhY2NvdW50LmlkLCB0aGlzKTtcbiAgICB0aGlzLl9zdGF0ZUJ5SW5zdGFuY2VJbmRleCA9IHt9O1xuICAgIHRoaXMuX29wZW5lZEluc3RhbmNlcyA9IFtdO1xuICAgIE9iamVjdC52YWx1ZXMoYWNjb3VudC5hY2NvdW50UmVnaW9ucylcbiAgICAgIC5mb3JFYWNoKHJlcGxpY2FJZCA9PiB0aGlzLl93ZWJzb2NrZXRDbGllbnQuYWRkUmVjb25uZWN0TGlzdGVuZXIodGhpcywgcmVwbGljYUlkKSk7XG4gICAgdGhpcy5fbG9nZ2VyID0gTG9nZ2VyTWFuYWdlci5nZXRMb2dnZXIoJ01ldGFBcGlDb25uZWN0aW9uJyk7XG4gIH1cblxuICAvKipcbiAgICogT3BlbnMgdGhlIGNvbm5lY3Rpb24uIENhbiBvbmx5IGJlIGNhbGxlZCB0aGUgZmlyc3QgdGltZSwgbmV4dCBjYWxscyB3aWxsIGJlIGlnbm9yZWQuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpbnN0YW5jZUlkIGNvbm5lY3Rpb24gaW5zdGFuY2UgaWRcbiAgICogQHJldHVybiB7UHJvbWlzZX0gcHJvbWlzZSByZXNvbHZpbmcgd2hlbiB0aGUgY29ubmVjdGlvbiBpcyBvcGVuZWRcbiAgICovXG4gIGFzeW5jIGNvbm5lY3QoaW5zdGFuY2VJZCkge1xuICAgIGlmICghdGhpcy5fb3BlbmVkSW5zdGFuY2VzLmluY2x1ZGVzKGluc3RhbmNlSWQpKSB7XG4gICAgICB0aGlzLl9vcGVuZWRJbnN0YW5jZXMucHVzaChpbnN0YW5jZUlkKTtcbiAgICB9XG4gICAgaWYgKCF0aGlzLl9vcGVuZWQpIHtcbiAgICAgIHRoaXMuX29wZW5lZCA9IHRydWU7XG4gICAgICBjb25zdCBhY2NvdW50UmVnaW9ucyA9IHRoaXMuX2FjY291bnQuYWNjb3VudFJlZ2lvbnM7XG4gICAgICB0aGlzLl93ZWJzb2NrZXRDbGllbnQuYWRkQWNjb3VudENhY2hlKHRoaXMuX2FjY291bnQuaWQsIGFjY291bnRSZWdpb25zKTtcbiAgICAgIE9iamVjdC5rZXlzKGFjY291bnRSZWdpb25zKS5mb3JFYWNoKHJlZ2lvbiA9PiB7XG4gICAgICAgIGlmICghdGhpcy5fb3B0aW9ucy5yZWdpb24gfHwgdGhpcy5fb3B0aW9ucy5yZWdpb24gPT09IHJlZ2lvbikge1xuICAgICAgICAgIHRoaXMuX3dlYnNvY2tldENsaWVudC5lbnN1cmVTdWJzY3JpYmUoYWNjb3VudFJlZ2lvbnNbcmVnaW9uXSwgMCk7XG4gICAgICAgICAgdGhpcy5fd2Vic29ja2V0Q2xpZW50LmVuc3VyZVN1YnNjcmliZShhY2NvdW50UmVnaW9uc1tyZWdpb25dLCAxKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENsb3NlcyB0aGUgY29ubmVjdGlvbi4gVGhlIGluc3RhbmNlIG9mIHRoZSBjbGFzcyBzaG91bGQgbm8gbG9uZ2VyIGJlIHVzZWQgYWZ0ZXIgdGhpcyBtZXRob2QgaXMgaW52b2tlZC5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGluc3RhbmNlSWQgY29ubmVjdGlvbiBpbnN0YW5jZSBpZFxuICAgKi9cbiAgYXN5bmMgY2xvc2UoaW5zdGFuY2VJZCkge1xuICAgIGlmICh0aGlzLl9vcGVuZWQpIHtcbiAgICAgIHRoaXMuX29wZW5lZEluc3RhbmNlcyA9IHRoaXMuX29wZW5lZEluc3RhbmNlcy5maWx0ZXIoaWQgPT4gaWQgIT09IGluc3RhbmNlSWQpO1xuICAgICAgaWYgKCF0aGlzLl9vcGVuZWRJbnN0YW5jZXMubGVuZ3RoICYmICF0aGlzLl9jbG9zZWQpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5fY29ubmVjdGlvblJlZ2lzdHJ5LnJlbW92ZVJwYyh0aGlzLmFjY291bnQpO1xuICAgICAgICB0aGlzLl93ZWJzb2NrZXRDbGllbnQucmVtb3ZlU3luY2hyb25pemF0aW9uTGlzdGVuZXIodGhpcy5hY2NvdW50LmlkLCB0aGlzKTtcbiAgICAgICAgdGhpcy5fd2Vic29ja2V0Q2xpZW50LnJlbW92ZUFjY291bnRDYWNoZSh0aGlzLmFjY291bnQuaWQpO1xuICAgICAgICB0aGlzLl93ZWJzb2NrZXRDbGllbnQucmVtb3ZlUmVjb25uZWN0TGlzdGVuZXIodGhpcyk7XG4gICAgICAgIHRoaXMuX2Nsb3NlZCA9IHRydWU7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogSW52b2tlZCB3aGVuIGNvbm5lY3Rpb24gdG8gTWV0YVRyYWRlciB0ZXJtaW5hbCBlc3RhYmxpc2hlZFxuICAgKiBAcGFyYW0ge1N0cmluZ30gaW5zdGFuY2VJbmRleCBpbmRleCBvZiBhbiBhY2NvdW50IGluc3RhbmNlIGNvbm5lY3RlZFxuICAgKiBAcGFyYW0ge051bWJlcn0gcmVwbGljYXMgbnVtYmVyIG9mIGFjY291bnQgcmVwbGljYXMgbGF1bmNoZWRcbiAgICogQHJldHVybiB7UHJvbWlzZX0gcHJvbWlzZSB3aGljaCByZXNvbHZlcyB3aGVuIHRoZSBhc3luY2hyb25vdXMgZXZlbnQgaXMgcHJvY2Vzc2VkXG4gICAqL1xuICBhc3luYyBvbkNvbm5lY3RlZChpbnN0YW5jZUluZGV4LCByZXBsaWNhcykge1xuICAgIGNvbnN0IHN0YXRlID0gdGhpcy5fZ2V0U3RhdGUoaW5zdGFuY2VJbmRleCk7XG4gICAgc3RhdGUuc3luY2hyb25pemVkID0gdHJ1ZTtcbiAgICBjb25zdCByZWdpb24gPSB0aGlzLmdldFJlZ2lvbihpbnN0YW5jZUluZGV4KTtcbiAgICB0aGlzLmNhbmNlbFJlZnJlc2gocmVnaW9uKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbnZva2VkIHdoZW4gY29ubmVjdGlvbiB0byBNZXRhVHJhZGVyIHRlcm1pbmFsIHRlcm1pbmF0ZWRcbiAgICogQHBhcmFtIHtTdHJpbmd9IGluc3RhbmNlSW5kZXggaW5kZXggb2YgYW4gYWNjb3VudCBpbnN0YW5jZSBjb25uZWN0ZWRcbiAgICogQHJldHVybiB7UHJvbWlzZX0gcHJvbWlzZSB3aGljaCByZXNvbHZlcyB3aGVuIHRoZSBhc3luY2hyb25vdXMgZXZlbnQgaXMgcHJvY2Vzc2VkXG4gICAqL1xuICBhc3luYyBvbkRpc2Nvbm5lY3RlZChpbnN0YW5jZUluZGV4KSB7XG4gICAgY29uc3Qgc3RhdGUgPSB0aGlzLl9nZXRTdGF0ZShpbnN0YW5jZUluZGV4KTtcbiAgICBzdGF0ZS5zeW5jaHJvbml6ZWQgPSBmYWxzZTtcbiAgICB0aGlzLl9sb2dnZXIuZGVidWcoYCR7dGhpcy5fYWNjb3VudC5pZH06JHtpbnN0YW5jZUluZGV4fTogZGlzY29ubmVjdGVkIGZyb20gYnJva2VyYCk7XG4gIH1cblxuICAvKipcbiAgICogSW52b2tlZCB3aGVuIGEgc3RyZWFtIGZvciBhbiBpbnN0YW5jZSBpbmRleCBpcyBjbG9zZWRcbiAgICogQHBhcmFtIHtTdHJpbmd9IGluc3RhbmNlSW5kZXggaW5kZXggb2YgYW4gYWNjb3VudCBpbnN0YW5jZSBjb25uZWN0ZWRcbiAgICovXG4gIGFzeW5jIG9uU3RyZWFtQ2xvc2VkKGluc3RhbmNlSW5kZXgpIHtcbiAgICBkZWxldGUgdGhpcy5fc3RhdGVCeUluc3RhbmNlSW5kZXhbaW5zdGFuY2VJbmRleF07XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBmbGFnIGluZGljYXRpbmcgc3RhdHVzIG9mIHN0YXRlIHN5bmNocm9uaXphdGlvbiB3aXRoIE1ldGFUcmFkZXIgdGVybWluYWxcbiAgICogQHJldHVybnMge0Jvb2xlYW59IGEgZmxhZyBpbmRpY2F0aW5nIHN0YXR1cyBvZiBzdGF0ZSBzeW5jaHJvbml6YXRpb24gd2l0aCBNZXRhVHJhZGVyIHRlcm1pbmFsXG4gICAqL1xuICBpc1N5bmNocm9uaXplZCgpIHtcbiAgICByZXR1cm4gT2JqZWN0LnZhbHVlczxhbnk+KHRoaXMuX3N0YXRlQnlJbnN0YW5jZUluZGV4KVxuICAgICAgLm1hcChpbnN0YW5jZSA9PiBpbnN0YW5jZS5zeW5jaHJvbml6ZWQpXG4gICAgICAuaW5jbHVkZXModHJ1ZSk7XG4gIH1cblxuICAvKipcbiAgICogV2FpdHMgdW50aWwgc3luY2hyb25pemF0aW9uIHRvIFJQQyBhcHBsaWNhdGlvbiBpcyBjb21wbGV0ZWRcbiAgICogQHBhcmFtIHtOdW1iZXJ9IHRpbWVvdXRJblNlY29uZHMgc3luY2hyb25pemF0aW9uIHRpbWVvdXQgaW4gc2Vjb25kcy4gRGVmYXVsdHMgdG8gNSBtaW51dGVzXG4gICAqIEByZXR1cm4ge1Byb21pc2V9IHByb21pc2Ugd2hpY2ggcmVzb2x2ZXMgd2hlbiBzeW5jaHJvbml6YXRpb24gdG8gUlBDIGFwcGxpY2F0aW9uIGlzIGNvbXBsZXRlZFxuICAgKiBAdGhyb3dzIHtUaW1lb3V0RXJyb3J9IGlmIGFwcGxpY2F0aW9uIGZhaWxlZCB0byBzeW5jaHJvbml6ZSB3aXRoIHRoZSB0ZW1pbmFsIHdpdGhpbiB0aW1lb3V0IGFsbG93ZWRcbiAgICovXG4gIGFzeW5jIHdhaXRTeW5jaHJvbml6ZWQodGltZW91dEluU2Vjb25kcz0zMDApIHtcbiAgICB0aGlzLl9jaGVja0lzQ29ubmVjdGlvbkFjdGl2ZSgpO1xuICAgIGNvbnN0IHN0YXJ0VGltZSA9IERhdGUubm93KCk7XG4gICAgbGV0IHN5bmNocm9uaXplZCA9IHRoaXMuaXNTeW5jaHJvbml6ZWQoKTtcbiAgICB3aGlsZSAoIXN5bmNocm9uaXplZCAmJiBzdGFydFRpbWUgKyB0aW1lb3V0SW5TZWNvbmRzICogMTAwMCA+IERhdGUubm93KCkpIHtcbiAgICAgIGF3YWl0IG5ldyBQcm9taXNlKHJlcyA9PiBzZXRUaW1lb3V0KHJlcywgMTAwMCkpO1xuICAgICAgc3luY2hyb25pemVkID0gdGhpcy5pc1N5bmNocm9uaXplZCgpO1xuICAgIH1cbiAgICBpZiAoIXN5bmNocm9uaXplZCkge1xuICAgICAgdGhyb3cgbmV3IFRpbWVvdXRFcnJvcignVGltZWQgb3V0IHdhaXRpbmcgZm9yIE1ldGFBcGkgdG8gc3luY2hyb25pemUgdG8gTWV0YVRyYWRlciBhY2NvdW50ICcgK1xuICAgICAgICB0aGlzLl9hY2NvdW50LmlkKTtcbiAgICB9XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lXG4gICAgd2hpbGUgKHRydWUpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHRoaXMuX3dlYnNvY2tldENsaWVudC53YWl0U3luY2hyb25pemVkKHRoaXMuX2FjY291bnQuaWQsIHVuZGVmaW5lZCwgJ1JQQycsIDUsICdSUEMnKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgaWYgKERhdGUubm93KCkgPiBzdGFydFRpbWUgKyB0aW1lb3V0SW5TZWNvbmRzICogMTAwMCkge1xuICAgICAgICAgIHRocm93IGVycjtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBJbnZva2VkIHdoZW4gY29ubmVjdGlvbiB0byBNZXRhQXBpIHdlYnNvY2tldCBBUEkgcmVzdG9yZWQgYWZ0ZXIgYSBkaXNjb25uZWN0XG4gICAqIEBwYXJhbSB7U3RyaW5nfSByZWdpb24gcmVjb25uZWN0ZWQgcmVnaW9uXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBpbnN0YW5jZU51bWJlciByZWNvbm5lY3RlZCBpbnN0YW5jZSBudW1iZXJcbiAgICogQHJldHVybiB7UHJvbWlzZX0gcHJvbWlzZSB3aGljaCByZXNvbHZlcyB3aGVuIGNvbm5lY3Rpb24gdG8gTWV0YUFwaSB3ZWJzb2NrZXQgQVBJIHJlc3RvcmVkIGFmdGVyIGEgZGlzY29ubmVjdFxuICAgKi9cbiAgYXN5bmMgb25SZWNvbm5lY3RlZChyZWdpb24sIGluc3RhbmNlTnVtYmVyKSB7XG4gICAgY29uc3QgaW5zdGFuY2VUZW1wbGF0ZSA9IGAke3JlZ2lvbn06JHtpbnN0YW5jZU51bWJlcn1gO1xuICAgIE9iamVjdC5rZXlzKHRoaXMuX3N0YXRlQnlJbnN0YW5jZUluZGV4KVxuICAgICAgLmZpbHRlcihrZXkgPT4ga2V5LnN0YXJ0c1dpdGgoYCR7aW5zdGFuY2VUZW1wbGF0ZX06YCkpLmZvckVhY2goa2V5ID0+IHtcbiAgICAgICAgZGVsZXRlIHRoaXMuX3N0YXRlQnlJbnN0YW5jZUluZGV4W2tleV07XG4gICAgICB9KTtcbiAgfVxuXG4gIF9nZXRTdGF0ZShpbnN0YW5jZUluZGV4KSB7XG4gICAgaWYgKCF0aGlzLl9zdGF0ZUJ5SW5zdGFuY2VJbmRleFtpbnN0YW5jZUluZGV4XSkge1xuICAgICAgdGhpcy5fc3RhdGVCeUluc3RhbmNlSW5kZXhbaW5zdGFuY2VJbmRleF0gPSB7XG4gICAgICAgIGluc3RhbmNlSW5kZXgsXG4gICAgICAgIHN5bmNocm9uaXplZDogZmFsc2UsXG4gICAgICB9O1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fc3RhdGVCeUluc3RhbmNlSW5kZXhbaW5zdGFuY2VJbmRleF07XG4gIH1cblxufVxuIl0sIm5hbWVzIjpbIlJwY01ldGFBcGlDb25uZWN0aW9uIiwiTWV0YUFwaUNvbm5lY3Rpb24iLCJjb25uZWN0IiwiaW5zdGFuY2VJZCIsIl9vcGVuZWRJbnN0YW5jZXMiLCJpbmNsdWRlcyIsInB1c2giLCJfb3BlbmVkIiwiYWNjb3VudFJlZ2lvbnMiLCJfYWNjb3VudCIsIl93ZWJzb2NrZXRDbGllbnQiLCJhZGRBY2NvdW50Q2FjaGUiLCJpZCIsIk9iamVjdCIsImtleXMiLCJmb3JFYWNoIiwicmVnaW9uIiwiX29wdGlvbnMiLCJlbnN1cmVTdWJzY3JpYmUiLCJjbG9zZSIsImZpbHRlciIsImxlbmd0aCIsIl9jbG9zZWQiLCJfY29ubmVjdGlvblJlZ2lzdHJ5IiwicmVtb3ZlUnBjIiwiYWNjb3VudCIsInJlbW92ZVN5bmNocm9uaXphdGlvbkxpc3RlbmVyIiwicmVtb3ZlQWNjb3VudENhY2hlIiwicmVtb3ZlUmVjb25uZWN0TGlzdGVuZXIiLCJvbkNvbm5lY3RlZCIsImluc3RhbmNlSW5kZXgiLCJyZXBsaWNhcyIsInN0YXRlIiwiX2dldFN0YXRlIiwic3luY2hyb25pemVkIiwiZ2V0UmVnaW9uIiwiY2FuY2VsUmVmcmVzaCIsIm9uRGlzY29ubmVjdGVkIiwiX2xvZ2dlciIsImRlYnVnIiwib25TdHJlYW1DbG9zZWQiLCJfc3RhdGVCeUluc3RhbmNlSW5kZXgiLCJpc1N5bmNocm9uaXplZCIsInZhbHVlcyIsIm1hcCIsImluc3RhbmNlIiwid2FpdFN5bmNocm9uaXplZCIsInRpbWVvdXRJblNlY29uZHMiLCJfY2hlY2tJc0Nvbm5lY3Rpb25BY3RpdmUiLCJzdGFydFRpbWUiLCJEYXRlIiwibm93IiwiUHJvbWlzZSIsInJlcyIsInNldFRpbWVvdXQiLCJUaW1lb3V0RXJyb3IiLCJ1bmRlZmluZWQiLCJlcnIiLCJvblJlY29ubmVjdGVkIiwiaW5zdGFuY2VOdW1iZXIiLCJpbnN0YW5jZVRlbXBsYXRlIiwia2V5Iiwic3RhcnRzV2l0aCIsImNvbnN0cnVjdG9yIiwib3B0aW9ucyIsIndlYnNvY2tldENsaWVudCIsImNvbm5lY3Rpb25SZWdpc3RyeSIsImFkZFN5bmNocm9uaXphdGlvbkxpc3RlbmVyIiwicmVwbGljYUlkIiwiYWRkUmVjb25uZWN0TGlzdGVuZXIiLCJMb2dnZXJNYW5hZ2VyIiwiZ2V0TG9nZ2VyIl0sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7OztlQVNxQkE7OzsrREFQSzswRUFDSTtxRUFDTDs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUtWLElBQUEsQUFBTUEsdUJBQU4sTUFBTUEsNkJBQTZCQywwQkFBaUI7SUFzQmpFOzs7O0dBSUMsR0FDRCxNQUFNQyxRQUFRQyxVQUFVLEVBQUU7UUFDeEIsSUFBSSxDQUFDLElBQUksQ0FBQ0MsZ0JBQWdCLENBQUNDLFFBQVEsQ0FBQ0YsYUFBYTtZQUMvQyxJQUFJLENBQUNDLGdCQUFnQixDQUFDRSxJQUFJLENBQUNIO1FBQzdCO1FBQ0EsSUFBSSxDQUFDLElBQUksQ0FBQ0ksT0FBTyxFQUFFO1lBQ2pCLElBQUksQ0FBQ0EsT0FBTyxHQUFHO1lBQ2YsTUFBTUMsaUJBQWlCLElBQUksQ0FBQ0MsUUFBUSxDQUFDRCxjQUFjO1lBQ25ELElBQUksQ0FBQ0UsZ0JBQWdCLENBQUNDLGVBQWUsQ0FBQyxJQUFJLENBQUNGLFFBQVEsQ0FBQ0csRUFBRSxFQUFFSjtZ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