anylang
Version:
A translator's kit that uses the free APIs of Google Translate, Yandex, Bing, ChatGPT, and other LLMs
351 lines (349 loc) • 44.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Scheduler = void 0;
var _Semaphore = require("../utils/Semaphore");
var __awaiter = void 0 && (void 0).__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());
});
};
/**
* 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
*/
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.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
}));
}
}
}
exports.Scheduler = Scheduler;
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZWR1bGluZy9TY2hlZHVsZXIuanMiLCJuYW1lcyI6WyJfU2VtYXBob3JlIiwicmVxdWlyZSIsIlNjaGVkdWxlciIsImNvbnN0cnVjdG9yIiwidHJhbnNsYXRvciIsImNvbmZpZyIsInRyYW5zbGF0ZVJldHJ5QXR0ZW1wdExpbWl0IiwiaXNBbGxvd0RpcmVjdFRyYW5zbGF0ZUJhZENodW5rcyIsImRpcmVjdFRyYW5zbGF0ZUxlbmd0aCIsInRyYW5zbGF0ZVBvb2xEZWxheSIsImNodW5rU2l6ZUZvckluc3RhbnRUcmFuc2xhdGUiLCJ0YXNrQmF0Y2hIYW5kbGVEZWxheSIsImFib3J0ZWRDb250ZXh0cyIsIlNldCIsImNvbnRleHRDb3VudGVyIiwidGFza0NvbnRhaW5lcnNTdG9yYWdlIiwidGltZXJzTWFwIiwiTWFwIiwidHJhbnNsYXRlUXVldWUiLCJnZXRJdGVtRnJvbVRyYW5zbGF0ZVF1ZXVlIiwiZG9uZSIsImxlbmd0aCIsInZhbHVlIiwiX2EiLCJwb3AiLCJ3b3JrZXJTdGF0ZSIsIk9iamVjdCIsImFzc2lnbiIsInNlbWFmb3IiLCJTZW1hcGhvcmUiLCJ0aW1lb3V0IiwiZ2V0UmVxdWVzdHNUaW1lb3V0IiwiYWJvcnQiLCJjb250ZXh0IiwiYWJvcnRUYXNrcyIsInRhc2tzIiwiZm9yRWFjaCIsInRhc2siLCJyZWplY3QiLCJFcnJvciIsImRlbGV0ZSIsImZpbHRlciIsImFkZCIsInRyYW5zbGF0ZSIsInRleHQiLCJmcm9tIiwidG8iLCJvcHRpb25zIiwicHJpb3JpdHkiLCJkaXJlY3RUcmFuc2xhdGUiLCJkaXJlY3RUcmFuc2xhdGVGb3JUaGlzUmVxdWVzdCIsImNoZWNrTGltaXRFeGNlZWRpbmciLCJtYWtlVGFzayIsInNwbGl0QW5kVHJhbnNsYXRlIiwiZnJlZSIsInRha2UiLCJmaW5hbGx5Iiwic3BsaXR0ZWRUZXh0IiwiY2hhcnNldEluZGV4ZXMiLCJ3b3Jkc0J1ZmZlciIsInRleHRNYXRjaCIsIm1hdGNoQWxsIiwibmV3UGFydCIsIm5ld0J1ZmZlciIsInB1c2giLCJjaGFyc0J1ZmZlciIsImV4dHJhQ2hhcnMiLCJvZmZzZXQiLCJzbGljZSIsImN0eFByZWZpeCIsIlByb21pc2UiLCJhbGwiLCJtYXAiLCJpbmRleCIsImluY2x1ZGVzIiwidGhlbiIsInRyYW5zbGF0ZWRQYXJ0cyIsImpvaW4iLCJyZXNvbHZlIiwiYWRkVG9UYXNrQ29udGFpbmVyIiwicGFyYW1zIiwiYXR0ZW1wdCIsImNvbnRhaW5lciIsInRhc2tDb250YWluZXIiLCJzb21lIiwia2V5IiwiZ2V0TGVuZ3RoTGltaXQiLCJuZXdUYXNrQ29udGFpbmVyIiwiYWRkVG9UcmFuc2xhdGVRdWV1ZSIsInVwZGF0ZURlbGF5Rm9yQWRkVG9UcmFuc2xhdGVRdWV1ZSIsImhhcyIsImdsb2JhbFRoaXMiLCJjbGVhclRpbWVvdXQiLCJnZXQiLCJzZXQiLCJzZXRUaW1lb3V0IiwiY29uY2F0Iiwic29ydCIsImEiLCJiIiwicnVuV29ya2VyIiwiY2F0Y2giLCJlcnJvciIsImZpcnN0SXRlcmF0aW9uIiwid29ya2VySGFuZGxlRGVsYXkiLCJyZXMiLCJpdGVyYXRlIiwidGV4dEFycmF5IiwiaSIsInRyYW5zbGF0ZUJhdGNoIiwicmVzdWx0IiwidHJhbnNsYXRlZFRleHQiLCJ0YXNrRXJyb3JIYW5kbGVyIiwicmVhc29uIiwiY29uc29sZSIsImV4cG9ydHMiXSwic291cmNlcyI6WyJzY2hlZHVsaW5nL1NjaGVkdWxlci50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBUcmFuc2xhdG9ySW5zdGFuY2VNZW1iZXJzIH0gZnJvbSAnLi4vdHJhbnNsYXRvcnMvVHJhbnNsYXRvcic7XG5pbXBvcnQgeyBTZW1hcGhvcmUgfSBmcm9tICcuLi91dGlscy9TZW1hcGhvcmUnO1xuaW1wb3J0IHsgSVNjaGVkdWxlciwgSVNjaGVkdWxlclRyYW5zbGF0ZU9wdGlvbnMgfSBmcm9tICcuJztcblxuaW50ZXJmYWNlIFNjaGVkdWxlckNvbmZpZyB7XG5cdC8qKlxuXHQgKiBOdW1iZXIgb2YgYXR0ZW1wdHMgZm9yIHJldHJ5IHJlcXVlc3Rcblx0ICovXG5cdHRyYW5zbGF0ZVJldHJ5QXR0ZW1wdExpbWl0PzogbnVtYmVyO1xuXG5cdC8qKlxuXHQgKiBJZiB0cnVlIC0gcmVqZWN0ZWQgcmVxdWVzdHMgd2lsbCB1c2UgZGlyZWN0IHRyYW5zbGF0ZVxuXHQgKi9cblx0aXNBbGxvd0RpcmVjdFRyYW5zbGF0ZUJhZENodW5rcz86IGJvb2xlYW47XG5cblx0LyoqXG5cdCAqIExlbmd0aCBvZiBzdHJpbmcgZm9yIGRpcmVjdCB0cmFuc2xhdGUuXG5cdCAqXG5cdCAqIG51bGwgZm9yIGRpc2FibGUgdGhlIGNvbmRpdGlvblxuXHQgKi9cblx0ZGlyZWN0VHJhbnNsYXRlTGVuZ3RoPzogbnVtYmVyIHwgbnVsbDtcblxuXHQvKipcblx0ICogRGVsYXkgZm9yIHRyYW5zbGF0ZSBhIGNodW5rLiBUaGUgYmlnZ2VyIHRoZSBtb3JlIHJlcXVlc3RzIHdpbGwgY29sbGVjdFxuXHQgKi9cblx0dHJhbnNsYXRlUG9vbERlbGF5PzogbnVtYmVyO1xuXG5cdC8qKlxuXHQgKiBXaGVuIGNodW5rIGNvbGxlY3QgdGhpcyBzaXplLCBpdCdzIHdpbGwgYmUgaW5zdGFudCBhZGQgdG8gYSB0cmFuc2xhdGUgcXVldWVcblx0ICpcblx0ICogbnVsbCBmb3IgZGlzYWJsZSB0aGUgY29uZGl0aW9uXG5cdCAqL1xuXHRjaHVua1NpemVGb3JJbnN0YW50VHJhbnNsYXRlPzogbnVtYmVyIHwgbnVsbDtcblxuXHQvKipcblx0ICogUGF1c2UgYmV0d2VlbiBoYW5kbGUgdGFzayBiYXRjaGVzXG5cdCAqXG5cdCAqIEl0IG1heSBiZSB1c2VmdWwgdG8gYXdhaXQgYWNjdW11bGF0aW5nIGEgdGFzayBiYXRjaGVzIGluIHF1ZXVlIHRvIGNvbnNpZGVyIHByaW9yaXR5IGJldHRlciBhbmQgZG9uJ3QgdHJhbnNsYXRlIGZpcnN0IHRhc2sgYmF0Y2ggaW1tZWRpYXRlbHlcblx0ICpcblx0ICogV0FSTklORzogdGhpcyBvcHRpb24gbXVzdCBiZSB1c2VkIG9ubHkgZm9yIGNvbnNpZGVyIHByaW9yaXR5IGJldHRlciEgU2V0IHNtYWxsIHZhbHVlIGFsd2F5cyAoMTAtNTBtcylcblx0ICpcblx0ICogV2hlbiB0aGlzIG9wdGlvbiBpcyBkaXNhYmxlZCAoYnkgZGVmYXVsdCkgYW5kIHlvdSBjYWxsIHRyYW5zbGF0ZSBtZXRob2QgZm9yIHRleHRzIHdpdGggcHJpb3JpdHkgMSBhbmQgdGhlbiBpbW1lZGlhdGVseSBmb3IgdGV4dCB3aXRoIHByaW9yaXR5IDIsIGZpcnN0IHJlcXVlc3Qgd2lsbCBoYXZlIGxlc3MgZGVsYXkgZm9yIHRyYW5zbGF0ZSBhbmQgd2lsbCB0cmFuc2xhdGUgZmlyc3QsIGV2ZW4gd2l0aCBsb3dlciBwcmlvcml0eSwgYmVjYXVzZSB3b3JrZXIgd2lsbCB0cmFuc2xhdGUgZmlyc3QgdGFzayBpbW1lZGlhdGVseSBhZnRlciBkZWxheSBkZWZpbmVkIGJ5IG9wdGlvbiBgdHJhbnNsYXRlUG9vbERlbGF5YFxuXHQgKi9cblx0dGFza0JhdGNoSGFuZGxlRGVsYXk/OiBudWxsIHwgbnVtYmVyO1xufVxuXG5pbnRlcmZhY2UgVGFza0NvbnN0cnVjdG9yIHtcblx0dGV4dDogc3RyaW5nO1xuXHRmcm9tOiBzdHJpbmc7XG5cdHRvOiBzdHJpbmc7XG5cblx0LyoqXG5cdCAqIFRvIGNvbWJpbmUgdGFza3MgYnkgdW5pcXVlIGtleVxuXHQgKi9cblx0Y29udGV4dD86IHN0cmluZztcblxuXHQvKipcblx0ICogVG8gY29tYmluZSBhbmQgc29ydCB0YXNrcyBieSBwcmlvcml0eVxuXHQgKi9cblx0cHJpb3JpdHk6IG51bWJlcjtcbn1cblxuaW50ZXJmYWNlIFRhc2tDb25zdHJ1Y3RvckludGVybmFsIGV4dGVuZHMgVGFza0NvbnN0cnVjdG9yIHtcblx0LyoqXG5cdCAqIEN1cnJlbnQgcmV0cnkgYXR0ZW1wdFxuXHQgKi9cblx0YXR0ZW1wdD86IG51bWJlcjtcblxuXHRyZXNvbHZlOiAodmFsdWU6IHN0cmluZyB8IFByb21pc2VMaWtlPHN0cmluZz4pID0+IHZvaWQ7XG5cdHJlamVjdDogKHJlYXNvbj86IHVua25vd24pID0+IHZvaWQ7XG59XG5cbmludGVyZmFjZSBUYXNrIHtcblx0dGV4dDogc3RyaW5nO1xuXHRmcm9tOiBzdHJpbmc7XG5cdHRvOiBzdHJpbmc7XG5cblx0LyoqXG5cdCAqIEN1cnJlbnQgcmV0cnkgYXR0ZW1wdFxuXHQgKi9cblx0YXR0ZW1wdDogbnVtYmVyO1xuXG5cdHJlc29sdmU6ICh2YWx1ZTogc3RyaW5nIHwgUHJvbWlzZUxpa2U8c3RyaW5nPikgPT4gdm9pZDtcblx0cmVqZWN0OiAocmVhc29uPzogdW5rbm93bikgPT4gdm9pZDtcbn1cblxuaW50ZXJmYWNlIFRhc2tDb250YWluZXIge1xuXHQvKipcblx0ICogRm9yIGNvbWJpbmUgdGFza3MgYnkgdW5pcXVlIGtleVxuXHQgKi9cblx0Y29udGV4dDogc3RyaW5nO1xuXG5cdHByaW9yaXR5OiBudW1iZXI7XG5cblx0ZnJvbTogc3RyaW5nO1xuXHR0bzogc3RyaW5nO1xuXHR0YXNrczogVGFza1tdO1xuXG5cdC8qKlxuXHQgKiBUb3RhbCBsZW5ndGggb2YgdGV4dCBmcm9tIGFsbCB0YXNrc1xuXHQgKi9cblx0bGVuZ3RoOiBudW1iZXI7XG59XG5cbnR5cGUgSXRlcmF0b3JTdGVwPFQ+ID0ge1xuXHRkb25lOiBib29sZWFuO1xuXHR2YWx1ZTogVCB8IG51bGw7XG59O1xuXG4vKipcbiAqIE1vZHVsZSBmb3Igc2NoZWR1bGluZyBhbmQgb3B0aW1pemF0aW9uIG9mIHRyYW5zbGF0ZSBhIHRleHQgc3RyZWFtc1xuICpcbiAqIC0gSXQgY2FuIHVuaW9uIG1hbnkgdHJhbnNsYXRlIHJlcXVlc3RzIHRvIG9uZVxuICogLSBZb3UgY2FuIGdyb3VwIGFueSByZXF1ZXN0cyBieSBjb250ZXh0XG4gKiAtIEl0J3MgY29uZmlndXJhYmxlLiBZb3UgY2FuIHNldCByZXRyeSBsaW1pdCBhbmQgZWRnZSBmb3IgZGlyZWN0IHRyYW5zbGF0ZVxuICovXG5leHBvcnQgY2xhc3MgU2NoZWR1bGVyIGltcGxlbWVudHMgSVNjaGVkdWxlciB7XG5cdHByaXZhdGUgcmVhZG9ubHkgc2VtYWZvcjtcblx0cHJpdmF0ZSByZWFkb25seSB0cmFuc2xhdG9yO1xuXHRwcml2YXRlIHJlYWRvbmx5IGNvbmZpZzogUmVxdWlyZWQ8U2NoZWR1bGVyQ29uZmlnPiA9IHtcblx0XHR0cmFuc2xhdGVSZXRyeUF0dGVtcHRMaW1pdDogMixcblx0XHRpc0FsbG93RGlyZWN0VHJhbnNsYXRlQmFkQ2h1bmtzOiB0cnVlLFxuXHRcdGRpcmVjdFRyYW5zbGF0ZUxlbmd0aDogbnVsbCxcblx0XHR0cmFuc2xhdGVQb29sRGVsYXk6IDMwMCxcblx0XHRjaHVua1NpemVGb3JJbnN0YW50VHJhbnNsYXRlOiBudWxsLFxuXHRcdHRhc2tCYXRjaEhhbmRsZURlbGF5OiBudWxsLFxuXHR9O1xuXG5cdGNvbnN0cnVjdG9yKHRyYW5zbGF0b3I6IFRyYW5zbGF0b3JJbnN0YW5jZU1lbWJlcnMsIGNvbmZpZz86IFNjaGVkdWxlckNvbmZpZykge1xuXHRcdHRoaXMudHJhbnNsYXRvciA9IHRyYW5zbGF0b3I7XG5cblx0XHR0aGlzLmNvbmZpZyA9IHsgLi4udGhpcy5jb25maWcsIC4uLmNvbmZpZyB9O1xuXG5cdFx0dGhpcy5zZW1hZm9yID0gbmV3IFNlbWFwaG9yZSh7IHRpbWVvdXQ6IHRyYW5zbGF0b3IuZ2V0UmVxdWVzdHNUaW1lb3V0KCkgfSk7XG5cdH1cblxuXHRwcml2YXRlIHJlYWRvbmx5IGFib3J0ZWRDb250ZXh0cyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuXHQvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L3JlcXVpcmUtYXdhaXRcblx0cHVibGljIGFzeW5jIGFib3J0KGNvbnRleHQ6IHN0cmluZykge1xuXHRcdGNvbnN0IGFib3J0VGFza3MgPSAodGFza3M6IFRhc2tbXSkgPT4ge1xuXHRcdFx0dGFza3MuZm9yRWFjaCgodGFzaykgPT4ge1xuXHRcdFx0XHR0YXNrLnJlamVjdChuZXcgRXJyb3IoJ1RyYW5zbGF0aW9uIGlzIGFib3J0ZWQgaW4gc2NoZWR1bGVyJykpO1xuXHRcdFx0fSk7XG5cdFx0fTtcblxuXHRcdC8vIENsZWFyIHRhc2tzXG5cdFx0Zm9yIChjb25zdCB0YXNrIG9mIHRoaXMudGFza0NvbnRhaW5lcnNTdG9yYWdlKSB7XG5cdFx0XHRpZiAoY29udGV4dCA9PT0gdGFzay5jb250ZXh0KSB7XG5cdFx0XHRcdHRoaXMudGFza0NvbnRhaW5lcnNTdG9yYWdlLmRlbGV0ZSh0YXNrKTtcblx0XHRcdFx0YWJvcnRUYXNrcyh0YXNrLnRhc2tzKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBSZW1vdmUgdGFza3MgZnJvbSB0cmFuc2xhdGlvbiBxdWV1ZVxuXHRcdHRoaXMudHJhbnNsYXRlUXVldWUgPSB0aGlzLnRyYW5zbGF0ZVF1ZXVlLmZpbHRlcigodGFzaykgPT4ge1xuXHRcdFx0Ly8gQWJvcnQgYW5kIGZpbHRlciBvdXQgbWF0Y2hlZCB0YXNrc1xuXHRcdFx0aWYgKGNvbnRleHQgPT09IHRhc2suY29udGV4dCkge1xuXHRcdFx0XHRhYm9ydFRhc2tzKHRhc2sudGFza3MpO1xuXHRcdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0cnVlO1xuXHRcdH0pO1xuXG5cdFx0Ly8gVE9ETzogYWJvcnQgZXZlbiBzZW50IHJlcXVlc3RzXG5cdFx0Ly8gQWJvcnQgaW4tZmxpZ2h0IHRyYW5zbGF0aW9uc1xuXHRcdHRoaXMuYWJvcnRlZENvbnRleHRzLmFkZChjb250ZXh0KTtcblx0fVxuXG5cdHByaXZhdGUgY29udGV4dENvdW50ZXIgPSAwO1xuXHRwdWJsaWMgYXN5bmMgdHJhbnNsYXRlKFxuXHRcdHRleHQ6IHN0cmluZyxcblx0XHRmcm9tOiBzdHJpbmcsXG5cdFx0dG86IHN0cmluZyxcblx0XHRvcHRpb25zPzogSVNjaGVkdWxlclRyYW5zbGF0ZU9wdGlvbnMsXG5cdCkge1xuXHRcdGNvbnN0IHtcblx0XHRcdGNvbnRleHQgPSAnJyxcblx0XHRcdHByaW9yaXR5ID0gMCxcblx0XHRcdGRpcmVjdFRyYW5zbGF0ZTogZGlyZWN0VHJhbnNsYXRlRm9yVGhpc1JlcXVlc3QgPSBmYWxzZSxcblx0XHR9ID0gb3B0aW9ucyA/PyB7fTtcblxuXHRcdGlmICh0aGlzLnRyYW5zbGF0b3IuY2hlY2tMaW1pdEV4Y2VlZGluZyh0ZXh0KSA8PSAwKSB7XG5cdFx0XHQvLyBEaXJlY3QgdHJhbnNsYXRlXG5cdFx0XHRpZiAoXG5cdFx0XHRcdGRpcmVjdFRyYW5zbGF0ZUZvclRoaXNSZXF1ZXN0IHx8XG5cdFx0XHRcdCh0aGlzLmNvbmZpZy5kaXJlY3RUcmFuc2xhdGVMZW5ndGggIT09IG51bGwgJiZcblx0XHRcdFx0XHR0ZXh0Lmxlbmd0aCA+PSB0aGlzLmNvbmZpZy5kaXJlY3RUcmFuc2xhdGVMZW5ndGgpXG5cdFx0XHQpIHtcblx0XHRcdFx0cmV0dXJuIHRoaXMuZGlyZWN0VHJhbnNsYXRlKHRleHQsIGZyb20sIHRvKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHJldHVybiB0aGlzLm1ha2VUYXNrKHsgdGV4dDogdGV4dCwgZnJvbSwgdG8sIGNvbnRleHQsIHByaW9yaXR5IH0pO1xuXHRcdFx0fVxuXHRcdH0gZWxzZSB7XG5cdFx0XHQvLyBTcGxpdCB0ZXh0IGJ5IHdvcmRzIGFuZCB0cmFuc2xhdGVcblx0XHRcdHJldHVybiB0aGlzLnNwbGl0QW5kVHJhbnNsYXRlKHRleHQsIGZyb20sIHRvLCBjb250ZXh0LCBwcmlvcml0eSk7XG5cdFx0fVxuXHR9XG5cblx0cHJpdmF0ZSBhc3luYyBkaXJlY3RUcmFuc2xhdGUodGV4dDogc3RyaW5nLCBmcm9tOiBzdHJpbmcsIHRvOiBzdHJpbmcpIHtcblx0XHRjb25zdCBmcmVlID0gYXdhaXQgdGhpcy5zZW1hZm9yLnRha2UoKTtcblx0XHRyZXR1cm4gdGhpcy50cmFuc2xhdG9yLnRyYW5zbGF0ZSh0ZXh0LCBmcm9tLCB0bykuZmluYWxseShmcmVlKTtcblx0fVxuXG5cdHByaXZhdGUgc3BsaXRBbmRUcmFuc2xhdGUoXG5cdFx0dGV4dDogc3RyaW5nLFxuXHRcdGZyb206IHN0cmluZyxcblx0XHR0bzogc3RyaW5nLFxuXHRcdGNvbnRleHQ6IHN0cmluZyxcblx0XHRwcmlvcml0eTogbnVtYmVyLFxuXHQpIHtcblx0XHRjb25zdCBzcGxpdHRlZFRleHQ6IHN0cmluZ1tdID0gW107XG5cdFx0Y29uc3QgY2hhcnNldEluZGV4ZXM6IG51bWJlcltdID0gW107XG5cblx0XHRsZXQgd29yZHNCdWZmZXIgPSAnJztcblx0XHRmb3IgKGNvbnN0IHRleHRNYXRjaCBvZiB0ZXh0Lm1hdGNoQWxsKC8oW15cXHNdKykoXFxzKikvZykpIHtcblx0XHRcdGNvbnN0IG5ld1BhcnQgPSB0ZXh0TWF0Y2hbMF07XG5cdFx0XHRjb25zdCBuZXdCdWZmZXIgPSB3b3Jkc0J1ZmZlciArIG5ld1BhcnQ7XG5cblx0XHRcdC8vIEFkZCB3b3JkIHRvIGJ1ZmZlciBpZiBjYW5cblx0XHRcdGlmICh0aGlzLnRyYW5zbGF0b3IuY2hlY2tMaW1pdEV4Y2VlZGluZyhuZXdCdWZmZXIpIDw9IDApIHtcblx0XHRcdFx0d29yZHNCdWZmZXIgPSBuZXdCdWZmZXI7XG5cdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBXcml0ZSBhbmQgY2xlYXIgYnVmZmVyIGlmIG5vdCBlbXB0aHlcblx0XHRcdGlmICh3b3Jkc0J1ZmZlci5sZW5ndGggPiAwKSB7XG5cdFx0XHRcdHNwbGl0dGVkVGV4dC5wdXNoKHdvcmRzQnVmZmVyKTtcblx0XHRcdFx0d29yZHNCdWZmZXIgPSAnJztcblx0XHRcdH1cblxuXHRcdFx0Ly8gSGFuZGxlIG5ldyBwYXJ0XG5cdFx0XHRpZiAodGhpcy50cmFuc2xhdG9yLmNoZWNrTGltaXRFeGNlZWRpbmcobmV3UGFydCkgPD0gMCkge1xuXHRcdFx0XHQvLyBBZGQgdG8gYnVmZmVyXG5cdFx0XHRcdHdvcmRzQnVmZmVyICs9IG5ld1BhcnQ7XG5cdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0Ly8gU2xpY2UgYnkgY2hhcnNcblx0XHRcdFx0bGV0IGNoYXJzQnVmZmVyID0gbmV3UGFydDtcblx0XHRcdFx0d2hpbGUgKGNoYXJzQnVmZmVyLmxlbmd0aCA+IDApIHtcblx0XHRcdFx0XHRjb25zdCBleHRyYUNoYXJzID0gdGhpcy50cmFuc2xhdG9yLmNoZWNrTGltaXRFeGNlZWRpbmcoY2hhcnNCdWZmZXIpO1xuXHRcdFx0XHRcdGlmIChleHRyYUNoYXJzID4gMCkge1xuXHRcdFx0XHRcdFx0Y29uc3Qgb2Zmc2V0ID0gY2hhcnNCdWZmZXIubGVuZ3RoIC0gZXh0cmFDaGFycztcblxuXHRcdFx0XHRcdFx0Ly8gV3JpdGUgc2xpY2UgYW5kIHJlbWFpbmRlclxuXHRcdFx0XHRcdFx0c3BsaXR0ZWRUZXh0LnB1c2goY2hhcnNCdWZmZXIuc2xpY2UoMCwgb2Zmc2V0KSk7XG5cdFx0XHRcdFx0XHRjaGFyc0J1ZmZlciA9IGNoYXJzQnVmZmVyLnNsaWNlKG9mZnNldCk7XG5cblx0XHRcdFx0XHRcdGNoYXJzZXRJbmRleGVzLnB1c2goc3BsaXR0ZWRUZXh0Lmxlbmd0aCAtIDEpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGNvbnN0IGN0eFByZWZpeCA9IGNvbnRleHQubGVuZ3RoID4gMCA/IGNvbnRleHQgKyAnOycgOiAnJztcblx0XHRyZXR1cm4gUHJvbWlzZS5hbGwoXG5cdFx0XHRzcGxpdHRlZFRleHQubWFwKCh0ZXh0LCBpbmRleCkgPT5cblx0XHRcdFx0Y2hhcnNldEluZGV4ZXMuaW5jbHVkZXMoaW5kZXgpXG5cdFx0XHRcdFx0PyB0ZXh0XG5cdFx0XHRcdFx0OiB0aGlzLm1ha2VUYXNrKHtcblx0XHRcdFx0XHRcdFx0dGV4dCxcblx0XHRcdFx0XHRcdFx0ZnJvbSxcblx0XHRcdFx0XHRcdFx0dG8sXG5cdFx0XHRcdFx0XHRcdGNvbnRleHQ6IGN0eFByZWZpeCArIGB0ZXh0IyR7dGhpcy5jb250ZXh0Q291bnRlcisrfWAsXG5cdFx0XHRcdFx0XHRcdHByaW9yaXR5LFxuXHRcdFx0XHRcdFx0fSksXG5cdFx0XHQpLFxuXHRcdCkudGhlbigodHJhbnNsYXRlZFBhcnRzKSA9PiB0cmFuc2xhdGVkUGFydHMuam9pbignJykpO1xuXHR9XG5cblx0cHJpdmF0ZSBtYWtlVGFzayh7IHRleHQsIGZyb20sIHRvLCBwcmlvcml0eSwgY29udGV4dCA9ICcnIH06IFRhc2tDb25zdHJ1Y3Rvcikge1xuXHRcdHJldHVybiBuZXcgUHJvbWlzZTxzdHJpbmc+KChyZXNvbHZlLCByZWplY3QpID0+IHtcblx0XHRcdHRoaXMuYWRkVG9UYXNrQ29udGFpbmVyKHtcblx0XHRcdFx0dGV4dCxcblx0XHRcdFx0ZnJvbSxcblx0XHRcdFx0dG8sXG5cdFx0XHRcdGNvbnRleHQsXG5cdFx0XHRcdHByaW9yaXR5LFxuXHRcdFx0XHRyZXNvbHZlLFxuXHRcdFx0XHRyZWplY3QsXG5cdFx0XHR9KTtcblx0XHR9KTtcblx0fVxuXG5cdHByaXZhdGUgcmVhZG9ubHkgdGFza0NvbnRhaW5lcnNTdG9yYWdlID0gbmV3IFNldDxUYXNrQ29udGFpbmVyPigpO1xuXHRwcml2YXRlIGFkZFRvVGFza0NvbnRhaW5lcihwYXJhbXM6IFRhc2tDb25zdHJ1Y3RvckludGVybmFsKSB7XG5cdFx0Y29uc3Qge1xuXHRcdFx0dGV4dCxcblx0XHRcdGZyb20sXG5cdFx0XHR0byxcblx0XHRcdGF0dGVtcHQgPSAwLFxuXHRcdFx0Y29udGV4dCA9ICcnLFxuXHRcdFx0cHJpb3JpdHksXG5cdFx0XHRyZXNvbHZlLFxuXHRcdFx0cmVqZWN0LFxuXHRcdH0gPSBwYXJhbXM7XG5cblx0XHQvLyBjcmVhdGUgdGFza1xuXHRcdGNvbnN0IHRhc2s6IFRhc2sgPSB7XG5cdFx0XHR0ZXh0LFxuXHRcdFx0ZnJvbSxcblx0XHRcdHRvLFxuXHRcdFx0YXR0ZW1wdCxcblx0XHRcdHJlc29sdmUsXG5cdFx0XHRyZWplY3QsXG5cdFx0fTtcblxuXHRcdGxldCBjb250YWluZXI6IFRhc2tDb250YWluZXIgfCBudWxsID0gbnVsbDtcblxuXHRcdC8vIHRyeSBhZGQgdG8gZXhpc3RzIGNvbnRhaW5lclxuXHRcdGZvciAoY29uc3QgdGFza0NvbnRhaW5lciBvZiB0aGlzLnRhc2tDb250YWluZXJzU3RvcmFnZSkge1xuXHRcdFx0Ly8gU2tpcCBjb250YWluZXJzIHdpdGggbm90IGVxdWFsIHBhcmFtZXRlcnNcblx0XHRcdGlmIChcblx0XHRcdFx0KFsnZnJvbScsICd0bycsICdjb250ZXh0JywgJ3ByaW9yaXR5J10gYXMgY29uc3QpLnNvbWUoXG5cdFx0XHRcdFx0KGtleSkgPT4gcGFyYW1zW2tleV0gIT09IHRhc2tDb250YWluZXJba2V5XSxcblx0XHRcdFx0KVxuXHRcdFx0KVxuXHRcdFx0XHRjb250aW51ZTtcblxuXHRcdFx0Ly8gTGlnaHR3ZWlnaHQgY2hlY2sgdG8gb3ZlcmZsb3dcblx0XHRcdC8vIE5PVEU6IERvIHN0cmljdCBjaGVjayBoZXJlIGlmIHlvdSBuZWVkIGNvbXBseSBhIGxpbWl0IGNvbnRyYWN0XG5cdFx0XHRpZiAoXG5cdFx0XHRcdHRoaXMudHJhbnNsYXRvci5nZXRMZW5ndGhMaW1pdCgpID49XG5cdFx0XHRcdHRhc2tDb250YWluZXIubGVuZ3RoICsgdGFzay50ZXh0Lmxlbmd0aFxuXHRcdFx0KSB7XG5cdFx0XHRcdHRhc2tDb250YWluZXIudGFza3MucHVzaCh0YXNrKTtcblx0XHRcdFx0dGFza0NvbnRhaW5lci5sZW5ndGggKz0gdGFzay50ZXh0Lmxlbmd0aDtcblx0XHRcdFx0Y29udGFpbmVyID0gdGFza0NvbnRhaW5lcjtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBtYWtlIGNvbnRhaW5lclxuXHRcdGlmIChjb250YWluZXIgPT09IG51bGwpIHtcblx0XHRcdGNvbnN0IG5ld1Rhc2tDb250YWluZXI6IFRhc2tDb250YWluZXIgPSB7XG5cdFx0XHRcdGNvbnRleHQsXG5cdFx0XHRcdHByaW9yaXR5LFxuXHRcdFx0XHRmcm9tLFxuXHRcdFx0XHR0byxcblx0XHRcdFx0dGFza3M6IFt0YXNrXSxcblx0XHRcdFx0bGVuZ3RoOiB0YXNrLnRleHQubGVuZ3RoLFxuXHRcdFx0fTtcblx0XHRcdHRoaXMudGFza0NvbnRhaW5lcnNTdG9yYWdlLmFkZChuZXdUYXNrQ29udGFpbmVyKTtcblx0XHRcdGNvbnRhaW5lciA9IG5ld1Rhc2tDb250YWluZXI7XG5cdFx0fVxuXG5cdFx0aWYgKFxuXHRcdFx0dGhpcy5jb25maWcuY2h1bmtTaXplRm9ySW5zdGFudFRyYW5zbGF0ZSAhPT0gbnVsbCAmJlxuXHRcdFx0Y29udGFpbmVyLmxlbmd0aCA+PSB0aGlzLmNvbmZpZy5jaHVua1NpemVGb3JJbnN0YW50VHJhbnNsYXRlXG5cdFx0KSB7XG5cdFx0XHR0aGlzLmFkZFRvVHJhbnNsYXRlUXVldWUoY29udGFpbmVyKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy51cGRhdGVEZWxheUZvckFkZFRvVHJhbnNsYXRlUXVldWUoY29udGFpbmVyKTtcblx0XHR9XG5cdH1cblxuXHRwcml2YXRlIHJlYWRvbmx5IHRpbWVyc01hcCA9IG5ldyBNYXA8VGFza0NvbnRhaW5lciwgbnVtYmVyIHwgTm9kZUpTLlRpbWVvdXQ+KCk7XG5cdHByaXZhdGUgdXBkYXRlRGVsYXlGb3JBZGRUb1RyYW5zbGF0ZVF1ZXVlKHRhc2tDb250YWluZXI6IFRhc2tDb250YWluZXIpIHtcblx0XHQvLyBGbHVzaCB0aW1lclxuXHRcdGlmICh0aGlzLnRpbWVyc01hcC5oYXModGFza0NvbnRhaW5lcikpIHtcblx0XHRcdC8vIER1ZSB0byBleHBlY3RhdGlvbiBydW4gb24gb25lIHBsYXRmb3JtLCB0aW1lciBvYmplY3RzIHdpbGwgc2FtZSBhbHdheXNcblx0XHRcdGdsb2JhbFRoaXMuY2xlYXJUaW1lb3V0KHRoaXMudGltZXJzTWFwLmdldCh0YXNrQ29udGFpbmVyKSk7XG5cdFx0fVxuXG5cdFx0dGhpcy50aW1lcnNNYXAuc2V0KFxuXHRcdFx0dGFza0NvbnRhaW5lcixcblx0XHRcdGdsb2JhbFRoaXMuc2V0VGltZW91dCgoKSA9PiB7XG5cdFx0XHRcdHRoaXMuYWRkVG9UcmFuc2xhdGVRdWV1ZSh0YXNrQ29udGFpbmVyKTtcblx0XHRcdH0sIHRoaXMuY29uZmlnLnRyYW5zbGF0ZVBvb2xEZWxheSksXG5cdFx0KTtcblx0fVxuXG5cdC8qKlxuXHQgKiBUYXNrcyBxdWV1ZSB3aXRoIGl0ZW1zIHNvcnRlZCBieSBwcmlvcml0eVxuXHQgKiBJdCBtdXN0IGJlIGhhbmRsZWQgZnJvbSBlbmQgdG8gc3RhcnRcblx0ICovXG5cdHByaXZhdGUgdHJhbnNsYXRlUXVldWU6IFRhc2tDb250YWluZXJbXSA9IFtdO1xuXHRwcml2YXRlIGFkZFRvVHJhbnNsYXRlUXVldWUodGFza0NvbnRhaW5lcjogVGFza0NvbnRhaW5lcikge1xuXHRcdC8vIEZsdXNoIHRpbWVyXG5cdFx0aWYgKHRoaXMudGltZXJzTWFwLmhhcyh0YXNrQ29udGFpbmVyKSkge1xuXHRcdFx0Ly8gRHVlIHRvIGV4cGVjdGF0aW9uIHJ1biBvbiBvbmUgcGxhdGZvcm0sIHRpbWVyIG9iamVjdHMgd2lsbCBzYW1lIGFsd2F5c1xuXHRcdFx0Z2xvYmFsVGhpcy5jbGVhclRpbWVvdXQodGhpcy50aW1lcnNNYXAuZ2V0KHRhc2tDb250YWluZXIpKTtcblx0XHRcdHRoaXMudGltZXJzTWFwLmRlbGV0ZSh0YXNrQ29udGFpbmVyKTtcblx0XHR9XG5cblx0XHR0aGlzLnRhc2tDb250YWluZXJzU3RvcmFnZS5kZWxldGUodGFza0NvbnRhaW5lcik7XG5cblx0XHQvLyBSZXNvcnQgcXVldWUgYnkgcHJpb3JpdHkgZWFjaCB0aW1lIHRvIGtlZXAgY29uc2lzdGVuY3lcblx0XHR0aGlzLnRyYW5zbGF0ZVF1ZXVlID0gdGhpcy50cmFuc2xhdGVRdWV1ZVxuXHRcdFx0LmNvbmNhdCh0YXNrQ29udGFpbmVyKVxuXHRcdFx0LnNvcnQoKGEsIGIpID0+IGEucHJpb3JpdHkgLSBiLnByaW9yaXR5KTtcblxuXHRcdGlmICghdGhpcy53b3JrZXJTdGF0ZSkge1xuXHRcdFx0dGhpcy5ydW5Xb3JrZXIoKS5jYXRjaCgoZXJyb3I6IHVua25vd24pID0+IHtcblx0XHRcdFx0dGhyb3cgZXJyb3I7XG5cdFx0XHR9KTtcblx0XHR9XG5cdH1cblxuXHQvKipcblx0ICogUmV0dXJuIGZpcnN0IGl0ZW0gZnJvbSBxdWV1ZSBhbmQgZGVsZXRlIGl0IGZyb20gcXVldWVcblx0ICogSXRlbXMgaXMgc29ydGVkIGJ5IHByaW9yaXR5XG5cdCAqL1xuXHRwcml2YXRlIHJlYWRvbmx5IGdldEl0ZW1Gcm9tVHJhbnNsYXRlUXVldWUgPSAoKTogSXRlcmF0b3JTdGVwPFRhc2tDb250YWluZXI+ID0+IHtcblx0XHRyZXR1cm4ge1xuXHRcdFx0ZG9uZTogdGhpcy50cmFuc2xhdGVRdWV1ZS5sZW5ndGggPT09IDAsXG5cdFx0XHR2YWx1ZTogdGhpcy50cmFuc2xhdGVRdWV1ZS5wb3AoKSA/PyBudWxsLFxuXHRcdH07XG5cdH07XG5cblx0cHJpdmF0ZSB3b3JrZXJTdGF0ZSA9IGZhbHNlO1xuXHRwcml2YXRlIGFzeW5jIHJ1bldvcmtlcigpIHtcblx0XHR0aGlzLndvcmtlclN0YXRlID0gdHJ1ZTtcblxuXHRcdGxldCBmaXJzdEl0ZXJhdGlvbiA9IHRydWU7XG5cdFx0Ly8gRGFlbW9uIGxvb3Bcblx0XHQvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXVubmVjZXNzYXJ5LWNvbmRpdGlvblxuXHRcdHdoaWxlICh0cnVlKSB7XG5cdFx0XHQvLyBEZWxheSBmaXJzdCBpdGVyYXRpb24gdG8gYXdhaXQgZmlsbCB0aGUgcXVldWUsIHRvIGNvbnNpZGVyIHByaW9yaXR5IGJldHRlclxuXHRcdFx0Y29uc3Qgd29ya2VySGFuZGxlRGVsYXkgPSB0aGlzLmNvbmZpZy50YXNrQmF0Y2hIYW5kbGVEZWxheTtcblx0XHRcdGlmICh3b3JrZXJIYW5kbGVEZWxheSAmJiBmaXJzdEl0ZXJhdGlvbikge1xuXHRcdFx0XHRhd2FpdCBuZXcgUHJvbWlzZSgocmVzKSA9PiBzZXRUaW1lb3V0KHJlcywgd29ya2VySGFuZGxlRGVsYXkpKTtcblx0XHRcdH1cblxuXHRcdFx0Zmlyc3RJdGVyYXRpb24gPSBmYWxzZTtcblxuXHRcdFx0Y29uc3QgaXRlcmF0ZSA9IHRoaXMuZ2V0SXRlbUZyb21UcmFuc2xhdGVRdWV1ZSgpO1xuXG5cdFx0XHQvLyBTa2lwIHdoZW4gcXVldWUgZW1wdHlcblx0XHRcdGlmIChpdGVyYXRlLmRvbmUgfHwgaXRlcmF0ZS52YWx1ZSA9PT0gbnVsbCkgYnJlYWs7XG5cblx0XHRcdGNvbnN0IHRhc2tDb250YWluZXIgPSBpdGVyYXRlLnZhbHVlO1xuXG5cdFx0XHRjb25zdCBmcmVlID0gYXdhaXQgdGhpcy5zZW1hZm9yLnRha2UoKTtcblxuXHRcdFx0Y29uc3QgdGV4dEFycmF5ID0gdGFza0NvbnRhaW5lci50YXNrcy5tYXAoKGkpID0+IGkudGV4dCk7XG5cdFx0XHRhd2FpdCB0aGlzLnRyYW5zbGF0b3Jcblx0XHRcdFx0LnRyYW5zbGF0ZUJhdGNoKHRleHRBcnJheSwgdGFza0NvbnRhaW5lci5mcm9tLCB0YXNrQ29udGFpbmVyLnRvKVxuXHRcdFx0XHQudGhlbigocmVzdWx0KSA9PiB7XG5cdFx0XHRcdFx0Zm9yIChsZXQgaW5kZXggPSAwOyBpbmRleCA8IHRhc2tDb250YWluZXIudGFza3MubGVuZ3RoOyBpbmRleCsrKSB7XG5cdFx0XHRcdFx0XHRjb25zdCB0YXNrID0gdGFza0NvbnRhaW5lci50YXNrc1tpbmRleF07XG5cblx0XHRcdFx0XHRcdGNvbnN0IHRyYW5zbGF0ZWRUZXh0ID0gcmVzdWx0W2luZGV4XTtcblx0XHRcdFx0XHRcdGlmICh0cmFuc2xhdGVkVGV4dCAhPT0gbnVsbCkge1xuXHRcdFx0XHRcdFx0XHR0YXNrLnJlc29sdmUodHJhbnNsYXRlZFRleHQpO1xuXHRcdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdFx0dGhpcy50YXNrRXJyb3JIYW5kbGVyKFxuXHRcdFx0XHRcdFx0XHRcdHRhc2ssXG5cdFx0XHRcdFx0XHRcdFx0bmV3IEVycm9yKFwiVHJhbnNsYXRvciBtb2R1bGUgY2FuJ3QgdHJhbnNsYXRlIHRoaXNcIiksXG5cdFx0XHRcdFx0XHRcdFx0dGFza0NvbnRhaW5lci5jb250ZXh0LFxuXHRcdFx0XHRcdFx0XHRcdHRhc2tDb250YWluZXIucHJpb3JpdHksXG5cdFx0XHRcdFx0XHRcdCk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9KVxuXHRcdFx0XHQuY2F0Y2goKHJlYXNvbjogdW5rbm93bikgPT4ge1xuXHRcdFx0XHRcdGNvbnNvbGUuZXJyb3IocmVhc29uKTtcblxuXHRcdFx0XHRcdGZvciAoY29uc3QgdGFzayBvZiB0YXNrQ29udGFpbmVyLnRhc2tzKSB7XG5cdFx0XHRcdFx0XHR0aGlzLnRhc2tFcnJvckhhbmRsZXIoXG5cdFx0XHRcdFx0XHRcdHRhc2ssXG5cdFx0XHRcdFx0XHRcdHJlYXNvbixcblx0XHRcdFx0XHRcdFx0dGFza0NvbnRhaW5lci5jb250ZXh0LFxuXHRcdFx0XHRcdFx0XHR0YXNrQ29udGFpbmVyLnByaW9yaXR5LFxuXHRcdFx0XHRcdFx0KTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0pXG5cdFx0XHRcdC5maW5hbGx5KGZyZWUpO1xuXHRcdH1cblxuXHRcdHRoaXMud29ya2VyU3RhdGUgPSBmYWxzZTtcblx0fVxuXG5cdHByaXZhdGUgdGFza0Vycm9ySGFuZGxlcihcblx0XHR0YXNrOiBUYXNrLFxuXHRcdGVycm9yOiB1bmtub3duLFxuXHRcdGNvbnRleHQ6IHN0cmluZyxcblx0XHRwcmlvcml0eTogbnVtYmVyLFxuXHQpIHtcblx0XHRpZiAodGhpcy5hYm9ydGVkQ29udGV4dHMuaGFzKGNvbnRleHQpKSB7XG5cdFx0XHR0YXNrLnJlamVjdChlcnJvcik7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0aWYgKHRhc2suYXR0ZW1wdCA+PSB0aGlzLmNvbmZpZy50cmFuc2xhdGVSZXRyeUF0dGVtcHRMaW1pdCkge1xuXHRcdFx0aWYgKHRoaXMuY29uZmlnLmlzQWxsb3dEaXJlY3RUcmFuc2xhdGVCYWRDaHVua3MpIHtcblx0XHRcdFx0Y29uc3QgeyB0ZXh0LCBmcm9tLCB0bywgcmVzb2x2ZSwgcmVqZWN0IH0gPSB0YXNrO1xuXHRcdFx0XHR0aGlzLmRpcmVjdFRyYW5zbGF0ZSh0ZXh0LCBmcm9tLCB0bykudGhlbihyZXNvbHZlLCByZWplY3QpO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0dGFzay5yZWplY3QoZXJyb3IpO1xuXHRcdFx0fVxuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLmFkZFRvVGFza0NvbnRhaW5lcih7XG5cdFx0XHRcdC4uLnRhc2ssXG5cdFx0XHRcdGF0dGVtcHQ6IHRhc2suYXR0ZW1wdCArIDEsXG5cdFx0XHRcdGNvbnRleHQsXG5cdFx0XHRcdHByaW9yaXR5LFxuXHRcdFx0fSk7XG5cdFx0fVxuXHR9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7OztBQUNBLElBQUFBLFVBQUEsR0FBQUMsT0FBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQTRHQTs7Ozs7OztBQU9NLE1BQU9DLFNBQVM7RUFZckJDLFlBQVlDLFVBQXFDLEVBQUVDLE1BQXdCO0lBVDFELEtBQUFBLE1BQU0sR0FBOEI7TUFDcERDLDBCQUEwQixFQUFFLENBQUM7TUFDN0JDLCtCQUErQixFQUFFLElBQUk7TUFDckNDLHFCQUFxQixFQUFFLElBQUk7TUFDM0JDLGtCQUFrQixFQUFFLEdBQUc7TUFDdkJDLDRCQUE0QixFQUFFLElBQUk7TUFDbENDLG9CQUFvQixFQUFFO0tBQ3RCO0lBVWdCLEtBQUFDLGVBQWUsR0FBRyxJQUFJQyxHQUFHLEVBQVU7SUFpQzVDLEtBQUFDLGNBQWMsR0FBRyxDQUFDO0lBbUhULEtBQUFDLHFCQUFxQixHQUFHLElBQUlGLEdBQUcsRUFBaUI7SUF1RWhELEtBQUFHLFNBQVMsR0FBRyxJQUFJQyxHQUFHLEVBQTBDO0lBZ0I5RTs7OztJQUlRLEtBQUFDLGNBQWMsR0FBb0IsRUFBRTtJQXVCNUM7Ozs7SUFJaUIsS0FBQUMseUJBQXlCLEdBQUcsTUFBa0M7O01BQzlFLE9BQU87UUFDTkMsSUFBSSxFQUFFLElBQUksQ0FBQ0YsY0FBYyxDQUFDRyxNQUFNLEtBQUssQ0FBQztRQUN0Q0MsS0FB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O1VBQ0Q7UUFDRCxDQUFDLENBQUMsQ0FDRHFELEtBQUssQ0FBRVksTUFBZSxJQUFJO1VBQzFCQyxPQUFPLENBQUNaLEtBQUssQ0FBQ1csTUFBTSxDQUFDO1VBRXJCLEtBQUssTUFBTTVFLElBQUksSUFBSThDLGFBQWEsQ0FBQ2hELEtBQUssRUFBRTtZQUN2QyxJQUFJLENBQUM2RSxnQkFBZ0IsQ0FDcEIzRSxJQUFJLEVBQ0o0RSxNQUFNLEVBQ045QixhQUFhLENBQUNsRCxPQUFPLEVBQ3JCa0QsYUFBYSxDQUFDbkMsUUFBUSxDQUN0QjtVQUNGO1FBQ0QsQ0FBQyxDQUFDLENBQ0RRLE9BQU8sQ0FBQ0YsSUFBSSxDQUFDO01BQ2hCO01BRUEsSUFBSSxDQUFDN0IsV0FBVyxHQUFHLEtBQUs7SUFDekIsQ0FBQzs7RUFFT3VGLGdCQUFnQkEsQ0FDdkIzRSxJQUFVLEVBQ1ZpRSxLQUFjLEVBQ2RyRSxPQUFlLEVBQ2ZlLFFBQWdCO0lBRWhCLElBQUksSUFBSSxDQUFDcEMsZUFBZSxDQUFDOEUsR0FBRyxDQUFDekQsT0FBTyxDQUFDLEVBQUU7TUFDdENJLElBQUksQ0FBQ0MsTUFBTSxDQUFDZ0UsS0FBSyxDQUFDO01BQ2xCO0lBQ0Q7SUFFQSxJQUFJakUsSUFBSSxDQUFDNEMsT0FBTyxJQUFJLElBQUksQ0FBQzVFLE1BQU0sQ0FBQ0MsMEJBQTBCLEVBQUU7TUFDM0QsSUFBSSxJQUFJLENBQUNELE1BQU0sQ0FBQ0UsK0JBQStCLEVBQUU7UUFDaEQsTUFBTTtVQUFFcUMsSUFBSTtVQUFFQyxJQUFJO1VBQUVDLEVBQUU7VUFBRWdDLE9BQU87VUFBRXhDO1FBQU0sQ0FBRSxHQUFHRCxJQUFJO1FBQ2hELElBQUksQ0FBQ1ksZUFBZSxDQUFDTCxJQUFJLEVBQUVDLElBQUksRUFBRUMsRUFBRSxDQUFDLENBQUM2QixJQUFJLENBQUNHLE9BQU8sRUFBRXhDLE1BQU0sQ0FBQztNQUMzRCxDQUFDLE1BQU07UUFDTkQsSUFBSSxDQUFDQyxNQUFNLENBQUNnRSxLQUFLLENBQUM7TUFDbkI7SUFDRCxDQUFDLE1BQU07TUFDTixJQUFJLENBQUN2QixrQkFBa0IsQ0FBQXJELE1BQUEsQ0FBQUMsTUFBQSxDQUFBRCxNQUFBLENBQUFDLE1BQUEsS0FDbkJVLElBQUk7UUFDUDRDLE9BQU8sRUFBRTVDLElBQUksQ0FBQzRDLE9BQU8sR0FBRyxDQUFDO1FBQ3pCaEQsT0FBTztRQUNQZTtNQUFRLEdBQ1A7SUFDSDtFQUNEOztBQUNBbUUsT0FBQSxDQUFBakgsU0FBQSxHQUFBQSxTQUFBIiwiaWdub3JlTGlzdCI6W119