UNPKG

anylang

Version:

A translator's kit that uses the free APIs of Google Translate, Yandex, Bing, ChatGPT, and other LLMs

312 lines (310 loc) 44.4 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { Semaphore } from '../utils/Semaphore'; /** * Module for scheduling and optimization of translate a text streams * * - It can union many translate requests to one * - You can group any requests by context * - It's configurable. You can set retry limit and edge for direct translate */ export class Scheduler { constructor(translator, config) { this.config = { translateRetryAttemptLimit: 2, isAllowDirectTranslateBadChunks: true, directTranslateLength: null, translatePoolDelay: 300, chunkSizeForInstantTranslate: null, taskBatchHandleDelay: null, }; this.abortedContexts = new Set(); this.contextCounter = 0; this.taskContainersStorage = new Set(); this.timersMap = new Map(); /** * Tasks queue with items sorted by priority * It must be handled from end to start */ this.translateQueue = []; /** * Return first item from queue and delete it from queue * Items is sorted by priority */ this.getItemFromTranslateQueue = () => { var _a; return { done: this.translateQueue.length === 0, value: (_a = this.translateQueue.pop()) !== null && _a !== void 0 ? _a : null, }; }; this.workerState = false; this.translator = translator; this.config = Object.assign(Object.assign({}, this.config), config); this.semafor = new Semaphore({ timeout: translator.getRequestsTimeout() }); } // eslint-disable-next-line @typescript-eslint/require-await abort(context) { return __awaiter(this, void 0, void 0, function* () { const abortTasks = (tasks) => { tasks.forEach((task) => { task.reject(new Error('Translation is aborted in scheduler')); }); }; // Clear tasks for (const task of this.taskContainersStorage) { if (context === task.context) { this.taskContainersStorage.delete(task); abortTasks(task.tasks); } } // Remove tasks from translation queue this.translateQueue = this.translateQueue.filter((task) => { // Abort and filter out matched tasks if (context === task.context) { abortTasks(task.tasks); return false; } return true; }); // TODO: abort even sent requests // Abort in-flight translations this.abortedContexts.add(context); }); } translate(text, from, to, options) { return __awaiter(this, void 0, void 0, function* () { const { context = '', priority = 0, directTranslate: directTranslateForThisRequest = false, } = options !== null && options !== void 0 ? options : {}; if (this.translator.checkLimitExceeding(text) <= 0) { // Direct translate if (directTranslateForThisRequest || (this.config.directTranslateLength !== null && text.length >= this.config.directTranslateLength)) { return this.directTranslate(text, from, to); } else { return this.makeTask({ text: text, from, to, context, priority }); } } else { // Split text by words and translate return this.splitAndTranslate(text, from, to, context, priority); } }); } directTranslate(text, from, to) { return __awaiter(this, void 0, void 0, function* () { const free = yield this.semafor.take(); return this.translator.translate(text, from, to).finally(free); }); } splitAndTranslate(text, from, to, context, priority) { const splittedText = []; const charsetIndexes = []; let wordsBuffer = ''; for (const textMatch of text.matchAll(/([^\s]+)(\s*)/g)) { const newPart = textMatch[0]; const newBuffer = wordsBuffer + newPart; // Add word to buffer if can if (this.translator.checkLimitExceeding(newBuffer) <= 0) { wordsBuffer = newBuffer; continue; } // Write and clear buffer if not empthy if (wordsBuffer.length > 0) { splittedText.push(wordsBuffer); wordsBuffer = ''; } // Handle new part if (this.translator.checkLimitExceeding(newPart) <= 0) { // Add to buffer wordsBuffer += newPart; continue; } else { // Slice by chars let charsBuffer = newPart; while (charsBuffer.length > 0) { const extraChars = this.translator.checkLimitExceeding(charsBuffer); if (extraChars > 0) { const offset = charsBuffer.length - extraChars; // Write slice and remainder splittedText.push(charsBuffer.slice(0, offset)); charsBuffer = charsBuffer.slice(offset); charsetIndexes.push(splittedText.length - 1); } } } } const ctxPrefix = context.length > 0 ? context + ';' : ''; return Promise.all(splittedText.map((text, index) => charsetIndexes.includes(index) ? text : this.makeTask({ text, from, to, context: ctxPrefix + `text#${this.contextCounter++}`, priority, }))).then((translatedParts) => translatedParts.join('')); } makeTask({ text, from, to, priority, context = '' }) { return new Promise((resolve, reject) => { this.addToTaskContainer({ text, from, to, context, priority, resolve, reject, }); }); } addToTaskContainer(params) { const { text, from, to, attempt = 0, context = '', priority, resolve, reject, } = params; // create task const task = { text, from, to, attempt, resolve, reject, }; let container = null; // try add to exists container for (const taskContainer of this.taskContainersStorage) { // Skip containers with not equal parameters if (['from', 'to', 'context', 'priority'].some((key) => params[key] !== taskContainer[key])) continue; // Lightweight check to overflow // NOTE: Do strict check here if you need comply a limit contract if (this.translator.getLengthLimit() >= taskContainer.length + task.text.length) { taskContainer.tasks.push(task); taskContainer.length += task.text.length; container = taskContainer; } } // make container if (container === null) { const newTaskContainer = { context, priority, from, to, tasks: [task], length: task.text.length, }; this.taskContainersStorage.add(newTaskContainer); container = newTaskContainer; } if (this.config.chunkSizeForInstantTranslate !== null && container.length >= this.config.chunkSizeForInstantTranslate) { this.addToTranslateQueue(container); } else { this.updateDelayForAddToTranslateQueue(container); } } updateDelayForAddToTranslateQueue(taskContainer) { // Flush timer if (this.timersMap.has(taskContainer)) { // Due to expectation run on one platform, timer objects will same always globalThis.clearTimeout(this.timersMap.get(taskContainer)); } this.timersMap.set(taskContainer, globalThis.setTimeout(() => { this.addToTranslateQueue(taskContainer); }, this.config.translatePoolDelay)); } addToTranslateQueue(taskContainer) { // Flush timer if (this.timersMap.has(taskContainer)) { // Due to expectation run on one platform, timer objects will same always globalThis.clearTimeout(this.timersMap.get(taskContainer)); this.timersMap.delete(taskContainer); } this.taskContainersStorage.delete(taskContainer); // Resort queue by priority each time to keep consistency this.translateQueue = this.translateQueue .concat(taskContainer) .sort((a, b) => a.priority - b.priority); if (!this.workerState) { this.runWorker().catch((error) => { throw error; }); } } runWorker() { return __awaiter(this, void 0, void 0, function* () { this.workerState = true; let firstIteration = true; // Daemon loop // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition while (true) { // Delay first iteration to await fill the queue, to consider priority better const workerHandleDelay = this.config.taskBatchHandleDelay; if (workerHandleDelay && firstIteration) { yield new Promise((res) => setTimeout(res, workerHandleDelay)); } firstIteration = false; const iterate = this.getItemFromTranslateQueue(); // Skip when queue empty if (iterate.done || iterate.value === null) break; const taskContainer = iterate.value; const free = yield this.semafor.take(); const textArray = taskContainer.tasks.map((i) => i.text); yield this.translator .translateBatch(textArray, taskContainer.from, taskContainer.to) .then((result) => { for (let index = 0; index < taskContainer.tasks.length; index++) { const task = taskContainer.tasks[index]; const translatedText = result[index]; if (translatedText !== null) { task.resolve(translatedText); } else { this.taskErrorHandler(task, new Error("Translator module can't translate this"), taskContainer.context, taskContainer.priority); } } }) .catch((reason) => { console.error(reason); for (const task of taskContainer.tasks) { this.taskErrorHandler(task, reason, taskContainer.context, taskContainer.priority); } }) .finally(free); } this.workerState = false; }); } taskErrorHandler(task, error, context, priority) { if (this.abortedContexts.has(context)) { task.reject(error); return; } if (task.attempt >= this.config.translateRetryAttemptLimit) { if (this.config.isAllowDirectTranslateBadChunks) { const { text, from, to, resolve, reject } = task; this.directTranslate(text, from, to).then(resolve, reject); } else { task.reject(error); } } else { this.addToTaskContainer(Object.assign(Object.assign({}, task), { attempt: task.attempt + 1, context, priority })); } } } //# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNjaGVkdWxpbmcvU2NoZWR1bGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7OztBQUNBLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQTRHL0M7Ozs7OztHQU1HO0FBQ0gsTUFBTSxPQUFPLFNBQVM7SUFZckIsWUFBWSxVQUFxQyxFQUFFLE1BQXdCO1FBVDFELFdBQU0sR0FBOEI7WUFDcEQsMEJBQTBCLEVBQUUsQ0FBQztZQUM3QiwrQkFBK0IsRUFBRSxJQUFJO1lBQ3JDLHFCQUFxQixFQUFFLElBQUk7WUFDM0Isa0JBQWtCLEVBQUUsR0FBRztZQUN2Qiw0QkFBNEIsRUFBRSxJQUFJO1lBQ2xDLG9CQUFvQixFQUFFLElBQUk7U0FDMUIsQ0FBQztRQVVlLG9CQUFlLEdBQUcsSUFBSSxHQUFHLEVBQVUsQ0FBQztRQWlDN0MsbUJBQWMsR0FBRyxDQUFDLENBQUM7UUFtSFYsMEJBQXFCLEdBQUcsSUFBSSxHQUFHLEVBQWlCLENBQUM7UUF1RWpELGNBQVMsR0FBRyxJQUFJLEdBQUcsRUFBMEMsQ0FBQztRQWdCL0U7OztXQUdHO1FBQ0ssbUJBQWMsR0FBb0IsRUFBRSxDQUFDO1FBdUI3Qzs7O1dBR0c7UUFDYyw4QkFBeUIsR0FBRyxHQUFnQyxFQUFFOztZQUM5RSxPQUFPO2dCQUNOLElBQUksRUFBRSxJQUFJLENBQUMsY0FBYyxDQUFDLE1BQU0sS0FBSyxDQUFDO2dCQUN0QyxLQUFLLEVBQUUsTUFBQSxJQUFJLENBQUMsY0FBYyxDQUFDLEdBQUcsRUFBRSxtQ0FBSSxJQUFJO2FBQ3hDLENBQUM7UUFDSCxDQUFDLENBQUM7UUFFTSxnQkFBVyxHQUFHLEtBQUssQ0FBQztRQXhSM0IsSUFBSSxDQUFDLFVBQVUsR0FBRyxVQUFVLENBQUM7UUFFN0IsSUFBSSxDQUFDLE1BQU0sbUNBQVEsSUFBSSxDQUFDLE1BQU0sR0FBSyxNQUFNLENBQUUsQ0FBQztRQUU1QyxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksU0FBUyxDQUFDLEVBQUUsT0FBTyxFQUFFLFVBQVUsQ0FBQyxrQkFBa0IsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUM1RSxDQUFDO0lBR0QsNERBQTREO0lBQy9DLEtBQUssQ0FBQyxPQUFlOztZQUNqQyxNQUFNLFVBQVUsR0FBRyxDQUFDLEtBQWEsRUFBRSxFQUFFO2dCQUNwQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUU7b0JBQ3RCLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxLQUFLLENBQUMscUNBQXFDLENBQUMsQ0FBQyxDQUFDO2dCQUMvRCxDQUFDLENBQUMsQ0FBQztZQUNKLENBQUMsQ0FBQztZQUVGLGNBQWM7WUFDZCxLQUFLLE1BQU0sSUFBSSxJQUFJLElBQUksQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO2dCQUMvQyxJQUFJLE9BQU8sS0FBSyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7b0JBQzlCLElBQUksQ0FBQyxxQkFBcUIsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7b0JBQ3hDLFVBQVUsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBQ3hCLENBQUM7WUFDRixDQUFDO1lBRUQsc0NBQXNDO1lBQ3RDLElBQUksQ0FBQyxjQUFjLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRTtnQkFDekQscUNBQXFDO2dCQUNyQyxJQUFJLE9BQU8sS0FBSyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7b0JBQzlCLFVBQVUsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7b0JBQ3ZCLE9BQU8sS0FBSyxDQUFDO2dCQUNkLENBQUM7Z0JBRUQsT0FBTyxJQUFJLENBQUM7WUFDYixDQUFDLENBQUMsQ0FBQztZQUVILGlDQUFpQztZQUNqQywrQkFBK0I7WUFDL0IsSUFBSSxDQUFDLGVBQWUsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDbkMsQ0FBQztLQUFBO0lBR1ksU0FBUyxDQUNyQixJQUFZLEVBQ1osSUFBWSxFQUNaLEVBQVUsRUFDVixPQUFvQzs7WUFFcEMsTUFBTSxFQUNMLE9BQU8sR0FBRyxFQUFFLEVBQ1osUUFBUSxHQUFHLENBQUMsRUFDWixlQUFlLEVBQUUsNkJBQTZCLEdBQUcsS0FBSyxHQUN0RCxHQUFHLE9BQU8sYUFBUCxPQUFPLGNBQVAsT0FBTyxHQUFJLEVBQUUsQ0FBQztZQUVsQixJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsbUJBQW1CLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7Z0JBQ3BELG1CQUFtQjtnQkFDbkIsSUFDQyw2QkFBNkI7b0JBQzdCLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxxQkFBcUIsS0FBSyxJQUFJO3dCQUMxQyxJQUFJLENBQUMsTUFBTSxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMscUJBQXFCLENBQUMsRUFDakQsQ0FBQztvQkFDRixPQUFPLElBQUksQ0FBQyxlQUFlLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQztnQkFDN0MsQ0FBQztxQkFBTSxDQUFDO29CQUNQLE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQztnQkFDbkUsQ0FBQztZQUNGLENBQUM7aUJBQU0sQ0FBQztnQkFDUCxvQ0FBb0M7Z0JBQ3BDLE9BQU8sSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLE9BQU8sRUFBRSxRQUFRLENBQUMsQ0FBQztZQUNsRSxDQUFDO1FBQ0YsQ0FBQztLQUFBO0lBRWEsZUFBZSxDQUFDLElBQVksRUFBRSxJQUFZLEVBQUUsRUFBVTs7WUFDbkUsTUFBTSxJQUFJLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDO1lBQ3ZDLE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDaEUsQ0FBQztLQUFBO0lBRU8saUJBQWlCLENBQ3hCLElBQVksRUFDWixJQUFZLEVBQ1osRUFBVSxFQUNWLE9BQWUsRUFDZixRQUFnQjtRQUVoQixNQUFNLFlBQVksR0FBYSxFQUFFLENBQUM7UUFDbEMsTUFBTSxjQUFjLEdBQWEsRUFBRSxDQUFDO1FBRXBDLElBQUksV0FBVyxHQUFHLEVBQUUsQ0FBQztRQUNyQixLQUFLLE1BQU0sU0FBUyxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLENBQUMsRUFBRSxDQUFDO1lBQ3pELE1BQU0sT0FBTyxHQUFHLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUM3QixNQUFNLFNBQVMsR0FBRyxXQUFXLEdBQUcsT0FBTyxDQUFDO1lBRXhDLDRCQUE0QjtZQUM1QixJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsbUJBQW1CLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7Z0JBQ3pELFdBQVcsR0FBRyxTQUFTLENBQUM7Z0JBQ3hCLFNBQVM7WUFDVixDQUFDO1lBRUQsdUNBQXVDO1lBQ3ZDLElBQUksV0FBVyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztnQkFDNUIsWUFBWSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztnQkFDL0IsV0FBVyxHQUFHLEVBQUUsQ0FBQztZQUNsQixDQUFDO1lBRUQsa0JBQWtCO1lBQ2xCLElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxtQkFBbUIsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztnQkFDdkQsZ0JBQWdCO2dCQUNoQixXQUFXLElBQUksT0FBTyxDQUFDO2dCQUN2QixTQUFTO1lBQ1YsQ0FBQztpQkFBTSxDQUFDO2dCQUNQLGlCQUFpQjtnQkFDakIsSUFBSSxXQUFXLEdBQUcsT0FBTyxDQUFDO2dCQUMxQixPQUFPLFdBQVcsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7b0JBQy9CLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsbUJBQW1CLENBQUMsV0FBVyxDQUFDLENBQUM7b0JBQ3BFLElBQUksVUFBVSxHQUFHLENBQUMsRUFBRSxDQUFDO3dCQUNwQixNQUFNLE1BQU0sR0FBRyxXQUFXLENBQUMsTUFBTSxHQUFHLFVBQVUsQ0FBQzt3QkFFL0MsNEJBQTRCO3dCQUM1QixZQUFZLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7d0JBQ2hELFdBQVcsR0FBRyxXQUFXLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDO3dCQUV4QyxjQUFjLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7b0JBQzlDLENBQUM7Z0JBQ0YsQ0FBQztZQUNGLENBQUM7UUFDRixDQUFDO1FBRUQsTUFBTSxTQUFTLEdBQUcsT0FBTyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sR0FBRyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUMxRCxPQUFPLE9BQU8sQ0FBQyxHQUFHLENBQ2pCLFlBQVksQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsS0FBSyxFQUFFLEVBQUUsQ0FDaEMsY0FBYyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUM7WUFDN0IsQ0FBQyxDQUFDLElBQUk7WUFDTixDQUFDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQztnQkFDZCxJQUFJO2dCQUNKLElBQUk7Z0JBQ0osRUFBRTtnQkFDRixPQUFPLEVBQUUsU0FBUyxHQUFHLFFBQVEsSUFBSSxDQUFDLGNBQWMsRUFBRSxFQUFFO2dCQUNwRCxRQUFRO2FBQ1IsQ0FBQyxDQUNKLENBQ0QsQ0FBQyxJQUFJLENBQUMsQ0FBQyxlQUFlLEVBQUUsRUFBRSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUN2RCxDQUFDO0lBRU8sUUFBUSxDQUFDLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsUUFBUSxFQUFFLE9BQU8sR0FBRyxFQUFFLEVBQW1CO1FBQzNFLE9BQU8sSUFBSSxPQUFPLENBQVMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7WUFDOUMsSUFBSSxDQUFDLGtCQUFrQixDQUFDO2dCQUN2QixJQUFJO2dCQUNKLElBQUk7Z0JBQ0osRUFBRTtnQkFDRixPQUFPO2dCQUNQLFFBQVE7Z0JBQ1IsT0FBTztnQkFDUCxNQUFNO2FBQ04sQ0FBQyxDQUFDO1FBQ0osQ0FBQyxDQUFDLENBQUM7SUFDSixDQUFDO0lBR08sa0JBQWtCLENBQUMsTUFBK0I7UUFDekQsTUFBTSxFQUNMLElBQUksRUFDSixJQUFJLEVBQ0osRUFBRSxFQUNGLE9BQU8sR0FBRyxDQUFDLEVBQ1gsT0FBTyxHQUFHLEVBQUUsRUFDWixRQUFRLEVBQ1IsT0FBTyxFQUNQLE1BQU0sR0FDTixHQUFHLE1BQU0sQ0FBQztRQUVYLGNBQWM7UUFDZCxNQUFNLElBQUksR0FBUztZQUNsQixJQUFJO1lBQ0osSUFBSTtZQUNKLEVBQUU7WUFDRixPQUFPO1lBQ1AsT0FBTztZQUNQLE1BQU07U0FDTixDQUFDO1FBRUYsSUFBSSxTQUFTLEdBQXlCLElBQUksQ0FBQztRQUUzQyw4QkFBOEI7UUFDOUIsS0FBSyxNQUFNLGFBQWEsSUFBSSxJQUFJLENBQUMscUJBQXFCLEVBQUUsQ0FBQztZQUN4RCw0Q0FBNEM7WUFDNUMsSUFDRSxDQUFDLE1BQU0sRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLFVBQVUsQ0FBVyxDQUFDLElBQUksQ0FDcEQsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsS0FBSyxhQUFhLENBQUMsR0FBRyxDQUFDLENBQzNDO2dCQUVELFNBQVM7WUFFVixnQ0FBZ0M7WUFDaEMsaUVBQWlFO1lBQ2pFLElBQ0MsSUFBSSxDQUFDLFVBQVUsQ0FBQyxjQUFjLEVBQUU7Z0JBQ2hDLGFBQWEsQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQ3RDLENBQUM7Z0JBQ0YsYUFBYSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQy9CLGFBQWEsQ0FBQyxNQUFNLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7Z0JBQ3pDLFNBQVMsR0FBRyxhQUFhLENBQUM7WUFDM0IsQ0FBQztRQUNGLENBQUM7UUFFRCxpQkFBaUI7UUFDakIsSUFBSSxTQUFTLEtBQUssSUFBSSxFQUFFLENBQUM7WUFDeEIsTUFBTSxnQkFBZ0IsR0FBa0I7Z0JBQ3ZDLE9BQU87Z0JBQ1AsUUFBUTtnQkFDUixJQUFJO2dCQUNKLEVBQUU7Z0JBQ0YsS0FBSyxFQUFFLENBQUMsSUFBSSxDQUFDO2dCQUNiLE1BQU0sRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU07YUFDeEIsQ0FBQztZQUNGLElBQUksQ0FBQyxxQkFBcUIsQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztZQUNqRCxTQUFTLEdBQUcsZ0JBQWdCLENBQUM7UUFDOUIsQ0FBQztRQUVELElBQ0MsSUFBSSxDQUFDLE1BQU0sQ0FBQyw0QkFBNEIsS0FBSyxJQUFJO1lBQ2pELFNBQVMsQ0FBQyxNQUFNLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyw0QkFBNEIsRUFDM0QsQ0FBQztZQUNGLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUNyQyxDQUFDO2FBQU0sQ0FBQztZQUNQLElBQUksQ0FBQyxpQ0FBaUMsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUNuRCxDQUFDO0lBQ0YsQ0FBQztJQUdPLGlDQUFpQyxDQUFDLGFBQTRCO1FBQ3JFLGNBQWM7UUFDZCxJQUFJLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxFQUFFLENBQUM7WUFDdkMseUVBQXlFO1lBQ3pFLFVBQVUsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQztRQUM1RCxDQUFDO1FBRUQsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQ2pCLGFBQWEsRUFDYixVQUFVLENBQUMsVUFBVSxDQUFDLEdBQUcsRUFBRTtZQUMxQixJQUFJLENBQUMsbUJBQW1CLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDekMsQ0FBQyxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsa0JBQWtCLENBQUMsQ0FDbEMsQ0FBQztJQUNILENBQUM7SUFPTyxtQkFBbUIsQ0FBQyxhQUE0QjtRQUN2RCxjQUFjO1FBQ2QsSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsRUFBRSxDQUFDO1lBQ3ZDLHlFQUF5RTtZQUN6RSxVQUFVLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUM7WUFDM0QsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDdEMsQ0FBQztRQUVELElBQUksQ0FBQyxxQkFBcUIsQ0FBQyxNQUFNLENBQUMsYUFBYSxDQUFDLENBQUM7UUFFakQseURBQXlEO1FBQ3pELElBQUksQ0FBQyxjQUFjLEdBQUcsSUFBSSxDQUFDLGNBQWM7YUFDdkMsTUFBTSxDQUFDLGFBQWEsQ0FBQzthQUNyQixJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsUUFBUSxHQUFHLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUUxQyxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBQ3ZCLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFjLEVBQUUsRUFBRTtnQkFDekMsTUFBTSxLQUFLLENBQUM7WUFDYixDQUFDLENBQUMsQ0FBQztRQUNKLENBQUM7SUFDRixDQUFDO0lBY2EsU0FBUzs7WUFDdEIsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUM7WUFFeEIsSUFBSSxjQUFjLEdBQUcsSUFBSSxDQUFDO1lBQzFCLGNBQWM7WUFDZCx1RUFBdUU7WUFDdkUsT0FBTyxJQUFJLEVBQUUsQ0FBQztnQkFDYiw2RUFBNkU7Z0JBQzdFLE1BQU0saUJBQWlCLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxvQkFBb0IsQ0FBQztnQkFDM0QsSUFBSSxpQkFBaUIsSUFBSSxjQUFjLEVBQUUsQ0FBQztvQkFDekMsTUFBTSxJQUFJLE9BQU8sQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLEdBQUcsRUFBRSxpQkFBaUIsQ0FBQyxDQUFDLENBQUM7Z0JBQ2hFLENBQUM7Z0JBRUQsY0FBYyxHQUFHLEtBQUssQ0FBQztnQkFFdkIsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLHlCQUF5QixFQUFFLENBQUM7Z0JBRWpELHdCQUF3QjtnQkFDeEIsSUFBSSxPQUFPLENBQUMsSUFBSSxJQUFJLE9BQU8sQ0FBQyxLQUFLLEtBQUssSUFBSTtvQkFBRSxNQUFNO2dCQUVsRCxNQUFNLGFBQWEsR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDO2dCQUVwQyxNQUFNLElBQUksR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLENBQUM7Z0JBRXZDLE1BQU0sU0FBUyxHQUFHLGFBQWEsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ3pELE1BQU0sSUFBSSxDQUFDLFVBQVU7cUJBQ25CLGNBQWMsQ0FBQyxTQUFTLEVBQUUsYUFBYSxDQUFDLElBQUksRUFBRSxhQUFhLENBQUMsRUFBRSxDQUFDO3FCQUMvRCxJQUFJLENBQUMsQ0FBQyxNQUFNLEVBQUUsRUFBRTtvQkFDaEIsS0FBSyxJQUFJLEtBQUssR0FBRyxDQUFDLEVBQUUsS0FBSyxHQUFHLGFBQWEsQ0FBQyxLQUFLLENBQUMsTUFBTSxFQUFFLEtBQUssRUFBRSxFQUFFLENBQUM7d0JBQ2pFLE1BQU0sSUFBSSxHQUFHLGFBQWEsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUM7d0JBRXhDLE1BQU0sY0FBYyxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQzt3QkFDckMsSUFBSSxjQUFjLEtBQUssSUFBSSxFQUFFLENBQUM7NEJBQzdCLElBQUksQ0FBQyxPQUFPLENBQUMsY0FBYyxDQUFDLENBQUM7d0JBQzlCLENBQUM7NkJBQU0sQ0FBQzs0QkFDUCxJQUFJLENBQUMsZ0JBQWdCLENBQ3BCLElBQUksRUFDSixJQUFJLEtBQUssQ0FBQyx3Q0FBd0MsQ0FBQyxFQUNuRCxhQUFhLENBQUMsT0FBTyxFQUNyQixhQUFhLENBQUMsUUFBUSxDQUN0QixDQUFDO3dCQUNILENBQUM7b0JBQ0YsQ0FBQztnQkFDRixDQUFDLENBQUM7cUJBQ0QsS0FBSyxDQUFDLENBQUMsTUFBZSxFQUFFLEVBQUU7b0JBQzFCLE9BQU8sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUM7b0JBRXRCLEtBQUssTUFBTSxJQUFJLElBQUksYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDO3dCQUN4QyxJQUFJLENBQUMsZ0JBQWdCLENBQ3BCLElBQUksRUFDSixNQUFNLEVBQ04sYUFBYSxDQUFDLE9BQU8sRUFDckIsYUFBYSxDQUFDLFFBQVEsQ0FDdEIsQ0FBQztvQkFDSCxDQUFDO2dCQUNGLENBQUMsQ0FBQztxQkFDRCxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDakIsQ0FBQztZQUVELElBQUksQ0FBQyxXQUFXLEdBQUcsS0FBSyxDQUFDO1FBQzFCLENBQUM7S0FBQTtJQUVPLGdCQUFnQixDQUN2QixJQUFVLEVBQ1YsS0FBYyxFQUNkLE9BQWUsRUFDZixRQUFnQjtRQUVoQixJQUFJLElBQUksQ0FBQyxlQUFlLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDdkMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUNuQixPQUFPO1FBQ1IsQ0FBQztRQUVELElBQUksSUFBSSxDQUFDLE9BQU8sSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLDBCQUEwQixFQUFFLENBQUM7WUFDNUQsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLCtCQUErQixFQUFFLENBQUM7Z0JBQ2pELE1BQU0sRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLEdBQUcsSUFBSSxDQUFDO2dCQUNqRCxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztZQUM1RCxDQUFDO2lCQUFNLENBQUM7Z0JBQ1AsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUNwQixDQUFDO1FBQ0YsQ0FBQzthQUFNLENBQUM7WUFDUCxJQUFJLENBQUMsa0JBQWtCLGlDQUNuQixJQUFJLEtBQ1AsT0FBTyxFQUFFLElBQUksQ0FBQyxPQUFPLEdBQUcsQ0FBQyxFQUN6QixPQUFPO2dCQUNQLFFBQVEsSUFDUCxDQUFDO1FBQ0osQ0FBQztJQUNGLENBQUM7Q0FDRCIsImZpbGUiOiJzY2hlZHVsaW5nL1NjaGVkdWxlci5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFRyYW5zbGF0b3JJbnN0YW5jZU1lbWJlcnMgfSBmcm9tICcuLi90cmFuc2xhdG9ycy9UcmFuc2xhdG9yJztcbmltcG9ydCB7IFNlbWFwaG9yZSB9IGZyb20gJy4uL3V0aWxzL1NlbWFwaG9yZSc7XG5pbXBvcnQgeyBJU2NoZWR1bGVyLCBJU2NoZWR1bGVyVHJhbnNsYXRlT3B0aW9ucyB9IGZyb20gJy4nO1xuXG5pbnRlcmZhY2UgU2NoZWR1bGVyQ29uZmlnIHtcblx0LyoqXG5cdCAqIE51bWJlciBvZiBhdHRlbXB0cyBmb3IgcmV0cnkgcmVxdWVzdFxuXHQgKi9cblx0dHJhbnNsYXRlUmV0cnlBdHRlbXB0TGltaXQ/OiBudW1iZXI7XG5cblx0LyoqXG5cdCAqIElmIHRydWUgLSByZWplY3RlZCByZXF1ZXN0cyB3aWxsIHVzZSBkaXJlY3QgdHJhbnNsYXRlXG5cdCAqL1xuXHRpc0FsbG93RGlyZWN0VHJhbnNsYXRlQmFkQ2h1bmtzPzogYm9vbGVhbjtcblxuXHQvKipcblx0ICogTGVuZ3RoIG9mIHN0cmluZyBmb3IgZGlyZWN0IHRyYW5zbGF0ZS5cblx0ICpcblx0ICogbnVsbCBmb3IgZGlzYWJsZSB0aGUgY29uZGl0aW9uXG5cdCAqL1xuXHRkaXJlY3RUcmFuc2xhdGVMZW5ndGg/OiBudW1iZXIgfCBudWxsO1xuXG5cdC8qKlxuXHQgKiBEZWxheSBmb3IgdHJhbnNsYXRlIGEgY2h1bmsuIFRoZSBiaWdnZXIgdGhlIG1vcmUgcmVxdWVzdHMgd2lsbCBjb2xsZWN0XG5cdCAqL1xuXHR0cmFuc2xhdGVQb29sRGVsYXk/OiBudW1iZXI7XG5cblx0LyoqXG5cdCAqIFdoZW4gY2h1bmsgY29sbGVjdCB0aGlzIHNpemUsIGl0J3Mgd2lsbCBiZSBpbnN0YW50IGFkZCB0byBhIHRyYW5zbGF0ZSBxdWV1ZVxuXHQgKlxuXHQgKiBudWxsIGZvciBkaXNhYmxlIHRoZSBjb25kaXRpb25cblx0ICovXG5cdGNodW5rU2l6ZUZvckluc3RhbnRUcmFuc2xhdGU/OiBudW1iZXIgfCBudWxsO1xuXG5cdC8qKlxuXHQgKiBQYXVzZSBiZXR3ZWVuIGhhbmRsZSB0YXNrIGJhdGNoZXNcblx0ICpcblx0ICogSXQgbWF5IGJlIHVzZWZ1bCB0byBhd2FpdCBhY2N1bXVsYXRpbmcgYSB0YXNrIGJhdGNoZXMgaW4gcXVldWUgdG8gY29uc2lkZXIgcHJpb3JpdHkgYmV0dGVyIGFuZCBkb24ndCB0cmFuc2xhdGUgZmlyc3QgdGFzayBiYXRjaCBpbW1lZGlhdGVseVxuXHQgKlxuXHQgKiBXQVJOSU5HOiB0aGlzIG9wdGlvbiBtdXN0IGJlIHVzZWQgb25seSBmb3IgY29uc2lkZXIgcHJpb3JpdHkgYmV0dGVyISBTZXQgc21hbGwgdmFsdWUgYWx3YXlzICgxMC01MG1zKVxuXHQgKlxuXHQgKiBXaGVuIHRoaXMgb3B0aW9uIGlzIGRpc2FibGVkIChieSBkZWZhdWx0KSBhbmQgeW91IGNhbGwgdHJhbnNsYXRlIG1ldGhvZCBmb3IgdGV4dHMgd2l0aCBwcmlvcml0eSAxIGFuZCB0aGVuIGltbWVkaWF0ZWx5IGZvciB0ZXh0IHdpdGggcHJpb3JpdHkgMiwgZmlyc3QgcmVxdWVzdCB3aWxsIGhhdmUgbGVzcyBkZWxheSBmb3IgdHJhbnNsYXRlIGFuZCB3aWxsIHRyYW5zbGF0ZSBmaXJzdCwgZXZlbiB3aXRoIGxvd2VyIHByaW9yaXR5LCBiZWNhdXNlIHdvcmtlciB3aWxsIHRyYW5zbGF0ZSBmaXJzdCB0YXNrIGltbWVkaWF0ZWx5IGFmdGVyIGRlbGF5IGRlZmluZWQgYnkgb3B0aW9uIGB0cmFuc2xhdGVQb29sRGVsYXlgXG5cdCAqL1xuXHR0YXNrQmF0Y2hIYW5kbGVEZWxheT86IG51bGwgfCBudW1iZXI7XG59XG5cbmludGVyZmFjZSBUYXNrQ29uc3RydWN0b3Ige1xuXHR0ZXh0OiBzdHJpbmc7XG5cdGZyb206IHN0cmluZztcblx0dG86IHN0cmluZztcblxuXHQvKipcblx0ICogVG8gY29tYmluZSB0YXNrcyBieSB1bmlxdWUga2V5XG5cdCAqL1xuXHRjb250ZXh0Pzogc3RyaW5nO1xuXG5cdC8qKlxuXHQgKiBUbyBjb21iaW5lIGFuZCBzb3J0IHRhc2tzIGJ5IHByaW9yaXR5XG5cdCAqL1xuXHRwcmlvcml0eTogbnVtYmVyO1xufVxuXG5pbnRlcmZhY2UgVGFza0NvbnN0cnVjdG9ySW50ZXJuYWwgZXh0ZW5kcyBUYXNrQ29uc3RydWN0b3Ige1xuXHQvKipcblx0ICogQ3VycmVudCByZXRyeSBhdHRlbXB0XG5cdCAqL1xuXHRhdHRlbXB0PzogbnVtYmVyO1xuXG5cdHJlc29sdmU6ICh2YWx1ZTogc3RyaW5nIHwgUHJvbWlzZUxpa2U8c3RyaW5nPikgPT4gdm9pZDtcblx0cmVqZWN0OiAocmVhc29uPzogdW5rbm93bikgPT4gdm9pZDtcbn1cblxuaW50ZXJmYWNlIFRhc2sge1xuXHR0ZXh0OiBzdHJpbmc7XG5cdGZyb206IHN0cmluZztcblx0dG86IHN0cmluZztcblxuXHQvKipcblx0ICogQ3VycmVudCByZXRyeSBhdHRlbXB0XG5cdCAqL1xuXHRhdHRlbXB0OiBudW1iZXI7XG5cblx0cmVzb2x2ZTogKHZhbHVlOiBzdHJpbmcgfCBQcm9taXNlTGlrZTxzdHJpbmc+KSA9PiB2b2lkO1xuXHRyZWplY3Q6IChyZWFzb24/OiB1bmtub3duKSA9PiB2b2lkO1xufVxuXG5pbnRlcmZhY2UgVGFza0NvbnRhaW5lciB7XG5cdC8qKlxuXHQgKiBGb3IgY29tYmluZSB0YXNrcyBieSB1bmlxdWUga2V5XG5cdCAqL1xuXHRjb250ZXh0OiBzdHJpbmc7XG5cblx0cHJpb3JpdHk6IG51bWJlcjtcblxuXHRmcm9tOiBzdHJpbmc7XG5cdHRvOiBzdHJpbmc7XG5cdHRhc2tzOiBUYXNrW107XG5cblx0LyoqXG5cdCAqIFRvdGFsIGxlbmd0aCBvZiB0ZXh0IGZyb20gYWxsIHRhc2tzXG5cdCAqL1xuXHRsZW5ndGg6IG51bWJlcjtcbn1cblxudHlwZSBJdGVyYXRvclN0ZXA8VD4gPSB7XG5cdGRvbmU6IGJvb2xlYW47XG5cdHZhbHVlOiBUIHwgbnVsbDtcbn07XG5cbi8qKlxuICogTW9kdWxlIGZvciBzY2hlZHVsaW5nIGFuZCBvcHRpbWl6YXRpb24gb2YgdHJhbnNsYXRlIGEgdGV4dCBzdHJlYW1zXG4gKlxuICogLSBJdCBjYW4gdW5pb24gbWFueSB0cmFuc2xhdGUgcmVxdWVzdHMgdG8gb25lXG4gKiAtIFlvdSBjYW4gZ3JvdXAgYW55IHJlcXVlc3RzIGJ5IGNvbnRleHRcbiAqIC0gSXQncyBjb25maWd1cmFibGUuIFlvdSBjYW4gc2V0IHJldHJ5IGxpbWl0IGFuZCBlZGdlIGZvciBkaXJlY3QgdHJhbnNsYXRlXG4gKi9cbmV4cG9ydCBjbGFzcyBTY2hlZHVsZXIgaW1wbGVtZW50cyBJU2NoZWR1bGVyIHtcblx0cHJpdmF0ZSByZWFkb25seSBzZW1hZm9yO1xuXHRwcml2YXRlIHJlYWRvbmx5IHRyYW5zbGF0b3I7XG5cdHByaXZhdGUgcmVhZG9ubHkgY29uZmlnOiBSZXF1aXJlZDxTY2hlZHVsZXJDb25maWc+ID0ge1xuXHRcdHRyYW5zbGF0ZVJldHJ5QXR0ZW1wdExpbWl0OiAyLFxuXHRcdGlzQWxsb3dEaXJlY3RUcmFuc2xhdGVCYWRDaHVua3M6IHRydWUsXG5cdFx0ZGlyZWN0VHJhbnNsYXRlTGVuZ3RoOiBudWxsLFxuXHRcdHRyYW5zbGF0ZVBvb2xEZWxheTogMzAwLFxuXHRcdGNodW5rU2l6ZUZvckluc3RhbnRUcmFuc2xhdGU6IG51bGwsXG5cdFx0dGFza0JhdGNoSGFuZGxlRGVsYXk6IG51bGwsXG5cdH07XG5cblx0Y29uc3RydWN0b3IodHJhbnNsYXRvcjogVHJhbnNsYXRvckluc3RhbmNlTWVtYmVycywgY29uZmlnPzogU2NoZWR1bGVyQ29uZmlnKSB7XG5cdFx0dGhpcy50cmFuc2xhdG9yID0gdHJhbnNsYXRvcjtcblxuXHRcdHRoaXMuY29uZmlnID0geyAuLi50aGlzLmNvbmZpZywgLi4uY29uZmlnIH07XG5cblx0XHR0aGlzLnNlbWFmb3IgPSBuZXcgU2VtYXBob3JlKHsgdGltZW91dDogdHJhbnNsYXRvci5nZXRSZXF1ZXN0c1RpbWVvdXQoKSB9KTtcblx0fVxuXG5cdHByaXZhdGUgcmVhZG9ubHkgYWJvcnRlZENvbnRleHRzID0gbmV3IFNldDxzdHJpbmc+KCk7XG5cdC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvcmVxdWlyZS1hd2FpdFxuXHRwdWJsaWMgYXN5bmMgYWJvcnQoY29udGV4dDogc3RyaW5nKSB7XG5cdFx0Y29uc3QgYWJvcnRUYXNrcyA9ICh0YXNrczogVGFza1tdKSA9PiB7XG5cdFx0XHR0YXNrcy5mb3JFYWNoKCh0YXNrKSA9PiB7XG5cdFx0XHRcdHRhc2sucmVqZWN0KG5ldyBFcnJvcignVHJhbnNsYXRpb24gaXMgYWJvcnRlZCBpbiBzY2hlZHVsZXInKSk7XG5cdFx0XHR9KTtcblx0XHR9O1xuXG5cdFx0Ly8gQ2xlYXIgdGFza3Ncblx0XHRmb3IgKGNvbnN0IHRhc2sgb2YgdGhpcy50YXNrQ29udGFpbmVyc1N0b3JhZ2UpIHtcblx0XHRcdGlmIChjb250ZXh0ID09PSB0YXNrLmNvbnRleHQpIHtcblx0XHRcdFx0dGhpcy50YXNrQ29udGFpbmVyc1N0b3JhZ2UuZGVsZXRlKHRhc2spO1xuXHRcdFx0XHRhYm9ydFRhc2tzKHRhc2sudGFza3MpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC8vIFJlbW92ZSB0YXNrcyBmcm9tIHRyYW5zbGF0aW9uIHF1ZXVlXG5cdFx0dGhpcy50cmFuc2xhdGVRdWV1ZSA9IHRoaXMudHJhbnNsYXRlUXVldWUuZmlsdGVyKCh0YXNrKSA9PiB7XG5cdFx0XHQvLyBBYm9ydCBhbmQgZmlsdGVyIG91dCBtYXRjaGVkIHRhc2tzXG5cdFx0XHRpZiAoY29udGV4dCA9PT0gdGFzay5jb250ZXh0KSB7XG5cdFx0XHRcdGFib3J0VGFza3ModGFzay50YXNrcyk7XG5cdFx0XHRcdHJldHVybiBmYWxzZTtcblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRydWU7XG5cdFx0fSk7XG5cblx0XHQvLyBUT0RPOiBhYm9ydCBldmVuIHNlbnQgcmVxdWVzdHNcblx0XHQvLyBBYm9ydCBpbi1mbGlnaHQgdHJhbnNsYXRpb25zXG5cdFx0dGhpcy5hYm9ydGVkQ29udGV4dHMuYWRkKGNvbnRleHQpO1xuXHR9XG5cblx0cHJpdmF0ZSBjb250ZXh0Q291bnRlciA9IDA7XG5cdHB1YmxpYyBhc3luYyB0cmFuc2xhdGUoXG5cdFx0dGV4dDogc3RyaW5nLFxuXHRcdGZyb206IHN0cmluZyxcblx0XHR0bzogc3RyaW5nLFxuXHRcdG9wdGlvbnM/OiBJU2NoZWR1bGVyVHJhbnNsYXRlT3B0aW9ucyxcblx0KSB7XG5cdFx0Y29uc3Qge1xuXHRcdFx0Y29udGV4dCA9ICcnLFxuXHRcdFx0cHJpb3JpdHkgPSAwLFxuXHRcdFx0ZGlyZWN0VHJhbnNsYXRlOiBkaXJlY3RUcmFuc2xhdGVGb3JUaGlzUmVxdWVzdCA9IGZhbHNlLFxuXHRcdH0gPSBvcHRpb25zID8/IHt9O1xuXG5cdFx0aWYgKHRoaXMudHJhbnNsYXRvci5jaGVja0xpbWl0RXhjZWVkaW5nKHRleHQpIDw9IDApIHtcblx0XHRcdC8vIERpcmVjdCB0cmFuc2xhdGVcblx0XHRcdGlmIChcblx0XHRcdFx0ZGlyZWN0VHJhbnNsYXRlRm9yVGhpc1JlcXVlc3QgfHxcblx0XHRcdFx0KHRoaXMuY29uZmlnLmRpcmVjdFRyYW5zbGF0ZUxlbmd0aCAhPT0gbnVsbCAmJlxuXHRcdFx0XHRcdHRleHQubGVuZ3RoID49IHRoaXMuY29uZmlnLmRpcmVjdFRyYW5zbGF0ZUxlbmd0aClcblx0XHRcdCkge1xuXHRcdFx0XHRyZXR1cm4gdGhpcy5kaXJlY3RUcmFuc2xhdGUodGV4dCwgZnJvbSwgdG8pO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0cmV0dXJuIHRoaXMubWFrZVRhc2soeyB0ZXh0OiB0ZXh0LCBmcm9tLCB0bywgY29udGV4dCwgcHJpb3JpdHkgfSk7XG5cdFx0XHR9XG5cdFx0fSBlbHNlIHtcblx0XHRcdC8vIFNwbGl0IHRleHQgYnkgd29yZHMgYW5kIHRyYW5zbGF0ZVxuXHRcdFx0cmV0dXJuIHRoaXMuc3BsaXRBbmRUcmFuc2xhdGUodGV4dCwgZnJvbSwgdG8sIGNvbnRleHQsIHByaW9yaXR5KTtcblx0XHR9XG5cdH1cblxuXHRwcml2YXRlIGFzeW5jIGRpcmVjdFRyYW5zbGF0ZSh0ZXh0OiBzdHJpbmcsIGZyb206IHN0cmluZywgdG86IHN0cmluZykge1xuXHRcdGNvbnN0IGZyZWUgPSBhd2FpdCB0aGlzLnNlbWFmb3IudGFrZSgpO1xuXHRcdHJldHVybiB0aGlzLnRyYW5zbGF0b3IudHJhbnNsYXRlKHRleHQsIGZyb20sIHRvKS5maW5hbGx5KGZyZWUpO1xuXHR9XG5cblx0cHJpdmF0ZSBzcGxpdEFuZFRyYW5zbGF0ZShcblx0XHR0ZXh0OiBzdHJpbmcsXG5cdFx0ZnJvbTogc3RyaW5nLFxuXHRcdHRvOiBzdHJpbmcsXG5cdFx0Y29udGV4dDogc3RyaW5nLFxuXHRcdHByaW9yaXR5OiBudW1iZXIsXG5cdCkge1xuXHRcdGNvbnN0IHNwbGl0dGVkVGV4dDogc3RyaW5nW10gPSBbXTtcblx0XHRjb25zdCBjaGFyc2V0SW5kZXhlczogbnVtYmVyW10gPSBbXTtcblxuXHRcdGxldCB3b3Jkc0J1ZmZlciA9ICcnO1xuXHRcdGZvciAoY29uc3QgdGV4dE1hdGNoIG9mIHRleHQubWF0Y2hBbGwoLyhbXlxcc10rKShcXHMqKS9nKSkge1xuXHRcdFx0Y29uc3QgbmV3UGFydCA9IHRleHRNYXRjaFswXTtcblx0XHRcdGNvbnN0IG5ld0J1ZmZlciA9IHdvcmRzQnVmZmVyICsgbmV3UGFydDtcblxuXHRcdFx0Ly8gQWRkIHdvcmQgdG8gYnVmZmVyIGlmIGNhblxuXHRcdFx0aWYgKHRoaXMudHJhbnNsYXRvci5jaGVja0xpbWl0RXhjZWVkaW5nKG5ld0J1ZmZlcikgPD0gMCkge1xuXHRcdFx0XHR3b3Jkc0J1ZmZlciA9IG5ld0J1ZmZlcjtcblx0XHRcdFx0Y29udGludWU7XG5cdFx0XHR9XG5cblx0XHRcdC8vIFdyaXRlIGFuZCBjbGVhciBidWZmZXIgaWYgbm90IGVtcHRoeVxuXHRcdFx0aWYgKHdvcmRzQnVmZmVyLmxlbmd0aCA+IDApIHtcblx0XHRcdFx0c3BsaXR0ZWRUZXh0LnB1c2god29yZHNCdWZmZXIpO1xuXHRcdFx0XHR3b3Jkc0J1ZmZlciA9ICcnO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBIYW5kbGUgbmV3IHBhcnRcblx0XHRcdGlmICh0aGlzLnRyYW5zbGF0b3IuY2hlY2tMaW1pdEV4Y2VlZGluZyhuZXdQYXJ0KSA8PSAwKSB7XG5cdFx0XHRcdC8vIEFkZCB0byBidWZmZXJcblx0XHRcdFx0d29yZHNCdWZmZXIgKz0gbmV3UGFydDtcblx0XHRcdFx0Y29udGludWU7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHQvLyBTbGljZSBieSBjaGFyc1xuXHRcdFx0XHRsZXQgY2hhcnNCdWZmZXIgPSBuZXdQYXJ0O1xuXHRcdFx0XHR3aGlsZSAoY2hhcnNCdWZmZXIubGVuZ3RoID4gMCkge1xuXHRcdFx0XHRcdGNvbnN0IGV4dHJhQ2hhcnMgPSB0aGlzLnRyYW5zbGF0b3IuY2hlY2tMaW1pdEV4Y2VlZGluZyhjaGFyc0J1ZmZlcik7XG5cdFx0XHRcdFx0aWYgKGV4dHJhQ2hhcnMgPiAwKSB7XG5cdFx0XHRcdFx0XHRjb25zdCBvZmZzZXQgPSBjaGFyc0J1ZmZlci5sZW5ndGggLSBleHRyYUNoYXJzO1xuXG5cdFx0XHRcdFx0XHQvLyBXcml0ZSBzbGljZSBhbmQgcmVtYWluZGVyXG5cdFx0XHRcdFx0XHRzcGxpdHRlZFRleHQucHVzaChjaGFyc0J1ZmZlci5zbGljZSgwLCBvZmZzZXQpKTtcblx0XHRcdFx0XHRcdGNoYXJzQnVmZmVyID0gY2hhcnNCdWZmZXIuc2xpY2Uob2Zmc2V0KTtcblxuXHRcdFx0XHRcdFx0Y2hhcnNldEluZGV4ZXMucHVzaChzcGxpdHRlZFRleHQubGVuZ3RoIC0gMSk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Y29uc3QgY3R4UHJlZml4ID0gY29udGV4dC5sZW5ndGggPiAwID8gY29udGV4dCArICc7JyA6ICcnO1xuXHRcdHJldHVybiBQcm9taXNlLmFsbChcblx0XHRcdHNwbGl0dGVkVGV4dC5tYXAoKHRleHQsIGluZGV4KSA9PlxuXHRcdFx0XHRjaGFyc2V0SW5kZXhlcy5pbmNsdWRlcyhpbmRleClcblx0XHRcdFx0XHQ/IHRleHRcblx0XHRcdFx0XHQ6IHRoaXMubWFrZVRhc2soe1xuXHRcdFx0XHRcdFx0XHR0ZXh0LFxuXHRcdFx0XHRcdFx0XHRmcm9tLFxuXHRcdFx0XHRcdFx0XHR0byxcblx0XHRcdFx0XHRcdFx0Y29udGV4dDogY3R4UHJlZml4ICsgYHRleHQjJHt0aGlzLmNvbnRleHRDb3VudGVyKyt9YCxcblx0XHRcdFx0XHRcdFx0cHJpb3JpdHksXG5cdFx0XHRcdFx0XHR9KSxcblx0XHRcdCksXG5cdFx0KS50aGVuKCh0cmFuc2xhdGVkUGFydHMpID0+IHRyYW5zbGF0ZWRQYXJ0cy5qb2luKCcnKSk7XG5cdH1cblxuXHRwcml2YXRlIG1ha2VUYXNrKHsgdGV4dCwgZnJvbSwgdG8sIHByaW9yaXR5LCBjb250ZXh0ID0gJycgfTogVGFza0NvbnN0cnVjdG9yKSB7XG5cdFx0cmV0dXJuIG5ldyBQcm9taXNlPHN0cmluZz4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuXHRcdFx0dGhpcy5hZGRUb1Rhc2tDb250YWluZXIoe1xuXHRcdFx0XHR0ZXh0LFxuXHRcdFx0XHRmcm9tLFxuXHRcdFx0XHR0byxcblx0XHRcdFx0Y29udGV4dCxcblx0XHRcdFx0cHJpb3JpdHksXG5cdFx0XHRcdHJlc29sdmUsXG5cdFx0XHRcdHJlamVjdCxcblx0XHRcdH0pO1xuXHRcdH0pO1xuXHR9XG5cblx0cHJpdmF0ZSByZWFkb25seSB0YXNrQ29udGFpbmVyc1N0b3JhZ2UgPSBuZXcgU2V0PFRhc2tDb250YWluZXI+KCk7XG5cdHByaXZhdGUgYWRkVG9UYXNrQ29udGFpbmVyKHBhcmFtczogVGFza0NvbnN0cnVjdG9ySW50ZXJuYWwpIHtcblx0XHRjb25zdCB7XG5cdFx0XHR0ZXh0LFxuXHRcdFx0ZnJvbSxcblx0XHRcdHRvLFxuXHRcdFx0YXR0ZW1wdCA9IDAsXG5cdFx0XHRjb250ZXh0ID0gJycsXG5cdFx0XHRwcmlvcml0eSxcblx0XHRcdHJlc29sdmUsXG5cdFx0XHRyZWplY3QsXG5cdFx0fSA9IHBhcmFtcztcblxuXHRcdC8vIGNyZWF0ZSB0YXNrXG5cdFx0Y29uc3QgdGFzazogVGFzayA9IHtcblx0XHRcdHRleHQsXG5cdFx0XHRmcm9tLFxuXHRcdFx0dG8sXG5cdFx0XHRhdHRlbXB0LFxuXHRcdFx0cmVzb2x2ZSxcblx0XHRcdHJlamVjdCxcblx0XHR9O1xuXG5cdFx0bGV0IGNvbnRhaW5lcjogVGFza0NvbnRhaW5lciB8IG51bGwgPSBudWxsO1xuXG5cdFx0Ly8gdHJ5IGFkZCB0byBleGlzdHMgY29udGFpbmVyXG5cdFx0Zm9yIChjb25zdCB0YXNrQ29udGFpbmVyIG9mIHRoaXMudGFza0NvbnRhaW5lcnNTdG9yYWdlKSB7XG5cdFx0XHQvLyBTa2lwIGNvbnRhaW5lcnMgd2l0aCBub3QgZXF1YWwgcGFyYW1ldGVyc1xuXHRcdFx0aWYgKFxuXHRcdFx0XHQoWydmcm9tJywgJ3RvJywgJ2NvbnRleHQnLCAncHJpb3JpdHknXSBhcyBjb25zdCkuc29tZShcblx0XHRcdFx0XHQoa2V5KSA9PiBwYXJhbXNba2V5XSAhPT0gdGFza0NvbnRhaW5lcltrZXldLFxuXHRcdFx0XHQpXG5cdFx0XHQpXG5cdFx0XHRcdGNvbnRpbnVlO1xuXG5cdFx0XHQvLyBMaWdodHdlaWdodCBjaGVjayB0byBvdmVyZmxvd1xuXHRcdFx0Ly8gTk9URTogRG8gc3RyaWN0IGNoZWNrIGhlcmUgaWYgeW91IG5lZWQgY29tcGx5IGEgbGltaXQgY29udHJhY3Rcblx0XHRcdGlmIChcblx0XHRcdFx0dGhpcy50cmFuc2xhdG9yLmdldExlbmd0aExpbWl0KCkgPj1cblx0XHRcdFx0dGFza0NvbnRhaW5lci5sZW5ndGggKyB0YXNrLnRleHQubGVuZ3RoXG5cdFx0XHQpIHtcblx0XHRcdFx0dGFza0NvbnRhaW5lci50YXNrcy5wdXNoKHRhc2spO1xuXHRcdFx0XHR0YXNrQ29udGFpbmVyLmxlbmd0aCArPSB0YXNrLnRleHQubGVuZ3RoO1xuXHRcdFx0XHRjb250YWluZXIgPSB0YXNrQ29udGFpbmVyO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC8vIG1ha2UgY29udGFpbmVyXG5cdFx0aWYgKGNvbnRhaW5lciA9PT0gbnVsbCkge1xuXHRcdFx0Y29uc3QgbmV3VGFza0NvbnRhaW5lcjogVGFza0NvbnRhaW5lciA9IHtcblx0XHRcdFx0Y29udGV4dCxcblx0XHRcdFx0cHJpb3JpdHksXG5cdFx0XHRcdGZyb20sXG5cdFx0XHRcdHRvLFxuXHRcdFx0XHR0YXNrczogW3Rhc2tdLFxuXHRcdFx0XHRsZW5ndGg6IHRhc2sudGV4dC5sZW5ndGgsXG5cdFx0XHR9O1xuXHRcdFx0dGhpcy50YXNrQ29udGFpbmVyc1N0b3JhZ2UuYWRkKG5ld1Rhc2tDb250YWluZXIpO1xuXHRcdFx0Y29udGFpbmVyID0gbmV3VGFza0NvbnRhaW5lcjtcblx0XHR9XG5cblx0XHRpZiAoXG5cdFx0XHR0aGlzLmNvbmZpZy5jaHVua1NpemVGb3JJbnN0YW50VHJhbnNsYXRlICE9PSBudWxsICYmXG5cdFx0XHRjb250YWluZXIubGVuZ3RoID49IHRoaXMuY29uZmlnLmNodW5rU2l6ZUZvckluc3RhbnRUcmFuc2xhdGVcblx0XHQpIHtcblx0XHRcdHRoaXMuYWRkVG9UcmFuc2xhdGVRdWV1ZShjb250YWluZXIpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLnVwZGF0ZURlbGF5Rm9yQWRkVG9UcmFuc2xhdGVRdWV1ZShjb250YWluZXIpO1xuXHRcdH1cblx0fVxuXG5cdHByaXZhdGUgcmVhZG9ubHkgdGltZXJzTWFwID0gbmV3IE1hcDxUYXNrQ29udGFpbmVyLCBudW1iZXIgfCBOb2RlSlMuVGltZW91dD4oKTtcblx0cHJpdmF0ZSB1cGRhdGVEZWxheUZvckFkZFRvVHJhbnNsYXRlUXVldWUodGFza0NvbnRhaW5lcjogVGFza0NvbnRhaW5lcikge1xuXHRcdC8vIEZsdXNoIHRpbWVyXG5cdFx0aWYgKHRoaXMudGltZXJzTWFwLmhhcyh0YXNrQ29udGFpbmVyKSkge1xuXHRcdFx0Ly8gRHVlIHRvIGV4cGVjdGF0aW9uIHJ1biBvbiBvbmUgcGxhdGZvcm0sIHRpbWVyIG9iamVjdHMgd2lsbCBzYW1lIGFsd2F5c1xuXHRcdFx0Z2xvYmFsVGhpcy5jbGVhclRpbWVvdXQodGhpcy50aW1lcnNNYXAuZ2V0KHRhc2tDb250YWluZXIpKTtcblx0XHR9XG5cblx0XHR0aGlzLnRpbWVyc01hcC5zZXQoXG5cdFx0XHR0YXNrQ29udGFpbmVyLFxuXHRcdFx0Z2xvYmFsVGhpcy5zZXRUaW1lb3V0KCgpID0+IHtcblx0XHRcdFx0dGhpcy5hZGRUb1RyYW5zbGF0ZVF1ZXVlKHRhc2tDb250YWluZXIpO1xuXHRcdFx0fSwgdGhpcy5jb25maWcudHJhbnNsYXRlUG9vbERlbGF5KSxcblx0XHQpO1xuXHR9XG5cblx0LyoqXG5cdCAqIFRhc2tzIHF1ZXVlIHdpdGggaXRlbXMgc29ydGVkIGJ5IHByaW9yaXR5XG5cdCAqIEl0IG11c3QgYmUgaGFuZGxlZCBmcm9tIGVuZCB0byBzdGFydFxuXHQgKi9cblx0cHJpdmF0ZSB0cmFuc2xhdGVRdWV1ZTogVGFza0NvbnRhaW5lcltdID0gW107XG5cdHByaXZhdGUgYWRkVG9UcmFuc2xhdGVRdWV1ZSh0YXNrQ29udGFpbmVyOiBUYXNrQ29udGFpbmVyKSB7XG5cdFx0Ly8gRmx1c2ggdGltZXJcblx0XHRpZiAodGhpcy50aW1lcnNNYXAuaGFzKHRhc2tDb250YWluZXIpKSB7XG5cdFx0XHQvLyBEdWUgdG8gZXhwZWN0YXRpb24gcnVuIG9uIG9uZSBwbGF0Zm9ybSwgdGltZXIgb2JqZWN0cyB3aWxsIHNhbWUgYWx3YXlzXG5cdFx0XHRnbG9iYWxUaGlzLmNsZWFyVGltZW91dCh0aGlzLnRpbWVyc01hcC5nZXQodGFza0NvbnRhaW5lcikpO1xuXHRcdFx0dGhpcy50aW1lcnNNYXAuZGVsZXRlKHRhc2tDb250YWluZXIpO1xuXHRcdH1cblxuXHRcdHRoaXMudGFza0NvbnRhaW5lcnNTdG9yYWdlLmRlbGV0ZSh0YXNrQ29udGFpbmVyKTtcblxuXHRcdC8vIFJlc29ydCBxdWV1ZSBieSBwcmlvcml0eSBlYWNoIHRpbWUgdG8ga2VlcCBjb25zaXN0ZW5jeVxuXHRcdHRoaXMudHJhbnNsYXRlUXVldWUgPSB0aGlzLnRyYW5zbGF0ZVF1ZXVlXG5cdFx0XHQuY29uY2F0KHRhc2tDb250YWluZXIpXG5cdFx0XHQuc29ydCgoYSwgYikgPT4gYS5wcmlvcml0eSAtIGIucHJpb3JpdHkpO1xuXG5cdFx0aWYgKCF0aGlzLndvcmtlclN0YXRlKSB7XG5cdFx0XHR0aGlzLnJ1bldvcmtlcigpLmNhdGNoKChlcnJvcjogdW5rbm93bikgPT4ge1xuXHRcdFx0XHR0aHJvdyBlcnJvcjtcblx0XHRcdH0pO1xuXHRcdH1cblx0fVxuXG5cdC8qKlxuXHQgKiBSZXR1cm4gZmlyc3QgaXRlbSBmcm9tIHF1ZXVlIGFuZCBkZWxldGUgaXQgZnJvbSBxdWV1ZVxuXHQgKiBJdGVtcyBpcyBzb3J0ZWQgYnkgcHJpb3JpdHlcblx0ICovXG5cdHByaXZhdGUgcmVhZG9ubHkgZ2V0SXRlbUZyb21UcmFuc2xhdGVRdWV1ZSA9ICgpOiBJdGVyYXRvclN0ZXA8VGFza0NvbnRhaW5lcj4gPT4ge1xuXHRcdHJldHVybiB7XG5cdFx0XHRkb25lOiB0aGlzLnRyYW5zbGF0ZVF1ZXVlLmxlbmd0aCA9PT0gMCxcblx0XHRcdHZhbHVlOiB0aGlzLnRyYW5zbGF0ZVF1ZXVlLnBvcCgpID8/IG51bGwsXG5cdFx0fTtcblx0fTtcblxuXHRwcml2YXRlIHdvcmtlclN0YXRlID0gZmFsc2U7XG5cdHByaXZhdGUgYXN5bmMgcnVuV29ya2VyKCkge1xuXHRcdHRoaXMud29ya2VyU3RhdGUgPSB0cnVlO1xuXG5cdFx0bGV0IGZpcnN0SXRlcmF0aW9uID0gdHJ1ZTtcblx0XHQvLyBEYWVtb24gbG9vcFxuXHRcdC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tdW5uZWNlc3NhcnktY29uZGl0aW9uXG5cdFx0d2hpbGUgKHRydWUpIHtcblx0XHRcdC8vIERlbGF5IGZpcnN0IGl0ZXJhdGlvbiB0byBhd2FpdCBmaWxsIHRoZSBxdWV1ZSwgdG8gY29uc2lkZXIgcHJpb3JpdHkgYmV0dGVyXG5cdFx0XHRjb25zdCB3b3JrZXJIYW5kbGVEZWxheSA9IHRoaXMuY29uZmlnLnRhc2tCYXRjaEhhbmRsZURlbGF5O1xuXHRcdFx0aWYgKHdvcmtlckhhbmRsZURlbGF5ICYmIGZpcnN0SXRlcmF0aW9uKSB7XG5cdFx0XHRcdGF3YWl0IG5ldyBQcm9taXNlKChyZXMpID0+IHNldFRpbWVvdXQocmVzLCB3b3JrZXJIYW5kbGVEZWxheSkpO1xuXHRcdFx0fVxuXG5cdFx0XHRmaXJzdEl0ZXJhdGlvbiA9IGZhbHNlO1xuXG5cdFx0XHRjb25zdCBpdGVyYXRlID0gdGhpcy5nZXRJdGVtRnJvbVRyYW5zbGF0ZVF1ZXVlKCk7XG5cblx0XHRcdC8vIFNraXAgd2hlbiBxdWV1ZSBlbXB0eVxuXHRcdFx0aWYgKGl0ZXJhdGUuZG9uZSB8fCBpdGVyYXRlLnZhbHVlID09PSBudWxsKSBicmVhaztcblxuXHRcdFx0Y29uc3QgdGFza0NvbnRhaW5lciA9IGl0ZXJhdGUudmFsdWU7XG5cblx0XHRcdGNvbnN0IGZyZWUgPSBhd2FpdCB0aGlzLnNlbWFmb3IudGFrZSgpO1xuXG5cdFx0XHRjb25zdCB0ZXh0QXJyYXkgPSB0YXNrQ29udGFpbmVyLnRhc2tzLm1hcCgoaSkgPT4gaS50ZXh0KTtcblx0XHRcdGF3YWl0IHRoaXMudHJhbnNsYXRvclxuXHRcdFx0XHQudHJhbnNsYXRlQmF0Y2godGV4dEFycmF5LCB0YXNrQ29udGFpbmVyLmZyb20sIHRhc2tDb250YWluZXIudG8pXG5cdFx0XHRcdC50aGVuKChyZXN1bHQpID0+IHtcblx0XHRcdFx0XHRmb3IgKGxldCBpbmRleCA9IDA7IGluZGV4IDwgdGFza0NvbnRhaW5lci50YXNrcy5sZW5ndGg7IGluZGV4KyspIHtcblx0XHRcdFx0XHRcdGNvbnN0IHRhc2sgPSB0YXNrQ29udGFpbmVyLnRhc2tzW2luZGV4XTtcblxuXHRcdFx0XHRcdFx0Y29uc3QgdHJhbnNsYXRlZFRleHQgPSByZXN1bHRbaW5kZXhdO1xuXHRcdFx0XHRcdFx0aWYgKHRyYW5zbGF0ZWRUZXh0ICE9PSBudWxsKSB7XG5cdFx0XHRcdFx0XHRcdHRhc2sucmVzb2x2ZSh0cmFuc2xhdGVkVGV4dCk7XG5cdFx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0XHR0aGlzLnRhc2tFcnJvckhhbmRsZXIoXG5cdFx0XHRcdFx0XHRcdFx0dGFzayxcblx0XHRcdFx0XHRcdFx0XHRuZXcgRXJyb3IoXCJUcmFuc2xhdG9yIG1vZHVsZSBjYW4ndCB0cmFuc2xhdGUgdGhpc1wiKSxcblx0XHRcdFx0XHRcdFx0XHR0YXNrQ29udGFpbmVyLmNvbnRleHQsXG5cdFx0XHRcdFx0XHRcdFx0dGFza0NvbnRhaW5lci5wcmlvcml0eSxcblx0XHRcdFx0XHRcdFx0KTtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0pXG5cdFx0XHRcdC5jYXRjaCgocmVhc29uOiB1bmtub3duKSA9PiB7XG5cdFx0XHRcdFx0Y29uc29sZS5lcnJvcihyZWFzb24pO1xuXG5cdFx0XHRcdFx0Zm9yIChjb25zdCB0YXNrIG9mIHRhc2tDb250YWluZXIudGFza3MpIHtcblx0XHRcdFx0XHRcdHRoaXMudGFza0Vycm9ySGFuZGxlcihcblx0XHRcdFx0XHRcdFx0dGFzayxcblx0XHRcdFx0XHRcdFx0cmVhc29uLFxuXHRcdFx0XHRcdFx0XHR0YXNrQ29udGFpbmVyLmNvbnRleHQsXG5cdFx0XHRcdFx0XHRcdHRhc2tDb250YWluZXIucHJpb3JpdHksXG5cdFx0XHRcdFx0XHQpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSlcblx0XHRcdFx0LmZpbmFsbHkoZnJlZSk7XG5cdFx0fVxuXG5cdFx0dGhpcy53b3JrZXJTdGF0ZSA9IGZhbHNlO1xuXHR9XG5cblx0cHJpdmF0ZSB0YXNrRXJyb3JIYW5kbGVyKFxuXHRcdHRhc2s6IFRhc2ssXG5cdFx0ZXJyb3I6IHVua25vd24sXG5cdFx0Y29udGV4dDogc3RyaW5nLFxuXHRcdHByaW9yaXR5OiBudW1iZXIsXG5cdCkge1xuXHRcdGlmICh0aGlzLmFib3J0ZWRDb250ZXh0cy5oYXMoY29udGV4dCkpIHtcblx0XHRcdHRhc2sucmVqZWN0KGVycm9yKTtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRpZiAodGFzay5hdHRlbXB0ID49IHRoaXMuY29uZmlnLnRyYW5zbGF0ZVJldHJ5QXR0ZW1wdExpbWl0KSB7XG5cdFx0XHRpZiAodGhpcy5jb25maWcuaXNBbGxvd0RpcmVjdFRyYW5zbGF0ZUJhZENodW5rcykge1xuXHRcdFx0XHRjb25zdCB7IHRleHQsIGZyb20sIHRvLCByZXNvbHZlLCByZWplY3QgfSA9IHRhc2s7XG5cdFx0XHRcdHRoaXMuZGlyZWN0VHJhbnNsYXRlKHRleHQsIGZyb20sIHRvKS50aGVuKHJlc29sdmUsIHJlamVjdCk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHR0YXNrLnJlamVjdChlcnJvcik7XG5cdFx0XHR9XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuYWRkVG9UYXNrQ29udGFpbmVyKHtcblx0XHRcdFx0Li4udGFzayxcblx0XHRcdFx0YXR0ZW1wdDogdGFzay5hdHRlbXB0ICsgMSxcblx0XHRcdFx0Y29udGV4dCxcblx0XHRcdFx0cHJpb3JpdHksXG5cdFx0XHR9KTtcblx0XHR9XG5cdH1cbn1cbiJdfQ==