gitlab-ci-local
Version:
Tired of pushing to test your .gitlab-ci.yml?
268 lines • 52.3 kB
JavaScript
import { Utils } from "./utils.js";
import fs from "fs-extra";
import assert, { AssertionError } from "assert";
import chalk from "chalk";
import { Parser } from "./parser.js";
import axios from "axios";
import globby from "globby";
import path from "path";
export class ParserIncludes {
static count = 0;
static resetCount() {
this.count = 0;
}
static normalizeTriggerInclude(gitlabData, opts) {
const { writeStreams } = opts;
for (const [jobName, jobData] of Object.entries(gitlabData ?? {})) {
if (typeof jobData.trigger?.include === "string") {
jobData.trigger.include = [{
local: jobData.trigger.include,
}];
}
else if (jobData.trigger?.project) {
writeStreams.memoStdout(chalk `{bgYellowBright WARN } The job: \`{blueBright ${jobName}}\` will be no-op. Multi-project pipeline is not supported by gitlab-ci-local\n`);
}
}
}
static async init(gitlabData, opts) {
const { argv } = opts;
this.count++;
assert(this.count <= opts.maximumIncludes + 1, // 1st init call is not counted
chalk `This GitLab CI configuration is invalid: Maximum of {blueBright ${opts.maximumIncludes}} nested includes are allowed!. This limit can be increased with the --maximum-includes cli flags.`);
let includeDatas = [];
const promises = [];
const { stateDir, cwd, fetchIncludes, gitData, expandVariables } = opts;
const include = this.expandInclude(gitlabData?.include, opts.variables);
this.normalizeTriggerInclude(gitlabData, opts);
// Find files to fetch from remote and place in .gitlab-ci-local/includes
for (const value of include) {
if (value["rules"]) {
const include_rules = value["rules"];
const rulesResult = Utils.getRulesResult({ argv, cwd, rules: include_rules, variables: opts.variables }, gitData);
if (rulesResult.when === "never") {
continue;
}
}
if (value["local"]) {
validateIncludeLocal(value["local"]);
const files = await globby(value["local"].replace(/^\//, ""), { dot: true, cwd });
if (files.length == 0) {
throw new AssertionError({ message: `Local include file cannot be found ${value["local"]}` });
}
}
else if (value["file"]) {
for (const fileValue of Array.isArray(value["file"]) ? value["file"] : [value["file"]]) {
promises.push(this.downloadIncludeProjectFile(cwd, stateDir, value["project"], value["ref"] || "HEAD", fileValue, gitData, fetchIncludes));
}
}
else if (value["template"]) {
const { project, ref, file, domain } = this.covertTemplateToProjectFile(value["template"]);
const url = `https://${domain}/${project}/-/raw/${ref}/${file}`;
promises.push(this.downloadIncludeRemote(cwd, stateDir, url, fetchIncludes));
}
else if (value["remote"]) {
promises.push(this.downloadIncludeRemote(cwd, stateDir, value["remote"], fetchIncludes));
}
}
await Promise.all(promises);
for (const value of include) {
if (value["rules"]) {
const include_rules = value["rules"];
const rulesResult = Utils.getRulesResult({ argv, cwd, rules: include_rules, variables: opts.variables }, gitData);
if (rulesResult.when === "never") {
continue;
}
}
if (value["local"]) {
const files = await globby([value["local"].replace(/^\//, "")], { dot: true, cwd });
for (const localFile of files) {
const content = await Parser.loadYaml(`${cwd}/${localFile}`, { inputs: value.inputs || {} }, expandVariables);
includeDatas = includeDatas.concat(await this.init(content, opts));
}
}
else if (value["project"]) {
for (const fileValue of Array.isArray(value["file"]) ? value["file"] : [value["file"]]) {
const fileDoc = await Parser.loadYaml(`${cwd}/${stateDir}/includes/${gitData.remote.host}/${value["project"]}/${value["ref"] || "HEAD"}/${fileValue}`, { inputs: value.inputs || {} }, expandVariables);
// Expand local includes inside a "project"-like include
fileDoc["include"] = this.expandInclude(fileDoc["include"], opts.variables);
fileDoc["include"].forEach((inner, i) => {
if (!inner["local"])
return;
if (inner["rules"]) {
const rulesResult = Utils.getRulesResult({ argv, cwd: opts.cwd, variables: opts.variables, rules: inner["rules"] }, gitData);
if (rulesResult.when === "never") {
return;
}
}
fileDoc["include"][i] = {
project: value["project"],
file: inner["local"].replace(/^\//, ""),
ref: value["ref"],
inputs: inner.inputs || {},
};
});
includeDatas = includeDatas.concat(await this.init(fileDoc, opts));
}
}
else if (value["component"]) {
const { domain, port, projectPath, componentName, ref } = this.parseIncludeComponent(value["component"]);
// converts component to project. gitlab allows two different file path ways to include a component
let files = [`${componentName}.yml`, `${componentName}/template.yml`, null];
const isLocalComponent = projectPath === `${gitData.remote.group}/${gitData.remote.project}` && ref === gitData.commit.SHA;
// If a file is present locally, keep only that one in the files array to avoid downloading the other one that never exists
if (!argv.fetchIncludes) {
for (const f of files) {
const localFileName = `${cwd}/${stateDir}/includes/${gitData.remote.host}/${projectPath}/${ref}/${f}`;
if (fs.existsSync(localFileName)) {
files = [f];
break;
}
}
}
for (const f of files) {
assert(f !== null, `This GitLab CI configuration is invalid: component: \`${value["component"]}\`. One of the files [${files}] must exist in \`${domain}` +
(port ? `:${port}` : "") + `/${projectPath}\``);
if (isLocalComponent) {
const localComponentInclude = `${cwd}/${f}`;
if (!(await fs.pathExists(localComponentInclude))) {
continue;
}
const content = await Parser.loadYaml(localComponentInclude, { inputs: value.inputs || {} }, expandVariables);
includeDatas = includeDatas.concat(await this.init(content, opts));
break;
}
else {
const localFileName = `${cwd}/${stateDir}/includes/${gitData.remote.host}/${projectPath}/${ref}/${f}`;
// Check remotely only if the file does not exist locally
if (!fs.existsSync(localFileName) && !(await Utils.remoteFileExist(cwd, f, ref, domain, projectPath, gitData.remote.schema, gitData.remote.port))) {
continue;
}
const fileDoc = {
include: {
project: projectPath,
file: f,
ref: ref,
inputs: value.inputs || {},
},
};
includeDatas = includeDatas.concat(await this.init(fileDoc, opts));
break;
}
}
}
else if (value["template"]) {
const { project, ref, file, domain } = this.covertTemplateToProjectFile(value["template"]);
const fsUrl = Utils.fsUrl(`https://${domain}/${project}/-/raw/${ref}/${file}`);
const fileDoc = await Parser.loadYaml(`${cwd}/${stateDir}/includes/${fsUrl}`, { inputs: value.inputs || {} }, expandVariables);
includeDatas = includeDatas.concat(await this.init(fileDoc, opts));
}
else if (value["remote"]) {
const fsUrl = Utils.fsUrl(value["remote"]);
const fileDoc = await Parser.loadYaml(`${cwd}/${stateDir}/includes/${fsUrl}`, { inputs: value.inputs || {} }, expandVariables);
includeDatas = includeDatas.concat(await this.init(fileDoc, opts));
}
else {
throw new AssertionError({ message: `Didn't understand include ${JSON.stringify(value)}` });
}
}
includeDatas.push(gitlabData);
return includeDatas;
}
static expandInclude(i, variables) {
let include = i || [];
if (include && include.length == null) {
include = [i];
}
if (typeof include === "string") {
include = [include];
}
for (const [index, entry] of Object.entries(include)) {
if (typeof entry === "string" && (entry.startsWith("https:") || entry.startsWith("http:"))) {
include[index] = { "remote": entry };
}
else if (typeof entry === "string") {
include[index] = { "local": entry };
}
else {
include[index] = entry;
}
}
for (const entry of include) {
for (const [key, value] of Object.entries(entry)) {
entry[key] = Utils.expandText(value, variables);
}
}
return include;
}
static covertTemplateToProjectFile(template) {
return {
domain: "gitlab.com",
project: "gitlab-org/gitlab",
ref: "HEAD",
file: `lib/gitlab/ci/templates/${template}`,
};
}
static parseIncludeComponent(component) {
assert(!component.includes("://"), `This GitLab CI configuration is invalid: component: \`${component}\` should not contain protocol`);
// eslint-disable-next-line no-useless-escape
const pattern = /(?<domain>[^/:\s]+)(:(?<port>\d+))?\/(?<projectPath>.+)\/(?<componentName>[^@]+)@(?<ref>.+)/; // https://regexr.com/7v7hm
const gitRemoteMatch = pattern.exec(component);
if (gitRemoteMatch?.groups == null)
throw new Error(`This is a bug, please create a github issue if this is something you're expecting to work. input: ${component}`);
return {
domain: gitRemoteMatch.groups["domain"],
port: gitRemoteMatch.groups["port"],
projectPath: gitRemoteMatch.groups["projectPath"],
componentName: `templates/${gitRemoteMatch.groups["componentName"]}`,
ref: gitRemoteMatch.groups["ref"],
};
}
static async downloadIncludeRemote(cwd, stateDir, url, fetchIncludes) {
const fsUrl = Utils.fsUrl(url);
try {
const target = `${cwd}/${stateDir}/includes/${fsUrl}`;
if (await fs.pathExists(target) && !fetchIncludes)
return;
const res = await axios.get(url, { headers: { "User-Agent": "gitlab-ci-local" } });
await fs.outputFile(target, res.data);
}
catch (e) {
throw new AssertionError({ message: `Remote include could not be fetched ${url}\n${e}` });
}
}
static async downloadIncludeProjectFile(cwd, stateDir, project, ref, file, gitData, fetchIncludes) {
const remote = gitData.remote;
const normalizedFile = file.replace(/^\/+/, "");
try {
const target = `${stateDir}/includes/${remote.host}/${project}/${ref}`;
if (await fs.pathExists(`${cwd}/${target}/${normalizedFile}`) && !fetchIncludes)
return;
if (remote.schema.startsWith("http")) {
const ext = "tmp-" + Math.random();
await fs.mkdirp(path.dirname(`${cwd}/${target}/${normalizedFile}`));
const gitCloneBranch = (ref === "HEAD") ? "" : `--branch ${ref}`;
await Utils.bashMulti([
`cd ${cwd}/${stateDir}`,
`git clone ${gitCloneBranch} -n --depth=1 --filter=tree:0 ${remote.schema}://${remote.host}:${remote.port}/${project}.git ${cwd}/${target}.${ext}`,
`cd ${cwd}/${target}.${ext}`,
`git sparse-checkout set --no-cone ${normalizedFile}`,
"git checkout",
`cd ${cwd}/${stateDir}`,
`cp ${cwd}/${target}.${ext}/${normalizedFile} ${cwd}/${target}/${normalizedFile}`,
], cwd);
}
else {
await fs.mkdirp(`${cwd}/${target}`);
await Utils.bash(`set -eou pipefail; git archive --remote=ssh://git@${remote.host}:${remote.port}/${project}.git ${ref} ${normalizedFile} | tar -f - -xC ${target}/`, cwd);
}
}
catch (e) {
throw new AssertionError({ message: `Project include could not be fetched { project: ${project}, ref: ${ref}, file: ${normalizedFile} }\n${e}` });
}
}
}
export function validateIncludeLocal(filePath) {
assert(!filePath.startsWith("./"), `\`${filePath}\` for include:local is invalid. Gitlab does not support relative path (ie. cannot start with \`./\`).`);
assert(!filePath.includes(".."), `\`${filePath}\` for include:local is invalid. Gitlab does not support directory traversal.`);
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFyc2VyLWluY2x1ZGVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsicGFyc2VyLWluY2x1ZGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sRUFBQyxLQUFLLEVBQUMsTUFBTSxZQUFZLENBQUM7QUFDakMsT0FBTyxFQUFFLE1BQU0sVUFBVSxDQUFDO0FBRzFCLE9BQU8sTUFBTSxFQUFFLEVBQUMsY0FBYyxFQUFDLE1BQU0sUUFBUSxDQUFDO0FBQzlDLE9BQU8sS0FBSyxNQUFNLE9BQU8sQ0FBQztBQUMxQixPQUFPLEVBQUMsTUFBTSxFQUFDLE1BQU0sYUFBYSxDQUFDO0FBQ25DLE9BQU8sS0FBSyxNQUFNLE9BQU8sQ0FBQztBQUMxQixPQUFPLE1BQU0sTUFBTSxRQUFRLENBQUM7QUFDNUIsT0FBTyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBY3hCLE1BQU0sT0FBTyxjQUFjO0lBQ2YsTUFBTSxDQUFDLEtBQUssR0FBVyxDQUFDLENBQUM7SUFFakMsTUFBTSxDQUFDLFVBQVU7UUFDYixJQUFJLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQztJQUNuQixDQUFDO0lBRU8sTUFBTSxDQUFDLHVCQUF1QixDQUFFLFVBQWUsRUFBRSxJQUErQjtRQUNwRixNQUFNLEVBQUMsWUFBWSxFQUFDLEdBQUcsSUFBSSxDQUFDO1FBQzVCLEtBQUssTUFBTSxDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFNLFVBQVUsSUFBSSxFQUFFLENBQUMsRUFBRSxDQUFDO1lBQ3JFLElBQUksT0FBTyxPQUFPLENBQUMsT0FBTyxFQUFFLE9BQU8sS0FBSyxRQUFRLEVBQUUsQ0FBQztnQkFDL0MsT0FBTyxDQUFDLE9BQU8sQ0FBQyxPQUFPLEdBQUcsQ0FBQzt3QkFDdkIsS0FBSyxFQUFFLE9BQU8sQ0FBQyxPQUFPLENBQUMsT0FBTztxQkFDakMsQ0FBRSxDQUFDO1lBQ1IsQ0FBQztpQkFBTSxJQUFJLE9BQU8sQ0FBQyxPQUFPLEVBQUUsT0FBTyxFQUFFLENBQUM7Z0JBQ2xDLFlBQVksQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFBLGtEQUFrRCxPQUFPLGlGQUFpRixDQUFDLENBQUM7WUFDN0ssQ0FBQztRQUNMLENBQUM7SUFDTCxDQUFDO0lBRUQsTUFBTSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUUsVUFBZSxFQUFFLElBQStCO1FBQy9ELE1BQU0sRUFBQyxJQUFJLEVBQUMsR0FBRyxJQUFJLENBQUM7UUFDcEIsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ2IsTUFBTSxDQUNGLElBQUksQ0FBQyxLQUFLLElBQUksSUFBSSxDQUFDLGVBQWUsR0FBRyxDQUFDLEVBQUUsK0JBQStCO1FBQ3ZFLEtBQUssQ0FBQSxtRUFBbUUsSUFBSSxDQUFDLGVBQWUsb0dBQW9HLENBQ25NLENBQUM7UUFDRixJQUFJLFlBQVksR0FBVSxFQUFFLENBQUM7UUFDN0IsTUFBTSxRQUFRLEdBQUcsRUFBRSxDQUFDO1FBQ3BCLE1BQU0sRUFBQyxRQUFRLEVBQUUsR0FBRyxFQUFFLGFBQWEsRUFBRSxPQUFPLEVBQUUsZUFBZSxFQUFDLEdBQUcsSUFBSSxDQUFDO1FBRXRFLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsVUFBVSxFQUFFLE9BQU8sRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7UUFFeEUsSUFBSSxDQUFDLHVCQUF1QixDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsQ0FBQztRQUMvQyx5RUFBeUU7UUFDekUsS0FBSyxNQUFNLEtBQUssSUFBSSxPQUFPLEVBQUUsQ0FBQztZQUMxQixJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO2dCQUNqQixNQUFNLGFBQWEsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7Z0JBQ3JDLE1BQU0sV0FBVyxHQUFHLEtBQUssQ0FBQyxjQUFjLENBQUMsRUFBQyxJQUFJLEVBQUUsR0FBRyxFQUFFLEtBQUssRUFBRSxhQUFhLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxTQUFTLEVBQUMsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDaEgsSUFBSSxXQUFXLENBQUMsSUFBSSxLQUFLLE9BQU8sRUFBRSxDQUFDO29CQUMvQixTQUFTO2dCQUNiLENBQUM7WUFDTCxDQUFDO1lBQ0QsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztnQkFDakIsb0JBQW9CLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7Z0JBQ3JDLE1BQU0sS0FBSyxHQUFHLE1BQU0sTUFBTSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxFQUFFLEVBQUMsR0FBRyxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUMsQ0FBQyxDQUFDO2dCQUNoRixJQUFJLEtBQUssQ0FBQyxNQUFNLElBQUksQ0FBQyxFQUFFLENBQUM7b0JBQ3BCLE1BQU0sSUFBSSxjQUFjLENBQUMsRUFBQyxPQUFPLEVBQUUsc0NBQXNDLEtBQUssQ0FBQyxPQUFPLENBQUMsRUFBRSxFQUFDLENBQUMsQ0FBQztnQkFDaEcsQ0FBQztZQUNMLENBQUM7aUJBQU0sSUFBSSxLQUFLLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztnQkFDdkIsS0FBSyxNQUFNLFNBQVMsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsQ0FBQztvQkFDckYsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsMEJBQTBCLENBQUMsR0FBRyxFQUFFLFFBQVEsRUFBRSxLQUFLLENBQUMsU0FBUyxDQUFDLEVBQUUsS0FBSyxDQUFDLEtBQUssQ0FBQyxJQUFJLE1BQU0sRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLGFBQWEsQ0FBQyxDQUFDLENBQUM7Z0JBQy9JLENBQUM7WUFDTCxDQUFDO2lCQUFNLElBQUksS0FBSyxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUM7Z0JBQzNCLE1BQU0sRUFBQyxPQUFPLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUMsR0FBRyxJQUFJLENBQUMsMkJBQTJCLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUM7Z0JBQ3pGLE1BQU0sR0FBRyxHQUFHLFdBQVcsTUFBTSxJQUFJLE9BQU8sVUFBVSxHQUFHLElBQUksSUFBSSxFQUFFLENBQUM7Z0JBQ2hFLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLHFCQUFxQixDQUFDLEdBQUcsRUFBRSxRQUFRLEVBQUUsR0FBRyxFQUFFLGFBQWEsQ0FBQyxDQUFDLENBQUM7WUFDakYsQ0FBQztpQkFBTSxJQUFJLEtBQUssQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO2dCQUN6QixRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxxQkFBcUIsQ0FBQyxHQUFHLEVBQUUsUUFBUSxFQUFFLEtBQUssQ0FBQyxRQUFRLENBQUMsRUFBRSxhQUFhLENBQUMsQ0FBQyxDQUFDO1lBQzdGLENBQUM7UUFFTCxDQUFDO1FBRUQsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBRTVCLEtBQUssTUFBTSxLQUFLLElBQUksT0FBTyxFQUFFLENBQUM7WUFDMUIsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztnQkFDakIsTUFBTSxhQUFhLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO2dCQUNyQyxNQUFNLFdBQVcsR0FBRyxLQUFLLENBQUMsY0FBYyxDQUFDLEVBQUMsSUFBSSxFQUFFLEdBQUcsRUFBRSxLQUFLLEVBQUUsYUFBYSxFQUFFLFNBQVMsRUFBRSxJQUFJLENBQUMsU0FBUyxFQUFDLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ2hILElBQUksV0FBVyxDQUFDLElBQUksS0FBSyxPQUFPLEVBQUUsQ0FBQztvQkFDL0IsU0FBUztnQkFDYixDQUFDO1lBQ0wsQ0FBQztZQUNELElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7Z0JBQ2pCLE1BQU0sS0FBSyxHQUFHLE1BQU0sTUFBTSxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBRSxFQUFDLEdBQUcsRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFDLENBQUMsQ0FBQztnQkFDbEYsS0FBSyxNQUFNLFNBQVMsSUFBSSxLQUFLLEVBQUUsQ0FBQztvQkFDNUIsTUFBTSxPQUFPLEdBQUcsTUFBTSxNQUFNLENBQUMsUUFBUSxDQUFDLEdBQUcsR0FBRyxJQUFJLFNBQVMsRUFBRSxFQUFFLEVBQUMsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLElBQUksRUFBRSxFQUFDLEVBQUUsZUFBZSxDQUFDLENBQUM7b0JBQzVHLFlBQVksR0FBRyxZQUFZLENBQUMsTUFBTSxDQUFDLE1BQU0sSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztnQkFDdkUsQ0FBQztZQUNMLENBQUM7aUJBQU0sSUFBSSxLQUFLLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztnQkFDMUIsS0FBSyxNQUFNLFNBQVMsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsQ0FBQztvQkFDckYsTUFBTSxPQUFPLEdBQUcsTUFBTSxNQUFNLENBQUMsUUFBUSxDQUNqQyxHQUFHLEdBQUcsSUFBSSxRQUFRLGFBQWEsT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLElBQUksS0FBSyxDQUFDLFNBQVMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxLQUFLLENBQUMsSUFBSSxNQUFNLElBQUksU0FBUyxFQUFFLEVBQzdHLEVBQUMsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLElBQUksRUFBRSxFQUFDLEVBQzVCLGVBQWUsQ0FBQyxDQUFDO29CQUN2Qix3REFBd0Q7b0JBQ3hELE9BQU8sQ0FBQyxTQUFTLENBQUMsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7b0JBQzVFLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxLQUFVLEVBQUUsQ0FBUyxFQUFFLEVBQUU7d0JBQ2pELElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDOzRCQUFFLE9BQU87d0JBQzVCLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7NEJBQ2pCLE1BQU0sV0FBVyxHQUFHLEtBQUssQ0FBQyxjQUFjLENBQUMsRUFBQyxJQUFJLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxTQUFTLEVBQUUsS0FBSyxFQUFFLEtBQUssQ0FBQyxPQUFPLENBQUMsRUFBQyxFQUFFLE9BQU8sQ0FBQyxDQUFDOzRCQUMzSCxJQUFJLFdBQVcsQ0FBQyxJQUFJLEtBQUssT0FBTyxFQUFFLENBQUM7Z0NBQy9CLE9BQU87NEJBQ1gsQ0FBQzt3QkFDTCxDQUFDO3dCQUNELE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRzs0QkFDcEIsT0FBTyxFQUFFLEtBQUssQ0FBQyxTQUFTLENBQUM7NEJBQ3pCLElBQUksRUFBRSxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUM7NEJBQ3ZDLEdBQUcsRUFBRSxLQUFLLENBQUMsS0FBSyxDQUFDOzRCQUNqQixNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU0sSUFBSSxFQUFFO3lCQUM3QixDQUFDO29CQUNOLENBQUMsQ0FBQyxDQUFDO29CQUVILFlBQVksR0FBRyxZQUFZLENBQUMsTUFBTSxDQUFDLE1BQU0sSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztnQkFDdkUsQ0FBQztZQUNMLENBQUM7aUJBQU0sSUFBSSxLQUFLLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQztnQkFDNUIsTUFBTSxFQUFDLE1BQU0sRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLGFBQWEsRUFBRSxHQUFHLEVBQUMsR0FBRyxJQUFJLENBQUMscUJBQXFCLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUM7Z0JBQ3ZHLG1HQUFtRztnQkFDbkcsSUFBSSxLQUFLLEdBQUcsQ0FBQyxHQUFHLGFBQWEsTUFBTSxFQUFFLEdBQUcsYUFBYSxlQUFlLEVBQUUsSUFBSSxDQUFDLENBQUM7Z0JBQzVFLE1BQU0sZ0JBQWdCLEdBQUcsV0FBVyxLQUFLLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLElBQUksT0FBTyxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsSUFBSSxHQUFHLEtBQUssT0FBTyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUM7Z0JBRTNILDJIQUEySDtnQkFDM0gsSUFBSSxDQUFDLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztvQkFDdEIsS0FBSyxNQUFNLENBQUMsSUFBSSxLQUFLLEVBQUUsQ0FBQzt3QkFDcEIsTUFBTSxhQUFhLEdBQUcsR0FBRyxHQUFHLElBQUksUUFBUSxhQUFhLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxJQUFJLFdBQVcsSUFBSSxHQUFHLElBQUksQ0FBQyxFQUFFLENBQUM7d0JBQ3RHLElBQUksRUFBRSxDQUFDLFVBQVUsQ0FBQyxhQUFhLENBQUMsRUFBRSxDQUFDOzRCQUMvQixLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQzs0QkFDWixNQUFNO3dCQUNWLENBQUM7b0JBQ0wsQ0FBQztnQkFDTCxDQUFDO2dCQUVELEtBQUssTUFBTSxDQUFDLElBQUksS0FBSyxFQUFFLENBQUM7b0JBQ3BCLE1BQU0sQ0FBQyxDQUFDLEtBQUssSUFBSSxFQUFFLHlEQUF5RCxLQUFLLENBQUMsV0FBVyxDQUFDLHlCQUF5QixLQUFLLHFCQUFxQixNQUFNLEVBQUU7d0JBQ3JJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsR0FBRyxJQUFJLFdBQVcsSUFBSSxDQUFDLENBQUM7b0JBRXBFLElBQUksZ0JBQWdCLEVBQUUsQ0FBQzt3QkFDbkIsTUFBTSxxQkFBcUIsR0FBRyxHQUFHLEdBQUcsSUFBSSxDQUFDLEVBQUUsQ0FBQzt3QkFDNUMsSUFBSSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsVUFBVSxDQUFDLHFCQUFxQixDQUFDLENBQUMsRUFBRSxDQUFDOzRCQUNoRCxTQUFTO3dCQUNiLENBQUM7d0JBRUQsTUFBTSxPQUFPLEdBQUcsTUFBTSxNQUFNLENBQUMsUUFBUSxDQUFDLHFCQUFxQixFQUFFLEVBQUMsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLElBQUksRUFBRSxFQUFDLEVBQUUsZUFBZSxDQUFDLENBQUM7d0JBQzVHLFlBQVksR0FBRyxZQUFZLENBQUMsTUFBTSxDQUFDLE1BQU0sSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQzt3QkFDbkUsTUFBTTtvQkFDVixDQUFDO3lCQUFNLENBQUM7d0JBQ0osTUFBTSxhQUFhLEdBQUcsR0FBRyxHQUFHLElBQUksUUFBUSxhQUFhLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxJQUFJLFdBQVcsSUFBSSxHQUFHLElBQUksQ0FBQyxFQUFFLENBQUM7d0JBQ3RHLHlEQUF5RDt3QkFDekQsSUFBSSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxDQUFDLE1BQU0sS0FBSyxDQUFDLGVBQWUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxFQUFFLEdBQUcsRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLE9BQU8sQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsRUFBRSxDQUFDOzRCQUNoSixTQUFTO3dCQUNiLENBQUM7d0JBRUQsTUFBTSxPQUFPLEdBQUc7NEJBQ1osT0FBTyxFQUFFO2dDQUNMLE9BQU8sRUFBRSxXQUFXO2dDQUNwQixJQUFJLEVBQUUsQ0FBQztnQ0FDUCxHQUFHLEVBQUUsR0FBRztnQ0FDUixNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU0sSUFBSSxFQUFFOzZCQUM3Qjt5QkFDSixDQUFDO3dCQUNGLFlBQVksR0FBRyxZQUFZLENBQUMsTUFBTSxDQUFDLE1BQU0sSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQzt3QkFDbkUsTUFBTTtvQkFDVixDQUFDO2dCQUNMLENBQUM7WUFDTCxDQUFDO2lCQUFNLElBQUksS0FBSyxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUM7Z0JBQzNCLE1BQU0sRUFBQyxPQUFPLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUMsR0FBRyxJQUFJLENBQUMsMkJBQTJCLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUM7Z0JBQ3pGLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUMsV0FBVyxNQUFNLElBQUksT0FBTyxVQUFVLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO2dCQUMvRSxNQUFNLE9BQU8sR0FBRyxNQUFNLE1BQU0sQ0FBQyxRQUFRLENBQ2pDLEdBQUcsR0FBRyxJQUFJLFFBQVEsYUFBYSxLQUFLLEVBQUUsRUFBRSxFQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsTUFBTSxJQUFJLEVBQUUsRUFBQyxFQUFFLGVBQWUsQ0FDeEYsQ0FBQztnQkFDRixZQUFZLEdBQUcsWUFBWSxDQUFDLE1BQU0sQ0FBQyxNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7WUFDdkUsQ0FBQztpQkFBTSxJQUFJLEtBQUssQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO2dCQUN6QixNQUFNLEtBQUssR0FBRyxLQUFLLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDO2dCQUMzQyxNQUFNLE9BQU8sR0FBRyxNQUFNLE1BQU0sQ0FBQyxRQUFRLENBQ2pDLEdBQUcsR0FBRyxJQUFJLFFBQVEsYUFBYSxLQUFLLEVBQUUsRUFBRSxFQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsTUFBTSxJQUFJLEVBQUUsRUFBQyxFQUFFLGVBQWUsQ0FDeEYsQ0FBQztnQkFDRixZQUFZLEdBQUcsWUFBWSxDQUFDLE1BQU0sQ0FBQyxNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7WUFDdkUsQ0FBQztpQkFBTSxDQUFDO2dCQUNKLE1BQU0sSUFBSSxjQUFjLENBQUMsRUFBQyxPQUFPLEVBQUUsNkJBQTZCLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLEVBQUUsRUFBQyxDQUFDLENBQUM7WUFDOUYsQ0FBQztRQUNMLENBQUM7UUFFRCxZQUFZLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQzlCLE9BQU8sWUFBWSxDQUFDO0lBQ3hCLENBQUM7SUFFRCxNQUFNLENBQUMsYUFBYSxDQUFFLENBQU0sRUFBRSxTQUFrQztRQUM1RCxJQUFJLE9BQU8sR0FBRyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3RCLElBQUksT0FBTyxJQUFJLE9BQU8sQ0FBQyxNQUFNLElBQUksSUFBSSxFQUFFLENBQUM7WUFDcEMsT0FBTyxHQUFHLENBQUUsQ0FBQyxDQUFFLENBQUM7UUFDcEIsQ0FBQztRQUNELElBQUksT0FBTyxPQUFPLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDOUIsT0FBTyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDeEIsQ0FBQztRQUVELEtBQUssTUFBTSxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDbkQsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEtBQUssQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLENBQUMsRUFBRSxDQUFDO2dCQUN6RixPQUFPLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBQyxRQUFRLEVBQUUsS0FBSyxFQUFDLENBQUM7WUFDdkMsQ0FBQztpQkFBTSxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsRUFBRSxDQUFDO2dCQUNuQyxPQUFPLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBQyxPQUFPLEVBQUUsS0FBSyxFQUFDLENBQUM7WUFDdEMsQ0FBQztpQkFBTSxDQUFDO2dCQUNKLE9BQU8sQ0FBQyxLQUFLLENBQUMsR0FBRyxLQUFLLENBQUM7WUFDM0IsQ0FBQztRQUVMLENBQUM7UUFFRCxLQUFLLE1BQU0sS0FBSyxJQUFJLE9BQU8sRUFBRSxDQUFDO1lBQzFCLEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0JBQy9DLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxLQUFLLENBQUMsVUFBVSxDQUFDLEtBQUssRUFBRSxTQUFTLENBQUMsQ0FBQztZQUNwRCxDQUFDO1FBQ0wsQ0FBQztRQUVELE9BQU8sT0FBTyxDQUFDO0lBQ25CLENBQUM7SUFFRCxNQUFNLENBQUMsMkJBQTJCLENBQUUsUUFBZ0I7UUFDaEQsT0FBTztZQUNILE1BQU0sRUFBRSxZQUFZO1lBQ3BCLE9BQU8sRUFBRSxtQkFBbUI7WUFDNUIsR0FBRyxFQUFFLE1BQU07WUFDWCxJQUFJLEVBQUUsMkJBQTJCLFFBQVEsRUFBRTtTQUM5QyxDQUFDO0lBQ04sQ0FBQztJQUVELE1BQU0sQ0FBQyxxQkFBcUIsQ0FBRSxTQUFpQjtRQUMzQyxNQUFNLENBQUMsQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxFQUFFLHlEQUF5RCxTQUFTLGdDQUFnQyxDQUFDLENBQUM7UUFDdkksNkNBQTZDO1FBQzdDLE1BQU0sT0FBTyxHQUFHLDZGQUE2RixDQUFDLENBQUMsMkJBQTJCO1FBQzFJLE1BQU0sY0FBYyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7UUFFL0MsSUFBSSxjQUFjLEVBQUUsTUFBTSxJQUFJLElBQUk7WUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLHFHQUFxRyxTQUFTLEVBQUUsQ0FBQyxDQUFDO1FBQ3RLLE9BQU87WUFDSCxNQUFNLEVBQUUsY0FBYyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUM7WUFDdkMsSUFBSSxFQUFFLGNBQWMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDO1lBQ25DLFdBQVcsRUFBRSxjQUFjLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQztZQUNqRCxhQUFhLEVBQUUsYUFBYSxjQUFjLENBQUMsTUFBTSxDQUFDLGVBQWUsQ0FBQyxFQUFFO1lBQ3BFLEdBQUcsRUFBRSxjQUFjLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQztTQUNwQyxDQUFDO0lBQ04sQ0FBQztJQUVELE1BQU0sQ0FBQyxLQUFLLENBQUMscUJBQXFCLENBQUUsR0FBVyxFQUFFLFFBQWdCLEVBQUUsR0FBVyxFQUFFLGFBQXNCO1FBQ2xHLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDL0IsSUFBSSxDQUFDO1lBQ0QsTUFBTSxNQUFNLEdBQUcsR0FBRyxHQUFHLElBQUksUUFBUSxhQUFhLEtBQUssRUFBRSxDQUFDO1lBQ3RELElBQUksTUFBTSxFQUFFLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsYUFBYTtnQkFBRSxPQUFPO1lBQzFELE1BQU0sR0FBRyxHQUFHLE1BQU0sS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsRUFBQyxPQUFPLEVBQUUsRUFBQyxZQUFZLEVBQUUsaUJBQWlCLEVBQUMsRUFBQyxDQUFDLENBQUM7WUFDL0UsTUFBTSxFQUFFLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDMUMsQ0FBQztRQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDVCxNQUFNLElBQUksY0FBYyxDQUFDLEVBQUMsT0FBTyxFQUFFLHVDQUF1QyxHQUFHLEtBQUssQ0FBQyxFQUFFLEVBQUMsQ0FBQyxDQUFDO1FBQzVGLENBQUM7SUFDTCxDQUFDO0lBRUQsTUFBTSxDQUFDLEtBQUssQ0FBQywwQkFBMEIsQ0FBRSxHQUFXLEVBQUUsUUFBZ0IsRUFBRSxPQUFlLEVBQUUsR0FBVyxFQUFFLElBQVksRUFBRSxPQUFnQixFQUFFLGFBQXNCO1FBQ3hKLE1BQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUM7UUFDOUIsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDaEQsSUFBSSxDQUFDO1lBQ0QsTUFBTSxNQUFNLEdBQUcsR0FBRyxRQUFRLGFBQWEsTUFBTSxDQUFDLElBQUksSUFBSSxPQUFPLElBQUksR0FBRyxFQUFFLENBQUM7WUFDdkUsSUFBSSxNQUFNLEVBQUUsQ0FBQyxVQUFVLENBQUMsR0FBRyxHQUFHLElBQUksTUFBTSxJQUFJLGNBQWMsRUFBRSxDQUFDLElBQUksQ0FBQyxhQUFhO2dCQUFFLE9BQU87WUFFeEYsSUFBSSxNQUFNLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO2dCQUNuQyxNQUFNLEdBQUcsR0FBRyxNQUFNLEdBQUcsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO2dCQUNuQyxNQUFNLEVBQUUsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEdBQUcsSUFBSSxNQUFNLElBQUksY0FBYyxFQUFFLENBQUMsQ0FBQyxDQUFDO2dCQUVwRSxNQUFNLGNBQWMsR0FBRyxDQUFDLEdBQUcsS0FBSyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxZQUFZLEdBQUcsRUFBRSxDQUFDO2dCQUNqRSxNQUFNLEtBQUssQ0FBQyxTQUFTLENBQUM7b0JBQ2xCLE1BQU0sR0FBRyxJQUFJLFFBQVEsRUFBRTtvQkFDdkIsYUFBYSxjQUFjLGlDQUFpQyxNQUFNLENBQUMsTUFBTSxNQUFNLE1BQU0sQ0FBQyxJQUFJLElBQUksTUFBTSxDQUFDLElBQUksSUFBSSxPQUFPLFFBQVEsR0FBRyxJQUFJLE1BQU0sSUFBSSxHQUFHLEVBQUU7b0JBQ2xKLE1BQU0sR0FBRyxJQUFJLE1BQU0sSUFBSSxHQUFHLEVBQUU7b0JBQzVCLHFDQUFxQyxjQUFjLEVBQUU7b0JBQ3JELGNBQWM7b0JBQ2QsTUFBTSxHQUFHLElBQUksUUFBUSxFQUFFO29CQUN2QixNQUFNLEdBQUcsSUFBSSxNQUFNLElBQUksR0FBRyxJQUFJLGNBQWMsSUFBSSxHQUFHLElBQUksTUFBTSxJQUFJLGNBQWMsRUFBRTtpQkFDcEYsRUFBRSxHQUFHLENBQUMsQ0FBQztZQUNaLENBQUM7aUJBQU0sQ0FBQztnQkFDSixNQUFNLEVBQUUsQ0FBQyxNQUFNLENBQUMsR0FBRyxHQUFHLElBQUksTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDcEMsTUFBTSxLQUFLLENBQUMsSUFBSSxDQUFDLHFEQUFxRCxNQUFNLENBQUMsSUFBSSxJQUFJLE1BQU0sQ0FBQyxJQUFJLElBQUksT0FBTyxRQUFRLEdBQUcsSUFBSSxjQUFjLG1CQUFtQixNQUFNLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQztZQUMvSyxDQUFDO1FBQ0wsQ0FBQztRQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDVCxNQUFNLElBQUksY0FBYyxDQUFDLEVBQUMsT0FBTyxFQUFFLG1EQUFtRCxPQUFPLFVBQVUsR0FBRyxXQUFXLGNBQWMsT0FBTyxDQUFDLEVBQUUsRUFBQyxDQUFDLENBQUM7UUFDcEosQ0FBQztJQUNMLENBQUM7O0FBR0wsTUFBTSxVQUFVLG9CQUFvQixDQUFFLFFBQWdCO0lBQ2xELE1BQU0sQ0FBQyxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxRQUFRLHdHQUF3RyxDQUFDLENBQUM7SUFDMUosTUFBTSxDQUFDLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsRUFBRSxLQUFLLFFBQVEsK0VBQStFLENBQUMsQ0FBQztBQUNuSSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtBcmd2fSBmcm9tIFwiLi9hcmd2LmpzXCI7XG5pbXBvcnQge1V0aWxzfSBmcm9tIFwiLi91dGlscy5qc1wiO1xuaW1wb3J0IGZzIGZyb20gXCJmcy1leHRyYVwiO1xuaW1wb3J0IHtXcml0ZVN0cmVhbXN9IGZyb20gXCIuL3dyaXRlLXN0cmVhbXMuanNcIjtcbmltcG9ydCB7R2l0RGF0YX0gZnJvbSBcIi4vZ2l0LWRhdGEuanNcIjtcbmltcG9ydCBhc3NlcnQsIHtBc3NlcnRpb25FcnJvcn0gZnJvbSBcImFzc2VydFwiO1xuaW1wb3J0IGNoYWxrIGZyb20gXCJjaGFsa1wiO1xuaW1wb3J0IHtQYXJzZXJ9IGZyb20gXCIuL3BhcnNlci5qc1wiO1xuaW1wb3J0IGF4aW9zIGZyb20gXCJheGlvc1wiO1xuaW1wb3J0IGdsb2JieSBmcm9tIFwiZ2xvYmJ5XCI7XG5pbXBvcnQgcGF0aCBmcm9tIFwicGF0aFwiO1xuXG50eXBlIFBhcnNlckluY2x1ZGVzSW5pdE9wdGlvbnMgPSB7XG4gICAgYXJndjogQXJndjtcbiAgICBjd2Q6IHN0cmluZztcbiAgICBzdGF0ZURpcjogc3RyaW5nO1xuICAgIHdyaXRlU3RyZWFtczogV3JpdGVTdHJlYW1zO1xuICAgIGdpdERhdGE6IEdpdERhdGE7XG4gICAgZmV0Y2hJbmNsdWRlczogYm9vbGVhbjtcbiAgICB2YXJpYWJsZXM6IHtba2V5OiBzdHJpbmddOiBzdHJpbmd9O1xuICAgIGV4cGFuZFZhcmlhYmxlczogYm9vbGVhbjtcbiAgICBtYXhpbXVtSW5jbHVkZXM6IG51bWJlcjtcbn07XG5cbmV4cG9ydCBjbGFzcyBQYXJzZXJJbmNsdWRlcyB7XG4gICAgcHJpdmF0ZSBzdGF0aWMgY291bnQ6IG51bWJlciA9IDA7XG5cbiAgICBzdGF0aWMgcmVzZXRDb3VudCAoKTogdm9pZCB7XG4gICAgICAgIHRoaXMuY291bnQgPSAwO1xuICAgIH1cblxuICAgIHByaXZhdGUgc3RhdGljIG5vcm1hbGl6ZVRyaWdnZXJJbmNsdWRlIChnaXRsYWJEYXRhOiBhbnksIG9wdHM6IFBhcnNlckluY2x1ZGVzSW5pdE9wdGlvbnMpIHtcbiAgICAgICAgY29uc3Qge3dyaXRlU3RyZWFtc30gPSBvcHRzO1xuICAgICAgICBmb3IgKGNvbnN0IFtqb2JOYW1lLCBqb2JEYXRhXSBvZiBPYmplY3QuZW50cmllczxhbnk+KGdpdGxhYkRhdGEgPz8ge30pKSB7XG4gICAgICAgICAgICBpZiAodHlwZW9mIGpvYkRhdGEudHJpZ2dlcj8uaW5jbHVkZSA9PT0gXCJzdHJpbmdcIikge1xuICAgICAgICAgICAgICAgIGpvYkRhdGEudHJpZ2dlci5pbmNsdWRlID0gW3tcbiAgICAgICAgICAgICAgICAgICAgbG9jYWw6IGpvYkRhdGEudHJpZ2dlci5pbmNsdWRlLFxuICAgICAgICAgICAgICAgIH0gXTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoam9iRGF0YS50cmlnZ2VyPy5wcm9qZWN0KSB7XG4gICAgICAgICAgICAgICAgd3JpdGVTdHJlYW1zLm1lbW9TdGRvdXQoY2hhbGtge2JnWWVsbG93QnJpZ2h0ICBXQVJOIH0gVGhlIGpvYjogXFxge2JsdWVCcmlnaHQgJHtqb2JOYW1lfX1cXGAgd2lsbCBiZSBuby1vcC4gTXVsdGktcHJvamVjdCBwaXBlbGluZSBpcyBub3Qgc3VwcG9ydGVkIGJ5IGdpdGxhYi1jaS1sb2NhbFxcbmApO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgc3RhdGljIGFzeW5jIGluaXQgKGdpdGxhYkRhdGE6IGFueSwgb3B0czogUGFyc2VySW5jbHVkZXNJbml0T3B0aW9ucyk6IFByb21pc2U8YW55W10+IHtcbiAgICAgICAgY29uc3Qge2FyZ3Z9ID0gb3B0cztcbiAgICAgICAgdGhpcy5jb3VudCsrO1xuICAgICAgICBhc3NlcnQoXG4gICAgICAgICAgICB0aGlzLmNvdW50IDw9IG9wdHMubWF4aW11bUluY2x1ZGVzICsgMSwgLy8gMXN0IGluaXQgY2FsbCBpcyBub3QgY291bnRlZFxuICAgICAgICAgICAgY2hhbGtgVGhpcyBHaXRMYWIgQ0kgY29uZmlndXJhdGlvbiBpcyBpbnZhbGlkOiBNYXhpbXVtIG9mIHtibHVlQnJpZ2h0ICR7b3B0cy5tYXhpbXVtSW5jbHVkZXN9fSBuZXN0ZWQgaW5jbHVkZXMgYXJlIGFsbG93ZWQhLiBUaGlzIGxpbWl0IGNhbiBiZSBpbmNyZWFzZWQgd2l0aCB0aGUgLS1tYXhpbXVtLWluY2x1ZGVzIGNsaSBmbGFncy5gXG4gICAgICAgICk7XG4gICAgICAgIGxldCBpbmNsdWRlRGF0YXM6IGFueVtdID0gW107XG4gICAgICAgIGNvbnN0IHByb21pc2VzID0gW107XG4gICAgICAgIGNvbnN0IHtzdGF0ZURpciwgY3dkLCBmZXRjaEluY2x1ZGVzLCBnaXREYXRhLCBleHBhbmRWYXJpYWJsZXN9ID0gb3B0cztcblxuICAgICAgICBjb25zdCBpbmNsdWRlID0gdGhpcy5leHBhbmRJbmNsdWRlKGdpdGxhYkRhdGE/LmluY2x1ZGUsIG9wdHMudmFyaWFibGVzKTtcblxuICAgICAgICB0aGlzLm5vcm1hbGl6ZVRyaWdnZXJJbmNsdWRlKGdpdGxhYkRhdGEsIG9wdHMpO1xuICAgICAgICAvLyBGaW5kIGZpbGVzIHRvIGZldGNoIGZyb20gcmVtb3RlIGFuZCBwbGFjZSBpbiAuZ2l0bGFiLWNpLWxvY2FsL2luY2x1ZGVzXG4gICAgICAgIGZvciAoY29uc3QgdmFsdWUgb2YgaW5jbHVkZSkge1xuICAgICAgICAgICAgaWYgKHZhbHVlW1wicnVsZXNcIl0pIHtcbiAgICAgICAgICAgICAgICBjb25zdCBpbmNsdWRlX3J1bGVzID0gdmFsdWVbXCJydWxlc1wiXTtcbiAgICAgICAgICAgICAgICBjb25zdCBydWxlc1Jlc3VsdCA9IFV0aWxzLmdldFJ1bGVzUmVzdWx0KHthcmd2LCBjd2QsIHJ1bGVzOiBpbmNsdWRlX3J1bGVzLCB2YXJpYWJsZXM6IG9wdHMudmFyaWFibGVzfSwgZ2l0RGF0YSk7XG4gICAgICAgICAgICAgICAgaWYgKHJ1bGVzUmVzdWx0LndoZW4gPT09IFwibmV2ZXJcIikge1xuICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAodmFsdWVbXCJsb2NhbFwiXSkge1xuICAgICAgICAgICAgICAgIHZhbGlkYXRlSW5jbHVkZUxvY2FsKHZhbHVlW1wibG9jYWxcIl0pO1xuICAgICAgICAgICAgICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgZ2xvYmJ5KHZhbHVlW1wibG9jYWxcIl0ucmVwbGFjZSgvXlxcLy8sIFwiXCIpLCB7ZG90OiB0cnVlLCBjd2R9KTtcbiAgICAgICAgICAgICAgICBpZiAoZmlsZXMubGVuZ3RoID09IDApIHtcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgbmV3IEFzc2VydGlvbkVycm9yKHttZXNzYWdlOiBgTG9jYWwgaW5jbHVkZSBmaWxlIGNhbm5vdCBiZSBmb3VuZCAke3ZhbHVlW1wibG9jYWxcIl19YH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAodmFsdWVbXCJmaWxlXCJdKSB7XG4gICAgICAgICAgICAgICAgZm9yIChjb25zdCBmaWxlVmFsdWUgb2YgQXJyYXkuaXNBcnJheSh2YWx1ZVtcImZpbGVcIl0pID8gdmFsdWVbXCJmaWxlXCJdIDogW3ZhbHVlW1wiZmlsZVwiXV0pIHtcbiAgICAgICAgICAgICAgICAgICAgcHJvbWlzZXMucHVzaCh0aGlzLmRvd25sb2FkSW5jbHVkZVByb2plY3RGaWxlKGN3ZCwgc3RhdGVEaXIsIHZhbHVlW1wicHJvamVjdFwiXSwgdmFsdWVbXCJyZWZcIl0gfHwgXCJIRUFEXCIsIGZpbGVWYWx1ZSwgZ2l0RGF0YSwgZmV0Y2hJbmNsdWRlcykpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAodmFsdWVbXCJ0ZW1wbGF0ZVwiXSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHtwcm9qZWN0LCByZWYsIGZpbGUsIGRvbWFpbn0gPSB0aGlzLmNvdmVydFRlbXBsYXRlVG9Qcm9qZWN0RmlsZSh2YWx1ZVtcInRlbXBsYXRlXCJdKTtcbiAgICAgICAgICAgICAgICBjb25zdCB1cmwgPSBgaHR0cHM6Ly8ke2RvbWFpbn0vJHtwcm9qZWN0fS8tL3Jhdy8ke3JlZn0vJHtmaWxlfWA7XG4gICAgICAgICAgICAgICAgcHJvbWlzZXMucHVzaCh0aGlzLmRvd25sb2FkSW5jbHVkZVJlbW90ZShjd2QsIHN0YXRlRGlyLCB1cmwsIGZldGNoSW5jbHVkZXMpKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAodmFsdWVbXCJyZW1vdGVcIl0pIHtcbiAgICAgICAgICAgICAgICBwcm9taXNlcy5wdXNoKHRoaXMuZG93bmxvYWRJbmNsdWRlUmVtb3RlKGN3ZCwgc3RhdGVEaXIsIHZhbHVlW1wicmVtb3RlXCJdLCBmZXRjaEluY2x1ZGVzKSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgfVxuXG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKHByb21pc2VzKTtcblxuICAgICAgICBmb3IgKGNvbnN0IHZhbHVlIG9mIGluY2x1ZGUpIHtcbiAgICAgICAgICAgIGlmICh2YWx1ZVtcInJ1bGVzXCJdKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgaW5jbHVkZV9ydWxlcyA9IHZhbHVlW1wicnVsZXNcIl07XG4gICAgICAgICAgICAgICAgY29uc3QgcnVsZXNSZXN1bHQgPSBVdGlscy5nZXRSdWxlc1Jlc3VsdCh7YXJndiwgY3dkLCBydWxlczogaW5jbHVkZV9ydWxlcywgdmFyaWFibGVzOiBvcHRzLnZhcmlhYmxlc30sIGdpdERhdGEpO1xuICAgICAgICAgICAgICAgIGlmIChydWxlc1Jlc3VsdC53aGVuID09PSBcIm5ldmVyXCIpIHtcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHZhbHVlW1wibG9jYWxcIl0pIHtcbiAgICAgICAgICAgICAgICBjb25zdCBmaWxlcyA9IGF3YWl0IGdsb2JieShbdmFsdWVbXCJsb2NhbFwiXS5yZXBsYWNlKC9eXFwvLywgXCJcIildLCB7ZG90OiB0cnVlLCBjd2R9KTtcbiAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IGxvY2FsRmlsZSBvZiBmaWxlcykge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBjb250ZW50ID0gYXdhaXQgUGFyc2VyLmxvYWRZYW1sKGAke2N3ZH0vJHtsb2NhbEZpbGV9YCwge2lucHV0czogdmFsdWUuaW5wdXRzIHx8IHt9fSwgZXhwYW5kVmFyaWFibGVzKTtcbiAgICAgICAgICAgICAgICAgICAgaW5jbHVkZURhdGFzID0gaW5jbHVkZURhdGFzLmNvbmNhdChhd2FpdCB0aGlzLmluaXQoY29udGVudCwgb3B0cykpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAodmFsdWVbXCJwcm9qZWN0XCJdKSB7XG4gICAgICAgICAgICAgICAgZm9yIChjb25zdCBmaWxlVmFsdWUgb2YgQXJyYXkuaXNBcnJheSh2YWx1ZVtcImZpbGVcIl0pID8gdmFsdWVbXCJmaWxlXCJdIDogW3ZhbHVlW1wiZmlsZVwiXV0pIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgZmlsZURvYyA9IGF3YWl0IFBhcnNlci5sb2FkWWFtbChcbiAgICAgICAgICAgICAgICAgICAgICAgIGAke2N3ZH0vJHtzdGF0ZURpcn0vaW5jbHVkZXMvJHtnaXREYXRhLnJlbW90ZS5ob3N0fS8ke3ZhbHVlW1wicHJvamVjdFwiXX0vJHt2YWx1ZVtcInJlZlwiXSB8fCBcIkhFQURcIn0vJHtmaWxlVmFsdWV9YFxuICAgICAgICAgICAgICAgICAgICAgICAgLCB7aW5wdXRzOiB2YWx1ZS5pbnB1dHMgfHwge319XG4gICAgICAgICAgICAgICAgICAgICAgICAsIGV4cGFuZFZhcmlhYmxlcyk7XG4gICAgICAgICAgICAgICAgICAgIC8vIEV4cGFuZCBsb2NhbCBpbmNsdWRlcyBpbnNpZGUgYSBcInByb2plY3RcIi1saWtlIGluY2x1ZGVcbiAgICAgICAgICAgICAgICAgICAgZmlsZURvY1tcImluY2x1ZGVcIl0gPSB0aGlzLmV4cGFuZEluY2x1ZGUoZmlsZURvY1tcImluY2x1ZGVcIl0sIG9wdHMudmFyaWFibGVzKTtcbiAgICAgICAgICAgICAgICAgICAgZmlsZURvY1tcImluY2x1ZGVcIl0uZm9yRWFjaCgoaW5uZXI6IGFueSwgaTogbnVtYmVyKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoIWlubmVyW1wibG9jYWxcIl0pIHJldHVybjtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChpbm5lcltcInJ1bGVzXCJdKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgcnVsZXNSZXN1bHQgPSBVdGlscy5nZXRSdWxlc1Jlc3VsdCh7YXJndiwgY3dkOiBvcHRzLmN3ZCwgdmFyaWFibGVzOiBvcHRzLnZhcmlhYmxlcywgcnVsZXM6IGlubmVyW1wicnVsZXNcIl19LCBnaXREYXRhKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAocnVsZXNSZXN1bHQud2hlbiA9PT0gXCJuZXZlclwiKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBmaWxlRG9jW1wiaW5jbHVkZVwiXVtpXSA9IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9qZWN0OiB2YWx1ZVtcInByb2plY3RcIl0sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZTogaW5uZXJbXCJsb2NhbFwiXS5yZXBsYWNlKC9eXFwvLywgXCJcIiksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVmOiB2YWx1ZVtcInJlZlwiXSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnB1dHM6IGlubmVyLmlucHV0cyB8fCB7fSxcbiAgICAgICAgICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgICAgIGluY2x1ZGVEYXRhcyA9IGluY2x1ZGVEYXRhcy5jb25jYXQoYXdhaXQgdGhpcy5pbml0KGZpbGVEb2MsIG9wdHMpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHZhbHVlW1wiY29tcG9uZW50XCJdKSB7XG4gICAgICAgICAgICAgICAgY29uc3Qge2RvbWFpbiwgcG9ydCwgcHJvamVjdFBhdGgsIGNvbXBvbmVudE5hbWUsIHJlZn0gPSB0aGlzLnBhcnNlSW5jbHVkZUNvbXBvbmVudCh2YWx1ZVtcImNvbXBvbmVudFwiXSk7XG4gICAgICAgICAgICAgICAgLy8gY29udmVydHMgY29tcG9uZW50IHRvIHByb2plY3QuIGdpdGxhYiBhbGxvd3MgdHdvIGRpZmZlcmVudCBmaWxlIHBhdGggd2F5cyB0byBpbmNsdWRlIGEgY29tcG9uZW50XG4gICAgICAgICAgICAgICAgbGV0IGZpbGVzID0gW2Ake2NvbXBvbmVudE5hbWV9LnltbGAsIGAke2NvbXBvbmVudE5hbWV9L3RlbXBsYXRlLnltbGAsIG51bGxdO1xuICAgICAgICAgICAgICAgIGNvbnN0IGlzTG9jYWxDb21wb25lbnQgPSBwcm9qZWN0UGF0aCA9PT0gYCR7Z2l0RGF0YS5yZW1vdGUuZ3JvdXB9LyR7Z2l0RGF0YS5yZW1vdGUucHJvamVjdH1gICYmIHJlZiA9PT0gZ2l0RGF0YS5jb21taXQuU0hBO1xuXG4gICAgICAgICAgICAgICAgLy8gSWYgYSBmaWxlIGlzIHByZXNlbnQgbG9jYWxseSwga2VlcCBvbmx5IHRoYXQgb25lIGluIHRoZSBmaWxlcyBhcnJheSB0byBhdm9pZCBkb3dubG9hZGluZyB0aGUgb3RoZXIgb25lIHRoYXQgbmV2ZXIgZXhpc3RzXG4gICAgICAgICAgICAgICAgaWYgKCFhcmd2LmZldGNoSW5jbHVkZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgZm9yIChjb25zdCBmIG9mIGZpbGVzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBsb2NhbEZpbGVOYW1lID0gYCR7Y3dkfS8ke3N0YXRlRGlyfS9pbmNsdWRlcy8ke2dpdERhdGEucmVtb3RlLmhvc3R9LyR7cHJvamVjdFBhdGh9LyR7cmVmfS8ke2Z9YDtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChmcy5leGlzdHNTeW5jKGxvY2FsRmlsZU5hbWUpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZXMgPSBbZl07XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IGYgb2YgZmlsZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgYXNzZXJ0KGYgIT09IG51bGwsIGBUaGlzIEdpdExhYiBDSSBjb25maWd1cmF0aW9uIGlzIGludmFsaWQ6IGNvbXBvbmVudDogXFxgJHt2YWx1ZVtcImNvbXBvbmVudFwiXX1cXGAuIE9uZSBvZiB0aGUgZmlsZXMgWyR7ZmlsZXN9XSBtdXN0IGV4aXN0IGluIFxcYCR7ZG9tYWlufWAgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChwb3J0ID8gYDoke3BvcnR9YCA6IFwiXCIpICsgYC8ke3Byb2plY3RQYXRofVxcYGApO1xuXG4gICAgICAgICAgICAgICAgICAgIGlmIChpc0xvY2FsQ29tcG9uZW50KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBsb2NhbENvbXBvbmVudEluY2x1ZGUgPSBgJHtjd2R9LyR7Zn1gO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCEoYXdhaXQgZnMucGF0aEV4aXN0cyhsb2NhbENvbXBvbmVudEluY2x1ZGUpKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBjb250ZW50ID0gYXdhaXQgUGFyc2VyLmxvYWRZYW1sKGxvY2FsQ29tcG9uZW50SW5jbHVkZSwge2lucHV0czogdmFsdWUuaW5wdXRzIHx8IHt9fSwgZXhwYW5kVmFyaWFibGVzKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVEYXRhcyA9IGluY2x1ZGVEYXRhcy5jb25jYXQoYXdhaXQgdGhpcy5pbml0KGNvbnRlbnQsIG9wdHMpKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgbG9jYWxGaWxlTmFtZSA9IGAke2N3ZH0vJHtzdGF0ZURpcn0vaW5jbHVkZXMvJHtnaXREYXRhLnJlbW90ZS5ob3N0fS8ke3Byb2plY3RQYXRofS8ke3JlZn0vJHtmfWA7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBDaGVjayByZW1vdGVseSBvbmx5IGlmIHRoZSBmaWxlIGRvZXMgbm90IGV4aXN0IGxvY2FsbHlcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICghZnMuZXhpc3RzU3luYyhsb2NhbEZpbGVOYW1lKSAmJiAhKGF3YWl0IFV0aWxzLnJlbW90ZUZpbGVFeGlzdChjd2QsIGYsIHJlZiwgZG9tYWluLCBwcm9qZWN0UGF0aCwgZ2l0RGF0YS5yZW1vdGUuc2NoZW1hLCBnaXREYXRhLnJlbW90ZS5wb3J0KSkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgZmlsZURvYyA9IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2plY3Q6IHByb2plY3RQYXRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlOiBmLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWY6IHJlZixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5wdXRzOiB2YWx1ZS5pbnB1dHMgfHwge30sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlRGF0YXMgPSBpbmNsdWRlRGF0YXMuY29uY2F0KGF3YWl0IHRoaXMuaW5pdChmaWxlRG9jLCBvcHRzKSk7XG4gICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAodmFsdWVbXCJ0ZW1wbGF0ZVwiXSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHtwcm9qZWN0LCByZWYsIGZpbGUsIGRvbWFpbn0gPSB0aGlzLmNvdmVydFRlbXBsYXRlVG9Qcm9qZWN0RmlsZSh2YWx1ZVtcInRlbXBsYXRlXCJdKTtcbiAgICAgICAgICAgICAgICBjb25zdCBmc1VybCA9IFV0aWxzLmZzVXJsKGBodHRwczovLyR7ZG9tYWlufS8ke3Byb2plY3R9Ly0vcmF3LyR7cmVmfS8ke2ZpbGV9YCk7XG4gICAgICAgICAgICAgICAgY29uc3QgZmlsZURvYyA9IGF3YWl0IFBhcnNlci5sb2FkWWFtbChcbiAgICAgICAgICAgICAgICAgICAgYCR7Y3dkfS8ke3N0YXRlRGlyfS9pbmNsdWRlcy8ke2ZzVXJsfWAsIHtpbnB1dHM6IHZhbHVlLmlucHV0cyB8fCB7fX0sIGV4cGFuZFZhcmlhYmxlc1xuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgaW5jbHVkZURhdGFzID0gaW5jbHVkZURhdGFzLmNvbmNhdChhd2FpdCB0aGlzLmluaXQoZmlsZURvYywgb3B0cykpO1xuICAgICAgICAgICAgfSBlbHNlIGlmICh2YWx1ZVtcInJlbW90ZVwiXSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGZzVXJsID0gVXRpbHMuZnNVcmwodmFsdWVbXCJyZW1vdGVcIl0pO1xuICAgICAgICAgICAgICAgIGNvbnN0IGZpbGVEb2MgPSBhd2FpdCBQYXJzZXIubG9hZFlhbWwoXG4gICAgICAgICAgICAgICAgICAgIGAke2N3ZH0vJHtzdGF0ZURpcn0vaW5jbHVkZXMvJHtmc1VybH1gLCB7aW5wdXRzOiB2YWx1ZS5pbnB1dHMgfHwge319LCBleHBhbmRWYXJpYWJsZXNcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIGluY2x1ZGVEYXRhcyA9IGluY2x1ZGVEYXRhcy5jb25jYXQoYXdhaXQgdGhpcy5pbml0KGZpbGVEb2MsIG9wdHMpKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEFzc2VydGlvbkVycm9yKHttZXNzYWdlOiBgRGlkbid0IHVuZGVyc3RhbmQgaW5jbHVkZSAke0pTT04uc3RyaW5naWZ5KHZhbHVlKX1gfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpbmNsdWRlRGF0YXMucHVzaChnaXRsYWJEYXRhKTtcbiAgICAgICAgcmV0dXJuIGluY2x1ZGVEYXRhcztcbiAgICB9XG5cbiAgICBzdGF0aWMgZXhwYW5kSW5jbHVkZSAoaTogYW55LCB2YXJpYWJsZXM6IHtba2V5OiBzdHJpbmddOiBzdHJpbmd9KTogYW55W10ge1xuICAgICAgICBsZXQgaW5jbHVkZSA9IGkgfHwgW107XG4gICAgICAgIGlmIChpbmNsdWRlICYmIGluY2x1ZGUubGVuZ3RoID09IG51bGwpIHtcbiAgICAgICAgICAgIGluY2x1ZGUgPSBbIGkgXTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodHlwZW9mIGluY2x1ZGUgPT09IFwic3RyaW5nXCIpIHtcbiAgICAgICAgICAgIGluY2x1ZGUgPSBbaW5jbHVkZV07XG4gICAgICAgIH1cblxuICAgICAgICBmb3IgKGNvbnN0IFtpbmRleCwgZW50cnldIG9mIE9iamVjdC5lbnRyaWVzKGluY2x1ZGUpKSB7XG4gICAgICAgICAgICBpZiAodHlwZW9mIGVudHJ5ID09PSBcInN0cmluZ1wiICYmIChlbnRyeS5zdGFydHNXaXRoKFwiaHR0cHM6XCIpIHx8IGVudHJ5LnN0YXJ0c1dpdGgoXCJodHRwOlwiKSkpIHtcbiAgICAgICAgICAgICAgICBpbmNsdWRlW2luZGV4XSA9IHtcInJlbW90ZVwiOiBlbnRyeX07XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZiBlbnRyeSA9PT0gXCJzdHJpbmdcIikge1xuICAgICAgICAgICAgICAgIGluY2x1ZGVbaW5kZXhdID0ge1wibG9jYWxcIjogZW50cnl9O1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBpbmNsdWRlW2luZGV4XSA9IGVudHJ5O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgIH1cblxuICAgICAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGluY2x1ZGUpIHtcbiAgICAgICAgICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGVudHJ5KSkge1xuICAgICAgICAgICAgICAgIGVudHJ5W2tleV0gPSBVdGlscy5leHBhbmRUZXh0KHZhbHVlLCB2YXJpYWJsZXMpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGluY2x1ZGU7XG4gICAgfVxuXG4gICAgc3RhdGljIGNvdmVydFRlbXBsYXRlVG9Qcm9qZWN0RmlsZSAodGVtcGxhdGU6IHN0cmluZyk6IHtwcm9qZWN0OiBzdHJpbmc7IHJlZjogc3RyaW5nOyBmaWxlOiBzdHJpbmc7IGRvbWFpbjogc3RyaW5nfSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBkb21haW46IFwiZ2l0bGFiLmNvbVwiLFxuICAgICAgICAgICAgcHJvamVjdDogXCJnaXRsYWItb3JnL2dpdGxhYlwiLFxuICAgICAgICAgICAgcmVmOiBcIkhFQURcIixcbiAgICAgICAgICAgIGZpbGU6IGBsaWIvZ2l0bGFiL2NpL3RlbXBsYXRlcy8ke3RlbXBsYXRlfWAsXG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgc3RhdGljIHBhcnNlSW5jbHVkZUNvbXBvbmVudCAoY29tcG9uZW50OiBzdHJpbmcpOiB7ZG9tYWluOiBzdHJpbmc7IHBvcnQ6IHN0cmluZzsgcHJvamVjdFBhdGg6IHN0cmluZzsgY29tcG9uZW50TmFtZTogc3RyaW5nOyByZWY6IHN0cmluZ30ge1xuICAgICAgICBhc3NlcnQoIWNvbXBvbmVudC5pbmNsdWRlcyhcIjovL1wiKSwgYFRoaXMgR2l0TGFiIENJIGNvbmZpZ3VyYXRpb24gaXMgaW52YWxpZDogY29tcG9uZW50OiBcXGAke2NvbXBvbmVudH1cXGAgc2hvdWxkIG5vdCBjb250YWluIHByb3RvY29sYCk7XG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11c2VsZXNzLWVzY2FwZVxuICAgICAgICBjb25zdCBwYXR0ZXJuID0gLyg/PGRvbWFpbj5bXi86XFxzXSspKDooPzxwb3J0PlxcZCspKT9cXC8oPzxwcm9qZWN0UGF0aD4uKylcXC8oPzxjb21wb25lbnROYW1lPlteQF0rKUAoPzxyZWY+LispLzsgLy8gaHR0cHM6Ly9yZWdleHIuY29tLzd2N2htXG4gICAgICAgIGNvbnN0IGdpdFJlbW90ZU1hdGNoID0gcGF0dGVybi5leGVjKGNvbXBvbmVudCk7XG5cbiAgICAgICAgaWYgKGdpdFJlbW90ZU1hdGNoPy5ncm91cHMgPT0gbnVsbCkgdGhyb3cgbmV3IEVycm9yKGBUaGlzIGlzIGEgYnVnLCBwbGVhc2UgY3JlYXRlIGEgZ2l0aHViIGlzc3VlIGlmIHRoaXMgaXMgc29tZXRoaW5nIHlvdSdyZSBleHBlY3RpbmcgdG8gd29yay4gaW5wdXQ6ICR7Y29tcG9uZW50fWApO1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgZG9tYWluOiBnaXRSZW1vdGVNYXRjaC5ncm91cHNbXCJkb21haW5cIl0sXG4gICAgICAgICAgICBwb3J0OiBnaXRSZW1vdGVNYXRjaC5ncm91cHNbXCJwb3J0XCJdLFxuICAgICAgICAgICAgcHJvamVjdFBhdGg6IGdpdFJlbW90ZU1hdGNoLmdyb3Vwc1tcInByb2plY3RQYXRoXCJdLFxuICAgICAgICAgICAgY29tcG9uZW50TmFtZTogYHRlbXBsYXRlcy8ke2dpdFJlbW90ZU1hdGNoLmdyb3Vwc1tcImNvbXBvbmVudE5hbWVcIl19YCxcbiAgICAgICAgICAgIHJlZjogZ2l0UmVtb3RlTWF0Y2guZ3JvdXBzW1wicmVmXCJdLFxuICAgICAgICB9O1xuICAgIH1cblxuICAgIHN0YXRpYyBhc3luYyBkb3dubG9hZEluY2x1ZGVSZW1vdGUgKGN3ZDogc3RyaW5nLCBzdGF0ZURpcjogc3RyaW5nLCB1cmw6IHN0cmluZywgZmV0Y2hJbmNsdWRlczogYm9vbGVhbik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBmc1VybCA9IFV0aWxzLmZzVXJsKHVybCk7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCB0YXJnZXQgPSBgJHtjd2R9LyR7c3RhdGVEaXJ9L2luY2x1ZGVzLyR7ZnNVcmx9YDtcbiAgICAgICAgICAgIGlmIChhd2FpdCBmcy5wYXRoRXhpc3RzKHRhcmdldCkgJiYgIWZldGNoSW5jbHVkZXMpIHJldHVybjtcbiAgICAgICAgICAgIGNvbnN0IHJlcyA9IGF3YWl0IGF4aW9zLmdldCh1cmwsIHtoZWFkZXJzOiB7XCJVc2VyLUFnZW50XCI6IFwiZ2l0bGFiLWNpLWxvY2FsXCJ9fSk7XG4gICAgICAgICAgICBhd2FpdCBmcy5vdXRwdXRGaWxlKHRhcmdldCwgcmVzLmRhdGEpO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgQXNzZXJ0aW9uRXJyb3Ioe21lc3NhZ2U6IGBSZW1vdGUgaW5jbHVkZSBjb3VsZCBub3QgYmUgZmV0Y2hlZCAke3VybH1cXG4ke2V9YH0pO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgc3RhdGljIGFzeW5jIGRvd25sb2FkSW5jbHVkZVByb2plY3RGaWxlIChjd2Q6IHN0cmluZywgc3RhdGVEaXI6IHN0cmluZywgcHJvamVjdDogc3RyaW5nLCByZWY6IHN0cmluZywgZmlsZTogc3RyaW5nLCBnaXREYXRhOiBHaXREYXRhLCBmZXRjaEluY2x1ZGVzOiBib29sZWFuKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IHJlbW90ZSA9IGdpdERhdGEucmVtb3RlO1xuICAgICAgICBjb25zdCBub3JtYWxpemVkRmlsZSA9IGZpbGUucmVwbGFjZSgvXlxcLysvLCBcIlwiKTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IHRhcmdldCA9IGAke3N0YXRlRGlyfS9pbmNsdWRlcy8ke3JlbW90ZS5ob3N0fS8ke3Byb2plY3R9LyR7cmVmfWA7XG4gICAgICAgICAgICBpZiAoYXdhaXQgZnMucGF0aEV4aXN0cyhgJHtjd2R9LyR7dGFyZ2V0fS8ke25vcm1hbGl6ZWRGaWxlfWApICYmICF