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)
205 lines (204 loc) • 23.7 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);
});
};
}
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;
}
import LoggerManager from '../logger';
import MetaApiConnection from './metaApiConnection';
import TimeoutError from '../clients/timeoutError';
let RpcMetaApiConnection = class RpcMetaApiConnection extends MetaApiConnection {
/**
* 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
*/ connect(instanceId) {
var _this = this;
return _async_to_generator(function*() {
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
*/ close(instanceId) {
var _this = this;
return _async_to_generator(function*() {
if (_this._opened) {
_this._openedInstances = _this._openedInstances.filter((id)=>id !== instanceId);
if (!_this._openedInstances.length && !_this._closed) {
yield _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
*/ onConnected(instanceIndex, replicas) {
var _this = this;
return _async_to_generator(function*() {
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
*/ onDisconnected(instanceIndex) {
var _this = this;
return _async_to_generator(function*() {
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
*/ onStreamClosed(instanceIndex) {
var _this = this;
return _async_to_generator(function*() {
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
*/ waitSynchronized(timeoutInSeconds = 300) {
var _this = this;
return _async_to_generator(function*() {
_this._checkIsConnectionActive();
const startTime = Date.now();
let synchronized = _this.isSynchronized();
while(!synchronized && startTime + timeoutInSeconds * 1000 > Date.now()){
yield new Promise((res)=>setTimeout(res, 1000));
synchronized = _this.isSynchronized();
}
if (!synchronized) {
throw new TimeoutError('Timed out waiting for MetaApi to synchronize to MetaTrader account ' + _this._account.id);
}
// eslint-disable-next-line
while(true){
try {
yield _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
*/ onReconnected(region, instanceNumber) {
var _this = this;
return _async_to_generator(function*() {
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 = LoggerManager.getLogger('MetaApiConnection');
}
};
/**
* Exposes MetaApi MetaTrader RPC API connection to consumers
*/ export { RpcMetaApiConnection as default };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCBMb2dnZXJNYW5hZ2VyIGZyb20gJy4uL2xvZ2dlcic7XG5pbXBvcnQgTWV0YUFwaUNvbm5lY3Rpb24gZnJvbSAnLi9tZXRhQXBpQ29ubmVjdGlvbic7XG5pbXBvcnQgVGltZW91dEVycm9yIGZyb20gJy4uL2NsaWVudHMvdGltZW91dEVycm9yJztcblxuLyoqXG4gKiBFeHBvc2VzIE1ldGFBcGkgTWV0YVRyYWRlciBSUEMgQVBJIGNvbm5lY3Rpb24gdG8gY29uc3VtZXJzXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFJwY01ldGFBcGlDb25uZWN0aW9uIGV4dGVuZHMgTWV0YUFwaUNvbm5lY3Rpb24ge1xuICBcbiAgcHJpdmF0ZSBfb3BlbmVkSW5zdGFuY2VzOiBhbnlbXTtcblxuICAvKipcbiAgICogQ29uc3RydWN0cyBNZXRhQXBpIE1ldGFUcmFkZXIgUlBDIEFwaSBjb25uZWN0aW9uXG4gICAqIEBwYXJhbSB7TWV0YUFwaU9wdHN9IG9wdGlvbnMgTWV0YUFwaSBvcHRpb25zXG4gICAqIEBwYXJhbSB7TWV0YUFwaVdlYnNvY2tldENsaWVudH0gd2Vic29ja2V0Q2xpZW50IE1ldGFBcGkgd2Vic29ja2V0IGNsaWVudFxuICAgKiBAcGFyYW0ge01ldGF0cmFkZXJBY2NvdW50fSBhY2NvdW50IE1ldGFUcmFkZXIgYWNjb3VudCBpZCB0byBjb25uZWN0IHRvXG4gICAqIEBwYXJhbSB7Q29ubmVjdGlvblJlZ2lzdHJ5fSBjb25uZWN0aW9uUmVnaXN0cnkgbWV0YXRyYWRlciBhY2NvdW50IGNvbm5lY3Rpb24gcmVnaXN0cnlcbiAgICovXG4gIGNvbnN0cnVjdG9yKG9wdGlvbnMsIHdlYnNvY2tldENsaWVudCwgYWNjb3VudCwgY29ubmVjdGlvblJlZ2lzdHJ5KSB7XG4gICAgc3VwZXIob3B0aW9ucywgd2Vic29ja2V0Q2xpZW50LCBhY2NvdW50LCAnUlBDJyk7XG4gICAgdGhpcy5fY29ubmVjdGlvblJlZ2lzdHJ5ID0gY29ubmVjdGlvblJlZ2lzdHJ5O1xuICAgIHRoaXMuX3dlYnNvY2tldENsaWVudC5hZGRTeW5jaHJvbml6YXRpb25MaXN0ZW5lcihhY2NvdW50LmlkLCB0aGlzKTtcbiAgICB0aGlzLl9zdGF0ZUJ5SW5zdGFuY2VJbmRleCA9IHt9O1xuICAgIHRoaXMuX29wZW5lZEluc3RhbmNlcyA9IFtdO1xuICAgIE9iamVjdC52YWx1ZXMoYWNjb3VudC5hY2NvdW50UmVnaW9ucylcbiAgICAgIC5mb3JFYWNoKHJlcGxpY2FJZCA9PiB0aGlzLl93ZWJzb2NrZXRDbGllbnQuYWRkUmVjb25uZWN0TGlzdGVuZXIodGhpcywgcmVwbGljYUlkKSk7XG4gICAgdGhpcy5fbG9nZ2VyID0gTG9nZ2VyTWFuYWdlci5nZXRMb2dnZXIoJ01ldGFBcGlDb25uZWN0aW9uJyk7XG4gIH1cblxuICAvKipcbiAgICogT3BlbnMgdGhlIGNvbm5lY3Rpb24uIENhbiBvbmx5IGJlIGNhbGxlZCB0aGUgZmlyc3QgdGltZSwgbmV4dCBjYWxscyB3aWxsIGJlIGlnbm9yZWQuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpbnN0YW5jZUlkIGNvbm5lY3Rpb24gaW5zdGFuY2UgaWRcbiAgICogQHJldHVybiB7UHJvbWlzZX0gcHJvbWlzZSByZXNvbHZpbmcgd2hlbiB0aGUgY29ubmVjdGlvbiBpcyBvcGVuZWRcbiAgICovXG4gIGFzeW5jIGNvbm5lY3QoaW5zdGFuY2VJZCkge1xuICAgIGlmICghdGhpcy5fb3BlbmVkSW5zdGFuY2VzLmluY2x1ZGVzKGluc3RhbmNlSWQpKSB7XG4gICAgICB0aGlzLl9vcGVuZWRJbnN0YW5jZXMucHVzaChpbnN0YW5jZUlkKTtcbiAgICB9XG4gICAgaWYgKCF0aGlzLl9vcGVuZWQpIHtcbiAgICAgIHRoaXMuX29wZW5lZCA9IHRydWU7XG4gICAgICBjb25zdCBhY2NvdW50UmVnaW9ucyA9IHRoaXMuX2FjY291bnQuYWNjb3VudFJlZ2lvbnM7XG4gICAgICB0aGlzLl93ZWJzb2NrZXRDbGllbnQuYWRkQWNjb3VudENhY2hlKHRoaXMuX2FjY291bnQuaWQsIGFjY291bnRSZWdpb25zKTtcbiAgICAgIE9iamVjdC5rZXlzKGFjY291bnRSZWdpb25zKS5mb3JFYWNoKHJlZ2lvbiA9PiB7XG4gICAgICAgIGlmICghdGhpcy5fb3B0aW9ucy5yZWdpb24gfHwgdGhpcy5fb3B0aW9ucy5yZWdpb24gPT09IHJlZ2lvbikge1xuICAgICAgICAgIHRoaXMuX3dlYnNvY2tldENsaWVudC5lbnN1cmVTdWJzY3JpYmUoYWNjb3VudFJlZ2lvbnNbcmVnaW9uXSwgMCk7XG4gICAgICAgICAgdGhpcy5fd2Vic29ja2V0Q2xpZW50LmVuc3VyZVN1YnNjcmliZShhY2NvdW50UmVnaW9uc1tyZWdpb25dLCAxKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENsb3NlcyB0aGUgY29ubmVjdGlvbi4gVGhlIGluc3RhbmNlIG9mIHRoZSBjbGFzcyBzaG91bGQgbm8gbG9uZ2VyIGJlIHVzZWQgYWZ0ZXIgdGhpcyBtZXRob2QgaXMgaW52b2tlZC5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGluc3RhbmNlSWQgY29ubmVjdGlvbiBpbnN0YW5jZSBpZFxuICAgKi9cbiAgYXN5bmMgY2xvc2UoaW5zdGFuY2VJZCkge1xuICAgIGlmICh0aGlzLl9vcGVuZWQpIHtcbiAgICAgIHRoaXMuX29wZW5lZEluc3RhbmNlcyA9IHRoaXMuX29wZW5lZEluc3RhbmNlcy5maWx0ZXIoaWQgPT4gaWQgIT09IGluc3RhbmNlSWQpO1xuICAgICAgaWYgKCF0aGlzLl9vcGVuZWRJbnN0YW5jZXMubGVuZ3RoICYmICF0aGlzLl9jbG9zZWQpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5fY29ubmVjdGlvblJlZ2lzdHJ5LnJlbW92ZVJwYyh0aGlzLmFjY291bnQpO1xuICAgICAgICB0aGlzLl93ZWJzb2NrZXRDbGllbnQucmVtb3ZlU3luY2hyb25pemF0aW9uTGlzdGVuZXIodGhpcy5hY2NvdW50LmlkLCB0aGlzKTtcbiAgICAgICAgdGhpcy5fd2Vic29ja2V0Q2xpZW50LnJlbW92ZUFjY291bnRDYWNoZSh0aGlzLmFjY291bnQuaWQpO1xuICAgICAgICB0aGlzLl93ZWJzb2NrZXRDbGllbnQucmVtb3ZlUmVjb25uZWN0TGlzdGVuZXIodGhpcyk7XG4gICAgICAgIHRoaXMuX2Nsb3NlZCA9IHRydWU7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogSW52b2tlZCB3aGVuIGNvbm5lY3Rpb24gdG8gTWV0YVRyYWRlciB0ZXJtaW5hbCBlc3RhYmxpc2hlZFxuICAgKiBAcGFyYW0ge1N0cmluZ30gaW5zdGFuY2VJbmRleCBpbmRleCBvZiBhbiBhY2NvdW50IGluc3RhbmNlIGNvbm5lY3RlZFxuICAgKiBAcGFyYW0ge051bWJlcn0gcmVwbGljYXMgbnVtYmVyIG9mIGFjY291bnQgcmVwbGljYXMgbGF1bmNoZWRcbiAgICogQHJldHVybiB7UHJvbWlzZX0gcHJvbWlzZSB3aGljaCByZXNvbHZlcyB3aGVuIHRoZSBhc3luY2hyb25vdXMgZXZlbnQgaXMgcHJvY2Vzc2VkXG4gICAqL1xuICBhc3luYyBvbkNvbm5lY3RlZChpbnN0YW5jZUluZGV4LCByZXBsaWNhcykge1xuICAgIGNvbnN0IHN0YXRlID0gdGhpcy5fZ2V0U3RhdGUoaW5zdGFuY2VJbmRleCk7XG4gICAgc3RhdGUuc3luY2hyb25pemVkID0gdHJ1ZTtcbiAgICBjb25zdCByZWdpb24gPSB0aGlzLmdldFJlZ2lvbihpbnN0YW5jZUluZGV4KTtcbiAgICB0aGlzLmNhbmNlbFJlZnJlc2gocmVnaW9uKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbnZva2VkIHdoZW4gY29ubmVjdGlvbiB0byBNZXRhVHJhZGVyIHRlcm1pbmFsIHRlcm1pbmF0ZWRcbiAgICogQHBhcmFtIHtTdHJpbmd9IGluc3RhbmNlSW5kZXggaW5kZXggb2YgYW4gYWNjb3VudCBpbnN0YW5jZSBjb25uZWN0ZWRcbiAgICogQHJldHVybiB7UHJvbWlzZX0gcHJvbWlzZSB3aGljaCByZXNvbHZlcyB3aGVuIHRoZSBhc3luY2hyb25vdXMgZXZlbnQgaXMgcHJvY2Vzc2VkXG4gICAqL1xuICBhc3luYyBvbkRpc2Nvbm5lY3RlZChpbnN0YW5jZUluZGV4KSB7XG4gICAgY29uc3Qgc3RhdGUgPSB0aGlzLl9nZXRTdGF0ZShpbnN0YW5jZUluZGV4KTtcbiAgICBzdGF0ZS5zeW5jaHJvbml6ZWQgPSBmYWxzZTtcbiAgICB0aGlzLl9sb2dnZXIuZGVidWcoYCR7dGhpcy5fYWNjb3VudC5pZH06JHtpbnN0YW5jZUluZGV4fTogZGlzY29ubmVjdGVkIGZyb20gYnJva2VyYCk7XG4gIH1cblxuICAvKipcbiAgICogSW52b2tlZCB3aGVuIGEgc3RyZWFtIGZvciBhbiBpbnN0YW5jZSBpbmRleCBpcyBjbG9zZWRcbiAgICogQHBhcmFtIHtTdHJpbmd9IGluc3RhbmNlSW5kZXggaW5kZXggb2YgYW4gYWNjb3VudCBpbnN0YW5jZSBjb25uZWN0ZWRcbiAgICovXG4gIGFzeW5jIG9uU3RyZWFtQ2xvc2VkKGluc3RhbmNlSW5kZXgpIHtcbiAgICBkZWxldGUgdGhpcy5fc3RhdGVCeUluc3RhbmNlSW5kZXhbaW5zdGFuY2VJbmRleF07XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBmbGFnIGluZGljYXRpbmcgc3RhdHVzIG9mIHN0YXRlIHN5bmNocm9uaXphdGlvbiB3aXRoIE1ldGFUcmFkZXIgdGVybWluYWxcbiAgICogQHJldHVybnMge0Jvb2xlYW59IGEgZmxhZyBpbmRpY2F0aW5nIHN0YXR1cyBvZiBzdGF0ZSBzeW5jaHJvbml6YXRpb24gd2l0aCBNZXRhVHJhZGVyIHRlcm1pbmFsXG4gICAqL1xuICBpc1N5bmNocm9uaXplZCgpIHtcbiAgICByZXR1cm4gT2JqZWN0LnZhbHVlczxhbnk+KHRoaXMuX3N0YXRlQnlJbnN0YW5jZUluZGV4KVxuICAgICAgLm1hcChpbnN0YW5jZSA9PiBpbnN0YW5jZS5zeW5jaHJvbml6ZWQpXG4gICAgICAuaW5jbHVkZXModHJ1ZSk7XG4gIH1cblxuICAvKipcbiAgICogV2FpdHMgdW50aWwgc3luY2hyb25pemF0aW9uIHRvIFJQQyBhcHBsaWNhdGlvbiBpcyBjb21wbGV0ZWRcbiAgICogQHBhcmFtIHtOdW1iZXJ9IHRpbWVvdXRJblNlY29uZHMgc3luY2hyb25pemF0aW9uIHRpbWVvdXQgaW4gc2Vjb25kcy4gRGVmYXVsdHMgdG8gNSBtaW51dGVzXG4gICAqIEByZXR1cm4ge1Byb21pc2V9IHByb21pc2Ugd2hpY2ggcmVzb2x2ZXMgd2hlbiBzeW5jaHJvbml6YXRpb24gdG8gUlBDIGFwcGxpY2F0aW9uIGlzIGNvbXBsZXRlZFxuICAgKiBAdGhyb3dzIHtUaW1lb3V0RXJyb3J9IGlmIGFwcGxpY2F0aW9uIGZhaWxlZCB0byBzeW5jaHJvbml6ZSB3aXRoIHRoZSB0ZW1pbmFsIHdpdGhpbiB0aW1lb3V0IGFsbG93ZWRcbiAgICovXG4gIGFzeW5jIHdhaXRTeW5jaHJvbml6ZWQodGltZW91dEluU2Vjb25kcz0zMDApIHtcbiAgICB0aGlzLl9jaGVja0lzQ29ubmVjdGlvbkFjdGl2ZSgpO1xuICAgIGNvbnN0IHN0YXJ0VGltZSA9IERhdGUubm93KCk7XG4gICAgbGV0IHN5bmNocm9uaXplZCA9IHRoaXMuaXNTeW5jaHJvbml6ZWQoKTtcbiAgICB3aGlsZSAoIXN5bmNocm9uaXplZCAmJiBzdGFydFRpbWUgKyB0aW1lb3V0SW5TZWNvbmRzICogMTAwMCA+IERhdGUubm93KCkpIHtcbiAgICAgIGF3YWl0IG5ldyBQcm9taXNlKHJlcyA9PiBzZXRUaW1lb3V0KHJlcywgMTAwMCkpO1xuICAgICAgc3luY2hyb25pemVkID0gdGhpcy5pc1N5bmNocm9uaXplZCgpO1xuICAgIH1cbiAgICBpZiAoIXN5bmNocm9uaXplZCkge1xuICAgICAgdGhyb3cgbmV3IFRpbWVvdXRFcnJvcignVGltZWQgb3V0IHdhaXRpbmcgZm9yIE1ldGFBcGkgdG8gc3luY2hyb25pemUgdG8gTWV0YVRyYWRlciBhY2NvdW50ICcgK1xuICAgICAgICB0aGlzLl9hY2NvdW50LmlkKTtcbiAgICB9XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lXG4gICAgd2hpbGUgKHRydWUpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHRoaXMuX3dlYnNvY2tldENsaWVudC53YWl0U3luY2hyb25pemVkKHRoaXMuX2FjY291bnQuaWQsIHVuZGVmaW5lZCwgJ1JQQycsIDUsICdSUEMnKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgaWYgKERhdGUubm93KCkgPiBzdGFydFRpbWUgKyB0aW1lb3V0SW5TZWNvbmRzICogMTAwMCkge1xuICAgICAgICAgIHRocm93IGVycjtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBJbnZva2VkIHdoZW4gY29ubmVjdGlvbiB0byBNZXRhQXBpIHdlYnNvY2tldCBBUEkgcmVzdG9yZWQgYWZ0ZXIgYSBkaXNjb25uZWN0XG4gICAqIEBwYXJhbSB7U3RyaW5nfSByZWdpb24gcmVjb25uZWN0ZWQgcmVnaW9uXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBpbnN0YW5jZU51bWJlciByZWNvbm5lY3RlZCBpbnN0YW5jZSBudW1iZXJcbiAgICogQHJldHVybiB7UHJvbWlzZX0gcHJvbWlzZSB3aGljaCByZXNvbHZlcyB3aGVuIGNvbm5lY3Rpb24gdG8gTWV0YUFwaSB3ZWJzb2NrZXQgQVBJIHJlc3RvcmVkIGFmdGVyIGEgZGlzY29ubmVjdFxuICAgKi9cbiAgYXN5bmMgb25SZWNvbm5lY3RlZChyZWdpb24sIGluc3RhbmNlTnVtYmVyKSB7XG4gICAgY29uc3QgaW5zdGFuY2VUZW1wbGF0ZSA9IGAke3JlZ2lvbn06JHtpbnN0YW5jZU51bWJlcn1gO1xuICAgIE9iamVjdC5rZXlzKHRoaXMuX3N0YXRlQnlJbnN0YW5jZUluZGV4KVxuICAgICAgLmZpbHRlcihrZXkgPT4ga2V5LnN0YXJ0c1dpdGgoYCR7aW5zdGFuY2VUZW1wbGF0ZX06YCkpLmZvckVhY2goa2V5ID0+IHtcbiAgICAgICAgZGVsZXRlIHRoaXMuX3N0YXRlQnlJbnN0YW5jZUluZGV4W2tleV07XG4gICAgICB9KTtcbiAgfVxuXG4gIF9nZXRTdGF0ZShpbnN0YW5jZUluZGV4KSB7XG4gICAgaWYgKCF0aGlzLl9zdGF0ZUJ5SW5zdGFuY2VJbmRleFtpbnN0YW5jZUluZGV4XSkge1xuICAgICAgdGhpcy5fc3RhdGVCeUluc3RhbmNlSW5kZXhbaW5zdGFuY2VJbmRleF0gPSB7XG4gICAgICAgIGluc3RhbmNlSW5kZXgsXG4gICAgICAgIHN5bmNocm9uaXplZDogZmFsc2UsXG4gICAgICB9O1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fc3RhdGVCeUluc3RhbmNlSW5kZXhbaW5zdGFuY2VJbmRleF07XG4gIH1cblxufVxuIl0sIm5hbWVzIjpbIkxvZ2dlck1hbmFnZXIiLCJNZXRhQXBpQ29ubmVjdGlvbiIsIlRpbWVvdXRFcnJvciIsIlJwY01ldGFBcGlDb25uZWN0aW9uIiwiY29ubmVjdCIsImluc3RhbmNlSWQiLCJfb3BlbmVkSW5zdGFuY2VzIiwiaW5jbHVkZXMiLCJwdXNoIiwiX29wZW5lZCIsImFjY291bnRSZWdpb25zIiwiX2FjY291bnQiLCJfd2Vic29ja2V0Q2xpZW50IiwiYWRkQWNjb3VudENhY2hlIiwiaWQiLCJPYmplY3QiLCJrZXlzIiwiZm9yRWFjaCIsInJlZ2lvbiIsIl9vcHRpb25zIiwiZW5zdXJlU3Vic2NyaWJlIiwiY2xvc2UiLCJmaWx0ZXIiLCJsZW5ndGgiLCJfY2xvc2VkIiwiX2Nvbm5lY3Rpb25SZWdpc3RyeSIsInJlbW92ZVJwYyIsImFjY291bnQiLCJyZW1vdmVTeW5jaHJvbml6YXRpb25MaXN0ZW5lciIsInJlbW92ZUFjY291bnRDYWNoZSIsInJlbW92ZVJlY29ubmVjdExpc3RlbmVyIiwib25Db25uZWN0ZWQiLCJpbnN0YW5jZUluZGV4IiwicmVwbGljYXMiLCJzdGF0ZSIsIl9nZXRTdGF0ZSIsInN5bmNocm9uaXplZCIsImdldFJlZ2lvbiIsImNhbmNlbFJlZnJlc2giLCJvbkRpc2Nvbm5lY3RlZCIsIl9sb2dnZXIiLCJkZWJ1ZyIsIm9uU3RyZWFtQ2xvc2VkIiwiX3N0YXRlQnlJbnN0YW5jZUluZGV4IiwiaXNTeW5jaHJvbml6ZWQiLCJ2YWx1ZXMiLCJtYXAiLCJpbnN0YW5jZSIsIndhaXRTeW5jaHJvbml6ZWQiLCJ0aW1lb3V0SW5TZWNvbmRzIiwiX2NoZWNrSXNDb25uZWN0aW9uQWN0aXZlIiwic3RhcnRUaW1lIiwiRGF0ZSIsIm5vdyIsIlByb21pc2UiLCJyZXMiLCJzZXRUaW1lb3V0IiwidW5kZWZpbmVkIiwiZXJyIiwib25SZWNvbm5lY3RlZCIsImluc3RhbmNlTnVtYmVyIiwiaW5zdGFuY2VUZW1wbGF0ZSIsImtleSIsInN0YXJ0c1dpdGgiLCJjb25zdHJ1Y3RvciIsIm9wdGlvbnMiLCJ3ZWJzb2NrZXRDbGllbnQiLCJjb25uZWN0aW9uUmVnaXN0cnkiLCJhZGRTeW5jaHJvbml6YXRpb25MaXN0ZW5lciIsInJlcGxpY2FJZCIsImFkZFJlY29ubmVjdExpc3RlbmVyIiwiZ2V0TG9nZ2VyIl0sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUVBLE9BQU9BLG1CQUFtQixZQUFZO0FBQ3RDLE9BQU9DLHVCQUF1QixzQkFBc0I7QUFDcEQsT0FBT0Msa0JBQWtCLDBCQUEwQjtBQUtwQyxJQUFBLEFBQU1DLHVCQUFOLE1BQU1BLDZCQUE2QkY7SUFzQmhEOzs7O0dBSUMsR0FDRCxBQUFNRyxRQUFRQyxVQUFVOztlQUF4QixvQkFBQTtZQUNFLElBQUksQ0FBQyxNQUFLQyxnQkFBZ0IsQ0FBQ0MsUUFBUSxDQUFDRixhQUFhO2dCQUMvQyxNQUFLQyxnQkFBZ0IsQ0FBQ0UsSUFBSSxDQUFDSDtZQUM3QjtZQUNBLElBQUksQ0FBQyxNQUFLSSxPQUFPLEVBQUU7Z0JBQ2pCLE1BQUtBLE9BQU8sR0FBRztnQkFDZixNQUFNQyxpQkFBaUIsTUFBS0MsUUFBUSxDQUFDRCxjQUFjO2dCQUNuRCxNQUFLRSxnQkFBZ0IsQ0FBQ0MsZUFB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