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)
201 lines (200 loc) • 26.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "default", {
enumerable: true,
get: function() {
return MetaApiConnection;
}
});
const _synchronizationListener = /*#__PURE__*/ _interop_require_default(require("../clients/metaApi/synchronizationListener"));
const _logger = /*#__PURE__*/ _interop_require_default(require("../logger"));
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 MetaApiConnection = class MetaApiConnection extends _synchronizationListener.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) {}
/**
* 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) {}
/**
* Returns MetaApi account
* @return {MetatraderAccount} MetaApi account
*/ get account() {
return this._account;
}
/**
* Returns connection application
* @return {String} connection application
*/ get application() {
return this._application;
}
/**
* Schedules the refresh task
* @param {string} region replica region
*/ scheduleRefresh(region) {
if (!this._refreshTasks[region]) {
var _this__options_connections;
var _this__options_connections_refreshReplicasMaxDelayInMs;
const delay = Math.random() * ((_this__options_connections_refreshReplicasMaxDelayInMs = (_this__options_connections = this._options.connections) === null || _this__options_connections === void 0 ? void 0 : _this__options_connections.refreshReplicasMaxDelayInMs) !== null && _this__options_connections_refreshReplicasMaxDelayInMs !== void 0 ? _this__options_connections_refreshReplicasMaxDelayInMs : 6 * 60 * 60 * 1000);
this._refreshTasks[region] = setTimeout(this._refreshReplicas, delay);
}
}
/**
* Cancels the scheduled refresh task
* @param {string} region replica region
*/ cancelRefresh(region) {
clearTimeout(this._refreshTasks[region]);
delete this._refreshTasks[region];
}
/**
* Refreshes account replicas
*/ async _refreshReplicas() {
Object.values(this._refreshTasks).forEach((task)=>clearTimeout(task));
this._refreshTasks = {};
const oldReplicas = {};
this._account.replicas.forEach((replica)=>oldReplicas[replica.region] = replica.id);
const newReplicas = {};
let isAccountUpdated = false;
try {
await this._account.reload();
isAccountUpdated = true;
this._account.replicas.forEach((replica)=>newReplicas[replica.region] = replica.id);
} catch (error) {
if (error.name === "NotFoundError") {
if (this._connectionRegistry) {
this._connectionRegistry.closeAllInstances(this._account.id);
}
}
}
if (isAccountUpdated) {
const deletedReplicas = {};
const addedReplicas = {};
Object.keys(oldReplicas).forEach((key)=>{
if (newReplicas[key] !== oldReplicas[key]) {
deletedReplicas[key] = oldReplicas[key];
}
});
Object.keys(newReplicas).forEach((key)=>{
if (newReplicas[key] !== oldReplicas[key]) {
addedReplicas[key] = newReplicas[key];
}
});
if (Object.keys(deletedReplicas).length) {
Object.values(deletedReplicas).forEach((replicaId)=>this._websocketClient.onAccountDeleted(replicaId));
}
if (Object.keys(deletedReplicas).length || Object.keys(addedReplicas).length) {
newReplicas[this._account.region] = this._account.id;
this._websocketClient.updateAccountCache(this._account.id, newReplicas);
Object.entries(this._account.accountRegions).forEach(([region, instance])=>{
if (!this._options.region || this._options.region === region) {
this._websocketClient.ensureSubscribe(instance, 0);
this._websocketClient.ensureSubscribe(instance, 1);
}
});
}
}
}
async _ensureSynchronized(instanceIndex, key) {
let state = this._getState(instanceIndex);
if (state && !this._closed) {
try {
const synchronizationResult = await this.synchronize(instanceIndex);
if (synchronizationResult) {
state.synchronized = true;
state.synchronizationRetryIntervalInSeconds = 1;
}
} catch (err) {
const level = this._latencyService.getSynchronizedAccountInstances(this._account.id).length ? "debug" : "error";
this._logger[level]("MetaApi websocket client for account " + this._account.id + ":" + instanceIndex + " failed to synchronize", err);
if (state.shouldSynchronize === key) {
setTimeout(this._ensureSynchronized.bind(this, instanceIndex, key), state.synchronizationRetryIntervalInSeconds * 1000);
state.synchronizationRetryIntervalInSeconds = Math.min(state.synchronizationRetryIntervalInSeconds * 2, 300);
}
}
}
}
synchronize(instanceIndex) {
return undefined;
}
_getState(instanceIndex) {
if (!this._stateByInstanceIndex["" + instanceIndex]) {
this._stateByInstanceIndex["" + instanceIndex] = {
instanceIndex,
ordersSynchronized: {},
dealsSynchronized: {},
shouldSynchronize: undefined,
synchronizationRetryIntervalInSeconds: 1,
synchronized: false,
lastDisconnectedSynchronizationId: undefined,
lastSynchronizationId: undefined,
disconnected: false
};
}
return this._stateByInstanceIndex["" + instanceIndex];
}
_checkIsConnectionActive() {
if (!this._opened) {
throw new Error("This connection has not been initialized yet, please invoke await connection.connect()");
}
if (this._closed) {
throw new Error("This connection has been closed, please create a new connection");
}
}
/**
* @typedef Config MetaApi options for connections
* @property {Options} [connections] MetaApi connections options. Only for tests. Will be ignored when set in SDK
*/ /**
* @typedef Options MetaApiConnection options
* @property {number} [refreshReplicasMaxDelayInMs = 6 * 60 * 60 * 1000] max delay before refreshing replicas delay
*/ /**
* Constructs MetaApi MetaTrader Api connection
* @param {MetaApiOpts & Config} options MetaApi options
* @param {MetaApiWebsocketClient} websocketClient MetaApi websocket client
* @param {MetatraderAccount} account MetaTrader account id to connect to
* @param {String} [application] application to use
*/ constructor(options, websocketClient, account, application){
super();
_define_property(this, "_options", void 0);
_define_property(this, "_websocketClient", void 0);
_define_property(this, "_latencyService", void 0);
_define_property(this, "_account", void 0);
_define_property(this, "_logger", void 0);
_define_property(this, "_application", void 0);
_define_property(this, "_refreshTasks", void 0);
_define_property(this, "_connectionRegistry", void 0);
_define_property(this, "_closed", void 0);
_define_property(this, "_stateByInstanceIndex", void 0);
_define_property(this, "_opened", void 0);
this._options = options;
this._websocketClient = websocketClient;
this._latencyService = websocketClient.latencyService;
this._account = account;
this._logger = _logger.default.getLogger("MetaApiConnection");
this._application = application;
this._refreshReplicas = this._refreshReplicas.bind(this);
this._refreshTasks = {};
}
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCBNZXRhQXBpV2Vic29ja2V0Q2xpZW50IGZyb20gJy4uL2NsaWVudHMvbWV0YUFwaS9tZXRhQXBpV2Vic29ja2V0LmNsaWVudCc7XG5pbXBvcnQgU3luY2hyb25pemF0aW9uTGlzdGVuZXIgZnJvbSAnLi4vY2xpZW50cy9tZXRhQXBpL3N5bmNocm9uaXphdGlvbkxpc3RlbmVyJztcbmltcG9ydCBMb2dnZXJNYW5hZ2VyLCB7TG9nZ2VyfSBmcm9tICcuLi9sb2dnZXInO1xuaW1wb3J0IE1ldGF0cmFkZXJBY2NvdW50IGZyb20gJy4vbWV0YXRyYWRlckFjY291bnQnO1xuXG4vKipcbiAqIEV4cG9zZXMgTWV0YUFwaSBNZXRhVHJhZGVyIEFQSSBjb25uZWN0aW9uIHRvIGNvbnN1bWVyc1xuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBNZXRhQXBpQ29ubmVjdGlvbiBleHRlbmRzIFN5bmNocm9uaXphdGlvbkxpc3RlbmVyIHtcbiAgXG4gIHByb3RlY3RlZCBfb3B0aW9uczogYW55O1xuICBwcm90ZWN0ZWQgX3dlYnNvY2tldENsaWVudDogTWV0YUFwaVdlYnNvY2tldENsaWVudDtcbiAgcHJvdGVjdGVkIF9sYXRlbmN5U2VydmljZTogYW55O1xuICBwcm90ZWN0ZWQgX2FjY291bnQ6IE1ldGF0cmFkZXJBY2NvdW50O1xuICBwcm90ZWN0ZWQgX2xvZ2dlcjogTG9nZ2VyO1xuICBwcm90ZWN0ZWQgX2FwcGxpY2F0aW9uOiBhbnk7XG4gIHByb3RlY3RlZCBfcmVmcmVzaFRhc2tzOiB7fTtcbiAgcHJvdGVjdGVkIF9jb25uZWN0aW9uUmVnaXN0cnk6IGFueTtcbiAgcHJvdGVjdGVkIF9jbG9zZWQ6IGFueTtcbiAgcHJvdGVjdGVkIF9zdGF0ZUJ5SW5zdGFuY2VJbmRleDogYW55O1xuICBwcm90ZWN0ZWQgX29wZW5lZDogYW55O1xuXG4gIC8qKlxuICAgKiBAdHlwZWRlZiBDb25maWcgTWV0YUFwaSBvcHRpb25zIGZvciBjb25uZWN0aW9uc1xuICAgKiBAcHJvcGVydHkge09wdGlvbnN9IFtjb25uZWN0aW9uc10gTWV0YUFwaSBjb25uZWN0aW9ucyBvcHRpb25zLiBPbmx5IGZvciB0ZXN0cy4gV2lsbCBiZSBpZ25vcmVkIHdoZW4gc2V0IGluIFNES1xuICAgKi9cblxuICAvKipcbiAgICogQHR5cGVkZWYgT3B0aW9ucyBNZXRhQXBpQ29ubmVjdGlvbiBvcHRpb25zXG4gICAqIEBwcm9wZXJ0eSB7bnVtYmVyfSBbcmVmcmVzaFJlcGxpY2FzTWF4RGVsYXlJbk1zID0gNiAqIDYwICogNjAgKiAxMDAwXSBtYXggZGVsYXkgYmVmb3JlIHJlZnJlc2hpbmcgcmVwbGljYXMgZGVsYXlcbiAgICovXG5cbiAgLyoqXG4gICAqIENvbnN0cnVjdHMgTWV0YUFwaSBNZXRhVHJhZGVyIEFwaSBjb25uZWN0aW9uXG4gICAqIEBwYXJhbSB7TWV0YUFwaU9wdHMgJiBDb25maWd9IG9wdGlvbnMgTWV0YUFwaSBvcHRpb25zXG4gICAqIEBwYXJhbSB7TWV0YUFwaVdlYnNvY2tldENsaWVudH0gd2Vic29ja2V0Q2xpZW50IE1ldGFBcGkgd2Vic29ja2V0IGNsaWVudFxuICAgKiBAcGFyYW0ge01ldGF0cmFkZXJBY2NvdW50fSBhY2NvdW50IE1ldGFUcmFkZXIgYWNjb3VudCBpZCB0byBjb25uZWN0IHRvXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBbYXBwbGljYXRpb25dIGFwcGxpY2F0aW9uIHRvIHVzZVxuICAgKi9cbiAgY29uc3RydWN0b3Iob3B0aW9ucywgd2Vic29ja2V0Q2xpZW50LCBhY2NvdW50LCBhcHBsaWNhdGlvbj8pIHtcbiAgICBzdXBlcigpO1xuICAgIHRoaXMuX29wdGlvbnMgPSBvcHRpb25zO1xuICAgIHRoaXMuX3dlYnNvY2tldENsaWVudCA9IHdlYnNvY2tldENsaWVudDtcbiAgICB0aGlzLl9sYXRlbmN5U2VydmljZSA9IHdlYnNvY2tldENsaWVudC5sYXRlbmN5U2VydmljZTtcbiAgICB0aGlzLl9hY2NvdW50ID0gYWNjb3VudDtcbiAgICB0aGlzLl9sb2dnZXIgPSBMb2dnZXJNYW5hZ2VyLmdldExvZ2dlcignTWV0YUFwaUNvbm5lY3Rpb24nKTtcbiAgICB0aGlzLl9hcHBsaWNhdGlvbiA9IGFwcGxpY2F0aW9uO1xuICAgIHRoaXMuX3JlZnJlc2hSZXBsaWNhcyA9IHRoaXMuX3JlZnJlc2hSZXBsaWNhcy5iaW5kKHRoaXMpO1xuICAgIHRoaXMuX3JlZnJlc2hUYXNrcyA9IHt9O1xuICB9XG5cbiAgLyoqXG4gICAqIE9wZW5zIHRoZSBjb25uZWN0aW9uLiBDYW4gb25seSBiZSBjYWxsZWQgdGhlIGZpcnN0IHRpbWUsIG5leHQgY2FsbHMgd2lsbCBiZSBpZ25vcmVkLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gaW5zdGFuY2VJZCBjb25uZWN0aW9uIGluc3RhbmNlIGlkXG4gICAqIEByZXR1cm4ge1Byb21pc2V9IHByb21pc2UgcmVzb2x2aW5nIHdoZW4gdGhlIGNvbm5lY3Rpb24gaXMgb3BlbmVkXG4gICAqL1xuICBhc3luYyBjb25uZWN0KGluc3RhbmNlSWQpIHt9XG5cbiAgLyoqXG4gICAqIENsb3NlcyB0aGUgY29ubmVjdGlvbi4gVGhlIGluc3RhbmNlIG9mIHRoZSBjbGFzcyBzaG91bGQgbm8gbG9uZ2VyIGJlIHVzZWQgYWZ0ZXIgdGhpcyBtZXRob2QgaXMgaW52b2tlZC5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGluc3RhbmNlSWQgY29ubmVjdGlvbiBpbnN0YW5jZSBpZFxuICAgKi9cbiAgYXN5bmMgY2xvc2UoaW5zdGFuY2VJZCkge31cblxuICAvKipcbiAgICogUmV0dXJucyBNZXRhQXBpIGFjY291bnRcbiAgICogQHJldHVybiB7TWV0YXRyYWRlckFjY291bnR9IE1ldGFBcGkgYWNjb3VudFxuICAgKi9cbiAgZ2V0IGFjY291bnQoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2FjY291bnQ7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBjb25uZWN0aW9uIGFwcGxpY2F0aW9uXG4gICAqIEByZXR1cm4ge1N0cmluZ30gY29ubmVjdGlvbiBhcHBsaWNhdGlvblxuICAgKi9cbiAgZ2V0IGFwcGxpY2F0aW9uKCkge1xuICAgIHJldHVybiB0aGlzLl9hcHBsaWNhdGlvbjtcbiAgfVxuXG4gIC8qKlxuICAgKiBTY2hlZHVsZXMgdGhlIHJlZnJlc2ggdGFza1xuICAgKiBAcGFyYW0ge3N0cmluZ30gcmVnaW9uIHJlcGxpY2EgcmVnaW9uXG4gICAqL1xuICBzY2hlZHVsZVJlZnJlc2gocmVnaW9uKSB7XG4gICAgaWYgKCF0aGlzLl9yZWZyZXNoVGFza3NbcmVnaW9uXSkge1xuICAgICAgY29uc3QgZGVsYXkgPSBNYXRoLnJhbmRvbSgpICogKHRoaXMuX29wdGlvbnMuY29ubmVjdGlvbnM/LnJlZnJlc2hSZXBsaWNhc01heERlbGF5SW5NcyA/PyA2ICogNjAgKiA2MCAqIDEwMDApO1xuICAgICAgdGhpcy5fcmVmcmVzaFRhc2tzW3JlZ2lvbl0gPSBzZXRUaW1lb3V0KHRoaXMuX3JlZnJlc2hSZXBsaWNhcywgZGVsYXkpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBDYW5jZWxzIHRoZSBzY2hlZHVsZWQgcmVmcmVzaCB0YXNrXG4gICAqIEBwYXJhbSB7c3RyaW5nfSByZWdpb24gcmVwbGljYSByZWdpb25cbiAgICovXG4gIGNhbmNlbFJlZnJlc2gocmVnaW9uKSB7XG4gICAgY2xlYXJUaW1lb3V0KHRoaXMuX3JlZnJlc2hUYXNrc1tyZWdpb25dKTtcbiAgICBkZWxldGUgdGhpcy5fcmVmcmVzaFRhc2tzW3JlZ2lvbl07XG4gIH1cblxuICAvKipcbiAgICogUmVmcmVzaGVzIGFjY291bnQgcmVwbGljYXNcbiAgICovXG4gIGFzeW5jIF9yZWZyZXNoUmVwbGljYXMoKSB7XG4gICAgT2JqZWN0LnZhbHVlczxhbnk+KHRoaXMuX3JlZnJlc2hUYXNrcykuZm9yRWFjaCh0YXNrID0+IGNsZWFyVGltZW91dCh0YXNrKSk7XG4gICAgdGhpcy5fcmVmcmVzaFRhc2tzID0ge307XG4gICAgY29uc3Qgb2xkUmVwbGljYXM6IE1ldGF0cmFkZXJBY2NvdW50LkFjY291bnRzQnlSZWdpb24gPSB7fTtcbiAgICB0aGlzLl9hY2NvdW50LnJlcGxpY2FzLmZvckVhY2gocmVwbGljYSA9PiBvbGRSZXBsaWNhc1tyZXBsaWNhLnJlZ2lvbl0gPSByZXBsaWNhLmlkKTtcbiAgICBjb25zdCBuZXdSZXBsaWNhczogTWV0YXRyYWRlckFjY291bnQuQWNjb3VudHNCeVJlZ2lvbiA9IHt9O1xuICAgIGxldCBpc0FjY291bnRVcGRhdGVkID0gZmFsc2U7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHRoaXMuX2FjY291bnQucmVsb2FkKCk7XG4gICAgICBpc0FjY291bnRVcGRhdGVkID0gdHJ1ZTtcbiAgICAgIHRoaXMuX2FjY291bnQucmVwbGljYXMuZm9yRWFjaChyZXBsaWNhID0+IG5ld1JlcGxpY2FzW3JlcGxpY2EucmVnaW9uXSA9IHJlcGxpY2EuaWQpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBpZiAoZXJyb3IubmFtZSA9PT0gJ05vdEZvdW5kRXJyb3InKSB7XG4gICAgICAgIGlmICh0aGlzLl9jb25uZWN0aW9uUmVnaXN0cnkpIHtcbiAgICAgICAgICB0aGlzLl9jb25uZWN0aW9uUmVnaXN0cnkuY2xvc2VBbGxJbnN0YW5jZXModGhpcy5fYWNjb3VudC5pZCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKGlzQWNjb3VudFVwZGF0ZWQpIHtcbiAgICAgIGNvbnN0IGRlbGV0ZWRSZXBsaWNhcyA9IHt9O1xuICAgICAgY29uc3QgYWRkZWRSZXBsaWNhcyA9IHt9O1xuICAgICAgT2JqZWN0LmtleXMob2xkUmVwbGljYXMpLmZvckVhY2goa2V5ID0+IHtcbiAgICAgICAgaWYgKG5ld1JlcGxpY2FzW2tleV0gIT09IG9sZFJlcGxpY2FzW2tleV0pIHtcbiAgICAgICAgICBkZWxldGVkUmVwbGljYXNba2V5XSA9IG9sZFJlcGxpY2FzW2tleV07XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgT2JqZWN0LmtleXMobmV3UmVwbGljYXMpLmZvckVhY2goa2V5ID0+IHtcbiAgICAgICAgaWYgKG5ld1JlcGxpY2FzW2tleV0gIT09IG9sZFJlcGxpY2FzW2tleV0pIHtcbiAgICAgICAgICBhZGRlZFJlcGxpY2FzW2tleV0gPSBuZXdSZXBsaWNhc1trZXldO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIGlmIChPYmplY3Qua2V5cyhkZWxldGVkUmVwbGljYXMpLmxlbmd0aCkge1xuICAgICAgICBPYmplY3QudmFsdWVzKGRlbGV0ZWRSZXBsaWNhcykuZm9yRWFjaChyZXBsaWNhSWQgPT4gXG4gICAgICAgICAgdGhpcy5fd2Vic29ja2V0Q2xpZW50Lm9uQWNjb3VudERlbGV0ZWQocmVwbGljYUlkKSk7XG4gICAgICB9XG4gICAgICBpZiAoT2JqZWN0LmtleXMoZGVsZXRlZFJlcGxpY2FzKS5sZW5ndGggfHwgT2JqZWN0LmtleXMoYWRkZWRSZXBsaWNhcykubGVuZ3RoKSB7XG4gICAgICAgIG5ld1JlcGxpY2FzW3RoaXMuX2FjY291bnQucmVnaW9uXSA9IHRoaXMuX2FjY291bnQuaWQ7XG4gICAgICAgIHRoaXMuX3dlYnNvY2tldENsaWVudC51cGRhdGVBY2NvdW50Q2FjaGUodGhpcy5fYWNjb3VudC5pZCwgbmV3UmVwbGljYXMpO1xuICAgICAgICBPYmplY3QuZW50cmllcyh0aGlzLl9hY2NvdW50LmFjY291bnRSZWdpb25zKS5mb3JFYWNoKChbcmVnaW9uLCBpbnN0YW5jZV0pID0+IHtcbiAgICAgICAgICBpZiAoIXRoaXMuX29wdGlvbnMucmVnaW9uIHx8IHRoaXMuX29wdGlvbnMucmVnaW9uID09PSByZWdpb24pIHtcbiAgICAgICAgICAgIHRoaXMuX3dlYnNvY2tldENsaWVudC5lbnN1cmVTdWJzY3JpYmUoaW5zdGFuY2UsIDApO1xuICAgICAgICAgICAgdGhpcy5fd2Vic29ja2V0Q2xpZW50LmVuc3VyZVN1YnNjcmliZShpbnN0YW5jZSwgMSk7XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBhc3luYyBfZW5zdXJlU3luY2hyb25pemVkKGluc3RhbmNlSW5kZXgsIGtleSkge1xuICAgIGxldCBzdGF0ZSA9IHRoaXMuX2dldFN0YXRlKGluc3RhbmNlSW5kZXgpO1xuICAgIGlmIChzdGF0ZSAmJiAhdGhpcy5fY2xvc2VkKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBzeW5jaHJvbml6YXRpb25SZXN1bHQgPSBhd2FpdCB0aGlzLnN5bmNocm9uaXplKGluc3RhbmNlSW5kZXgpO1xuICAgICAgICBpZiAoc3luY2hyb25pemF0aW9uUmVzdWx0KSB7XG4gICAgICAgICAgc3RhdGUuc3luY2hyb25pemVkID0gdHJ1ZTtcbiAgICAgICAgICBzdGF0ZS5zeW5jaHJvbml6YXRpb25SZXRyeUludGVydmFsSW5TZWNvbmRzID0gMTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGNvbnN0IGxldmVsID0gdGhpcy5fbGF0ZW5jeVNlcnZpY2UuZ2V0U3luY2hyb25pemVkQWNjb3VudEluc3RhbmNlcyh0aGlzLl9hY2NvdW50LmlkKS5sZW5ndGggPyAnZGVidWcnIDogJ2Vycm9yJztcbiAgICAgICAgdGhpcy5fbG9nZ2VyW2xldmVsXSgnTWV0YUFwaSB3ZWJzb2NrZXQgY2xpZW50IGZvciBhY2NvdW50ICcgKyB0aGlzLl9hY2NvdW50LmlkICtcbiAgICAgICAgICAnOicgKyBpbnN0YW5jZUluZGV4ICsgJyBmYWlsZWQgdG8gc3luY2hyb25pemUnLCBlcnIpO1xuICAgICAgICBpZiAoc3RhdGUuc2hvdWxkU3luY2hyb25pemUgPT09IGtleSkge1xuICAgICAgICAgIHNldFRpbWVvdXQodGhpcy5fZW5zdXJlU3luY2hyb25pemVkLmJpbmQodGhpcywgaW5zdGFuY2VJbmRleCwga2V5KSxcbiAgICAgICAgICAgIHN0YXRlLnN5bmNocm9uaXphdGlvblJldHJ5SW50ZXJ2YWxJblNlY29uZHMgKiAxMDAwKTtcbiAgICAgICAgICBzdGF0ZS5zeW5jaHJvbml6YXRpb25SZXRyeUludGVydmFsSW5TZWNvbmRzID0gTWF0aC5taW4oc3RhdGUuc3luY2hyb25pemF0aW9uUmV0cnlJbnRlcnZhbEluU2Vjb25kcyAqIDIsIDMwMCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgXG4gIHN5bmNocm9uaXplKGluc3RhbmNlSW5kZXg6IGFueSkge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cblxuICBfZ2V0U3RhdGUoaW5zdGFuY2VJbmRleCkge1xuICAgIGlmICghdGhpcy5fc3RhdGVCeUluc3RhbmNlSW5kZXhbJycgKyBpbnN0YW5jZUluZGV4XSkge1xuICAgICAgdGhpcy5fc3RhdGVCeUluc3RhbmNlSW5kZXhbJycgKyBpbnN0YW5jZUluZGV4XSA9IHtcbiAgICAgICAgaW5zdGFuY2VJbmRleCxcbiAgICAgICAgb3JkZXJzU3luY2hyb25pemVkOiB7fSxcbiAgICAgICAgZGVhbHNTeW5jaHJvbml6ZWQ6IHt9LFxuICAgICAgICBzaG91bGRTeW5jaHJvbml6ZTogdW5kZWZpbmVkLFxuICAgICAgICBzeW5jaHJvbml6YXRpb25SZXRyeUludGVydmFsSW5TZWNvbmRzOiAxLFxuICAgICAgICBzeW5jaHJvbml6ZWQ6IGZhbHNlLFxuICAgICAgICBsYXN0RGlzY29ubmVjdGVkU3luY2hyb25pemF0aW9uSWQ6IHVuZGVmaW5lZCxcbiAgICAgICAgbGFzdFN5bmNocm9uaXphdGlvbklkOiB1bmRlZmluZWQsXG4gICAgICAgIGRpc2Nvbm5lY3RlZDogZmFsc2VcbiAgICAgIH07XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9zdGF0ZUJ5SW5zdGFuY2VJbmRleFsnJyArIGluc3RhbmNlSW5kZXhdO1xuICB9XG5cbiAgX2NoZWNrSXNDb25uZWN0aW9uQWN0aXZlKCkge1xuICAgIGlmICghdGhpcy5fb3BlbmVkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1RoaXMgY29ubmVjdGlvbiBoYXMgbm90IGJlZW4gaW5pdGlhbGl6ZWQgeWV0LCBwbGVhc2UgaW52b2tlIGF3YWl0IGNvbm5lY3Rpb24uY29ubmVjdCgpJyk7XG4gICAgfVxuICAgIGlmICh0aGlzLl9jbG9zZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignVGhpcyBjb25uZWN0aW9uIGhhcyBiZWVuIGNsb3NlZCwgcGxlYXNlIGNyZWF0ZSBhIG5ldyBjb25uZWN0aW9uJyk7XG4gICAgfVxuICB9XG5cbn1cbiJdLCJuYW1lcyI6WyJNZXRhQXBpQ29ubmVjdGlvbiIsIlN5bmNocm9uaXphdGlvbkxpc3RlbmVyIiwiY29ubmVjdCIsImluc3RhbmNlSWQiLCJjbG9zZSIsImFjY291bnQiLCJfYWNjb3VudCIsImFwcGxpY2F0aW9uIiwiX2FwcGxpY2F0aW9uIiwic2NoZWR1bGVSZWZyZXNoIiwicmVnaW9uIiwiX3JlZnJlc2hUYXNrcyIsImRlbGF5IiwiTWF0aCIsInJhbmRvbSIsIl9vcHRpb25zIiwiY29ubmVjdGlvbnMiLCJyZWZyZXNoUmVwbGljYXNNYXhEZWxheUluTXMiLCJzZXRUaW1lb3V0IiwiX3JlZnJlc2hSZXBsaWNhcyIsImNhbmNlbFJlZnJlc2giLCJjbGVhclRpbWVvdXQiLCJPYmplY3QiLCJ2YWx1ZXMiLCJmb3JFYWNoIiwidGFzayIsIm9sZFJlcGxpY2FzIiwicmVwbGljYXMiLCJyZXBsaWNhIiwiaWQiLCJuZXdSZXBsaWNhcyIsImlzQWNjb3VudFVwZGF0ZWQiLCJyZWxvYWQiLCJlcnJvciIsIm5hbWUiLCJfY29ubmVjdGlvblJlZ2lzdHJ5IiwiY2xvc2VBbGxJbnN0YW5jZXMiLCJkZWxldGVkUmVwbGljYXMiLCJhZGRlZFJlcGxpY2FzIiwia2V5cyIsImtleSIsImxlbmd0aCIsInJlcGxpY2FJZCIsIl93ZWJzb2NrZXRDbGllbnQiLCJvbkFjY291bnREZWxldGVkIiwidXBkYXRlQWNjb3VudENhY2hlIiwiZW50cmllcyIsImFjY291bnRSZWdpb25zIiwiaW5zdGFuY2UiLCJlbnN1cmVTdWJzY3JpYmUiLCJfZW5zdXJlU3luY2hyb25pemVkIiwiaW5zdGFuY2VJbmRleCIsInN0YXRlIiwiX2dldFN0YXRlIiwiX2Nsb3NlZCIsInN5bmNocm9uaXphdGlvblJlc3VsdCIsInN5bmNocm9uaXplIiwic3luY2hyb25pemVkIiwic3luY2hyb25pemF0aW9uUmV0cnlJbnRlcnZhbEluU2Vjb25kcyIsImVyciIsImxldmVsIiwiX2xhdGVuY3lTZXJ2aWNlIiwiZ2V0U3luY2hyb25pemVkQWNjb3VudEluc3RhbmNlcyIsIl9sb2dnZXIiLCJzaG91bGRTeW5jaHJvbml6ZSIsImJpbmQiLCJtaW4iLCJ1bmRlZmluZWQiLCJfc3RhdGVCeUluc3RhbmNlSW5kZXgiLCJvcmRlcnNTeW5jaHJvbml6ZWQiLCJkZWFsc1N5bmNocm9uaXplZCIsImxhc3REaXNjb25uZWN0ZWRTeW5jaHJvbml6YXRpb25JZCIsImxhc3RTeW5jaHJvbml6YXRpb25JZCIsImRpc2Nvbm5lY3RlZCIsIl9jaGVja0lzQ29ubmVjdGlvbkFjdGl2ZSIsIl9vcGVuZWQiLCJFcnJvciIsImNvbnN0cnVjdG9yIiwib3B0aW9ucyIsIndlYnNvY2tldENsaWVudCIsImxhdGVuY3lTZXJ2aWNlIiwiTG9nZ2VyTWFuYWdlciIsImdldExvZ2dlciJdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7ZUFVcUJBOzs7Z0ZBUGU7K0RBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFNckIsSUFBQSxBQUFNQSxvQkFBTixNQUFNQSwwQkFBMEJDLGdDQUF1QjtJQTJDcEU7Ozs7R0FJQyxHQUNELE1BQU1DLFFBQVFDLFVBQVUsRUFBRSxDQUFDO0lBRTNCOzs7R0FHQyxHQUNELE1BQU1DLE1BQU1ELFVBQVUsRUFBRSxDQUFDO0lBRXpCOzs7R0FHQyxHQUNELElBQUlFLFVBQVU7UUFDWixPQUFPLElBQUksQ0FBQ0MsUUFBUTtJQUN0QjtJQUVBOzs7R0FHQyxHQUNELElBQUlDLGNBQWM7UUFDaEIsT0FBTyxJQUFJLENBQUNDLFlBQVk7SUFDMUI7SUFFQTs7O0dBR0MsR0FDREMsZ0JBQWdCQyxNQUFNLEVBQUU7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