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)

647 lines (646 loc) 82.1 kB
'use strict'; 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 TimeoutError from '../clients/timeoutError'; const HistoryDatabase = require('./historyDatabase/index').default; import ExpertAdvisor from './expertAdvisor'; import { ValidationError } from '../clients/errorHandler'; import MetatraderAccountReplica from './metatraderAccountReplica'; /** * Implements a MetaTrader account entity */ let MetatraderAccount = class MetatraderAccount { /** * Returns unique account id * @return {string} unique account id */ get id() { return this._data._id; } /** * Returns current account state. One of CREATED, DEPLOYING, DEPLOYED, DEPLOY_FAILED, UNDEPLOYING, * UNDEPLOYED, UNDEPLOY_FAILED, DELETING, DELETE_FAILED, REDEPLOY_FAILED, DRAFT * @return {State} current account state */ get state() { return this._data.state; } /** * Returns MetaTrader magic to place trades using * @return {number} MetaTrader magic to place trades using */ get magic() { return this._data.magic; } /** * Returns terminal & broker connection status, one of CONNECTED, DISCONNECTED, DISCONNECTED_FROM_BROKER * @return {ConnectionStatus} terminal & broker connection status */ get connectionStatus() { return this._data.connectionStatus; } /** * Returns quote streaming interval in seconds * @return {number} quote streaming interval in seconds */ get quoteStreamingIntervalInSeconds() { return this._data.quoteStreamingIntervalInSeconds; } /** * Returns symbol provided by broker * @return {string} any symbol provided by broker */ get symbol() { return this._data.symbol; } /** * Returns reliability value. Possible values are regular and high * @return {Reliability} account reliability value */ get reliability() { return this._data.reliability; } /** * Returns user-defined account tags * @return {Array<string>} user-defined account tags */ get tags() { return this._data.tags; } /** * Returns extra information which can be stored together with your account * @return {Object} extra information which can be stored together with your account */ get metadata() { return this._data.metadata; } /** * Returns number of resource slots to allocate to account. Allocating extra resource slots * results in better account performance under load which is useful for some applications. E.g. if you have many * accounts copying the same strategy via CopyFactory API, then you can increase resourceSlots to get a lower trade * copying latency. Please note that allocating extra resource slots is a paid option. Please note that high * reliability accounts use redundant infrastructure, so that each resource slot for a high reliability account * is billed as 2 standard resource slots. * @return {number} number of resource slots to allocate to account */ get resourceSlots() { return this._data.resourceSlots; } /** * Returns the number of CopyFactory 2 resource slots to allocate to account. * Allocating extra resource slots results in lower trade copying latency. Please note that allocating extra resource * slots is a paid option. Please also note that CopyFactory 2 uses redundant infrastructure so that * each CopyFactory resource slot is billed as 2 standard resource slots. You will be billed for CopyFactory 2 * resource slots only if you have added your account to CopyFactory 2 by specifying copyFactoryRoles field. * @return {number} number of CopyFactory 2 resource slots to allocate to account */ get copyFactoryResourceSlots() { return this._data.copyFactoryResourceSlots; } /** * Returns account region * @return {string} account region value */ get region() { return this._data.region; } /** * Returns the time account was created at, in ISO format * @returns {string} the time account was created at, in ISO format */ get createdAt() { return new Date(this._data.createdAt); } /** * Returns human-readable account name * @return {string} human-readable account name */ get name() { return this._data.name; } /** * Returns flag indicating if trades should be placed as manual trades on this account * @return {boolean} flag indicating if trades should be placed as manual trades on this account */ get manualTrades() { return this._data.manualTrades; } /** * Returns default trade slippage in points * @return {number} default trade slippage in points */ get slippage() { return this._data.slippage; } /** * Returns id of the account's provisioning profile * @return {string} id of the account's provisioning profile */ get provisioningProfileId() { return this._data.provisioningProfileId; } /** * Returns MetaTrader account login * @return {string} MetaTrader account number */ get login() { return this._data.login; } /** * Returns MetaTrader server name to connect to * @return {string} MetaTrader server name to connect to */ get server() { return this._data.server; } /** * Returns account type. Possible values are cloud-g1, cloud-g2 * @return {Type} account type */ get type() { return this._data.type; } /** * Returns MT version. Possible values are 4 and 5 * @return {Version} MT version */ get version() { return this._data.version; } /** * Returns hash-code of the account * @return {number} hash-code of the account */ get hash() { return this._data.hash; } /** * Returns 3-character ISO currency code of the account base currency. The setting is to be used * for copy trading accounts which use national currencies only, such as some Brazilian brokers. You should not alter * this setting unless you understand what you are doing. * @return {string} 3-character ISO currency code of the account base currency */ get baseCurrency() { return this._data.baseCurrency; } /** * Returns account roles for CopyFactory2 application. Possible values are `PROVIDER` and `SUBSCRIBER` * @return {Array<CopyFactoryRoles>} account roles for CopyFactory2 application */ get copyFactoryRoles() { return this._data.copyFactoryRoles; } /** * Returns flag indicating that risk management API is enabled on account * @return {boolean} flag indicating that risk management API is enabled on account */ get riskManagementApiEnabled() { return this._data.riskManagementApiEnabled; } /** * Returns flag indicating that MetaStats API is enabled on account * @return {boolean} flag indicating that MetaStats API is enabled on account */ get metastatsApiEnabled() { return this._data.metastatsApiEnabled; } /** * Returns configured dedicated IP protocol to connect to the trading account terminal * @return {DedicatedIp} */ get allocateDedicatedIp() { return this._data.allocateDedicatedIp; } /** * Returns active account connections * @return {Array<AccountConnection>} active account connections */ get connections() { return this._data.connections; } /** * Returns flag indicating that account is primary * @return {boolean} flag indicating that account is primary */ get primaryReplica() { return this._data.primaryReplica; } /** * Returns user id * @return {string} user id */ get userId() { return this._data.userId; } /** * Returns primary account id * @return {string} primary account id */ get primaryAccountId() { return this._data.primaryAccountId; } /** * Returns account replicas from DTO * @return {MetatraderAccountReplica[]} account replicas from DTO */ get accountReplicas() { return this._data.accountReplicas; } /** * Returns account replica instances * @return {MetatraderAccountReplica[]} account replica instances */ get replicas() { return this._replicas; } /** * Returns a dictionary with account's available regions and replicas * @returns accounts by region */ get accountRegions() { const regions = { [this.region]: this.id }; this.replicas.forEach((replica)=>regions[replica.region] = replica.id); return regions; } /** * Reloads MetaTrader account from API * @return {Promise} promise resolving when MetaTrader account is updated */ reload() { var _this = this; return _async_to_generator(function*() { _this._data = yield _this._metatraderAccountClient.getAccount(_this.id); const updatedReplicaData = _this._data.accountReplicas || []; const regions = updatedReplicaData.map((replica)=>replica.region); const createdReplicaRegions = _this._replicas.map((replica)=>replica.region); _this._replicas = _this._replicas.filter((replica)=>regions.includes(replica.region)); _this._replicas.forEach((replica)=>{ const updatedData = updatedReplicaData.find((replicaData)=>replicaData.region === replica.region); replica.updateData(updatedData); }); updatedReplicaData.forEach((replica)=>{ if (!createdReplicaRegions.includes(replica.region)) { _this._replicas.push(new MetatraderAccountReplica(replica, _this, _this._metatraderAccountClient)); } }); })(); } /** * Removes a trading account and stops the API server serving the account. * The account state such as downloaded market data history will be removed as well when you remove the account. * @return {Promise} promise resolving when account is scheduled for deletion */ remove() { var _this = this; return _async_to_generator(function*() { _this._connectionRegistry.closeAllInstances(_this.id); yield _this._metatraderAccountClient.deleteAccount(_this.id); const fileManager = HistoryDatabase.getInstance(); yield fileManager.clear(_this.id, _this._application); if (_this.type !== 'self-hosted') { try { yield _this.reload(); } catch (err) { if (err.name !== 'NotFoundError') { throw err; } } } })(); } /** * Starts API server and trading terminal for trading account. * This request will be ignored if the account is already deployed. * @returns {Promise} promise resolving when account is scheduled for deployment */ deploy() { var _this = this; return _async_to_generator(function*() { yield _this._metatraderAccountClient.deployAccount(_this.id); yield _this.reload(); })(); } /** * Stops API server and trading terminal for trading account. * This request will be ignored if trading account is already undeployed * @returns {Promise} promise resolving when account is scheduled for undeployment */ undeploy() { var _this = this; return _async_to_generator(function*() { _this._connectionRegistry.closeAllInstances(_this.id); yield _this._metatraderAccountClient.undeployAccount(_this.id); yield _this.reload(); })(); } /** * Redeploys trading account. This is equivalent to undeploy immediately followed by deploy * @returns {Promise} promise resolving when account is scheduled for redeployment */ redeploy() { var _this = this; return _async_to_generator(function*() { yield _this._metatraderAccountClient.redeployAccount(_this.id); yield _this.reload(); })(); } /** * Increases trading account reliability in order to increase the expected account uptime. * The account will be temporary stopped to perform this action. * Note that increasing reliability is a paid option * @returns {Promise} promise resolving when account reliability is increased */ increaseReliability() { var _this = this; return _async_to_generator(function*() { yield _this._metatraderAccountClient.increaseReliability(_this.id); yield _this.reload(); })(); } /** * Enables risk management API for trading account. * The account will be temporary stopped to perform this action. * Note that risk management API is a paid option * @returns {Promise} promise resolving when account risk management is enabled */ enableRiskManagementApi() { var _this = this; return _async_to_generator(function*() { yield _this._metatraderAccountClient.enableRiskManagementApi(_this.id); yield _this.reload(); })(); } /** * Enables copy factory API for trading account. * The account will be temporary stopped to perform this action. * Note that copy factory API is a paid option (see * @param {Array<CopyFactoryRoles>} copyFactoryRoles copy factory roles * @param {number} copyFactoryResourceSlots copy factory resource slots * @return {Promise} promise resolving when account copy factory is enabled */ enableCopyFactoryApi(copyFactoryRoles, copyFactoryResourceSlots) { var _this = this; return _async_to_generator(function*() { yield _this._metatraderAccountClient.enableCopyFactoryApi(_this.id, copyFactoryRoles, copyFactoryResourceSlots); yield _this.reload(); })(); } /** * Enables MetaStats API for trading account. * The account will be temporary stopped to perform this action. * Note that this is a paid option * @returns {Promise} promise resolving when account MetaStats API is enabled */ enableMetaStatsApi() { var _this = this; return _async_to_generator(function*() { yield _this._metatraderAccountClient.enableMetaStatsApi(_this.id); yield _this.reload(); })(); } /** * Waits until API server has finished deployment and account reached the DEPLOYED state * @param {number} timeoutInSeconds wait timeout in seconds, default is 5m * @param {number} intervalInMilliseconds interval between account reloads while waiting for a change, default is 1s * @return {Promise} promise which resolves when account is deployed * @throws {TimeoutError} if account have not reached the DEPLOYED state within timeout allowed */ waitDeployed(timeoutInSeconds = 300, intervalInMilliseconds = 1000) { var _this = this; return _async_to_generator(function*() { let startTime = Date.now(); yield _this.reload(); while(_this.state !== 'DEPLOYED' && startTime + timeoutInSeconds * 1000 > Date.now()){ yield _this._delay(intervalInMilliseconds); yield _this.reload(); } if (_this.state !== 'DEPLOYED') { throw new TimeoutError('Timed out waiting for account ' + _this.id + ' to be deployed'); } })(); } /** * Waits until API server has finished undeployment and account reached the UNDEPLOYED state * @param {number} timeoutInSeconds wait timeout in seconds, default is 5m * @param {number} intervalInMilliseconds interval between account reloads while waiting for a change, default is 1s * @return {Promise} promise which resolves when account is deployed * @throws {TimeoutError} if account have not reached the UNDEPLOYED state within timeout allowed */ waitUndeployed(timeoutInSeconds = 300, intervalInMilliseconds = 1000) { var _this = this; return _async_to_generator(function*() { let startTime = Date.now(); yield _this.reload(); while(_this.state !== 'UNDEPLOYED' && startTime + timeoutInSeconds * 1000 > Date.now()){ yield _this._delay(intervalInMilliseconds); yield _this.reload(); } if (_this.state !== 'UNDEPLOYED') { throw new TimeoutError('Timed out waiting for account ' + _this.id + ' to be undeployed'); } })(); } /** * Waits until account has been deleted * @param {number} timeoutInSeconds wait timeout in seconds, default is 5m * @param {number} intervalInMilliseconds interval between account reloads while waiting for a change, default is 1s * @return {Promise} promise which resolves when account is deleted * @throws {TimeoutError} if account was not deleted within timeout allowed */ waitRemoved(timeoutInSeconds = 300, intervalInMilliseconds = 1000) { var _this = this; return _async_to_generator(function*() { let startTime = Date.now(); try { yield _this.reload(); while(startTime + timeoutInSeconds * 1000 > Date.now()){ yield _this._delay(intervalInMilliseconds); yield _this.reload(); } throw new TimeoutError('Timed out waiting for account ' + _this.id + ' to be deleted'); } catch (err) { if (err.name === 'NotFoundError') { return; } else { throw err; } } })(); } /** * Waits until API server has connected to the terminal and terminal has connected to the broker * @param {number} timeoutInSeconds wait timeout in seconds, default is 5m * @param {number} intervalInMilliseconds interval between account reloads while waiting for a change, default is 1s * @return {Promise} promise which resolves when API server is connected to the broker * @throws {TimeoutError} if account have not connected to the broker within timeout allowed */ waitConnected(timeoutInSeconds = 300, intervalInMilliseconds = 1000) { var _this = this; return _async_to_generator(function*() { const checkConnected = ()=>{ return [ _this.connectionStatus ].concat(_this.replicas.map((replica)=>replica.connectionStatus)).includes('CONNECTED'); }; let startTime = Date.now(); yield _this.reload(); while(!checkConnected() && startTime + timeoutInSeconds * 1000 > Date.now()){ yield _this._delay(intervalInMilliseconds); yield _this.reload(); } if (!checkConnected()) { throw new TimeoutError('Timed out waiting for account ' + _this.id + ' to connect to the broker'); } })(); } /** * Connects to MetaApi. There is only one connection per account. Subsequent calls to this method will return the same connection. * @param {HistoryStorage} historyStorage optional history storage * @param {Date} [historyStartTime] history start time. Used for tests * @return {StreamingMetaApiConnectionInstance} MetaApi connection instance */ getStreamingConnection(historyStorage, historyStartTime) { if (this._metaApiWebsocketClient.region && this._metaApiWebsocketClient.region !== this.region) { throw new ValidationError(`Account ${this.id} is not on specified region ${this._metaApiWebsocketClient.region}`); } return this._connectionRegistry.connectStreaming(this, historyStorage, historyStartTime); } /** * Connects to MetaApi via RPC connection instance. * @returns {RpcMetaApiConnectionInstance} MetaApi connection instance */ getRPCConnection() { if (this._metaApiWebsocketClient.region && this._metaApiWebsocketClient.region !== this.region) { throw new ValidationError(`Account ${this.id} is not on specified region ${this._metaApiWebsocketClient.region}`); } return this._connectionRegistry.connectRpc(this); } /** * Updates trading account. * Please redeploy the trading account in order for updated settings to take effect * @param {MetatraderAccountUpdateDto} account updated account information * @return {Promise} promise resolving when account is updated */ update(account) { var _this = this; return _async_to_generator(function*() { yield _this._metatraderAccountClient.updateAccount(_this.id, account); yield _this.reload(); })(); } /** * Creates a trading account replica in a region different from trading account region and starts a cloud API server for it * @param {NewMetaTraderAccountDto} account MetaTrader account data * @return {Promise<MetatraderAccountReplica>} promise resolving with created MetaTrader account replica entity */ createReplica(account) { var _this = this; return _async_to_generator(function*() { yield _this._metatraderAccountClient.createAccountReplica(_this.id, account); yield _this.reload(); return _this._replicas.find((r)=>r.region === account.region); })(); } /** * Retrieves expert advisor of current account * @returns {Promise<ExpertAdvisor[]>} promise resolving with an array of expert advisor entities */ getExpertAdvisors() { var _this = this; return _async_to_generator(function*() { _this._checkExpertAdvisorAllowed(); let expertAdvisors = yield _this._expertAdvisorClient.getExpertAdvisors(_this.id); return expertAdvisors.map((e)=>new ExpertAdvisor(e, _this.id, _this._expertAdvisorClient)); })(); } /** * Retrieves a expert advisor of current account by id * @param {String} expertId expert advisor id * @returns {Promise<ExpertAdvisor>} promise resolving with expert advisor entity */ getExpertAdvisor(expertId) { var _this = this; return _async_to_generator(function*() { _this._checkExpertAdvisorAllowed(); let expertAdvisor = yield _this._expertAdvisorClient.getExpertAdvisor(_this.id, expertId); return new ExpertAdvisor(expertAdvisor, _this.id, _this._expertAdvisorClient); })(); } /** * Creates an expert advisor * @param {string} expertId expert advisor id * @param {NewExpertAdvisorDto} expert expert advisor data * @returns {Promise<ExpertAdvisor>} promise resolving with expert advisor entity */ createExpertAdvisor(expertId, expert) { var _this = this; return _async_to_generator(function*() { _this._checkExpertAdvisorAllowed(); yield _this._expertAdvisorClient.updateExpertAdvisor(_this.id, expertId, expert); return _this.getExpertAdvisor(expertId); })(); } /** * Returns historical candles for a specific symbol and timeframe from the MetaTrader account. * See https://metaapi.cloud/docs/client/restApi/api/retrieveMarketData/readHistoricalCandles/ * @param {string} symbol symbol to retrieve candles for (e.g. a currency pair or an index) * @param {string} timeframe defines the timeframe according to which the candles must be generated. Allowed values * for MT5 are 1m, 2m, 3m, 4m, 5m, 6m, 10m, 12m, 15m, 20m, 30m, 1h, 2h, 3h, 4h, 6h, 8h, 12h, 1d, 1w, 1mn. Allowed * values for MT4 are 1m, 5m, 15m 30m, 1h, 4h, 1d, 1w, 1mn * @param {Date} [startTime] time to start loading candles from. Note that candles are loaded in backwards direction, so * this should be the latest time. Leave empty to request latest candles. * @param {number} [limit] maximum number of candles to retrieve. Must be less or equal to 1000 * @return {Promise<Array<MetatraderCandle>>} promise resolving with historical candles downloaded */ getHistoricalCandles(symbol, timeframe, startTime, limit) { return this._historicalMarketDataClient.getHistoricalCandles(this.id, this.region, symbol, timeframe, startTime, limit); } /** * Returns historical ticks for a specific symbol from the MetaTrader account. This API is not supported by MT4 * accounts. * See https://metaapi.cloud/docs/client/restApi/api/retrieveMarketData/readHistoricalTicks/ * @param {string} symbol symbol to retrieve ticks for (e.g. a currency pair or an index) * @param {Date} [startTime] time to start loading ticks from. Note that candles are loaded in forward direction, so * this should be the earliest time. Leave empty to request latest candles. * @param {number} [offset] number of ticks to skip (you can use it to avoid requesting ticks from previous request * twice) * @param {number} [limit] maximum number of ticks to retrieve. Must be less or equal to 1000 * @return {Promise<Array<MetatraderTick>>} promise resolving with historical ticks downloaded */ getHistoricalTicks(symbol, startTime, offset, limit) { return this._historicalMarketDataClient.getHistoricalTicks(this.id, this.region, symbol, startTime, offset, limit); } /** * Generates trading account configuration link by account id. * @param {number} [ttlInDays] Lifetime of the link in days. Default is 7. * @return {Promise<ConfigurationLink>} promise resolving with configuration link */ createConfigurationLink(ttlInDays) { var _this = this; return _async_to_generator(function*() { const configurationLink = yield _this._metatraderAccountClient.createConfigurationLink(_this.id, ttlInDays); return configurationLink; })(); } _checkExpertAdvisorAllowed() { if (this.version !== 4 || this.type !== 'cloud-g1') { throw new ValidationError('Custom expert advisor is available only for MT4 G1 accounts'); } } _delay(timeoutInMilliseconds) { return new Promise((res)=>setTimeout(res, timeoutInMilliseconds)); } /** * Constructs a MetaTrader account entity * @param {MetatraderAccountDto} data MetaTrader account data * @param {MetatraderAccountClient} metatraderAccountClient MetaTrader account REST API client * @param {MetaApiWebsocketClient} metaApiWebsocketClient MetaApi websocket client * @param {ConnectionRegistry} connectionRegistry metatrader account connection registry * @param {ExpertAdvisorClient} expertAdvisorClient expert advisor REST API client * @param {HistoricalMarketDataClient} historicalMarketDataClient historical market data HTTP API client * @param {string} application application name */ constructor(data, metatraderAccountClient, metaApiWebsocketClient, connectionRegistry, expertAdvisorClient, historicalMarketDataClient, application){ _define_property(this, "_data", void 0); _define_property(this, "_metatraderAccountClient", void 0); _define_property(this, "_metaApiWebsocketClient", void 0); _define_property(this, "_connectionRegistry", void 0); _define_property(this, "_expertAdvisorClient", void 0); _define_property(this, "_historicalMarketDataClient", void 0); _define_property(this, "_application", void 0); _define_property(this, "_replicas", void 0); this._data = data; this._metatraderAccountClient = metatraderAccountClient; this._metaApiWebsocketClient = metaApiWebsocketClient; this._connectionRegistry = connectionRegistry; this._expertAdvisorClient = expertAdvisorClient; this._historicalMarketDataClient = historicalMarketDataClient; this._application = application; this._replicas = (data.accountReplicas || []).map((replica)=>new MetatraderAccountReplica(replica, this, metatraderAccountClient)); } }; export default MetatraderAccount; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCBUaW1lb3V0RXJyb3IgZnJvbSAnLi4vY2xpZW50cy90aW1lb3V0RXJyb3InO1xuaW1wb3J0IFJwY01ldGFBcGlDb25uZWN0aW9uSW5zdGFuY2UgZnJvbSAnLi9ycGNNZXRhQXBpQ29ubmVjdGlvbkluc3RhbmNlJztcbmltcG9ydCBTdHJlYW1pbmdNZXRhQXBpQ29ubmVjdGlvbkluc3RhbmNlIGZyb20gJy4vc3RyZWFtaW5nTWV0YUFwaUNvbm5lY3Rpb25JbnN0YW5jZSc7XG5jb25zdCBIaXN0b3J5RGF0YWJhc2UgPSByZXF1aXJlKCcuL2hpc3RvcnlEYXRhYmFzZS9pbmRleCcpLmRlZmF1bHQ7XG5pbXBvcnQgRXhwZXJ0QWR2aXNvciBmcm9tICcuL2V4cGVydEFkdmlzb3InO1xuaW1wb3J0IHtWYWxpZGF0aW9uRXJyb3J9IGZyb20gJy4uL2NsaWVudHMvZXJyb3JIYW5kbGVyJztcbmltcG9ydCBNZXRhdHJhZGVyQWNjb3VudFJlcGxpY2EgZnJvbSAnLi9tZXRhdHJhZGVyQWNjb3VudFJlcGxpY2EnO1xuLy9lc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbWF4LWxlblxuaW1wb3J0IE1ldGF0cmFkZXJBY2NvdW50Q2xpZW50LCB7XG4gIFJlbGlhYmlsaXR5LCBTdGF0ZSwgVmVyc2lvbiwgQ29ubmVjdGlvblN0YXR1cywgQ29weUZhY3RvcnlSb2xlcywgVHlwZSwgQWNjb3VudENvbm5lY3Rpb24sIENvbmZpZ3VyYXRpb25MaW5rLFxuICBNZXRhdHJhZGVyQWNjb3VudER0bywgRGVkaWNhdGVkSXAsXG4gIE5ld01ldGFUcmFkZXJBY2NvdW50UmVwbGljYUR0byxcbiAgTWV0YXRyYWRlckFjY291bnRVcGRhdGVEdG9cbn0gZnJvbSAnLi4vY2xpZW50cy9tZXRhQXBpL21ldGF0cmFkZXJBY2NvdW50LmNsaWVudCc7XG5pbXBvcnQgdHlwZSBDb25uZWN0aW9uUmVnaXN0cnkgZnJvbSAnLi9jb25uZWN0aW9uUmVnaXN0cnknO1xuaW1wb3J0IHR5cGUgTWV0YUFwaVdlYnNvY2tldENsaWVudCBmcm9tICcuLi9jbGllbnRzL21ldGFBcGkvbWV0YUFwaVdlYnNvY2tldC5jbGllbnQnO1xuaW1wb3J0IHR5cGUgRXhwZXJ0QWR2aXNvckNsaWVudCBmcm9tICcuLi9jbGllbnRzL21ldGFBcGkvZXhwZXJ0QWR2aXNvci5jbGllbnQnO1xuaW1wb3J0IHR5cGUgSGlzdG9yaWNhbE1hcmtldERhdGFDbGllbnQgZnJvbSAnLi4vY2xpZW50cy9tZXRhQXBpL2hpc3RvcmljYWxNYXJrZXREYXRhLmNsaWVudCc7XG5pbXBvcnQgSGlzdG9yeVN0b3JhZ2UgZnJvbSAnLi9oaXN0b3J5U3RvcmFnZSc7XG5pbXBvcnQge05ld0V4cGVydEFkdmlzb3JEdG99IGZyb20gJy4uL2NsaWVudHMvbWV0YUFwaS9leHBlcnRBZHZpc29yLmNsaWVudCc7XG5pbXBvcnQge01ldGF0cmFkZXJDYW5kbGUsIE1ldGF0cmFkZXJUaWNrfSBmcm9tICcuLi9jbGllbnRzL21ldGFBcGkvbWV0YUFwaVdlYnNvY2tldC5jbGllbnQnO1xuXG4vKipcbiAqIEltcGxlbWVudHMgYSBNZXRhVHJhZGVyIGFjY291bnQgZW50aXR5XG4gKi9cbmNsYXNzIE1ldGF0cmFkZXJBY2NvdW50IHtcbiAgXG4gIHByaXZhdGUgX2RhdGE6IGFueTtcbiAgcHJpdmF0ZSBfbWV0YXRyYWRlckFjY291bnRDbGllbnQ6IGFueTtcbiAgcHJpdmF0ZSBfbWV0YUFwaVdlYnNvY2tldENsaWVudDogYW55O1xuICBwcml2YXRlIF9jb25uZWN0aW9uUmVnaXN0cnk6IGFueTtcbiAgcHJpdmF0ZSBfZXhwZXJ0QWR2aXNvckNsaWVudDogYW55O1xuICBwcml2YXRlIF9oaXN0b3JpY2FsTWFya2V0RGF0YUNsaWVudDogYW55O1xuICBwcml2YXRlIF9hcHBsaWNhdGlvbjogYW55O1xuICBwcml2YXRlIF9yZXBsaWNhczogYW55O1xuXG4gIC8qKlxuICAgKiBDb25zdHJ1Y3RzIGEgTWV0YVRyYWRlciBhY2NvdW50IGVudGl0eVxuICAgKiBAcGFyYW0ge01ldGF0cmFkZXJBY2NvdW50RHRvfSBkYXRhIE1ldGFUcmFkZXIgYWNjb3VudCBkYXRhXG4gICAqIEBwYXJhbSB7TWV0YXRyYWRlckFjY291bnRDbGllbnR9IG1ldGF0cmFkZXJBY2NvdW50Q2xpZW50IE1ldGFUcmFkZXIgYWNjb3VudCBSRVNUIEFQSSBjbGllbnRcbiAgICogQHBhcmFtIHtNZXRhQXBpV2Vic29ja2V0Q2xpZW50fSBtZXRhQXBpV2Vic29ja2V0Q2xpZW50IE1ldGFBcGkgd2Vic29ja2V0IGNsaWVudFxuICAgKiBAcGFyYW0ge0Nvbm5lY3Rpb25SZWdpc3RyeX0gY29ubmVjdGlvblJlZ2lzdHJ5IG1ldGF0cmFkZXIgYWNjb3VudCBjb25uZWN0aW9uIHJlZ2lzdHJ5XG4gICAqIEBwYXJhbSB7RXhwZXJ0QWR2aXNvckNsaWVudH0gZXhwZXJ0QWR2aXNvckNsaWVudCBleHBlcnQgYWR2aXNvciBSRVNUIEFQSSBjbGllbnRcbiAgICogQHBhcmFtIHtIaXN0b3JpY2FsTWFya2V0RGF0YUNsaWVudH0gaGlzdG9yaWNhbE1hcmtldERhdGFDbGllbnQgaGlzdG9yaWNhbCBtYXJrZXQgZGF0YSBIVFRQIEFQSSBjbGllbnRcbiAgICogQHBhcmFtIHtzdHJpbmd9IGFwcGxpY2F0aW9uIGFwcGxpY2F0aW9uIG5hbWVcbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgIGRhdGE6IE1ldGF0cmFkZXJBY2NvdW50RHRvLCBtZXRhdHJhZGVyQWNjb3VudENsaWVudDogTWV0YXRyYWRlckFjY291bnRDbGllbnQsXG4gICAgbWV0YUFwaVdlYnNvY2tldENsaWVudDogTWV0YUFwaVdlYnNvY2tldENsaWVudCwgY29ubmVjdGlvblJlZ2lzdHJ5OiBDb25uZWN0aW9uUmVnaXN0cnksXG4gICAgZXhwZXJ0QWR2aXNvckNsaWVudDogRXhwZXJ0QWR2aXNvckNsaWVudCwgaGlzdG9yaWNhbE1hcmtldERhdGFDbGllbnQ6IEhpc3RvcmljYWxNYXJrZXREYXRhQ2xpZW50LFxuICAgIGFwcGxpY2F0aW9uOiBzdHJpbmdcbiAgKSB7XG4gICAgdGhpcy5fZGF0YSA9IGRhdGE7XG4gICAgdGhpcy5fbWV0YXRyYWRlckFjY291bnRDbGllbnQgPSBtZXRhdHJhZGVyQWNjb3VudENsaWVudDtcbiAgICB0aGlzLl9tZXRhQXBpV2Vic29ja2V0Q2xpZW50ID0gbWV0YUFwaVdlYnNvY2tldENsaWVudDtcbiAgICB0aGlzLl9jb25uZWN0aW9uUmVnaXN0cnkgPSBjb25uZWN0aW9uUmVnaXN0cnk7XG4gICAgdGhpcy5fZXhwZXJ0QWR2aXNvckNsaWVudCA9IGV4cGVydEFkdmlzb3JDbGllbnQ7XG4gICAgdGhpcy5faGlzdG9yaWNhbE1hcmtldERhdGFDbGllbnQgPSBoaXN0b3JpY2FsTWFya2V0RGF0YUNsaWVudDtcbiAgICB0aGlzLl9hcHBsaWNhdGlvbiA9IGFwcGxpY2F0aW9uO1xuICAgIHRoaXMuX3JlcGxpY2FzID0gKGRhdGEuYWNjb3VudFJlcGxpY2FzIHx8IFtdKVxuICAgICAgLm1hcChyZXBsaWNhID0+IG5ldyBNZXRhdHJhZGVyQWNjb3VudFJlcGxpY2EocmVwbGljYSwgdGhpcywgbWV0YXRyYWRlckFjY291bnRDbGllbnQpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHVuaXF1ZSBhY2NvdW50IGlkXG4gICAqIEByZXR1cm4ge3N0cmluZ30gdW5pcXVlIGFjY291bnQgaWRcbiAgICovXG4gIGdldCBpZCgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLl9pZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGN1cnJlbnQgYWNjb3VudCBzdGF0ZS4gT25lIG9mIENSRUFURUQsIERFUExPWUlORywgREVQTE9ZRUQsIERFUExPWV9GQUlMRUQsIFVOREVQTE9ZSU5HLFxuICAgKiBVTkRFUExPWUVELCBVTkRFUExPWV9GQUlMRUQsIERFTEVUSU5HLCBERUxFVEVfRkFJTEVELCBSRURFUExPWV9GQUlMRUQsIERSQUZUXG4gICAqIEByZXR1cm4ge1N0YXRlfSBjdXJyZW50IGFjY291bnQgc3RhdGVcbiAgICovXG4gIGdldCBzdGF0ZSgpOiBTdGF0ZSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuc3RhdGU7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBNZXRhVHJhZGVyIG1hZ2ljIHRvIHBsYWNlIHRyYWRlcyB1c2luZ1xuICAgKiBAcmV0dXJuIHtudW1iZXJ9IE1ldGFUcmFkZXIgbWFnaWMgdG8gcGxhY2UgdHJhZGVzIHVzaW5nXG4gICAqL1xuICBnZXQgbWFnaWMoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5tYWdpYztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRlcm1pbmFsICYgYnJva2VyIGNvbm5lY3Rpb24gc3RhdHVzLCBvbmUgb2YgQ09OTkVDVEVELCBESVNDT05ORUNURUQsIERJU0NPTk5FQ1RFRF9GUk9NX0JST0tFUlxuICAgKiBAcmV0dXJuIHtDb25uZWN0aW9uU3RhdHVzfSB0ZXJtaW5hbCAmIGJyb2tlciBjb25uZWN0aW9uIHN0YXR1c1xuICAgKi9cbiAgZ2V0IGNvbm5lY3Rpb25TdGF0dXMoKTogQ29ubmVjdGlvblN0YXR1cyB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuY29ubmVjdGlvblN0YXR1cztcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJldHVybnMgcXVvdGUgc3RyZWFtaW5nIGludGVydmFsIGluIHNlY29uZHMgXG4gICAqIEByZXR1cm4ge251bWJlcn0gcXVvdGUgc3RyZWFtaW5nIGludGVydmFsIGluIHNlY29uZHNcbiAgICovXG4gIGdldCBxdW90ZVN0cmVhbWluZ0ludGVydmFsSW5TZWNvbmRzKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEucXVvdGVTdHJlYW1pbmdJbnRlcnZhbEluU2Vjb25kcztcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJldHVybnMgc3ltYm9sIHByb3ZpZGVkIGJ5IGJyb2tlciBcbiAgICogQHJldHVybiB7c3RyaW5nfSBhbnkgc3ltYm9sIHByb3ZpZGVkIGJ5IGJyb2tlclxuICAgKi9cbiAgZ2V0IHN5bWJvbCgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnN5bWJvbDtcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJldHVybnMgcmVsaWFiaWxpdHkgdmFsdWUuIFBvc3NpYmxlIHZhbHVlcyBhcmUgcmVndWxhciBhbmQgaGlnaFxuICAgKiBAcmV0dXJuIHtSZWxpYWJpbGl0eX0gYWNjb3VudCByZWxpYWJpbGl0eSB2YWx1ZVxuICAgKi9cbiAgZ2V0IHJlbGlhYmlsaXR5KCk6IFJlbGlhYmlsaXR5IHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5yZWxpYWJpbGl0eTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJldHVybnMgdXNlci1kZWZpbmVkIGFjY291bnQgdGFnc1xuICAgKiBAcmV0dXJuIHtBcnJheTxzdHJpbmc+fSB1c2VyLWRlZmluZWQgYWNjb3VudCB0YWdzXG4gICAqL1xuICBnZXQgdGFncygpOiBBcnJheTxzdHJpbmc+IHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS50YWdzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgZXh0cmEgaW5mb3JtYXRpb24gd2hpY2ggY2FuIGJlIHN0b3JlZCB0b2dldGhlciB3aXRoIHlvdXIgYWNjb3VudFxuICAgKiBAcmV0dXJuIHtPYmplY3R9IGV4dHJhIGluZm9ybWF0aW9uIHdoaWNoIGNhbiBiZSBzdG9yZWQgdG9nZXRoZXIgd2l0aCB5b3VyIGFjY291bnRcbiAgICovXG4gIGdldCBtZXRhZGF0YSgpOiBPYmplY3Qge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLm1ldGFkYXRhO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgbnVtYmVyIG9mIHJlc291cmNlIHNsb3RzIHRvIGFsbG9jYXRlIHRvIGFjY291bnQuIEFsbG9jYXRpbmcgZXh0cmEgcmVzb3VyY2Ugc2xvdHNcbiAgICogcmVzdWx0cyBpbiBiZXR0ZXIgYWNjb3VudCBwZXJmb3JtYW5jZSB1bmRlciBsb2FkIHdoaWNoIGlzIHVzZWZ1bCBmb3Igc29tZSBhcHBsaWNhdGlvbnMuIEUuZy4gaWYgeW91IGhhdmUgbWFueVxuICAgKiBhY2NvdW50cyBjb3B5aW5nIHRoZSBzYW1lIHN0cmF0ZWd5IHZpYSBDb3B5RmFjdG9yeSBBUEksIHRoZW4geW91IGNhbiBpbmNyZWFzZSByZXNvdXJjZVNsb3RzIHRvIGdldCBhIGxvd2VyIHRyYWRlXG4gICAqIGNvcHlpbmcgbGF0ZW5jeS4gUGxlYXNlIG5vdGUgdGhhdCBhbGxvY2F0aW5nIGV4dHJhIHJlc291cmNlIHNsb3RzIGlzIGEgcGFpZCBvcHRpb24uIFBsZWFzZSBub3RlIHRoYXQgaGlnaFxuICAgKiByZWxpYWJpbGl0eSBhY2NvdW50cyB1c2UgcmVkdW5kYW50IGluZnJhc3RydWN0dXJlLCBzbyB0aGF0IGVhY2ggcmVzb3VyY2Ugc2xvdCBmb3IgYSBoaWdoIHJlbGlhYmlsaXR5IGFjY291bnRcbiAgICogaXMgYmlsbGVkIGFzIDIgc3RhbmRhcmQgcmVzb3VyY2Ugc2xvdHMuXG4gICAqIEByZXR1cm4ge251bWJlcn0gbnVtYmVyIG9mIHJlc291cmNlIHNsb3RzIHRvIGFsbG9jYXRlIHRvIGFjY291bnRcbiAgICovXG4gIGdldCByZXNvdXJjZVNsb3RzKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEucmVzb3VyY2VTbG90cztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBudW1iZXIgb2YgQ29weUZhY3RvcnkgMiByZXNvdXJjZSBzbG90cyB0byBhbGxvY2F0ZSB0byBhY2NvdW50LlxuICAgKiBBbGxvY2F0aW5nIGV4dHJhIHJlc291cmNlIHNsb3RzIHJlc3VsdHMgaW4gbG93ZXIgdHJhZGUgY29weWluZyBsYXRlbmN5LiBQbGVhc2Ugbm90ZSB0aGF0IGFsbG9jYXRpbmcgZXh0cmEgcmVzb3VyY2VcbiAgICogc2xvdHMgaXMgYSBwYWlkIG9wdGlvbi4gUGxlYXNlIGFsc28gbm90ZSB0aGF0IENvcHlGYWN0b3J5IDIgdXNlcyByZWR1bmRhbnQgaW5mcmFzdHJ1Y3R1cmUgc28gdGhhdFxuICAgKiBlYWNoIENvcHlGYWN0b3J5IHJlc291cmNlIHNsb3QgaXMgYmlsbGVkIGFzIDIgc3RhbmRhcmQgcmVzb3VyY2Ugc2xvdHMuIFlvdSB3aWxsIGJlIGJpbGxlZCBmb3IgQ29weUZhY3RvcnkgMlxuICAgKiByZXNvdXJjZSBzbG90cyBvbmx5IGlmIHlvdSBoYXZlIGFkZGVkIHlvdXIgYWNjb3VudCB0byBDb3B5RmFjdG9yeSAyIGJ5IHNwZWNpZnlpbmcgY29weUZhY3RvcnlSb2xlcyBmaWVsZC5cbiAgICogQHJldHVybiB7bnVtYmVyfSBudW1iZXIgb2YgQ29weUZhY3RvcnkgMiByZXNvdXJjZSBzbG90cyB0byBhbGxvY2F0ZSB0byBhY2NvdW50XG4gICAqL1xuICBnZXQgY29weUZhY3RvcnlSZXNvdXJjZVNsb3RzKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuY29weUZhY3RvcnlSZXNvdXJjZVNsb3RzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYWNjb3VudCByZWdpb25cbiAgICogQHJldHVybiB7c3RyaW5nfSBhY2NvdW50IHJlZ2lvbiB2YWx1ZVxuICAgKi9cbiAgZ2V0IHJlZ2lvbigpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnJlZ2lvbjtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSB0aW1lIGFjY291bnQgd2FzIGNyZWF0ZWQgYXQsIGluIElTTyBmb3JtYXRcbiAgICogQHJldHVybnMge3N0cmluZ30gdGhlIHRpbWUgYWNjb3VudCB3YXMgY3JlYXRlZCBhdCwgaW4gSVNPIGZvcm1hdFxuICAgKi9cbiAgZ2V0IGNyZWF0ZWRBdCgpOiBEYXRlIHtcbiAgICByZXR1cm4gbmV3IERhdGUodGhpcy5fZGF0YS5jcmVhdGVkQXQpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgaHVtYW4tcmVhZGFibGUgYWNjb3VudCBuYW1lXG4gICAqIEByZXR1cm4ge3N0cmluZ30gaHVtYW4tcmVhZGFibGUgYWNjb3VudCBuYW1lXG4gICAqL1xuICBnZXQgbmFtZSgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLm5hbWU7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBSZXR1cm5zIGZsYWcgaW5kaWNhdGluZyBpZiB0cmFkZXMgc2hvdWxkIGJlIHBsYWNlZCBhcyBtYW51YWwgdHJhZGVzIG9uIHRoaXMgYWNjb3VudFxuICAgKiBAcmV0dXJuIHtib29sZWFufSBmbGFnIGluZGljYXRpbmcgaWYgdHJhZGVzIHNob3VsZCBiZSBwbGFjZWQgYXMgbWFudWFsIHRyYWRlcyBvbiB0aGlzIGFjY291bnRcbiAgICovXG4gIGdldCBtYW51YWxUcmFkZXMoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEubWFudWFsVHJhZGVzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgZGVmYXVsdCB0cmFkZSBzbGlwcGFnZSBpbiBwb2ludHNcbiAgICogQHJldHVybiB7bnVtYmVyfSBkZWZhdWx0IHRyYWRlIHNsaXBwYWdlIGluIHBvaW50c1xuICAgKi9cbiAgZ2V0IHNsaXBwYWdlKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuc2xpcHBhZ2U7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBSZXR1cm5zIGlkIG9mIHRoZSBhY2NvdW50J3MgcHJvdmlzaW9uaW5nIHByb2ZpbGVcbiAgICogQHJldHVybiB7c3RyaW5nfSBpZCBvZiB0aGUgYWNjb3VudCdzIHByb3Zpc2lvbmluZyBwcm9maWxlXG4gICAqL1xuICBnZXQgcHJvdmlzaW9uaW5nUHJvZmlsZUlkKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEucHJvdmlzaW9uaW5nUHJvZmlsZUlkO1xuICB9XG4gIFxuICAvKipcbiAgICogUmV0dXJucyBNZXRhVHJhZGVyIGFjY291bnQgbG9naW5cbiAgICogQHJldHVybiB7c3RyaW5nfSBNZXRhVHJhZGVyIGFjY291bnQgbnVtYmVyXG4gICAqL1xuICBnZXQgbG9naW4oKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5sb2dpbjtcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJldHVybnMgTWV0YVRyYWRlciBzZXJ2ZXIgbmFtZSB0byBjb25uZWN0IHRvXG4gICAqIEByZXR1cm4ge3N0cmluZ30gTWV0YVRyYWRlciBzZXJ2ZXIgbmFtZSB0byBjb25uZWN0IHRvXG4gICAqL1xuICBnZXQgc2VydmVyKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuc2VydmVyO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYWNjb3VudCB0eXBlLiBQb3NzaWJsZSB2YWx1ZXMgYXJlIGNsb3VkLWcxLCBjbG91ZC1nMlxuICAgKiBAcmV0dXJuIHtUeXBlfSBhY2NvdW50IHR5cGVcbiAgICovXG4gIGdldCB0eXBlKCk6IFR5cGUge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnR5cGU7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBNVCB2ZXJzaW9uLiBQb3NzaWJsZSB2YWx1ZXMgYXJlIDQgYW5kIDVcbiAgICogQHJldHVybiB7VmVyc2lvbn0gTVQgdmVyc2lvblxuICAgKi9cbiAgZ2V0IHZlcnNpb24oKTogVmVyc2lvbiB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEudmVyc2lvbjtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGhhc2gtY29kZSBvZiB0aGUgYWNjb3VudFxuICAgKiBAcmV0dXJuIHtudW1iZXJ9IGhhc2gtY29kZSBvZiB0aGUgYWNjb3VudFxuICAgKi9cbiAgZ2V0IGhhc2goKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5oYXNoO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgMy1jaGFyYWN0ZXIgSVNPIGN1cnJlbmN5IGNvZGUgb2YgdGhlIGFjY291bnQgYmFzZSBjdXJyZW5jeS4gVGhlIHNldHRpbmcgaXMgdG8gYmUgdXNlZFxuICAgKiBmb3IgY29weSB0cmFkaW5nIGFjY291bnRzIHdoaWNoIHVzZSBuYXRpb25hbCBjdXJyZW5jaWVzIG9ubHksIHN1Y2ggYXMgc29tZSBCcmF6aWxpYW4gYnJva2Vycy4gWW91IHNob3VsZCBub3QgYWx0ZXJcbiAgICogdGhpcyBzZXR0aW5nIHVubGVzcyB5b3UgdW5kZXJzdGFuZCB3aGF0IHlvdSBhcmUgZG9pbmcuXG4gICAqIEByZXR1cm4ge3N0cmluZ30gMy1jaGFyYWN0ZXIgSVNPIGN1cnJlbmN5IGNvZGUgb2YgdGhlIGFjY291bnQgYmFzZSBjdXJyZW5jeVxuICAgKi9cbiAgZ2V0IGJhc2VDdXJyZW5jeSgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLmJhc2VDdXJyZW5jeTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGFjY291bnQgcm9sZXMgZm9yIENvcHlGYWN0b3J5MiBhcHBsaWNhdGlvbi4gUG9zc2libGUgdmFsdWVzIGFyZSBgUFJPVklERVJgIGFuZCBgU1VCU0NSSUJFUmBcbiAgICogQHJldHVybiB7QXJyYXk8Q29weUZhY3RvcnlSb2xlcz59IGFjY291bnQgcm9sZXMgZm9yIENvcHlGYWN0b3J5MiBhcHBsaWNhdGlvblxuICAgKi9cbiAgZ2V0IGNvcHlGYWN0b3J5Um9sZXMoKTogQXJyYXk8Q29weUZhY3RvcnlSb2xlcz4ge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLmNvcHlGYWN0b3J5Um9sZXM7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBSZXR1cm5zIGZsYWcgaW5kaWNhdGluZyB0aGF0IHJpc2sgbWFuYWdlbWVudCBBUEkgaXMgZW5hYmxlZCBvbiBhY2NvdW50XG4gICAqIEByZXR1cm4ge2Jvb2xlYW59IGZsYWcgaW5kaWNhdGluZyB0aGF0IHJpc2sgbWFuYWdlbWVudCBBUEkgaXMgZW5hYmxlZCBvbiBhY2NvdW50XG4gICAqL1xuICBnZXQgcmlza01hbmFnZW1lbnRBcGlFbmFibGVkKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnJpc2tNYW5hZ2VtZW50QXBpRW5hYmxlZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGZsYWcgaW5kaWNhdGluZyB0aGF0IE1ldGFTdGF0cyBBUEkgaXMgZW5hYmxlZCBvbiBhY2NvdW50XG4gICAqIEByZXR1cm4ge2Jvb2xlYW59IGZsYWcgaW5kaWNhdGluZyB0aGF0IE1ldGFTdGF0cyBBUEkgaXMgZW5hYmxlZCBvbiBhY2NvdW50XG4gICAqL1xuICBnZXQgbWV0YXN0YXRzQXBpRW5hYmxlZCgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5tZXRhc3RhdHNBcGlFbmFibGVkO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgY29uZmlndXJlZCBkZWRpY2F0ZWQgSVAgcHJvdG9jb2wgdG8gY29ubmVjdCB0byB0aGUgdHJhZGluZyBhY2NvdW50IHRlcm1pbmFsXG4gICAqIEByZXR1cm4ge0RlZGljYXRlZElwfVxuICAgKi9cbiAgZ2V0IGFsbG9jYXRlRGVkaWNhdGVkSXAoKTogRGVkaWNhdGVkSXAge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLmFsbG9jYXRlRGVkaWNhdGVkSXA7XG4gIH1cbiAgICBcbiAgLyoqXG4gICAqIFJldHVybnMgYWN0aXZlIGFjY291bnQgY29ubmVjdGlvbnNcbiAgICogQHJldHVybiB7QXJyYXk8QWNjb3VudENvbm5lY3Rpb24+fSBhY3RpdmUgYWNjb3VudCBjb25uZWN0aW9uc1xuICAgKi9cbiAgZ2V0IGNvbm5lY3Rpb25zKCk6IEFycmF5PEFjY291bnRDb25uZWN0aW9uPiB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuY29ubmVjdGlvbnM7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBmbGFnIGluZGljYXRpbmcgdGhhdCBhY2NvdW50IGlzIHByaW1hcnlcbiAgICogQHJldHVybiB7Ym9vbGVhbn0gZmxhZyBpbmRpY2F0aW5nIHRoYXQgYWNjb3VudCBpcyBwcmltYXJ5XG4gICAqL1xuICBnZXQgcHJpbWFyeVJlcGxpY2EoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEucHJpbWFyeVJlcGxpY2E7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB1c2VyIGlkXG4gICAqIEByZXR1cm4ge3N0cmluZ30gdXNlciBpZFxuICAgKi9cbiAgZ2V0IHVzZXJJZCgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnVzZXJJZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHByaW1hcnkgYWNjb3VudCBpZFxuICAgKiBAcmV0dXJuIHtzdHJpbmd9IHByaW1hcnkgYWNjb3VudCBpZFxuICAgKi9cbiAgZ2V0IHByaW1hcnlBY2NvdW50SWQoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5wcmltYXJ5QWNjb3VudElkO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYWNjb3VudCByZXBsaWNhcyBmcm9tIERUT1xuICAgKiBAcmV0dXJuIHtNZXRhdHJhZGVyQWNjb3VudFJlcGxpY2FbXX0gYWNjb3VudCByZXBsaWNhcyBmcm9tIERUT1xuICAgKi9cbiAgZ2V0IGFjY291bnRSZXBsaWNhcygpOiBNZXRhdHJhZGVyQWNjb3VudFJlcGxpY2FbXSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuYWNjb3VudFJlcGxpY2FzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYWNjb3VudCByZXBsaWNhIGluc3RhbmNlc1xuICAgKiBAcmV0dXJuIHtNZXRhdHJhZGVyQWNjb3VudFJlcGxpY2FbXX0gYWNjb3VudCByZXBsaWNhIGluc3RhbmNlc1xuICAgKi9cbiAgZ2V0IHJlcGxpY2FzKCk6IE1ldGF0cmFkZXJBY2NvdW50UmVwbGljYVtdIHtcbiAgICByZXR1cm4gdGhpcy5fcmVwbGljYXM7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBhIGRpY3Rpb25hcnkgd2l0aCBhY2NvdW50J3MgYXZhaWxhYmxlIHJlZ2lvbnMgYW5kIHJlcGxpY2FzXG4gICAqIEByZXR1cm5zIGFjY291bnRzIGJ5IHJlZ2lvblxuICAgKi9cbiAgZ2V0IGFjY291bnRSZWdpb25zKCk6IE1ldGF0cmFkZXJBY2NvdW50LkFjY291bnRzQnlSZWdpb24ge1xuICAgIGNvbnN0IHJlZ2lvbnMgPSB7W3RoaXMucmVnaW9uXTogdGhpcy5pZH07XG4gICAgdGhpcy5yZXBsaWNhcy5mb3JFYWNoKHJlcGxpY2EgPT4gcmVnaW9uc1tyZXBsaWNhLnJlZ2lvbl0gPSByZXBsaWNhLmlkKTtcbiAgICByZXR1cm4gcmVnaW9ucztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWxvYWRzIE1ldGFUcmFkZXIgYWNjb3VudCBmcm9tIEFQSVxuICAgKiBAcmV0dXJuIHtQcm9taXNlfSBwcm9taXNlIHJlc29sdmluZyB3aGVuIE1ldGFUcmFkZXIgYWNjb3VudCBpcyB1cGRhdGVkXG4gICAqL1xuICBhc3luYyByZWxvYWQoKSB7XG4gICAgdGhpcy5fZGF0YSA9IGF3YWl0IHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50LmdldEFjY291bnQodGhpcy5pZCk7XG4gICAgY29uc3QgdXBkYXRlZFJlcGxpY2FEYXRhID0gKHRoaXMuX2RhdGEuYWNjb3VudFJlcGxpY2FzIHx8IFtdKTtcbiAgICBjb25zdCByZWdpb25zID0gdXBkYXRlZFJlcGxpY2FEYXRhLm1hcChyZXBsaWNhID0+IHJlcGxpY2EucmVnaW9uKTtcbiAgICBjb25zdCBjcmVhdGVkUmVwbGljYVJlZ2lvbnMgPSB0aGlzLl9yZXBsaWNhcy5tYXAocmVwbGljYSA9PiByZXBsaWNhLnJlZ2lvbik7XG4gICAgdGhpcy5fcmVwbGljYXMgPSB0aGlzLl9yZXBsaWNhcy5maWx0ZXIocmVwbGljYSA9PiByZWdpb25zLmluY2x1ZGVzKHJlcGxpY2EucmVnaW9uKSk7XG4gICAgdGhpcy5fcmVwbGljYXMuZm9yRWFjaChyZXBsaWNhID0+IHtcbiAgICAgIGNvbnN0IHVwZGF0ZWREYXRhID0gdXBkYXRlZFJlcGxpY2FEYXRhLmZpbmQocmVwbGljYURhdGEgPT4gcmVwbGljYURhdGEucmVnaW9uID09PSByZXBsaWNhLnJlZ2lvbik7XG4gICAgICByZXBsaWNhLnVwZGF0ZURhdGEodXBkYXRlZERhdGEpO1xuICAgIH0pO1xuICAgIHVwZGF0ZWRSZXBsaWNhRGF0YS5mb3JFYWNoKHJlcGxpY2EgPT4ge1xuICAgICAgaWYoIWNyZWF0ZWRSZXBsaWNhUmVnaW9ucy5pbmNsdWRlcyhyZXBsaWNhLnJlZ2lvbikpIHtcbiAgICAgICAgdGhpcy5fcmVwbGljYXMucHVzaChuZXcgTWV0YXRyYWRlckFjY291bnRSZXBsaWNhKHJlcGxpY2EsIHRoaXMsIHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50KSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyBhIHRyYWRpbmcgYWNjb3VudCBhbmQgc3RvcHMgdGhlIEFQSSBzZXJ2ZXIgc2VydmluZyB0aGUgYWNjb3VudC5cbiAgICogVGhlIGFjY291bnQgc3RhdGUgc3VjaCBhcyBkb3dubG9hZGVkIG1hcmtldCBkYXRhIGhpc3Rvcnkgd2lsbCBiZSByZW1vdmVkIGFzIHdlbGwgd2hlbiB5b3UgcmVtb3ZlIHRoZSBhY2NvdW50LlxuICAgKiBAcmV0dXJuIHtQcm9taXNlfSBwcm9taXNlIHJlc29sdmluZyB3aGVuIGFjY291bnQgaXMgc2NoZWR1bGVkIGZvciBkZWxldGlvblxuICAgKi9cbiAgYXN5bmMgcmVtb3ZlKCkge1xuICAgIHRoaXMuX2Nvbm5lY3Rpb25SZWdpc3RyeS5jbG9zZUFsbEluc3RhbmNlcyh0aGlzLmlkKTtcbiAgICBhd2FpdCB0aGlzLl9tZXRhdHJhZGVyQWNjb3VudENsaWVudC5kZWxldGVBY2NvdW50KHRoaXMuaWQpO1xuICAgIGNvbnN0IGZpbGVNYW5hZ2VyID0gSGlzdG9yeURhdGFiYXNlLmdldEluc3RhbmNlKCk7XG4gICAgYXdhaXQgZmlsZU1hbmFnZXIuY2xlYXIodGhpcy5pZCwgdGhpcy5fYXBwbGljYXRpb24pO1xuICAgIGlmICgodGhpcy50eXBlIGFzIGFueSkgIT09ICdzZWxmLWhvc3RlZCcpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHRoaXMucmVsb2FkKCk7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgaWYgKGVyci5uYW1lICE9PSAnTm90Rm91bmRFcnJvcicpIHtcbiAgICAgICAgICB0aHJvdyBlcnI7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogU3RhcnRzIEFQSSBzZXJ2ZXIgYW5kIHRyYWRpbmcgdGVybWluYWwgZm9yIHRyYWRpbmcgYWNjb3VudC5cbiAgICogVGhpcyByZXF1ZXN0IHdpbGwgYmUgaWdub3JlZCBpZiB0aGUgYWNjb3VudCBpcyBhbHJlYWR5IGRlcGxveWVkLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZX0gcHJvbWlzZSByZXNvbHZpbmcgd2hlbiBhY2NvdW50IGlzIHNjaGVkdWxlZCBmb3IgZGVwbG95bWVudFxuICAgKi9cbiAgYXN5bmMgZGVwbG95KCkge1xuICAgIGF3YWl0IHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50LmRlcGxveUFjY291bnQodGhpcy5pZCk7XG4gICAgYXdhaXQgdGhpcy5yZWxvYWQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdG9wcyBBUEkgc2VydmVyIGFuZCB0cmFkaW5nIHRlcm1pbmFsIGZvciB0cmFkaW5nIGFjY291bnQuXG4gICAqIFRoaXMgcmVxdWVzdCB3aWxsIGJlIGlnbm9yZWQgaWYgdHJhZGluZyBhY2NvdW50IGlzIGFscmVhZHkgdW5kZXBsb3llZFxuICAgKiBAcmV0dXJucyB7UHJvbWlzZX0gcHJvbWlzZSByZXNvbHZpbmcgd2hlbiBhY2NvdW50IGlzIHNjaGVkdWxlZCBmb3IgdW5kZXBsb3ltZW50XG4gICAqL1xuICBhc3luYyB1bmRlcGxveSgpIHtcbiAgICB0aGlzLl9jb25uZWN0aW9uUmVnaXN0cnkuY2xvc2VBbGxJbnN0YW5jZXModGhpcy5pZCk7XG4gICAgYXdhaXQgdGhpcy5fbWV0YXRyYWRlckFjY291bnRDbGllbnQudW5kZXBsb3lBY2NvdW50KHRoaXMuaWQpO1xuICAgIGF3YWl0IHRoaXMucmVsb2FkKCk7XG4gIH1cblxuICAvKipcbiAgICogUmVkZXBsb3lzIHRyYWRpbmcgYWNjb3VudC4gVGhpcyBpcyBlcXVpdmFsZW50IHRvIHVuZGVwbG95IGltbWVkaWF0ZWx5IGZvbGxvd2VkIGJ5IGRlcGxveVxuICAgKiBAcmV0dXJucyB7UHJvbWlzZX0gcHJvbWlzZSByZXNvbHZpbmcgd2hlbiBhY2NvdW50IGlzIHNjaGVkdWxlZCBmb3IgcmVkZXBsb3ltZW50XG4gICAqL1xuICBhc3luYyByZWRlcGxveSgpIHtcbiAgICBhd2FpdCB0aGlzLl9tZXRhdHJhZGVyQWNjb3VudENsaWVudC5yZWRlcGxveUFjY291bnQodGhpcy5pZCk7XG4gICAgYXdhaXQgdGhpcy5yZWxvYWQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbmNyZWFzZXMgdHJhZGluZyBhY2NvdW50IHJlbGlhYmlsaXR5IGluIG9yZGVyIHRvIGluY3JlYXNlIHRoZSBleHBlY3RlZCBhY2NvdW50IHVwdGltZS5cbiAgICogVGhlIGFjY291bnQgd2lsbCBiZSB0ZW1wb3Jhcnkgc3RvcHBlZCB0byBwZXJmb3JtIHRoaXMgYWN0aW9uLlxuICAgKiBOb3RlIHRoYXQgaW5jcmVhc2luZyByZWxpYWJpbGl0eSBpcyBhIHBhaWQgb3B0aW9uXG4gICAqIEByZXR1cm5zIHtQcm9taXNlfSBwcm9taXNlIHJlc29sdmluZyB3aGVuIGFjY291bnQgcmVsaWFiaWxpdHkgaXMgaW5jcmVhc2VkXG4gICAqL1xuICBhc3luYyBpbmNyZWFzZVJlbGlhYmlsaXR5KCkge1xuICAgIGF3YWl0IHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50LmluY3JlYXNlUmVsaWFiaWxpdHkodGhpcy5pZCk7XG4gICAgYXdhaXQgdGhpcy5yZWxvYWQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFbmFibGVzIHJpc2sgbWFuYWdlbWVudCBBUEkgZm9yIHRyYWRpbmcgYWNjb3VudC5cbiAgICogVGhlIGFjY291bnQgd2lsbCBiZSB0ZW1wb3Jhcnkgc3RvcHBlZCB0byBwZXJmb3JtIHRoaXMgYWN0aW9uLlxuICAgKiBOb3RlIHRoYXQgcmlzayBtYW5hZ2VtZW50IEFQSSBpcyBhIHBhaWQgb3B0aW9uXG4gICAqIEByZXR1cm5zIHtQcm9taXNlfSBwcm9taXNlIHJlc29sdmluZyB3aGVuIGFjY291bnQgcmlzayBtYW5hZ2VtZW50IGlzIGVuYWJsZWRcbiAgICovXG4gIGFzeW5jIGVuYWJsZVJpc2tNYW5hZ2VtZW50QXBpKCkge1xuICAgIGF3YWl0IHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50LmVuYWJsZVJpc2tNYW5hZ2VtZW50QXBpKHRoaXMuaWQpO1xuICAgIGF3YWl0IHRoaXMucmVsb2FkKCk7XG4gIH1cblxuICAvKipcbiAgICogRW5hYmxlcyBjb3B5IGZhY3RvcnkgQVBJIGZvciB0cmFkaW5nIGFjY291bnQuXG4gICAqIFRoZSBhY2NvdW50IHdpbGwgYmUgd