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)

265 lines (264 loc) 28.6 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); }); }; } 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+IHtcbiAgbGV0IHJlc3VsdDogVDtcbiAgYXdhaXQgd2FpdFRydWUoYXN5bmMgKCkgPT4ge1xuICAgIHJlc3VsdCA9IGF3YWl0IGNhbGxhYmxlKCk7XG4gICAgaWYgKHJlc3VsdCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gIH0sIGludGVydmFsSW5Ncywgb3B0aW9ucyk7XG4gIHJldHVybiByZXN1bHQ7XG59XG5cbi8qKlxuICogQ2FsY3VsYXRlcyBleHBvbmVudGlhbCBiYWNrb2ZmIGRlbGF5LiBBdCB0aGUgaW5pdGlhbCBpdGVyYXRpb24sIHRoZXJlIGlzIG5vIGRlbGF5LiBBdCB0aGUgbmV4dCBpdGVyYXRpb24sIHRoZSBkZWxheVxuICogaXMgYHN0YXJ0RGVsYXlgLiBGdXJ0aGVyLCBhdCB0aGUgZXZlcnkgbmV4dCBpdGVyYXRpb24gdGhlIHByZXZpb3VzIGRlbGF5IG11bHRpcGxpZXMgdG8gMlxuICogQHBhcmFtIGl0ZXJhdGlvbiBjdXJyZW50IGl0ZXJhdGlvbiwgd2hlcmUgMCBpcyBpbml0aWFsIGl0ZXJhdGlvbiB3aXRob3V0IGRlbGF5aW5nXG4gKiBAcGFyYW0gc3RhcnREZWxheSBzdGFydCBkZWxheVxuICogQHBhcmFtIG1heERlbGF5IG1heGltdW0gZGVsYXlcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGV4cEJhY2tvZmZEZWxheShpdGVyYXRpb246IG51bWJlciwgc3RhcnREZWxheTogbnVtYmVyLCBtYXhEZWxheTogbnVtYmVyKSB7XG4gIGlmIChpdGVyYXRpb24gPT09IDApIHtcbiAgICByZXR1cm4gMDtcbiAgfVxuICByZXR1cm4gTWF0aC5taW4oc3RhcnREZWxheSAqIE1hdGgucG93KDIsIGl0ZXJhdGlvbiAtIDEpLCBtYXhEZWxheSk7XG59XG4iXSwibmFtZXMiOlsiVGltZW91dEVycm9yIiwiTG9nZ2VyTWFuYWdlciIsIl8iLCJyZWFsU2V0VGltZW91dCIsInNldFRpbWVvdXQiLCJyZWFsRGF0ZU5vdyIsIkRhdGUiLCJub3ciLCJiaW5kIiwiY3JlYXRlSGFuZGxlUHJvbWlzZSIsInJlc29sdmUiLCJyZWplY3QiLCJwcm9taXNlIiwiUHJvbWlzZSIsInJlcyIsInJlaiIsImNvbXBsZXRlZCIsInJlc3VsdCIsInJlc29sdmVkIiwiZXJyIiwicmVqZWN0ZWQiLCJlcnJvciIsInRpbWVvdXQiLCJtaWxsaXNlY29uZHMiLCJlcnJvck1lc3NhZ2UiLCJmaW5hbGx5IiwiY2xlYXJUaW1lb3V0IiwiY2F0Y2giLCJ3cmFwSGFuZGxlUHJvbWlzZSIsInRoZW4iLCJlbnN1cmVQcm9taXNlIiwiY2FsbCIsImRlbGF5IiwibXMiLCJvcHRpb25zIiwiY2FuY2VsZWQiLCJpZ25vcmVTaW5vbkNsb2NrIiwiT2JqZWN0IiwiZGVmaW5lUHJvcGVydHkiLCJnZXQiLCJlbnVtZXJhYmxlIiwiY29uZmlndXJhYmxlIiwiY2FuY2VsIiwiYXNzZW1ibGVMb2c0anNDb25maWciLCJjb25maWciLCJhcHBlbmRlcnMiLCJjb25zb2xlIiwidHlwZSIsImNhdGVnb3JpZXMiLCJkZWZhdWx0Iiwia2V5cyIsImxldmVsIiwiZGVmYXVsdExldmVsIiwibGV2ZWxzIiwiZm9yRWFjaCIsImNhdGVnb3J5Iiwid2FpdCIsImNhbGxhYmxlIiwiaW50ZXJ2YWxJbk1zIiwibG9nZ2VyIiwiZ2V0TG9nZ2VyIiwiZGVmYXVsdFRvIiwiZGF0ZU5vdyIsImxhc3RFcnJvciIsInRpbWVzQXQiLCJ0aW1lb3V0SW5NcyIsImRlYnVnIiwid2FpdFRydWUiLCJ3YWl0UGFzcyIsIndhaXRBbnkiLCJ1bmRlZmluZWQiLCJleHBCYWNrb2ZmRGVsYXkiLCJpdGVyYXRpb24iLCJzdGFydERlbGF5IiwibWF4RGVsYXkiLCJNYXRoIiwibWluIiwicG93Il0sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBRUEsT0FBT0Esa0JBQWtCLDBCQUEwQjtBQUNuRCxPQUFPQyxtQkFBbUIsWUFBWTtBQUV0QyxPQUFPQyxPQUFPLFNBQVM7QUFFdkIsTUFBTUMsaUJBQWlCQztBQUN2QixNQUFNQyxjQUFjQyxLQUFLQyxHQUFHLENBQUNDLElBQUksQ0FBQ0Y7QUFpQ2xDOzs7O0NBSUMsR0FDRCxPQUFPLFNBQVNHO0lBQ2QsSUFBSUMsU0FBU0M7SUFDYixJQUFJQyxVQUFVLElBQUlDLFFBQVEsQ0FBQ0MsS0FBS0M7UUFDOUJMLFVBQVVJO1FBQ1ZILFNBQVNJO0lBQ1g7SUFDQUgsUUFBUUksU0FBUyxHQUFHO0lBQ3BCSixRQUFRRixPQUFPLEdBQUcsQ0FBQ087UUFDakIsSUFBSSxDQUFDTCxRQUFRSSxTQUFTLEVBQUU7WUFDdEJKLFFBQVFJLFNBQVMsR0FBRztZQUNwQkosUUFBUU0sUUFBUSxHQUFHO1lBQ25CTixRQUFRSyxNQUFNLEdBQUdBO1lBQ2pCUCxRQUFRTztRQUNWO0lBQ0Y7SUFDQUwsUUFBUUQsTUFBTSxHQUFHLENBQUNRO1FBQ2hCLElBQUksQ0FBQ1AsUUFBUUksU0FBUyxFQUFFO1lBQ3RCSixRQUFRSSxTQUFTLEdBQUc7WUFDcEJKLFFBQVFRLFFBQVEsR0FBRztZQUNuQlIsUUFBUVMsS0FBSyxHQUFHRjtZQUNoQlIsT0FBT1E7UUFDVDtJQUNGO0lBQ0FQLFFBQVFVLE9BQU8sR0FBRyxDQUFDQyxjQUFjQztRQUMvQixJQUFJLENBQUNaLFFBQVFJLFNBQVMsRUFBRTtZQUN0QixJQUFJTSxVQUFVbEIsV0FBVyxJQUFNUSxRQUFRRCxNQUFNLENBQUMsSUFBSVgsYUFBYXdCLGdCQUFnQkQ7WUFDL0VYLFFBQVFhLE9BQU8sQ0FBQyxJQUFNQyxhQUFhSixVQUFVSyxLQUFLLENBQUMsS0FBTztRQUM1RDtRQUNBLE9BQU9mO0lBQ1Q7SUFDQUEsUUFBUWUsS0FBSyxDQUFDLEtBQU87SUFDckIsT0FBT2Y7QUFDVDtBQUVBOzs7O0NBSUMsR0FDRCxPQUFPLFNBQVNnQixrQkFBcUJoQixPQUFtQjtJQUN0RCxJQUFJSyxTQUFTUjtJQUNiRyxRQUFRaUIsSUFBSSxDQUFDWixPQUFPUCxPQUFPLEVBQUVpQixLQUFLLENBQUMsS0FBTztJQUMxQ2YsUUFBUWUsS0FBSyxDQUFDVixPQUFPTixNQUFNO0lBQzNCLE9BQU9NO0FBQ1Q7QUFFQTs7OztDQUlDLEdBQ0QsZ0JBQXNCYSxjQUFpQkMsSUFBMkI7V0FBNUNEOztTQUFBQTtJQUFBQSxpQkFBZixvQkFBQSxVQUFnQ0MsSUFBMkI7UUFDaEUsT0FBT0E7SUFDVDtXQUZzQkQ7O0FBVXRCOzs7OztDQUtDLEdBQ0QsT0FBTyxTQUFTRSxNQUFNQyxFQUFVLEVBQUVDLE9BQXNCO0lBQ3RELElBQUl4QjtJQUNKLElBQUlZO0lBQ0osSUFBSWEsV0FBVztJQUNmLElBQUlsQixTQUFTLElBQUlKLFFBQWNDLENBQUFBO1FBQzdCUSxVQUFVWSxDQUFBQSxvQkFBQUEsOEJBQUFBLFFBQVNFLGdCQUFnQixJQUFHakMsZUFBZVcsS0FBS21CLE1BQU03QixXQUFXVSxLQUFLbUI7UUFDaEZ2QixVQUFVSTtJQUNaO0lBQ0F1QixPQUFPQyxjQUFjLENBQUNyQixRQUFRLFlBQVk7UUFDeENzQixLQUFLLElBQU1KO1FBQ1hLLFlBQVk7UUFDWkMsY0FBYztJQUNoQjtJQUNBeEIsT0FBT3lCLE1BQU0sR0FBRztRQUNkUCxXQUFXO1FBQ1hULGFBQWFKO1FBQ2JaO0lBQ0Y7SUFDQSxPQUFPTztBQUNUO0FBaUJBOzs7Ozs7Q0FNQyxHQUNELE9BQU8sU0FBUzBCLHFCQUFxQkMsU0FBYyxDQUFDLENBQUM7SUFDbkQsSUFBSUMsWUFBWTtRQUFDQyxTQUFTO1lBQUNDLE1BQU07UUFBUztJQUFDO0lBQzNDLElBQUlDLGFBQWE7UUFDZkMsU0FBUztZQUNQSixXQUFXUixPQUFPYSxJQUFJLENBQUNMO1lBQ3ZCTSxPQUFPUCxPQUFPUSxZQUFZLElBQUk7UUFDaEM7SUFDRjtJQUNBZixPQUFPYSxJQUFJLENBQUNOLE9BQU9TLE1BQU0sSUFBSSxDQUFDLEdBQUdDLE9BQU8sQ0FBQyxDQUFDQztRQUN4Q1AsVUFBVSxDQUFDTyxTQUFTLEdBQUc7WUFDckJWLFdBQVdSLE9BQU9hLElBQUksQ0FBQ0w7WUFDdkJNLE9BQU9QLE9BQU9TLE1BQU0sQ0FBQ0UsU0FBUztRQUNoQztJQUNGO0lBQ0EsT0FBTztRQUFDVjtRQUFXRztJQUFVO0FBQy9CO0FBUUE7Ozs7Ozs7Q0FPQyxHQUNELHNDQUFzQztBQUN0QyxnQkFBc0JRLEtBQUtDLFFBQVE7V0FBYkQ7O1NBQUFBO0lBQUFBLFFBQWYsb0JBQUEsVUFBb0JDLFFBQVEsRUFBRUMsZUFBZSxFQUFFLEVBQUV4QixPQUFvQztRQUMxRixNQUFNeUIsU0FBUzFELGNBQWMyRCxTQUFTLENBQUM7UUFDdkMsSUFBSSxPQUFPRixpQkFBaUIsVUFBVTtZQUNwQyw2QkFBNkI7WUFDN0J4QixVQUFVd0I7WUFDVkEsZUFBZXhELEVBQUUyRCxTQUFTLENBQUMsQUFBQzNCLFFBQWdCd0IsWUFBWSxFQUFFO1FBQzVEO1FBQ0EsTUFBTUksVUFBVTVCLENBQUFBLG9CQUFBQSw4QkFBQUEsUUFBU0UsZ0JBQWdCLElBQUcvQixjQUFjLElBQU1DLEtBQUtDLEdBQUc7UUFDeEUsSUFBSVUsU0FBUyxPQUFPOEM7UUFDcEIsSUFBSUMsVUFBVUYsWUFBWTVELEVBQUUyRCxTQUFTLENBQUMzQixvQkFBQUEsOEJBQUFBLFFBQVMrQixXQUFXLEVBQUU7UUFDNUQsTUFBTyxDQUFDaEQsVUFBVTZDLFlBQVlFLFFBQVM7WUFDckMsSUFBSTtnQkFDRi9DLFNBQVMsTUFBTXdDO1lBQ2pCLEVBQUUsT0FBT3RDLEtBQUs7Z0JBQ1o0QyxZQUFZNUM7Z0JBQ1p3QyxPQUFPTyxLQUFLLENBQUMsdUJBQXVCL0M7Z0JBQ3BDLElBQUkyQyxhQUFhRSxTQUFTO29CQUN4QixNQUFNN0M7Z0JBQ1I7WUFDRixTQUFVO2dCQUNSLElBQUksQ0FBQ0YsUUFBUTtvQkFDWDBDLE9BQU9PLEtBQUssQ0FBQyxpQ0FBaUNqRDtvQkFDOUMsTUFBTWUsTUFBTTBCLGNBQWN4QjtnQkFDNUI7WUFDRjtRQUNGO1FBQ0EsSUFBSTRCLGFBQWFFLFNBQVM7WUFDeEIsSUFBSUQsV0FBVztnQkFDYixNQUFNQTtZQUNSO1lBQ0EsTUFBTSxJQUFJL0QsYUFBYTtRQUN6QjtRQUNBLE9BQU9pQjtJQUNUO1dBakNzQnVDOztBQW1DdEI7Ozs7Ozs7Q0FPQyxHQUNELE9BQU8sU0FBU1csU0FBU1YsUUFBUSxFQUFFQyxlQUFlLEVBQUUsRUFBRXhCLE9BQW9DO0lBQ3hGLE9BQU9zQixLQUFLQyxVQUFVQyxjQUFjeEI7QUFDdEM7QUFFQTs7Ozs7OztDQU9DLEdBQ0QsZ0JBQXNCa0MsU0FDcEJYLFFBQStCO1dBRFhXOztTQUFBQTtJQUFBQSxZQUFmLG9CQUFBLFVBQ0xYLFFBQStCLEVBQy9CQyxlQUFlLEVBQUUsRUFDakJ4QixPQUFvQztRQUVwQyxJQUFJakI7UUFDSixNQUFNdUMsbUJBQUssb0JBQUE7WUFDVHZDLFNBQVMsTUFBTXdDO1lBQ2YsT0FBTztRQUNULElBQUdDLGNBQWN4QjtRQUNqQixPQUFPakI7SUFDVDtXQVhzQm1EOztBQWF0Qjs7Ozs7OztDQU9DLEdBQ0QsZ0JBQXNCQyxRQUNwQlosUUFBK0I7V0FEWFk7O1NBQUFBO0lBQUFBLFdBQWYsb0JBQUEsVUFDTFosUUFBK0IsRUFBRUMsZUFBZSxFQUFFLEVBQUV4QixPQUFxQjtRQUV6RSxJQUFJakI7UUFDSixNQUFNa0QsdUJBQVMsb0JBQUE7WUFDYmxELFNBQVMsTUFBTXdDO1lBQ2YsSUFBSXhDLFdBQVdxRCxXQUFXO2dCQUN4QixPQUFPO1lBQ1Q7UUFDRixJQUFHWixjQUFjeEI7UUFDakIsT0FBT2pCO0lBQ1Q7V0FYc0JvRDs7QUFhdEI7Ozs7OztDQU1DLEdBQ0QsT0FBTyxTQUFTRSxnQkFBZ0JDLFNBQWlCLEVBQUVDLFVBQWtCLEVBQUVDLFFBQWdCO0lBQ3JGLElBQUlGLGNBQWMsR0FBRztRQUNuQixPQUFPO0lBQ1Q7SUFDQSxPQUFPRyxLQUFLQyxHQUFHLENBQUNILGFBQWFFLEtBQUtFLEdBQUcsQ0FBQyxHQUFHTCxZQUFZLElBQUlFO0FBQzNEIn0=