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
JavaScript
"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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