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)
575 lines • 80 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "default", {
enumerable: true,
get: function() {
return _default;
}
});
const _timeoutError = /*#__PURE__*/ _interop_require_default(require("../clients/timeoutError"));
const _expertAdvisor = /*#__PURE__*/ _interop_require_default(require("./expertAdvisor"));
const _errorHandler = require("../clients/errorHandler");
const _metatraderAccountReplica = /*#__PURE__*/ _interop_require_default(require("./metatraderAccountReplica"));
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
};
}
const HistoryDatabase = require("./historyDatabase/index").default;
/**
* 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
*/ async reload() {
this._data = await 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.default(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
*/ async remove() {
this._connectionRegistry.closeAllInstances(this.id);
await this._metatraderAccountClient.deleteAccount(this.id);
const fileManager = HistoryDatabase.getInstance();
await fileManager.clear(this.id, this._application);
if (this.type !== "self-hosted") {
try {
await 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
*/ async deploy() {
await this._metatraderAccountClient.deployAccount(this.id);
await 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
*/ async undeploy() {
this._connectionRegistry.closeAllInstances(this.id);
await this._metatraderAccountClient.undeployAccount(this.id);
await this.reload();
}
/**
* Redeploys trading account. This is equivalent to undeploy immediately followed by deploy
* @returns {Promise} promise resolving when account is scheduled for redeployment
*/ async redeploy() {
await this._metatraderAccountClient.redeployAccount(this.id);
await 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
*/ async increaseReliability() {
await this._metatraderAccountClient.increaseReliability(this.id);
await 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
*/ async enableRiskManagementApi() {
await this._metatraderAccountClient.enableRiskManagementApi(this.id);
await 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
*/ async enableCopyFactoryApi(copyFactoryRoles, copyFactoryResourceSlots) {
await this._metatraderAccountClient.enableCopyFactoryApi(this.id, copyFactoryRoles, copyFactoryResourceSlots);
await 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
*/ async enableMetaStatsApi() {
await this._metatraderAccountClient.enableMetaStatsApi(this.id);
await 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
*/ async waitDeployed(timeoutInSeconds = 300, intervalInMilliseconds = 1000) {
let startTime = Date.now();
await this.reload();
while(this.state !== "DEPLOYED" && startTime + timeoutInSeconds * 1000 > Date.now()){
await this._delay(intervalInMilliseconds);
await this.reload();
}
if (this.state !== "DEPLOYED") {
throw new _timeoutError.default("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
*/ async waitUndeployed(timeoutInSeconds = 300, intervalInMilliseconds = 1000) {
let startTime = Date.now();
await this.reload();
while(this.state !== "UNDEPLOYED" && startTime + timeoutInSeconds * 1000 > Date.now()){
await this._delay(intervalInMilliseconds);
await this.reload();
}
if (this.state !== "UNDEPLOYED") {
throw new _timeoutError.default("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
*/ async waitRemoved(timeoutInSeconds = 300, intervalInMilliseconds = 1000) {
let startTime = Date.now();
try {
await this.reload();
while(startTime + timeoutInSeconds * 1000 > Date.now()){
await this._delay(intervalInMilliseconds);
await this.reload();
}
throw new _timeoutError.default("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
*/ async waitConnected(timeoutInSeconds = 300, intervalInMilliseconds = 1000) {
const checkConnected = ()=>{
return [
this.connectionStatus
].concat(this.replicas.map((replica)=>replica.connectionStatus)).includes("CONNECTED");
};
let startTime = Date.now();
await this.reload();
while(!checkConnected() && startTime + timeoutInSeconds * 1000 > Date.now()){
await this._delay(intervalInMilliseconds);
await this.reload();
}
if (!checkConnected()) {
throw new _timeoutError.default("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 _errorHandler.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 _errorHandler.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
*/ async update(account) {
await this._metatraderAccountClient.updateAccount(this.id, account);
await 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
*/ async createReplica(account) {
await this._metatraderAccountClient.createAccountReplica(this.id, account);
await 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
*/ async getExpertAdvisors() {
this._checkExpertAdvisorAllowed();
let expertAdvisors = await this._expertAdvisorClient.getExpertAdvisors(this.id);
return expertAdvisors.map((e)=>new _expertAdvisor.default(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
*/ async getExpertAdvisor(expertId) {
this._checkExpertAdvisorAllowed();
let expertAdvisor = await this._expertAdvisorClient.getExpertAdvisor(this.id, expertId);
return new _expertAdvisor.default(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
*/ async createExpertAdvisor(expertId, expert) {
this._checkExpertAdvisorAllowed();
await 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
*/ async createConfigurationLink(ttlInDays) {
const configurationLink = await this._metatraderAccountClient.createConfigurationLink(this.id, ttlInDays);
return configurationLink;
}
_checkExpertAdvisorAllowed() {
if (this.version !== 4 || this.type !== "cloud-g1") {
throw new _errorHandler.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.default(replica, this, metatraderAccountClient));
}
};
const _default = MetatraderAccount;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCBUaW1lb3V0RXJyb3IgZnJvbSAnLi4vY2xpZW50cy90aW1lb3V0RXJyb3InO1xuaW1wb3J0IFJwY01ldGFBcGlDb25uZWN0aW9uSW5zdGFuY2UgZnJvbSAnLi9ycGNNZXRhQXBpQ29ubmVjdGlvbkluc3RhbmNlJztcbmltcG9ydCBTdHJlYW1pbmdNZXRhQXBpQ29ubmVjdGlvbkluc3RhbmNlIGZyb20gJy4vc3RyZWFtaW5nTWV0YUFwaUNvbm5lY3Rpb25JbnN0YW5jZSc7XG5jb25zdCBIaXN0b3J5RGF0YWJhc2UgPSByZXF1aXJlKCcuL2hpc3RvcnlEYXRhYmFzZS9pbmRleCcpLmRlZmF1bHQ7XG5pbXBvcnQgRXhwZXJ0QWR2aXNvciBmcm9tICcuL2V4cGVydEFkdmlzb3InO1xuaW1wb3J0IHtWYWxpZGF0aW9uRXJyb3J9IGZyb20gJy4uL2NsaWVudHMvZXJyb3JIYW5kbGVyJztcbmltcG9ydCBNZXRhdHJhZGVyQWNjb3VudFJlcGxpY2EgZnJvbSAnLi9tZXRhdHJhZGVyQWNjb3VudFJlcGxpY2EnO1xuLy9lc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbWF4LWxlblxuaW1wb3J0IE1ldGF0cmFkZXJBY2NvdW50Q2xpZW50LCB7XG4gIFJlbGlhYmlsaXR5LCBTdGF0ZSwgVmVyc2lvbiwgQ29ubmVjdGlvblN0YXR1cywgQ29weUZhY3RvcnlSb2xlcywgVHlwZSwgQWNjb3VudENvbm5lY3Rpb24sIENvbmZpZ3VyYXRpb25MaW5rLFxuICBNZXRhdHJhZGVyQWNjb3VudER0bywgRGVkaWNhdGVkSXAsXG4gIE5ld01ldGFUcmFkZXJBY2NvdW50UmVwbGljYUR0byxcbiAgTWV0YXRyYWRlckFjY291bnRVcGRhdGVEdG9cbn0gZnJvbSAnLi4vY2xpZW50cy9tZXRhQXBpL21ldGF0cmFkZXJBY2NvdW50LmNsaWVudCc7XG5pbXBvcnQgdHlwZSBDb25uZWN0aW9uUmVnaXN0cnkgZnJvbSAnLi9jb25uZWN0aW9uUmVnaXN0cnknO1xuaW1wb3J0IHR5cGUgTWV0YUFwaVdlYnNvY2tldENsaWVudCBmcm9tICcuLi9jbGllbnRzL21ldGFBcGkvbWV0YUFwaVdlYnNvY2tldC5jbGllbnQnO1xuaW1wb3J0IHR5cGUgRXhwZXJ0QWR2aXNvckNsaWVudCBmcm9tICcuLi9jbGllbnRzL21ldGFBcGkvZXhwZXJ0QWR2aXNvci5jbGllbnQnO1xuaW1wb3J0IHR5cGUgSGlzdG9yaWNhbE1hcmtldERhdGFDbGllbnQgZnJvbSAnLi4vY2xpZW50cy9tZXRhQXBpL2hpc3RvcmljYWxNYXJrZXREYXRhLmNsaWVudCc7XG5pbXBvcnQgSGlzdG9yeVN0b3JhZ2UgZnJvbSAnLi9oaXN0b3J5U3RvcmFnZSc7XG5pbXBvcnQge05ld0V4cGVydEFkdmlzb3JEdG99IGZyb20gJy4uL2NsaWVudHMvbWV0YUFwaS9leHBlcnRBZHZpc29yLmNsaWVudCc7XG5pbXBvcnQge01ldGF0cmFkZXJDYW5kbGUsIE1ldGF0cmFkZXJUaWNrfSBmcm9tICcuLi9jbGllbnRzL21ldGFBcGkvbWV0YUFwaVdlYnNvY2tldC5jbGllbnQnO1xuXG4vKipcbiAqIEltcGxlbWVudHMgYSBNZXRhVHJhZGVyIGFjY291bnQgZW50aXR5XG4gKi9cbmNsYXNzIE1ldGF0cmFkZXJBY2NvdW50IHtcbiAgXG4gIHByaXZhdGUgX2RhdGE6IGFueTtcbiAgcHJpdmF0ZSBfbWV0YXRyYWRlckFjY291bnRDbGllbnQ6IGFueTtcbiAgcHJpdmF0ZSBfbWV0YUFwaVdlYnNvY2tldENsaWVudDogYW55O1xuICBwcml2YXRlIF9jb25uZWN0aW9uUmVnaXN0cnk6IGFueTtcbiAgcHJpdmF0ZSBfZXhwZXJ0QWR2aXNvckNsaWVudDogYW55O1xuICBwcml2YXRlIF9oaXN0b3JpY2FsTWFya2V0RGF0YUNsaWVudDogYW55O1xuICBwcml2YXRlIF9hcHBsaWNhdGlvbjogYW55O1xuICBwcml2YXRlIF9yZXBsaWNhczogYW55O1xuXG4gIC8qKlxuICAgKiBDb25zdHJ1Y3RzIGEgTWV0YVRyYWRlciBhY2NvdW50IGVudGl0eVxuICAgKiBAcGFyYW0ge01ldGF0cmFkZXJBY2NvdW50RHRvfSBkYXRhIE1ldGFUcmFkZXIgYWNjb3VudCBkYXRhXG4gICAqIEBwYXJhbSB7TWV0YXRyYWRlckFjY291bnRDbGllbnR9IG1ldGF0cmFkZXJBY2NvdW50Q2xpZW50IE1ldGFUcmFkZXIgYWNjb3VudCBSRVNUIEFQSSBjbGllbnRcbiAgICogQHBhcmFtIHtNZXRhQXBpV2Vic29ja2V0Q2xpZW50fSBtZXRhQXBpV2Vic29ja2V0Q2xpZW50IE1ldGFBcGkgd2Vic29ja2V0IGNsaWVudFxuICAgKiBAcGFyYW0ge0Nvbm5lY3Rpb25SZWdpc3RyeX0gY29ubmVjdGlvblJlZ2lzdHJ5IG1ldGF0cmFkZXIgYWNjb3VudCBjb25uZWN0aW9uIHJlZ2lzdHJ5XG4gICAqIEBwYXJhbSB7RXhwZXJ0QWR2aXNvckNsaWVudH0gZXhwZXJ0QWR2aXNvckNsaWVudCBleHBlcnQgYWR2aXNvciBSRVNUIEFQSSBjbGllbnRcbiAgICogQHBhcmFtIHtIaXN0b3JpY2FsTWFya2V0RGF0YUNsaWVudH0gaGlzdG9yaWNhbE1hcmtldERhdGFDbGllbnQgaGlzdG9yaWNhbCBtYXJrZXQgZGF0YSBIVFRQIEFQSSBjbGllbnRcbiAgICogQHBhcmFtIHtzdHJpbmd9IGFwcGxpY2F0aW9uIGFwcGxpY2F0aW9uIG5hbWVcbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgIGRhdGE6IE1ldGF0cmFkZXJBY2NvdW50RHRvLCBtZXRhdHJhZGVyQWNjb3VudENsaWVudDogTWV0YXRyYWRlckFjY291bnRDbGllbnQsXG4gICAgbWV0YUFwaVdlYnNvY2tldENsaWVudDogTWV0YUFwaVdlYnNvY2tldENsaWVudCwgY29ubmVjdGlvblJlZ2lzdHJ5OiBDb25uZWN0aW9uUmVnaXN0cnksXG4gICAgZXhwZXJ0QWR2aXNvckNsaWVudDogRXhwZXJ0QWR2aXNvckNsaWVudCwgaGlzdG9yaWNhbE1hcmtldERhdGFDbGllbnQ6IEhpc3RvcmljYWxNYXJrZXREYXRhQ2xpZW50LFxuICAgIGFwcGxpY2F0aW9uOiBzdHJpbmdcbiAgKSB7XG4gICAgdGhpcy5fZGF0YSA9IGRhdGE7XG4gICAgdGhpcy5fbWV0YXRyYWRlckFjY291bnRDbGllbnQgPSBtZXRhdHJhZGVyQWNjb3VudENsaWVudDtcbiAgICB0aGlzLl9tZXRhQXBpV2Vic29ja2V0Q2xpZW50ID0gbWV0YUFwaVdlYnNvY2tldENsaWVudDtcbiAgICB0aGlzLl9jb25uZWN0aW9uUmVnaXN0cnkgPSBjb25uZWN0aW9uUmVnaXN0cnk7XG4gICAgdGhpcy5fZXhwZXJ0QWR2aXNvckNsaWVudCA9IGV4cGVydEFkdmlzb3JDbGllbnQ7XG4gICAgdGhpcy5faGlzdG9yaWNhbE1hcmtldERhdGFDbGllbnQgPSBoaXN0b3JpY2FsTWFya2V0RGF0YUNsaWVudDtcbiAgICB0aGlzLl9hcHBsaWNhdGlvbiA9IGFwcGxpY2F0aW9uO1xuICAgIHRoaXMuX3JlcGxpY2FzID0gKGRhdGEuYWNjb3VudFJlcGxpY2FzIHx8IFtdKVxuICAgICAgLm1hcChyZXBsaWNhID0+IG5ldyBNZXRhdHJhZGVyQWNjb3VudFJlcGxpY2EocmVwbGljYSwgdGhpcywgbWV0YXRyYWRlckFjY291bnRDbGllbnQpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHVuaXF1ZSBhY2NvdW50IGlkXG4gICAqIEByZXR1cm4ge3N0cmluZ30gdW5pcXVlIGFjY291bnQgaWRcbiAgICovXG4gIGdldCBpZCgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLl9pZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGN1cnJlbnQgYWNjb3VudCBzdGF0ZS4gT25lIG9mIENSRUFURUQsIERFUExPWUlORywgREVQTE9ZRUQsIERFUExPWV9GQUlMRUQsIFVOREVQTE9ZSU5HLFxuICAgKiBVTkRFUExPWUVELCBVTkRFUExPWV9GQUlMRUQsIERFTEVUSU5HLCBERUxFVEVfRkFJTEVELCBSRURFUExPWV9GQUlMRUQsIERSQUZUXG4gICAqIEByZXR1cm4ge1N0YXRlfSBjdXJyZW50IGFjY291bnQgc3RhdGVcbiAgICovXG4gIGdldCBzdGF0ZSgpOiBTdGF0ZSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuc3RhdGU7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBNZXRhVHJhZGVyIG1hZ2ljIHRvIHBsYWNlIHRyYWRlcyB1c2luZ1xuICAgKiBAcmV0dXJuIHtudW1iZXJ9IE1ldGFUcmFkZXIgbWFnaWMgdG8gcGxhY2UgdHJhZGVzIHVzaW5nXG4gICAqL1xuICBnZXQgbWFnaWMoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5tYWdpYztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRlcm1pbmFsICYgYnJva2VyIGNvbm5lY3Rpb24gc3RhdHVzLCBvbmUgb2YgQ09OTkVDVEVELCBESVNDT05ORUNURUQsIERJU0NPTk5FQ1RFRF9GUk9NX0JST0tFUlxuICAgKiBAcmV0dXJuIHtDb25uZWN0aW9uU3RhdHVzfSB0ZXJtaW5hbCAmIGJyb2tlciBjb25uZWN0aW9uIHN0YXR1c1xuICAgKi9cbiAgZ2V0IGNvbm5lY3Rpb25TdGF0dXMoKTogQ29ubmVjdGlvblN0YXR1cyB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuY29ubmVjdGlvblN0YXR1cztcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJldHVybnMgcXVvdGUgc3RyZWFtaW5nIGludGVydmFsIGluIHNlY29uZHMgXG4gICAqIEByZXR1cm4ge251bWJlcn0gcXVvdGUgc3RyZWFtaW5nIGludGVydmFsIGluIHNlY29uZHNcbiAgICovXG4gIGdldCBxdW90ZVN0cmVhbWluZ0ludGVydmFsSW5TZWNvbmRzKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEucXVvdGVTdHJlYW1pbmdJbnRlcnZhbEluU2Vjb25kcztcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJldHVybnMgc3ltYm9sIHByb3ZpZGVkIGJ5IGJyb2tlciBcbiAgICogQHJldHVybiB7c3RyaW5nfSBhbnkgc3ltYm9sIHByb3ZpZGVkIGJ5IGJyb2tlclxuICAgKi9cbiAgZ2V0IHN5bWJvbCgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnN5bWJvbDtcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJldHVybnMgcmVsaWFiaWxpdHkgdmFsdWUuIFBvc3NpYmxlIHZhbHVlcyBhcmUgcmVndWxhciBhbmQgaGlnaFxuICAgKiBAcmV0dXJuIHtSZWxpYWJpbGl0eX0gYWNjb3VudCByZWxpYWJpbGl0eSB2YWx1ZVxuICAgKi9cbiAgZ2V0IHJlbGlhYmlsaXR5KCk6IFJlbGlhYmlsaXR5IHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5yZWxpYWJpbGl0eTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJldHVybnMgdXNlci1kZWZpbmVkIGFjY291bnQgdGFnc1xuICAgKiBAcmV0dXJuIHtBcnJheTxzdHJpbmc+fSB1c2VyLWRlZmluZWQgYWNjb3VudCB0YWdzXG4gICAqL1xuICBnZXQgdGFncygpOiBBcnJheTxzdHJpbmc+IHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS50YWdzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgZXh0cmEgaW5mb3JtYXRpb24gd2hpY2ggY2FuIGJlIHN0b3JlZCB0b2dldGhlciB3aXRoIHlvdXIgYWNjb3VudFxuICAgKiBAcmV0dXJuIHtPYmplY3R9IGV4dHJhIGluZm9ybWF0aW9uIHdoaWNoIGNhbiBiZSBzdG9yZWQgdG9nZXRoZXIgd2l0aCB5b3VyIGFjY291bnRcbiAgICovXG4gIGdldCBtZXRhZGF0YSgpOiBPYmplY3Qge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLm1ldGFkYXRhO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgbnVtYmVyIG9mIHJlc291cmNlIHNsb3RzIHRvIGFsbG9jYXRlIHRvIGFjY291bnQuIEFsbG9jYXRpbmcgZXh0cmEgcmVzb3VyY2Ugc2xvdHNcbiAgICogcmVzdWx0cyBpbiBiZXR0ZXIgYWNjb3VudCBwZXJmb3JtYW5jZSB1bmRlciBsb2FkIHdoaWNoIGlzIHVzZWZ1bCBmb3Igc29tZSBhcHBsaWNhdGlvbnMuIEUuZy4gaWYgeW91IGhhdmUgbWFueVxuICAgKiBhY2NvdW50cyBjb3B5aW5nIHRoZSBzYW1lIHN0cmF0ZWd5IHZpYSBDb3B5RmFjdG9yeSBBUEksIHRoZW4geW91IGNhbiBpbmNyZWFzZSByZXNvdXJjZVNsb3RzIHRvIGdldCBhIGxvd2VyIHRyYWRlXG4gICAqIGNvcHlpbmcgbGF0ZW5jeS4gUGxlYXNlIG5vdGUgdGhhdCBhbGxvY2F0aW5nIGV4dHJhIHJlc291cmNlIHNsb3RzIGlzIGEgcGFpZCBvcHRpb24uIFBsZWFzZSBub3RlIHRoYXQgaGlnaFxuICAgKiByZWxpYWJpbGl0eSBhY2NvdW50cyB1c2UgcmVkdW5kYW50IGluZnJhc3RydWN0dXJlLCBzbyB0aGF0IGVhY2ggcmVzb3VyY2Ugc2xvdCBmb3IgYSBoaWdoIHJlbGlhYmlsaXR5IGFjY291bnRcbiAgICogaXMgYmlsbGVkIGFzIDIgc3RhbmRhcmQgcmVzb3VyY2Ugc2xvdHMuXG4gICAqIEByZXR1cm4ge251bWJlcn0gbnVtYmVyIG9mIHJlc291cmNlIHNsb3RzIHRvIGFsbG9jYXRlIHRvIGFjY291bnRcbiAgICovXG4gIGdldCByZXNvdXJjZVNsb3RzKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEucmVzb3VyY2VTbG90cztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBudW1iZXIgb2YgQ29weUZhY3RvcnkgMiByZXNvdXJjZSBzbG90cyB0byBhbGxvY2F0ZSB0byBhY2NvdW50LlxuICAgKiBBbGxvY2F0aW5nIGV4dHJhIHJlc291cmNlIHNsb3RzIHJlc3VsdHMgaW4gbG93ZXIgdHJhZGUgY29weWluZyBsYXRlbmN5LiBQbGVhc2Ugbm90ZSB0aGF0IGFsbG9jYXRpbmcgZXh0cmEgcmVzb3VyY2VcbiAgICogc2xvdHMgaXMgYSBwYWlkIG9wdGlvbi4gUGxlYXNlIGFsc28gbm90ZSB0aGF0IENvcHlGYWN0b3J5IDIgdXNlcyByZWR1bmRhbnQgaW5mcmFzdHJ1Y3R1cmUgc28gdGhhdFxuICAgKiBlYWNoIENvcHlGYWN0b3J5IHJlc291cmNlIHNsb3QgaXMgYmlsbGVkIGFzIDIgc3RhbmRhcmQgcmVzb3VyY2Ugc2xvdHMuIFlvdSB3aWxsIGJlIGJpbGxlZCBmb3IgQ29weUZhY3RvcnkgMlxuICAgKiByZXNvdXJjZSBzbG90cyBvbmx5IGlmIHlvdSBoYXZlIGFkZGVkIHlvdXIgYWNjb3VudCB0byBDb3B5RmFjdG9yeSAyIGJ5IHNwZWNpZnlpbmcgY29weUZhY3RvcnlSb2xlcyBmaWVsZC5cbiAgICogQHJldHVybiB7bnVtYmVyfSBudW1iZXIgb2YgQ29weUZhY3RvcnkgMiByZXNvdXJjZSBzbG90cyB0byBhbGxvY2F0ZSB0byBhY2NvdW50XG4gICAqL1xuICBnZXQgY29weUZhY3RvcnlSZXNvdXJjZVNsb3RzKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuY29weUZhY3RvcnlSZXNvdXJjZVNsb3RzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYWNjb3VudCByZWdpb25cbiAgICogQHJldHVybiB7c3RyaW5nfSBhY2NvdW50IHJlZ2lvbiB2YWx1ZVxuICAgKi9cbiAgZ2V0IHJlZ2lvbigpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnJlZ2lvbjtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSB0aW1lIGFjY291bnQgd2FzIGNyZWF0ZWQgYXQsIGluIElTTyBmb3JtYXRcbiAgICogQHJldHVybnMge3N0cmluZ30gdGhlIHRpbWUgYWNjb3VudCB3YXMgY3JlYXRlZCBhdCwgaW4gSVNPIGZvcm1hdFxuICAgKi9cbiAgZ2V0IGNyZWF0ZWRBdCgpOiBEYXRlIHtcbiAgICByZXR1cm4gbmV3IERhdGUodGhpcy5fZGF0YS5jcmVhdGVkQXQpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgaHVtYW4tcmVhZGFibGUgYWNjb3VudCBuYW1lXG4gICAqIEByZXR1cm4ge3N0cmluZ30gaHVtYW4tcmVhZGFibGUgYWNjb3VudCBuYW1lXG4gICAqL1xuICBnZXQgbmFtZSgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLm5hbWU7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBSZXR1cm5zIGZsYWcgaW5kaWNhdGluZyBpZiB0cmFkZXMgc2hvdWxkIGJlIHBsYWNlZCBhcyBtYW51YWwgdHJhZGVzIG9uIHRoaXMgYWNjb3VudFxuICAgKiBAcmV0dXJuIHtib29sZWFufSBmbGFnIGluZGljYXRpbmcgaWYgdHJhZGVzIHNob3VsZCBiZSBwbGFjZWQgYXMgbWFudWFsIHRyYWRlcyBvbiB0aGlzIGFjY291bnRcbiAgICovXG4gIGdldCBtYW51YWxUcmFkZXMoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEubWFudWFsVHJhZGVzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgZGVmYXVsdCB0cmFkZSBzbGlwcGFnZSBpbiBwb2ludHNcbiAgICogQHJldHVybiB7bnVtYmVyfSBkZWZhdWx0IHRyYWRlIHNsaXBwYWdlIGluIHBvaW50c1xuICAgKi9cbiAgZ2V0IHNsaXBwYWdlKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuc2xpcHBhZ2U7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBSZXR1cm5zIGlkIG9mIHRoZSBhY2NvdW50J3MgcHJvdmlzaW9uaW5nIHByb2ZpbGVcbiAgICogQHJldHVybiB7c3RyaW5nfSBpZCBvZiB0aGUgYWNjb3VudCdzIHByb3Zpc2lvbmluZyBwcm9maWxlXG4gICAqL1xuICBnZXQgcHJvdmlzaW9uaW5nUHJvZmlsZUlkKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEucHJvdmlzaW9uaW5nUHJvZmlsZUlkO1xuICB9XG4gIFxuICAvKipcbiAgICogUmV0dXJucyBNZXRhVHJhZGVyIGFjY291bnQgbG9naW5cbiAgICogQHJldHVybiB7c3RyaW5nfSBNZXRhVHJhZGVyIGFjY291bnQgbnVtYmVyXG4gICAqL1xuICBnZXQgbG9naW4oKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5sb2dpbjtcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJldHVybnMgTWV0YVRyYWRlciBzZXJ2ZXIgbmFtZSB0byBjb25uZWN0IHRvXG4gICAqIEByZXR1cm4ge3N0cmluZ30gTWV0YVRyYWRlciBzZXJ2ZXIgbmFtZSB0byBjb25uZWN0IHRvXG4gICAqL1xuICBnZXQgc2VydmVyKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuc2VydmVyO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYWNjb3VudCB0eXBlLiBQb3NzaWJsZSB2YWx1ZXMgYXJlIGNsb3VkLWcxLCBjbG91ZC1nMlxuICAgKiBAcmV0dXJuIHtUeXBlfSBhY2NvdW50IHR5cGVcbiAgICovXG4gIGdldCB0eXBlKCk6IFR5cGUge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnR5cGU7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBNVCB2ZXJzaW9uLiBQb3NzaWJsZSB2YWx1ZXMgYXJlIDQgYW5kIDVcbiAgICogQHJldHVybiB7VmVyc2lvbn0gTVQgdmVyc2lvblxuICAgKi9cbiAgZ2V0IHZlcnNpb24oKTogVmVyc2lvbiB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEudmVyc2lvbjtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGhhc2gtY29kZSBvZiB0aGUgYWNjb3VudFxuICAgKiBAcmV0dXJuIHtudW1iZXJ9IGhhc2gtY29kZSBvZiB0aGUgYWNjb3VudFxuICAgKi9cbiAgZ2V0IGhhc2goKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5oYXNoO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgMy1jaGFyYWN0ZXIgSVNPIGN1cnJlbmN5IGNvZGUgb2YgdGhlIGFjY291bnQgYmFzZSBjdXJyZW5jeS4gVGhlIHNldHRpbmcgaXMgdG8gYmUgdXNlZFxuICAgKiBmb3IgY29weSB0cmFkaW5nIGFjY291bnRzIHdoaWNoIHVzZSBuYXRpb25hbCBjdXJyZW5jaWVzIG9ubHksIHN1Y2ggYXMgc29tZSBCcmF6aWxpYW4gYnJva2Vycy4gWW91IHNob3VsZCBub3QgYWx0ZXJcbiAgICogdGhpcyBzZXR0aW5nIHVubGVzcyB5b3UgdW5kZXJzdGFuZCB3aGF0IHlvdSBhcmUgZG9pbmcuXG4gICAqIEByZXR1cm4ge3N0cmluZ30gMy1jaGFyYWN0ZXIgSVNPIGN1cnJlbmN5IGNvZGUgb2YgdGhlIGFjY291bnQgYmFzZSBjdXJyZW5jeVxuICAgKi9cbiAgZ2V0IGJhc2VDdXJyZW5jeSgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLmJhc2VDdXJyZW5jeTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGFjY291bnQgcm9sZXMgZm9yIENvcHlGYWN0b3J5MiBhcHBsaWNhdGlvbi4gUG9zc2libGUgdmFsdWVzIGFyZSBgUFJPVklERVJgIGFuZCBgU1VCU0NSSUJFUmBcbiAgICogQHJldHVybiB7QXJyYXk8Q29weUZhY3RvcnlSb2xlcz59IGFjY291bnQgcm9sZXMgZm9yIENvcHlGYWN0b3J5MiBhcHBsaWNhdGlvblxuICAgKi9cbiAgZ2V0IGNvcHlGYWN0b3J5Um9sZXMoKTogQXJyYXk8Q29weUZhY3RvcnlSb2xlcz4ge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLmNvcHlGYWN0b3J5Um9sZXM7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBSZXR1cm5zIGZsYWcgaW5kaWNhdGluZyB0aGF0IHJpc2sgbWFuYWdlbWVudCBBUEkgaXMgZW5hYmxlZCBvbiBhY2NvdW50XG4gICAqIEByZXR1cm4ge2Jvb2xlYW59IGZsYWcgaW5kaWNhdGluZyB0aGF0IHJpc2sgbWFuYWdlbWVudCBBUEkgaXMgZW5hYmxlZCBvbiBhY2NvdW50XG4gICAqL1xuICBnZXQgcmlza01hbmFnZW1lbnRBcGlFbmFibGVkKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnJpc2tNYW5hZ2VtZW50QXBpRW5hYmxlZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGZsYWcgaW5kaWNhdGluZyB0aGF0IE1ldGFTdGF0cyBBUEkgaXMgZW5hYmxlZCBvbiBhY2NvdW50XG4gICAqIEByZXR1cm4ge2Jvb2xlYW59IGZsYWcgaW5kaWNhdGluZyB0aGF0IE1ldGFTdGF0cyBBUEkgaXMgZW5hYmxlZCBvbiBhY2NvdW50XG4gICAqL1xuICBnZXQgbWV0YXN0YXRzQXBpRW5hYmxlZCgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5tZXRhc3RhdHNBcGlFbmFibGVkO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgY29uZmlndXJlZCBkZWRpY2F0ZWQgSVAgcHJvdG9jb2wgdG8gY29ubmVjdCB0byB0aGUgdHJhZGluZyBhY2NvdW50IHRlcm1pbmFsXG4gICAqIEByZXR1cm4ge0RlZGljYXRlZElwfVxuICAgKi9cbiAgZ2V0IGFsbG9jYXRlRGVkaWNhdGVkSXAoKTogRGVkaWNhdGVkSXAge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLmFsbG9jYXRlRGVkaWNhdGVkSXA7XG4gIH1cbiAgICBcbiAgLyoqXG4gICAqIFJldHVybnMgYWN0aXZlIGFjY291bnQgY29ubmVjdGlvbnNcbiAgICogQHJldHVybiB7QXJyYXk8QWNjb3VudENvbm5lY3Rpb24+fSBhY3RpdmUgYWNjb3VudCBjb25uZWN0aW9uc1xuICAgKi9cbiAgZ2V0IGNvbm5lY3Rpb25zKCk6IEFycmF5PEFjY291bnRDb25uZWN0aW9uPiB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuY29ubmVjdGlvbnM7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBmbGFnIGluZGljYXRpbmcgdGhhdCBhY2NvdW50IGlzIHByaW1hcnlcbiAgICogQHJldHVybiB7Ym9vbGVhbn0gZmxhZyBpbmRpY2F0aW5nIHRoYXQgYWNjb3VudCBpcyBwcmltYXJ5XG4gICAqL1xuICBnZXQgcHJpbWFyeVJlcGxpY2EoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEucHJpbWFyeVJlcGxpY2E7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB1c2VyIGlkXG4gICAqIEByZXR1cm4ge3N0cmluZ30gdXNlciBpZFxuICAgKi9cbiAgZ2V0IHVzZXJJZCgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnVzZXJJZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHByaW1hcnkgYWNjb3VudCBpZFxuICAgKiBAcmV0dXJuIHtzdHJpbmd9IHByaW1hcnkgYWNjb3VudCBpZFxuICAgKi9cbiAgZ2V0IHByaW1hcnlBY2NvdW50SWQoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5wcmltYXJ5QWNjb3VudElkO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYWNjb3VudCByZXBsaWNhcyBmcm9tIERUT1xuICAgKiBAcmV0dXJuIHtNZXRhdHJhZGVyQWNjb3VudFJlcGxpY2FbXX0gYWNjb3VudCByZXBsaWNhcyBmcm9tIERUT1xuICAgKi9cbiAgZ2V0IGFjY291bnRSZXBsaWNhcygpOiBNZXRhdHJhZGVyQWNjb3VudFJlcGxpY2FbXSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuYWNjb3VudFJlcGxpY2FzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYWNjb3VudCByZXBsaWNhIGluc3RhbmNlc1xuICAgKiBAcmV0dXJuIHtNZXRhdHJhZGVyQWNjb3VudFJlcGxpY2FbXX0gYWNjb3VudCByZXBsaWNhIGluc3RhbmNlc1xuICAgKi9cbiAgZ2V0IHJlcGxpY2FzKCk6IE1ldGF0cmFkZXJBY2NvdW50UmVwbGljYVtdIHtcbiAgICByZXR1cm4gdGhpcy5fcmVwbGljYXM7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBhIGRpY3Rpb25hcnkgd2l0aCBhY2NvdW50J3MgYXZhaWxhYmxlIHJlZ2lvbnMgYW5kIHJlcGxpY2FzXG4gICAqIEByZXR1cm5zIGFjY291bnRzIGJ5IHJlZ2lvblxuICAgKi9cbiAgZ2V0IGFjY291bnRSZWdpb25zKCk6IE1ldGF0cmFkZXJBY2NvdW50LkFjY291bnRzQnlSZWdpb24ge1xuICAgIGNvbnN0IHJlZ2lvbnMgPSB7W3RoaXMucmVnaW9uXTogdGhpcy5pZH07XG4gICAgdGhpcy5yZXBsaWNhcy5mb3JFYWNoKHJlcGxpY2EgPT4gcmVnaW9uc1tyZXBsaWNhLnJlZ2lvbl0gPSByZXBsaWNhLmlkKTtcbiAgICByZXR1cm4gcmVnaW9ucztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWxvYWRzIE1ldGFUcmFkZXIgYWNjb3VudCBmcm9tIEFQSVxuICAgKiBAcmV0dXJuIHtQcm9taXNlfSBwcm9taXNlIHJlc29sdmluZyB3aGVuIE1ldGFUcmFkZXIgYWNjb3VudCBpcyB1cGRhdGVkXG4gICAqL1xuICBhc3luYyByZWxvYWQoKSB7XG4gICAgdGhpcy5fZGF0YSA9IGF3YWl0IHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50LmdldEFjY291bnQodGhpcy5pZCk7XG4gICAgY29uc3QgdXBkYXRlZFJlcGxpY2FEYXRhID0gKHRoaXMuX2RhdGEuYWNjb3VudFJlcGxpY2FzIHx8IFtdKTtcbiAgICBjb25zdCByZWdpb25zID0gdXBkYXRlZFJlcGxpY2FEYXRhLm1hcChyZXBsaWNhID0+IHJlcGxpY2EucmVnaW9uKTtcbiAgICBjb25zdCBjcmVhdGVkUmVwbGljYVJlZ2lvbnMgPSB0aGlzLl9yZXBsaWNhcy5tYXAocmVwbGljYSA9PiByZXBsaWNhLnJlZ2lvbik7XG4gICAgdGhpcy5fcmVwbGljYXMgPSB0aGlzLl9yZXBsaWNhcy5maWx0ZXIocmVwbGljYSA9PiByZWdpb25zLmluY2x1ZGVzKHJlcGxpY2EucmVnaW9uKSk7XG4gICAgdGhpcy5fcmVwbGljYXMuZm9yRWFjaChyZXBsaWNhID0+IHtcbiAgICAgIGNvbnN0IHVwZGF0ZWREYXRhID0gdXBkYXRlZFJlcGxpY2FEYXRhLmZpbmQocmVwbGljYURhdGEgPT4gcmVwbGljYURhdGEucmVnaW9uID09PSByZXBsaWNhLnJlZ2lvbik7XG4gICAgICByZXBsaWNhLnVwZGF0ZURhdGEodXBkYXRlZERhdGEpO1xuICAgIH0pO1xuICAgIHVwZGF0ZWRSZXBsaWNhRGF0YS5mb3JFYWNoKHJlcGxpY2EgPT4ge1xuICAgICAgaWYoIWNyZWF0ZWRSZXBsaWNhUmVnaW9ucy5pbmNsdWRlcyhyZXBsaWNhLnJlZ2lvbikpIHtcbiAgICAgICAgdGhpcy5fcmVwbGljYXMucHVzaChuZXcgTWV0YXRyYWRlckFjY291bnRSZXBsaWNhKHJlcGxpY2EsIHRoaXMsIHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50KSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyBhIHRyYWRpbmcgYWNjb3VudCBhbmQgc3RvcHMgdGhlIEFQSSBzZXJ2ZXIgc2VydmluZyB0aGUgYWNjb3VudC5cbiAgICogVGhlIGFjY291bnQgc3RhdGUgc3VjaCBhcyBkb3dubG9hZGVkIG1hcmtldCBkYXRhIGhpc3Rvcnkgd2lsbCBiZSByZW1vdmVkIGFzIHdlbGwgd2hlbiB5b3UgcmVtb3ZlIHRoZSBhY2NvdW50LlxuICAgKiBAcmV0dXJuIHtQcm9taXNlfSBwcm9taXNlIHJlc29sdmluZyB3aGVuIGFjY291bnQgaXMgc2NoZWR1bGVkIGZvciBkZWxldGlvblxuICAgKi9cbiAgYXN5bmMgcmVtb3ZlKCkge1xuICAgIHRoaXMuX2Nvbm5lY3Rpb25SZWdpc3RyeS5jbG9zZUFsbEluc3RhbmNlcyh0aGlzLmlkKTtcbiAgICBhd2FpdCB0aGlzLl9tZXRhdHJhZGVyQWNjb3VudENsaWVudC5kZWxldGVBY2NvdW50KHRoaXMuaWQpO1xuICAgIGNvbnN0IGZpbGVNYW5hZ2VyID0gSGlzdG9yeURhdGFiYXNlLmdldEluc3RhbmNlKCk7XG4gICAgYXdhaXQgZmlsZU1hbmFnZXIuY2xlYXIodGhpcy5pZCwgdGhpcy5fYXBwbGljYXRpb24pO1xuICAgIGlmICgodGhpcy50eXBlIGFzIGFueSkgIT09ICdzZWxmLWhvc3RlZCcpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHRoaXMucmVsb2FkKCk7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgaWYgKGVyci5uYW1lICE9PSAnTm90Rm91bmRFcnJvcicpIHtcbiAgICAgICAgICB0aHJvdyBlcnI7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogU3RhcnRzIEFQSSBzZXJ2ZXIgYW5kIHRyYWRpbmcgdGVybWluYWwgZm9yIHRyYWRpbmcgYWNjb3VudC5cbiAgICogVGhpcyByZXF1ZXN0IHdpbGwgYmUgaWdub3JlZCBpZiB0aGUgYWNjb3VudCBpcyBhbHJlYWR5IGRlcGxveWVkLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZX0gcHJvbWlzZSByZXNvbHZpbmcgd2hlbiBhY2NvdW50IGlzIHNjaGVkdWxlZCBmb3IgZGVwbG95bWVudFxuICAgKi9cbiAgYXN5bmMgZGVwbG95KCkge1xuICAgIGF3YWl0IHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50LmRlcGxveUFjY291bnQodGhpcy5pZCk7XG4gICAgYXdhaXQgdGhpcy5yZWxvYWQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdG9wcyBBUEkgc2VydmVyIGFuZCB0cmFkaW5nIHRlcm1pbmFsIGZvciB0cmFkaW5nIGFjY291bnQuXG4gICAqIFRoaXMgcmVxdWVzdCB3aWxsIGJlIGlnbm9yZWQgaWYgdHJhZGluZyBhY2NvdW50IGlzIGFscmVhZHkgdW5kZXBsb3llZFxuICAgKiBAcmV0dXJucyB7UHJvbWlzZX0gcHJvbWlzZSByZXNvbHZpbmcgd2hlbiBhY2NvdW50IGlzIHNjaGVkdWxlZCBmb3IgdW5kZXBsb3ltZW50XG4gICAqL1xuICBhc3luYyB1bmRlcGxveSgpIHtcbiAgICB0aGlzLl9jb25uZWN0aW9uUmVnaXN0cnkuY2xvc2VBbGxJbnN0YW5jZXModGhpcy5pZCk7XG4gICAgYXdhaXQgdGhpcy5fbWV0YXRyYWRlckFjY291bnRDbGllbnQudW5kZXBsb3lBY2NvdW50KHRoaXMuaWQpO1xuICAgIGF3YWl0IHRoaXMucmVsb2FkKCk7XG4gIH1cblxuICAvKipcbiAgICogUmVkZXBsb3lzIHRyYWRpbmcgYWNjb3VudC4gVGhpcyBpcyBlcXVpdmFsZW50IHRvIHVuZGVwbG95IGltbWVkaWF0ZWx5IGZvbGxvd2VkIGJ5IGRlcGxveVxuICAgKiBAcmV0dXJucyB7UHJvbWlzZX0gcHJvbWlzZSByZXNvbHZpbmcgd2hlbiBhY2NvdW50IGlzIHNjaGVkdWxlZCBmb3IgcmVkZXBsb3ltZW50XG4gICAqL1xuICBhc3luYyByZWRlcGxveSgpIHtcbiAgICBhd2FpdCB0aGlzLl9tZXRhdHJhZGVyQWNjb3VudENsaWVudC5yZWRlcGxveUFjY291bnQodGhpcy5pZCk7XG4gICAgYXdhaXQgdGhpcy5yZWxvYWQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbmNyZWFzZXMgdHJhZGluZyBhY2NvdW50IHJlbGlhYmlsaXR5IGluIG9yZGVyIHRvIGluY3JlYXNlIHRoZSBleHBlY3RlZCBhY2NvdW50IHVwdGltZS5cbiAgICogVGhlIGFjY291bnQgd2lsbCBiZSB0ZW1wb3Jhcnkgc3RvcHBlZCB0byBwZXJmb3JtIHRoaXMgYWN0aW9uLlxuICAgKiBOb3RlIHRoYXQgaW5jcmVhc2luZyByZWxpYWJpbGl0eSBpcyBhIHBhaWQgb3B0aW9uXG4gICAqIEByZXR1cm5zIHtQcm9taXNlfSBwcm9taXNlIHJlc29sdmluZyB3aGVuIGFjY291bnQgcmVsaWFiaWxpdHkgaXMgaW5jcmVhc2VkXG4gICAqL1xuICBhc3luYyBpbmNyZWFzZVJlbGlhYmlsaXR5KCkge1xuICAgIGF3YWl0IHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50LmluY3JlYXNlUmVsaWFiaWxpdHkodGhpcy5pZCk7XG4gICAgYXdhaXQgdGhpcy5yZWxvYWQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFbmFibGVzIHJpc2sgbWFuYWdlbWVudCBBUEkgZm9yIHRyYWRpbmcgYWNjb3VudC5cbiAgICogVGhlIGFjY291bnQgd2lsbCBiZSB0ZW1wb3Jhcnkgc3RvcHBlZCB0byBwZXJmb3JtIHRoaXMgYWN0aW9uLlxuICAgKiBOb3RlIHRoYXQgcmlzayBtYW5hZ2VtZW50IEFQSSBpcyBhIHBhaWQgb3B0aW9uXG4gICAqIEByZXR1cm5zIHtQcm9taXNlfSBwcm9taXNlIHJlc29sdmluZyB3aGVuIGFjY291bnQgcmlzayBtYW5hZ2VtZW50IGlzIGVuYWJsZWRcbiAgICovXG4gIGFzeW5jIGVuYWJsZVJpc2tNYW5hZ2VtZW50QXBpKCkge1xuICAgIGF3YWl0IHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50LmVuYWJsZVJpc2tNYW5hZ2VtZW50QXBpKHRoaXMuaWQpO1xuICAgIGF3YWl0IHRoaXMucmVsb2FkKCk7XG4gIH1cblxuICAvKipcbiAgICogRW5hYmxlcyBjb3B5IGZhY3RvcnkgQVBJIGZvciB0cmFkaW5nIGFjY291bnQuXG4gICAqIFRoZSBhY2NvdW50IHdpbGwgYmUgdGVtcG9yYXJ5IHN0b3BwZWQgdG8gcGVyZm9ybSB0aGlzIGFjdGlvbi5cbiAgICogTm90ZSB0aGF0IGNvcHkgZmFjdG9yeSBBUEkgaXMgYSBwYWlkIG9wdGlvbiAoc2VlXG4gICAqIEBwYXJhbSB7QXJyYXk8Q29weUZhY3RvcnlSb2xlcz59IGNvcHlGYWN0b3J5Um9sZXMgY29weSBmYWN0b3J5IHJvbGVzXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBjb3B5RmFjdG9yeVJlc291cmNlU2xvdHMgY29weSBmYWN0b3J5IHJlc291cmNlIHNsb3RzXG4gICAqIEByZXR1cm4ge1Byb21pc2V9IHByb21pc2UgcmVzb2x2aW5nIHdoZW4gYWNjb3VudCBjb3B5IGZhY3RvcnkgaXMgZW5hYmxlZFxuICAgKi9cbiAgYXN5bmMgZW5hYmxlQ29weUZhY3RvcnlBcGkoY29weUZhY3RvcnlSb2xlczogQXJyYXk8Q29weUZhY3RvcnlSb2xlcz4sIGNvcHlGYWN0b3J5UmVzb3VyY2VTbG90czogbnVtYmVyKSB7XG4gICAgYXdhaXQgdGhpcy5fbWV0YXRyYWRlckFjY291bnRDbGllbnQuZW5hYmxlQ29weUZhY3RvcnlBcGkodGhpcy5pZCwgY29weUZhY3RvcnlSb2xlcywgY29weUZhY3RvcnlSZXNvdXJjZVNsb3RzKTtcbiAgICBhd2FpdCB0aGlzLnJlbG9hZCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEVuYWJsZXMgTWV0YVN0YXRzIEFQSSBmb3IgdHJhZGluZyBhY2NvdW50LlxuICAgKiBUaGUgYWNjb3VudCB3aWxsIGJlIHRlbXBvcmFyeSBzdG9wcGVkIHRvIHBlcmZvcm0gdGhpcyBhY3Rpb24uXG4gICAqIE5vdGUgdGhhdCB0aGlzIGlzIGEgcGFpZCBvcHRpb25cbiAgICogQHJldHVybnMge1Byb21pc2V9IHByb21pc2UgcmVzb2x2aW5nIHdoZW4gYWNjb3VudCBNZXRhU3RhdHMgQVBJIGlzIGVuYWJsZWRcbiAgICovXG4gIGFzeW5jIGVuYWJsZU1ldGFTdGF0c0FwaSgpIHtcbiAgICBhd2FpdCB0aGlzLl9tZXRhdHJhZGVyQWNjb3VudENsaWVudC5lbmFibGVNZXRhU3RhdHNBcGkodGhpcy5pZCk7XG4gICAgYXdhaXQgdGhpcy5yZWxvYWQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBXYWl0cyB1bnRpbCBBUEkgc2VydmVyIGhhcyBmaW5pc2hlZCBkZXBsb3ltZW50IGFuZCBhY2NvdW50IHJlYWNoZWQgdGhlIERFUExPWUVEIHN0YXRlXG4gICAqIEBwYXJhbSB7bnVtYmVyfSB0aW1lb3V0SW5TZWNvbmRzIHdhaXQgdGltZW91dCBpbiBzZWNvbmRzLCBkZWZhdWx0IGlzIDVtXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBpbnRlcnZhbEluTWlsbGlzZWNvbmRzIGludGVydmFsIGJldHdlZW4gYWNjb3VudCByZWxvYWRzIHdoaWxlIHdhaXRpbmcgZm9yIGEgY2hhbmdlLCBkZWZhdWx0IGlzIDFzXG4gICAqIEByZXR1cm4ge1Byb21pc2V9IHByb21pc2Ugd2hpY2ggcmVzb2x2ZXMgd2hlbiBhY2NvdW50IGlzIGRlcGxveWVkXG4gICAqIEB0aHJvd3Mge1RpbWVvdXRFcnJvcn0gaWYgYWNjb3VudCBoYXZlIG5vdCByZWFjaGVkIHRoZSBERVBMT1lFRCBzdGF0ZSB3aXRoaW4gdGltZW91dCBhbGxvd2VkXG4gICAqL1xuICBhc3luYyB3YWl0RGVwbG95ZWQodGltZW91dEluU2Vjb25kcyA9IDMwMCwgaW50ZXJ2YWxJbk1pbGxpc2Vjb25kcyA9IDEwMDApIHtcbiAgICBsZXQgc3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICBhd2FpdCB0aGlzLnJlbG9hZCgpO1xuICAgIHdoaWxlICh0aGlzLnN0YXRlICE9PSAnREVQTE9ZRUQnICYmIChzdGFydFRpbWUgKyB0aW1lb3V0SW5TZWNvbmRzICogMTAwMCkgPiBEYXRlLm5vdygpKSB7XG4gICAgICBhd2FpdCB0aGlzLl9kZWxheShpbnRlcnZhbEluTWlsbGlzZWNvbmRzKTtcbiAgICAgIGF3YWl0IHRoaXMucmVsb2FkKCk7XG4gICAgfVxuICAgIGlmICh0aGlzLnN0YXRlICE9PSAnREVQTE9ZRUQnKSB7XG4gICAgICB0aHJvdyBuZXcgVGltZW91dEVycm9yKCdUaW1lZCBvdXQgd2FpdGluZyBmb3IgYWNjb3VudCAnICsgdGhpcy5pZCArICcgdG8gYmUgZGVwbG95ZWQnKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogV2FpdHMgdW50aWwgQVBJIHNlcnZlciBoYXMgZmluaXNoZWQgdW5kZXBsb3ltZW50IGFuZCBhY2NvdW50IHJlYWNoZWQgdGhlIFVOREVQTE9ZRUQgc3RhdGVcbiAgICogQHBhcmFtIHtudW1iZXJ9IHRpbWVvdXRJblNlY29uZHMgd2FpdCB0aW1lb3V0IGluIHNlY29uZHMsIGRlZmF1bHQgaXMgNW1cbiAgICogQHBhcmFtIHtudW1iZXJ9IGludGVydmFsSW5NaWxsaXNlY29uZHMgaW50ZXJ2YWwgYmV0d2VlbiBhY2NvdW50IHJlbG9hZHMgd2hpbGUgd2FpdGluZyBmb3IgYSBjaGFuZ2UsIGRlZmF1bHQgaXMgMXNcbiAgICogQHJldHVybiB7UHJvbWlzZX0gcHJvbWlzZSB3aGljaCByZXNvbHZlcyB3aGVuIGFjY291bnQgaXMgZGVwbG95ZWRcbiAgICogQHRocm93cyB7VGltZW91dEVycm9yfSBpZiBhY2NvdW50IGhhdmUgbm90IHJlYWNoZWQgdGhlIFVOREVQTE9ZRUQgc3RhdGUgd2l0aGluIHRpbWVvdXQgYWxsb3dlZFxuICAgKi9cbiAgYXN5bmMgd2FpdFVuZGVwbG95ZWQodGltZW91dEluU2Vjb25kcyA9IDMwMCwgaW50ZXJ2YWxJbk1pbGxpc2Vjb25kcyA9IDEwMDApIHtcbiAgICBsZXQgc3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICBhd2FpdCB0aGlzLnJlbG9hZCgpO1xuICAgIHdoaWxlICh0aGlzLnN0YXRlICE9PSAnVU5ERVBMT1lFRCcgJiYgKHN0YXJ0VGltZSArIHRpbWVvdXRJblNlY29uZHMgKiAxMDAwKSA+IERhdGUubm93KCkpIHtcbiAgICAgIGF3YWl0IHRoaXMuX2RlbGF5KGludGVydmFsSW5NaWxsaXNlY29uZHMpO1xuICAgICAgYXdhaXQgdGhpcy5yZWxvYWQoKTtcbiAgICB9XG4gICAgaWYgKHRoaXMuc3RhdGUgIT09ICdVTkRFUExPWUVEJykge1xuICAgICAgdGhyb3cgbmV3IFRpbWVvdXRFcnJvcignVGltZWQgb3V0IHdhaXRpbmcgZm9yIGFjY291bnQgJyArIHRoaXMuaWQgKyAnIHRvIGJlIHVuZGVwbG95ZWQnKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogV2FpdHMgdW50aWwgYWNjb3VudCBoYXMgYmVlbiBkZWxldGVkXG4gICAqIEBwYXJhbSB7bnVtYmVyfSB0aW1lb3V0SW5TZWNvbmRzIHdhaXQgdGltZW91dCBpbiBzZWNvbmRzLCBkZWZhdWx0IGlzIDVtXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBpbnRlcnZhbEluTWlsbGlzZWNvbmRzIGludGVydmFsIGJldHdlZW4gYWNjb3VudCByZWxvYWRzIHdoaWxlIHdhaXRpbmcgZm9yIGEgY2hhbmdlLCBkZWZhdWx0IGlzIDFzXG4gICAqIEByZXR1cm4ge1Byb21pc2V9IHByb21pc2Ugd2hpY2ggcmVzb2x2ZXMgd2hlbiBhY2NvdW50IGlzIGRlbGV0ZWRcbiAgICogQHRocm93cyB7VGltZW91dEVycm9yfSBpZiBhY2NvdW50IHdhcyBub3QgZGVsZXRlZCB3aXRoaW4gdGltZW91dCBhbGxvd2VkXG4gICAqL1xuICBhc3luYyB3YWl0UmVtb3ZlZCh0aW1lb3V0SW5TZWNvbmRzID0gMzAwLCBpbnRlcnZhbEluTWlsbGlzZWNvbmRzID0gMTAwMCkge1xuICAgIGxldCBzdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0aGlzLnJlbG9hZCgpO1xuICAgICAgd2hpbGUgKHN0YXJ0VGltZSArIHRpbWVvdXRJblNlY29uZHMgKiAxMDAwID4gRGF0ZS5ub3coKSkge1xuICAgICAgICBhd2FpdCB0aGlzLl9kZWxheShpbnRlcnZhbEluTWlsbGlzZWNvbmRzKTtcbiAgICAgICAgYXdhaXQgdGhpcy5yZWxvYWQoKTtcbiAgICAgIH1cbiAgICAgIHRocm93IG5ldyBUaW1lb3V0RXJyb3IoJ1RpbWVkIG91dCB3YWl0aW5nIGZvciBhY2NvdW50ICcgKyB0aGlzLmlkICsgJyB0byBiZSBkZWxldGVkJyk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBpZiAoZXJyLm5hbWUgPT09ICdOb3RGb3VuZEVycm9yJykge1xuICAgICAgICByZXR1cm47XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBlcnI7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFdhaXRzIHVudGlsIEFQSSBzZXJ2ZXIgaGFzIGNvbm5lY3RlZCB0byB0aGUgdGVybWluYWwgYW5kIHRlcm1pbmFsIGhhcyBjb25uZWN0ZWQgdG8gdGhlIGJyb2tlclxuICAgKiBAcGFyYW0ge251bWJlcn0gdGltZW91dEluU2Vjb25kcyB3YWl0IHRpbWVvdXQgaW4gc2Vjb25kcywgZGVmYXVsdCBpcyA1bVxuICAgKiBAcGFyYW0ge251bWJlcn0gaW50ZXJ2YWxJbk1pbGxpc2Vjb25kcyBpbnRlcnZhbCBiZXR3ZWVuIGFjY291bnQgcmVsb2FkcyB3aGlsZSB3YWl0aW5nIGZvciBhIGNoYW5nZSwgZGVmYXVsdCBpcyAxc1xuICAgKiBAcmV0dXJuIHtQcm9taXNlfSBwcm9taXNlIHdoaWNoIHJlc29sdmVzIHdoZW4gQVBJIHNlcnZlciBpcyBjb25uZWN0ZWQgdG8gdGhlIGJyb2tlclxuICAgKiBAdGhyb3dzIHtUaW1lb3V0RXJyb3J9IGlmIGFjY291bnQgaGF2ZSBub3QgY29ubmVjdGVkIHRvIHRoZSBicm9rZXIgd2l0aGluIHRpbWVvdXQgYWxsb3dlZFxuICAgKi9cbiAgYXN5bmMgd2FpdENvbm5lY3RlZCh0aW1lb3V0SW5TZWNvbmRzID0gMzAwLCBpbnRlcnZhbEluTWlsbGlzZWNvbmRzID0gMTAwMCkge1xuICAgIGNvbnN0IGNoZWNrQ29ubmVjdGVkID0gKCkgPT4ge1xuICAgICAgcmV0dXJuIFt0aGlzLmNvbm5lY3Rpb25TdGF0dXNdLmNvbmNhdCh0aGlzLnJlcGxpY2FzLm1hcChyZXBsaWNhID0+IFxuICAgICAgICByZXBsaWNhLmNvbm5lY3Rpb25TdGF0dXMpKS5pbmNsdWRlcygnQ09OTkVDVEVEJyk7XG4gICAgfTtcblxuICAgIGxldCBzdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuICAgIGF3YWl0IHRoaXMucmVsb2FkKCk7XG4gICAgd2hpbGUgKCFjaGVja0Nvbm5lY3RlZCgpICYmIChzdGFydFRpbWUgKyB0aW1lb3V0SW5TZWNvbmRzICogMTAwMCkgPiBEYXRlLm5vdygpKSB7XG4gICAgICBhd2FpdCB0aGlzLl9kZWxheShpbnRlcnZhbEluTWlsbGlzZWNvbmRzKTtcbiAgICAgIGF3YWl0IHRoaXMucmVsb2FkKCk7XG4gICAgfVxuICAgIGlmICghY2hlY2tDb25uZWN0ZWQoKSkge1xuICAgICAgdGhyb3cgbmV3IFRpbWVvdXRFcnJvcignVGltZWQgb3V0IHdhaXRpbmcgZm9yIGFjY291bnQgJyArIHRoaXMuaWQgKyAnIHRvIGNvbm5lY3QgdG8gdGhlIGJyb2tlcicpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBDb25uZWN0cyB0byBNZXRhQXBpLiBUaGVyZSBpcyBvbmx5IG9uZSBjb25uZWN0aW9uIHBlciBhY2NvdW50LiBTdWJzZXF1ZW50IGNhbGxzIHRvIHRoaXMgbWV0aG9kIHdpbGwgcmV0dXJuIHRoZSBzYW1lIGNvbm5lY3Rpb24uXG4gICAqIEBwYXJhbSB7SGlzdG9yeVN0b3JhZ2V9IGhpc3RvcnlTdG9yYWdlIG9wdGlvbmFsIGhpc3Rvcnkgc3RvcmFnZVxuICAgKiBAcGFyYW0ge0RhdGV9IFtoaXN0b3J5U3RhcnRUaW1lXSBoaXN0b3J5IHN0YXJ0IHRpbWUuIFVzZWQgZm9yIHRlc3RzXG4gICAqIEByZXR1cm4ge1N0cmVhbWluZ01ldGFBcGlDb25uZWN0aW9uSW5zdGFuY2V9IE1ldGFBcGkgY29ubmVjdGlvbiBpbnN0YW5jZVxuICAgKi9cbiAgZ2V0U3RyZWFtaW5nQ29ubmVjdGlvbihoaXN0b3J5U3RvcmFnZT86IEhpc3RvcnlTdG9yYWdlLCBoaXN0b3J5U3RhcnRUaW1lPzogRGF0ZSk6IFN0cmVhbWluZ01ldGFBcGlDb25uZWN0aW9uSW5zdGFuY2Uge1xuICAgIGlmKHRoaXMuX21ldGFBcGlXZWJzb2NrZXRDbGllbnQucmVnaW9uICYmIHRoaXMuX21ldGFBcGlXZWJzb2NrZXRDbGllbnQucmVnaW9uICE9PSB0aGlzLnJlZ2lvbikge1xuICAgICAgdGhyb3cgbmV3IFZhbGlkYXRpb25FcnJvcihcbiAgICAgICAgYEFjY291bnQgJHt0aGlzLmlkfSBpcyBub3Qgb24gc3BlY2lmaWVkIHJlZ2lvbiAke3RoaXMuX21ldGFBcGlXZWJzb2NrZXRDbGllbnQucmVnaW9ufWBcbiAgICAgICk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuX2Nvbm5lY3Rpb25SZWdpc3RyeS5jb25uZWN0U3RyZWFtaW5nKHRoaXMsIGhpc3RvcnlTdG9yYWdlLCBoaXN0b3J5U3RhcnRUaW1lKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb25uZWN0cyB0byBNZXRhQXBpIHZpYSBSUEMgY29ubmVjdGlvbiBpbnN0YW5jZS5cbiAgICogQHJldHVybnMge1JwY01ldGFBcGlDb25uZWN0aW9uSW5zdGFuY2V9IE1ldGFBcGkgY29ubmVjdGlvbiBpbnN0YW5jZVxuICAgKi9cbiAgZ2V0UlBDQ29ubmVjdGlvbigpOiBScGNNZXRhQXBpQ29ubmVjdGlvbkluc3RhbmNlIHtcbiAgICBpZih0aGlzLl9tZXRhQXBpV2Vic29ja2V0Q2xpZW50LnJlZ2lvbiAmJiB0aGlzLl9tZXRhQXBpV2Vic29ja2V0Q2xpZW50LnJlZ2lvbiAhPT0gdGhpcy5yZWdpb24pIHtcbiAgICAgIHRocm93IG5ldyBWYWxpZGF0aW9uRXJyb3IoXG4gICAgICAgIGBBY2NvdW50ICR7dGhpcy5pZH0gaXMgbm90IG9uIHNwZWNpZmllZCByZWdpb24gJHt0aGlzLl9tZXRhQXBpV2Vic29ja2V0Q2xpZW50LnJlZ2lvbn1gXG4gICAgICApO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fY29ubmVjdGlvblJlZ2lzdHJ5LmNvbm5lY3RScGModGhpcyk7XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlcyB0cmFkaW5nIGFjY291bnQuIFxuICAgKiBQbGVhc2UgcmVkZXBsb3kgdGhlIHRyYWRpbmcgYWNjb3VudCBpbiBvcmRlciBmb3IgdXBkYXRlZCBzZXR0aW5ncyB0byB0YWtlIGVmZmVjdFxuICAgKiBAcGFyYW0ge01ldGF0cmFkZXJBY2NvdW50VXBkYXRlRHRvfSBhY2NvdW50IHVwZGF0ZWQgYWNjb3VudCBpbmZvcm1hdGlvblxuICAgKiBAcmV0dXJuIHtQcm9taXNlfSBwcm9taXNlIHJlc29sdmluZyB3aGVuIGFjY291bnQgaXMgdXBkYXRlZFxuICAgKi9cbiAgYXN5bmMgdXBkYXRlKGFjY291bnQ6IE1ldGF0cmFkZXJBY2NvdW50VXBkYXRlRHRvKSB7XG4gICAgYXdhaXQgdGhpcy5fbWV0YXRyYWRlckFjY291bnRDbGllbnQudXBkYXRlQWNjb3VudCh0aGlzLmlkLCBhY2NvdW50KTtcbiAgICBhd2FpdCB0aGlzLnJlbG9hZCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSB0cmFkaW5nIGFjY291bnQgcmVwbGljYSBpbiBhIHJlZ2lvbiBkaWZmZXJlbnQgZnJvbSB0cmFkaW5nIGFjY291bnQgcmVnaW9uIGFuZCBzdGFydHMgYSBjbG91ZCBBUEkgc2VydmVyIGZvciBpdFxuICAgKiBAcGFyYW0ge05ld01ldGFUcmFkZXJBY2NvdW50RHRvfSBhY2NvdW50IE1ldGFUcmFkZXIgYWNjb3VudCBkYXRhXG4gICAqIEByZXR1cm4ge1Byb21pc2U8TWV0YXRyYWRlckFjY291bnRSZXBsaWNhPn0gcHJvbWlzZSByZXNvbHZpbmcgd2l0aCBjcmVhdGVkIE1ldGFUcmFkZXIgYWNjb3VudCByZXBsaWNhIGVudGl0eVxuICAgKi9cbiAgYXN5bmMgY3JlYXRlUmVwbGljYShhY2NvdW50OiBOZXdNZXRhVHJhZGVyQWNjb3VudFJlcGxpY2FEdG8pOiBQcm9taXNlPE1ldGF0cmFkZXJBY2NvdW50UmVwbGljYT4ge1xuICAgIGF3YWl0IHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50LmNyZWF0ZUFjY291bnRSZXBsaWNhKHRoaXMuaWQsIGFjY291bnQpO1xuICAgIGF3YWl0IHRoaXMucmVsb2FkKCk7XG4gICAgcmV0dXJuIHRoaXMuX3JlcGxpY2FzLmZpbmQociA9PiByLnJlZ2lvbiA9PT0gYWNjb3VudC5yZWdpb24pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHJpZXZlcyBleHBlcnQgYWR2aXNvciBvZiBjdXJyZW50IGFjY291bnRcbiAgICogQHJldHVybnMge1Byb21pc2U8RXhwZXJ0QWR2aXNvcltdPn0gcHJvbWlzZSByZXNvbHZpbmcgd2l0aCBhbiBhcnJheSBvZiBleHBlcnQgYWR2aXNvciBlbnRpdGllc1xuICAgKi9cbiAgYXN5bmMgZ2V0RXhwZXJ0QWR2aXNvcnMoKTogUHJvbWlzZTxFeHBlcnRBZHZpc29yW10+IHtcbiAgICB0aGlzLl9jaGVja0V4cGVydEFkdmlzb3JBbGxvd2VkKCk7XG4gICAgbGV0IGV4cGVydEFkdmlzb3JzID0gYXdhaXQgdGhpcy5fZXhwZXJ0QWR2aXNvckNsaWVudC5nZXRFeHBlcnRBZHZpc29ycyh0aGlzLmlkKTtcbiAgICByZXR1cm4gZXhwZXJ0QWR2aXNvcnMubWFwKGUgPT4gbmV3IEV4cGVydEFkdmlzb3IoZSwgdGhpcy5pZCwgdGhpcy5fZXhwZXJ0QWR2aXNvckNsaWVudCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHJpZXZlcyBhIGV4cGVydCBhZHZpc29yIG9mIGN1cnJlbnQgYWNjb3VudCBieSBpZFxuICAgKiBAcGFyYW0ge1N0cmluZ30gZXhwZXJ0SWQgZXhwZXJ0IGFkdmlzb3IgaWRcbiAgICogQHJldHVybnMge1Byb21pc2U8RXhwZXJ0QWR2aXNvcj59IHByb21pc2UgcmVzb2x2aW5nIHdpdGggZXhwZXJ0IGFkdmlzb3IgZW50aXR5XG4gICAqL1xuICBhc3luYyBnZXRFeHBlcnRBZHZpc29yKGV4cGVydElkOiBzdHJpbmcpOiBQcm9taXNlPEV4cGVydEFkdmlzb3I+IHtcbiAgICB0aGlzLl9jaGVja0V4cGVydEFkdmlzb3JBbGxvd2VkKCk7XG4gICAgbGV0IGV4cGVydEFkdmlzb3IgPSBhd2FpdCB0aGlzLl9leHBlcnRBZHZpc29yQ2xpZW50LmdldEV4cGVydEFkdmlzb3IodGhpcy5pZCwgZXhwZXJ0SWQpO1xuICAgIHJldHVybiBuZXcgRXhwZXJ0QWR2aXNvcihleHBlcnRBZHZpc29yLCB0aGlzLmlkLCB0aGlzLl9leHBlcnRBZHZpc29yQ2xpZW50KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGFuIGV4cGVydCBhZHZpc29yXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBleHBlcnRJZCBleHBlcnQgYWR2aXNvciBpZFxuICAgKiBAcGFyYW0ge05ld0V4cGVydEFkdmlzb3JEdG99IGV4cGVydCBleHBlcnQgYWR2aXNvciBkYXRhXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPEV4cGVydEFkdmlzb3I+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIGV4cGVydCBhZHZpc29yIGVudGl0eVxuICAgKi9cbiAgYXN5bmMgY3JlYXRlRXhwZXJ0QWR2aXNvcihleHBlcnRJZDogc3RyaW5nLCBleHBlcnQ6IE5ld0V4cGVydEFkdmlzb3JEdG8pOiBQcm9taXNlPEV4cGVydEFkdmlzb3I+IHtcbiAgICB0aGlzLl9jaGVja0V4cGVydEFkdmlzb3JBbGxvd2VkKCk7XG4gICAgYXdhaXQgdGhpcy5fZXhwZXJ0QWR2aXNvckNsaWVudC51cGRhdGVFeHBlcnRBZHZpc29yKHRoaXMuaWQsIGV4cGVydElkLCBleHBlcnQpO1xuICAgIHJldHVybiB0aGlzLmdldEV4cGVydEFkdmlzb3IoZXhwZXJ0SWQpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgaGlzdG9yaWNhbCBjYW5kbGVzIGZvciBhIHNwZWNpZmljIHN5bWJvbCBhbmQgdGltZWZyYW1lIGZyb20gdGhlIE1ldGFUcmFkZXIgYWNjb3VudC5cbiAgICogU2VlIGh0dHBzOi8vbWV0YWFwaS5jbG91ZC9kb2NzL2NsaWVudC9yZXN0QXBpL2FwaS9yZXRyaWV2ZU1hcmtldERhdGEvcmVhZEhpc3RvcmljYWxDYW5kbGVzL1xuICAgKiBAcGFyYW0ge3N0cmluZ30gc3ltYm9sIHN5bWJvbCB0byByZXRyaWV2ZSBjYW5kbGVzIGZvciAoZS5nLiBhIGN1cnJlbmN5IHBhaXIgb3IgYW4gaW5kZXgpXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB0aW1lZnJhbWUgZGVmaW5lcyB0aGUgdGltZWZyYW1lIGFjY29yZGluZyB0byB3aGljaCB0aGUgY2FuZGxlcyBtdXN0IGJlIGdlbmVyYXRlZC4gQWxsb3dlZCB2YWx1ZXNcbiAgICogZm9yIE1UNSBhcmUgMW0sIDJtLCAzbSwgNG0sIDVtLCA2bSwgMTBtLCAxMm0sIDE1bSwgMjBtLCAzMG0sIDFoLCAyaCwgM2gsIDRoLCA2aCwgOGgsIDEyaCwgMWQsIDF3LCAxbW4uIEFsbG93ZWRcbiAgICogdmFsdWVzIGZvciBNVDQgYXJlIDFtLCA1bSwgMTVtIDMwbSwgMWgsIDRoLCAxZCwgMXcsIDFtblxuICAgKiBAcGFyYW0ge0RhdGV9IFtzdGFydFRpbWVdIHRpbWUgdG8gc3RhcnQgbG9hZGluZyBjYW5kbGVzIGZyb20uIE5vdGUgdGhhdCBjYW5kbGVzIGFyZSBsb2FkZWQgaW4gYmFja3dhcmRzIGRpcmVjdGlvbiwgc29cbiAgICogdGhpcyBzaG91bGQgYmUgdGhlIGxhdGVzdCB0aW1lLiBMZWF2ZSBlbXB0eSB0byByZXF1ZXN0IGxhdGVzdCBjYW5kbGVzLlxuICAgKiBAcGFyYW0ge251bWJlcn0gW2xpbWl0XSBtYXhpbXVtIG51bWJlciBvZiBjYW5kbGVzIHRvIHJldHJpZXZlLiBNdXN0IGJlIGxlc3Mgb3IgZXF1YWwgdG8gMTAwMFxuICAgKiBAcmV0dXJuIHtQcm9taXNlPEFycmF5PE1ldGF0cmFkZXJDYW5kbGU+Pn0gcHJvbWlzZSByZXNvbHZpbmcgd2l0aCBoaXN0b3JpY2FsIGNhbmRsZXMgZG93bmxvYWRlZFxuICAgKi9cbiAgZ2V0SGlzdG9yaWNhbENhbmRsZXMoXG4gICAgc3ltYm9sOiBzdHJpbmcsIHRpbWVmcmFtZTogc3RyaW5nLCBzdGFydFRpbWU/OiBEYXRlLCBsaW1pdD86IG51bWJlclxuICApOiBQcm9taXNlPEFycmF5PE1ldGF0cmFkZXJDYW5kbGU+PiB7XG4gICAgcmV0dXJuIHRoaXMuX2hpc3RvcmljYWxNYXJrZXREYXRhQ2xpZW50LmdldEhpc3RvcmljYWxDYW5kbGVzKHRoaXMuaWQsIHRoaXMucmVnaW9uLCBzeW1ib2wsXG4gICAgICB0aW1lZnJhbWUsIHN0YXJ0VGltZSwgbGltaXQpO1xuICB9XG4gIFxuICAvKipcbiAgICogUmV0dXJucyBoaXN0b3JpY2FsIHRpY2tzIGZvciBhIHNwZWNpZmljIHN5bWJvbCBmcm9tIHRoZSBNZXRhVHJhZGVyIGFjY291bnQuIFRoaXMgQVBJIGlzIG5vdCBzdXBwb3J0ZWQgYnkgTVQ0XG4gICAqIGFjY291bnRzLlxuICAgKiBTZWUgaHR0cHM6Ly9tZXRhYXBpLmNsb3VkL2RvY3MvY2xpZW50L3Jlc3RBcGkvYXBpL3JldHJpZXZlTWFya2V0RGF0YS9yZWFkSGlzdG9yaWNhbFRpY2tzL1xuICAgKiBAcGFyYW0ge3N0cmluZ30gc3ltYm9sIHN5bWJvbCB0byByZXRyaWV2ZSB0aWNrcyBmb3IgKGUuZy4gYSBjdXJyZW5jeSBwYWlyIG9yIGFuIGluZGV4KVxuICAgKiBAcGFyYW0ge0RhdGV9IFtzdGFydFRpbWVdIHRpbWUgdG8gc3RhcnQgbG9hZGluZyB0aWNrcyBmcm9tLiBOb3RlIHRoYXQgY2FuZGxlcyBhcmUgbG9hZGVkIGluIGZvcndhcmQgZGlyZWN0aW9uLCBzb1xuICAgKiB0aGlzIHNob3VsZCBiZSB0aGUgZWFybGllc3QgdGltZS4gTGVhdmUgZW1wdHkgdG8gcmVxdWVzdCBsYXRlc3QgY2FuZGxlcy5cbiAgICogQHBhcmFtIHtudW1iZXJ9IFtvZmZzZXRdIG51bWJlciBvZiB0aWNrcyB0byBza2lwICh5b3UgY2FuIHVzZSBpdCB0byBhdm9pZCByZXF1ZXN0aW5nIHRpY2tzIGZyb20gcHJldmlvdXMgcmVxdWVzdFxuICAgKiB0d2ljZSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtsaW1pdF0gbWF4aW11bSBudW1iZXIgb2YgdGlja3MgdG8gcmV0cmlldmUuIE11c3QgYmUgbGVzcyBvciBlcXVhbCB0byAxMDAwXG4gICAqIEByZXR1cm4ge1Byb21pc2U8QXJyYXk8TWV0YXRyYWRlclRpY2s+Pn0gcHJvbWlzZSByZXNvbHZpbmcgd2l0aCBoaXN0b3JpY2FsIHRpY2tzIGRvd25sb2FkZWRcbiAgICovXG4gIGdldEhpc3RvcmljYWxUaWNrcyhcbiAgICBzeW1ib2w6IHN0cmluZywgc3RhcnRUaW1lPzogRGF0ZSwgb2Zmc2V0PzogbnVtYmVyLCBsaW1pdD86IG51bWJlclxuICApOiBQcm9taXNlPEFycmF5PE1ldGF0cmFkZXJUaWNrPj4ge1xuICAgIHJldHVybiB0aGlzLl9oaXN0b3JpY2FsTWFya2V0RGF0YUNsaWVudC5nZXRIaXN0b3JpY2FsVGlja3ModGhpcy5pZCwgdGhpcy5yZWdpb24sIHN5bWJvbCwgc3RhcnRUaW1lLCBvZmZzZXQsIGxpbWl0KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZW5lcmF0ZXMgdHJhZGluZyBhY2NvdW50IGNvbmZpZ3VyYXRpb24gbGluayBieSBhY2NvdW50IGlkLlxuICAgKiBAcGFyYW0ge251bWJlcn0gW3R0bEluRGF5c10gTGlmZXRpbWUgb2YgdGhlIGxpbmsgaW4gZGF5cy4gRGVmYXVsdCBpcyA3LlxuICAgKiBAcmV0dXJuIHtQcm9taXNlPENvbmZpZ3VyYXRpb25MaW5rPn0gcHJvbWlzZSByZXNvbHZpbmcgd2l0aCBjb25maWd1cmF0aW9uIGxpbmtcbiAgICovXG4gIGFzeW5jIGNyZWF0ZUNvbmZpZ3VyYXRpb25MaW5rKHR0bEluRGF5cz86IG51bWJlcik6IFByb21pc2U8Q29uZmlndXJhdGlvbkxpbms+IHtcbiAgICBjb25zdCBjb25maWd1cmF0aW9uTGluayA9IGF3YWl0IHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50LmNyZWF0ZUNvbmZpZ3VyYXRpb25MaW5rKHRoaXMuaWQsIHR0bEluRGF5cyk7XG4gICAgcmV0dXJuIGNvbmZpZ3VyYXRpb25MaW5rO1xuICB9XG5cbiAgcHJpdmF0ZSBfY2hlY2tFeHBlcnRBZHZpc29yQWxsb3dlZCgpIHtcbiAgICBpZiAodGhpcy52ZXJzaW9uICE9PSA0IHx8IHRoaXMudHlwZSAhPT0gJ2Nsb3VkLWcxJykge1xuICAgICAgdGhyb3cgbmV3IFZhbGlkYXRpb25FcnJvcignQ3VzdG9tIGV4cGVydCBhZHZpc29yIGlzIGF2YWlsYWJsZSBvbmx5IGZvciBNVDQgRzEgYWNjb3VudHMnKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIF9kZWxheSh0aW1lb3V0SW5NaWxsaXNlY29uZHMpIHtcbiAgICByZXR1cm4gbmV3IFByb21pc2UocmVzID0+IHNldFRpbWVvdXQocmVzLCB0aW1lb3V0SW5NaWxsaXNlY29uZHMpKTtcbiAgfVxuXG59XG5cbm5hbWVzcGFjZSBNZXRhdHJhZGVyQWNjb3VudCB7XG5cbiAgLyoqIFJlcGxpY2EgYWNjb3VudCBJRHMgYnkgcmVnaW9uLCBpbmNsdWRpbmcgcHJpbWFyeSByZXBsaWNhICovXG4gIGV4cG9ydCB0eXBlIEFjY291bnRzQnlSZWdpb24gPSB7W3JlZ2lvbjogc3RyaW5nXTogc3RyaW5nfTtcbn1cblxuZXhwb3J0IGRlZmF1bHQgTWV0YXRyYWRlckFjY291bnQ7XG4iXSwibmFtZXMiOlsiSGlzdG9yeURhdGFiYXNlIiwicmVxdWlyZSIsImRlZmF1bHQiLCJNZXRhdHJhZGVyQWNjb3VudCIsImlkIiwiX2RhdGEiLCJfaWQiLCJzdGF0ZSIsIm1hZ2ljIiwiY29ubmVjdGlvblN0YXR1cyIsInF1b3RlU3RyZWFtaW5nSW50ZXJ2YWxJblNlY29uZHMiLCJzeW1ib2wiLCJyZWxpYWJpbGl0eSIsInRhZ3MiLCJtZXRhZGF0YSIsInJlc291cmNlU2xvdHMiLCJjb3B5RmFjdG9yeVJlc291cmNlU2xvdHMiLCJyZWdpb24iLCJjcmVhdGVkQXQiLCJEYXRlIiwibmFtZSIsIm1hbnVhbFRyYWRlcyIsInNsaXBwYWdlIiwicHJvdmlzaW9uaW5nUHJvZmlsZUlkIiwibG9naW4iLCJzZXJ2ZXIiLCJ0eXBlIiwidmVyc2lvbiIsImhhc2giLCJiYXNlQ3VycmVuY3kiLCJjb3B5RmFjdG9yeVJvbGVzIiwicmlza01hbmFnZW1lbnRBcGlFbmFibGVkIiwibWV0YXN0YXRzQXBpRW5hYmxlZCIsImFsbG9jYXRlRGVkaWNhdGVkSXAiLCJjb25uZWN0aW9ucyIsInByaW1hcnlSZXBsaWNhIiwidXNlcklkIiwicHJpbWFyeUFjY291bnRJZCIsImFjY291bnRSZXBsaWNhcyIsInJlcGxpY2FzIiwiX3JlcGxpY2FzIiwiYWNjb3VudFJlZ2lvbnMiLCJyZWdpb25zIiwiZm9yRWFjaCIsInJlcGxpY2EiLCJyZWxvYWQiLCJfbWV0YXRyYWRlckFjY291bnRDbGllbnQiLCJnZXRBY2NvdW50IiwidXBkYXRlZFJlcGxpY2FEYXRhIiwibWFwIiwiY3JlYXRlZFJlcGxpY2FSZWdpb25zIiwiZmlsdGVyIiwiaW5jbHVkZXMiLCJ1cGRhdGVkRGF0YSIsImZpbmQiLCJyZXBsaWNhRGF0YSIsInVwZGF0ZURhdGEiLCJwdXNoIiwiTWV0YXRyYWRlckFjY291bnRSZXBsaWNhIiwicmVtb3ZlIiwiX2Nvbm5lY3Rpb25SZWdpc3RyeSIsImNsb3NlQWxsSW5zdGFuY2VzIiwiZGVsZXRlQWNjb3VudCIsImZpbGVNYW5hZ2VyIiwiZ2V0SW5zdGFuY2UiLCJjbGVhciIsIl9hcHBsaWNhdGlvbiIsImVyciIsImRlcGxveSIsImRlcGxveUFjY291bnQiLCJ1bmRlcGxveSIsInVuZGVwbG95QWNjb3VudCIsInJlZGVwbG95IiwicmVkZXBsb3lBY2NvdW50IiwiaW5jcmVhc2VSZWxpYWJpbGl0eSIsImVuYWJsZVJpc2tNYW5hZ2VtZW50QXBpIiwiZW5hYmxlQ29weUZhY3RvcnlBcGkiLCJlbmFibGVNZXRhU3RhdHNBcGkiLCJ3YWl0RGVwbG95ZWQiLCJ0aW1lb3V0SW5TZWNvbmRzIiwiaW50ZXJ2YWxJbk1pbGxpc2Vjb25kcyIsInN0YXJ0VGltZSIsIm5vdyIsIl9kZWxheSIsIlRpbWVvdXRFcnJvciIsIndhaXRVbmRlcGxveWVkIiwid2FpdFJlbW92ZWQiLCJ3YWl0Q29ubmVjdGVkIiwiY2hlY2tDb25uZWN0ZWQiLCJjb25jYXQiLCJnZXRTdHJlYW1pbmdDb25uZWN0aW9uIiwiaGlzdG9yeVN0b3JhZ2UiLCJoaXN0b3J5U3RhcnRUaW1lIiwiX21ldGFBcGlXZWJzb2NrZXRDbGllbnQiLCJWYWxpZGF0aW9uRXJyb3IiLCJjb25uZWN0U3RyZWFtaW5nIiwiZ2V0UlBDQ29ubmVjdGlvbiIsImNvbm5lY3RScGMiLCJ1cGRhdGUiLCJhY2NvdW50IiwidXBkYXRlQWNjb3VudCIsImNyZWF0ZVJlcGxpY2EiLCJjcmVhdGVBY2NvdW50UmVwbGljYSIsInIiLCJnZXRFeHBlcnRBZHZpc29ycyIsIl9jaGVja0V4cGVydEFkdmlzb3JBbGxvd2VkIiwiZXhwZXJ0QWR2aXNvcnMiLCJfZXhwZXJ0QWR2aXNvckNsaWVudCIsImUiLCJFeHBlcnRBZHZpc29yIiwiZ2V0RXhwZXJ0QWR2aXNvciIsImV4cGVydElkIiwiZXhwZXJ0QWR2aXNvciIsImNyZWF0ZUV4cGVydEFkdmlzb3IiLCJleHBlcnQiLCJ1cGRhdGVFeHBlcnRBZHZpc29yIiwiZ2V0SGlzdG9yaWNhbENhbmRsZXMiLCJ0aW1lZnJhbWUiLCJsaW1pdCIsIl9oaXN0b3JpY2FsTWFya2V0RGF0YUNsaWVudCIsImdldEhpc3RvcmljYWxUaWNrcyIsIm9mZnNldCIsImNyZWF0ZUNvbmZpZ3VyYXRpb25MaW5rIiwidHRsSW5EYXlzIiwiY29uZmlndXJhdGlvbkxpbmsiLCJ0aW1lb3V0SW5NaWxsaXNlY29uZHMiLCJQcm9taXNlIiwicmVzIiwic2V0VGltZW91dCIsImNvbnN0cnVjdG9yIiwiZGF0YSIsIm1ldGF0cmFkZXJBY2NvdW50Q2xpZW50IiwibWV0YUFwaVdlYnNvY2tldENsaWVudCIsImNvbm5lY3Rpb25SZWdpc3RyeSIsImV4cGVydEFkdmlzb3JDbGllbnQiLCJoaXN0b3JpY2FsTWFya2V0RGF0YUNsaWVudCIsImFwcGxpY2F0aW9uIl0sIm1hcHBpbmdzIjoiQUFBQTs7OzsrQkFpc0JBOzs7ZUFBQTs7O3FFQS9yQnlCO3NFQUlDOzhCQUNJO2lGQUNPOzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBSHJDLE1BQU1BLGtCQUFrQkMsUUFBUSwyQkFBMkJDLE9BQU87QUFtQmxFOztDQUVDLEdBQ0QsSUFBQSxBQUFNQyxvQkFBTixNQUFNQTtJQXNDSjs7O0dBR0MsR0FDRCxJQUFJQyxLQUFhO1FBQ2YsT0FBTyxJQUFJLENBQUNDLEtBQUssQ0FBQ0MsR0FBRztJQUN2QjtJQUVBOzs7O0dBSUMsR0FDRCxJQUFJQyxRQUFlO1FBQ2pCLE9BQU8sSUFBSSxDQUFDRixLQUFLLENBQUNFLEtBQUs7SUFDekI7SUFFQTs7O0dBR0MsR0FDRCxJQUFJQyxRQUFnQjtRQUNsQixPQUFPLElBQUksQ0FBQ0gsS0FBSyxDQUFDRyxLQUFLO0lBQ3pCO0lBRUE7OztHQUdDLEdBQ0QsSUFBSUMsbUJBQXFDO1FBQ3ZDLE9BQU8sSUFBSSxDQUFDSixLQUFLLENBQUNJLGdCQUFnQjtJQUNwQztJQUVBOzs7R0FHQyxHQUNELElBQUlDLGtDQUEwQztRQUM1QyxPQUFPLElBQUksQ0FBQ0wsS0FBSyxDQUFDSywrQkFBK0I7SUFDbkQ7SUFFQTs7O0dBR0MsR0FDRCxJQUFJQyxTQUFpQjtRQUNuQixPQUFPLElBQUksQ0FBQ04sS0FBSyxD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