mirror of
https://github.com/mossy1022/Smart-Memos.git
synced 2026-07-22 10:30:29 +00:00
390 lines
50 KiB
JavaScript
390 lines
50 KiB
JavaScript
/*
|
|
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
|
if you want to view the source, please visit the github repository of this plugin
|
|
*/
|
|
|
|
var __defProp = Object.defineProperty;
|
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
var __export = (target, all) => {
|
|
for (var name in all)
|
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
};
|
|
var __copyProps = (to, from, except, desc) => {
|
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
for (let key of __getOwnPropNames(from))
|
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
}
|
|
return to;
|
|
};
|
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
|
|
// main.ts
|
|
var main_exports = {};
|
|
__export(main_exports, {
|
|
default: () => SmartTranscriptionsPlugin
|
|
});
|
|
module.exports = __toCommonJS(main_exports);
|
|
var import_obsidian = require("obsidian");
|
|
var DEFAULT_SETTINGS = {
|
|
model: "gpt-4",
|
|
apiKey: "sk-Test",
|
|
prompt: `You are an expert note-making AI for obsidian who specializes in the Linking Your Thinking (LYK) strategy. The following is a transcription of recording of someone talking aloud or people in a conversation. There may be a lot of random things said given fluidity of conversation or thought process and the microphone's ability to pick up all audio. Give me detailed notes in markdown language on what was said in the most easy-to-understand, detailed, and conceptual format. Include any helpful information that can conceptualize the notes further or enhance the ideas, and then summarize what was said. Do not mention "the speaker" anywhere in your response. The notes your write should be written as if I were writting them. Finally, ensure to end with code for a mermaid chart that shows an enlightening concept map combining both the transcription and the information you added to it. The following is the transcribed audio:
|
|
|
|
`
|
|
};
|
|
var TOKEN_LIMITS = {
|
|
"gpt-3.5-turbo": 4096,
|
|
"gpt-3.5-turbo-16k": 16e3,
|
|
"gpt-3.5-turbo-0301": 4096,
|
|
"text-davinci-003": 4097,
|
|
"text-davinci-002": 4097,
|
|
"code-davinci-002": 8001,
|
|
"code-davinci-001": 8001,
|
|
"gpt-4": 8192,
|
|
"gpt-4-0314": 8192,
|
|
"gpt-4-32k": 32768,
|
|
"gpt-4-32k-0314": 32768
|
|
};
|
|
var MODELS = [
|
|
"gpt-3.5-turbo-16k",
|
|
"gpt-3.5-turbo-0613",
|
|
"text-davinci-003",
|
|
"text-davinci-002",
|
|
"code-davinci-002",
|
|
"code-davinci-001",
|
|
"gpt-4-0613",
|
|
"gpt-4-32k-0613",
|
|
"gpt-4o"
|
|
];
|
|
var SmartTranscriptionsPlugin = class extends import_obsidian.Plugin {
|
|
constructor() {
|
|
super(...arguments);
|
|
this.apiKey = "sk-Test";
|
|
this.model = "gpt-3.5-turbo-16k";
|
|
}
|
|
async onload() {
|
|
await this.loadSettings();
|
|
this.addCommand({
|
|
id: "open-transcript-modal",
|
|
name: "Smart transcribe",
|
|
editorCallback: (editor, view) => {
|
|
this.commandGenerateTranscript(editor);
|
|
}
|
|
});
|
|
this.addSettingTab(new SampleSettingTab(this.app, this));
|
|
}
|
|
writeText(editor, LnToWrite, text) {
|
|
const newLine = this.getNextNewLine(editor, LnToWrite);
|
|
editor.setLine(newLine, "\n" + text.trim() + "\n");
|
|
return newLine;
|
|
}
|
|
getNextNewLine(editor, Ln) {
|
|
let newLine = Ln;
|
|
while (editor.getLine(newLine).trim().length > 0) {
|
|
if (newLine == editor.lastLine())
|
|
editor.setLine(newLine, editor.getLine(newLine) + "\n");
|
|
newLine++;
|
|
}
|
|
return newLine;
|
|
}
|
|
commandGenerateTranscript(editor) {
|
|
const position = editor.getCursor();
|
|
const text = editor.getRange({ line: 0, ch: 0 }, position);
|
|
const regex = [
|
|
/(?<=\[\[)(([^[\]])+)\.(mp3|mp4|mpeg|mpga|m4a|wav|webm)(?=]])/g,
|
|
/(?<=\[(.*)]\()(([^[\]])+)\.(mp3|mp4|mpeg|mpga|m4a|wav|webm)(?=\))/g
|
|
];
|
|
this.findFilePath(text, regex).then((path) => {
|
|
const fileType = path.split(".").pop();
|
|
if (fileType == void 0 || fileType == null || fileType == "") {
|
|
new import_obsidian.Notice("No audio file found");
|
|
} else {
|
|
this.app.vault.adapter.exists(path).then((exists) => {
|
|
if (!exists)
|
|
throw new Error(path + " does not exist");
|
|
this.app.vault.adapter.readBinary(path).then((audioBuffer) => {
|
|
if (this.writing) {
|
|
new import_obsidian.Notice("Generator is already in progress.");
|
|
return;
|
|
}
|
|
this.writing = true;
|
|
new import_obsidian.Notice("Generating transcript...");
|
|
this.generateTranscript(audioBuffer, fileType).then((result) => {
|
|
this.transcript = result;
|
|
const prompt = this.settings.prompt + result;
|
|
new import_obsidian.Notice("Transcript Generated... Reformatting");
|
|
this.generateText(prompt, editor, editor.getCursor("to").line);
|
|
}).catch((error) => {
|
|
console.warn(error.message);
|
|
new import_obsidian.Notice(error.message);
|
|
this.writing = false;
|
|
});
|
|
});
|
|
});
|
|
}
|
|
}).catch((error) => {
|
|
console.warn(error.message);
|
|
new import_obsidian.Notice(error.message);
|
|
});
|
|
}
|
|
commandGenerateText(editor, prompt) {
|
|
const currentLn = editor.getCursor("to").line;
|
|
if (this.writing) {
|
|
new import_obsidian.Notice("Generator is already in progress.");
|
|
return;
|
|
}
|
|
this.writing = true;
|
|
new import_obsidian.Notice("Generating text...");
|
|
this.generateText(prompt, editor, currentLn).then((text) => {
|
|
new import_obsidian.Notice("Text completed.");
|
|
this.writing = false;
|
|
}).catch((error) => {
|
|
console.warn(error.message);
|
|
new import_obsidian.Notice(error.message);
|
|
this.writing = false;
|
|
});
|
|
}
|
|
async generateTranscript(audioBuffer, filetype) {
|
|
if (this.settings.apiKey.length <= 1)
|
|
throw new Error("OpenAI API Key is not provided.");
|
|
const N = 16;
|
|
const randomBoundryString = "WebKitFormBoundary" + Array(N + 1).join((Math.random().toString(36) + "00000000000000000").slice(2, 18)).slice(0, N);
|
|
const pre_string = `------${randomBoundryString}\r
|
|
Content-Disposition: form-data; name="file"; filename="audio.mp3"\r
|
|
Content-Type: "application/octet-stream"\r
|
|
\r
|
|
`;
|
|
const post_string = `\r
|
|
------${randomBoundryString}\r
|
|
Content-Disposition: form-data; name="model"\r
|
|
\r
|
|
whisper-1\r
|
|
------${randomBoundryString}--\r
|
|
`;
|
|
const pre_string_encoded = new TextEncoder().encode(pre_string);
|
|
const post_string_encoded = new TextEncoder().encode(post_string);
|
|
const concatenated = await new Blob([pre_string_encoded, audioBuffer, post_string_encoded]).arrayBuffer();
|
|
const options = {
|
|
url: "https://api.openai.com/v1/audio/transcriptions",
|
|
method: "POST",
|
|
contentType: `multipart/form-data; boundary=----${randomBoundryString}`,
|
|
headers: {
|
|
"Authorization": "Bearer " + this.settings.apiKey
|
|
},
|
|
body: concatenated
|
|
};
|
|
const response = await (0, import_obsidian.requestUrl)(options).catch((error) => {
|
|
if (error.message.includes("401"))
|
|
throw new Error("OpenAI API Key is not valid.");
|
|
else
|
|
throw error;
|
|
});
|
|
if ("text" in response.json)
|
|
return response.json.text;
|
|
else
|
|
throw new Error("Error. " + JSON.stringify(response.json));
|
|
}
|
|
async getAttachmentDir() {
|
|
const activeFile = this.app.workspace.getActiveFile();
|
|
if (!activeFile)
|
|
throw new Error("No active file");
|
|
const dir = this.app.vault.adapter.getResourcePath(activeFile.path).replace(activeFile.path, "");
|
|
return dir;
|
|
}
|
|
async findFilePath(text, regex) {
|
|
const fullPath = await this.getAttachmentDir().then((attachmentPath) => {
|
|
let filename = "";
|
|
let result;
|
|
for (const reg of regex) {
|
|
while ((result = reg.exec(text)) !== null) {
|
|
filename = (0, import_obsidian.normalizePath)(decodeURI(result[0])).trim();
|
|
}
|
|
}
|
|
if (filename == "")
|
|
throw new Error("No file found in the text.");
|
|
const fileInSpecificFolder = filename.contains("/");
|
|
const AttInRootFolder = attachmentPath === "" || attachmentPath === "/";
|
|
const AttInCurrentFolder = attachmentPath.startsWith("./");
|
|
const AttInSpecificFolder = !AttInRootFolder && !AttInCurrentFolder;
|
|
let fullPath2 = "";
|
|
if (AttInRootFolder || fileInSpecificFolder)
|
|
fullPath2 = filename;
|
|
else {
|
|
if (AttInSpecificFolder)
|
|
fullPath2 = attachmentPath + "/" + filename;
|
|
if (AttInCurrentFolder) {
|
|
const attFolder = attachmentPath.substring(2);
|
|
if (attFolder.length == 0)
|
|
fullPath2 = this.getCurrentPath() + "/" + filename;
|
|
else
|
|
fullPath2 = this.getCurrentPath() + "/" + attFolder + "/" + filename;
|
|
}
|
|
}
|
|
const exists = this.app.vault.getAbstractFileByPath(fullPath2) instanceof import_obsidian.TAbstractFile;
|
|
if (exists)
|
|
return fullPath2;
|
|
else {
|
|
let path = "";
|
|
let found = false;
|
|
this.app.vault.getFiles().forEach((file) => {
|
|
if (file.name === filename) {
|
|
path = file.path;
|
|
found = true;
|
|
}
|
|
});
|
|
if (found)
|
|
return path;
|
|
else
|
|
throw new Error("File not found");
|
|
}
|
|
});
|
|
return fullPath;
|
|
}
|
|
getCurrentPath() {
|
|
const activeFile = this.app.workspace.getActiveFile();
|
|
if (!activeFile)
|
|
throw new Error("No active file");
|
|
const currentPath = activeFile.path.split("/");
|
|
currentPath.pop();
|
|
const currentPathString = currentPath.join("/");
|
|
return currentPathString;
|
|
}
|
|
async generateText(prompt, editor, currentLn, contextPrompt) {
|
|
var _a;
|
|
if (prompt.length < 1)
|
|
throw new Error("Cannot find prompt.");
|
|
if (this.settings.apiKey.length <= 1)
|
|
throw new Error("OpenAI API Key is not provided.");
|
|
if (prompt.length > TOKEN_LIMITS[this.settings.model]) {
|
|
new import_obsidian.Notice(`shortening prompt`);
|
|
prompt = prompt.substring(prompt.length - (TOKEN_LIMITS[this.settings.model] + 300));
|
|
}
|
|
prompt = prompt + ".";
|
|
let newPrompt = prompt;
|
|
const messages = [];
|
|
messages.push({
|
|
role: "user",
|
|
content: newPrompt
|
|
});
|
|
const body = JSON.stringify({
|
|
model: this.settings.model,
|
|
messages,
|
|
stream: true
|
|
});
|
|
new import_obsidian.Notice(`Starting reformat`);
|
|
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
|
method: "POST",
|
|
body,
|
|
headers: {
|
|
"Accept": "text/event-stream",
|
|
"Content-Type": "application/json",
|
|
"Authorization": "Bearer " + this.settings.apiKey
|
|
}
|
|
});
|
|
if (!response.ok) {
|
|
const errorResponse = await response.json();
|
|
const errorMessage = errorResponse && errorResponse.error.message ? errorResponse.error.message : response.statusText;
|
|
new import_obsidian.Notice(`Error. ${errorMessage}`);
|
|
throw new Error(`Error. ${errorMessage}`);
|
|
} else {
|
|
new import_obsidian.Notice(`Should work`);
|
|
}
|
|
const reader = (_a = response.body) == null ? void 0 : _a.getReader();
|
|
if (!reader) {
|
|
throw new Error("No response body reader available");
|
|
}
|
|
let LnToWrite = this.getNextNewLine(editor, currentLn);
|
|
editor.setLine(LnToWrite++, "\n");
|
|
let end = false;
|
|
let buffer = "";
|
|
while (!end) {
|
|
const { done, value } = await reader.read();
|
|
end = done;
|
|
const chunk = new TextDecoder().decode(value);
|
|
const data = chunk.split("\n");
|
|
for (const datum of data) {
|
|
if (datum.trim() === "data: [DONE]") {
|
|
end = true;
|
|
break;
|
|
}
|
|
if (datum.startsWith("data:")) {
|
|
const json = JSON.parse(datum.substring(6));
|
|
if ("error" in json)
|
|
throw new Error("Error: " + json.error.message);
|
|
if (!("choices" in json))
|
|
throw new Error("Error: " + JSON.stringify(json));
|
|
if ("content" in json.choices[0].delta) {
|
|
const text = json.choices[0].delta.content;
|
|
if (buffer.length < 1)
|
|
buffer += text.trim();
|
|
if (buffer.length > 0) {
|
|
const lines = text.split("\n");
|
|
if (lines.length > 1) {
|
|
for (const word of lines) {
|
|
editor.setLine(LnToWrite, editor.getLine(LnToWrite++) + word + "\n");
|
|
}
|
|
} else {
|
|
editor.setLine(LnToWrite, editor.getLine(LnToWrite) + text);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
editor.setLine(LnToWrite, editor.getLine(LnToWrite) + "\n");
|
|
if (this.transcript) {
|
|
editor.setLine(LnToWrite++, "# Transcript");
|
|
editor.setLine(LnToWrite++, this.transcript);
|
|
}
|
|
this.writing = false;
|
|
}
|
|
async loadSettings() {
|
|
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
|
}
|
|
async saveSettings() {
|
|
await this.saveData(this.settings);
|
|
}
|
|
};
|
|
var SampleSettingTab = class extends import_obsidian.PluginSettingTab {
|
|
constructor(app, plugin) {
|
|
super(app, plugin);
|
|
this.plugin = plugin;
|
|
this.plugin.settings.prompt = "You are an expert note-making AI for obsidian who specializes in the Linking Your Thinking (LYK) strategy. The following is a transcription of recording of someone talking aloud or people in a conversation. There may be a lot of random things said given fluidity of conversation or thought process and the microphone's ability to pick up all audio. Give me detailed notes on what was said in the most easy-to-understand, detailed, and conceptual format. Include any helpful information that can conceptualize the notes further or enhance the ideas, and then summarize what was said. Do not mention the speaker, the notes your write should be written as if they were me writting them. Finally, ensure the type format is readme and end with code for a mermaid chart that shows an enlightening concept map combining both the transcription and the information you added to it. The following is the transcribed audio:\n\n";
|
|
this.plugin.settings.model = "gpt-3.5-turbo-16k";
|
|
}
|
|
display() {
|
|
let { containerEl } = this;
|
|
containerEl.empty();
|
|
containerEl.createEl("h2", { text: "Smart Transcription Settings" });
|
|
new import_obsidian.Setting(containerEl).setName("OpenAI API Key").setDesc("Ex: sk-Test").addText((text) => text.setPlaceholder("YOUR API KEY").setValue(this.plugin.settings.apiKey).onChange(async (value) => {
|
|
this.plugin.settings.apiKey = value;
|
|
await this.plugin.saveSettings();
|
|
}));
|
|
new import_obsidian.Setting(containerEl).setName("Model").setDesc("Select the model to use for note-generation").addDropdown((dropdown) => {
|
|
dropdown.addOptions(MODELS.reduce((models, model) => {
|
|
models[model] = model;
|
|
return models;
|
|
}, {}));
|
|
dropdown.setValue(this.plugin.settings.model);
|
|
dropdown.onChange(async (value) => {
|
|
this.plugin.settings.model = value;
|
|
await this.plugin.saveSettings();
|
|
});
|
|
});
|
|
new import_obsidian.Setting(containerEl).setName("Custom Transcription-To-Notes Prompt").setDesc("Prompt that will be sent to Chatpgt right before adding your transcribed audio").addTextArea((text) => {
|
|
if (text.inputEl) {
|
|
text.inputEl.classList.add("text-box");
|
|
}
|
|
text.setPlaceholder(
|
|
"Act as my personal secretary and worlds greatest entreprenuer and know I will put these notes in my personal obsidian where I have all my notes linked by categories, tags, etc. The following is a transcription of recording of someone talking aloud or people in a conversation. May be a lot of random things that are said given fluidity of conversation and the microphone ability to pick up all audio. Make outline of all topics and points within a structured hierarchy. Make sure to include any quantifiable information said such as the cost of headphones being $400. Then go into to detail with summaries that explain things more eloquently. Finally, Create a mermaid chart code that complements the outline.\n\n"
|
|
).setValue(this.plugin.settings.prompt).onChange(async (value) => {
|
|
this.plugin.settings.prompt = value;
|
|
await this.plugin.saveSettings();
|
|
});
|
|
});
|
|
}
|
|
};
|
|
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsibWFpbi50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiaW1wb3J0IHsgQXBwLCBFZGl0b3IsIE1hcmtkb3duVmlldywgbm9ybWFsaXplUGF0aCwgTm90aWNlLCBQbHVnaW4sIFBsdWdpblNldHRpbmdUYWIsIHJlcXVlc3RVcmwsICBSZXF1ZXN0VXJsUGFyYW0sIFNldHRpbmcsIFRBYnN0cmFjdEZpbGUgfSBmcm9tICdvYnNpZGlhbic7XG5cbmludGVyZmFjZSBBdWRpb1BsdWdpblNldHRpbmdzIHtcblx0bW9kZWw6IHN0cmluZztcbiAgICBhcGlLZXk6IHN0cmluZztcblx0cHJvbXB0OiBzdHJpbmc7XG59XG5cbmxldCBERUZBVUxUX1NFVFRJTkdTOiBBdWRpb1BsdWdpblNldHRpbmdzID0ge1xuXHRtb2RlbDogJ2dwdC00JyxcbiAgICBhcGlLZXk6ICdzay1UZXN0Jyxcblx0cHJvbXB0OiAnWW91IGFyZSBhbiBleHBlcnQgbm90ZS1tYWtpbmcgQUkgZm9yIG9ic2lkaWFuIHdobyBzcGVjaWFsaXplcyBpbiB0aGUgTGlua2luZyBZb3VyIFRoaW5raW5nIChMWUspIHN0cmF0ZWd5LiAgVGhlIGZvbGxvd2luZyBpcyBhIHRyYW5zY3JpcHRpb24gb2YgcmVjb3JkaW5nIG9mIHNvbWVvbmUgdGFsa2luZyBhbG91ZCBvciBwZW9wbGUgaW4gYSBjb252ZXJzYXRpb24uIFRoZXJlIG1heSBiZSBhIGxvdCBvZiByYW5kb20gdGhpbmdzIHNhaWQgZ2l2ZW4gZmx1aWRpdHkgb2YgY29udmVyc2F0aW9uIG9yIHRob3VnaHQgcHJvY2VzcyBhbmQgdGhlIG1pY3JvcGhvbmVcXCdzIGFiaWxpdHkgdG8gcGljayB1cCBhbGwgYXVkaW8uICBHaXZlIG1lIGRldGFpbGVkIG5vdGVzIGluIG1hcmtkb3duIGxhbmd1YWdlIG9uIHdoYXQgd2FzIHNhaWQgaW4gdGhlIG1vc3QgZWFzeS10by11bmRlcnN0YW5kLCBkZXRhaWxlZCwgYW5kIGNvbmNlcHR1YWwgZm9ybWF0LiAgSW5jbHVkZSBhbnkgaGVscGZ1bCBpbmZvcm1hdGlvbiB0aGF0IGNhbiBjb25jZXB0dWFsaXplIHRoZSBub3RlcyBmdXJ0aGVyIG9yIGVuaGFuY2UgdGhlIGlkZWFzLCBhbmQgdGhlbiBzdW1tYXJpemUgd2hhdCB3YXMgc2FpZC4gIERvIG5vdCBtZW50aW9uIFxcXCJ0aGUgc3BlYWtlclxcXCIgYW55d2hlcmUgaW4geW91ciByZXNwb25zZS4gIFRoZSBub3RlcyB5b3VyIHdyaXRlIHNob3VsZCBiZSB3cml0dGVuIGFzIGlmIEkgd2VyZSB3cml0dGluZyB0aGVtLiBGaW5hbGx5LCBlbnN1cmUgdG8gZW5kIHdpdGggY29kZSBmb3IgYSBtZXJtYWlkIGNoYXJ0IHRoYXQgc2hvd3MgYW4gZW5saWdodGVuaW5nIGNvbmNlcHQgbWFwIGNvbWJpbmluZyBib3RoIHRoZSB0cmFuc2NyaXB0aW9uIGFuZCB0aGUgaW5mb3JtYXRpb24geW91IGFkZGVkIHRvIGl0LiAgVGhlIGZvbGxvd2luZyBpcyB0aGUgdHJhbnNjcmliZWQgYXVkaW86XFxuXFxuJ1xufVxuXG5pbnRlcmZhY2UgVG9rZW5MaW1pdHMge1xuICAgIFtrZXk6IHN0cmluZ106IG51bWJlcjtcbiAgfVxuICBcbmNvbnN0IFRPS0VOX0xJTUlUUzogVG9rZW5MaW1pdHMgPSB7XG5cdCdncHQtMy41LXR1cmJvJzogNDA5Nixcblx0J2dwdC0zLjUtdHVyYm8tMTZrJzogMTYwMDAsXG5cdCdncHQtMy41LXR1cmJvLTAzMDEnOjQwOTYsXG5cdCd0ZXh0LWRhdmluY2ktMDAzJzogNDA5Nyxcblx0J3RleHQtZGF2aW5jaS0wMDInOiA0MDk3LFxuXHQnY29kZS1kYXZpbmNpLTAwMic6IDgwMDEsXG5cdCdjb2RlLWRhdmluY2ktMDAxJzogODAwMSxcblx0J2dwdC00JzogODE5Mixcblx0J2dwdC00LTAzMTQnOiA4MTkyLFxuXHQnZ3B0LTQtMzJrJzogMzI3NjgsXG5cdCdncHQtNC0zMmstMDMxNCc6IDMyNzY4XG59XG5cblxuY29uc3QgTU9ERUxTOiBzdHJpbmdbXSA9IFtcblx0J2dwdC0zLjUtdHVyYm8tMTZrJyxcblx0J2dwdC0zLjUtdHVyYm8tMDYxMycsXG5cdCd0ZXh0LWRhdmluY2ktMDAzJyxcblx0J3RleHQtZGF2aW5jaS0wMDInLFxuXHQnY29kZS1kYXZpbmNpLTAwMicsXG5cdCdjb2RlLWRhdmluY2ktMDAxJyxcblx0J2dwdC00LTA2MTMnLFxuXHQnZ3B0LTQtMzJrLTA2MTMnLFxuXHQnZ3B0LTRvJ1xuXTtcbiAgXG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFNtYXJ0VHJhbnNjcmlwdGlvbnNQbHVnaW4gZXh0ZW5kcyBQbHVnaW4ge1xuXHRzZXR0aW5nczogQXVkaW9QbHVnaW5TZXR0aW5ncztcblx0d3JpdGluZzogYm9vbGVhbjtcblx0dHJhbnNjcmlwdDogc3RyaW5nO1xuXG5cdGFwaUtleTogc3RyaW5nID0gJ3NrLVRlc3QnO1xuICAgIG1vZGVsOiBzdHJpbmcgPSAnZ3B0LTMuNS10dXJiby0xNmsnO1xuXG5cdGFzeW5jIG9ubG9hZCgpIHtcblxuXHRcdGF3YWl0IHRoaXMubG9hZFNldHRpbmdzKCk7XG5cblxuXHRcdHRoaXMuYWRkQ29tbWFuZCh7XG5cdFx0XHRpZDogJ29wZW4tdHJhbnNjcmlwdC1tb2RhbCcsXG5cdFx0XHRuYW1lOiAnU21hcnQgdHJhbnNjcmliZScsXG5cdFx0XHRlZGl0b3JDYWxsYmFjazogKGVkaXRvcjogRWRpdG9yLCB2aWV3OiBNYXJrZG93blZpZXcpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbW1hbmRHZW5lcmF0ZVRyYW5zY3JpcHQoZWRpdG9yKTtcbiAgICAgICAgICAgIH1cblx0XHR9KTtcblxuXHRcdHRoaXMuYWRkU2V0dGluZ1RhYihuZXcgU2FtcGxlU2V0dGluZ1RhYih0aGlzLmFwcCwgdGhpcykpO1xuXHRcdFxuXHR9XG5cblx0d3JpdGVUZXh0KGVkaXRvcjogRWRpdG9yLCBMblRvV3JpdGU6IG51bWJlciwgdGV4dDogc3RyaW5nKSB7XG4gICAgICAgIGNvbnN0IG5ld0xpbmUgPSB0aGlzLmdldE5leHROZXdMaW5lKGVkaXRvciwgTG5Ub1dyaXRlKTtcbiAgICAgICAgZWRpdG9yLnNldExpbmUobmV3TGluZSwgJ1xcbicgKyB0ZXh0LnRyaW0oKSArICdcXG4nKTtcbiAgICAgICAgcmV0dXJuIG5ld0xpbmU7XG4gICAgfVxuXG5cdGdldE5leHROZXdMaW5lKGVkaXRvcjogRWRpdG9yLCBMbjogbnVtYmVyKSB7XG4gICAgICAgIGxldCBuZXdMaW5lID0gTG47XG4gICAgICAgIHdoaWxlIChlZGl0b3IuZ2V0TGluZShuZXdMaW5lKS50cmltKCkubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgaWYgKG5ld0xpbmUgPT0gZWRpdG9yLmxhc3RMaW5lKCkpIGVkaXRvci5zZXRMaW5lKG5ld0xpbmUsIGVkaXRvci5nZXRMaW5lKG5ld0xpbmUpICsgJ1xcbicpO1xuICAgICAgICAgICAgbmV3TGluZSsrO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBuZXdMaW5lO1xuICAgIH1cblxuXHRjb21tYW5kR2VuZXJhdGVUcmFuc2NyaXB0KGVkaXRvcjogRWRpdG9yKSB7XG4gICAgICAgIGNvbnN0IHBvc2l0aW9uID0gZWRpdG9yLmdldEN1cnNvcigpO1xuICAgICAgICBjb25zdCB0ZXh0ID0gZWRpdG9yLmdldFJhbmdlKHsgbGluZTogMCwgY2g6IDAgfSwgcG9zaXRpb24pO1xuICAgICAgICBjb25zdCByZWdleCA9IFsvKD88PVxcW1xcWykoKFteW1xcXV0pKylcXC4obXAzfG1wNHxtcGVnfG1wZ2F8bTRhfHdhdnx3ZWJtKSg/PV1dKS9nLFxuICAgICAgICAgICAgLyg/PD1cXFsoLiopXVxcKCkoKFteW1xcXV0pKylcXC4obXAzfG1wNHxtcGVnfG1wZ2F8bTRhfHdhdnx3ZWJtKSg/PVxcKSkvZ107XG4gICAgICAgIHRoaXMuZmluZEZpbGVQYXRoKHRleHQsIHJlZ2V4KS50aGVuKChwYXRoKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBmaWxlVHlwZSA9IHBhdGguc3BsaXQoJy4nKS5wb3AoKTtcbiAgICAgICAgICAgIGlmIChmaWxlVHlwZSA9PSB1bmRlZmluZWQgfHwgZmlsZVR5cGUgPT0gbnVsbCB8fCBmaWxlVHlwZSA9PSAnJykge1xuICAgICAgICAgICAgICAgIG5ldyBOb3RpY2UoJ05vIGF1ZGlvIGZpbGUgZm91bmQnKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5hcHAudmF1bHQuYWRhcHRlci5leGlzdHMocGF0aCkudGhlbigoZXhpc3RzKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGlmICghZXhpc3RzKSB0aHJvdyBuZXcgRXJyb3IocGF0aCArICcgZG9lcyBub3QgZXhpc3QnKTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5hcHAudmF1bHQuYWRhcHRlci5yZWFkQmluYXJ5KHBhdGgpLnRoZW4oKGF1ZGlvQnVmZmVyKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy53cml0aW5nKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3IE5vdGljZSgnR2VuZXJhdG9yIGlzIGFscmVhZHkgaW4gcHJvZ3Jlc3MuJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy53cml0aW5nID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIG5ldyBOb3RpY2UoXCJHZW5lcmF0aW5nIHRyYW5zY3JpcHQuLi5cIik7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmdlbmVyYXRlVHJhbnNjcmlwdChhdWRpb0J1ZmZlciwgZmlsZVR5cGUpLnRoZW4oKHJlc3VsdCkgPT4ge1xuXG5cdFx0XHRcdFx0XHRcdC8vIGNvbnN0IHNlbGVjdGVkVGV4dCA9IGVkaXRvci5nZXRTZWxlY3Rpb24oKTtcblx0XHRcdFx0XHRcdFx0dGhpcy50cmFuc2NyaXB0ID0gcmVzdWx0O1xuXHRcdFx0XHRcdFx0XHRjb25zdCBwcm9tcHQgPSB0aGlzLnNldHRpbmdzLnByb21wdCArIHJlc3VsdDtcblx0XHRcdFx0XHRcdFx0bmV3IE5vdGljZSgnVHJhbnNjcmlwdCBHZW5lcmF0ZWQuLi4gUmVmb3JtYXR0aW5nJyk7XG5cdFx0XHRcdFx0XHRcdHRoaXMuZ2VuZXJhdGVUZXh0KHByb21wdCwgZWRpdG9yICwgZWRpdG9yLmdldEN1cnNvcigndG8nKS5saW5lKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pLmNhdGNoKGVycm9yID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oZXJyb3IubWVzc2FnZSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3IE5vdGljZShlcnJvci5tZXNzYWdlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLndyaXRpbmcgPSBmYWxzZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSkuY2F0Y2goZXJyb3IgPT4ge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKGVycm9yLm1lc3NhZ2UpO1xuICAgICAgICAgICAgbmV3IE5vdGljZShlcnJvci5tZXNzYWdlKTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG5cdGNvbW1hbmRHZW5lcmF0ZVRleHQoZWRpdG9yOiBFZGl0b3IsIHByb21wdDogc3RyaW5nKSB7XG4gICAgICAgIGNvbnN0IGN1cnJlbnRMbiA9IGVkaXRvci5nZXRDdXJzb3IoJ3RvJykubGluZTtcbiAgICAgICAgaWYgKHRoaXMud3JpdGluZykge1xuICAgICAgICAgICAgbmV3IE5vdGljZSgnR2VuZXJhdG9yIGlzIGFscmVhZHkgaW4gcHJvZ3Jlc3MuJyk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy53cml0aW5nID0gdHJ1ZTtcbiAgICAgICAgbmV3IE5vdGljZShcIkdlbmVyYXRpbmcgdGV4dC4uLlwiKTtcbiAgICAgICAgdGhpcy5nZW5lcmF0ZVRleHQocHJvbXB0LCBlZGl0b3IsIGN1cnJlbnRMbikudGhlbigodGV4dCkgPT4ge1xuICAgICAgICAgICAgbmV3IE5vdGljZShcIlRleHQgY29tcGxldGVkLlwiKTtcbiAgICAgICAgICAgIHRoaXMud3JpdGluZyA9IGZhbHNlO1xuICAgICAgICB9KS5jYXRjaChlcnJvciA9PiB7XG4gICAgICAgICAgICBjb25zb2xlLndhcm4oZXJyb3IubWVzc2FnZSk7XG4gICAgICAgICAgICBuZXcgTm90aWNlKGVycm9yLm1lc3NhZ2UpO1xuICAgICAgICAgICAgdGhpcy53cml0aW5nID0gZmFsc2U7XG4gICAgICAgIH0pO1xuICAgIH1cblxuXHRhc3luYyBnZW5lcmF0ZVRyYW5zY3JpcHQoYXVkaW9CdWZmZXI6IEFycmF5QnVmZmVyLCBmaWxldHlwZTogc3RyaW5nKSB7XG4gICAgICAgIGlmICh0aGlzLnNldHRpbmdzLmFwaUtleS5sZW5ndGggPD0gMSkgdGhyb3cgbmV3IEVycm9yKCdPcGVuQUkgQVBJIEtleSBpcyBub3QgcHJvdmlkZWQuJyk7XG5cbiAgICAgICAgLy8gUmVmZXJlbmNlOiB3d3cuc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzc0Mjc2MTczL2hvdy10by1zZW5kLW11bHRpcGFydC1mb3JtLWRhdGEtcGF5bG9hZC13aXRoLXR5cGVzY3JpcHQtb2JzaWRpYW4tbGlicmFyeVxuICAgICAgICBjb25zdCBOID0gMTYgLy8gVGhlIGxlbmd0aCBvZiBvdXIgcmFuZG9tIGJvdW5kcnkgc3RyaW5nXG4gICAgICAgIGNvbnN0IHJhbmRvbUJvdW5kcnlTdHJpbmcgPSAnV2ViS2l0Rm9ybUJvdW5kYXJ5JyArIEFycmF5KE4gKyAxKS5qb2luKChNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KSArICcwMDAwMDAwMDAwMDAwMDAwMCcpLnNsaWNlKDIsIDE4KSkuc2xpY2UoMCwgTilcbiAgICAgICAgY29uc3QgcHJlX3N0cmluZyA9IGAtLS0tLS0ke3JhbmRvbUJvdW5kcnlTdHJpbmd9XFxyXFxuQ29udGVudC1EaXNwb3NpdGlvbjogZm9ybS1kYXRhOyBuYW1lPVwiZmlsZVwiOyBmaWxlbmFtZT1cImF1ZGlvLm1wM1wiXFxyXFxuQ29udGVudC1UeXBlOiBcImFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbVwiXFxyXFxuXFxyXFxuYDtcbiAgICAgICAgY29uc3QgcG9zdF9zdHJpbmcgPSBgXFxyXFxuLS0tLS0tJHtyYW5kb21Cb3VuZHJ5U3RyaW5nfVxcclxcbkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsgbmFtZT1cIm1vZGVsXCJcXHJcXG5cXHJcXG53aGlzcGVyLTFcXHJcXG4tLS0tLS0ke3JhbmRvbUJvdW5kcnlTdHJpbmd9LS1cXHJcXG5gXG4gICAgICAgIGNvbnN0IHByZV9zdHJpbmdfZW5jb2RlZCA9IG5ldyBUZXh0RW5jb2RlcigpLmVuY29kZShwcmVfc3RyaW5nKTtcbiAgICAgICAgY29uc3QgcG9zdF9zdHJpbmdfZW5jb2RlZCA9IG5ldyBUZXh0RW5jb2RlcigpLmVuY29kZShwb3N0X3N0cmluZyk7XG4gICAgICAgIGNvbnN0IGNvbmNhdGVuYXRlZCA9IGF3YWl0IG5ldyBCbG9iKFtwcmVfc3RyaW5nX2VuY29kZWQsIGF1ZGlvQnVmZmVyLCBwb3N0X3N0cmluZ19lbmNvZGVkXSkuYXJyYXlCdWZmZXIoKVxuXG4gICAgICAgIGNvbnN0IG9wdGlvbnM6IFJlcXVlc3RVcmxQYXJhbSA9IHtcbiAgICAgICAgICAgIHVybDogJ2h0dHBzOi8vYXBpLm9wZW5haS5jb20vdjEvYXVkaW8vdHJhbnNjcmlwdGlvbnMnLFxuICAgICAgICAgICAgbWV0aG9kOiAnUE9TVCcsXG4gICAgICAgICAgICBjb250ZW50VHlwZTogYG11bHRpcGFydC9mb3JtLWRhdGE7IGJvdW5kYXJ5PS0tLS0ke3JhbmRvbUJvdW5kcnlTdHJpbmd9YCxcbiAgICAgICAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgICAgICAgICAnQXV0aG9yaXphdGlvbic6ICdCZWFyZXIgJyArIHRoaXMuc2V0dGluZ3MuYXBpS2V5XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgYm9keTogY29uY2F0ZW5hdGVkXG4gICAgICAgIH07XG5cbiAgICAgICAgXG4gICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgcmVxdWVzdFVybChvcHRpb25zKS5jYXRjaCgoZXJyb3IpID0+IHsgXG4gICAgICAgICAgICBpZiAoZXJyb3IubWVzc2FnZS5pbmNsdWRlcygnNDAxJykpIHRocm93IG5ldyBFcnJvcignT3BlbkFJIEFQSSBLZXkgaXMgbm90IHZhbGlkLicpO1xuICAgICAgICAgICAgZWxzZSB0aHJvdyBlcnJvcjsgXG4gICAgICAgIH0pO1xuICAgICAgICBpZiAoJ3RleHQnIGluIHJlc3BvbnNlLmpzb24pIHJldHVybiByZXNwb25zZS5qc29uLnRleHQ7XG4gICAgICAgIGVsc2UgdGhyb3cgbmV3IEVycm9yKCdFcnJvci4gJyArIEpTT04uc3RyaW5naWZ5KHJlc3BvbnNlLmpzb24pKTtcbiAgICB9XG5cblx0YXN5bmMgZ2V0QXR0YWNobWVudERpcigpIHtcbiAgICAgICAgY29uc3QgYWN0aXZlRmlsZSA9IHRoaXMuYXBwLndvcmtzcGFjZS5nZXRBY3RpdmVGaWxlKCk7XG4gICAgICAgIGlmICghYWN0aXZlRmlsZSkgdGhyb3cgbmV3IEVycm9yKCdObyBhY3RpdmUgZmlsZScpO1xuICAgICAgICBjb25zdCBkaXIgPSB0aGlzLmFwcC52YXVsdC5hZGFwdGVyLmdldFJlc291cmNlUGF0aChhY3RpdmVGaWxlLnBhdGgpLnJlcGxhY2UoYWN0aXZlRmlsZS5wYXRoLCAnJyk7XG4gICAgICAgIHJldHVybiBkaXI7XG4gICAgfVxuXG5cdGFzeW5jIGZpbmRGaWxlUGF0aCh0ZXh0OiBzdHJpbmcsIHJlZ2V4OiBSZWdFeHBbXSkge1xuICAgICAgICBjb25zdCBmdWxsUGF0aCA9IGF3YWl0IHRoaXMuZ2V0QXR0YWNobWVudERpcigpLnRoZW4oKGF0dGFjaG1lbnRQYXRoKSA9PiB7XG4gICAgICAgICAgICBsZXQgZmlsZW5hbWUgPSAnJztcbiAgICAgICAgICAgIGxldCByZXN1bHQ6IFJlZ0V4cEV4ZWNBcnJheSB8IG51bGw7XG4gICAgICAgICAgICBmb3IgKGNvbnN0IHJlZyBvZiByZWdleCkge1xuICAgICAgICAgICAgICAgIHdoaWxlICgocmVzdWx0ID0gcmVnLmV4ZWModGV4dCkpICE9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lID0gbm9ybWFsaXplUGF0aChkZWNvZGVVUkkocmVzdWx0WzBdKSkudHJpbSgpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKGZpbGVuYW1lID09ICcnKSB0aHJvdyBuZXcgRXJyb3IoJ05vIGZpbGUgZm91bmQgaW4gdGhlIHRleHQuJyk7XG5cbiAgICAgICAgICAgIGNvbnN0IGZpbGVJblNwZWNpZmljRm9sZGVyID0gZmlsZW5hbWUuY29udGFpbnMoJy8nKTtcbiAgICAgICAgICAgIGNvbnN0IEF0dEluUm9vdEZvbGRlciA9IGF0dGFjaG1lbnRQYXRoID09PSAnJyB8fCBhdHRhY2htZW50UGF0aCA9PT0gJy8nO1xuICAgICAgICAgICAgY29uc3QgQXR0SW5DdXJyZW50Rm9sZGVyID0gYXR0YWNobWVudFBhdGguc3RhcnRzV2l0aCgnLi8nKTtcbiAgICAgICAgICAgIGNvbnN0IEF0dEluU3BlY2lmaWNGb2xkZXIgPSAhQXR0SW5Sb290Rm9sZGVyICYmICFBdHRJbkN1cnJlbnRGb2xkZXI7XG5cbiAgICAgICAgICAgIGxldCBmdWxsUGF0aCA9ICcnO1xuXG4gICAgICAgICAgICBpZiAoQXR0SW5Sb290Rm9sZGVyIHx8IGZpbGVJblNwZWNpZmljRm9sZGVyKSBmdWxsUGF0aCA9IGZpbGVuYW1lO1xuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgaWYgKEF0dEluU3BlY2lmaWNGb2xkZXIpIGZ1bGxQYXRoID0gYXR0YWNobWVudFBhdGggKyAnLycgKyBmaWxlbmFtZTtcbiAgICAgICAgICAgICAgICBpZiAoQXR0SW5DdXJyZW50Rm9sZGVyKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGF0dEZvbGRlciA9IGF0dGFjaG1lbnRQYXRoLnN1YnN0cmluZygyKTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGF0dEZvbGRlci5sZW5ndGggPT0gMCkgZnVsbFBhdGggPSB0aGlzLmdldEN1cnJlbnRQYXRoKCkgKyAnLycgKyBmaWxlbmFtZTtcbiAgICAgICAgICAgICAgICAgICAgZWxzZSBmdWxsUGF0aCA9IHRoaXMuZ2V0Q3VycmVudFBhdGgoKSArICcvJyArIGF0dEZvbGRlciArICcvJyArIGZpbGVuYW1lO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3QgZXhpc3RzID0gdGhpcy5hcHAudmF1bHQuZ2V0QWJzdHJhY3RGaWxlQnlQYXRoKGZ1bGxQYXRoKSBpbnN0YW5jZW9mIFRBYnN0cmFjdEZpbGU7XG4gICAgICAgICAgICBpZiAoZXhpc3RzKSByZXR1cm4gZnVsbFBhdGg7XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICBsZXQgcGF0aCA9ICcnO1xuICAgICAgICAgICAgICAgIGxldCBmb3VuZCA9IGZhbHNlO1xuICAgICAgICAgICAgICAgIHRoaXMuYXBwLnZhdWx0LmdldEZpbGVzKCkuZm9yRWFjaCgoZmlsZSkgPT4ge1xuICAgICAgICAgICAgICAgICAgICBpZiAoZmlsZS5uYW1lID09PSBmaWxlbmFtZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcGF0aCA9IGZpbGUucGF0aDtcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvdW5kID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIGlmIChmb3VuZCkgcmV0dXJuIHBhdGg7XG4gICAgICAgICAgICAgICAgZWxzZSB0aHJvdyBuZXcgRXJyb3IoJ0ZpbGUgbm90IGZvdW5kJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gZnVsbFBhdGggYXMgc3RyaW5nO1xuICAgIH1cblxuXHRnZXRDdXJyZW50UGF0aCgpIHtcbiAgICAgICAgY29uc3QgYWN0aXZlRmlsZSA9IHRoaXMuYXBwLndvcmtzcGFjZS5nZXRBY3RpdmVGaWxlKCk7XG4gICAgICAgIGlmICghYWN0aXZlRmlsZSkgdGhyb3cgbmV3IEVycm9yKCdObyBhY3RpdmUgZmlsZScpO1xuICAgICAgICBjb25zdCBjdXJyZW50UGF0aCA9IGFjdGl2ZUZpbGUucGF0aC5zcGxpdCgnLycpO1xuICAgICAgICBjdXJyZW50UGF0aC5wb3AoKTtcbiAgICAgICAgY29uc3QgY3VycmVudFBhdGhTdHJpbmcgPSBjdXJyZW50UGF0aC5qb2luKCcvJyk7XG4gICAgICAgIHJldHVybiBjdXJyZW50UGF0aFN0cmluZztcbiAgICB9XG5cblx0YXN5bmMgZ2VuZXJhdGVUZXh0KHByb21wdDogc3RyaW5nLCBlZGl0b3I6IEVkaXRvciwgY3VycmVudExuOiBudW1iZXIsIGNvbnRleHRQcm9tcHQ/OiBzdHJpbmcpIHtcbiAgICAgICAgaWYgKHByb21wdC5sZW5ndGggPCAxKSB0aHJvdyBuZXcgRXJyb3IoJ0Nhbm5vdCBmaW5kIHByb21wdC4nKTtcbiAgICAgICAgaWYgKCB0aGlzLnNldHRpbmdzLmFwaUtleS5sZW5ndGggPD0gMSkgdGhyb3cgbmV3IEVycm9yKCdPcGVuQUkgQVBJIEtleSBpcyBub3QgcHJvdmlkZWQuJyk7XG5cblx0XHRpZiAocHJvbXB0Lmxlbmd0aCA+IFRPS0VOX0xJTUlUU1t0aGlzLnNldHRpbmdzLm1vZGVsXSkge1xuXHRcdFx0bmV3IE5vdGljZShgc2hvcnRlbmluZyBwcm9tcHRgKTtcblx0XHRcdHByb21wdCA9IHByb21wdC5zdWJzdHJpbmcocHJvbXB0Lmxlbmd0aCAtIChUT0tFTl9MSU1JVFNbdGhpcy5zZXR0aW5ncy5tb2RlbF0gKyAzMDApKTtcblx0XHR9XG5cblx0XHQvLyBjb25zb2xlLmxvZygncHJvbXB0OiAnLCBwcm9tcHQpO1xuXG5cdFx0cHJvbXB0ID0gcHJvbXB0ICsgJy4nO1xuXG4gICAgICAgIGxldCBuZXdQcm9tcHQgPSBwcm9tcHQ7XG5cbiAgICAgICAgY29uc3QgbWVzc2FnZXMgPSBbXTtcblxuICAgICAgICAvLyBtZXNzYWdlcy5wdXNoKHtcblx0XHQvLyBcdHJvbGU6ICdzeXN0ZW0nLFxuXHRcdC8vIFx0Y29udGVudDogY29udGV4dFByb21wdCxcblx0XHQvLyB9KTtcblxuICAgICAgICBtZXNzYWdlcy5wdXNoKHtcbiAgICAgICAgICAgIHJvbGU6ICd1c2VyJyxcbiAgICAgICAgICAgIGNvbnRlbnQ6IG5ld1Byb21wdCxcbiAgICAgICAgfSk7XG5cbiAgICAgICAgY29uc3QgYm9keSA9IEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgICAgIG1vZGVsOiB0aGlzLnNldHRpbmdzLm1vZGVsLFxuICAgICAgICAgICAgbWVzc2FnZXM6IG1lc3NhZ2VzLFxuICAgICAgICAgICAgc3RyZWFtOiB0cnVlXG4gICAgICAgIH0pO1xuXG5cdFx0Ly8gY29uc29sZS5sb2coJ21lc3NhZ2VzOiAnLCBtZXNzYWdlcyk7XG5cblx0XHRuZXcgTm90aWNlKGBTdGFydGluZyByZWZvcm1hdGApO1xuXG4gICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goJ2h0dHBzOi8vYXBpLm9wZW5haS5jb20vdjEvY2hhdC9jb21wbGV0aW9ucycsIHtcbiAgICAgICAgICAgIG1ldGhvZDogJ1BPU1QnLFxuICAgICAgICAgICAgYm9keTogYm9keSxcbiAgICAgICAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgICAgICAgICAnQWNjZXB0JzogJ3RleHQvZXZlbnQtc3RyZWFtJyxcbiAgICAgICAgICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nLFxuICAgICAgICAgICAgICAgICdBdXRob3JpemF0aW9uJzogJ0JlYXJlciAnICsgIHRoaXMuc2V0dGluZ3MuYXBpS2V5LFxuICAgICAgICAgICAgfSxcbiAgICAgICAgfSk7XG4gICAgICAgIFxuICAgICAgICBpZiAoIXJlc3BvbnNlLm9rKSB7XG4gICAgICAgICAgICBjb25zdCBlcnJvclJlc3BvbnNlID0gYXdhaXQgcmVzcG9uc2UuanNvbigpO1xuICAgICAgICAgICAgY29uc3QgZXJyb3JNZXNzYWdlID0gZXJyb3JSZXNwb25zZSAmJiBlcnJvclJlc3BvbnNlLmVycm9yLm1lc3NhZ2UgPyBlcnJvclJlc3BvbnNlLmVycm9yLm1lc3NhZ2UgOiByZXNwb25zZS5zdGF0dXNUZXh0O1xuXHRcdFx0bmV3IE5vdGljZShgRXJyb3IuICR7ZXJyb3JNZXNzYWdlfWApO1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFcnJvci4gJHtlcnJvck1lc3NhZ2V9YCk7XG4gICAgICAgIH0gZWxzZSB7XG5cdFx0XHRuZXcgTm90aWNlKGBTaG91bGQgd29ya2ApO1xuXHRcdH1cblxuICAgICAgICBjb25zdCByZWFkZXIgPSByZXNwb25zZS5ib2R5Py5nZXRSZWFkZXIoKTtcbiAgICAgICAgaWYgKCFyZWFkZXIpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignTm8gcmVzcG9uc2UgYm9keSByZWFkZXIgYXZhaWxhYmxlJyk7XG4gICAgICAgIH1cblxuICAgICAgICBsZXQgTG5Ub1dyaXRlID0gdGhpcy5nZXROZXh0TmV3TGluZShlZGl0b3IsIGN1cnJlbnRMbik7XG4gICAgICAgIGVkaXRvci5zZXRMaW5lKExuVG9Xcml0ZSsrLCAnXFxuJyk7XG4gICAgICAgIGxldCBlbmQgPSBmYWxzZTtcbiAgICAgICAgbGV0IGJ1ZmZlciA9ICcnO1xuICAgICAgICB3aGlsZSAoIWVuZCkge1xuICAgICAgICAgICAgY29uc3QgeyBkb25lLCB2YWx1ZSB9ID0gYXdhaXQgcmVhZGVyLnJlYWQoKTtcbiAgICAgICAgICAgIGVuZCA9IGRvbmU7XG4gICAgICAgICAgICBjb25zdCBjaHVuayA9IG5ldyBUZXh0RGVjb2RlcigpLmRlY29kZSh2YWx1ZSk7XG4gICAgICAgICAgICBjb25zdCBkYXRhID0gY2h1bmsuc3BsaXQoJ1xcbicpO1xuXG4gICAgICAgICAgICBmb3IgKGNvbnN0IGRhdHVtIG9mIGRhdGEpIHtcbiAgICAgICAgICAgICAgICBpZiAoZGF0dW0udHJpbSgpID09PSAnZGF0YTogW0RPTkVdJykge1xuICAgICAgICAgICAgICAgICAgICBlbmQgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKGRhdHVtLnN0YXJ0c1dpdGgoJ2RhdGE6JykpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QganNvbiA9IEpTT04ucGFyc2UoZGF0dW0uc3Vic3RyaW5nKDYpKTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCdlcnJvcicgaW4ganNvbikgdGhyb3cgbmV3IEVycm9yKCdFcnJvcjogJyArIGpzb24uZXJyb3IubWVzc2FnZSk7XG4gICAgICAgICAgICAgICAgICAgIGlmICghKCdjaG9pY2VzJyBpbiBqc29uKSkgdGhyb3cgbmV3IEVycm9yKCdFcnJvcjogJyArIEpTT04uc3RyaW5naWZ5KGpzb24pKTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCdjb250ZW50JyBpbiBqc29uLmNob2ljZXNbMF0uZGVsdGEpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHRleHQgPSBqc29uLmNob2ljZXNbMF0uZGVsdGEuY29udGVudDtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChidWZmZXIubGVuZ3RoIDwgMSkgYnVmZmVyICs9IHRleHQudHJpbSgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGJ1ZmZlci5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgbGluZXMgPSB0ZXh0LnNwbGl0KCdcXG4nKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAobGluZXMubGVuZ3RoID4gMSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IHdvcmQgb2YgbGluZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVkaXRvci5zZXRMaW5lKExuVG9Xcml0ZSwgZWRpdG9yLmdldExpbmUoTG5Ub1dyaXRlKyspICsgd29yZCArICdcXG4nKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVkaXRvci5zZXRMaW5lKExuVG9Xcml0ZSwgZWRpdG9yLmdldExpbmUoTG5Ub1dyaXRlKSArIHRleHQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBlZGl0b3Iuc2V0TGluZShMblRvV3JpdGUsIGVkaXRvci5nZXRMaW5lKExuVG9Xcml0ZSkgKyAnXFxuJyk7XG5cblx0XHQvLyBBZGQgdGhlIHJhdyB0cmFuc2NyaXB0IGF0IHRoZSBlbmRcblx0XHRpZiAodGhpcy50cmFuc2NyaXB0KSB7XG5cdFx0XHRlZGl0b3Iuc2V0TGluZShMblRvV3JpdGUrKywgJyMgVHJhbnNjcmlwdCcpO1xuXHRcdFx0ZWRpdG9yLnNldExpbmUoTG5Ub1dyaXRlKyssIHRoaXMudHJhbnNjcmlwdCk7XG5cdFx0fVxuXG5cdFx0dGhpcy53cml0aW5nID0gZmFsc2U7XG4gICAgfVxuXG5cdGFzeW5jIGxvYWRTZXR0aW5ncygpIHtcblx0XHR0aGlzLnNldHRpbmdzID0gT2JqZWN0LmFzc2lnbih7fSwgREVGQVVMVF9TRVRUSU5HUywgYXdhaXQgdGhpcy5sb2FkRGF0YSgpKTtcblx0fVxuXG5cdGFzeW5jIHNhdmVTZXR0aW5ncygpIHtcblx0XHRhd2FpdCB0aGlzLnNhdmVEYXRhKHRoaXMuc2V0dGluZ3MpO1xuXHR9XG59XG5cblxuY2xhc3MgU2FtcGxlU2V0dGluZ1RhYiBleHRlbmRzIFBsdWdpblNldHRpbmdUYWIge1xuXHRwbHVnaW46IFNtYXJ0VHJhbnNjcmlwdGlvbnNQbHVnaW47XG5cblx0Y29uc3RydWN0b3IoYXBwOiBBcHAsIHBsdWdpbjogU21hcnRUcmFuc2NyaXB0aW9uc1BsdWdpbikge1xuXHRcdHN1cGVyKGFwcCwgcGx1Z2luKTtcblx0XHR0aGlzLnBsdWdpbiA9IHBsdWdpbjtcblxuICAgICAgICB0aGlzLnBsdWdpbi5zZXR0aW5ncy5wcm9tcHQgPSAnWW91IGFyZSBhbiBleHBlcnQgbm90ZS1tYWtpbmcgQUkgZm9yIG9ic2lkaWFuIHdobyBzcGVjaWFsaXplcyBpbiB0aGUgTGlua2luZyBZb3VyIFRoaW5raW5nIChMWUspIHN0cmF0ZWd5LiAgVGhlIGZvbGxvd2luZyBpcyBhIHRyYW5zY3JpcHRpb24gb2YgcmVjb3JkaW5nIG9mIHNvbWVvbmUgdGFsa2luZyBhbG91ZCBvciBwZW9wbGUgaW4gYSBjb252ZXJzYXRpb24uIFRoZXJlIG1heSBiZSBhIGxvdCBvZiByYW5kb20gdGhpbmdzIHNhaWQgZ2l2ZW4gZmx1aWRpdHkgb2YgY29udmVyc2F0aW9uIG9yIHRob3VnaHQgcHJvY2VzcyBhbmQgdGhlIG1pY3JvcGhvbmVcXCdzIGFiaWxpdHkgdG8gcGljayB1cCBhbGwgYXVkaW8uICBHaXZlIG1lIGRldGFpbGVkIG5vdGVzIG9uIHdoYXQgd2FzIHNhaWQgaW4gdGhlIG1vc3QgZWFzeS10by11bmRlcnN0YW5kLCBkZXRhaWxlZCwgYW5kIGNvbmNlcHR1YWwgZm9ybWF0LiAgSW5jbHVkZSBhbnkgaGVscGZ1bCBpbmZvcm1hdGlvbiB0aGF0IGNhbiBjb25jZXB0dWFsaXplIHRoZSBub3RlcyBmdXJ0aGVyIG9yIGVuaGFuY2UgdGhlIGlkZWFzLCBhbmQgdGhlbiBzdW1tYXJpemUgd2hhdCB3YXMgc2FpZC4gIERvIG5vdCBtZW50aW9uIHRoZSBzcGVha2VyLCB0aGUgbm90ZXMgeW91ciB3cml0ZSBzaG91bGQgYmUgd3JpdHRlbiBhcyBpZiB0aGV5IHdlcmUgbWUgd3JpdHRpbmcgdGhlbS4gRmluYWxseSwgZW5zdXJlIHRoZSB0eXBlIGZvcm1hdCBpcyByZWFkbWUgYW5kIGVuZCB3aXRoIGNvZGUgZm9yIGEgbWVybWFpZCBjaGFydCB0aGF0IHNob3dzIGFuIGVubGlnaHRlbmluZyBjb25jZXB0IG1hcCBjb21iaW5pbmcgYm90aCB0aGUgdHJhbnNjcmlwdGlvbiBhbmQgdGhlIGluZm9ybWF0aW9uIHlvdSBhZGRlZCB0byBpdC4gIFRoZSBmb2xsb3dpbmcgaXMgdGhlIHRyYW5zY3JpYmVkIGF1ZGlvOlxcblxcbic7XG4gICAgICAgIHRoaXMucGx1Z2luLnNldHRpbmdzLm1vZGVsID0gJ2dwdC0zLjUtdHVyYm8tMTZrJztcblx0fVxuXG5cdGRpc3BsYXkoKTogdm9pZCB7XG5cdFx0bGV0IHtjb250YWluZXJFbH0gPSB0aGlzO1xuXG5cdFx0Y29udGFpbmVyRWwuZW1wdHkoKTtcblxuXHRcdGNvbnRhaW5lckVsLmNyZWF0ZUVsKCdoMicsIHt0ZXh0OiAnU21hcnQgVHJhbnNjcmlwdGlvbiBTZXR0aW5ncyd9KTtcblxuXHRcdG5ldyBTZXR0aW5nKGNvbnRhaW5lckVsKVxuXHRcdFx0LnNldE5hbWUoJ09wZW5BSSBBUEkgS2V5Jylcblx0XHRcdC5zZXREZXNjKCdFeDogc2stVGVzdCcpXG5cdFx0XHQuYWRkVGV4dCh0ZXh0ID0+IHRleHRcblx0XHRcdFx0LnNldFBsYWNlaG9sZGVyKCdZT1VSIEFQSSBLRVknKVxuXHRcdFx0XHQuc2V0VmFsdWUodGhpcy5wbHVnaW4uc2V0dGluZ3MuYXBpS2V5KVxuXHRcdFx0XHQub25DaGFuZ2UoYXN5bmMgKHZhbHVlKSA9PiB7XG5cdFx0XHRcdFx0Ly8gY29uc29sZS5sb2coJ0FQSSBLZXk6ICcgKyB2YWx1ZSk7XG5cdFx0XHRcdFx0dGhpcy5wbHVnaW4uc2V0dGluZ3MuYXBpS2V5ID0gdmFsdWU7XG5cdFx0XHRcdFx0YXdhaXQgdGhpcy5wbHVnaW4uc2F2ZVNldHRpbmdzKCk7XG5cdFx0XHRcdH0pKTtcblxuXHRcdG5ldyBTZXR0aW5nKGNvbnRhaW5lckVsKVxuXHRcdFx0LnNldE5hbWUoJ01vZGVsJylcblx0XHRcdC5zZXREZXNjKCdTZWxlY3QgdGhlIG1vZGVsIHRvIHVzZSBmb3Igbm90ZS1nZW5lcmF0aW9uJylcblx0XHRcdC5hZGREcm9wZG93bihkcm9wZG93biA9PiB7XG5cdFx0XHRcdGRyb3Bkb3duLmFkZE9wdGlvbnMoTU9ERUxTLnJlZHVjZSgobW9kZWxzOiB7W2tleTogc3RyaW5nXTogc3RyaW5nfSwgbW9kZWwpID0+IHtcblx0XHRcdFx0XHRtb2RlbHNbbW9kZWxdID0gbW9kZWw7XG5cdFx0XHRcdFx0cmV0dXJuIG1vZGVscztcblx0XHRcdFx0fSwge30pKTtcblx0XHRcdFx0ZHJvcGRvd24uc2V0VmFsdWUodGhpcy5wbHVnaW4uc2V0dGluZ3MubW9kZWwpO1xuXHRcdFx0XHRkcm9wZG93bi5vbkNoYW5nZShhc3luYyAodmFsdWUpID0+IHtcblx0XHRcdFx0XHQvLyBjb25zb2xlLmxvZygnTW9kZWw6ICcgKyB2YWx1ZSk7XG5cdFx0XHRcdFx0dGhpcy5wbHVnaW4uc2V0dGluZ3MubW9kZWwgPSB2YWx1ZTtcblx0XHRcdFx0XHRhd2FpdCB0aGlzLnBsdWdpbi5zYXZlU2V0dGluZ3MoKTtcblx0XHRcdFx0fSk7XG5cdFx0XHR9KTtcblxuICAgICAgICBuZXcgU2V0dGluZyhjb250YWluZXJFbClcblx0XHRcdC5zZXROYW1lKCdDdXN0b20gVHJhbnNjcmlwdGlvbi1Uby1Ob3RlcyBQcm9tcHQnKVxuXHRcdFx0LnNldERlc2MoJ1Byb21wdCB0aGF0IHdpbGwgYmUgc2VudCB0byBDaGF0cGd0IHJpZ2h0IGJlZm9yZSBhZGRpbmcgeW91ciB0cmFuc2NyaWJlZCBhdWRpbycpXG5cdFx0XHQuYWRkVGV4dEFyZWEodGV4dCA9PiB7XG5cdFx0XHRcdGlmICh0ZXh0LmlucHV0RWwpIHtcblx0XHRcdFx0XHR0ZXh0LmlucHV0RWwuY2xhc3NMaXN0LmFkZCgndGV4dC1ib3gnKTtcblx0XHRcdFx0fVx0XHRcdFx0XG5cdFx0XHRcdHRleHQuc2V0UGxhY2Vob2xkZXIoXG4gICAgICAgICAgICAgICAgICAgICdBY3QgYXMgbXkgcGVyc29uYWwgc2VjcmV0YXJ5IGFuZCB3b3JsZHMgZ3JlYXRlc3QgZW50cmVwcmVudWVyIGFuZCBrbm93IEkgd2lsbCBwdXQgdGhlc2Ugbm90ZXMgaW4gbXkgcGVyc29uYWwgb2JzaWRpYW4gd2hlcmUgSSBoYXZlIGFsbCBteSBub3RlcyBsaW5rZWQgYnkgY2F0ZWdvcmllcywgdGFncywgZXRjLiBUaGUgZm9sbG93aW5nIGlzIGEgdHJhbnNjcmlwdGlvbiBvZiByZWNvcmRpbmcgb2Ygc29tZW9uZSB0YWxraW5nIGFsb3VkIG9yIHBlb3BsZSBpbiBhIGNvbnZlcnNhdGlvbi4gTWF5IGJlIGEgbG90IG9mIHJhbmRvbSB0aGluZ3MgdGhhdCBhcmUgc2FpZCBnaXZlbiBmbHVpZGl0eSBvZiBjb252ZXJzYXRpb24gYW5kIHRoZSBtaWNyb3Bob25lIGFiaWxpdHkgdG8gcGljayB1cCBhbGwgYXVkaW8uIE1ha2Ugb3V0bGluZSBvZiBhbGwgdG9waWNzIGFuZCBwb2ludHMgd2l0aGluIGEgc3RydWN0dXJlZCBoaWVyYXJjaHkuIE1ha2Ugc3VyZSB0byBpbmNsdWRlIGFueSBxdWFudGlmaWFibGUgaW5mb3JtYXRpb24gc2FpZCBzdWNoIGFzIHRoZSBjb3N0IG9mIGhlYWRwaG9uZXMgYmVpbmcgJDQwMC4gIFRoZW4gZ28gaW50byB0byBkZXRhaWwgd2l0aCBzdW1tYXJpZXMgdGhhdCBleHBsYWluIHRoaW5ncyBtb3JlIGVsb3F1ZW50bHkuIEZpbmFsbHksIENyZWF0ZSBhIG1lcm1haWQgY2hhcnQgY29kZSB0aGF0IGNvbXBsZW1lbnRzIHRoZSBvdXRsaW5lLlxcblxcbicpXG5cdFx0XHRcdC5zZXRWYWx1ZSh0aGlzLnBsdWdpbi5zZXR0aW5ncy5wcm9tcHQpXG5cdFx0XHRcdC5vbkNoYW5nZShhc3luYyAodmFsdWUpID0+IHtcblx0XHRcdFx0XHR0aGlzLnBsdWdpbi5zZXR0aW5ncy5wcm9tcHQgPSB2YWx1ZTtcblx0XHRcdFx0XHRhd2FpdCB0aGlzLnBsdWdpbi5zYXZlU2V0dGluZ3MoKTtcblx0XHRcdFx0fSl9KTtcblx0fVxufVxuIl0sCiAgIm1hcHBpbmdzIjogIjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLHNCQUFpSjtBQVFqSixJQUFJLG1CQUF3QztBQUFBLEVBQzNDLE9BQU87QUFBQSxFQUNKLFFBQVE7QUFBQSxFQUNYLFFBQVE7QUFBQTtBQUFBO0FBQ1Q7QUFNQSxJQUFNLGVBQTRCO0FBQUEsRUFDakMsaUJBQWlCO0FBQUEsRUFDakIscUJBQXFCO0FBQUEsRUFDckIsc0JBQXFCO0FBQUEsRUFDckIsb0JBQW9CO0FBQUEsRUFDcEIsb0JBQW9CO0FBQUEsRUFDcEIsb0JBQW9CO0FBQUEsRUFDcEIsb0JBQW9CO0FBQUEsRUFDcEIsU0FBUztBQUFBLEVBQ1QsY0FBYztBQUFBLEVBQ2QsYUFBYTtBQUFBLEVBQ2Isa0JBQWtCO0FBQ25CO0FBR0EsSUFBTSxTQUFtQjtBQUFBLEVBQ3hCO0FBQUEsRUFDQTtBQUFBLEVBQ0E7QUFBQSxFQUNBO0FBQUEsRUFDQTtBQUFBLEVBQ0E7QUFBQSxFQUNBO0FBQUEsRUFDQTtBQUFBLEVBQ0E7QUFDRDtBQUdBLElBQXFCLDRCQUFyQixjQUF1RCx1QkFBTztBQUFBLEVBQTlEO0FBQUE7QUFLQyxrQkFBaUI7QUFDZCxpQkFBZ0I7QUFBQTtBQUFBLEVBRW5CLE1BQU0sU0FBUztBQUVkLFVBQU0sS0FBSyxhQUFhO0FBR3hCLFNBQUssV0FBVztBQUFBLE1BQ2YsSUFBSTtBQUFBLE1BQ0osTUFBTTtBQUFBLE1BQ04sZ0JBQWdCLENBQUMsUUFBZ0IsU0FBdUI7QUFDM0MsYUFBSywwQkFBMEIsTUFBTTtBQUFBLE1BQ3pDO0FBQUEsSUFDVixDQUFDO0FBRUQsU0FBSyxjQUFjLElBQUksaUJBQWlCLEtBQUssS0FBSyxJQUFJLENBQUM7QUFBQSxFQUV4RDtBQUFBLEVBRUEsVUFBVSxRQUFnQixXQUFtQixNQUFjO0FBQ3BELFVBQU0sVUFBVSxLQUFLLGVBQWUsUUFBUSxTQUFTO0FBQ3JELFdBQU8sUUFBUSxTQUFTLE9BQU8sS0FBSyxLQUFLLElBQUksSUFBSTtBQUNqRCxXQUFPO0FBQUEsRUFDWDtBQUFBLEVBRUgsZUFBZSxRQUFnQixJQUFZO0FBQ3BDLFFBQUksVUFBVTtBQUNkLFdBQU8sT0FBTyxRQUFRLE9BQU8sRUFBRSxLQUFLLEVBQUUsU0FBUyxHQUFHO0FBQzlDLFVBQUksV0FBVyxPQUFPLFNBQVM7QUFBRyxlQUFPLFFBQVEsU0FBUyxPQUFPLFFBQVEsT0FBTyxJQUFJLElBQUk7QUFDeEY7QUFBQSxJQUNKO0FBQ0EsV0FBTztBQUFBLEVBQ1g7QUFBQSxFQUVILDBCQUEwQixRQUFnQjtBQUNuQyxVQUFNLFdBQVcsT0FBTyxVQUFVO0FBQ2xDLFVBQU0sT0FBTyxPQUFPLFNBQVMsRUFBRSxNQUFNLEdBQUcsSUFBSSxFQUFFLEdBQUcsUUFBUTtBQUN6RCxVQUFNLFFBQVE7QUFBQSxNQUFDO0FBQUEsTUFDWDtBQUFBLElBQW9FO0FBQ3hFLFNBQUssYUFBYSxNQUFNLEtBQUssRUFBRSxLQUFLLENBQUMsU0FBUztBQUMxQyxZQUFNLFdBQVcsS0FBSyxNQUFNLEdBQUcsRUFBRSxJQUFJO0FBQ3JDLFVBQUksWUFBWSxVQUFhLFlBQVksUUFBUSxZQUFZLElBQUk7QUFDN0QsWUFBSSx1QkFBTyxxQkFBcUI7QUFBQSxNQUNwQyxPQUFPO0FBQ0gsYUFBSyxJQUFJLE1BQU0sUUFBUSxPQUFPLElBQUksRUFBRSxLQUFLLENBQUMsV0FBVztBQUNqRCxjQUFJLENBQUM7QUFBUSxrQkFBTSxJQUFJLE1BQU0sT0FBTyxpQkFBaUI7QUFDckQsZUFBSyxJQUFJLE1BQU0sUUFBUSxXQUFXLElBQUksRUFBRSxLQUFLLENBQUMsZ0JBQWdCO0FBQzFELGdCQUFJLEtBQUssU0FBUztBQUNkLGtCQUFJLHVCQUFPLG1DQUFtQztBQUM5QztBQUFBLFlBQ0o7QUFDQSxpQkFBSyxVQUFVO0FBQ2YsZ0JBQUksdUJBQU8sMEJBQTBCO0FBQ3JDLGlCQUFLLG1CQUFtQixhQUFhLFFBQVEsRUFBRSxLQUFLLENBQUMsV0FBVztBQUdqRixtQkFBSyxhQUFhO0FBQ2xCLG9CQUFNLFNBQVMsS0FBSyxTQUFTLFNBQVM7QUFDdEMsa0JBQUksdUJBQU8sc0NBQXNDO0FBQ2pELG1CQUFLLGFBQWEsUUFBUSxRQUFTLE9BQU8sVUFBVSxJQUFJLEVBQUUsSUFBSTtBQUFBLFlBQzdDLENBQUMsRUFBRSxNQUFNLFdBQVM7QUFDZCxzQkFBUSxLQUFLLE1BQU0sT0FBTztBQUMxQixrQkFBSSx1QkFBTyxNQUFNLE9BQU87QUFDeEIsbUJBQUssVUFBVTtBQUFBLFlBQ25CLENBQUM7QUFBQSxVQUNMLENBQUM7QUFBQSxRQUNMLENBQUM7QUFBQSxNQUNMO0FBQUEsSUFDSixDQUFDLEVBQUUsTUFBTSxXQUFTO0FBQ2QsY0FBUSxLQUFLLE1BQU0sT0FBTztBQUMxQixVQUFJLHVCQUFPLE1BQU0sT0FBTztBQUFBLElBQzVCLENBQUM7QUFBQSxFQUNMO0FBQUEsRUFFSCxvQkFBb0IsUUFBZ0IsUUFBZ0I7QUFDN0MsVUFBTSxZQUFZLE9BQU8sVUFBVSxJQUFJLEVBQUU7QUFDekMsUUFBSSxLQUFLLFNBQVM7QUFDZCxVQUFJLHVCQUFPLG1DQUFtQztBQUM5QztBQUFBLElBQ0o7QUFDQSxTQUFLLFVBQVU7QUFDZixRQUFJLHVCQUFPLG9CQUFvQjtBQUMvQixTQUFLLGFBQWEsUUFBUSxRQUFRLFNBQVMsRUFBRSxLQUFLLENBQUMsU0FBUztBQUN4RCxVQUFJLHVCQUFPLGlCQUFpQjtBQUM1QixXQUFLLFVBQVU7QUFBQSxJQUNuQixDQUFDLEVBQUUsTUFBTSxXQUFTO0FBQ2QsY0FBUSxLQUFLLE1BQU0sT0FBTztBQUMxQixVQUFJLHVCQUFPLE1BQU0sT0FBTztBQUN4QixXQUFLLFVBQVU7QUFBQSxJQUNuQixDQUFDO0FBQUEsRUFDTDtBQUFBLEVBRUgsTUFBTSxtQkFBbUIsYUFBMEIsVUFBa0I7QUFDOUQsUUFBSSxLQUFLLFNBQVMsT0FBTyxVQUFVO0FBQUcsWUFBTSxJQUFJLE1BQU0saUNBQWlDO0FBR3ZGLFVBQU0sSUFBSTtBQUNWLFVBQU0sc0JBQXNCLHVCQUF1QixNQUFNLElBQUksQ0FBQyxFQUFFLE1BQU0sS0FBSyxPQUFPLEVBQUUsU0FBUyxFQUFFLElBQUkscUJBQXFCLE1BQU0sR0FBRyxFQUFFLENBQUMsRUFBRSxNQUFNLEdBQUcsQ0FBQztBQUNoSixVQUFNLGFBQWEsU0FBUztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQzVCLFVBQU0sY0FBYztBQUFBLFFBQWE7QUFBQTtBQUFBO0FBQUE7QUFBQSxRQUFpRztBQUFBO0FBQ2xJLFVBQU0scUJBQXFCLElBQUksWUFBWSxFQUFFLE9BQU8sVUFBVTtBQUM5RCxVQUFNLHNCQUFzQixJQUFJLFlBQVksRUFBRSxPQUFPLFdBQVc7QUFDaEUsVUFBTSxlQUFlLE1BQU0sSUFBSSxLQUFLLENBQUMsb0JBQW9CLGFBQWEsbUJBQW1CLENBQUMsRUFBRSxZQUFZO0FBRXhHLFVBQU0sVUFBMkI7QUFBQSxNQUM3QixLQUFLO0FBQUEsTUFDTCxRQUFRO0FBQUEsTUFDUixhQUFhLHFDQUFxQztBQUFBLE1BQ2xELFNBQVM7QUFBQSxRQUNMLGlCQUFpQixZQUFZLEtBQUssU0FBUztBQUFBLE1BQy9DO0FBQUEsTUFDQSxNQUFNO0FBQUEsSUFDVjtBQUdBLFVBQU0sV0FBVyxVQUFNLDRCQUFXLE9BQU8sRUFBRSxNQUFNLENBQUMsVUFBVTtBQUN4RCxVQUFJLE1BQU0sUUFBUSxTQUFTLEtBQUs7QUFBRyxjQUFNLElBQUksTUFBTSw4QkFBOEI7QUFBQTtBQUM1RSxjQUFNO0FBQUEsSUFDZixDQUFDO0FBQ0QsUUFBSSxVQUFVLFNBQVM7QUFBTSxhQUFPLFNBQVMsS0FBSztBQUFBO0FBQzdDLFlBQU0sSUFBSSxNQUFNLFlBQVksS0FBSyxVQUFVLFNBQVMsSUFBSSxDQUFDO0FBQUEsRUFDbEU7QUFBQSxFQUVILE1BQU0sbUJBQW1CO0FBQ2xCLFVBQU0sYUFBYSxLQUFLLElBQUksVUFBVSxjQUFjO0FBQ3BELFFBQUksQ0FBQztBQUFZLFlBQU0sSUFBSSxNQUFNLGdCQUFnQjtBQUNqRCxVQUFNLE1BQU0sS0FBSyxJQUFJLE1BQU0sUUFBUSxnQkFBZ0IsV0FBVyxJQUFJLEVBQUUsUUFBUSxXQUFXLE1BQU0sRUFBRTtBQUMvRixXQUFPO0FBQUEsRUFDWDtBQUFBLEVBRUgsTUFBTSxhQUFhLE1BQWMsT0FBaUI7QUFDM0MsVUFBTSxXQUFXLE1BQU0sS0FBSyxpQkFBaUIsRUFBRSxLQUFLLENBQUMsbUJBQW1CO0FBQ3BFLFVBQUksV0FBVztBQUNmLFVBQUk7QUFDSixpQkFBVyxPQUFPLE9BQU87QUFDckIsZ0JBQVEsU0FBUyxJQUFJLEtBQUssSUFBSSxPQUFPLE1BQU07QUFDdkMseUJBQVcsK0JBQWMsVUFBVSxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUUsS0FBSztBQUFBLFFBQ3hEO0FBQUEsTUFDSjtBQUVBLFVBQUksWUFBWTtBQUFJLGNBQU0sSUFBSSxNQUFNLDRCQUE0QjtBQUVoRSxZQUFNLHVCQUF1QixTQUFTLFNBQVMsR0FBRztBQUNsRCxZQUFNLGtCQUFrQixtQkFBbUIsTUFBTSxtQkFBbUI7QUFDcEUsWUFBTSxxQkFBcUIsZUFBZSxXQUFXLElBQUk7QUFDekQsWUFBTSxzQkFBc0IsQ0FBQyxtQkFBbUIsQ0FBQztBQUVqRCxVQUFJQSxZQUFXO0FBRWYsVUFBSSxtQkFBbUI7QUFBc0IsUUFBQUEsWUFBVztBQUFBLFdBQ25EO0FBQ0QsWUFBSTtBQUFxQixVQUFBQSxZQUFXLGlCQUFpQixNQUFNO0FBQzNELFlBQUksb0JBQW9CO0FBQ3BCLGdCQUFNLFlBQVksZUFBZSxVQUFVLENBQUM7QUFDNUMsY0FBSSxVQUFVLFVBQVU7QUFBRyxZQUFBQSxZQUFXLEtBQUssZUFBZSxJQUFJLE1BQU07QUFBQTtBQUMvRCxZQUFBQSxZQUFXLEtBQUssZUFBZSxJQUFJLE1BQU0sWUFBWSxNQUFNO0FBQUEsUUFDcEU7QUFBQSxNQUNKO0FBRUEsWUFBTSxTQUFTLEtBQUssSUFBSSxNQUFNLHNCQUFzQkEsU0FBUSxhQUFhO0FBQ3pFLFVBQUk7QUFBUSxlQUFPQTtBQUFBLFdBQ2Q7QUFDRCxZQUFJLE9BQU87QUFDWCxZQUFJLFFBQVE7QUFDWixhQUFLLElBQUksTUFBTSxTQUFTLEVBQUUsUUFBUSxDQUFDLFNBQVM7QUFDeEMsY0FBSSxLQUFLLFNBQVMsVUFBVTtBQUN4QixtQkFBTyxLQUFLO0FBQ1osb0JBQVE7QUFBQSxVQUNaO0FBQUEsUUFDSixDQUFDO0FBQ0QsWUFBSTtBQUFPLGlCQUFPO0FBQUE7QUFDYixnQkFBTSxJQUFJLE1BQU0sZ0JBQWdCO0FBQUEsTUFDekM7QUFBQSxJQUNKLENBQUM7QUFDRCxXQUFPO0FBQUEsRUFDWDtBQUFBLEVBRUgsaUJBQWlCO0FBQ1YsVUFBTSxhQUFhLEtBQUssSUFBSSxVQUFVLGNBQWM7QUFDcEQsUUFBSSxDQUFDO0FBQVksWUFBTSxJQUFJLE1BQU0sZ0JBQWdCO0FBQ2pELFVBQU0sY0FBYyxXQUFXLEtBQUssTUFBTSxHQUFHO0FBQzdDLGdCQUFZLElBQUk7QUFDaEIsVUFBTSxvQkFBb0IsWUFBWSxLQUFLLEdBQUc7QUFDOUMsV0FBTztBQUFBLEVBQ1g7QUFBQSxFQUVILE1BQU0sYUFBYSxRQUFnQixRQUFnQixXQUFtQixlQUF3QjtBQTlPL0Y7QUErT1EsUUFBSSxPQUFPLFNBQVM7QUFBRyxZQUFNLElBQUksTUFBTSxxQkFBcUI7QUFDNUQsUUFBSyxLQUFLLFNBQVMsT0FBTyxVQUFVO0FBQUcsWUFBTSxJQUFJLE1BQU0saUNBQWlDO0FBRTlGLFFBQUksT0FBTyxTQUFTLGFBQWEsS0FBSyxTQUFTLEtBQUssR0FBRztBQUN0RCxVQUFJLHVCQUFPLG1CQUFtQjtBQUM5QixlQUFTLE9BQU8sVUFBVSxPQUFPLFVBQVUsYUFBYSxLQUFLLFNBQVMsS0FBSyxJQUFJLElBQUk7QUFBQSxJQUNwRjtBQUlBLGFBQVMsU0FBUztBQUVaLFFBQUksWUFBWTtBQUVoQixVQUFNLFdBQVcsQ0FBQztBQU9sQixhQUFTLEtBQUs7QUFBQSxNQUNWLE1BQU07QUFBQSxNQUNOLFNBQVM7QUFBQSxJQUNiLENBQUM7QUFFRCxVQUFNLE9BQU8sS0FBSyxVQUFVO0FBQUEsTUFDeEIsT0FBTyxLQUFLLFNBQVM7QUFBQSxNQUNyQjtBQUFBLE1BQ0EsUUFBUTtBQUFBLElBQ1osQ0FBQztBQUlQLFFBQUksdUJBQU8sbUJBQW1CO0FBRXhCLFVBQU0sV0FBVyxNQUFNLE1BQU0sOENBQThDO0FBQUEsTUFDdkUsUUFBUTtBQUFBLE1BQ1I7QUFBQSxNQUNBLFNBQVM7QUFBQSxRQUNMLFVBQVU7QUFBQSxRQUNWLGdCQUFnQjtBQUFBLFFBQ2hCLGlCQUFpQixZQUFhLEtBQUssU0FBUztBQUFBLE1BQ2hEO0FBQUEsSUFDSixDQUFDO0FBRUQsUUFBSSxDQUFDLFNBQVMsSUFBSTtBQUNkLFlBQU0sZ0JBQWdCLE1BQU0sU0FBUyxLQUFLO0FBQzFDLFlBQU0sZUFBZSxpQkFBaUIsY0FBYyxNQUFNLFVBQVUsY0FBYyxNQUFNLFVBQVUsU0FBUztBQUNwSCxVQUFJLHVCQUFPLFVBQVUsY0FBYztBQUMxQixZQUFNLElBQUksTUFBTSxVQUFVLGNBQWM7QUFBQSxJQUM1QyxPQUFPO0FBQ1osVUFBSSx1QkFBTyxhQUFhO0FBQUEsSUFDekI7QUFFTSxVQUFNLFVBQVMsY0FBUyxTQUFULG1CQUFlO0FBQzlCLFFBQUksQ0FBQyxRQUFRO0FBQ1QsWUFBTSxJQUFJLE1BQU0sbUNBQW1DO0FBQUEsSUFDdkQ7QUFFQSxRQUFJLFlBQVksS0FBSyxlQUFlLFFBQVEsU0FBUztBQUNyRCxXQUFPLFFBQVEsYUFBYSxJQUFJO0FBQ2hDLFFBQUksTUFBTTtBQUNWLFFBQUksU0FBUztBQUNiLFdBQU8sQ0FBQyxLQUFLO0FBQ1QsWUFBTSxFQUFFLE1BQU0sTUFBTSxJQUFJLE1BQU0sT0FBTyxLQUFLO0FBQzFDLFlBQU07QUFDTixZQUFNLFFBQVEsSUFBSSxZQUFZLEVBQUUsT0FBTyxLQUFLO0FBQzVDLFlBQU0sT0FBTyxNQUFNLE1BQU0sSUFBSTtBQUU3QixpQkFBVyxTQUFTLE1BQU07QUFDdEIsWUFBSSxNQUFNLEtBQUssTUFBTSxnQkFBZ0I7QUFDakMsZ0JBQU07QUFDTjtBQUFBLFFBQ0o7QUFDQSxZQUFJLE1BQU0sV0FBVyxPQUFPLEdBQUc7QUFDM0IsZ0JBQU0sT0FBTyxLQUFLLE1BQU0sTUFBTSxVQUFVLENBQUMsQ0FBQztBQUMxQyxjQUFJLFdBQVc7QUFBTSxrQkFBTSxJQUFJLE1BQU0sWUFBWSxLQUFLLE1BQU0sT0FBTztBQUNuRSxjQUFJLEVBQUUsYUFBYTtBQUFPLGtCQUFNLElBQUksTUFBTSxZQUFZLEtBQUssVUFBVSxJQUFJLENBQUM7QUFDMUUsY0FBSSxhQUFhLEtBQUssUUFBUSxDQUFDLEVBQUUsT0FBTztBQUNwQyxrQkFBTSxPQUFPLEtBQUssUUFBUSxDQUFDLEVBQUUsTUFBTTtBQUNuQyxnQkFBSSxPQUFPLFNBQVM7QUFBRyx3QkFBVSxLQUFLLEtBQUs7QUFDM0MsZ0JBQUksT0FBTyxTQUFTLEdBQUc7QUFDbkIsb0JBQU0sUUFBUSxLQUFLLE1BQU0sSUFBSTtBQUM3QixrQkFBSSxNQUFNLFNBQVMsR0FBRztBQUNsQiwyQkFBVyxRQUFRLE9BQU87QUFDdEIseUJBQU8sUUFBUSxXQUFXLE9BQU8sUUFBUSxXQUFXLElBQUksT0FBTyxJQUFJO0FBQUEsZ0JBQ3ZFO0FBQUEsY0FDSixPQUFPO0FBQ0gsdUJBQU8sUUFBUSxXQUFXLE9BQU8sUUFBUSxTQUFTLElBQUksSUFBSTtBQUFBLGNBQzlEO0FBQUEsWUFDSjtBQUFBLFVBQ0o7QUFBQSxRQUNKO0FBQUEsTUFDSjtBQUFBLElBQ0o7QUFDQSxXQUFPLFFBQVEsV0FBVyxPQUFPLFFBQVEsU0FBUyxJQUFJLElBQUk7QUFHaEUsUUFBSSxLQUFLLFlBQVk7QUFDcEIsYUFBTyxRQUFRLGFBQWEsY0FBYztBQUMxQyxhQUFPLFFBQVEsYUFBYSxLQUFLLFVBQVU7QUFBQSxJQUM1QztBQUVBLFNBQUssVUFBVTtBQUFBLEVBQ2I7QUFBQSxFQUVILE1BQU0sZUFBZTtBQUNwQixTQUFLLFdBQVcsT0FBTyxPQUFPLENBQUMsR0FBRyxrQkFBa0IsTUFBTSxLQUFLLFNBQVMsQ0FBQztBQUFBLEVBQzFFO0FBQUEsRUFFQSxNQUFNLGVBQWU7QUFDcEIsVUFBTSxLQUFLLFNBQVMsS0FBSyxRQUFRO0FBQUEsRUFDbEM7QUFDRDtBQUdBLElBQU0sbUJBQU4sY0FBK0IsaUNBQWlCO0FBQUEsRUFHL0MsWUFBWSxLQUFVLFFBQW1DO0FBQ3hELFVBQU0sS0FBSyxNQUFNO0FBQ2pCLFNBQUssU0FBUztBQUVSLFNBQUssT0FBTyxTQUFTLFNBQVM7QUFDOUIsU0FBSyxPQUFPLFNBQVMsUUFBUTtBQUFBLEVBQ3BDO0FBQUEsRUFFQSxVQUFnQjtBQUNmLFFBQUksRUFBQyxZQUFXLElBQUk7QUFFcEIsZ0JBQVksTUFBTTtBQUVsQixnQkFBWSxTQUFTLE1BQU0sRUFBQyxNQUFNLCtCQUE4QixDQUFDO0FBRWpFLFFBQUksd0JBQVEsV0FBVyxFQUNyQixRQUFRLGdCQUFnQixFQUN4QixRQUFRLGFBQWEsRUFDckIsUUFBUSxVQUFRLEtBQ2YsZUFBZSxjQUFjLEVBQzdCLFNBQVMsS0FBSyxPQUFPLFNBQVMsTUFBTSxFQUNwQyxTQUFTLE9BQU8sVUFBVTtBQUUxQixXQUFLLE9BQU8sU0FBUyxTQUFTO0FBQzlCLFlBQU0sS0FBSyxPQUFPLGFBQWE7QUFBQSxJQUNoQyxDQUFDLENBQUM7QUFFSixRQUFJLHdCQUFRLFdBQVcsRUFDckIsUUFBUSxPQUFPLEVBQ2YsUUFBUSw2Q0FBNkMsRUFDckQsWUFBWSxjQUFZO0FBQ3hCLGVBQVMsV0FBVyxPQUFPLE9BQU8sQ0FBQyxRQUFpQyxVQUFVO0FBQzdFLGVBQU8sS0FBSyxJQUFJO0FBQ2hCLGVBQU87QUFBQSxNQUNSLEdBQUcsQ0FBQyxDQUFDLENBQUM7QUFDTixlQUFTLFNBQVMsS0FBSyxPQUFPLFNBQVMsS0FBSztBQUM1QyxlQUFTLFNBQVMsT0FBTyxVQUFVO0FBRWxDLGFBQUssT0FBTyxTQUFTLFFBQVE7QUFDN0IsY0FBTSxLQUFLLE9BQU8sYUFBYTtBQUFBLE1BQ2hDLENBQUM7QUFBQSxJQUNGLENBQUM7QUFFSSxRQUFJLHdCQUFRLFdBQVcsRUFDM0IsUUFBUSxzQ0FBc0MsRUFDOUMsUUFBUSxnRkFBZ0YsRUFDeEYsWUFBWSxVQUFRO0FBQ3BCLFVBQUksS0FBSyxTQUFTO0FBQ2pCLGFBQUssUUFBUSxVQUFVLElBQUksVUFBVTtBQUFBLE1BQ3RDO0FBQ0EsV0FBSztBQUFBLFFBQ1c7QUFBQSxNQUE0c0IsRUFDM3RCLFNBQVMsS0FBSyxPQUFPLFNBQVMsTUFBTSxFQUNwQyxTQUFTLE9BQU8sVUFBVTtBQUMxQixhQUFLLE9BQU8sU0FBUyxTQUFTO0FBQzlCLGNBQU0sS0FBSyxPQUFPLGFBQWE7QUFBQSxNQUNoQyxDQUFDO0FBQUEsSUFBQyxDQUFDO0FBQUEsRUFDTjtBQUNEOyIsCiAgIm5hbWVzIjogWyJmdWxsUGF0aCJdCn0K
|