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)

171 lines (170 loc) 30 kB
'use strict'; import HttpClient from '../clients/httpClient'; import ProvisioningProfileClient from '../clients/metaApi/provisioningProfile.client'; import ProvisioningProfileApi from './provisioningProfileApi'; import MetaApiWebsocketClient from '../clients/metaApi/metaApiWebsocket.client'; import MetatraderAccountApi from './metatraderAccountApi'; import MetatraderAccountClient from '../clients/metaApi/metatraderAccount.client'; import MetatraderAccountGeneratorApi from './metatraderAccountGeneratorApi'; import MetatraderAccountGeneratorClient from '../clients/metaApi/metatraderAccountGenerator.client'; import HistoricalMarketDataClient from '../clients/metaApi/historicalMarketData.client'; import ClientApiClient from '../clients/metaApi/clientApi.client'; import ConnectionRegistry from './connectionRegistry'; import { ValidationError } from '../clients/errorHandler'; import OptionsValidator from '../clients/optionsValidator'; import LatencyMonitor from './latencyMonitor'; import ExpertAdvisorClient from '../clients/metaApi/expertAdvisor.client'; import LoggerManager from '../logger'; import DomainClient from '../clients/domain.client'; import TerminalHashManager from './terminalHashManager'; import TokenManagementClient from '../clients/metaApi/tokenManagement.client'; import TokenManagementApi from './tokenManagementApi'; import _ from 'lodash'; let MetaApi = class MetaApi { /** * Enables using Log4js logger with extended log levels for debugging instead of * console.* functions. Note that log4js configuration performed by the user. */ static enableLog4jsLogging() { LoggerManager.useLog4js(); } /** * Returns provisioning profile API * @returns {ProvisioningProfileApi} provisioning profile API */ get provisioningProfileApi() { return this._provisioningProfileApi; } /** * Returns MetaTrader account API * @return {MetatraderAccountApi} MetaTrader account API */ get metatraderAccountApi() { return this._metatraderAccountApi; } /** * Returns MetaTrader account generator API * @return {MetatraderDemoAccountApi} MetaTrader account generator API */ get metatraderAccountGeneratorApi() { return this._metatraderAccountGeneratorApi; } /** * Returns MetaApi application latency monitor * @return {LatencyMonitor} latency monitor */ get latencyMonitor() { return this._latencyMonitor; } /** * Returns token management API * @returns {TokenManagementApi} token management API */ get tokenManagementApi() { return this._tokenManagementApi; } /** * Closes all clients and connections and stops all internal jobs */ close() { this._metaApiWebsocketClient.removeLatencyListener(this._latencyMonitor); this._metaApiWebsocketClient.close(); this._metaApiWebsocketClient.stop(); this._terminalHashManager._stop(); } /** * Constructs MetaApi class instance * @param {String} token authorization token * @param {MetaApiOpts} opts application options */ // eslint-disable-next-line complexity constructor(token, opts){ const validator = new OptionsValidator(); opts = _.omit(opts || {}, [ 'connections' ]); const application = opts.application || 'MetaApi'; const domain = opts.domain || 'agiliumtrade.agiliumtrade.ai'; const requestTimeout = validator.validateNonZero(opts.requestTimeout, 60, 'requestTimeout'); const historicalMarketDataRequestTimeout = validator.validateNonZero(opts.historicalMarketDataRequestTimeout, 240, 'historicalMarketDataRequestTimeout'); const connectTimeout = validator.validateNonZero(opts.connectTimeout, 60, 'connectTimeout'); const packetOrderingTimeout = validator.validateNonZero(opts.packetOrderingTimeout, 60, 'packetOrderingTimeout'); const retryOpts = opts.retryOpts || {}; const packetLogger = opts.packetLogger || {}; const synchronizationThrottler = opts.synchronizationThrottler || {}; const accountGeneratorRequestTimeout = validator.validateNonZero(opts.accountGeneratorRequestTimeout, 240, 'accountGeneratorRequestTimeout'); if (!application.match(/[a-zA-Z0-9_]+/)) { throw new ValidationError('Application name must be non-empty string consisting from letters, digits and _ only'); } const useSharedClientApi = opts.useSharedClientApi || false; const refreshSubscriptionsOpts = opts.refreshSubscriptionsOpts || {}; const httpClient = new HttpClient(requestTimeout, retryOpts); const domainClient = new DomainClient(httpClient, token, domain); const historicalMarketDataHttpClient = new HttpClient(historicalMarketDataRequestTimeout, retryOpts); const accountGeneratorHttpClient = new HttpClient(accountGeneratorRequestTimeout, retryOpts); const clientApiClient = new ClientApiClient(httpClient, domainClient); const tokenManagmentClient = new TokenManagementClient(httpClient, domainClient); this._terminalHashManager = new TerminalHashManager(clientApiClient, opts.keepHashTrees); this._metaApiWebsocketClient = new MetaApiWebsocketClient(this, domainClient, token, { application, domain, requestTimeout, connectTimeout, packetLogger, packetOrderingTimeout, synchronizationThrottler, retryOpts, useSharedClientApi, region: opts.region, unsubscribeThrottlingIntervalInSeconds: opts.unsubscribeThrottlingIntervalInSeconds }); this._provisioningProfileApi = new ProvisioningProfileApi(new ProvisioningProfileClient(httpClient, domainClient)); this._connectionRegistry = new ConnectionRegistry(opts, this._metaApiWebsocketClient, this._terminalHashManager, application, refreshSubscriptionsOpts); let historicalMarketDataClient = new HistoricalMarketDataClient(historicalMarketDataHttpClient, domainClient); this._metatraderAccountApi = new MetatraderAccountApi(new MetatraderAccountClient(httpClient, domainClient), this._metaApiWebsocketClient, this._connectionRegistry, new ExpertAdvisorClient(httpClient, domainClient), historicalMarketDataClient, application); this._metatraderAccountGeneratorApi = new MetatraderAccountGeneratorApi(new MetatraderAccountGeneratorClient(accountGeneratorHttpClient, domainClient)); this._tokenManagementApi = new TokenManagementApi(tokenManagmentClient); if (opts.enableLatencyTracking || opts.enableLatencyMonitor) { this._latencyMonitor = new LatencyMonitor(); this._metaApiWebsocketClient.addLatencyListener(this._latencyMonitor); } this._logger = LoggerManager.getLogger('MetaAPI'); if (process.env.IS_BROWSER) { if (!this._tokenManagementApi.areTokenResourcesNarrowedDown(token)) { this._logger.warn('USING THE ADMIN TOKEN'); // eslint-disable-next-line max-len this._logger.info('It seems like you are using a admin API token. Since the token can be retrieven from the browser or mobile apps by end user this can lead to your application being compromised, unless you understand what are you doing. Please use Token Management API (https://github.com/metaapi/metaapi-javascript-sdk/blob/master/docs/tokenManagementApi.md) in your backend application to produce secure tokens which you can then use in web UI or mobile apps.'); } } } }; /** * Request retry options * @typedef {Object} RetryOpts * @property {Number} [retries] maximum amount of request retries, default value is 5 * @property {Number} [minDelayInSeconds] minimum delay in seconds until request retry, default value is 1 * @property {Number} [maxDelayInSeconds] maximum delay in seconds until request retry, default value is 30 * @property {Number} [longRunningRequestTimeoutInMinutes] timeout in minutes for long running requests, default 10 */ /** * Subscriptions refresh options * @typedef {Object} RefreshSubscriptionsOpts * @property {Number} [minDelayInSeconds] minimum delay in seconds until subscriptions refresh request, * default value is 1 * @property {Number} [maxDelayInSeconds] maximum delay in seconds until subscriptions refresh request, * default value is 600 */ /** * MetaApi options * @typedef {Object} MetaApiOpts * @property {String} [application] application id * @property {String} [domain] domain to connect to, default is agiliumtrade.agiliumtrade.ai * @property {String} [region] region to connect to * @property {Number} [requestTimeout] timeout for socket requests in seconds * @property {Number} [connectTimeout] timeout for connecting to server in seconds * @property {Number} [packetOrderingTimeout] packet ordering timeout in seconds * @property {PacketLoggerOpts} [packetLogger] packet logger options * @property {Boolean} [enableLatencyMonitor] an option to enable latency tracking * @property {Boolean} [enableLatencyTracking] an option to enable latency tracking * @property {SynchronizationThrottlerOpts} [synchronizationThrottler] options for synchronization throttler * @property {RetryOpts} [retryOpts] options for request retries * @property {Boolean} [useSharedClientApi] option to use a shared server * @property {RefreshSubscriptionsOpts} [refreshSubscriptionsOpts] subscriptions refresh options * @property {Number} [unsubscribeThrottlingIntervalInSeconds] a timeout in seconds for throttling repeat unsubscribe * requests when synchronization packets still arrive after unsubscription, default is 10 seconds * @property {number} [accountGeneratorRequestTimeout] MT account generator API request timeout. Default is 4 minutes * @property {boolean} [keepHashTrees] if set to true, unused data will not be cleared (for use in debugging) */ /** * MetaApi MetaTrader API SDK */ export { MetaApi as default }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCBIdHRwQ2xpZW50IGZyb20gJy4uL2NsaWVudHMvaHR0cENsaWVudCc7XG5pbXBvcnQgUHJvdmlzaW9uaW5nUHJvZmlsZUNsaWVudCBmcm9tICcuLi9jbGllbnRzL21ldGFBcGkvcHJvdmlzaW9uaW5nUHJvZmlsZS5jbGllbnQnO1xuaW1wb3J0IFByb3Zpc2lvbmluZ1Byb2ZpbGVBcGkgZnJvbSAnLi9wcm92aXNpb25pbmdQcm9maWxlQXBpJztcbmltcG9ydCBNZXRhQXBpV2Vic29ja2V0Q2xpZW50IGZyb20gJy4uL2NsaWVudHMvbWV0YUFwaS9tZXRhQXBpV2Vic29ja2V0LmNsaWVudCc7XG5pbXBvcnQgTWV0YXRyYWRlckFjY291bnRBcGkgZnJvbSAnLi9tZXRhdHJhZGVyQWNjb3VudEFwaSc7XG5pbXBvcnQgTWV0YXRyYWRlckFjY291bnRDbGllbnQgZnJvbSAnLi4vY2xpZW50cy9tZXRhQXBpL21ldGF0cmFkZXJBY2NvdW50LmNsaWVudCc7XG5pbXBvcnQgTWV0YXRyYWRlckFjY291bnRHZW5lcmF0b3JBcGkgZnJvbSAnLi9tZXRhdHJhZGVyQWNjb3VudEdlbmVyYXRvckFwaSc7XG5pbXBvcnQgTWV0YXRyYWRlckFjY291bnRHZW5lcmF0b3JDbGllbnQgZnJvbSAnLi4vY2xpZW50cy9tZXRhQXBpL21ldGF0cmFkZXJBY2NvdW50R2VuZXJhdG9yLmNsaWVudCc7XG5pbXBvcnQgSGlzdG9yaWNhbE1hcmtldERhdGFDbGllbnQgZnJvbSAnLi4vY2xpZW50cy9tZXRhQXBpL2hpc3RvcmljYWxNYXJrZXREYXRhLmNsaWVudCc7XG5pbXBvcnQgQ2xpZW50QXBpQ2xpZW50IGZyb20gJy4uL2NsaWVudHMvbWV0YUFwaS9jbGllbnRBcGkuY2xpZW50JztcbmltcG9ydCBDb25uZWN0aW9uUmVnaXN0cnkgZnJvbSAnLi9jb25uZWN0aW9uUmVnaXN0cnknO1xuaW1wb3J0IHtWYWxpZGF0aW9uRXJyb3J9IGZyb20gJy4uL2NsaWVudHMvZXJyb3JIYW5kbGVyJztcbmltcG9ydCBPcHRpb25zVmFsaWRhdG9yIGZyb20gJy4uL2NsaWVudHMvb3B0aW9uc1ZhbGlkYXRvcic7XG5pbXBvcnQgTGF0ZW5jeU1vbml0b3IgZnJvbSAnLi9sYXRlbmN5TW9uaXRvcic7XG5pbXBvcnQgRXhwZXJ0QWR2aXNvckNsaWVudCBmcm9tICcuLi9jbGllbnRzL21ldGFBcGkvZXhwZXJ0QWR2aXNvci5jbGllbnQnO1xuaW1wb3J0IExvZ2dlck1hbmFnZXIgZnJvbSAnLi4vbG9nZ2VyJztcbmltcG9ydCBEb21haW5DbGllbnQgZnJvbSAnLi4vY2xpZW50cy9kb21haW4uY2xpZW50JztcbmltcG9ydCBUZXJtaW5hbEhhc2hNYW5hZ2VyIGZyb20gJy4vdGVybWluYWxIYXNoTWFuYWdlcic7XG5pbXBvcnQgVG9rZW5NYW5hZ2VtZW50Q2xpZW50IGZyb20gJy4uL2NsaWVudHMvbWV0YUFwaS90b2tlbk1hbmFnZW1lbnQuY2xpZW50JztcbmltcG9ydCBUb2tlbk1hbmFnZW1lbnRBcGkgZnJvbSAnLi90b2tlbk1hbmFnZW1lbnRBcGknO1xuaW1wb3J0IF8gZnJvbSAnbG9kYXNoJztcblxuLyoqXG4gKiBSZXF1ZXN0IHJldHJ5IG9wdGlvbnNcbiAqIEB0eXBlZGVmIHtPYmplY3R9IFJldHJ5T3B0c1xuICogQHByb3BlcnR5IHtOdW1iZXJ9IFtyZXRyaWVzXSBtYXhpbXVtIGFtb3VudCBvZiByZXF1ZXN0IHJldHJpZXMsIGRlZmF1bHQgdmFsdWUgaXMgNVxuICogQHByb3BlcnR5IHtOdW1iZXJ9IFttaW5EZWxheUluU2Vjb25kc10gbWluaW11bSBkZWxheSBpbiBzZWNvbmRzIHVudGlsIHJlcXVlc3QgcmV0cnksIGRlZmF1bHQgdmFsdWUgaXMgMVxuICogQHByb3BlcnR5IHtOdW1iZXJ9IFttYXhEZWxheUluU2Vjb25kc10gbWF4aW11bSBkZWxheSBpbiBzZWNvbmRzIHVudGlsIHJlcXVlc3QgcmV0cnksIGRlZmF1bHQgdmFsdWUgaXMgMzBcbiAqIEBwcm9wZXJ0eSB7TnVtYmVyfSBbbG9uZ1J1bm5pbmdSZXF1ZXN0VGltZW91dEluTWludXRlc10gdGltZW91dCBpbiBtaW51dGVzIGZvciBsb25nIHJ1bm5pbmcgcmVxdWVzdHMsIGRlZmF1bHQgMTBcbiAqL1xuXG4vKipcbiAqIFN1YnNjcmlwdGlvbnMgcmVmcmVzaCBvcHRpb25zXG4gKiBAdHlwZWRlZiB7T2JqZWN0fSBSZWZyZXNoU3Vic2NyaXB0aW9uc09wdHNcbiAqIEBwcm9wZXJ0eSB7TnVtYmVyfSBbbWluRGVsYXlJblNlY29uZHNdIG1pbmltdW0gZGVsYXkgaW4gc2Vjb25kcyB1bnRpbCBzdWJzY3JpcHRpb25zIHJlZnJlc2ggcmVxdWVzdCxcbiAqIGRlZmF1bHQgdmFsdWUgaXMgMVxuICogQHByb3BlcnR5IHtOdW1iZXJ9IFttYXhEZWxheUluU2Vjb25kc10gbWF4aW11bSBkZWxheSBpbiBzZWNvbmRzIHVudGlsIHN1YnNjcmlwdGlvbnMgcmVmcmVzaCByZXF1ZXN0LFxuICogZGVmYXVsdCB2YWx1ZSBpcyA2MDBcbiAqL1xuXG4vKipcbiAqIE1ldGFBcGkgb3B0aW9uc1xuICogQHR5cGVkZWYge09iamVjdH0gTWV0YUFwaU9wdHNcbiAqIEBwcm9wZXJ0eSB7U3RyaW5nfSBbYXBwbGljYXRpb25dIGFwcGxpY2F0aW9uIGlkXG4gKiBAcHJvcGVydHkge1N0cmluZ30gW2RvbWFpbl0gZG9tYWluIHRvIGNvbm5lY3QgdG8sIGRlZmF1bHQgaXMgYWdpbGl1bXRyYWRlLmFnaWxpdW10cmFkZS5haVxuICogQHByb3BlcnR5IHtTdHJpbmd9IFtyZWdpb25dIHJlZ2lvbiB0byBjb25uZWN0IHRvXG4gKiBAcHJvcGVydHkge051bWJlcn0gW3JlcXVlc3RUaW1lb3V0XSB0aW1lb3V0IGZvciBzb2NrZXQgcmVxdWVzdHMgaW4gc2Vjb25kc1xuICogQHByb3BlcnR5IHtOdW1iZXJ9IFtjb25uZWN0VGltZW91dF0gdGltZW91dCBmb3IgY29ubmVjdGluZyB0byBzZXJ2ZXIgaW4gc2Vjb25kc1xuICogQHByb3BlcnR5IHtOdW1iZXJ9IFtwYWNrZXRPcmRlcmluZ1RpbWVvdXRdIHBhY2tldCBvcmRlcmluZyB0aW1lb3V0IGluIHNlY29uZHNcbiAqIEBwcm9wZXJ0eSB7UGFja2V0TG9nZ2VyT3B0c30gW3BhY2tldExvZ2dlcl0gcGFja2V0IGxvZ2dlciBvcHRpb25zXG4gKiBAcHJvcGVydHkge0Jvb2xlYW59IFtlbmFibGVMYXRlbmN5TW9uaXRvcl0gYW4gb3B0aW9uIHRvIGVuYWJsZSBsYXRlbmN5IHRyYWNraW5nXG4gKiBAcHJvcGVydHkge0Jvb2xlYW59IFtlbmFibGVMYXRlbmN5VHJhY2tpbmddIGFuIG9wdGlvbiB0byBlbmFibGUgbGF0ZW5jeSB0cmFja2luZ1xuICogQHByb3BlcnR5IHtTeW5jaHJvbml6YXRpb25UaHJvdHRsZXJPcHRzfSBbc3luY2hyb25pemF0aW9uVGhyb3R0bGVyXSBvcHRpb25zIGZvciBzeW5jaHJvbml6YXRpb24gdGhyb3R0bGVyXG4gKiBAcHJvcGVydHkge1JldHJ5T3B0c30gW3JldHJ5T3B0c10gb3B0aW9ucyBmb3IgcmVxdWVzdCByZXRyaWVzXG4gKiBAcHJvcGVydHkge0Jvb2xlYW59IFt1c2VTaGFyZWRDbGllbnRBcGldIG9wdGlvbiB0byB1c2UgYSBzaGFyZWQgc2VydmVyXG4gKiBAcHJvcGVydHkge1JlZnJlc2hTdWJzY3JpcHRpb25zT3B0c30gW3JlZnJlc2hTdWJzY3JpcHRpb25zT3B0c10gc3Vic2NyaXB0aW9ucyByZWZyZXNoIG9wdGlvbnNcbiAqIEBwcm9wZXJ0eSB7TnVtYmVyfSBbdW5zdWJzY3JpYmVUaHJvdHRsaW5nSW50ZXJ2YWxJblNlY29uZHNdIGEgdGltZW91dCBpbiBzZWNvbmRzIGZvciB0aHJvdHRsaW5nIHJlcGVhdCB1bnN1YnNjcmliZVxuICogcmVxdWVzdHMgd2hlbiBzeW5jaHJvbml6YXRpb24gcGFja2V0cyBzdGlsbCBhcnJpdmUgYWZ0ZXIgdW5zdWJzY3JpcHRpb24sIGRlZmF1bHQgaXMgMTAgc2Vjb25kc1xuICogQHByb3BlcnR5IHtudW1iZXJ9IFthY2NvdW50R2VuZXJhdG9yUmVxdWVzdFRpbWVvdXRdIE1UIGFjY291bnQgZ2VuZXJhdG9yIEFQSSByZXF1ZXN0IHRpbWVvdXQuIERlZmF1bHQgaXMgNCBtaW51dGVzXG4gKiBAcHJvcGVydHkge2Jvb2xlYW59IFtrZWVwSGFzaFRyZWVzXSBpZiBzZXQgdG8gdHJ1ZSwgdW51c2VkIGRhdGEgd2lsbCBub3QgYmUgY2xlYXJlZCAoZm9yIHVzZSBpbiBkZWJ1Z2dpbmcpXG4gKi9cblxuLyoqXG4gKiBNZXRhQXBpIE1ldGFUcmFkZXIgQVBJIFNES1xuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBNZXRhQXBpIHtcblxuICAvKipcbiAgICogRW5hYmxlcyB1c2luZyBMb2c0anMgbG9nZ2VyIHdpdGggZXh0ZW5kZWQgbG9nIGxldmVscyBmb3IgZGVidWdnaW5nIGluc3RlYWQgb2ZcbiAgICogY29uc29sZS4qIGZ1bmN0aW9ucy4gTm90ZSB0aGF0IGxvZzRqcyBjb25maWd1cmF0aW9uIHBlcmZvcm1lZCBieSB0aGUgdXNlci5cbiAgICovXG4gIHN0YXRpYyBlbmFibGVMb2c0anNMb2dnaW5nKCkge1xuICAgIExvZ2dlck1hbmFnZXIudXNlTG9nNGpzKCk7XG4gIH1cblxuICAvKipcbiAgICogQ29uc3RydWN0cyBNZXRhQXBpIGNsYXNzIGluc3RhbmNlXG4gICAqIEBwYXJhbSB7U3RyaW5nfSB0b2tlbiBhdXRob3JpemF0aW9uIHRva2VuXG4gICAqIEBwYXJhbSB7TWV0YUFwaU9wdHN9IG9wdHMgYXBwbGljYXRpb24gb3B0aW9uc1xuICAgKi9cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGNvbXBsZXhpdHlcbiAgY29uc3RydWN0b3IodG9rZW4sIG9wdHMpIHtcbiAgICBjb25zdCB2YWxpZGF0b3IgPSBuZXcgT3B0aW9uc1ZhbGlkYXRvcigpO1xuICAgIG9wdHMgPSBfLm9taXQob3B0cyB8fCB7fSwgWydjb25uZWN0aW9ucyddKTtcbiAgICBjb25zdCBhcHBsaWNhdGlvbiA9IG9wdHMuYXBwbGljYXRpb24gfHwgJ01ldGFBcGknO1xuICAgIGNvbnN0IGRvbWFpbiA9IG9wdHMuZG9tYWluIHx8ICdhZ2lsaXVtdHJhZGUuYWdpbGl1bXRyYWRlLmFpJztcbiAgICBjb25zdCByZXF1ZXN0VGltZW91dCA9IHZhbGlkYXRvci52YWxpZGF0ZU5vblplcm8ob3B0cy5yZXF1ZXN0VGltZW91dCwgNjAsICdyZXF1ZXN0VGltZW91dCcpO1xuICAgIGNvbnN0IGhpc3RvcmljYWxNYXJrZXREYXRhUmVxdWVzdFRpbWVvdXQgPSB2YWxpZGF0b3IudmFsaWRhdGVOb25aZXJvKFxuICAgICAgb3B0cy5oaXN0b3JpY2FsTWFya2V0RGF0YVJlcXVlc3RUaW1lb3V0LCAyNDAsICdoaXN0b3JpY2FsTWFya2V0RGF0YVJlcXVlc3RUaW1lb3V0Jyk7XG4gICAgY29uc3QgY29ubmVjdFRpbWVvdXQgPSB2YWxpZGF0b3IudmFsaWRhdGVOb25aZXJvKG9wdHMuY29ubmVjdFRpbWVvdXQsIDYwLCAnY29ubmVjdFRpbWVvdXQnKTtcbiAgICBjb25zdCBwYWNrZXRPcmRlcmluZ1RpbWVvdXQgPSB2YWxpZGF0b3IudmFsaWRhdGVOb25aZXJvKG9wdHMucGFja2V0T3JkZXJpbmdUaW1lb3V0LCA2MCwgJ3BhY2tldE9yZGVyaW5nVGltZW91dCcpO1xuICAgIGNvbnN0IHJldHJ5T3B0cyA9IG9wdHMucmV0cnlPcHRzIHx8IHt9O1xuICAgIGNvbnN0IHBhY2tldExvZ2dlciA9IG9wdHMucGFja2V0TG9nZ2VyIHx8IHt9O1xuICAgIGNvbnN0IHN5bmNocm9uaXphdGlvblRocm90dGxlciA9IG9wdHMuc3luY2hyb25pemF0aW9uVGhyb3R0bGVyIHx8IHt9O1xuICAgIGNvbnN0IGFjY291bnRHZW5lcmF0b3JSZXF1ZXN0VGltZW91dCA9IHZhbGlkYXRvci52YWxpZGF0ZU5vblplcm8ob3B0cy5hY2NvdW50R2VuZXJhdG9yUmVxdWVzdFRpbWVvdXQsIDI0MCxcbiAgICAgICdhY2NvdW50R2VuZXJhdG9yUmVxdWVzdFRpbWVvdXQnKTtcbiAgICBpZiAoIWFwcGxpY2F0aW9uLm1hdGNoKC9bYS16QS1aMC05X10rLykpIHtcbiAgICAgIHRocm93IG5ldyBWYWxpZGF0aW9uRXJyb3IoJ0FwcGxpY2F0aW9uIG5hbWUgbXVzdCBiZSBub24tZW1wdHkgc3RyaW5nIGNvbnNpc3RpbmcgZnJvbSBsZXR0ZXJzLCBkaWdpdHMgYW5kIF8gb25seScpO1xuICAgIH1cbiAgICBjb25zdCB1c2VTaGFyZWRDbGllbnRBcGkgPSBvcHRzLnVzZVNoYXJlZENsaWVudEFwaSB8fCBmYWxzZTtcbiAgICBjb25zdCByZWZyZXNoU3Vic2NyaXB0aW9uc09wdHMgPSBvcHRzLnJlZnJlc2hTdWJzY3JpcHRpb25zT3B0cyB8fCB7fTtcbiAgICBjb25zdCBodHRwQ2xpZW50ID0gbmV3IEh0dHBDbGllbnQocmVxdWVzdFRpbWVvdXQsIHJldHJ5T3B0cyk7XG4gICAgY29uc3QgZG9tYWluQ2xpZW50ID0gbmV3IERvbWFpbkNsaWVudChodHRwQ2xpZW50LCB0b2tlbiwgZG9tYWluKTtcbiAgICBjb25zdCBoaXN0b3JpY2FsTWFya2V0RGF0YUh0dHBDbGllbnQgPSBuZXcgSHR0cENsaWVudChoaXN0b3JpY2FsTWFya2V0RGF0YVJlcXVlc3RUaW1lb3V0LCByZXRyeU9wdHMpO1xuICAgIGNvbnN0IGFjY291bnRHZW5lcmF0b3JIdHRwQ2xpZW50ID0gbmV3IEh0dHBDbGllbnQoYWNjb3VudEdlbmVyYXRvclJlcXVlc3RUaW1lb3V0LCByZXRyeU9wdHMpO1xuICAgIGNvbnN0IGNsaWVudEFwaUNsaWVudCA9IG5ldyBDbGllbnRBcGlDbGllbnQoaHR0cENsaWVudCwgZG9tYWluQ2xpZW50KTsgXG4gICAgY29uc3QgdG9rZW5NYW5hZ21lbnRDbGllbnQgPSBuZXcgVG9rZW5NYW5hZ2VtZW50Q2xpZW50KGh0dHBDbGllbnQsIGRvbWFpbkNsaWVudCk7IFxuICAgIHRoaXMuX3Rlcm1pbmFsSGFzaE1hbmFnZXIgPSBuZXcgVGVybWluYWxIYXNoTWFuYWdlcihjbGllbnRBcGlDbGllbnQsIG9wdHMua2VlcEhhc2hUcmVlcyk7XG4gICAgdGhpcy5fbWV0YUFwaVdlYnNvY2tldENsaWVudCA9IG5ldyBNZXRhQXBpV2Vic29ja2V0Q2xpZW50KHRoaXMsIGRvbWFpbkNsaWVudCwgdG9rZW4sXG4gICAgICB7YXBwbGljYXRpb24sIGRvbWFpbixcbiAgICAgICAgcmVxdWVzdFRpbWVvdXQsIGNvbm5lY3RUaW1lb3V0LCBwYWNrZXRMb2dnZXIsIHBhY2tldE9yZGVyaW5nVGltZW91dCwgc3luY2hyb25pemF0aW9uVGhyb3R0bGVyLCByZXRyeU9wdHMsXG4gICAgICAgIHVzZVNoYXJlZENsaWVudEFwaSwgcmVnaW9uOiBvcHRzLnJlZ2lvbixcbiAgICAgICAgdW5zdWJzY3JpYmVUaHJvdHRsaW5nSW50ZXJ2YWxJblNlY29uZHM6IG9wdHMudW5zdWJzY3JpYmVUaHJvdHRsaW5nSW50ZXJ2YWxJblNlY29uZHN9KTtcbiAgICB0aGlzLl9wcm92aXNpb25pbmdQcm9maWxlQXBpID0gbmV3IFByb3Zpc2lvbmluZ1Byb2ZpbGVBcGkobmV3IFByb3Zpc2lvbmluZ1Byb2ZpbGVDbGllbnQoaHR0cENsaWVudCwgZG9tYWluQ2xpZW50KSk7XG4gICAgdGhpcy5fY29ubmVjdGlvblJlZ2lzdHJ5ID0gbmV3IENvbm5lY3Rpb25SZWdpc3RyeShvcHRzLCB0aGlzLl9tZXRhQXBpV2Vic29ja2V0Q2xpZW50LCB0aGlzLl90ZXJtaW5hbEhhc2hNYW5hZ2VyLFxuICAgICAgYXBwbGljYXRpb24sIHJlZnJlc2hTdWJzY3JpcHRpb25zT3B0cyk7XG4gICAgbGV0IGhpc3RvcmljYWxNYXJrZXREYXRhQ2xpZW50ID0gbmV3IEhpc3RvcmljYWxNYXJrZXREYXRhQ2xpZW50KGhpc3RvcmljYWxNYXJrZXREYXRhSHR0cENsaWVudCwgZG9tYWluQ2xpZW50KTtcbiAgICB0aGlzLl9tZXRhdHJhZGVyQWNjb3VudEFwaSA9IG5ldyBNZXRhdHJhZGVyQWNjb3VudEFwaShuZXcgTWV0YXRyYWRlckFjY291bnRDbGllbnQoaHR0cENsaWVudCwgZG9tYWluQ2xpZW50KSxcbiAgICAgIHRoaXMuX21ldGFBcGlXZWJzb2NrZXRDbGllbnQsIHRoaXMuX2Nvbm5lY3Rpb25SZWdpc3RyeSwgXG4gICAgICBuZXcgRXhwZXJ0QWR2aXNvckNsaWVudChodHRwQ2xpZW50LCBkb21haW5DbGllbnQpLCBoaXN0b3JpY2FsTWFya2V0RGF0YUNsaWVudCwgYXBwbGljYXRpb24pO1xuICAgIHRoaXMuX21ldGF0cmFkZXJBY2NvdW50R2VuZXJhdG9yQXBpID0gbmV3IE1ldGF0cmFkZXJBY2NvdW50R2VuZXJhdG9yQXBpKFxuICAgICAgbmV3IE1ldGF0cmFkZXJBY2NvdW50R2VuZXJhdG9yQ2xpZW50KGFjY291bnRHZW5lcmF0b3JIdHRwQ2xpZW50LCBkb21haW5DbGllbnQpKTtcbiAgICB0aGlzLl90b2tlbk1hbmFnZW1lbnRBcGkgPSBuZXcgVG9rZW5NYW5hZ2VtZW50QXBpKHRva2VuTWFuYWdtZW50Q2xpZW50KTtcbiAgICBpZiAob3B0cy5lbmFibGVMYXRlbmN5VHJhY2tpbmcgfHwgb3B0cy5lbmFibGVMYXRlbmN5TW9uaXRvcikge1xuICAgICAgdGhpcy5fbGF0ZW5jeU1vbml0b3IgPSBuZXcgTGF0ZW5jeU1vbml0b3IoKTtcbiAgICAgIHRoaXMuX21ldGFBcGlXZWJzb2NrZXRDbGllbnQuYWRkTGF0ZW5jeUxpc3RlbmVyKHRoaXMuX2xhdGVuY3lNb25pdG9yKTtcbiAgICB9XG4gICAgXG4gICAgdGhpcy5fbG9nZ2VyID0gTG9nZ2VyTWFuYWdlci5nZXRMb2dnZXIoJ01ldGFBUEknKTtcbiAgICBpZiAocHJvY2Vzcy5lbnYuSVNfQlJPV1NFUikge1xuICAgICAgaWYgKCF0aGlzLl90b2tlbk1hbmFnZW1lbnRBcGkuYXJlVG9rZW5SZXNvdXJjZXNOYXJyb3dlZERvd24odG9rZW4pKSB7XG4gICAgICAgIHRoaXMuX2xvZ2dlci53YXJuKCdVU0lORyBUSEUgQURNSU4gVE9LRU4nKTtcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG1heC1sZW5cbiAgICAgICAgdGhpcy5fbG9nZ2VyLmluZm8oJ0l0IHNlZW1zIGxpa2UgeW91IGFyZSB1c2luZyBhIGFkbWluIEFQSSB0b2tlbi4gU2luY2UgdGhlIHRva2VuIGNhbiBiZSByZXRyaWV2ZW4gZnJvbSB0aGUgYnJvd3NlciBvciBtb2JpbGUgYXBwcyBieSBlbmQgdXNlciB0aGlzIGNhbiBsZWFkIHRvIHlvdXIgYXBwbGljYXRpb24gYmVpbmcgY29tcHJvbWlzZWQsIHVubGVzcyB5b3UgdW5kZXJzdGFuZCB3aGF0IGFyZSB5b3UgZG9pbmcuIFBsZWFzZSB1c2UgVG9rZW4gTWFuYWdlbWVudCBBUEkgKGh0dHBzOi8vZ2l0aHViLmNvbS9tZXRhYXBpL21ldGFhcGktamF2YXNjcmlwdC1zZGsvYmxvYi9tYXN0ZXIvZG9jcy90b2tlbk1hbmFnZW1lbnRBcGkubWQpIGluIHlvdXIgYmFja2VuZCBhcHBsaWNhdGlvbiB0byBwcm9kdWNlIHNlY3VyZSB0b2tlbnMgd2hpY2ggeW91IGNhbiB0aGVuIHVzZSBpbiB3ZWIgVUkgb3IgbW9iaWxlIGFwcHMuJyk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgcHJvdmlzaW9uaW5nIHByb2ZpbGUgQVBJXG4gICAqIEByZXR1cm5zIHtQcm92aXNpb25pbmdQcm9maWxlQXBpfSBwcm92aXNpb25pbmcgcHJvZmlsZSBBUElcbiAgICovXG4gIGdldCBwcm92aXNpb25pbmdQcm9maWxlQXBpKCkge1xuICAgIHJldHVybiB0aGlzLl9wcm92aXNpb25pbmdQcm9maWxlQXBpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgTWV0YVRyYWRlciBhY2NvdW50IEFQSVxuICAgKiBAcmV0dXJuIHtNZXRhdHJhZGVyQWNjb3VudEFwaX0gTWV0YVRyYWRlciBhY2NvdW50IEFQSVxuICAgKi9cbiAgZ2V0IG1ldGF0cmFkZXJBY2NvdW50QXBpKCkge1xuICAgIHJldHVybiB0aGlzLl9tZXRhdHJhZGVyQWNjb3VudEFwaTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIE1ldGFUcmFkZXIgYWNjb3VudCBnZW5lcmF0b3IgQVBJXG4gICAqIEByZXR1cm4ge01ldGF0cmFkZXJEZW1vQWNjb3VudEFwaX0gTWV0YVRyYWRlciBhY2NvdW50IGdlbmVyYXRvciBBUElcbiAgICovXG4gIGdldCBtZXRhdHJhZGVyQWNjb3VudEdlbmVyYXRvckFwaSgpIHtcbiAgICByZXR1cm4gdGhpcy5fbWV0YXRyYWRlckFjY291bnRHZW5lcmF0b3JBcGk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBNZXRhQXBpIGFwcGxpY2F0aW9uIGxhdGVuY3kgbW9uaXRvclxuICAgKiBAcmV0dXJuIHtMYXRlbmN5TW9uaXRvcn0gbGF0ZW5jeSBtb25pdG9yXG4gICAqL1xuICBnZXQgbGF0ZW5jeU1vbml0b3IoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2xhdGVuY3lNb25pdG9yO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdG9rZW4gbWFuYWdlbWVudCBBUElcbiAgICogQHJldHVybnMge1Rva2VuTWFuYWdlbWVudEFwaX0gdG9rZW4gbWFuYWdlbWVudCBBUElcbiAgICovXG4gIGdldCB0b2tlbk1hbmFnZW1lbnRBcGkoKSB7XG4gICAgcmV0dXJuIHRoaXMuX3Rva2VuTWFuYWdlbWVudEFwaTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDbG9zZXMgYWxsIGNsaWVudHMgYW5kIGNvbm5lY3Rpb25zIGFuZCBzdG9wcyBhbGwgaW50ZXJuYWwgam9ic1xuICAgKi9cbiAgY2xvc2UoKSB7XG4gICAgdGhpcy5fbWV0YUFwaVdlYnNvY2tldENsaWVudC5yZW1vdmVMYXRlbmN5TGlzdGVuZXIodGhpcy5fbGF0ZW5jeU1vbml0b3IpO1xuICAgIHRoaXMuX21ldGFBcGlXZWJzb2NrZXRDbGllbnQuY2xvc2UoKTtcbiAgICB0aGlzLl9tZXRhQXBpV2Vic29ja2V0Q2xpZW50LnN0b3AoKTtcbiAgICB0aGlzLl90ZXJtaW5hbEhhc2hNYW5hZ2VyLl9zdG9wKCk7XG4gIH1cbn1cbiJdLCJuYW1lcyI6WyJIdHRwQ2xpZW50IiwiUHJvdmlzaW9uaW5nUHJvZmlsZUNsaWVudCIsIlByb3Zpc2lvbmluZ1Byb2ZpbGVBcGkiLCJNZXRhQXBpV2Vic29ja2V0Q2xpZW50IiwiTWV0YXRyYWRlckFjY291bnRBcGkiLCJNZXRhdHJhZGVyQWNjb3VudENsaWVudCIsIk1ldGF0cmFkZXJBY2NvdW50R2VuZXJhdG9yQXBpIiwiTWV0YXRyYWRlckFjY291bnRHZW5lcmF0b3JDbGllbnQiLCJIaXN0b3JpY2FsTWFya2V0RGF0YUNsaWVudCIsIkNsaWVudEFwaUNsaWVudCIsIkNvbm5lY3Rpb25SZWdpc3RyeSIsIlZhbGlkYXRpb25FcnJvciIsIk9wdGlvbnNWYWxpZGF0b3IiLCJMYXRlbmN5TW9uaXRvciIsIkV4cGVydEFkdmlzb3JDbGllbnQiLCJMb2dnZXJNYW5hZ2VyIiwiRG9tYWluQ2xpZW50IiwiVGVybWluYWxIYXNoTWFuYWdlciIsIlRva2VuTWFuYWdlbWVudENsaWVudCIsIlRva2VuTWFuYWdlbWVudEFwaSIsIl8iLCJNZXRhQXBpIiwiZW5hYmxlTG9nNGpzTG9nZ2luZyIsInVzZUxvZzRqcyIsInByb3Zpc2lvbmluZ1Byb2ZpbGVBcGkiLCJfcHJvdmlzaW9uaW5nUHJvZmlsZUFwaSIsIm1ldGF0cmFkZXJBY2NvdW50QXBpIiwiX21ldGF0cmFkZXJBY2NvdW50QXBpIiwibWV0YXRyYWRlckFjY291bnRHZW5lcmF0b3JBcGkiLCJfbWV0YXRyYWRlckFjY291bnRHZW5lcmF0b3JBcGkiLCJsYXRlbmN5TW9uaXRvciIsIl9sYXRlbmN5TW9uaXRvciIsInRva2VuTWFuYWdlbWVudEFwaSIsIl90b2tlbk1hbmFnZW1lbnRBcGkiLCJjbG9zZSIsIl9tZXRhQXBpV2Vic29ja2V0Q2xpZW50IiwicmVtb3ZlTGF0ZW5jeUxpc3RlbmVyIiwic3RvcCIsIl90ZXJtaW5hbEhhc2hNYW5hZ2VyIiwiX3N0b3AiLCJjb25zdHJ1Y3RvciIsInRva2VuIiwib3B0cyIsInZhbGlkYXRvciIsIm9taXQiLCJhcHBsaWNhdGlvbiIsImRvbWFpbiIsInJlcXVlc3RUaW1lb3V0IiwidmFsaWRhdGVOb25aZXJvIiwiaGlzdG9yaWNhbE1hcmtldERhdGFSZXF1ZXN0VGltZW91dCIsImNvbm5lY3RUaW1lb3V0IiwicGFja2V0T3JkZXJpbmdUaW1lb3V0IiwicmV0cnlPcHRzIiwicGFja2V0TG9nZ2VyIiwic3luY2hyb25pemF0aW9uVGhyb3R0bGVyIiwiYWNjb3VudEdlbmVyYXRvclJlcXVlc3RUaW1lb3V0IiwibWF0Y2giLCJ1c2VTaGFyZWRDbGllbnRBcGkiLCJyZWZyZXNoU3Vic2NyaXB0aW9uc09wdHMiLCJodHRwQ2xpZW50IiwiZG9tYWluQ2xpZW50IiwiaGlzdG9yaWNhbE1hcmtldERhdGFIdHRwQ2xpZW50IiwiYWNjb3VudEdlbmVyYXRvckh0dHBDbGllbnQiLCJjbGllbnRBcGlDbGllbnQiLCJ0b2tlbk1hbmFnbWVudENsaWVudCIsImtlZXBIYXNoVHJlZXMiLCJyZWdpb24iLCJ1bnN1YnNjcmliZVRocm90dGxpbmdJbnRlcnZhbEluU2Vjb25kcyIsIl9jb25uZWN0aW9uUmVnaXN0cnkiLCJoaXN0b3JpY2FsTWFya2V0RGF0YUNsaWVudCIsImVuYWJsZUxhdGVuY3lUcmFja2luZyIsImVuYWJsZUxhdGVuY3lNb25pdG9yIiwiYWRkTGF0ZW5jeUxpc3RlbmVyIiwiX2xvZ2dlciIsImdldExvZ2dlciIsInByb2Nlc3MiLCJlbnYiLCJJU19CUk9XU0VSIiwiYXJlVG9rZW5SZXNvdXJjZXNOYXJyb3dlZERvd24iLCJ3YXJuIiwiaW5mbyJdLCJtYXBwaW5ncyI6IkFBQUE7QUFFQSxPQUFPQSxnQkFBZ0Isd0JBQXdCO0FBQy9DLE9BQU9DLCtCQUErQixnREFBZ0Q7QUFDdEYsT0FBT0MsNEJBQTRCLDJCQUEyQjtBQUM5RCxPQUFPQyw0QkFBNEIsNkNBQTZDO0FBQ2hGLE9BQU9DLDBCQUEwQix5QkFBeUI7QUFDMUQsT0FBT0MsNkJBQTZCLDhDQUE4QztBQUNsRixPQUFPQyxtQ0FBbUMsa0NBQWtDO0FBQzVFLE9B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