@tricoteuses/assemblee
Version:
Retrieve, clean up & handle French Assemblée nationale's open data
517 lines (515 loc) • 104 kB
JavaScript
import assert from "assert";
import commandLineArgs from "command-line-args";
import fs from "fs-extra";
import path from "path";
import { datasets, DatasetStructure, EnabledDatasets, getEnabledDatasets, filterByLegislature } from "../datasets.mjs";
import { existingDateToJson, patchedDateToJson } from "../dates.mjs";
import * as git from "../git.mjs";
import { walkDir } from "../file_systems.mjs";
import { pathFromCompteRenduUid, pathFromDocumentUid, pathFromDossierParlementaireUid, pathFromQuestionUid, pathFromReunionUid, pathFromScrutinUid } from "../loaders.mjs";
import { Convert as ActeursEtOrganesRawConvert } from "../raw_types/acteurs_et_organes.mjs";
import { Convert as AgendasRawConvert } from "../raw_types/agendas.mjs";
import { Convert as AmendementsRawConvert } from "../raw_types/amendements.mjs";
import { Convert as DossiersLegislatifsRawConvert } from "../raw_types/dossiers_legislatifs.mjs";
import { Convert as ScrutinsRawConvert } from "../raw_types/scrutins.mjs";
import { Convert as QuestionsRawConvert } from "../raw_types/questions.mjs";
import { Convert as ComptesRendusRawConvert } from "../raw_types/debats.mjs";
import { cloneOption, commitOption, categoriesOption, legislatureOption, remoteOption, silentOption, verboseOption, dataDirDefaultOption, pullOption, fetchDocumentsOption, parseDocumentsOption, documentFormatOption } from "./shared/cli_helpers.mjs";
function parseArgs(argv) {
const optionsDefinitions = [cloneOption, commitOption, categoriesOption, legislatureOption, remoteOption, silentOption, verboseOption, dataDirDefaultOption, fetchDocumentsOption, parseDocumentsOption, documentFormatOption, pullOption, {
alias: "V",
help: "don't validate raw data",
name: "no-validate-raw",
type: Boolean
}];
const options = commandLineArgs(optionsDefinitions, {
argv: argv
});
return options;
}
function clearDirectory(dirPath) {
fs.ensureDirSync(dirPath);
for (const filename of fs.readdirSync(dirPath)) {
if (!filename.startsWith(".")) {
fs.removeSync(path.join(dirPath, filename));
}
}
}
function cloneGit(gitGroupUrl, datasetName, dataDir) {
git.clone(gitGroupUrl, datasetName, dataDir);
}
function reorganizeData(options) {
const dataDir = options.dataDir;
Date.prototype.toJSON = patchedDateToJson;
let exitCode = options.commit ? 10 : 0;
const gitGroupUrl = options.clone?.trim().replace(/\/+$/, "");
const validateRawData = !options["no-validate-raw"];
const enabledDatasets = getEnabledDatasets(options.categories);
let chosenDatasets = filterByLegislature(datasets, options.legislature);
function commitGit(datasetReorganizedDir, options) {
if (options.commit) {
const errorCode = git.commitAndPush(datasetReorganizedDir, "Nouvelle moisson", options.remote);
if (exitCode === 10 && errorCode !== 10 || exitCode === 0 && errorCode !== 0 && errorCode !== 10) {
exitCode = errorCode;
}
}
}
if (enabledDatasets & EnabledDatasets.ActeursEtOrganes) {
for (const dataset of chosenDatasets.acteursEtOrganes) {
const originalJsonDirectoryOrFilePath = path.join(dataDir, dataset.filename);
if (!options.silent) {
console.log(`Reorganizing ${originalJsonDirectoryOrFilePath}`);
}
const datasetName = path.basename(originalJsonDirectoryOrFilePath, ".json");
cloneGit(gitGroupUrl, datasetName, dataDir);
const datasetReorganizedDir = path.join(dataDir, datasetName);
if (options.pull) {
git.resetAndPull(datasetReorganizedDir);
}
clearDirectory(datasetReorganizedDir);
const acteursReorganizedDir = path.join(datasetReorganizedDir, "acteurs");
fs.mkdirSync(acteursReorganizedDir);
const organesReorganizedDir = path.join(datasetReorganizedDir, "organes");
fs.mkdirSync(organesReorganizedDir);
switch (dataset.structure) {
case DatasetStructure.SingleFile:
{
const originalJson = fs.readFileSync(originalJsonDirectoryOrFilePath, {
encoding: "utf8"
});
const originalData = validateRawData ? ActeursEtOrganesRawConvert.toActeursEtOrganes(originalJson) : JSON.parse(originalJson);
for (const acteur of originalData.export.acteurs.acteur) {
const reorganizedJsonFilePath = path.join(acteursReorganizedDir, acteur.uid["#text"] + ".json");
fs.writeFileSync(reorganizedJsonFilePath, JSON.stringify(acteur, null, 2), {
encoding: "utf8"
});
}
for (const organe of originalData.export.organes.organe) {
const reorganizedJsonFilePath = path.join(organesReorganizedDir, organe.uid + ".json");
fs.writeFileSync(reorganizedJsonFilePath, JSON.stringify(organe, null, 2), {
encoding: "utf8"
});
}
}
break;
case DatasetStructure.SegmentedFiles:
{
const deportsByActeurUid = {};
if (fs.existsSync(path.join(originalJsonDirectoryOrFilePath, "deport"))) {
for (const deportSplitPath of walkDir(originalJsonDirectoryOrFilePath, ["deport"])) {
const deportFilename = deportSplitPath[deportSplitPath.length - 1];
if (!deportFilename.endsWith(".json")) {
continue;
}
const deportOriginalFilePath = path.join(originalJsonDirectoryOrFilePath, ...deportSplitPath);
if (options.verbose) {
console.log(` Loading "deport" file: ${deportOriginalFilePath}…`);
}
const deportOriginalJson = fs.readFileSync(deportOriginalFilePath, {
encoding: "utf8"
});
const deportOriginal = validateRawData ? ActeursEtOrganesRawConvert.toDeport(deportOriginalJson) : JSON.parse(deportOriginalJson);
const deport = deportOriginal.deport;
let deports = deportsByActeurUid[deport.acteurRef];
if (deports === undefined) {
deports = deportsByActeurUid[deport.acteurRef] = [];
}
deports.push(deport);
}
for (const deports of Object.values(deportsByActeurUid)) {
deports.sort((a, b) => a.uid.length === b.uid.length ? a.uid.localeCompare(b.uid) : a.uid.length - b.uid.length);
}
}
const mandatsByActeurUid = {};
if (fs.existsSync(path.join(originalJsonDirectoryOrFilePath, "mandat"))) {
for (const mandatSplitPath of walkDir(originalJsonDirectoryOrFilePath, ["mandat"])) {
const mandatFilename = mandatSplitPath[mandatSplitPath.length - 1];
if (!mandatFilename.endsWith(".json")) {
continue;
}
const mandatOriginalFilePath = path.join(originalJsonDirectoryOrFilePath, ...mandatSplitPath);
if (options.verbose) {
console.log(` Loading "mandat" file: ${mandatOriginalFilePath}…`);
}
const mandatOriginalJson = fs.readFileSync(mandatOriginalFilePath, {
encoding: "utf8"
});
const mandatOriginal = validateRawData ? ActeursEtOrganesRawConvert.toMandatWrapper(mandatOriginalJson) : JSON.parse(mandatOriginalJson);
const mandat = mandatOriginal.mandat;
let mandats = mandatsByActeurUid[mandat.acteurRef];
if (mandats === undefined) {
mandats = mandatsByActeurUid[mandat.acteurRef] = [];
}
mandats.push(mandat);
}
for (const mandats of Object.values(mandatsByActeurUid)) {
mandats.sort((a, b) => a.uid.length === b.uid.length ? a.uid.localeCompare(b.uid) : a.uid.length - b.uid.length);
}
}
for (const acteurSplitPath of walkDir(originalJsonDirectoryOrFilePath, ["acteur"])) {
const acteurFilename = acteurSplitPath[acteurSplitPath.length - 1];
if (!acteurFilename.endsWith(".json")) {
continue;
}
const acteurOriginalFilePath = path.join(originalJsonDirectoryOrFilePath, ...acteurSplitPath);
if (options.verbose) {
console.log(` Reorganizing "acteur" file: ${acteurOriginalFilePath}…`);
}
const acteurOriginalJson = fs.readFileSync(acteurOriginalFilePath, {
encoding: "utf8"
});
const acteurOriginal = validateRawData ? ActeursEtOrganesRawConvert.toActeur(acteurOriginalJson) : JSON.parse(acteurOriginalJson);
const acteur = acteurOriginal.acteur;
const acteurUid = acteur.uid["#text"];
const deports = deportsByActeurUid[acteurUid];
if (deports !== undefined) {
assert.strictEqual(acteur.deports, undefined);
acteur.deports = {
deport: deports
};
}
const mandats = mandatsByActeurUid[acteurUid];
if (mandats !== undefined) {
assert.strictEqual(acteur.mandats, undefined);
acteur.mandats = {
mandat: mandats
};
}
const acteurReorganizedJsonFilePath = path.join(acteursReorganizedDir, acteurUid + ".json");
fs.writeFileSync(acteurReorganizedJsonFilePath, JSON.stringify(acteur, null, 2), {
encoding: "utf8"
});
}
for (const organeSplitPath of walkDir(originalJsonDirectoryOrFilePath, ["organe"])) {
const organeFilename = organeSplitPath[organeSplitPath.length - 1];
if (!organeFilename.endsWith(".json")) {
continue;
}
const organeOriginalFilePath = path.join(originalJsonDirectoryOrFilePath, ...organeSplitPath);
if (options.verbose) {
console.log(` Reorganizing "organe" file: ${organeOriginalFilePath}…`);
}
const organeOriginalJson = fs.readFileSync(organeOriginalFilePath, {
encoding: "utf8"
});
const organeOriginal = validateRawData ? ActeursEtOrganesRawConvert.toOrgane(organeOriginalJson) : JSON.parse(organeOriginalJson);
const organe = organeOriginal.organe;
const organeReorganizedJsonFilePath = path.join(organesReorganizedDir, organe.uid + ".json");
fs.writeFileSync(organeReorganizedJsonFilePath, JSON.stringify(organe, null, 2), {
encoding: "utf8"
});
}
}
break;
}
commitGit(datasetReorganizedDir, options);
}
}
if (enabledDatasets & EnabledDatasets.Agendas) {
for (const dataset of chosenDatasets.agendas) {
const originalJsonDirectoryOrFilePath = path.join(dataDir, dataset.filename);
if (!options.silent) {
console.log(`Reorganizing ${originalJsonDirectoryOrFilePath}`);
}
const datasetName = path.basename(originalJsonDirectoryOrFilePath, ".json");
cloneGit(gitGroupUrl, datasetName, dataDir);
const datasetReorganizedDir = path.join(dataDir, datasetName);
clearDirectory(datasetReorganizedDir);
switch (dataset.structure) {
case DatasetStructure.SingleFile:
{
const originalJson = fs.readFileSync(originalJsonDirectoryOrFilePath, {
encoding: "utf8"
});
const originalData = validateRawData ? AgendasRawConvert.toAgendas(originalJson) : JSON.parse(originalJson);
for (const reunion of originalData.reunions.reunion) {
writeReorganizedJson(datasetReorganizedDir, reunion, pathFromReunionUid);
}
}
break;
case DatasetStructure.SegmentedFiles:
{
for (const reunionSplitPath of walkDir(originalJsonDirectoryOrFilePath, ["reunion"])) {
const reunionFilename = reunionSplitPath[reunionSplitPath.length - 1];
if (!reunionFilename.endsWith(".json")) {
continue;
}
const reunionOriginalFilePath = path.join(originalJsonDirectoryOrFilePath, ...reunionSplitPath);
if (options.verbose) {
console.log(` Reorganizing "reunion" file: ${reunionOriginalFilePath}…`);
}
const reunionOriginalJson = fs.readFileSync(reunionOriginalFilePath, {
encoding: "utf8"
});
const reunionOriginal = validateRawData ? AgendasRawConvert.toReunion(reunionOriginalJson) : JSON.parse(reunionOriginalJson);
const reunion = reunionOriginal.reunion;
writeReorganizedJson(datasetReorganizedDir, reunion, pathFromReunionUid);
}
}
break;
}
commitGit(datasetReorganizedDir, options);
}
}
if (enabledDatasets & EnabledDatasets.Amendements) {
for (const dataset of chosenDatasets.amendements) {
const originalJsonDirectoryOrFilePath = path.join(dataDir, dataset.filename);
if (!options.silent) {
console.log(`Reorganizing ${originalJsonDirectoryOrFilePath}`);
}
const datasetName = path.basename(originalJsonDirectoryOrFilePath, ".json");
cloneGit(gitGroupUrl, datasetName, dataDir);
const datasetReorganizedDir = path.join(dataDir, datasetName);
clearDirectory(datasetReorganizedDir);
switch (dataset.structure) {
case DatasetStructure.SingleFile:
{
const originalJson = fs.readFileSync(originalJsonDirectoryOrFilePath, {
encoding: "utf8"
});
const originalData = validateRawData ? AmendementsRawConvert.toAmendements(originalJson) : JSON.parse(originalJson);
for (const texteLegislatif of originalData.textesEtAmendements.texteleg) {
for (const amendement of texteLegislatif.amendements) {
writeAmendementReorganizedJson(datasetReorganizedDir, amendement);
}
}
}
break;
case DatasetStructure.SegmentedFiles:
{
for (const amendementSplitPath of walkDir(originalJsonDirectoryOrFilePath)) {
const amendementFilename = amendementSplitPath[amendementSplitPath.length - 1];
if (!amendementFilename.endsWith(".json")) {
continue;
}
const amendementOriginalFilePath = path.join(originalJsonDirectoryOrFilePath, ...amendementSplitPath);
if (options.verbose) {
console.log(` Reorganizing "amendement" file: ${amendementOriginalFilePath}…`);
}
const amendementOriginalJson = fs.readFileSync(amendementOriginalFilePath, {
encoding: "utf8"
});
const amendementOriginal = validateRawData ? AmendementsRawConvert.toAmendementWrapper(amendementOriginalJson) : JSON.parse(amendementOriginalJson);
const amendement = amendementOriginal.amendement;
writeAmendementReorganizedJson(datasetReorganizedDir, amendement);
}
}
break;
}
commitGit(datasetReorganizedDir, options);
}
}
if (enabledDatasets & EnabledDatasets.DossiersLegislatifs) {
for (const dataset of chosenDatasets.dossiersLegislatifs) {
const originalJsonDirectoryOrFilePath = path.join(dataDir, dataset.filename);
if (!options.silent) {
console.log(`Reorganizing ${originalJsonDirectoryOrFilePath}`);
}
const datasetName = path.basename(originalJsonDirectoryOrFilePath, ".json");
cloneGit(gitGroupUrl, datasetName, dataDir);
const datasetReorganizedDir = path.join(dataDir, datasetName);
clearDirectory(datasetReorganizedDir);
const documentsDir = path.join(datasetReorganizedDir, "documents");
fs.mkdirSync(documentsDir);
const dossiersDir = path.join(datasetReorganizedDir, "dossiers");
fs.mkdirSync(dossiersDir);
switch (dataset.structure) {
case DatasetStructure.SingleFile:
{
const originalJson = fs.readFileSync(originalJsonDirectoryOrFilePath, {
encoding: "utf8"
});
const originalData = validateRawData ? DossiersLegislatifsRawConvert.toDossiersLegislatifs(originalJson) : JSON.parse(originalJson);
for (const document of originalData.export.textesLegislatifs.document) {
writeReorganizedJson(documentsDir, document, pathFromDocumentUid);
}
for (const {
dossierParlementaire
} of originalData.export.dossiersLegislatifs.dossier) {
writeReorganizedJson(dossiersDir, dossierParlementaire, pathFromDossierParlementaireUid);
}
}
break;
case DatasetStructure.SegmentedFiles:
{
for (const documentSplitPath of walkDir(originalJsonDirectoryOrFilePath, ["document"])) {
const documentFilename = documentSplitPath[documentSplitPath.length - 1];
if (!documentFilename.endsWith(".json")) {
continue;
}
const documentOriginalFilePath = path.join(originalJsonDirectoryOrFilePath, ...documentSplitPath);
if (options.verbose) {
console.log(` Reorganizing "document" file: ${documentOriginalFilePath}…`);
}
const documentOriginalJson = fs.readFileSync(documentOriginalFilePath, {
encoding: "utf8"
});
const documentOriginal = validateRawData ? DossiersLegislatifsRawConvert.toDocument(documentOriginalJson) : JSON.parse(documentOriginalJson);
const document = documentOriginal.document;
writeReorganizedJson(documentsDir, document, pathFromDocumentUid);
}
for (const dossierParlementaireSplitPath of walkDir(originalJsonDirectoryOrFilePath, ["dossierParlementaire"])) {
const dossierParlementaireFilename = dossierParlementaireSplitPath[dossierParlementaireSplitPath.length - 1];
if (!dossierParlementaireFilename.endsWith(".json")) {
continue;
}
const dossierParlementaireOriginalFilePath = path.join(originalJsonDirectoryOrFilePath, ...dossierParlementaireSplitPath);
if (options.verbose) {
console.log(` Reorganizing "dossierParlementaire" file: ${dossierParlementaireOriginalFilePath}…`);
}
const dossierParlementaireOriginalJson = fs.readFileSync(dossierParlementaireOriginalFilePath, {
encoding: "utf8"
});
const dossierParlementaireOriginal = validateRawData ? DossiersLegislatifsRawConvert.toDossierParlementaire(dossierParlementaireOriginalJson) : JSON.parse(dossierParlementaireOriginalJson);
const dossierParlementaire = dossierParlementaireOriginal.dossierParlementaire;
writeReorganizedJson(dossiersDir, dossierParlementaire, pathFromDossierParlementaireUid);
}
}
break;
}
commitGit(datasetReorganizedDir, options);
}
}
if (enabledDatasets & EnabledDatasets.Scrutins) {
for (let dataset of chosenDatasets.scrutins) {
const originalJsonDirectoryOrFilePath = path.join(dataDir, dataset.filename);
if (!options.silent) {
console.log(`Reorganizing ${originalJsonDirectoryOrFilePath}`);
}
const datasetName = path.basename(originalJsonDirectoryOrFilePath, ".json");
cloneGit(gitGroupUrl, datasetName, dataDir);
const datasetReorganizedDir = path.join(dataDir, datasetName);
clearDirectory(datasetReorganizedDir);
switch (dataset.structure) {
case DatasetStructure.SingleFile:
{
const originalJson = fs.readFileSync(originalJsonDirectoryOrFilePath, {
encoding: "utf8"
});
const originalData = validateRawData ? ScrutinsRawConvert.toScrutins(originalJson) : JSON.parse(originalJson);
for (const scrutin of originalData.scrutins.scrutin) {
writeReorganizedJson(datasetReorganizedDir, scrutin, pathFromScrutinUid);
}
}
break;
case DatasetStructure.SegmentedFiles:
{
for (const scrutinSplitPath of walkDir(originalJsonDirectoryOrFilePath)) {
const scrutinFilename = scrutinSplitPath[scrutinSplitPath.length - 1];
if (!scrutinFilename.endsWith(".json")) {
continue;
}
const scrutinOriginalFilePath = path.join(originalJsonDirectoryOrFilePath, ...scrutinSplitPath);
if (options.verbose) {
console.log(` Reorganizing "scrutin" file: ${scrutinOriginalFilePath}…`);
}
const scrutinOriginalJson = fs.readFileSync(scrutinOriginalFilePath, {
encoding: "utf8"
});
const scrutinOriginal = validateRawData ? ScrutinsRawConvert.toScrutinWrapper(scrutinOriginalJson) : JSON.parse(scrutinOriginalJson);
const scrutin = scrutinOriginal.scrutin;
writeReorganizedJson(datasetReorganizedDir, scrutin, pathFromScrutinUid);
}
}
break;
}
commitGit(datasetReorganizedDir, options);
}
}
if (enabledDatasets & EnabledDatasets.Questions) {
for (const dataset of datasets.questions) {
const originalJsonDirectoryOrFilePath = path.join(dataDir, dataset.filename);
if (!options.silent) {
console.log(`Reorganizing ${originalJsonDirectoryOrFilePath}`);
}
const datasetName = path.basename(originalJsonDirectoryOrFilePath, ".json");
cloneGit(gitGroupUrl, datasetName, dataDir);
const datasetReorganizedDir = path.join(dataDir, datasetName);
clearDirectory(datasetReorganizedDir);
switch (dataset.structure) {
case DatasetStructure.SegmentedFiles:
{
for (const questionSplitPath of walkDir(originalJsonDirectoryOrFilePath)) {
const questionFilename = questionSplitPath[questionSplitPath.length - 1];
if (!questionFilename.endsWith(".json")) {
continue;
}
const questionOriginalFilePath = path.join(originalJsonDirectoryOrFilePath, ...questionSplitPath);
if (options.verbose) {
console.log(` Reorganizing "question" file: ${questionOriginalFilePath}…`);
}
const questionOriginalJson = fs.readFileSync(questionOriginalFilePath, {
encoding: "utf8"
});
const questionOriginal = validateRawData ? QuestionsRawConvert.toQuestionWrapper(questionOriginalJson) : JSON.parse(questionOriginalJson);
const question = questionOriginal.question;
writeReorganizedJson(datasetReorganizedDir, question, pathFromQuestionUid);
}
}
break;
}
commitGit(datasetReorganizedDir, options);
}
}
if (enabledDatasets & EnabledDatasets.ComptesRendusSeances) {
for (const dataset of datasets.comptesRendusSeances) {
const originalJsonDirectoryOrFilePath = path.join(dataDir, dataset.filename);
if (!options.silent) {
console.log(`Reorganizing ${originalJsonDirectoryOrFilePath}`);
}
const datasetName = path.basename(originalJsonDirectoryOrFilePath, ".xml");
cloneGit(gitGroupUrl, datasetName, dataDir);
const datasetReorganizedDir = path.join(dataDir, datasetName);
clearDirectory(datasetReorganizedDir);
switch (dataset.structure) {
case DatasetStructure.SegmentedFiles:
{
for (const compteRenduSeanceSplitPath of walkDir(originalJsonDirectoryOrFilePath)) {
const compteRenduSeanceFilename = compteRenduSeanceSplitPath[compteRenduSeanceSplitPath.length - 1];
if (!compteRenduSeanceFilename.endsWith(".json")) {
continue;
}
const compteRenduSeanceOriginalFilePath = path.join(originalJsonDirectoryOrFilePath, ...compteRenduSeanceSplitPath);
if (options.verbose) {
console.log(` Reorganizing "compteRenduSeance" file: ${compteRenduSeanceOriginalFilePath}…`);
}
const compteRenduSeanceOriginalJson = fs.readFileSync(compteRenduSeanceOriginalFilePath, {
encoding: "utf8"
});
const compteRenduSeanceOriginal = validateRawData ? ComptesRendusRawConvert.toCompteRenduWrapper(compteRenduSeanceOriginalJson) : JSON.parse(compteRenduSeanceOriginalJson);
const compteRenduSeance = compteRenduSeanceOriginal.compteRendu;
writeReorganizedJson(datasetReorganizedDir, compteRenduSeance, pathFromCompteRenduUid);
}
}
break;
}
commitGit(datasetReorganizedDir, options);
}
}
// Restore standard conversion of dates to JSON.
Date.prototype.toJSON = existingDateToJson;
return exitCode;
}
function writeAmendementReorganizedJson(datasetReorganizedDir, amendement) {
const uidTexteLegislatif = amendement.texteLegislatifRef;
assert.notStrictEqual(uidTexteLegislatif, undefined);
const texteLegislatifDir = path.join(datasetReorganizedDir, uidTexteLegislatif);
fs.ensureDirSync(texteLegislatifDir);
const reorganizedJsonFilePath = path.join(texteLegislatifDir, amendement.uid + ".json");
fs.writeFileSync(reorganizedJsonFilePath, JSON.stringify(amendement, null, 2), {
encoding: "utf8"
});
}
function writeReorganizedJson(dir, obj, pathFromUid) {
const basename = pathFromUid(dir, obj.uid);
fs.writeFileSync(`${basename}.json`, JSON.stringify(obj, null, 2), {
encoding: "utf8"
});
}
function main(argv) {
const options = parseArgs(argv);
return reorganizeData(options);
}
/* istanbul ignore if */
if (process.argv[1].endsWith("reorganize_data.ts")) process.exit(main(process.argv));
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhc3NlcnQiLCJjb21tYW5kTGluZUFyZ3MiLCJmcyIsInBhdGgiLCJkYXRhc2V0cyIsIkRhdGFzZXRTdHJ1Y3R1cmUiLCJFbmFibGVkRGF0YXNldHMiLCJnZXRFbmFibGVkRGF0YXNldHMiLCJmaWx0ZXJCeUxlZ2lzbGF0dXJlIiwiZXhpc3RpbmdEYXRlVG9Kc29uIiwicGF0Y2hlZERhdGVUb0pzb24iLCJnaXQiLCJ3YWxrRGlyIiwicGF0aEZyb21Db21wdGVSZW5kdVVpZCIsInBhdGhGcm9tRG9jdW1lbnRVaWQiLCJwYXRoRnJvbURvc3NpZXJQYXJsZW1lbnRhaXJlVWlkIiwicGF0aEZyb21RdWVzdGlvblVpZCIsInBhdGhGcm9tUmV1bmlvblVpZCIsInBhdGhGcm9tU2NydXRpblVpZCIsIkNvbnZlcnQiLCJBY3RldXJzRXRPcmdhbmVzUmF3Q29udmVydCIsIkFnZW5kYXNSYXdDb252ZXJ0IiwiQW1lbmRlbWVudHNSYXdDb252ZXJ0IiwiRG9zc2llcnNMZWdpc2xhdGlmc1Jhd0NvbnZlcnQiLCJTY3J1dGluc1Jhd0NvbnZlcnQiLCJRdWVzdGlvbnNSYXdDb252ZXJ0IiwiQ29tcHRlc1JlbmR1c1Jhd0NvbnZlcnQiLCJjbG9uZU9wdGlvbiIsImNvbW1pdE9wdGlvbiIsImNhdGVnb3JpZXNPcHRpb24iLCJsZWdpc2xhdHVyZU9wdGlvbiIsInJlbW90ZU9wdGlvbiIsInNpbGVudE9wdGlvbiIsInZlcmJvc2VPcHRpb24iLCJkYXRhRGlyRGVmYXVsdE9wdGlvbiIsInB1bGxPcHRpb24iLCJmZXRjaERvY3VtZW50c09wdGlvbiIsInBhcnNlRG9jdW1lbnRzT3B0aW9uIiwiZG9jdW1lbnRGb3JtYXRPcHRpb24iLCJwYXJzZUFyZ3MiLCJhcmd2Iiwib3B0aW9uc0RlZmluaXRpb25zIiwiYWxpYXMiLCJoZWxwIiwibmFtZSIsInR5cGUiLCJCb29sZWFuIiwib3B0aW9ucyIsImNsZWFyRGlyZWN0b3J5IiwiZGlyUGF0aCIsImVuc3VyZURpclN5bmMiLCJmaWxlbmFtZSIsInJlYWRkaXJTeW5jIiwic3RhcnRzV2l0aCIsInJlbW92ZVN5bmMiLCJqb2luIiwiY2xvbmVHaXQiLCJnaXRHcm91cFVybCIsImRhdGFzZXROYW1lIiwiZGF0YURpciIsImNsb25lIiwicmVvcmdhbml6ZURhdGEiLCJEYXRlIiwicHJvdG90eXBlIiwidG9KU09OIiwiZXhpdENvZGUiLCJjb21taXQiLCJ0cmltIiwicmVwbGFjZSIsInZhbGlkYXRlUmF3RGF0YSIsImVuYWJsZWREYXRhc2V0cyIsImNhdGVnb3JpZXMiLCJjaG9zZW5EYXRhc2V0cyIsImxlZ2lzbGF0dXJlIiwiY29tbWl0R2l0IiwiZGF0YXNldFJlb3JnYW5pemVkRGlyIiwiZXJyb3JDb2RlIiwiY29tbWl0QW5kUHVzaCIsInJlbW90ZSIsIkFjdGV1cnNFdE9yZ2FuZXMiLCJkYXRhc2V0IiwiYWN0ZXVyc0V0T3JnYW5lcyIsIm9yaWdpbmFsSnNvbkRpcmVjdG9yeU9yRmlsZVBhdGgiLCJzaWxlbnQiLCJjb25zb2xlIiwibG9nIiwiYmFzZW5hbWUiLCJwdWxsIiwicmVzZXRBbmRQdWxsIiwiYWN0ZXVyc1Jlb3JnYW5pemVkRGlyIiwibWtkaXJTeW5jIiwib3JnYW5lc1Jlb3JnYW5pemVkRGlyIiwic3RydWN0dXJlIiwiU2luZ2xlRmlsZSIsIm9yaWdpbmFsSnNvbiIsInJlYWRGaWxlU3luYyIsImVuY29kaW5nIiwib3JpZ2luYWxEYXRhIiwidG9BY3RldXJzRXRPcmdhbmVzIiwiSlNPTiIsInBhcnNlIiwiYWN0ZXVyIiwiZXhwb3J0IiwiYWN0ZXVycyIsInJlb3JnYW5pemVkSnNvbkZpbGVQYXRoIiwidWlkIiwid3JpdGVGaWxlU3luYyIsInN0cmluZ2lmeSIsIm9yZ2FuZSIsIm9yZ2FuZXMiLCJTZWdtZW50ZWRGaWxlcyIsImRlcG9ydHNCeUFjdGV1clVpZCIsImV4aXN0c1N5bmMiLCJkZXBvcnRTcGxpdFBhdGgiLCJkZXBvcnRGaWxlbmFtZSIsImxlbmd0aCIsImVuZHNXaXRoIiwiZGVwb3J0T3JpZ2luYWxGaWxlUGF0aCIsInZlcmJvc2UiLCJkZXBvcnRPcmlnaW5hbEpzb24iLCJkZXBvcnRPcmlnaW5hbCIsInRvRGVwb3J0IiwiZGVwb3J0IiwiZGVwb3J0cyIsImFjdGV1clJlZiIsInVuZGVmaW5lZCIsInB1c2giLCJPYmplY3QiLCJ2YWx1ZXMiLCJzb3J0IiwiYSIsImIiLCJsb2NhbGVDb21wYXJlIiwibWFuZGF0c0J5QWN0ZXVyVWlkIiwibWFuZGF0U3BsaXRQYXRoIiwibWFuZGF0RmlsZW5hbWUiLCJtYW5kYXRPcmlnaW5hbEZpbGVQYXRoIiwibWFuZGF0T3JpZ2luYWxKc29uIiwibWFuZGF0T3JpZ2luYWwiLCJ0b01hbmRhdFdyYXBwZXIiLCJtYW5kYXQiLCJtYW5kYXRzIiwiYWN0ZXVyU3BsaXRQYXRoIiwiYWN0ZXVyRmlsZW5hbWUiLCJhY3RldXJPcmlnaW5hbEZpbGVQYXRoIiwiYWN0ZXVyT3JpZ2luYWxKc29uIiwiYWN0ZXVyT3JpZ2luYWwiLCJ0b0FjdGV1ciIsImFjdGV1clVpZCIsInN0cmljdEVxdWFsIiwiYWN0ZXVyUmVvcmdhbml6ZWRKc29uRmlsZVBhdGgiLCJvcmdhbmVTcGxpdFBhdGgiLCJvcmdhbmVGaWxlbmFtZSIsIm9yZ2FuZU9yaWdpbmFsRmlsZVBhdGgiLCJvcmdhbmVPcmlnaW5hbEpzb24iLCJvcmdhbmVPcmlnaW5hbCIsInRvT3JnYW5lIiwib3JnYW5lUmVvcmdhbml6ZWRKc29uRmlsZVBhdGgiLCJBZ2VuZGFzIiwiYWdlbmRhcyIsInRvQWdlbmRhcyIsInJldW5pb24iLCJyZXVuaW9ucyIsIndyaXRlUmVvcmdhbml6ZWRKc29uIiwicmV1bmlvblNwbGl0UGF0aCIsInJldW5pb25GaWxlbmFtZSIsInJldW5pb25PcmlnaW5hbEZpbGVQYXRoIiwicmV1bmlvbk9yaWdpbmFsSnNvbiIsInJldW5pb25PcmlnaW5hbCIsInRvUmV1bmlvbiIsIkFtZW5kZW1lbnRzIiwiYW1lbmRlbWVudHMiLCJ0b0FtZW5kZW1lbnRzIiwidGV4dGVMZWdpc2xhdGlmIiwidGV4dGVzRXRBbWVuZGVtZW50cyIsInRleHRlbGVnIiwiYW1lbmRlbWVudCIsIndyaXRlQW1lbmRlbWVudFJlb3JnYW5pemVkSnNvbiIsImFtZW5kZW1lbnRTcGxpdFBhdGgiLCJhbWVuZGVtZW50RmlsZW5hbWUiLCJhbWVuZGVtZW50T3JpZ2luYWxGaWxlUGF0aCIsImFtZW5kZW1lbnRPcmlnaW5hbEpzb24iLCJhbWVuZGVtZW50T3JpZ2luYWwiLCJ0b0FtZW5kZW1lbnRXcmFwcGVyIiwiRG9zc2llcnNMZWdpc2xhdGlmcyIsImRvc3NpZXJzTGVnaXNsYXRpZnMiLCJkb2N1bWVudHNEaXIiLCJkb3NzaWVyc0RpciIsInRvRG9zc2llcnNMZWdpc2xhdGlmcyIsImRvY3VtZW50IiwidGV4dGVzTGVnaXNsYXRpZnMiLCJkb3NzaWVyUGFybGVtZW50YWlyZSIsImRvc3NpZXIiLCJkb2N1bWVudFNwbGl0UGF0aCIsImRvY3VtZW50RmlsZW5hbWUiLCJkb2N1bWVudE9yaWdpbmFsRmlsZVBhdGgiLCJkb2N1bWVudE9yaWdpbmFsSnNvbiIsImRvY3VtZW50T3JpZ2luYWwiLCJ0b0RvY3VtZW50IiwiZG9zc2llclBhcmxlbWVudGFpcmVTcGxpdFBhdGgiLCJkb3NzaWVyUGFybGVtZW50YWlyZUZpbGVuYW1lIiwiZG9zc2llclBhcmxlbWVudGFpcmVPcmlnaW5hbEZpbGVQYXRoIiwiZG9zc2llclBhcmxlbWVudGFpcmVPcmlnaW5hbEpzb24iLCJkb3NzaWVyUGFybGVtZW50YWlyZU9yaWdpbmFsIiwidG9Eb3NzaWVyUGFybGVtZW50YWlyZSIsIlNjcnV0aW5zIiwic2NydXRpbnMiLCJ0b1NjcnV0aW5zIiwic2NydXRpbiIsInNjcnV0aW5TcGxpdFBhdGgiLCJzY3J1dGluRmlsZW5hbWUiLCJzY3J1dGluT3JpZ2luYWxGaWxlUGF0aCIsInNjcnV0aW5PcmlnaW5hbEpzb24iLCJzY3J1dGluT3JpZ2luYWwiLCJ0b1NjcnV0aW5XcmFwcGVyIiwiUXVlc3Rpb25zIiwicXVlc3Rpb25zIiwicXVlc3Rpb25TcGxpdFBhdGgiLCJxdWVzdGlvbkZpbGVuYW1lIiwicXVlc3Rpb25PcmlnaW5hbEZpbGVQYXRoIiwicXVlc3Rpb25PcmlnaW5hbEpzb24iLCJxdWVzdGlvbk9yaWdpbmFsIiwidG9RdWVzdGlvbldyYXBwZXIiLCJxdWVzdGlvbiIsIkNvbXB0ZXNSZW5kdXNTZWFuY2VzIiwiY29tcHRlc1JlbmR1c1NlYW5jZXMiLCJjb21wdGVSZW5kdVNlYW5jZVNwbGl0UGF0aCIsImNvbXB0ZVJlbmR1U2VhbmNlRmlsZW5hbWUiLCJjb21wdGVSZW5kdVNlYW5jZU9yaWdpbmFsRmlsZVBhdGgiLCJjb21wdGVSZW5kdVNlYW5jZU9yaWdpbmFsSnNvbiIsImNvbXB0ZVJlbmR1U2VhbmNlT3JpZ2luYWwiLCJ0b0NvbXB0ZVJlbmR1V3JhcHBlciIsImNvbXB0ZVJlbmR1U2VhbmNlIiwiY29tcHRlUmVuZHUiLCJ1aWRUZXh0ZUxlZ2lzbGF0aWYiLCJ0ZXh0ZUxlZ2lzbGF0aWZSZWYiLCJub3RTdHJpY3RFcXVhbCIsInRleHRlTGVnaXNsYXRpZkRpciIsImRpciIsIm9iaiIsInBhdGhGcm9tVWlkIiwibWFpbiIsInByb2Nlc3MiLCJleGl0Il0sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NjcmlwdHMvcmVvcmdhbml6ZV9kYXRhLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBhc3NlcnQgZnJvbSBcImFzc2VydFwiXG5pbXBvcnQgY29tbWFuZExpbmVBcmdzIGZyb20gXCJjb21tYW5kLWxpbmUtYXJnc1wiXG5pbXBvcnQgZnMgZnJvbSBcImZzLWV4dHJhXCJcbmltcG9ydCBwYXRoIGZyb20gXCJwYXRoXCJcbmltcG9ydCB7XG4gIGRhdGFzZXRzLFxuICBEYXRhc2V0U3RydWN0dXJlLFxuICBFbmFibGVkRGF0YXNldHMsXG4gIGdldEVuYWJsZWREYXRhc2V0cyxcbiAgZmlsdGVyQnlMZWdpc2xhdHVyZSxcbn0gZnJvbSBcIi4uL2RhdGFzZXRzXCJcbmltcG9ydCB7IGV4aXN0aW5nRGF0ZVRvSnNvbiwgcGF0Y2hlZERhdGVUb0pzb24gfSBmcm9tIFwiLi4vZGF0ZXNcIlxuaW1wb3J0ICogYXMgZ2l0IGZyb20gXCIuLi9naXRcIlxuaW1wb3J0IHsgd2Fsa0RpciB9IGZyb20gXCIuLi9maWxlX3N5c3RlbXNcIlxuaW1wb3J0IHtcbiAgcGF0aEZyb21Db21wdGVSZW5kdVVpZCxcbiAgcGF0aEZyb21Eb2N1bWVudFVpZCxcbiAgcGF0aEZyb21Eb3NzaWVyUGFybGVtZW50YWlyZVVpZCxcbiAgcGF0aEZyb21RdWVzdGlvblVpZCxcbiAgcGF0aEZyb21SZXVuaW9uVWlkLFxuICBwYXRoRnJvbVNjcnV0aW5VaWQsXG59IGZyb20gXCIuLi9sb2FkZXJzXCJcbmltcG9ydCB7IENvbnZlcnQgYXMgQWN0ZXVyc0V0T3JnYW5lc1Jhd0NvbnZlcnQgfSBmcm9tIFwiLi4vcmF3X3R5cGVzL2FjdGV1cnNfZXRfb3JnYW5lc1wiXG5pbXBvcnQgeyBDb252ZXJ0IGFzIEFnZW5kYXNSYXdDb252ZXJ0IH0gZnJvbSBcIi4uL3Jhd190eXBlcy9hZ2VuZGFzXCJcbmltcG9ydCB7IENvbnZlcnQgYXMgQW1lbmRlbWVudHNSYXdDb252ZXJ0IH0gZnJvbSBcIi4uL3Jhd190eXBlcy9hbWVuZGVtZW50c1wiXG5pbXBvcnQgeyBDb252ZXJ0IGFzIERvc3NpZXJzTGVnaXNsYXRpZnNSYXdDb252ZXJ0IH0gZnJvbSBcIi4uL3Jhd190eXBlcy9kb3NzaWVyc19sZWdpc2xhdGlmc1wiXG5pbXBvcnQgeyBDb252ZXJ0IGFzIFNjcnV0aW5zUmF3Q29udmVydCB9IGZyb20gXCIuLi9yYXdfdHlwZXMvc2NydXRpbnNcIlxuaW1wb3J0IHsgQ29udmVydCBhcyBRdWVzdGlvbnNSYXdDb252ZXJ0IH0gZnJvbSBcIi4uL3Jhd190eXBlcy9xdWVzdGlvbnNcIlxuaW1wb3J0IHsgQ29udmVydCBhcyBDb21wdGVzUmVuZHVzUmF3Q29udmVydCB9IGZyb20gXCIuLi9yYXdfdHlwZXMvZGViYXRzXCJcbmltcG9ydCB7XG4gIGNsb25lT3B0aW9uLFxuICBjb21taXRPcHRpb24sXG4gIGNhdGVnb3JpZXNPcHRpb24sXG4gIGxlZ2lzbGF0dXJlT3B0aW9uLFxuICByZW1vdGVPcHRpb24sXG4gIHNpbGVudE9wdGlvbixcbiAgdmVyYm9zZU9wdGlvbixcbiAgZGF0YURpckRlZmF1bHRPcHRpb24sXG4gIHB1bGxPcHRpb24sXG4gIGZldGNoRG9jdW1lbnRzT3B0aW9uLFxuICBwYXJzZURvY3VtZW50c09wdGlvbixcbiAgZG9jdW1lbnRGb3JtYXRPcHRpb24sXG59IGZyb20gXCIuL3NoYXJlZC9jbGlfaGVscGVyc1wiXG5cbmZ1bmN0aW9uIHBhcnNlQXJncyhhcmd2OiBzdHJpbmdbXSk6IGFueSB7XG4gIGNvbnN0IG9wdGlvbnNEZWZpbml0aW9ucyA9IFtcbiAgICBjbG9uZU9wdGlvbixcbiAgICBjb21taXRPcHRpb24sXG4gICAgY2F0ZWdvcmllc09wdGlvbixcbiAgICBsZWdpc2xhdHVyZU9wdGlvbixcbiAgICByZW1vdGVPcHRpb24sXG4gICAgc2lsZW50T3B0aW9uLFxuICAgIHZlcmJvc2VPcHRpb24sXG4gICAgZGF0YURpckRlZmF1bHRPcHRpb24sXG4gICAgZmV0Y2hEb2N1bWVudHNPcHRpb24sXG4gICAgcGFyc2VEb2N1bWVudHNPcHRpb24sXG4gICAgZG9jdW1lbnRGb3JtYXRPcHRpb24sXG4gICAgcHVsbE9wdGlvbixcbiAgICB7XG4gICAgICBhbGlhczogXCJWXCIsXG4gICAgICBoZWxwOiBcImRvbid0IHZhbGlkYXRlIHJhdyBkYXRhXCIsXG4gICAgICBuYW1lOiBcIm5vLXZhbGlkYXRlLXJhd1wiLFxuICAgICAgdHlwZTogQm9vbGVhbixcbiAgICB9LFxuICBdXG4gIGNvbnN0IG9wdGlvbnMgPSBjb21tYW5kTGluZUFyZ3Mob3B0aW9uc0RlZmluaXRpb25zLCB7XG4gICAgYXJndjogYXJndixcbiAgfSlcbiAgcmV0dXJuIG9wdGlvbnNcbn1cblxuZnVuY3Rpb24gY2xlYXJEaXJlY3RvcnkoZGlyUGF0aDogc3RyaW5nKTogdm9pZCB7XG4gIGZzLmVuc3VyZURpclN5bmMoZGlyUGF0aClcbiAgZm9yIChjb25zdCBmaWxlbmFtZSBvZiBmcy5yZWFkZGlyU3luYyhkaXJQYXRoKSkge1xuICAgIGlmICghZmlsZW5hbWUuc3RhcnRzV2l0aChcIi5cIikpIHtcbiAgICAgIGZzLnJlbW92ZVN5bmMocGF0aC5qb2luKGRpclBhdGgsIGZpbGVuYW1lKSlcbiAgICB9XG4gIH1cbn1cblxuZnVuY3Rpb24gY2xvbmVHaXQoXG4gIGdpdEdyb3VwVXJsOiBzdHJpbmcgfCB1bmRlZmluZWQsXG4gIGRhdGFzZXROYW1lOiBzdHJpbmcsXG4gIGRhdGFEaXI6IHN0cmluZyxcbik6IHZvaWQge1xuICBnaXQuY2xvbmUoZ2l0R3JvdXBVcmwsIGRhdGFzZXROYW1lLCBkYXRhRGlyKVxufVxuXG5mdW5jdGlvbiByZW9yZ2FuaXplRGF0YShvcHRpb25zOiBhbnkpOiBudW1iZXIge1xuICBjb25zdCBkYXRhRGlyID0gb3B0aW9ucy5kYXRhRGlyXG4gIERhdGUucHJvdG90eXBlLnRvSlNPTiA9IHBhdGNoZWREYXRlVG9Kc29uXG4gIGxldCBleGl0Q29kZSA9IG9wdGlvbnMuY29tbWl0ID8gMTAgOiAwXG4gIGNvbnN0IGdpdEdyb3VwVXJsID0gb3B0aW9ucy5jbG9uZT8udHJpbSgpLnJlcGxhY2UoL1xcLyskLywgXCJcIilcbiAgY29uc3QgdmFsaWRhdGVSYXdEYXRhID0gIW9wdGlvbnNbXCJuby12YWxpZGF0ZS1yYXdcIl1cbiAgY29uc3QgZW5hYmxlZERhdGFzZXRzID0gZ2V0RW5hYmxlZERhdGFzZXRzKG9wdGlvbnMuY2F0ZWdvcmllcylcbiAgbGV0IGNob3NlbkRhdGFzZXRzID0gZmlsdGVyQnlMZWdpc2xhdHVyZShkYXRhc2V0cywgb3B0aW9ucy5sZWdpc2xhdHVyZSlcblxuICBmdW5jdGlvbiBjb21taXRHaXQoZGF0YXNldFJlb3JnYW5pemVkRGlyOiBzdHJpbmcsIG9wdGlvbnM6IGFueSk6IHZvaWQge1xuICAgIGlmIChvcHRpb25zLmNvbW1pdCkge1xuICAgICAgY29uc3QgZXJyb3JDb2RlID0gZ2l0LmNvbW1pdEFuZFB1c2goXG4gICAgICAgIGRhdGFzZXRSZW9yZ2FuaXplZERpcixcbiAgICAgICAgXCJOb3V2ZWxsZSBtb2lzc29uXCIsXG4gICAgICAgIG9wdGlvbnMucmVtb3RlLFxuICAgICAgKVxuICAgICAgaWYgKFxuICAgICAgICAoZXhpdENvZGUgPT09IDEwICYmIGVycm9yQ29kZSAhPT0gMTApIHx8XG4gICAgICAgIChleGl0Q29kZSA9PT0gMCAmJiBlcnJvckNvZGUgIT09IDAgJiYgZXJyb3JDb2RlICE9PSAxMClcbiAgICAgICkge1xuICAgICAgICBleGl0Q29kZSA9IGVycm9yQ29kZVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGlmIChlbmFibGVkRGF0YXNldHMgJiBFbmFibGVkRGF0YXNldHMuQWN0ZXVyc0V0T3JnYW5lcykge1xuICAgIGZvciAoY29uc3QgZGF0YXNldCBvZiBjaG9zZW5EYXRhc2V0cy5hY3RldXJzRXRPcmdhbmVzKSB7XG4gICAgICBjb25zdCBvcmlnaW5hbEpzb25EaXJlY3RvcnlPckZpbGVQYXRoOiBzdHJpbmcgPSBwYXRoLmpvaW4oXG4gICAgICAgIGRhdGFEaXIsXG4gICAgICAgIGRhdGFzZXQuZmlsZW5hbWUsXG4gICAgICApXG4gICAgICBpZiAoIW9wdGlvbnMuc2lsZW50KSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGBSZW9yZ2FuaXppbmcgJHtvcmlnaW5hbEpzb25EaXJlY3RvcnlPckZpbGVQYXRofWApXG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGRhdGFzZXROYW1lID0gcGF0aC5iYXNlbmFtZShcbiAgICAgICAgb3JpZ2luYWxKc29uRGlyZWN0b3J5T3JGaWxlUGF0aCxcbiAgICAgICAgXCIuanNvblwiLFxuICAgICAgKVxuICAgICAgY2xvbmVHaXQoZ2l0R3JvdXBVcmwsIGRhdGFzZXROYW1lLCBkYXRhRGlyKVxuXG4gICAgICBjb25zdCBkYXRhc2V0UmVvcmdhbml6ZWREaXIgPSBwYXRoLmpvaW4oZGF0YURpciwgZGF0YXNldE5hbWUpXG4gICAgICBpZiAob3B0aW9ucy5wdWxsKSB7XG4gICAgICAgIGdpdC5yZXNldEFuZFB1bGwoZGF0YXNldFJlb3JnYW5pemVkRGlyKVxuICAgICAgfVxuICAgICAgY2xlYXJEaXJlY3RvcnkoZGF0YXNldFJlb3JnYW5pemVkRGlyKVxuXG4gICAgICBjb25zdCBhY3RldXJzUmVvcmdhbml6ZWREaXIgPSBwYXRoLmpvaW4oZGF0YXNldFJlb3JnYW5pemVkRGlyLCBcImFjdGV1cnNcIilcbiAgICAgIGZzLm1rZGlyU3luYyhhY3RldXJzUmVvcmdhbml6ZWREaXIpXG4gICAgICBjb25zdCBvcmdhbmVzUmVvcmdhbml6ZWREaXIgPSBwYXRoLmpvaW4oZGF0YXNldFJlb3JnYW5pemVkRGlyLCBcIm9yZ2FuZXNcIilcbiAgICAgIGZzLm1rZGlyU3luYyhvcmdhbmVzUmVvcmdhbml6ZWREaXIpXG5cbiAgICAgIHN3aXRjaCAoZGF0YXNldC5zdHJ1Y3R1cmUpIHtcbiAgICAgICAgY2FzZSBEYXRhc2V0U3RydWN0dXJlLlNpbmdsZUZpbGU6XG4gICAgICAgICAge1xuICAgICAgICAgICAgY29uc3Qgb3JpZ2luYWxKc29uOiBzdHJpbmcgPSBmcy5yZWFkRmlsZVN5bmMoXG4gICAgICAgICAgICAgIG9yaWdpbmFsSnNvbkRpcmVjdG9yeU9yRmlsZVBhdGgsXG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBlbmNvZGluZzogXCJ1dGY4XCIsXG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICApXG4gICAgICAgICAgICBjb25zdCBvcmlnaW5hbERhdGE6IGFueSA9IHZhbGlkYXRlUmF3RGF0YVxuICAgICAgICAgICAgICA/IEFjdGV1cnNFdE9yZ2FuZXNSYXdDb252ZXJ0LnRvQWN0ZXVyc0V0T3JnYW5lcyhvcmlnaW5hbEpzb24pXG4gICAgICAgICAgICAgIDogSlNPTi5wYXJzZShvcmlnaW5hbEpzb24pXG5cbiAgICAgICAgICAgIGZvciAoY29uc3QgYWN0ZXVyIG9mIG9yaWdpbmFsRGF0YS5leHBvcnQuYWN0ZXVycy5hY3RldXIpIHtcbiAgICAgICAgICAgICAgY29uc3QgcmVvcmdhbml6ZWRKc29uRmlsZVBhdGg6IHN0cmluZyA9IHBhdGguam9pbihcbiAgICAgICAgICAgICAgICBhY3RldXJzUmVvcmdhbml6ZWREaXIsXG4gICAgICAgICAgICAgICAgYWN0ZXVyLnVpZFtcIiN0ZXh0XCJdICsgXCIuanNvblwiLFxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgIGZzLndyaXRlRmlsZVN5bmMoXG4gICAgICAgICAgICAgICAgcmVvcmdhbml6ZWRKc29uRmlsZVBhdGgsXG4gICAgICAgICAgICAgICAgSlNPTi5zdHJpbmdpZnkoYWN0ZXVyLCBudWxsLCAyKSxcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICBlbmNvZGluZzogXCJ1dGY4XCIsXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBmb3IgKGNvbnN0IG9yZ2FuZSBvZiBvcmlnaW5hbERhdGEuZXhwb3J0Lm9yZ2FuZXMub3JnYW5lKSB7XG4gICAgICAgICAgICAgIGNvbnN0IHJlb3JnYW5pemVkSnNvbkZpbGVQYXRoOiBzdHJpbmcgPSBwYXRoLmpvaW4oXG4gICAgICAgICAgICAgICAgb3JnYW5lc1Jlb3JnYW5pemVkRGlyLFxuICAgICAgICAgICAgICAgIG9yZ2FuZS51aWQgKyBcIi5qc29uXCIsXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgZnMud3JpdGVGaWxlU3luYyhcbiAgICAgICAgICAgICAgICByZW9yZ2FuaXplZEpzb25GaWxlUGF0aCxcbiAgICAgICAgICAgICAgICBKU09OLnN0cmluZ2lmeShvcmdhbmUsIG51bGwsIDIpLFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgIGVuY29kaW5nOiBcInV0ZjhcIixcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrXG4gICAgICAgIGNhc2UgRGF0YXNldFN0cnVjdHVyZS5TZWdtZW50ZWRGaWxlczpcbiAgICAgICAgICB7XG4gICAgICAgICAgICBjb25zdCBkZXBvcnRzQnlBY3RldXJVaWQ6IHtcbiAgICAgICAgICAgICAgW2FjdGV1clVpZDogc3RyaW5nXTogeyB1aWQ6IHN0cmluZyB9W11cbiAgICAgICAgICAgIH0gPSB7fVxuICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICBmcy5leGlzdHNTeW5jKFxuICAgICAgICAgICAgICAgIHBhdGguam9pbihvcmlnaW5hbEpzb25EaXJlY3RvcnlPckZpbGVQYXRoLCBcImRlcG9ydFwiKSxcbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgIGZvciAoY29uc3QgZGVwb3J0U3BsaXRQYXRoIG9mIHdhbGtEaXIoXG4gICAgICAgICAgICAgICAgb3JpZ2luYWxKc29uRGlyZWN0b3J5T3JGaWxlUGF0aCxcbiAgICAgICAgICAgICAgICBbXCJkZXBvcnRcIl0sXG4gICAgICAgICAgICAgICkpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBkZXBvcnRGaWxlbmFtZSA9XG4gICAgICAgICAgICAgICAgICBkZXBvcnRTcGxpdFBhdGhbZGVwb3J0U3BsaXRQYXRoLmxlbmd0aCAtIDFdXG4gICAgICAgICAgICAgICAgaWYgKCFkZXBvcnRGaWxlbmFtZS5lbmRzV2l0aChcIi5qc29uXCIpKSB7XG4gICAgICAgICAgICAgICAgICBjb250aW51ZVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBjb25zdCBkZXBvcnRPcmlnaW5hbEZpbGVQYXRoID0gcGF0aC5qb2luKFxuICAgICAgICAgICAgICAgICAgb3JpZ2luYWxKc29uRGlyZWN0b3J5T3JGaWxlUGF0aCxcbiAgICAgICAgICAgICAgICAgIC4uLmRlcG9ydFNwbGl0UGF0aCxcbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgaWYgKG9wdGlvbnMudmVyYm9zZSkge1xuICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgICAgIGAgIExvYWRpbmcgXCJkZXBvcnRcIiBmaWxlOiAke2RlcG9ydE9yaWdpbmFsRmlsZVBhdGh94oCmYCxcbiAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgY29uc3QgZGVwb3J0T3JpZ2luYWxKc29uOiBzdHJpbmcgPSBmcy5yZWFkRmlsZVN5bmMoXG4gICAgICAgICAgICAgICAgICBkZXBvcnRPcmlnaW5hbEZpbGVQYXRoLFxuICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBlbmNvZGluZzogXCJ1dGY4XCIsXG4gICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICBjb25zdCBkZXBvcnRPcmlnaW5hbDogYW55ID0gdmFsaWRhdGVSYXdEYXRhXG4gICAgICAgICAgICAgICAgICA/IEFjdGV1cnNFdE9yZ2FuZXNSYXdDb252ZXJ0LnRvRGVwb3J0KGRlcG9ydE9yaWdpbmFsSnNvbilcbiAgICAgICAgICAgICAgICAgIDogSlNPTi5wYXJzZShkZXBvcnRPcmlnaW5hbEpzb24pXG4gICAgICAgICAgICAgICAgY29uc3QgZGVwb3J0ID0gZGVwb3J0T3JpZ2luYWwuZGVwb3J0XG4gICAgICAgICAgICAgICAgbGV0IGRlcG9ydHMgPSBkZXBvcnRzQnlBY3RldXJVaWRbZGVwb3J0LmFjdGV1clJlZl1cbiAgICAgICAgICAgICAgICBpZiAoZGVwb3J0cyA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgICBkZXBvcnRzID0gZGVwb3J0c0J5QWN0ZXVyVWlkW2RlcG9ydC5hY3RldXJSZWZdID0gW11cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZGVwb3J0cy5wdXNoKGRlcG9ydClcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIGZvciAoY29uc3QgZGVwb3J0cyBvZiBPYmplY3QudmFsdWVzKGRlcG9ydHNCeUFjdGV1clVpZCkpIHtcbiAgICAgICAgICAgICAgICBkZXBvcnRzLnNvcnQoKGEsIGIpID0+XG4gICAgICAgICAgICAgICAgICBhLnVpZC5sZW5ndGggPT09IGIudWlkLmxlbmd0aFxuICAgICAgICAgICAgICAgICAgICA/IGEudWlkLmxvY2FsZUNvbXBhcmUoYi51aWQpXG4gICAgICAgICAgICAgICAgICAgIDogYS51aWQubGVuZ3RoIC0gYi51aWQubGVuZ3RoLFxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCBtYW5kYXRzQnlBY3RldXJVaWQ6IHtcbiAgICAgICAgICAgICAgW2FjdGV1clVpZDogc3RyaW5nXTogeyB1aWQ6IHN0cmluZyB9W11cbiAgICAgICAgICAgIH0gPSB7fVxuICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICBmcy5leGlzdHNTeW5jKFxuICAgICAgICAgICAgICAgIHBhdGguam9pbihvcmlnaW5hbEpzb25EaXJlY3RvcnlPckZpbGVQYXRoLCBcIm1hbmRhdFwiKSxcbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgIGZvciAoY29uc3QgbWFuZGF0U3BsaXRQYXRoIG9mIHdhbGtEaXIoXG4gICAgICAgICAgICAgICAgb3JpZ2luYWxKc29uRGlyZWN0b3J5T3JGaWxlUGF0aCxcbiAgICAgICAgICAgICAgICBbXCJtYW5kYXRcIl0sXG4gICAgICAgICAgICAgICkpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBtYW5kYXRGaWxlbmFtZSA9XG4gICAgICAgICAgICAgICAgICBtYW5kYXRTcGxpdFBhdGhbbWFuZGF0U3BsaXRQYXRoLmxlbmd0aCAtIDFdXG4gICAgICAgICAgICAgICAgaWYgKCFtYW5kYXRGaWxlbmFtZS5lbmRzV2l0aChcIi5qc29uXCIpKSB7XG4gICAgICAgICAgICAgICAgICBjb250aW51ZVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBjb25zdCBtYW5kYXRPcmlnaW5hbEZpbGVQYXRoID0gcGF0aC5qb2luKFxuICAgICAgICAgICAgICAgICAgb3JpZ2luYWxKc29uRGlyZWN0b3J5T3JGaWxlUGF0aCxcbiAgICAgICAgICAgICAgICAgIC4uLm1hbmRhdFNwbGl0UGF0aCxcbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgaWYgKG9wdGlvbnMudmVyYm9zZSkge1xuICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgICAgIGAgIExvYWRpbmcgXCJtYW5kYXRcIiBmaWxlOiAke21hbmRhdE9yaWdpbmFsRmlsZVBhdGh94oCmYCxcbiAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgY29uc3QgbWFuZGF0T3JpZ2luYWxKc29uOiBzdHJpbmcgPSBmcy5yZWFkRmlsZVN5bmMoXG4gICAgICAgICAgICAgICAgICBtYW5kYXRPcmlnaW5hbEZpbGVQYXRoLFxuICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBlbmNvZGluZzogXCJ1dGY4XCIsXG4gICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICBjb25zdCBtYW5kYXRPcmlnaW5hbDogYW55ID0gdmFsaWRhdGVSYXdEYXRhXG4gICAgICAgICAgICAgICAgICA/IEFjdGV1cnNFdE9yZ2FuZXNSYXdDb252ZXJ0LnRvTWFuZGF0V3JhcHBlcihcbiAgICAgICAgICAgICAgICAgICAgICBtYW5kYXRPcmlnaW5hbEpzb24sXG4gICAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICAgIDogSlNPTi5wYXJzZShtYW5kYXRPcmlnaW5hbEpzb24pXG4gICAgICAgICAgICAgICAgY29uc3QgbWFuZGF0ID0gbWFuZGF0T3JpZ2luYWwubWFuZGF0XG4gICAgICAgICAgICAgICAgbGV0IG1hbmRhdHMgPSBtYW5kYXRzQnlBY3RldXJVaWRbbWFuZGF0LmFjdGV1clJlZl1cbiAgICAgICAgICAgICAgICBpZiAobWFuZGF0cyA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgICBtYW5kYXRzID0gbWFuZGF0c0J5QWN0ZXVyVWlkW21hbmRhdC5hY3RldXJSZWZdID0gW11cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgbWFuZGF0cy5wdXNoKG1hbmRhdClcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIGZvciAoY29uc3QgbWFuZGF0cyBvZiBPYmplY3QudmFsdWVzKG1hbmRhdHNCeUFjdGV1clVpZCkpIHtcbiAgICAgICAgICAgICAgICBtYW5kYXRzLnNvcnQoKGEsIGIpID0+XG4gICAgICAgICAgICAgICAgICBhLnVpZC5sZW5ndGggPT09IGIudWlkLmxlbmd0aFxuICAgICAgICAgICAgICAgICAgICA/IGEudWlkLmxvY2FsZUNvbXBhcmUoYi51aWQpXG4gICAgICAgICAgICAgICAgICAgIDogYS51aWQubGVuZ3RoIC0gYi51aWQubGVuZ3RoLFxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBmb3IgKGNvbnN0IGFjdGV1clNwbGl0UGF0aCBvZiB3YWxrRGlyKFxuICAgICAgICAgICAgICBvcmlnaW5hbEpzb25EaXJlY3RvcnlPckZpbGVQYXRoLFxuICAgICAgICAgICAgICBbXCJhY3RldXJcIl0sXG4gICAgICAgICAgICApKSB7XG4gICAgICAgICAgICAgIGNvbnN0IGFjdGV1ckZpbGVuYW1lID0gYWN0ZXVyU3BsaXRQYXRoW2FjdGV1clNwbGl0UGF0aC5sZW5ndGggLSAxXVxuICAgICAgICAgICAgICBpZiAoIWFjdGV1ckZpbGVuYW1lLmVuZHNXaXRoKFwiLmpzb25cIikpIHtcbiAgICAgICAgICAgICAgICBjb250aW51ZVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGNvbnN0IGFjdGV1ck9yaWdpbmFsRmlsZVBhdGggPSBwYXRoLmpvaW4oXG4gICAgICAgICAgICAgICAgb3JpZ2luYWxKc29uRGlyZWN0b3J5T3JGaWxlUGF0aCxcbiAgICAgICAgICAgICAgICAuLi5hY3RldXJTcGxpdFBhdGgsXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgaWYgKG9wdGlvbnMudmVyYm9zZSkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgICAgYCAgUmVvcmdhbml6aW5nIFwiYWN0ZXVyXCIgZmlsZTogJHthY3RldXJPcmlnaW5hbEZpbGVQYXRofeKApmAsXG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGNvbnN0IGFjdGV1ck9yaWdpbmFsSnNvbjogc3RyaW5nID0gZnMucmVhZEZpbGVTeW5jKFxuICAgICAgICAgICAgICAgIGFjdGV1ck9yaWdpbmFsRmlsZVBhdGgsXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgZW5jb2Rpbmc6IFwidXRmOFwiLFxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgY29uc3QgYWN0ZXVyT3JpZ2luYWw6IGFueSA9IHZhbGlkYXRlUmF3RGF0YVxuICAgICAgICAgICAgICAgID8gQWN0ZXVyc0V0T3JnYW5lc1Jhd0NvbnZlcnQudG9BY3RldXIoYWN0ZXVyT3JpZ2luYWxKc29uKVxuICAgICAgICAgICAgICAgIDogSlNPTi5wYXJzZShhY3RldXJPcmlnaW5hbEpzb24pXG4gICAgICAgICAgICAgIGNvbnN0IGFjdGV1ciA9IGFjdGV1ck9yaWdpbmFsLmFjdGV1clxuICAgICAgICAgICAgICBjb25zdCBhY3RldXJVaWQgPSBhY3RldXIudWlkW1wiI3RleHRcIl1cbiAgICAgICAgICAgICAgY29uc3QgZGVwb3J0cyA9IGRlcG9ydHNCeUFjdGV1clVpZFthY3RldXJVaWRdXG4gICAgICAgICAgICAgIGlmIChkZXBvcnRzICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICBhc3NlcnQuc3RyaWN0RXF1YWwoYWN0ZXVyLmRlcG9ydHMsIHVuZGVmaW5lZClcbiAgICAgICAgICAgICAgICBhY3RldXIuZGVwb3J0cyA9IHsgZGVwb3J0OiBkZXBvcnRzIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBjb25zdCBtYW5kYXRzID0gbWFuZGF0c0J5QWN0ZXVyVWlkW2FjdGV1clVpZF1cbiAgICAgICAgICAgICAgaWYgKG1hbmRhdHMgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIGFzc2VydC5zdHJpY3RFcXVhbChhY3RldXIubWFuZGF0cywgdW5kZWZpbmVkKVxuICAgICAgICAgICAgICAgIGFjdGV1ci5tYW5kYXRzID0geyBtYW5kYXQ6IG1hbmRhdHMgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGNvbnN0IGFjdGV1clJlb3JnYW5pemVkSnNvbkZpbGVQYXRoOiBzdHJpbmcgPSBwYXRoLmpvaW4oXG4gICAgICAgICAgICAgICAgYWN0ZXVyc1Jlb3JnYW5pemVkRGlyLFxuICAgICAgICAgICAgICAgIGFjdGV1clVpZCArIFwiLmpzb25cIixcbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICBmcy53cml0ZUZpbGVTeW5jKFxuICAgICAgICAgICAgICAgIGFjdGV1clJlb3JnYW5pemVkSnNvbkZpbGVQYXRoLFxuICAgICAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KGFjdGV1ciwgbnVsbCwgMiksXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgZW5jb2Rpbmc6IFwidXRmOFwiLFxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgZm9yIChjb25zdCBvcmdhbmVTcGxpdFBhdGggb2Ygd2Fsa0RpcihcbiAgICAgICAgICAgICAgb3JpZ2luYWxKc29uRGlyZWN0b3J5T3JGaWxlUGF0aCxcbiAgICAgICAgICAgICAgW1wib3JnYW5lXCJdLFxuICAgICAgICAgICAgKSkge1xuICAgICAgICAgICAgICBjb25zdCBvcmdhbmVGaWxlbmFtZSA9IG9yZ2FuZVNwbGl0UGF0aFtvcmdhbmVTcGxpdFBhdGgubGVuZ3RoIC0gMV1cbiAgICAgICAgICAgICAgaWYgKCFvcmdhbmVGaWxlbmFtZS5lbmRzV2l0aChcIi5qc29uXCIpKSB7XG4gICAgICAgICAgICAgICAgY29udGludWVcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBjb25zdCBvcmdhbmVPcmlnaW5hbEZpbGVQYXRoID0gcGF0aC5qb2luKFxuICAgICAgICAgICAgICAgIG9yaWdpbmFsSnNvbkRpcmVjdG9yeU9yRmlsZVBhdGgsXG4gICAgICAgICAgICAgICAgLi4ub3JnYW5lU3BsaXRQYXRoLFxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgIGlmIChvcHRpb25zLnZlcmJvc2UpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICAgIGAgIFJlb3JnYW5pemluZyBcIm9yZ2FuZVwiIGZpbGU6ICR7b3JnYW5lT3JpZ2luYWxGaWxlUGF0aH3igKZgLFxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBjb25zdCBvcmdhbmVPcmlnaW5hbEpzb246IHN0cmluZyA9IGZzLnJlYWRGaWxlU3luYyhcbiAgICAgICAgICAgICAgICBvcmdhbmVPcmlnaW5hbEZpbGVQYXRoLFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgIGVuY29kaW5nOiBcInV0ZjhcIixcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgIGNvbnN0IG9yZ2FuZU9yaWdpbmFsOiBhbnkgPSB2YWxpZGF0ZVJhd0RhdGFcbiAgICAgICAgICAgICAgICA/IEFjdGV1cnNFdE9yZ2FuZXNSYXdDb252ZXJ0LnRvT3JnYW5lKG9yZ2FuZU9yaWdpbmFsSnNvbilcbiAgICAgICAgICAgICAgICA6IEpTT04ucGFyc2Uob3JnYW5lT3JpZ2luYWxKc29uKVxuICAgICAgICAgICAgICBjb25zdCBvcmdhbmUgPSBvcmdhbmVPcmlnaW5hbC5vcmdhbmVcbiAgICAgICAgICAgICAgY29uc3Qgb3JnYW5lUmVvcmdhbml6ZWRKc29uRmlsZVBhdGg6IHN0cmluZyA9IHBhdGguam9pbihcbiAgICAgICAgICAgICAgICBvcmdhbmVzUmVvcmdhbml6ZWREaXIsXG4gICAgICAgICAgICAgICAgb3JnYW5lLnVpZCArIFwiLmpzb25cIixcbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICBmcy53cml0ZUZpbGVTeW5jKFxuICAgICAgICAgICAgICAgIG9yZ2FuZVJlb3JnYW5pemVkSnNvbkZpbGVQYXRoLFxuICAgICAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KG9yZ2FuZSwgbnVsbCwgMiksXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgZW5jb2Rpbmc6IFwidXRmOFwiLFxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICA