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)
265 lines (264 loc) • 28.6 kB
JavaScript
;
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);
});
};
}
import TimeoutError from '../clients/timeoutError';
import LoggerManager from '../logger';
import _ from 'lodash';
const realSetTimeout = setTimeout;
const realDateNow = Date.now.bind(Date);
/**
* Creates a promise that can be used as a handle. It will not raise errors when rejected until it is explicitly
* awaited or catch is set
* @returns modified handle promise
*/ export function createHandlePromise() {
let resolve, reject;
let promise = new Promise((res, rej)=>{
resolve = res;
reject = rej;
});
promise.completed = false;
promise.resolve = (result)=>{
if (!promise.completed) {
promise.completed = true;
promise.resolved = true;
promise.result = result;
resolve(result);
}
};
promise.reject = (err)=>{
if (!promise.completed) {
promise.completed = true;
promise.rejected = true;
promise.error = err;
reject(err);
}
};
promise.timeout = (milliseconds, errorMessage)=>{
if (!promise.completed) {
let timeout = setTimeout(()=>promise.reject(new TimeoutError(errorMessage)), milliseconds);
promise.finally(()=>clearTimeout(timeout)).catch(()=>{});
}
return promise;
};
promise.catch(()=>{});
return promise;
}
/**
* Wraps a promise into a handle promise
* @param promise native promise
* @returns handle promise
*/ export function wrapHandlePromise(promise) {
let result = createHandlePromise();
promise.then(result.resolve).catch(()=>{});
promise.catch(result.reject);
return result;
}
/**
* This function ensures that a promise is returned
* @param call call
* @returns promise
*/ export function ensurePromise(call) {
return _ensurePromise.apply(this, arguments);
}
function _ensurePromise() {
_ensurePromise = _async_to_generator(function*(call) {
return call();
});
return _ensurePromise.apply(this, arguments);
}
/**
* Waits specified delay
* @param ms Milliseconds to wait
* @param options Additional options
* @return promise resolving when the delay has ended
*/ export function delay(ms, options) {
let resolve;
let timeout;
let canceled = false;
let result = new Promise((res)=>{
timeout = (options === null || options === void 0 ? void 0 : options.ignoreSinonClock) ? realSetTimeout(res, ms) : setTimeout(res, ms);
resolve = res;
});
Object.defineProperty(result, 'canceled', {
get: ()=>canceled,
enumerable: true,
configurable: true
});
result.cancel = ()=>{
canceled = true;
clearTimeout(timeout);
resolve();
};
return result;
}
/**
* Assembles log4js config from logging level map
* @param {Object} [config] log4js config
* @param {String} [config.defaultLevel = 'INFO'] Default logging level
* @param {Object} [config.levels] Logging levels
* @return {Object} Log4js config
*/ export function assembleLog4jsConfig(config = {}) {
let appenders = {
console: {
type: 'console'
}
};
let categories = {
default: {
appenders: Object.keys(appenders),
level: config.defaultLevel || 'INFO'
}
};
Object.keys(config.levels || {}).forEach((category)=>{
categories[category] = {
appenders: Object.keys(appenders),
level: config.levels[category]
};
});
return {
appenders,
categories
};
}
/**
* Waits untill specified callable will pass successfully and return true. Uses log4js logger named `helpers.wait`
* @param {() => boolean|Promise<boolean>} callable Callable to call until it returns true
* @param {Number} [intervalInMs = 25] Interval in milliseconds between the checks
* @param {WaitOptions & DelayOptions} [options] Additional wait options
* @return {Promise} Promise resolving with callable return value when waited
* @throws {Error|TimeoutError} Error from the callable or timeout error when timed out
*/ // eslint-disable-next-line complexity
export function wait(callable) {
return _wait.apply(this, arguments);
}
function _wait() {
_wait = _async_to_generator(function*(callable, intervalInMs = 25, options) {
const logger = LoggerManager.getLogger('helpers.wait');
if (typeof intervalInMs === 'object') {
// for backward compatibility
options = intervalInMs;
intervalInMs = _.defaultTo(options.intervalInMs, 1000);
}
const dateNow = (options === null || options === void 0 ? void 0 : options.ignoreSinonClock) ? realDateNow : ()=>Date.now();
let result = false, lastError;
let timesAt = dateNow() + _.defaultTo(options === null || options === void 0 ? void 0 : options.timeoutInMs, 30000);
while(!result && dateNow() < timesAt){
try {
result = yield callable();
} catch (err) {
lastError = err;
logger.debug('The executor failed', err);
if (dateNow() >= timesAt) {
throw err;
}
} finally{
if (!result) {
logger.debug('Waiting because the result is', result);
yield delay(intervalInMs, options);
}
}
}
if (dateNow() >= timesAt) {
if (lastError) {
throw lastError;
}
throw new TimeoutError('Timed out till specified callable returns true');
}
return result;
});
return _wait.apply(this, arguments);
}
/**
* Waits untill specified callable will pass successfully and return true. Uses log4js logger named `helpers.wait`
* @param {() => boolean|Promise<boolean>} callable Callable to call until it returns true
* @param {Number} [intervalInMs = 25] Interval in milliseconds between the checks
* @param {WaitOptions & DelayOptions} [options] Additional wait options
* @return {Promise} Promise resolving with callable return value when waited
* @throws {Error|TimeoutError} Error from the callable or timeout error when timed out
*/ export function waitTrue(callable, intervalInMs = 25, options) {
return wait(callable, intervalInMs, options);
}
/**
* Waits untill specified callable will pass successfully. Uses log4js logger named `helpers.wait`
* @param {() => boolean|Promise<boolean>} callable Callable to call
* @param {Number} [intervalInMs = 25] Interval in milliseconds between the checks
* @param {WaitOptions & DelayOptions} [options] Additional wait options
* @return {Promise} Promise resolving with callable return value when waited
* @throws {Error|TimeoutError} Error from the callable or timeout error when timed out
*/ export function waitPass(callable) {
return _waitPass.apply(this, arguments);
}
function _waitPass() {
_waitPass = _async_to_generator(function*(callable, intervalInMs = 25, options) {
let result;
yield wait(/*#__PURE__*/ _async_to_generator(function*() {
result = yield callable();
return true;
}), intervalInMs, options);
return result;
});
return _waitPass.apply(this, arguments);
}
/**
* Waits untill specified callable successfully returns any non-undefined value. Uses log4js logger named `helpers.wait`
* @param callable Callable to call
* @param intervalInMs Interval in milliseconds between the checks
* @param options Additional wait options
* @return Promise resolving with callable return value when waited
* @throws {Error|TimeoutError} Error from the callable or timeout error when timed out
*/ export function waitAny(callable) {
return _waitAny.apply(this, arguments);
}
function _waitAny() {
_waitAny = _async_to_generator(function*(callable, intervalInMs = 25, options) {
let result;
yield waitTrue(/*#__PURE__*/ _async_to_generator(function*() {
result = yield callable();
if (result !== undefined) {
return true;
}
}), intervalInMs, options);
return result;
});
return _waitAny.apply(this, arguments);
}
/**
* Calculates exponential backoff delay. At the initial iteration, there is no delay. At the next iteration, the delay
* is `startDelay`. Further, at the every next iteration the previous delay multiplies to 2
* @param iteration current iteration, where 0 is initial iteration without delaying
* @param startDelay start delay
* @param maxDelay maximum delay
*/ export function expBackoffDelay(iteration, startDelay, maxDelay) {
if (iteration === 0) {
return 0;
}
return Math.min(startDelay * Math.pow(2, iteration - 1), maxDelay);
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCBUaW1lb3V0RXJyb3IgZnJvbSAnLi4vY2xpZW50cy90aW1lb3V0RXJyb3InO1xuaW1wb3J0IExvZ2dlck1hbmFnZXIgZnJvbSAnLi4vbG9nZ2VyJztcbmltcG9ydCB7UHJvbWlzZU9yTm90fSBmcm9tICcuLi90eXBlcy91dGlsJztcbmltcG9ydCBfIGZyb20gJ2xvZGFzaCc7XG5cbmNvbnN0IHJlYWxTZXRUaW1lb3V0ID0gc2V0VGltZW91dDtcbmNvbnN0IHJlYWxEYXRlTm93ID0gRGF0ZS5ub3cuYmluZChEYXRlKTtcblxuLyoqIEFuIGV4dGVuZGVkIHByb21pc2Ugd2l0aCBhZGRpdGlvbmFsIHByb3BlcnRpZXMgYW5kIG1ldGhvZHMgKi9cbmV4cG9ydCBpbnRlcmZhY2UgSGFuZGxlUHJvbWlzZTxUPiBleHRlbmRzIFByb21pc2U8VD4ge1xuICAvKiogV2hldGhlciB0aGUgcHJvbWlzZSBpcyByZXNvbHZlZCBvciByZWplY3RlZCAqL1xuICBjb21wbGV0ZWQ6IGJvb2xlYW4sXG4gIC8qKiBXaGV0aGVyIHRoZSBwcm9taXNlIGlzIHJlc29sdmVkICovXG4gIHJlc29sdmVkPzogYm9vbGVhbixcbiAgLyoqIFdoZXRoZXIgdGhlIHByb21pc2UgaXMgcmVqZWN0ZWQgKi9cbiAgcmVqZWN0ZWQ/OiBib29sZWFuLFxuICAvKiogUmVzdWx0IHZhbHVlIHRoZSBwcm9taXNlIHJlc29sdmVkIHdpdGggKi9cbiAgcmVzdWx0PzogVCxcbiAgLyoqIEVycm9yIHRoZSBwcm9taXNlIHJlamVjdGVkIHdpdGggKi9cbiAgZXJyb3I/OiBFcnJvcixcbiAgLyoqXG4gICAqIFJlc29sdmVzIHRoZSBwcm9taXNlIHdpdGggc3BlY2lmaWVkIHZhbHVlXG4gICAqIEBwYXJhbSByZXN1bHQgVmFsdWUgdG8gcmVzb2x2ZSB0aGUgcHJvbWlzZSB3aXRoXG4gICAqL1xuICByZXNvbHZlKHJlc3VsdD86IFQpOiB2b2lkLFxuICAvKipcbiAgICogUmVqZWN0cyB0aGUgcHJvbWlzZSB3aXRoIHNwZWNpZmllZCBlcnJvclxuICAgKiBAcGFyYW0gZXJyIEVycm9yIHRvIHJlamVjdCB0aGUgcHJvbWlzZSB3aXRoXG4gICAqL1xuICByZWplY3QoZXJyOiBFcnJvcik6IHZvaWQsXG4gIC8qKlxuICAgKiBBZGRzIGEgdGltZW91dCB0byByZWplY3QgdGhlIHByb21pc2Ugd2l0aCBgVGltZW91dEVycm9yYFxuICAgKiBAcGFyYW0gbWlsbGlzZWNvbmRzIHRpbWVvdXQgaW4gbWlsbGlzZWNvbmRzXG4gICAqIEBwYXJhbSBlcnJvck1lc3NhZ2UgZXJyb3IgbWVzc2FnZVxuICAgKiBAcmV0dXJucyBzZWxmXG4gICAqL1xuICB0aW1lb3V0KG1pbGxpc2Vjb25kczogbnVtYmVyLCBlcnJvck1lc3NhZ2U6IHN0cmluZyk6IEhhbmRsZVByb21pc2U8VD5cbn1cblxuLyoqXG4gKiBDcmVhdGVzIGEgcHJvbWlzZSB0aGF0IGNhbiBiZSB1c2VkIGFzIGEgaGFuZGxlLiBJdCB3aWxsIG5vdCByYWlzZSBlcnJvcnMgd2hlbiByZWplY3RlZCB1bnRpbCBpdCBpcyBleHBsaWNpdGx5XG4gKiBhd2FpdGVkIG9yIGNhdGNoIGlzIHNldFxuICogQHJldHVybnMgbW9kaWZpZWQgaGFuZGxlIHByb21pc2VcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUhhbmRsZVByb21pc2U8VD4oKTogSGFuZGxlUHJvbWlzZTxUPiB7XG4gIGxldCByZXNvbHZlLCByZWplY3Q7XG4gIGxldCBwcm9taXNlID0gbmV3IFByb21pc2UoKHJlcywgcmVqKSA9PiB7XG4gICAgcmVzb2x2ZSA9IHJlcztcbiAgICByZWplY3QgPSByZWo7XG4gIH0pIGFzIEhhbmRsZVByb21pc2U8VD47XG4gIHByb21pc2UuY29tcGxldGVkID0gZmFsc2U7XG4gIHByb21pc2UucmVzb2x2ZSA9IChyZXN1bHQpID0+IHtcbiAgICBpZiAoIXByb21pc2UuY29tcGxldGVkKSB7XG4gICAgICBwcm9taXNlLmNvbXBsZXRlZCA9IHRydWU7XG4gICAgICBwcm9taXNlLnJlc29sdmVkID0gdHJ1ZTtcbiAgICAgIHByb21pc2UucmVzdWx0ID0gcmVzdWx0O1xuICAgICAgcmVzb2x2ZShyZXN1bHQpO1xuICAgIH1cbiAgfTtcbiAgcHJvbWlzZS5yZWplY3QgPSAoZXJyKSA9PiB7XG4gICAgaWYgKCFwcm9taXNlLmNvbXBsZXRlZCkge1xuICAgICAgcHJvbWlzZS5jb21wbGV0ZWQgPSB0cnVlO1xuICAgICAgcHJvbWlzZS5yZWplY3RlZCA9IHRydWU7XG4gICAgICBwcm9taXNlLmVycm9yID0gZXJyO1xuICAgICAgcmVqZWN0KGVycik7XG4gICAgfVxuICB9O1xuICBwcm9taXNlLnRpbWVvdXQgPSAobWlsbGlzZWNvbmRzLCBlcnJvck1lc3NhZ2UpID0+IHtcbiAgICBpZiAoIXByb21pc2UuY29tcGxldGVkKSB7XG4gICAgICBsZXQgdGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4gcHJvbWlzZS5yZWplY3QobmV3IFRpbWVvdXRFcnJvcihlcnJvck1lc3NhZ2UpKSwgbWlsbGlzZWNvbmRzKTtcbiAgICAgIHByb21pc2UuZmluYWxseSgoKSA9PiBjbGVhclRpbWVvdXQodGltZW91dCkpLmNhdGNoKCgpID0+IHt9KTtcbiAgICB9XG4gICAgcmV0dXJuIHByb21pc2U7XG4gIH07XG4gIHByb21pc2UuY2F0Y2goKCkgPT4ge30pO1xuICByZXR1cm4gcHJvbWlzZTtcbn1cblxuLyoqXG4gKiBXcmFwcyBhIHByb21pc2UgaW50byBhIGhhbmRsZSBwcm9taXNlXG4gKiBAcGFyYW0gcHJvbWlzZSBuYXRpdmUgcHJvbWlzZVxuICogQHJldHVybnMgaGFuZGxlIHByb21pc2VcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHdyYXBIYW5kbGVQcm9taXNlPFQ+KHByb21pc2U6IFByb21pc2U8VD4pOiBIYW5kbGVQcm9taXNlPFQ+IHtcbiAgbGV0IHJlc3VsdCA9IGNyZWF0ZUhhbmRsZVByb21pc2U8VD4oKTtcbiAgcHJvbWlzZS50aGVuKHJlc3VsdC5yZXNvbHZlKS5jYXRjaCgoKSA9PiB7fSk7XG4gIHByb21pc2UuY2F0Y2gocmVzdWx0LnJlamVjdCk7XG4gIHJldHVybiByZXN1bHQ7XG59XG5cbi8qKlxuICogVGhpcyBmdW5jdGlvbiBlbnN1cmVzIHRoYXQgYSBwcm9taXNlIGlzIHJldHVybmVkXG4gKiBAcGFyYW0gY2FsbCBjYWxsXG4gKiBAcmV0dXJucyBwcm9taXNlXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBlbnN1cmVQcm9taXNlPFQ+KGNhbGw6ICgpID0+IFByb21pc2VPck5vdDxUPik6IFByb21pc2U8VD4ge1xuICByZXR1cm4gY2FsbCgpO1xufVxuXG4vKiogQWRkaXRpb25hbCBkZWxheSBvcHRpb25zICovXG5leHBvcnQgdHlwZSBEZWxheU9wdGlvbnMgPSB7XG4gIC8qKiBXaGV0aGVyIHRvIGRlbGF5IHJlYWwgdGltZSwgaWYgYSBzdHViYmVkIGZyb3plbiBgc2lub25gIGNsb2NrIGlzIHVzZWQgKi9cbiAgaWdub3JlU2lub25DbG9jaz86IGJvb2xlYW5cbn07XG5cbi8qKlxuICogV2FpdHMgc3BlY2lmaWVkIGRlbGF5XG4gKiBAcGFyYW0gbXMgTWlsbGlzZWNvbmRzIHRvIHdhaXRcbiAqIEBwYXJhbSBvcHRpb25zIEFkZGl0aW9uYWwgb3B0aW9uc1xuICogQHJldHVybiBwcm9taXNlIHJlc29sdmluZyB3aGVuIHRoZSBkZWxheSBoYXMgZW5kZWRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRlbGF5KG1zOiBudW1iZXIsIG9wdGlvbnM/OiBEZWxheU9wdGlvbnMpOiBEZWxheVByb21pc2Uge1xuICBsZXQgcmVzb2x2ZTogKCkgPT4gdm9pZDtcbiAgbGV0IHRpbWVvdXQ6IE5vZGVKUy5UaW1lb3V0O1xuICBsZXQgY2FuY2VsZWQgPSBmYWxzZTtcbiAgbGV0IHJlc3VsdCA9IG5ldyBQcm9taXNlPHZvaWQ+KHJlcyA9PiB7XG4gICAgdGltZW91dCA9IG9wdGlvbnM/Lmlnbm9yZVNpbm9uQ2xvY2sgPyByZWFsU2V0VGltZW91dChyZXMsIG1zKSA6IHNldFRpbWVvdXQocmVzLCBtcyk7XG4gICAgcmVzb2x2ZSA9IHJlcztcbiAgfSkgYXMgRGVsYXlQcm9taXNlO1xuICBPYmplY3QuZGVmaW5lUHJvcGVydHkocmVzdWx0LCAnY2FuY2VsZWQnLCB7XG4gICAgZ2V0OiAoKSA9PiBjYW5jZWxlZCxcbiAgICBlbnVtZXJhYmxlOiB0cnVlLFxuICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZVxuICB9KTtcbiAgcmVzdWx0LmNhbmNlbCA9ICgpID0+IHtcbiAgICBjYW5jZWxlZCA9IHRydWU7XG4gICAgY2xlYXJUaW1lb3V0KHRpbWVvdXQpO1xuICAgIHJlc29sdmUoKTtcbiAgfTtcbiAgcmV0dXJuIHJlc3VsdDtcbn1cblxuLyoqXG4gKiBEZWxheSBwcm9taXNlXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRGVsYXlQcm9taXNlIGV4dGVuZHMgUHJvbWlzZTx2b2lkPiB7XG4gIC8qKlxuICAgKiBSZXR1cm5zIHdoZXRoZXIgdGhlIHByb21pc2UgaXMgY2FuY2VsZWRcbiAgICogQHJldHVybnMgd2hldGhlciBjYW5jZWxlZFxuICAgKi9cbiAgZ2V0IGNhbmNlbGVkKCk6IGJvb2xlYW47XG4gIC8qKlxuICAgKiBDYW5jZWxzIHdhaXRpbmcgYW5kIHJlc29sdmVzIHRoZSBwcm9taXNlIGltbWVkaWF0ZWx5XG4gICAqL1xuICBjYW5jZWwoKTogdm9pZDtcbn1cblxuLyoqXG4gKiBBc3NlbWJsZXMgbG9nNGpzIGNvbmZpZyBmcm9tIGxvZ2dpbmcgbGV2ZWwgbWFwXG4gKiBAcGFyYW0ge09iamVjdH0gW2NvbmZpZ10gbG9nNGpzIGNvbmZpZ1xuICogQHBhcmFtIHtTdHJpbmd9IFtjb25maWcuZGVmYXVsdExldmVsID0gJ0lORk8nXSBEZWZhdWx0IGxvZ2dpbmcgbGV2ZWxcbiAqIEBwYXJhbSB7T2JqZWN0fSBbY29uZmlnLmxldmVsc10gTG9nZ2luZyBsZXZlbHNcbiAqIEByZXR1cm4ge09iamVjdH0gTG9nNGpzIGNvbmZpZ1xuICovXG5leHBvcnQgZnVuY3Rpb24gYXNzZW1ibGVMb2c0anNDb25maWcoY29uZmlnOiBhbnkgPSB7fSkge1xuICBsZXQgYXBwZW5kZXJzID0ge2NvbnNvbGU6IHt0eXBlOiAnY29uc29sZSd9fTtcbiAgbGV0IGNhdGVnb3JpZXMgPSB7XG4gICAgZGVmYXVsdDoge1xuICAgICAgYXBwZW5kZXJzOiBPYmplY3Qua2V5cyhhcHBlbmRlcnMpLFxuICAgICAgbGV2ZWw6IGNvbmZpZy5kZWZhdWx0TGV2ZWwgfHwgJ0lORk8nXG4gICAgfVxuICB9O1xuICBPYmplY3Qua2V5cyhjb25maWcubGV2ZWxzIHx8IHt9KS5mb3JFYWNoKChjYXRlZ29yeSkgPT4ge1xuICAgIGNhdGVnb3JpZXNbY2F0ZWdvcnldID0ge1xuICAgICAgYXBwZW5kZXJzOiBPYmplY3Qua2V5cyhhcHBlbmRlcnMpLFxuICAgICAgbGV2ZWw6IGNvbmZpZy5sZXZlbHNbY2F0ZWdvcnldXG4gICAgfTtcbiAgfSk7XG4gIHJldHVybiB7YXBwZW5kZXJzLCBjYXRlZ29yaWVzfTtcbn1cblxuLyoqIE9wdGlvbnMgZm9yIGB3YWl0KmAgZnVuY3Rpb25zICovXG5leHBvcnQgdHlwZSBXYWl0T3B0aW9ucyA9IHtcbiAgLyoqIFdhaXQgdGltZW91dCBpbiBtaWxsaXNlY29uZHMuIERlZmF1bHRzIHRvIGAzMDAwMGAgKi9cbiAgdGltZW91dEluTXM/OiBudW1iZXJcbn07XG5cbi8qKlxuICogV2FpdHMgdW50aWxsIHNwZWNpZmllZCBjYWxsYWJsZSB3aWxsIHBhc3Mgc3VjY2Vzc2Z1bGx5IGFuZCByZXR1cm4gdHJ1ZS4gVXNlcyBsb2c0anMgbG9nZ2VyIG5hbWVkIGBoZWxwZXJzLndhaXRgXG4gKiBAcGFyYW0geygpID0+IGJvb2xlYW58UHJvbWlzZTxib29sZWFuPn0gY2FsbGFibGUgQ2FsbGFibGUgdG8gY2FsbCB1bnRpbCBpdCByZXR1cm5zIHRydWVcbiAqIEBwYXJhbSB7TnVtYmVyfSBbaW50ZXJ2YWxJbk1zID0gMjVdIEludGVydmFsIGluIG1pbGxpc2Vjb25kcyBiZXR3ZWVuIHRoZSBjaGVja3NcbiAqIEBwYXJhbSB7V2FpdE9wdGlvbnMgJiBEZWxheU9wdGlvbnN9IFtvcHRpb25zXSBBZGRpdGlvbmFsIHdhaXQgb3B0aW9uc1xuICogQHJldHVybiB7UHJvbWlzZX0gUHJvbWlzZSByZXNvbHZpbmcgd2l0aCBjYWxsYWJsZSByZXR1cm4gdmFsdWUgd2hlbiB3YWl0ZWRcbiAqIEB0aHJvd3Mge0Vycm9yfFRpbWVvdXRFcnJvcn0gRXJyb3IgZnJvbSB0aGUgY2FsbGFibGUgb3IgdGltZW91dCBlcnJvciB3aGVuIHRpbWVkIG91dFxuICovXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY29tcGxleGl0eVxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHdhaXQoY2FsbGFibGUsIGludGVydmFsSW5NcyA9IDI1LCBvcHRpb25zPzogV2FpdE9wdGlvbnMgJiBEZWxheU9wdGlvbnMpIHtcbiAgY29uc3QgbG9nZ2VyID0gTG9nZ2VyTWFuYWdlci5nZXRMb2dnZXIoJ2hlbHBlcnMud2FpdCcpO1xuICBpZiAodHlwZW9mIGludGVydmFsSW5NcyA9PT0gJ29iamVjdCcpIHtcbiAgICAvLyBmb3IgYmFja3dhcmQgY29tcGF0aWJpbGl0eVxuICAgIG9wdGlvbnMgPSBpbnRlcnZhbEluTXM7XG4gICAgaW50ZXJ2YWxJbk1zID0gXy5kZWZhdWx0VG8oKG9wdGlvbnMgYXMgYW55KS5pbnRlcnZhbEluTXMsIDEwMDApO1xuICB9XG4gIGNvbnN0IGRhdGVOb3cgPSBvcHRpb25zPy5pZ25vcmVTaW5vbkNsb2NrID8gcmVhbERhdGVOb3cgOiAoKSA9PiBEYXRlLm5vdygpO1xuICBsZXQgcmVzdWx0ID0gZmFsc2UsIGxhc3RFcnJvcjtcbiAgbGV0IHRpbWVzQXQgPSBkYXRlTm93KCkgKyBfLmRlZmF1bHRUbyhvcHRpb25zPy50aW1lb3V0SW5NcywgMzAwMDApO1xuICB3aGlsZSAoIXJlc3VsdCAmJiBkYXRlTm93KCkgPCB0aW1lc0F0KSB7XG4gICAgdHJ5IHtcbiAgICAgIHJlc3VsdCA9IGF3YWl0IGNhbGxhYmxlKCk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBsYXN0RXJyb3IgPSBlcnI7XG4gICAgICBsb2dnZXIuZGVidWcoJ1RoZSBleGVjdXRvciBmYWlsZWQnLCBlcnIpO1xuICAgICAgaWYgKGRhdGVOb3coKSA+PSB0aW1lc0F0KSB7XG4gICAgICAgIHRocm93IGVycjtcbiAgICAgIH1cbiAgICB9IGZpbmFsbHkge1xuICAgICAgaWYgKCFyZXN1bHQpIHtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKCdXYWl0aW5nIGJlY2F1c2UgdGhlIHJlc3VsdCBpcycsIHJlc3VsdCk7XG4gICAgICAgIGF3YWl0IGRlbGF5KGludGVydmFsSW5Ncywgb3B0aW9ucyk7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIGlmIChkYXRlTm93KCkgPj0gdGltZXNBdCkge1xuICAgIGlmIChsYXN0RXJyb3IpIHtcbiAgICAgIHRocm93IGxhc3RFcnJvcjtcbiAgICB9XG4gICAgdGhyb3cgbmV3IFRpbWVvdXRFcnJvcignVGltZWQgb3V0IHRpbGwgc3BlY2lmaWVkIGNhbGxhYmxlIHJldHVybnMgdHJ1ZScpO1xuICB9XG4gIHJldHVybiByZXN1bHQ7XG59XG5cbi8qKlxuICogV2FpdHMgdW50aWxsIHNwZWNpZmllZCBjYWxsYWJsZSB3aWxsIHBhc3Mgc3VjY2Vzc2Z1bGx5IGFuZCByZXR1cm4gdHJ1ZS4gVXNlcyBsb2c0anMgbG9nZ2VyIG5hbWVkIGBoZWxwZXJzLndhaXRgXG4gKiBAcGFyYW0geygpID0+IGJvb2xlYW58UHJvbWlzZTxib29sZWFuPn0gY2FsbGFibGUgQ2FsbGFibGUgdG8gY2FsbCB1bnRpbCBpdCByZXR1cm5zIHRydWVcbiAqIEBwYXJhbSB7TnVtYmVyfSBbaW50ZXJ2YWxJbk1zID0gMjVdIEludGVydmFsIGluIG1pbGxpc2Vjb25kcyBiZXR3ZWVuIHRoZSBjaGVja3NcbiAqIEBwYXJhbSB7V2FpdE9wdGlvbnMgJiBEZWxheU9wdGlvbnN9IFtvcHRpb25zXSBBZGRpdGlvbmFsIHdhaXQgb3B0aW9uc1xuICogQHJldHVybiB7UHJvbWlzZX0gUHJvbWlzZSByZXNvbHZpbmcgd2l0aCBjYWxsYWJsZSByZXR1cm4gdmFsdWUgd2hlbiB3YWl0ZWRcbiAqIEB0aHJvd3Mge0Vycm9yfFRpbWVvdXRFcnJvcn0gRXJyb3IgZnJvbSB0aGUgY2FsbGFibGUgb3IgdGltZW91dCBlcnJvciB3aGVuIHRpbWVkIG91dFxuICovXG5leHBvcnQgZnVuY3Rpb24gd2FpdFRydWUoY2FsbGFibGUsIGludGVydmFsSW5NcyA9IDI1LCBvcHRpb25zPzogV2FpdE9wdGlvbnMgJiBEZWxheU9wdGlvbnMpIHtcbiAgcmV0dXJuIHdhaXQoY2FsbGFibGUsIGludGVydmFsSW5Ncywgb3B0aW9ucyk7XG59XG5cbi8qKlxuICogV2FpdHMgdW50aWxsIHNwZWNpZmllZCBjYWxsYWJsZSB3aWxsIHBhc3Mgc3VjY2Vzc2Z1bGx5LiBVc2VzIGxvZzRqcyBsb2dnZXIgbmFtZWQgYGhlbHBlcnMud2FpdGBcbiAqIEBwYXJhbSB7KCkgPT4gYm9vbGVhbnxQcm9taXNlPGJvb2xlYW4+fSBjYWxsYWJsZSBDYWxsYWJsZSB0byBjYWxsXG4gKiBAcGFyYW0ge051bWJlcn0gW2ludGVydmFsSW5NcyA9IDI1XSBJbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgYmV0d2VlbiB0aGUgY2hlY2tzXG4gKiBAcGFyYW0ge1dhaXRPcHRpb25zICYgRGVsYXlPcHRpb25zfSBbb3B0aW9uc10gQWRkaXRpb25hbCB3YWl0IG9wdGlvbnNcbiAqIEByZXR1cm4ge1Byb21pc2V9IFByb21pc2UgcmVzb2x2aW5nIHdpdGggY2FsbGFibGUgcmV0dXJuIHZhbHVlIHdoZW4gd2FpdGVkXG4gKiBAdGhyb3dzIHtFcnJvcnxUaW1lb3V0RXJyb3J9IEVycm9yIGZyb20gdGhlIGNhbGxhYmxlIG9yIHRpbWVvdXQgZXJyb3Igd2hlbiB0aW1lZCBvdXRcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHdhaXRQYXNzPFQgPSB2b2lkPihcbiAgY2FsbGFibGU6ICgpID0+IFByb21pc2VPck5vdDxUPixcbiAgaW50ZXJ2YWxJbk1zID0gMjUsXG4gIG9wdGlvbnM/OiBXYWl0T3B0aW9ucyAmIERlbGF5T3B0aW9uc1xuKTogUHJvbWlzZTxUPiB7XG4gIGxldCByZXN1bHQ7XG4gIGF3YWl0IHdhaXQoYXN5bmMgKCkgPT4ge1xuICAgIHJlc3VsdCA9IGF3YWl0IGNhbGxhYmxlKCk7XG4gICAgcmV0dXJuIHRydWU7XG4gIH0sIGludGVydmFsSW5Ncywgb3B0aW9ucyk7XG4gIHJldHVybiByZXN1bHQ7XG59XG5cbi8qKlxuICogV2FpdHMgdW50aWxsIHNwZWNpZmllZCBjYWxsYWJsZSBzdWNjZXNzZnVsbHkgcmV0dXJucyBhbnkgbm9uLXVuZGVmaW5lZCB2YWx1ZS4gVXNlcyBsb2c0anMgbG9nZ2VyIG5hbWVkIGBoZWxwZXJzLndhaXRgXG4gKiBAcGFyYW0gY2FsbGFibGUgQ2FsbGFibGUgdG8gY2FsbFxuICogQHBhcmFtIGludGVydmFsSW5NcyBJbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgYmV0d2VlbiB0aGUgY2hlY2tzXG4gKiBAcGFyYW0gb3B0aW9ucyBBZGRpdGlvbmFsIHdhaXQgb3B0aW9uc1xuICogQHJldHVybiBQcm9taXNlIHJlc29sdmluZyB3aXRoIGNhbGxhYmxlIHJldHVybiB2YWx1ZSB3aGVuIHdhaXRlZFxuICogQHRocm93cyB7RXJyb3J8VGltZW91dEVycm9yfSBFcnJvciBmcm9tIHRoZSBjYWxsYWJsZSBvciB0aW1lb3V0IGVycm9yIHdoZW4gdGltZWQgb3V0XG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiB3YWl0QW55PFQ+KFxuICBjYWxsYWJsZTogKCkgPT4gUHJvbWlzZU9yTm90PFQ+LCBpbnRlcnZhbEluTXMgPSAyNSwgb3B0aW9ucz86IFdhaXRPcHRpb25zXG4pOiBQcm9taXNlPFQ+IHtcbiAgbGV0IHJlc3VsdDogVDtcbiAgYXdhaXQgd2FpdFRydWUoYXN5bmMgKCkgPT4ge1xuICAgIHJlc3VsdCA9IGF3YWl0IGNhbGxhYmxlKCk7XG4gICAgaWYgKHJlc3VsdCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gIH0sIGludGVydmFsSW5Ncywgb3B0aW9ucyk7XG4gIHJldHVybiByZXN1bHQ7XG59XG5cbi8qKlxuICogQ2FsY3VsYXRlcyBleHBvbmVudGlhbCBiYWNrb2ZmIGRlbGF5LiBBdCB0aGUgaW5pdGlhbCBpdGVyYXRpb24sIHRoZXJlIGlzIG5vIGRlbGF5LiBBdCB0aGUgbmV4dCBpdGVyYXRpb24sIHRoZSBkZWxheVxuICogaXMgYHN0YXJ0RGVsYXlgLiBGdXJ0aGVyLCBhdCB0aGUgZXZlcnkgbmV4dCBpdGVyYXRpb24gdGhlIHByZXZpb3VzIGRlbGF5IG11bHRpcGxpZXMgdG8gMlxuICogQHBhcmFtIGl0ZXJhdGlvbiBjdXJyZW50IGl0ZXJhdGlvbiwgd2hlcmUgMCBpcyBpbml0aWFsIGl0ZXJhdGlvbiB3aXRob3V0IGRlbGF5aW5nXG4gKiBAcGFyYW0gc3RhcnREZWxheSBzdGFydCBkZWxheVxuICogQHBhcmFtIG1heERlbGF5IG1heGltdW0gZGVsYXlcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGV4cEJhY2tvZmZEZWxheShpdGVyYXRpb246IG51bWJlciwgc3RhcnREZWxheTogbnVtYmVyLCBtYXhEZWxheTogbnVtYmVyKSB7XG4gIGlmIChpdGVyYXRpb24gPT09IDApIHtcbiAgICByZXR1cm4gMDtcbiAgfVxuICByZXR1cm4gTWF0aC5taW4oc3RhcnREZWxheSAqIE1hdGgucG93KDIsIGl0ZXJhdGlvbiAtIDEpLCBtYXhEZWxheSk7XG59XG4iXSwibmFtZXMiOlsiVGltZW91dEVycm9yIiwiTG9nZ2VyTWFuYWdlciIsIl8iLCJyZWFsU2V0VGltZW91dCIsInNldFRpbWVvdXQiLCJyZWFsRGF0ZU5vdyIsIkRhdGUiLCJub3ciLCJiaW5kIiwiY3JlYXRlSGFuZGxlUHJvbWlzZSIsInJlc29sdmUiLCJyZWplY3QiLCJwcm9taXNlIiwiUHJvbWlzZSIsInJlcyIsInJlaiIsImNvbXBsZXRlZCIsInJlc3VsdCIsInJlc29sdmVkIiwiZXJyIiwicmVqZWN0ZWQiLCJlcnJvciIsInRpbWVvdXQiLCJtaWxsaXNlY29uZHMiLCJlcnJvck1lc3NhZ2UiLCJmaW5hbGx5IiwiY2xlYXJUaW1lb3V0IiwiY2F0Y2giLCJ3cmFwSGFuZGxlUHJvbWlzZSIsInRoZW4iLCJlbnN1cmVQcm9taXNlIiwiY2FsbCIsImRlbGF5IiwibXMiLCJvcHRpb25zIiwiY2FuY2VsZWQiLCJpZ25vcmVTaW5vbkNsb2NrIiwiT2JqZWN0IiwiZGVmaW5lUHJvcGVydHkiLCJnZXQiLCJlbnVtZXJhYmxlIiwiY29uZmlndXJhYmxlIiwiY2FuY2VsIiwiYXNzZW1ibGVMb2c0anNDb25maWciLCJjb25maWciLCJhcHBlbmRlcnMiLCJjb25zb2xlIiwidHlwZSIsImNhdGVnb3JpZXMiLCJkZWZhdWx0Iiwia2V5cyIsImxldmVsIiwiZGVmYXVsdExldmVsIiwibGV2ZWxzIiwiZm9yRWFjaCIsImNhdGVnb3J5Iiwid2FpdCIsImNhbGxhYmxlIiwiaW50ZXJ2YWxJbk1zIiwibG9nZ2VyIiwiZ2V0TG9nZ2VyIiwiZGVmYXVsdFRvIiwiZGF0ZU5vdyIsImxhc3RFcnJvciIsInRpbWVzQXQiLCJ0aW1lb3V0SW5NcyIsImRlYnVnIiwid2FpdFRydWUiLCJ3YWl0UGFzcyIsIndhaXRBbnkiLCJ1bmRlZmluZWQiLCJleHBCYWNrb2ZmRGVsYXkiLCJpdGVyYXRpb24iLCJzdGFydERlbGF5IiwibWF4RGVsYXkiLCJNYXRoIiwibWluIiwicG93Il0sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBRUEsT0FBT0Esa0JBQWtCLDBCQUEwQjtBQUNuRCxPQUFPQyxtQkFBbUIsWUFBWTtBQUV0QyxPQUFPQyxPQUFPLFNBQVM7QUFFdkIsTUFBTUMsaUJBQWlCQztBQUN2QixNQUFNQyxjQUFjQyxLQUFLQyxHQUFHLENBQUNDLElBQUksQ0FBQ0Y7QUFpQ2xDOzs7O0NBSUMsR0FDRCxPQUFPLFNBQVNHO0lBQ2QsSUFBSUMsU0FBU0M7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