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)

231 lines (230 loc) 28.2 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; } function _object_spread(target) { for(var i = 1; i < arguments.length; i++){ var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === "function") { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function(key) { _define_property(target, key, source[key]); }); } return target; } const axios = require('axios'); import { UnauthorizedError, ForbiddenError, ApiError, ValidationError, InternalError, NotFoundError, TooManyRequestsError, ConflictError } from './errorHandler'; import OptionsValidator from './optionsValidator'; import TimeoutError from './timeoutError'; import LoggerManager from '../logger'; import _ from 'lodash'; let HttpClient = class HttpClient { /** * Performs a request. Response errors are returned as ApiError or subclasses. * @param {Object} options request options * @returns {Object|String|any} request result */ request(options, type = '', retryCounter = 0, endTime = Date.now() + this._maxRetryDelay * this._retries, isLongRunning = false) { var _this = this; return _async_to_generator(function*() { options.timeout = _this._timeout; let retryAfterSeconds = 0; options.callback = (e, res)=>{ _this._logger.debug(`${type}: received request response with status ${res === null || res === void 0 ? void 0 : res.status}`); if ((res === null || res === void 0 ? void 0 : res.status) === 202) { var _res_data_metadata, _res_data; var _res_headers_retryafter; retryAfterSeconds = (_res_headers_retryafter = res.headers['retry-after']) !== null && _res_headers_retryafter !== void 0 ? _res_headers_retryafter : (_res_data = res.data) === null || _res_data === void 0 ? void 0 : (_res_data_metadata = _res_data.metadata) === null || _res_data_metadata === void 0 ? void 0 : _res_data_metadata.recommendedRetryTime; _this._logger.debug(`${type}: retry after value is ${retryAfterSeconds}`); if (isNaN(retryAfterSeconds)) { retryAfterSeconds = Math.max((new Date(retryAfterSeconds).getTime() - Date.now()) / 1000, 1); } if (!isLongRunning) { endTime = Date.now() + _this._longRunningRequestTimeout; isLongRunning = true; } } }; let body; try { const response = yield _this._makeRequest(options, type); options.callback(null, response); body = response && response.data || undefined; } catch (err) { retryCounter = yield _this._handleError(err, type, retryCounter, endTime); return _this.request(options, type, retryCounter, endTime); } if (retryAfterSeconds) { if (body && body.message) { _this._logger.info(`Retrying request in ${Math.floor(retryAfterSeconds)} seconds because request ` + 'returned message:', body.message); } yield _this._handleRetry(endTime, retryAfterSeconds * 1000); body = yield _this.request(options, type, retryCounter, endTime, isLongRunning); } return body; })(); } _makeRequest(options, type) { var _optionsToLog_headers; let optionsToLog = _.cloneDeep(options); if ((_optionsToLog_headers = optionsToLog.headers) === null || _optionsToLog_headers === void 0 ? void 0 : _optionsToLog_headers['auth-token']) { optionsToLog.headers['auth-token'] = '...'; } this._logger.debug(`${type}: sending a request with options`, JSON.stringify(optionsToLog)); return axios(_object_spread({ transitional: { clarifyTimeoutError: true } }, options)); } _wait(pause) { return _async_to_generator(function*() { yield new Promise((res)=>setTimeout(res, pause)); })(); } _handleRetry(endTime, retryAfter) { var _this = this; return _async_to_generator(function*() { if (endTime > Date.now() + retryAfter) { yield _this._wait(retryAfter); } else { throw new TimeoutError('Timed out waiting for the response'); } })(); } _handleError(err, type, retryCounter, endTime) { var _this = this; return _async_to_generator(function*() { const error = _this._convertError(err); if ([ 'ConflictError', 'InternalError', 'ApiError', 'TimeoutError' ].includes(error.name) && retryCounter < _this._retries) { const pause = Math.min(Math.pow(2, retryCounter) * _this._minRetryDelay, _this._maxRetryDelay); yield _this._wait(pause); return retryCounter + 1; } else if (error.name === 'TooManyRequestsError') { const retryTime = new Date(error.metadata.recommendedRetryTime).getTime(); if (retryTime < endTime) { _this._logger.debug(`${type} request has failed with TooManyRequestsError (HTTP status code 429). ` + `Will retry request in ${Math.ceil((retryTime - Date.now()) / 1000)} seconds`); yield _this._wait(retryTime - Date.now()); return retryCounter; } } throw error; })(); } // eslint-disable-next-line complexity _convertError(err) { var _err_config; const errorResponse = err.response || {}; const errorData = errorResponse.data || {}; const status = errorResponse.status || err.status; const url = err === null || err === void 0 ? void 0 : (_err_config = err.config) === null || _err_config === void 0 ? void 0 : _err_config.url; const errMsg = errorData.message || err.message; const errMsgDefault = errorData.message || err.code || err.message; switch(status){ case 400: return new ValidationError(errMsg, errorData.details || err.details, url); case 401: return new UnauthorizedError(errMsg, url); case 403: return new ForbiddenError(errMsg, url); case 404: return new NotFoundError(errMsg, url); case 409: return new ConflictError(errMsg, url); case 429: return new TooManyRequestsError(errMsg, errorData.metadata || err.metadata, url); case 500: return new InternalError(errMsg, url); default: return new ApiError(ApiError, errMsgDefault, status, url); } } /** * Constructs HttpClient class instance * @param {Number} timeout request timeout in seconds * @param {RetryOptions} [retryOpts] retry options */ constructor(timeout = 60, retryOpts = {}){ _define_property(this, "_timeout", void 0); _define_property(this, "_retries", void 0); _define_property(this, "_minRetryDelay", void 0); _define_property(this, "_maxRetryDelay", void 0); _define_property(this, "_longRunningRequestTimeout", void 0); _define_property(this, "_logger", void 0); const validator = new OptionsValidator(); this._timeout = timeout * 1000; this._retries = validator.validateNumber(retryOpts.retries, 5, 'retryOpts.retries'); this._minRetryDelay = validator.validateNonZero(retryOpts.minDelayInSeconds, 1, 'retryOpts.minDelayInSeconds') * 1000; this._maxRetryDelay = validator.validateNonZero(retryOpts.maxDelayInSeconds, 30, 'retryOpts.maxDelayInSeconds') * 1000; this._longRunningRequestTimeout = validator.validateNumber(retryOpts.longRunningRequestTimeoutInMinutes, 10, 'retryOpts.longRunningRequestTimeoutInMinutes') * 60 * 1000; this._logger = LoggerManager.getLogger('HttpClient'); } }; /** * HTTP client library based on axios */ export { HttpClient as default }; /** * HTTP client service mock for tests */ export class HttpClientMock extends HttpClient { _makeRequest(...args) { return this._requestFn(...args); } /** * Constructs HTTP client mock * @param {Function(options:Object):Promise} requestFn mocked request function * @param {Number} timeout request timeout in seconds * @param {RetryOptions} retryOpts retry options */ constructor(requestFn, timeout, retryOpts){ super(timeout, retryOpts); _define_property(this, "_requestFn", void 0); this._requestFn = requestFn; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmNvbnN0IGF4aW9zID0gcmVxdWlyZSgnYXhpb3MnKTtcblxuaW1wb3J0IHtcbiAgVW5hdXRob3JpemVkRXJyb3IsIEZvcmJpZGRlbkVycm9yLCBBcGlFcnJvciwgVmFsaWRhdGlvbkVycm9yLCBJbnRlcm5hbEVycm9yLCBcbiAgTm90Rm91bmRFcnJvciwgVG9vTWFueVJlcXVlc3RzRXJyb3IsIENvbmZsaWN0RXJyb3Jcbn0gZnJvbSAnLi9lcnJvckhhbmRsZXInO1xuaW1wb3J0IE9wdGlvbnNWYWxpZGF0b3IgZnJvbSAnLi9vcHRpb25zVmFsaWRhdG9yJztcbmltcG9ydCBUaW1lb3V0RXJyb3IgZnJvbSAnLi90aW1lb3V0RXJyb3InO1xuaW1wb3J0IExvZ2dlck1hbmFnZXIsIHtMb2dnZXJ9IGZyb20gJy4uL2xvZ2dlcic7XG5pbXBvcnQgXyBmcm9tICdsb2Rhc2gnO1xuXG4vKipcbiAqIEhUVFAgY2xpZW50IGxpYnJhcnkgYmFzZWQgb24gYXhpb3NcbiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgSHR0cENsaWVudCB7XG4gIFxuICBwcml2YXRlIF90aW1lb3V0OiBudW1iZXI7XG4gIHByaXZhdGUgX3JldHJpZXM6IGFueTtcbiAgcHJpdmF0ZSBfbWluUmV0cnlEZWxheTogbnVtYmVyO1xuICBwcml2YXRlIF9tYXhSZXRyeURlbGF5OiBudW1iZXI7XG4gIHByaXZhdGUgX2xvbmdSdW5uaW5nUmVxdWVzdFRpbWVvdXQ6IG51bWJlcjtcbiAgcHJpdmF0ZSBfbG9nZ2VyOiBMb2dnZXI7XG4gIFxuICAvKipcbiAgICogQ29uc3RydWN0cyBIdHRwQ2xpZW50IGNsYXNzIGluc3RhbmNlXG4gICAqIEBwYXJhbSB7TnVtYmVyfSB0aW1lb3V0IHJlcXVlc3QgdGltZW91dCBpbiBzZWNvbmRzXG4gICAqIEBwYXJhbSB7UmV0cnlPcHRpb25zfSBbcmV0cnlPcHRzXSByZXRyeSBvcHRpb25zXG4gICAqL1xuICBjb25zdHJ1Y3Rvcih0aW1lb3V0ID0gNjAsIHJldHJ5T3B0czogUmV0cnlPcHRpb25zID0ge30pIHtcbiAgICBjb25zdCB2YWxpZGF0b3IgPSBuZXcgT3B0aW9uc1ZhbGlkYXRvcigpO1xuXG4gICAgdGhpcy5fdGltZW91dCA9IHRpbWVvdXQgKiAxMDAwO1xuICAgIHRoaXMuX3JldHJpZXMgPSB2YWxpZGF0b3IudmFsaWRhdGVOdW1iZXIocmV0cnlPcHRzLnJldHJpZXMsIDUsICdyZXRyeU9wdHMucmV0cmllcycpO1xuICAgIHRoaXMuX21pblJldHJ5RGVsYXkgPSB2YWxpZGF0b3IudmFsaWRhdGVOb25aZXJvKHJldHJ5T3B0cy5taW5EZWxheUluU2Vjb25kcywgMSxcbiAgICAgICdyZXRyeU9wdHMubWluRGVsYXlJblNlY29uZHMnKSAqIDEwMDA7XG4gICAgdGhpcy5fbWF4UmV0cnlEZWxheSA9IHZhbGlkYXRvci52YWxpZGF0ZU5vblplcm8ocmV0cnlPcHRzLm1heERlbGF5SW5TZWNvbmRzLCAzMCxcbiAgICAgICdyZXRyeU9wdHMubWF4RGVsYXlJblNlY29uZHMnKSAqIDEwMDA7XG4gICAgdGhpcy5fbG9uZ1J1bm5pbmdSZXF1ZXN0VGltZW91dCA9IHZhbGlkYXRvci52YWxpZGF0ZU51bWJlcihyZXRyeU9wdHMubG9uZ1J1bm5pbmdSZXF1ZXN0VGltZW91dEluTWludXRlcywgMTAsXG4gICAgICAncmV0cnlPcHRzLmxvbmdSdW5uaW5nUmVxdWVzdFRpbWVvdXRJbk1pbnV0ZXMnKSAqIDYwICogMTAwMDtcbiAgICB0aGlzLl9sb2dnZXIgPSBMb2dnZXJNYW5hZ2VyLmdldExvZ2dlcignSHR0cENsaWVudCcpO1xuICB9XG5cbiAgLyoqXG4gICAqIFBlcmZvcm1zIGEgcmVxdWVzdC4gUmVzcG9uc2UgZXJyb3JzIGFyZSByZXR1cm5lZCBhcyBBcGlFcnJvciBvciBzdWJjbGFzc2VzLlxuICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyByZXF1ZXN0IG9wdGlvbnNcbiAgICogQHJldHVybnMge09iamVjdHxTdHJpbmd8YW55fSByZXF1ZXN0IHJlc3VsdFxuICAgKi9cbiAgYXN5bmMgcmVxdWVzdDxUID0gYW55PihcbiAgICBvcHRpb25zLCB0eXBlID0gJycsIHJldHJ5Q291bnRlciA9IDAsIGVuZFRpbWUgPSBEYXRlLm5vdygpICsgdGhpcy5fbWF4UmV0cnlEZWxheSAqIHRoaXMuX3JldHJpZXMsXG4gICAgaXNMb25nUnVubmluZyA9IGZhbHNlXG4gICk6IFByb21pc2U8VD4ge1xuICAgIG9wdGlvbnMudGltZW91dCA9IHRoaXMuX3RpbWVvdXQ7XG4gICAgXG4gICAgbGV0IHJldHJ5QWZ0ZXJTZWNvbmRzID0gMDtcbiAgICBvcHRpb25zLmNhbGxiYWNrID0gKGUsIHJlcykgPT4ge1xuICAgICAgdGhpcy5fbG9nZ2VyLmRlYnVnKGAke3R5cGV9OiByZWNlaXZlZCByZXF1ZXN0IHJlc3BvbnNlIHdpdGggc3RhdHVzICR7cmVzPy5zdGF0dXN9YCk7XG4gICAgICBpZiAocmVzPy5zdGF0dXMgPT09IDIwMikge1xuICAgICAgICByZXRyeUFmdGVyU2Vjb25kcyA9IHJlcy5oZWFkZXJzWydyZXRyeS1hZnRlciddID8/IHJlcy5kYXRhPy5tZXRhZGF0YT8ucmVjb21tZW5kZWRSZXRyeVRpbWU7XG4gICAgICAgIHRoaXMuX2xvZ2dlci5kZWJ1ZyhgJHt0eXBlfTogcmV0cnkgYWZ0ZXIgdmFsdWUgaXMgJHtyZXRyeUFmdGVyU2Vjb25kc31gKTtcblxuICAgICAgICBpZiAoaXNOYU4ocmV0cnlBZnRlclNlY29uZHMpKSB7XG4gICAgICAgICAgcmV0cnlBZnRlclNlY29uZHMgPSBNYXRoLm1heCgobmV3IERhdGUocmV0cnlBZnRlclNlY29uZHMpLmdldFRpbWUoKSAtIERhdGUubm93KCkpIC8gMTAwMCwgMSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFpc0xvbmdSdW5uaW5nKSB7XG4gICAgICAgICAgZW5kVGltZSA9IERhdGUubm93KCkgKyB0aGlzLl9sb25nUnVubmluZ1JlcXVlc3RUaW1lb3V0O1xuICAgICAgICAgIGlzTG9uZ1J1bm5pbmcgPSB0cnVlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfTtcblxuICAgIGxldCBib2R5O1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgdGhpcy5fbWFrZVJlcXVlc3Qob3B0aW9ucywgdHlwZSk7XG4gICAgICBvcHRpb25zLmNhbGxiYWNrKG51bGwsIHJlc3BvbnNlKTtcbiAgICAgIGJvZHkgPSAocmVzcG9uc2UgJiYgcmVzcG9uc2UuZGF0YSkgfHwgdW5kZWZpbmVkO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgcmV0cnlDb3VudGVyID0gYXdhaXQgdGhpcy5faGFuZGxlRXJyb3IoZXJyLCB0eXBlLCByZXRyeUNvdW50ZXIsIGVuZFRpbWUpO1xuICAgICAgcmV0dXJuIHRoaXMucmVxdWVzdChvcHRpb25zLCB0eXBlLCByZXRyeUNvdW50ZXIsIGVuZFRpbWUpO1xuICAgIH1cblxuICAgIGlmIChyZXRyeUFmdGVyU2Vjb25kcykge1xuICAgICAgaWYgKGJvZHkgJiYgYm9keS5tZXNzYWdlKSB7XG4gICAgICAgIHRoaXMuX2xvZ2dlci5pbmZvKGBSZXRyeWluZyByZXF1ZXN0IGluICR7TWF0aC5mbG9vcihyZXRyeUFmdGVyU2Vjb25kcyl9IHNlY29uZHMgYmVjYXVzZSByZXF1ZXN0IGAgK1xuICAgICAgICAgICdyZXR1cm5lZCBtZXNzYWdlOicsIGJvZHkubWVzc2FnZSk7XG4gICAgICB9XG4gICAgICBhd2FpdCB0aGlzLl9oYW5kbGVSZXRyeShlbmRUaW1lLCByZXRyeUFmdGVyU2Vjb25kcyAqIDEwMDApO1xuICAgICAgYm9keSA9IGF3YWl0IHRoaXMucmVxdWVzdChvcHRpb25zLCB0eXBlLCByZXRyeUNvdW50ZXIsIGVuZFRpbWUsIGlzTG9uZ1J1bm5pbmcpO1xuICAgIH1cblxuICAgIHJldHVybiBib2R5O1xuICB9XG5cbiAgX21ha2VSZXF1ZXN0KG9wdGlvbnMsIHR5cGUpIHtcbiAgICBsZXQgb3B0aW9uc1RvTG9nID0gXy5jbG9uZURlZXAob3B0aW9ucyk7XG4gICAgaWYgKG9wdGlvbnNUb0xvZy5oZWFkZXJzPy5bJ2F1dGgtdG9rZW4nXSkge1xuICAgICAgb3B0aW9uc1RvTG9nLmhlYWRlcnNbJ2F1dGgtdG9rZW4nXSA9ICcuLi4nO1xuICAgIH1cbiAgICB0aGlzLl9sb2dnZXIuZGVidWcoYCR7dHlwZX06IHNlbmRpbmcgYSByZXF1ZXN0IHdpdGggb3B0aW9uc2AsIEpTT04uc3RyaW5naWZ5KG9wdGlvbnNUb0xvZykpO1xuICAgIHJldHVybiBheGlvcyh7XG4gICAgICB0cmFuc2l0aW9uYWw6IHtcbiAgICAgICAgY2xhcmlmeVRpbWVvdXRFcnJvcjogdHJ1ZVxuICAgICAgfSxcbiAgICAgIC4uLm9wdGlvbnNcbiAgICB9KTtcbiAgfVxuICBcbiAgYXN5bmMgX3dhaXQocGF1c2UpIHtcbiAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXMgPT4gc2V0VGltZW91dChyZXMsIHBhdXNlKSk7XG4gIH1cbiAgXG4gIGFzeW5jIF9oYW5kbGVSZXRyeShlbmRUaW1lLCByZXRyeUFmdGVyKSB7XG4gICAgaWYgKGVuZFRpbWUgPiBEYXRlLm5vdygpICsgcmV0cnlBZnRlcikge1xuICAgICAgYXdhaXQgdGhpcy5fd2FpdChyZXRyeUFmdGVyKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IFRpbWVvdXRFcnJvcignVGltZWQgb3V0IHdhaXRpbmcgZm9yIHRoZSByZXNwb25zZScpO1xuICAgIH1cbiAgfVxuICBcbiAgYXN5bmMgX2hhbmRsZUVycm9yKGVyciwgdHlwZSwgcmV0cnlDb3VudGVyLCBlbmRUaW1lKSB7XG4gICAgY29uc3QgZXJyb3IgPSB0aGlzLl9jb252ZXJ0RXJyb3IoZXJyKTtcblxuICAgIGlmIChcbiAgICAgIFsnQ29uZmxpY3RFcnJvcicsICdJbnRlcm5hbEVycm9yJywgJ0FwaUVycm9yJywgJ1RpbWVvdXRFcnJvciddLmluY2x1ZGVzKGVycm9yLm5hbWUpICYmIFxuICAgICAgcmV0cnlDb3VudGVyIDwgdGhpcy5fcmV0cmllc1xuICAgICkge1xuICAgICAgY29uc3QgcGF1c2UgPSBNYXRoLm1pbihNYXRoLnBvdygyLCByZXRyeUNvdW50ZXIpICogdGhpcy5fbWluUmV0cnlEZWxheSwgdGhpcy5fbWF4UmV0cnlEZWxheSk7XG4gICAgICBhd2FpdCB0aGlzLl93YWl0KHBhdXNlKTtcblxuICAgICAgcmV0dXJuIHJldHJ5Q291bnRlciArIDE7XG4gICAgfSBlbHNlIGlmIChlcnJvci5uYW1lID09PSAnVG9vTWFueVJlcXVlc3RzRXJyb3InKSB7XG4gICAgICBjb25zdCByZXRyeVRpbWUgPSBuZXcgRGF0ZSgoZXJyb3IgYXMgVG9vTWFueVJlcXVlc3RzRXJyb3IpLm1ldGFkYXRhLnJlY29tbWVuZGVkUmV0cnlUaW1lKS5nZXRUaW1lKCk7XG4gICAgICBpZiAocmV0cnlUaW1lIDwgZW5kVGltZSkge1xuICAgICAgICB0aGlzLl9sb2dnZXIuZGVidWcoYCR7dHlwZX0gcmVxdWVzdCBoYXMgZmFpbGVkIHdpdGggVG9vTWFueVJlcXVlc3RzRXJyb3IgKEhUVFAgc3RhdHVzIGNvZGUgNDI5KS4gYCArXG4gICAgICAgICAgYFdpbGwgcmV0cnkgcmVxdWVzdCBpbiAke01hdGguY2VpbCgocmV0cnlUaW1lIC0gRGF0ZS5ub3coKSkgLyAxMDAwKX0gc2Vjb25kc2ApO1xuICAgICAgICBhd2FpdCB0aGlzLl93YWl0KHJldHJ5VGltZSAtIERhdGUubm93KCkpO1xuXG4gICAgICAgIHJldHVybiByZXRyeUNvdW50ZXI7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhyb3cgZXJyb3I7XG4gIH1cblxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY29tcGxleGl0eVxuICBfY29udmVydEVycm9yKGVycikge1xuICAgIGNvbnN0IGVycm9yUmVzcG9uc2UgPSBlcnIucmVzcG9uc2UgfHwge307XG4gICAgY29uc3QgZXJyb3JEYXRhID0gZXJyb3JSZXNwb25zZS5kYXRhIHx8IHt9O1xuICAgIGNvbnN0IHN0YXR1cyA9IGVycm9yUmVzcG9uc2Uuc3RhdHVzIHx8IGVyci5zdGF0dXM7XG4gICAgY29uc3QgdXJsID0gZXJyPy5jb25maWc/LnVybDtcblxuICAgIGNvbnN0IGVyck1zZyA9IGVycm9yRGF0YS5tZXNzYWdlIHx8IGVyci5tZXNzYWdlO1xuICAgIGNvbnN0IGVyck1zZ0RlZmF1bHQgPSBlcnJvckRhdGEubWVzc2FnZSB8fCBlcnIuY29kZSB8fCBlcnIubWVzc2FnZTtcblxuICAgIHN3aXRjaCAoc3RhdHVzKSB7XG4gICAgY2FzZSA0MDA6XG4gICAgICByZXR1cm4gbmV3IFZhbGlkYXRpb25FcnJvcihlcnJNc2csIGVycm9yRGF0YS5kZXRhaWxzIHx8IGVyci5kZXRhaWxzLCB1cmwpO1xuICAgIGNhc2UgNDAxOlxuICAgICAgcmV0dXJuIG5ldyBVbmF1dGhvcml6ZWRFcnJvcihlcnJNc2csIHVybCk7XG4gICAgY2FzZSA0MDM6XG4gICAgICByZXR1cm4gbmV3IEZvcmJpZGRlbkVycm9yKGVyck1zZywgdXJsKTtcbiAgICBjYXNlIDQwNDpcbiAgICAgIHJldHVybiBuZXcgTm90Rm91bmRFcnJvcihlcnJNc2csIHVybCk7XG4gICAgY2FzZSA0MDk6XG4gICAgICByZXR1cm4gbmV3IENvbmZsaWN0RXJyb3IoZXJyTXNnLCB1cmwpO1xuICAgIGNhc2UgNDI5OlxuICAgICAgcmV0dXJuIG5ldyBUb29NYW55UmVxdWVzdHNFcnJvcihlcnJNc2csIGVycm9yRGF0YS5tZXRhZGF0YSB8fCBlcnIubWV0YWRhdGEsIHVybCk7XG4gICAgY2FzZSA1MDA6XG4gICAgICByZXR1cm4gbmV3IEludGVybmFsRXJyb3IoZXJyTXNnLCB1cmwpO1xuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4gbmV3IEFwaUVycm9yKEFwaUVycm9yLCBlcnJNc2dEZWZhdWx0LCBzdGF0dXMsIHVybCk7XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogSFRUUCBjbGllbnQgc2VydmljZSBtb2NrIGZvciB0ZXN0c1xuICovXG5leHBvcnQgY2xhc3MgSHR0cENsaWVudE1vY2sgZXh0ZW5kcyBIdHRwQ2xpZW50IHtcbiAgX3JlcXVlc3RGbjogYW55O1xuICAvKipcbiAgICogQ29uc3RydWN0cyBIVFRQIGNsaWVudCBtb2NrXG4gICAqIEBwYXJhbSB7RnVuY3Rpb24ob3B0aW9uczpPYmplY3QpOlByb21pc2V9IHJlcXVlc3RGbiBtb2NrZWQgcmVxdWVzdCBmdW5jdGlvblxuICAgKiBAcGFyYW0ge051bWJlcn0gdGltZW91dCByZXF1ZXN0IHRpbWVvdXQgaW4gc2Vjb25kc1xuICAgKiBAcGFyYW0ge1JldHJ5T3B0aW9uc30gcmV0cnlPcHRzIHJldHJ5IG9wdGlvbnNcbiAgICovXG4gIGNvbnN0cnVjdG9yKHJlcXVlc3RGbiwgdGltZW91dD8sIHJldHJ5T3B0cz8pIHtcbiAgICBzdXBlcih0aW1lb3V0LCByZXRyeU9wdHMpO1xuICAgIHRoaXMuX3JlcXVlc3RGbiA9IHJlcXVlc3RGbjtcbiAgfVxuXG4gIF9tYWtlUmVxdWVzdCguLi5hcmdzKSB7XG4gICAgcmV0dXJuIHRoaXMuX3JlcXVlc3RGbiguLi5hcmdzKTtcbiAgfVxuXG59XG5cbi8qKlxuICogcmV0cnkgb3B0aW9uc1xuICovXG5leHBvcnQgZGVjbGFyZSB0eXBlIFJldHJ5T3B0aW9ucyA9IHtcblxuICAvKipcbiAgICogdGhlIG51bWJlciBvZiBhdHRlbXB0cyB0byByZXRyeSBmYWlsZWQgcmVxdWVzdCwgZGVmYXVsdCA1XG4gICAqL1xuICByZXRyaWVzPzogbnVtYmVyLFxuXG4gIC8qKlxuICAgKiBtaW5pbXVtIGRlbGF5IGluIHNlY29uZHMgYmVmb3JlIHJldHJ5aW5nLCBkZWZhdWx0IDFcbiAgICovXG4gIG1pbkRlbGF5SW5TZWNvbmRzPzogbnVtYmVyLFxuICBcbiAgLyoqXG4gICAqIG1heGltdW0gZGVsYXkgaW4gc2Vjb25kcyBiZWZvcmUgcmV0cnlpbmcsIGRlZmF1bHQgMzBcbiAgICovXG4gIG1heERlbGF5SW5TZWNvbmRzPzogbnVtYmVyLFxuXG4gIC8qKlxuICAgKiB0aW1lb3V0IGluIG1pbnV0ZXMgZm9yIGxvbmcgcnVubmluZyByZXF1ZXN0cywgZGVmYXVsdCAxMFxuICAgKi9cbiAgbG9uZ1J1bm5pbmdSZXF1ZXN0VGltZW91dEluTWludXRlcz86IG51bWJlcixcblxuICAvKipcbiAgICogdGltZSB0byBkaXNhYmxlIG5ldyBzdWJzY3JpcHRpb25zIGZvclxuICAgKi9cbiAgc3Vic2NyaWJlQ29vbGRvd25JblNlY29uZHM/OiBudW1iZXJcbn1cbiJdLCJuYW1lcyI6WyJheGlvcyIsInJlcXVpcmUiLCJVbmF1dGhvcml6ZWRFcnJvciIsIkZvcmJpZGRlbkVycm9yIiwiQXBpRXJyb3IiLCJWYWxpZGF0aW9uRXJyb3IiLCJJbnRlcm5hbEVycm9yIiwiTm90Rm91bmRFcnJvciIsIlRvb01hbnlSZXF1ZXN0c0Vycm9yIiwiQ29uZmxpY3RFcnJvciIsIk9wdGlvbnNWYWxpZGF0b3IiLCJUaW1lb3V0RXJyb3IiLCJMb2dnZXJNYW5hZ2VyIiwiXyIsIkh0dHBDbGllbnQiLCJyZXF1ZXN0Iiwib3B0aW9ucyIsInR5cGUiLCJyZXRyeUNvdW50ZXIiLCJlbmRUaW1lIiwiRGF0ZSIsIm5vdyIsIl9tYXhSZXRyeURlbGF5IiwiX3JldHJpZXMiLCJpc0xvbmdSdW5uaW5nIiwidGltZW91dCIsIl90aW1lb3V0IiwicmV0cnlBZnRlclNlY29uZHMiLCJjYWxsYmFjayIsImUiLCJyZXMiLCJfbG9nZ2VyIiwiZGVidWciLCJzdGF0dXMiLCJoZWFkZXJzIiwiZGF0YSIsIm1ldGFkYXRhIiwicmVjb21tZW5kZWRSZXRyeVRpbWUiLCJpc05hTiIsIk1hdGgiLCJtYXgiLCJnZXRUaW1lIiwiX2xvbmdSdW5uaW5nUmVxdWVzdFRpbWVvdXQiLCJib2R5IiwicmVzcG9uc2UiLCJfbWFrZVJlcXVlc3QiLCJ1bmRlZmluZWQiLCJlcnIiLCJfaGFuZGxlRXJyb3IiLCJtZXNzYWdlIiwiaW5mbyIsImZsb29yIiwiX2hhbmRsZVJldHJ5Iiwib3B0aW9uc1RvTG9nIiwiY2xvbmVEZWVwIiwiSlNPTiIsInN0cmluZ2lmeSIsInRyYW5zaXRpb25hbCIsImNsYXJpZnlUaW1lb3V0RXJyb3IiLCJfd2FpdCIsInBhdXNlIiwiUHJvbWlzZSIsInNldFRpbWVvdXQiLCJyZXRyeUFmdGVyIiwiZXJyb3IiLCJfY29udmVydEVycm9yIiwiaW5jbHVkZXMiLCJuYW1lIiwibWluIiwicG93IiwiX21pblJldHJ5RGVsYXkiLCJyZXRyeVRpbWUiLCJjZWlsIiwiZXJyb3JSZXNwb25zZSIsImVycm9yRGF0YSIsInVybCIsImNvbmZpZyIsImVyck1zZyIsImVyck1zZ0RlZmF1bHQiLCJjb2RlIiwiZGV0YWlscyIsImNvbnN0cnVjdG9yIiwicmV0cnlPcHRzIiwidmFsaWRhdG9yIiwidmFsaWRhdGVOdW1iZXIiLCJyZXRyaWVzIiwidmFsaWRhdGVOb25aZXJvIiwibWluRGVsYXlJblNlY29uZHMiLCJtYXhEZWxheUluU2Vjb25kcyIsImxvbmdSdW5uaW5nUmVxdWVzdFRpbWVvdXRJbk1pbnV0ZXMiLCJnZXRMb2dnZXIiLCJIdHRwQ2xpZW50TW9jayIsImFyZ3MiLCJfcmVxdWVzdEZuIiwicmVxdWVzdEZuIl0sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUVBLE1BQU1BLFFBQVFDLFFBQVE7QUFFdEIsU0FDRUMsaUJBQWlCLEVBQUVDLGNBQWMsRUFBRUMsUUFBUSxFQUFFQyxlQUFlLEVBQUVDLGFBQWEsRUFDM0VDLGFBQWEsRUFBRUMsb0JBQW9CLEVBQUVDLGFBQWEsUUFDN0MsaUJBQWlCO0FBQ3hCLE9BQU9DLHNCQUFzQixxQkFBcUI7QUFDbEQsT0FBT0Msa0JBQWtCLGlCQUFpQjtBQUMxQyxPQUFPQyxtQkFBNkIsWUFBWTtBQUNoRCxPQUFPQyxPQUFPLFNBQVM7QUFLUixJQUFBLEFBQU1DLGFBQU4sTUFBTUE7SUE0Qm5COzs7O0dBSUMsR0FDRCxBQUFNQyxRQUNKQyxPQUFPLEVBQUVDLE9BQU8sRUFBRSxFQUFFQyxlQUFlLENBQUMsRUFBRUMsVUFBVUMs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