vite-plugin-react-server
Version:
Vite plugin for React Server Components (RSC)
486 lines (484 loc) • 91.5 kB
JavaScript
/**
* vite-plugin-react-server
* Copyright (c) Nico Brinkkemper
* MIT License
*/
import { DEFAULT_CONFIG, BASE_PATTERNS, DEFAULT_LOADER_CONFIG } from './defaults.js';
import { join, resolve } from 'node:path';
import { pluginRoot } from '../root.js';
import { createInputNormalizer } from '../helpers/inputNormalizer.js';
import { resolveDirectiveMatcher } from './resolveDirectiveMatcher.js';
import { resolveAllowedDirectives } from './resolveAllowedDirectives.js';
import { resolveRegExp } from './resolveRegExp.js';
import { handleError } from '../error/handleError.js';
import { createLogger } from 'vite';
import { getCondition } from './getCondition.js';
import { getNodeEnv } from './getNodeEnv.js';
import { getEnvironmentId, getStashedUserOptions, stashUserOptions } from './stashedOptionsState.js';
import { existsSync, readFileSync } from 'node:fs';
import { createRollupLikeHash } from './createRollupLikeHash.js';
const handleSearchQuery = (path) => {
const searchQuery = path.split("?")[1];
if (!searchQuery) return path;
const folder = path.split("/").slice(0, -1).join("/");
const filename = path.split("/").pop();
const fileNameExtIndex = filename?.lastIndexOf(".");
const fileNameWithoutExt = filename?.slice(0, fileNameExtIndex);
const extension = filename?.slice(fileNameExtIndex);
return `${folder}/${fileNameWithoutExt}.${searchQuery}.${extension}`;
};
const registerPath = (path, pattern, ext) => {
if (pattern && !pattern.test(path) || ext && !path.endsWith(ext)) {
return path + ext;
}
return path;
};
let originalOptions = null;
const resolveOptions = function _resolveOptions(options = {}, forceResolve = false, logger = createLogger(!options.verbose ? "error" : "info", {
prefix: "vite:plugin-react-server/config#resolveOptions"
})) {
if (!forceResolve && originalOptions == null) {
originalOptions = options;
} else if (originalOptions != null && options !== originalOptions) {
if (options.verbose) {
logger.info("options changed, forcing re-resolve");
}
forceResolve = true;
}
if (originalOptions != null && originalOptions.projectRoot !== options.projectRoot) {
if (options.verbose) {
logger.info(`projectRoot changed from ${originalOptions.projectRoot} to ${options.projectRoot}, forcing re-resolve`);
}
forceResolve = true;
}
const panicThreshold = typeof options.panicThreshold === "string" ? options.panicThreshold : DEFAULT_CONFIG.PANIC_THRESHOLD;
const envId = getEnvironmentId(getCondition(), process.env.NODE_ENV ?? "production");
const stashedOptions = getStashedUserOptions(envId);
if (stashedOptions && !forceResolve) {
return {
type: "success",
userOptions: stashedOptions
};
}
const loaderMode = options.loader?.mode ?? void 0;
const moduleBase = typeof options.moduleBase === "string" ? options.moduleBase : DEFAULT_CONFIG.MODULE_BASE;
const projectRoot = options.projectRoot ?? originalOptions?.projectRoot ?? process.cwd();
if (options.verbose && options.projectRoot != originalOptions?.projectRoot) {
logger.info(`[resolveOptions] new projectRoot: ${projectRoot}`);
}
const preserveModulesRoot = options.build?.preserveModulesRoot ?? DEFAULT_CONFIG.BUILD.preserveModulesRoot;
const currentMode = getNodeEnv(process.env.NODE_ENV);
const isDevelopment = currentMode === "development";
const preserveModulesRootString = !isDevelopment && preserveModulesRoot === false ? moduleBase : void 0;
const client = typeof options.build?.client === "string" ? options.build.client : DEFAULT_CONFIG.BUILD.client;
const outDir = typeof options.build?.outDir === "string" ? options.build.outDir : DEFAULT_CONFIG.BUILD.outDir;
const moduleBasePath = typeof options.moduleBasePath === "string" ? options.moduleBasePath : DEFAULT_CONFIG.MODULE_BASE_PATH;
const moduleBaseURL = typeof options.moduleBaseURL === "string" ? options.moduleBaseURL : DEFAULT_CONFIG.MODULE_BASE_URL;
const moduleRootPath = typeof options.moduleRootPath === "string" ? options.moduleRootPath : join(projectRoot, outDir, client);
const publicOrigin = typeof options.publicOrigin === "string" ? options.publicOrigin : DEFAULT_CONFIG.PUBLIC_ORIGIN;
const rscWorkerPath = typeof options.rscWorkerPath === "string" ? resolve(projectRoot, options.rscWorkerPath) : resolve(pluginRoot, DEFAULT_CONFIG.RSC_WORKER_PATH);
const htmlWorkerPath = typeof options.htmlWorkerPath === "string" ? resolve(projectRoot, options.htmlWorkerPath) : resolve(pluginRoot, DEFAULT_CONFIG.HTML_WORKER_PATH);
const loaderPath = typeof options.loaderPath === "string" ? resolve(projectRoot, options.loaderPath) : resolve(pluginRoot, DEFAULT_CONFIG.LOADER_PATH);
const jsExtension = typeof options.build?.jsExtension === "string" ? options.build.jsExtension : DEFAULT_CONFIG.BUILD.jsExtension;
const cssExtension = typeof options.build?.cssExtension === "string" ? options.build.cssExtension : DEFAULT_CONFIG.BUILD.cssExtension;
const cssModuleExtension = typeof options.build?.cssModuleExtension === "string" ? options.build.cssModuleExtension : DEFAULT_CONFIG.BUILD.cssModuleExtension;
const htmlExtension = typeof options.build?.htmlExtension === "string" ? options.build.htmlExtension : DEFAULT_CONFIG.BUILD.htmlExtension;
const jsonExtension = typeof options.build?.jsonExtension === "string" ? options.build.jsonExtension : DEFAULT_CONFIG.BUILD.jsonExtension;
const rscExtension = typeof options.build?.rscExtension === "string" ? options.build.rscExtension : DEFAULT_CONFIG.BUILD.rscExtension;
const rscOutputPath = typeof options.build?.rscOutputPath === "string" ? options.build.rscOutputPath : DEFAULT_CONFIG.BUILD.rscOutputPath;
const htmlOutputPath = typeof options.build?.htmlOutputPath === "string" ? options.build.htmlOutputPath : DEFAULT_CONFIG.BUILD.htmlOutputPath;
const modulePattern = resolveRegExp(
options.autoDiscover?.modulePattern,
DEFAULT_CONFIG.AUTO_DISCOVER.modulePattern
);
const jsonPattern = resolveRegExp(
options.autoDiscover?.jsonPattern,
DEFAULT_CONFIG.AUTO_DISCOVER.jsonPattern
);
const cssPattern = resolveRegExp(
options.autoDiscover?.cssPattern,
DEFAULT_CONFIG.AUTO_DISCOVER.cssPattern
);
const htmlPattern = resolveRegExp(
options.autoDiscover?.htmlPattern,
DEFAULT_CONFIG.AUTO_DISCOVER.htmlPattern
);
const rscPattern = resolveRegExp(
options.autoDiscover?.rscPattern,
DEFAULT_CONFIG.AUTO_DISCOVER.rscPattern
);
const clientPattern = resolveRegExp(
options.autoDiscover?.clientPattern,
DEFAULT_CONFIG.AUTO_DISCOVER.clientPattern
);
const serverPattern = resolveRegExp(
options.autoDiscover?.serverPattern,
DEFAULT_CONFIG.AUTO_DISCOVER.serverPattern
);
const nodePattern = resolveRegExp(
options.autoDiscover?.nodePattern,
DEFAULT_CONFIG.AUTO_DISCOVER.nodeOnly
);
const propsPattern = resolveRegExp(
options.autoDiscover?.propsPattern,
DEFAULT_CONFIG.AUTO_DISCOVER.propsPattern
);
const pagePattern = resolveRegExp(
options.autoDiscover?.pagePattern,
DEFAULT_CONFIG.AUTO_DISCOVER.pagePattern
);
const cssModulePattern = resolveRegExp(
options.autoDiscover?.cssModulePattern,
DEFAULT_CONFIG.AUTO_DISCOVER.cssModulePattern
);
const vendorPattern = resolveRegExp(
options.autoDiscover?.vendorPattern,
DEFAULT_CONFIG.AUTO_DISCOVER.vendorPattern
);
const virtualPattern = resolveRegExp(
options.autoDiscover?.virtualPattern,
DEFAULT_CONFIG.AUTO_DISCOVER.virtualPattern
);
const dotPattern = resolveRegExp(
options.autoDiscover?.dotPattern,
BASE_PATTERNS.DOT_FILES
);
const serverDirective = resolveRegExp(
options.loader?.serverDirective,
DEFAULT_LOADER_CONFIG.serverDirective
);
const clientDirective = resolveRegExp(
options.loader?.clientDirective,
DEFAULT_LOADER_CONFIG.clientDirective
);
const isServerFunctionCode = resolveDirectiveMatcher(
serverDirective,
(code, moduleId) => code.match(serverDirective) != null || typeof moduleId === "string" && serverPattern.test(moduleId) || false
);
const isClientComponentCode = resolveDirectiveMatcher(
clientDirective,
(code, moduleId) => code.match(clientDirective) != null || typeof moduleId === "string" && clientPattern.test(moduleId) || false
);
const isClientComponentByCode = resolveDirectiveMatcher(
clientDirective,
(code) => code.match(clientDirective) != null || false
);
const isClientComponentByName = (moduleId, _transformedModuleId) => typeof moduleId === "string" && clientPattern.test(moduleId) || false;
const hashOption = options.build?.hash ?? DEFAULT_CONFIG.BUILD.hash;
const hash = (input, _ssr, sourceContent) => {
if (!input) return "";
if (new RegExp(BASE_PATTERNS.EXT.NODE).test(input)) {
return input;
}
if (input.includes("node_modules")) {
return input;
}
const virtualPattern2 = resolveRegExp(
options.autoDiscover?.virtualPattern,
DEFAULT_CONFIG.AUTO_DISCOVER.virtualPattern
);
if (input.includes("_virtual") || virtualPattern2 && virtualPattern2.test(input)) {
return input;
}
if (hashOption === "false") {
return input;
}
if (htmlPattern.test(input) || rscPattern.test(input) || pagePattern.test(input) || propsPattern.test(input) || serverPattern.test(input)) {
return input;
}
let contentToHash;
if (sourceContent) {
contentToHash = sourceContent;
} else {
try {
const sourcePath = resolve(projectRoot, input);
if (existsSync(sourcePath)) {
contentToHash = readFileSync(sourcePath, "utf-8");
if (options.verbose) {
logger.info(`[hash] Reading source: ${sourcePath} (${contentToHash.length} chars)`);
}
} else {
contentToHash = input;
if (options.verbose) {
logger.warn(`[hash] File not found: ${sourcePath}, using filename: ${input}`);
}
}
} catch (error) {
contentToHash = input;
if (options.verbose) {
logger.warn(`[hash] Error reading ${input}: ${error}`);
}
}
}
const hashCharacters = typeof hashOption === "object" && hashOption?.format === "hex" ? "hex" : "base36";
const contentHash = createRollupLikeHash(contentToHash, hashCharacters);
const extensionIndex = input.lastIndexOf(".");
if (extensionIndex !== -1) {
const extension = input.slice(extensionIndex);
const filename = input.slice(0, extensionIndex);
return filename + "-" + contentHash + extension;
} else {
return input + "-" + contentHash;
}
};
const getOutputPath = (n, isAsset = false) => {
if (!n) return "";
let path = handleSearchQuery(n);
path = path.startsWith(moduleBase + moduleBasePath) ? path.slice(moduleBase.length + moduleBasePath.length) : path;
if (isAsset) {
const isModuleFile = modulePattern.test(path) || clientPattern.test(path) || serverPattern.test(path) || propsPattern.test(path) || pagePattern.test(path);
if (!isModuleFile) {
return path;
}
}
if (vendorPattern.test(path))
return registerPath(path, vendorPattern, jsExtension);
if (cssModulePattern.test(path))
return registerPath(path, cssModulePattern, cssExtension);
if (cssPattern.test(path))
return registerPath(path, cssPattern, cssExtension);
if (clientPattern.test(path))
return registerPath(path, clientPattern, jsExtension);
if (htmlPattern.test(path))
return registerPath(path, htmlPattern, htmlExtension);
if (jsonPattern.test(path))
return registerPath(path, jsonPattern, jsonExtension);
if (propsPattern.test(path))
return registerPath(path, propsPattern, jsExtension);
if (pagePattern.test(path))
return registerPath(path, pagePattern, jsExtension);
if (serverPattern.test(path))
return registerPath(path, serverPattern, jsExtension);
if (modulePattern.test(path))
return registerPath(path, modulePattern, jsExtension);
return registerPath(path, modulePattern, jsExtension);
};
const normalizer = options.normalizer ?? createInputNormalizer({
root: projectRoot,
preserveModulesRoot: preserveModulesRootString,
removeExtension: true,
moduleBasePath,
moduleBaseURL
});
function entryFile(n, ssr, sourceContent) {
const normalizedName = normalizer(n.name)[0];
let outputPath = getOutputPath(normalizedName);
if (preserveModulesRoot && n.name.startsWith("src/")) {
if (!outputPath.startsWith("src/")) {
if (outputPath.startsWith("/")) {
outputPath = "src" + outputPath;
} else {
outputPath = "src/" + outputPath;
}
}
}
return hash(outputPath, ssr, sourceContent);
}
function chunkFile(n, ssr, sourceContent) {
const normalizedName = normalizer(n.name)[0];
let outputPath = getOutputPath(normalizedName);
if (preserveModulesRoot && n.name.startsWith("src/")) {
if (!outputPath.startsWith("src/")) {
if (outputPath.startsWith("/")) {
outputPath = "src" + outputPath;
} else {
outputPath = "src/" + outputPath;
}
}
}
return hash(outputPath, ssr, sourceContent);
}
function assetFile(n, _ssr = false) {
const uniqueNames = Array.from(new Set(n.names));
let firstName = uniqueNames[0];
const assetsDir = build.assetsDir || "assets";
if (firstName.startsWith(assetsDir + "/" + moduleBase + "/")) {
firstName = assetsDir + "/" + firstName.slice((assetsDir + "/" + moduleBase + "/").length);
} else if (moduleBasePath && firstName.startsWith(moduleBasePath)) {
firstName = firstName.slice(moduleBasePath.length);
} else if (moduleBaseURL && moduleBaseURL !== "/" && moduleBaseURL !== "" && firstName.startsWith(moduleBaseURL)) {
firstName = firstName.slice(moduleBaseURL.length);
} else if (firstName.startsWith(moduleBase + "/")) {
firstName = firstName.slice(moduleBase.length + 1);
}
if (firstName.endsWith(".css")) {
if (!firstName.startsWith(assetsDir + "/")) {
firstName = assetsDir + "/" + firstName;
}
return hash(firstName);
}
return hash(getOutputPath(firstName, true));
}
const build = {
pages: options.build?.pages ?? DEFAULT_CONFIG.BUILD.pages,
assetsDir: options.build?.assetsDir ?? DEFAULT_CONFIG.BUILD.assetsDir,
client: options.build?.client ?? DEFAULT_CONFIG.BUILD.client,
server: options.build?.server ?? DEFAULT_CONFIG.BUILD.server,
static: options.build?.static ?? DEFAULT_CONFIG.BUILD.static,
api: options.build?.api ?? DEFAULT_CONFIG.BUILD.api,
preserveModulesRoot: options.build?.preserveModulesRoot ?? DEFAULT_CONFIG.BUILD.preserveModulesRoot,
outDir: options.build?.outDir ?? DEFAULT_CONFIG.BUILD.outDir,
hash: options.build?.hash ?? DEFAULT_CONFIG.BUILD.hash,
extensionMap: {
// File extensions first (more specific patterns should come first)
[BASE_PATTERNS.EXT.CSS]: cssExtension,
[BASE_PATTERNS.EXT.JSON]: jsonExtension,
[BASE_PATTERNS.EXT.HTML]: htmlExtension,
[BASE_PATTERNS.EXT.RSC]: rscExtension,
// Special case for .node files
[BASE_PATTERNS.EXT.NODE]: BASE_PATTERNS.EXT.NODE + (options.build?.jsExtension ?? DEFAULT_CONFIG.BUILD.jsExtension),
// General module pattern last (less specific)
[BASE_PATTERNS.MODULE]: jsExtension,
...options.build?.extensionMap
},
entryFile,
chunkFile,
assetFile,
rscOutputPath,
htmlOutputPath,
moduleExtension: jsExtension,
jsExtension,
cssExtension,
htmlExtension,
jsonExtension,
rscExtension,
cssModuleExtension,
nodeExtension: DEFAULT_CONFIG.BUILD.nodeExtension,
useRscWorker: options.build?.useRscWorker ?? DEFAULT_CONFIG.BUILD.useRscWorker,
useHtmlWorker: options.build?.useHtmlWorker ?? // Force useHtmlWorker to true when build.pages is explicitly configured, regardless of default logic
(options.build?.pages && (Array.isArray(options.build.pages) || typeof options.build.pages === "function")) ? true : DEFAULT_CONFIG.BUILD.useHtmlWorker,
renderMode: options.build?.renderMode ?? "parallel",
batchSize: options.build?.batchSize ?? 8
};
const dev = {
useHtmlWorker: options.dev?.useHtmlWorker ?? DEFAULT_CONFIG.DEV.useHtmlWorker,
useRscWorker: options.dev?.useRscWorker ?? DEFAULT_CONFIG.DEV.useRscWorker
};
const autoDiscover = {
clientEntry: options.autoDiscover?.clientEntry ?? DEFAULT_CONFIG.AUTO_DISCOVER.clientEntry,
serverEntry: options.autoDiscover?.serverEntry ?? DEFAULT_CONFIG.AUTO_DISCOVER.serverEntry,
cssEntry: options.autoDiscover?.cssEntry ?? DEFAULT_CONFIG.AUTO_DISCOVER.cssEntry,
jsonEntry: options.autoDiscover?.jsonEntry ?? DEFAULT_CONFIG.AUTO_DISCOVER.jsonEntry,
htmlEntry: options.autoDiscover?.htmlEntry ?? DEFAULT_CONFIG.AUTO_DISCOVER.htmlEntry,
modulePattern,
jsonPattern,
cssPattern,
htmlPattern,
rscPattern,
clientPattern,
serverPattern,
nodePattern,
propsPattern,
pagePattern,
cssModulePattern,
vendorPattern,
virtualPattern,
dotPattern
};
const allowedDirectives = resolveAllowedDirectives(
options.loader?.allowedDirectives ?? DEFAULT_LOADER_CONFIG.allowedDirectives
);
const loader = loaderMode ? {
serverDirective: resolveRegExp(
options.loader?.serverDirective,
DEFAULT_LOADER_CONFIG.serverDirective
),
clientDirective: resolveRegExp(
options.loader?.clientDirective,
DEFAULT_LOADER_CONFIG.clientDirective
),
allowedDirectives,
getDirectiveType: options.loader?.getDirectiveType ?? options.loader?.allowedDirectives ? (directive) => {
if (options.loader?.allowedDirectives) {
if (Array.isArray(options.loader?.allowedDirectives)) {
return options.loader?.allowedDirectives.includes(directive) ? directive === "use server" ? "server" : "client" : void 0;
} else {
const config = options.loader?.allowedDirectives[directive];
return config ? directive === "use server" ? "server" : "client" : void 0;
}
}
return void 0;
} : DEFAULT_LOADER_CONFIG.getDirectiveType,
mode: loaderMode,
importServerPath: options.loader?.importServerPath ?? DEFAULT_CONFIG.RSC_LOADER[loaderMode].importServerPath,
importClientPath: options.loader?.importClientPath ?? DEFAULT_CONFIG.RSC_LOADER[loaderMode].importClientPath,
registerClientReferenceName: options.loader?.registerClientReferenceName ?? DEFAULT_CONFIG.RSC_LOADER[loaderMode].registerClientReferenceName,
registerServerReferenceName: options.loader?.registerServerReferenceName ?? DEFAULT_CONFIG.RSC_LOADER[loaderMode].registerServerReferenceName,
isServerFunctionCode,
isClientComponentCode,
isClientComponentByCode,
isClientComponentByName,
parse: options?.loader?.parse ?? DEFAULT_LOADER_CONFIG.parse,
moduleID: options?.loader?.moduleID ?? options?.moduleID
} : void 0;
const pipeableStreamOptions = options.pipeableStreamOptions ? options.pipeableStreamOptions : {};
try {
const userOptions = {
projectRoot,
moduleBase,
moduleBasePath,
moduleBaseURL,
moduleRootPath,
publicOrigin,
build,
dev,
verbose: options.verbose ?? DEFAULT_CONFIG.VERBOSE,
availableEnvironments: options.availableEnvironments,
strategy: options.strategy,
onMetrics: typeof options.onMetrics === "function" ? options.onMetrics : DEFAULT_CONFIG.ON_METRICS,
onEvent: typeof options.onEvent === "function" ? options.onEvent : DEFAULT_CONFIG.ON_EVENT,
Page: options.Page,
props: options.props,
Html: options.Html ?? DEFAULT_CONFIG.HTML,
Root: options.Root ?? DEFAULT_CONFIG.ROOT,
components: options.components,
normalizer,
moduleID: options.moduleID,
// if not provided, will be created when config hook is called
pageExportName: options.pageExportName ?? DEFAULT_CONFIG.PAGE_EXPORT_NAME,
propsExportName: options.propsExportName ?? DEFAULT_CONFIG.PROPS_EXPORT_NAME,
htmlExportName: options.htmlExportName ?? DEFAULT_CONFIG.HTML_EXPORT_NAME,
rootExportName: options.rootExportName ?? DEFAULT_CONFIG.ROOT_EXPORT_NAME,
css: {
inlineCss: options.css?.inlineCss ?? DEFAULT_CONFIG.CSS.inlineCss,
inlineThreshold: options.css?.inlineThreshold ?? DEFAULT_CONFIG.CSS.inlineThreshold,
inlinePatterns: options.css?.inlinePatterns ?? DEFAULT_CONFIG.CSS.inlinePatterns,
linkPatterns: options.css?.linkPatterns ?? DEFAULT_CONFIG.CSS.linkPatterns
},
htmlWorkerPath,
rscWorkerPath,
loaderPath,
reactLoaderPath: options.reactLoaderPath ?? DEFAULT_CONFIG.REACT_LOADER_PATH,
cssLoaderPath: options.cssLoaderPath ?? DEFAULT_CONFIG.CSS_LOADER_PATH,
envLoaderPath: options.envLoaderPath ?? DEFAULT_CONFIG.ENV_LOADER_PATH,
clientEntry: options.clientEntry ?? DEFAULT_CONFIG.CLIENT_ENTRY,
serverEntry: options.serverEntry ?? DEFAULT_CONFIG.SERVER_ENTRY,
clientPackages: options.clientPackages,
autoDiscover,
loader,
pipeableStreamOptions,
rscTimeout: typeof options.rscTimeout === "number" ? options.rscTimeout : DEFAULT_CONFIG.RSC_TIMEOUT,
htmlTimeout: typeof options.htmlTimeout === "number" ? options.htmlTimeout : DEFAULT_CONFIG.HTML_TIMEOUT,
htmlWorkerStartupTimeout: typeof options.htmlWorkerStartupTimeout === "number" ? options.htmlWorkerStartupTimeout : DEFAULT_CONFIG.HTML_WORKER_STARTUP_TIMEOUT,
rscWorkerStartupTimeout: typeof options.rscWorkerStartupTimeout === "number" ? options.rscWorkerStartupTimeout : DEFAULT_CONFIG.RSC_WORKER_STARTUP_TIMEOUT,
fileWriteTimeout: typeof options.fileWriteTimeout === "number" ? options.fileWriteTimeout : DEFAULT_CONFIG.FILE_WRITE_TIMEOUT,
workerShutdownTimeout: typeof options.workerShutdownTimeout === "number" ? options.workerShutdownTimeout : DEFAULT_CONFIG.WORKER_SHUTDOWN_TIMEOUT,
panicThreshold
};
stashUserOptions(envId, userOptions);
return {
type: "success",
userOptions
};
} catch (error) {
return {
type: "error",
error: handleError({
error,
logger,
panicThreshold})
};
}
};
export { resolveOptions };
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzb2x2ZU9wdGlvbnMuanMiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3BsdWdpbi9jb25maWcvcmVzb2x2ZU9wdGlvbnMudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUgeyBQcmVSZW5kZXJlZEFzc2V0LCBQcmVSZW5kZXJlZENodW5rIH0gZnJvbSBcInJvbGx1cFwiO1xuaW1wb3J0IHR5cGUge1xuICBTdHJlYW1QbHVnaW5PcHRpb25zLFxuICBSZXNvbHZlZFVzZXJPcHRpb25zLFxuICBQYWdlTmFtZSxcbiAgUHJvcHNOYW1lLFxuICBIdG1sTmFtZSxcbiAgUm9vdE5hbWUsXG59IGZyb20gXCIuLi90eXBlcy5qc1wiO1xuaW1wb3J0IHtcbiAgQkFTRV9QQVRURVJOUyxcbiAgREVGQVVMVF9DT05GSUcsXG4gIERFRkFVTFRfTE9BREVSX0NPTkZJRyxcbn0gZnJvbSBcIi4vZGVmYXVsdHMuanNcIjtcbmltcG9ydCB7IGpvaW4sIHJlc29sdmUgfSBmcm9tIFwibm9kZTpwYXRoXCI7XG5pbXBvcnQgeyBwbHVnaW5Sb290IH0gZnJvbSBcIi4uL3Jvb3QuanNcIjtcbmltcG9ydCB7IGNyZWF0ZUlucHV0Tm9ybWFsaXplciB9IGZyb20gXCIuLi9oZWxwZXJzL2lucHV0Tm9ybWFsaXplci5qc1wiO1xuaW1wb3J0IHsgcmVzb2x2ZURpcmVjdGl2ZU1hdGNoZXIgfSBmcm9tIFwiLi9yZXNvbHZlRGlyZWN0aXZlTWF0Y2hlci5qc1wiO1xuaW1wb3J0IHsgcmVzb2x2ZUFsbG93ZWREaXJlY3RpdmVzIH0gZnJvbSBcIi4vcmVzb2x2ZUFsbG93ZWREaXJlY3RpdmVzLmpzXCI7XG5pbXBvcnQgeyByZXNvbHZlUmVnRXhwIH0gZnJvbSBcIi4vcmVzb2x2ZVJlZ0V4cC5qc1wiO1xuaW1wb3J0IHR5cGUgeyBMb2FkZXJDb25maWcgfSBmcm9tIFwiLi4vbG9hZGVyL3R5cGVzLmpzXCI7XG5pbXBvcnQgeyBoYW5kbGVFcnJvciB9IGZyb20gXCIuLi9lcnJvci9oYW5kbGVFcnJvci5qc1wiO1xuaW1wb3J0IHsgY3JlYXRlTG9nZ2VyLCB0eXBlIExvZ2dlciB9IGZyb20gXCJ2aXRlXCI7XG5pbXBvcnQgeyBnZXRDb25kaXRpb24gfSBmcm9tIFwiLi9nZXRDb25kaXRpb24uanNcIjtcbmltcG9ydCB7IGdldE5vZGVFbnYgfSBmcm9tIFwiLi9nZXROb2RlRW52LmpzXCI7XG5pbXBvcnQgeyBzdGFzaFVzZXJPcHRpb25zLCBnZXRTdGFzaGVkVXNlck9wdGlvbnMsIGdldEVudmlyb25tZW50SWQgfSBmcm9tIFwiLi9zdGFzaGVkT3B0aW9uc1N0YXRlLmpzXCI7XG5pbXBvcnQgeyByZWFkRmlsZVN5bmMsIGV4aXN0c1N5bmMgfSBmcm9tIFwibm9kZTpmc1wiO1xuaW1wb3J0IHsgY3JlYXRlUm9sbHVwTGlrZUhhc2ggfSBmcm9tIFwiLi9jcmVhdGVSb2xsdXBMaWtlSGFzaC5qc1wiO1xuXG5leHBvcnQgdHlwZSBSZXNvbHZlT3B0aW9uc1JldHVybiA9XG4gIHwge1xuICAgICAgdHlwZTogXCJzdWNjZXNzXCI7XG4gICAgICB1c2VyT3B0aW9uczogUmVzb2x2ZWRVc2VyT3B0aW9ucztcbiAgICAgIGVycm9yPzogbmV2ZXI7XG4gICAgfVxuICB8IHsgdHlwZTogXCJlcnJvclwiOyBlcnJvcjogdW5rbm93bjsgdXNlck9wdGlvbnM/OiBuZXZlciB9O1xuXG5leHBvcnQgdHlwZSBSZXNvbHZlT3B0aW9uc0ZuID0gKFxuICBvcHRpb25zOiBTdHJlYW1QbHVnaW5PcHRpb25zLFxuICBmb3JjZVJlc29sdmU/OiBib29sZWFuLFxuICBsb2dnZXI/OiBMb2dnZXJcbikgPT4gUmVzb2x2ZU9wdGlvbnNSZXR1cm47XG5cbi8vIC8qKlxuLy8gICogRW5zdXJlcyBhIHBhdGggZW5kcyB3aXRoIC5qcyBleHRlbnNpb25cbi8vICAqL1xuLy8gY29uc3QgYWRkRXh0ZW5zaW9uID0gKHBhdGg6IHN0cmluZywgZXh0ZW5zaW9uOiBzdHJpbmcgPSBcImpzXCIpID0+IHtcbi8vICAgaWYgKHBhdGguZW5kc1dpdGgoYC4ke2V4dGVuc2lvbn1gKSkgcmV0dXJuIHBhdGg7XG4vLyAgIGlmIChwYXRoLmVuZHNXaXRoKFwiLy5cIikpIHJldHVybiBwYXRoLnNsaWNlKDAsIC0yKSArIFwiLlwiICsgZXh0ZW5zaW9uO1xuLy8gICBpZiAocGF0aC5lbmRzV2l0aChcIi5cIikpIHJldHVybiBwYXRoICsgXCIuXCIgKyBleHRlbnNpb247XG4vLyAgIHJldHVybiBwYXRoICsgXCIuXCIgKyBleHRlbnNpb247XG4vLyB9O1xuXG4vKipcbiAqIEhhbmRsZXMgc2VhcmNoIHF1ZXJ5IHBhcmFtZXRlcnMgaW4gZmlsZSBwYXRoc1xuICovXG5jb25zdCBoYW5kbGVTZWFyY2hRdWVyeSA9IChwYXRoOiBzdHJpbmcpID0+IHtcbiAgY29uc3Qgc2VhcmNoUXVlcnkgPSBwYXRoLnNwbGl0KFwiP1wiKVsxXTtcbiAgaWYgKCFzZWFyY2hRdWVyeSkgcmV0dXJuIHBhdGg7XG4gIGNvbnN0IGZvbGRlciA9IHBhdGguc3BsaXQoXCIvXCIpLnNsaWNlKDAsIC0xKS5qb2luKFwiL1wiKTtcbiAgY29uc3QgZmlsZW5hbWUgPSBwYXRoLnNwbGl0KFwiL1wiKS5wb3AoKTtcbiAgY29uc3QgZmlsZU5hbWVFeHRJbmRleCA9IGZpbGVuYW1lPy5sYXN0SW5kZXhPZihcIi5cIik7XG4gIGNvbnN0IGZpbGVOYW1lV2l0aG91dEV4dCA9IGZpbGVuYW1lPy5zbGljZSgwLCBmaWxlTmFtZUV4dEluZGV4KTtcbiAgY29uc3QgZXh0ZW5zaW9uID0gZmlsZW5hbWU/LnNsaWNlKGZpbGVOYW1lRXh0SW5kZXgpO1xuICByZXR1cm4gYCR7Zm9sZGVyfS8ke2ZpbGVOYW1lV2l0aG91dEV4dH0uJHtzZWFyY2hRdWVyeX0uJHtleHRlbnNpb259YDtcbn07XG5cbi8qKlxuICogUmVnaXN0ZXJzIGEgcGF0aCB3aXRoIGFuIG9wdGlvbmFsIHBhdHRlcm4gbWF0Y2hlciBhbmQgZXh0ZW5zaW9uLlxuICogSWYgYSBwYXR0ZXJuIG1hdGNoZXMgYW5kIHRoZSBwYXRoIGRvZXNuJ3QgZW5kIHdpdGggdGhlIGV4dGVuc2lvbiwgdGhlIGV4dGVuc2lvbiBpcyBhcHBlbmRlZC5cbiAqXG4gKiBAcGFyYW0gcGF0aCAtIFRoZSBwYXRoIHRvIHJlZ2lzdGVyXG4gKiBAcGFyYW0gcGF0dGVybiAtIE9wdGlvbmFsIHBhdHRlcm4gbWF0Y2hlciBmdW5jdGlvbiB0aGF0IHJldHVybnMgdHJ1ZSBpZiB0aGUgcGF0aCBtYXRjaGVzXG4gKiBAcGFyYW0gZXh0IC0gT3B0aW9uYWwgZXh0ZW5zaW9uIHRvIGFwcGVuZCBpZiBwYXR0ZXJuIG1hdGNoZXMgYW5kIHBhdGggZG9lc24ndCBhbHJlYWR5IGVuZCB3aXRoIGl0XG4gKi9cbmNvbnN0IHJlZ2lzdGVyUGF0aCA9IChwYXRoOiBzdHJpbmcsIHBhdHRlcm4/OiBSZWdFeHAsIGV4dD86IHN0cmluZykgPT4ge1xuICAvLyBJZiB3ZSBoYXZlIGEgcGF0dGVybiBhbmQgaXQgZG9lc24ndCBtYXRjaCwgb3Igd2UgaGF2ZSBhbiBleHRlbnNpb24gYW5kIHRoZSBwYXRoIGRvZXNuJ3QgZW5kIHdpdGggaXQsIGFwcGVuZCB0aGUgZXh0ZW5zaW9uXG4gIGlmICgocGF0dGVybiAmJiAhcGF0dGVybi50ZXN0KHBhdGgpKSB8fCAoZXh0ICYmICFwYXRoLmVuZHNXaXRoKGV4dCkpKSB7XG4gICAgcmV0dXJuIHBhdGggKyBleHQ7XG4gIH1cbiAgcmV0dXJuIHBhdGg7XG59O1xuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBNYWluIE9wdGlvbnMgUmVzb2x2ZXJcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxubGV0IG9yaWdpbmFsT3B0aW9uczogYW55IHwgbnVsbCA9IG51bGw7XG4vKipcbiAqIFJlc29sdmVzIHRoZSB1c2VyIG9wdGlvbnMgZm9yIHRoZSBwbHVnaW4uXG4gKlxuICogQHBhcmFtIG9wdGlvbnMgLSBUaGUgdXNlciBvcHRpb25zIHRvIHJlc29sdmUuXG4gKiBAcmV0dXJucyBUaGUgcmVzb2x2ZWQgb3B0aW9ucy5cbiAqL1xuZXhwb3J0IGNvbnN0IHJlc29sdmVPcHRpb25zOiBSZXNvbHZlT3B0aW9uc0ZuID0gZnVuY3Rpb24gX3Jlc29sdmVPcHRpb25zKFxuICBvcHRpb25zID0ge30gYXMgU3RyZWFtUGx1Z2luT3B0aW9ucyxcbiAgZm9yY2VSZXNvbHZlID0gZmFsc2UsXG4gIGxvZ2dlciA9IGNyZWF0ZUxvZ2dlcighb3B0aW9ucy52ZXJib3NlID8gXCJlcnJvclwiIDogXCJpbmZvXCIsIHtcbiAgICBwcmVmaXg6IFwidml0ZTpwbHVnaW4tcmVhY3Qtc2VydmVyL2NvbmZpZyNyZXNvbHZlT3B0aW9uc1wiLFxuICB9KVxuKSB7XG4gIGlmICghZm9yY2VSZXNvbHZlICYmIG9yaWdpbmFsT3B0aW9ucyA9PSBudWxsKSB7XG4gICAgb3JpZ2luYWxPcHRpb25zID0gb3B0aW9ucztcbiAgfSBlbHNlIGlmIChvcmlnaW5hbE9wdGlvbnMgIT0gbnVsbCAmJiBvcHRpb25zICE9PSBvcmlnaW5hbE9wdGlvbnMpIHtcbiAgICBpZiAob3B0aW9ucy52ZXJib3NlKSB7XG4gICAgICBsb2dnZXIuaW5mbyhcIm9wdGlvbnMgY2hhbmdlZCwgZm9yY2luZyByZS1yZXNvbHZlXCIpO1xuICAgIH1cbiAgICBmb3JjZVJlc29sdmUgPSB0cnVlO1xuICB9XG4gIFxuICAvLyBGb3JjZSByZS1yZXNvbHZlIGlmIHByb2plY3RSb290IGNoYW5nZWRcbiAgaWYgKG9yaWdpbmFsT3B0aW9ucyAhPSBudWxsICYmIG9yaWdpbmFsT3B0aW9ucy5wcm9qZWN0Um9vdCAhPT0gb3B0aW9ucy5wcm9qZWN0Um9vdCkge1xuICAgIGlmIChvcHRpb25zLnZlcmJvc2UpIHtcbiAgICAgIGxvZ2dlci5pbmZvKGBwcm9qZWN0Um9vdCBjaGFuZ2VkIGZyb20gJHtvcmlnaW5hbE9wdGlvbnMucHJvamVjdFJvb3R9IHRvICR7b3B0aW9ucy5wcm9qZWN0Um9vdH0sIGZvcmNpbmcgcmUtcmVzb2x2ZWApO1xuICAgIH1cbiAgICBmb3JjZVJlc29sdmUgPSB0cnVlO1xuICB9XG4gIFxuICBjb25zdCBwYW5pY1RocmVzaG9sZCA9XG4gICAgdHlwZW9mIG9wdGlvbnMucGFuaWNUaHJlc2hvbGQgPT09IFwic3RyaW5nXCJcbiAgICAgID8gb3B0aW9ucy5wYW5pY1RocmVzaG9sZFxuICAgICAgOiBERUZBVUxUX0NPTkZJRy5QQU5JQ19USFJFU0hPTEQ7XG4gIC8vIHNpbmNlIHBhbmljVGhyZXNob2xkIGFmZmVjdHMgdGhlIGJlaGF2aW9yIG9mIHRoZSBwbHVnaW4sIHdlIG5lZWQgdG8gcmUtcmVzb2x2ZSB0aGUgb3B0aW9ucyBpZiBpdCBjaGFuZ2VzXG4gIGNvbnN0IGVudklkID0gZ2V0RW52aXJvbm1lbnRJZChnZXRDb25kaXRpb24oKSwgcHJvY2Vzcy5lbnYuTk9ERV9FTlYgPz8gXCJwcm9kdWN0aW9uXCIpO1xuXG4gIC8vIFJldHVybiBzdGFzaGVkIG9wdGlvbnMgaWYgYXZhaWxhYmxlXG4gIGNvbnN0IHN0YXNoZWRPcHRpb25zID0gZ2V0U3Rhc2hlZFVzZXJPcHRpb25zKGVudklkKTtcbiAgaWYgKHN0YXNoZWRPcHRpb25zICYmICFmb3JjZVJlc29sdmUpIHtcbiAgICByZXR1cm4ge1xuICAgICAgdHlwZTogXCJzdWNjZXNzXCIsXG4gICAgICB1c2VyT3B0aW9uczogc3Rhc2hlZE9wdGlvbnMgYXMgbmV2ZXIsXG4gICAgfTtcbiAgfVxuXG4gIGNvbnN0IGxvYWRlck1vZGUgPSBvcHRpb25zLmxvYWRlcj8ubW9kZSA/PyB1bmRlZmluZWQ7XG4gIC8vIE1vZHVsZSBwYXRoIGNvbmZpZ3VyYXRpb25cbiAgY29uc3QgbW9kdWxlQmFzZSA9XG4gICAgdHlwZW9mIG9wdGlvbnMubW9kdWxlQmFzZSA9PT0gXCJzdHJpbmdcIlxuICAgICAgPyBvcHRpb25zLm1vZHVsZUJhc2VcbiAgICAgIDogREVGQVVMVF9DT05GSUcuTU9EVUxFX0JBU0U7XG5cbiAgLy8gQmFzaWMgY29uZmlndXJhdGlvbiAtIHVzZSB0aGUgZmlyc3QgcHJvamVjdFJvb3QgdGhhdCB3YXMgcHJvdmlkZWQsIG9yIGZhbGwgYmFjayB0byBjdXJyZW50IG9wdGlvbnNcbiAgY29uc3QgcHJvamVjdFJvb3QgPSBvcHRpb25zLnByb2plY3RSb290ID8/IG9yaWdpbmFsT3B0aW9ucz8ucHJvamVjdFJvb3QgPz8gcHJvY2Vzcy5jd2QoKTtcbiAgXG4gIGlmIChvcHRpb25zLnZlcmJvc2UgJiYgb3B0aW9ucy5wcm9qZWN0Um9vdCAhPSBvcmlnaW5hbE9wdGlvbnM/LnByb2plY3RSb290KSB7XG4gICAgbG9nZ2VyLmluZm8oYFtyZXNvbHZlT3B0aW9uc10gbmV3IHByb2plY3RSb290OiAke3Byb2plY3RSb290fWApO1xuICB9XG5cbiAgLy8gQnVpbGQgb3B0aW9uc1xuICBjb25zdCBwcmVzZXJ2ZU1vZHVsZXNSb290ID1cbiAgICBvcHRpb25zLmJ1aWxkPy5wcmVzZXJ2ZU1vZHVsZXNSb290ID8/XG4gICAgREVGQVVMVF9DT05GSUcuQlVJTEQucHJlc2VydmVNb2R1bGVzUm9vdDtcblxuICAvLyBHZXQgY3VycmVudCBtb2RlIHRvIGNoZWNrIGlmIHdlJ3JlIGluIGRldmVsb3BtZW50XG4gIGNvbnN0IGN1cnJlbnRNb2RlID0gZ2V0Tm9kZUVudihwcm9jZXNzLmVudi5OT0RFX0VOVik7XG4gIGNvbnN0IGlzRGV2ZWxvcG1lbnQgPSBjdXJyZW50TW9kZSA9PT0gXCJkZXZlbG9wbWVudFwiO1xuXG4gIC8vIFJvbGx1cCdzIHByZXNlcnZlTW9kdWxlc1Jvb3Qgd29ya3MgaW4gcmV2ZXJzZSBvZiB3aGF0IHlvdSdkIGV4cGVjdDpcbiAgLy8gLSBXaGVuIHVzZXIgd2FudHMgcHJlc2VydmF0aW9uICh0cnVlKTogcGFzcyB1bmRlZmluZWQgdG8gUm9sbHVwIChkb24ndCBzdHJpcCBhbnl0aGluZylcbiAgLy8gLSBXaGVuIHVzZXIgd2FudHMgc3RyaXBwaW5nIChmYWxzZSk6IHBhc3MgbW9kdWxlQmFzZSB0byBSb2xsdXAgKHN0cmlwIHRoaXMgcGF0aClcbiAgLy8gQ1JJVElDQUw6IEluIGRldmVsb3BtZW50IG1vZGUsIE5FVkVSIHN0cmlwIG1vZHVsZUJhc2UgKHNyYy8pIGJlY2F1c2UgVml0ZSBzZXJ2ZXMgZnJvbSBzb3VyY2UgbG9jYXRpb25zXG4gIGNvbnN0IHByZXNlcnZlTW9kdWxlc1Jvb3RTdHJpbmcgPVxuICAgICFpc0RldmVsb3BtZW50ICYmIHByZXNlcnZlTW9kdWxlc1Jvb3QgPT09IGZhbHNlXG4gICAgICA/IG1vZHVsZUJhc2UgLy8gU3RyaXAgc3JjLyBmcm9tIG91dHB1dCBwYXRocyAocHJvZHVjdGlvbiBvbmx5KVxuICAgICAgOiB1bmRlZmluZWQ7IC8vIEtlZXAgc3JjLyBpbiBvdXRwdXQgcGF0aHNcblxuICBjb25zdCBjbGllbnQgPVxuICAgIHR5cGVvZiBvcHRpb25zLmJ1aWxkPy5jbGllbnQgPT09IFwic3RyaW5nXCJcbiAgICAgID8gb3B0aW9ucy5idWlsZC5jbGllbnRcbiAgICAgIDogREVGQVVMVF9DT05GSUcuQlVJTEQuY2xpZW50O1xuXG4gIGNvbnN0IG91dERpciA9XG4gICAgdHlwZW9mIG9wdGlvbnMuYnVpbGQ/Lm91dERpciA9PT0gXCJzdHJpbmdcIlxuICAgICAgPyBvcHRpb25zLmJ1aWxkLm91dERpclxuICAgICAgOiBERUZBVUxUX0NPTkZJRy5CVUlMRC5vdXREaXI7XG5cbiAgLy8gVXNlIGRlZmF1bHRzIGZvciBub3cgLSB0aGVzZSB3aWxsIGJlIHVwZGF0ZWQgbGF0ZXIgd2hlbiB3ZSBoYXZlIHRoZSBjb25maWcgd2l0aCBwcm9wZXIgcHJlZml4XG4gIGNvbnN0IG1vZHVsZUJhc2VQYXRoID1cbiAgICB0eXBlb2Ygb3B0aW9ucy5tb2R1bGVCYXNlUGF0aCA9PT0gXCJzdHJpbmdcIlxuICAgICAgPyBvcHRpb25zLm1vZHVsZUJhc2VQYXRoXG4gICAgICA6IERFRkFVTFRfQ09ORklHLk1PRFVMRV9CQVNFX1BBVEg7XG5cbiAgY29uc3QgbW9kdWxlQmFzZVVSTCA9XG4gICAgdHlwZW9mIG9wdGlvbnMubW9kdWxlQmFzZVVSTCA9PT0gXCJzdHJpbmdcIlxuICAgICAgPyBvcHRpb25zLm1vZHVsZUJhc2VVUkxcbiAgICAgIDogREVGQVVMVF9DT05GSUcuTU9EVUxFX0JBU0VfVVJMO1xuXG4gIGNvbnN0IG1vZHVsZVJvb3RQYXRoID1cbiAgICB0eXBlb2Ygb3B0aW9ucy5tb2R1bGVSb290UGF0aCA9PT0gXCJzdHJpbmdcIlxuICAgICAgPyBvcHRpb25zLm1vZHVsZVJvb3RQYXRoXG4gICAgICA6IGpvaW4ocHJvamVjdFJvb3QsIG91dERpciwgY2xpZW50KTtcblxuICBjb25zdCBwdWJsaWNPcmlnaW4gPVxuICAgIHR5cGVvZiBvcHRpb25zLnB1YmxpY09yaWdpbiA9PT0gXCJzdHJpbmdcIlxuICAgICAgPyBvcHRpb25zLnB1YmxpY09yaWdpblxuICAgICAgOiBERUZBVUxUX0NPTkZJRy5QVUJMSUNfT1JJR0lOO1xuXG5cbiAgY29uc3QgcnNjV29ya2VyUGF0aCA9XG4gICAgdHlwZW9mIG9wdGlvbnMucnNjV29ya2VyUGF0aCA9PT0gXCJzdHJpbmdcIlxuICAgICAgPyByZXNvbHZlKHByb2plY3RSb290LCBvcHRpb25zLnJzY1dvcmtlclBhdGgpXG4gICAgICA6IHJlc29sdmUocGx1Z2luUm9vdCwgREVGQVVMVF9DT05GSUcuUlNDX1dPUktFUl9QQVRIKTtcblxuICBjb25zdCBodG1sV29ya2VyUGF0aCA9XG4gICAgdHlwZW9mIG9wdGlvbnMuaHRtbFdvcmtlclBhdGggPT09IFwic3RyaW5nXCJcbiAgICAgID8gcmVzb2x2ZShwcm9qZWN0Um9vdCwgb3B0aW9ucy5odG1sV29ya2VyUGF0aClcbiAgICAgIDogcmVzb2x2ZShwbHVnaW5Sb290LCBERUZBVUxUX0NPTkZJRy5IVE1MX1dPUktFUl9QQVRIKTtcblxuICBjb25zdCBsb2FkZXJQYXRoID1cbiAgICB0eXBlb2Ygb3B0aW9ucy5sb2FkZXJQYXRoID09PSBcInN0cmluZ1wiXG4gICAgICA/IHJlc29sdmUocHJvamVjdFJvb3QsIG9wdGlvbnMubG9hZGVyUGF0aClcbiAgICAgIDogcmVzb2x2ZShwbHVnaW5Sb290LCBERUZBVUxUX0NPTkZJRy5MT0FERVJfUEFUSCk7XG5cbiAgY29uc3QganNFeHRlbnNpb24gPVxuICAgIHR5cGVvZiBvcHRpb25zLmJ1aWxkPy5qc0V4dGVuc2lvbiA9PT0gXCJzdHJpbmdcIlxuICAgICAgPyBvcHRpb25zLmJ1aWxkLmpzRXh0ZW5zaW9uXG4gICAgICA6IERFRkFVTFRfQ09ORklHLkJVSUxELmpzRXh0ZW5zaW9uO1xuICBjb25zdCBjc3NFeHRlbnNpb24gPVxuICAgIHR5cGVvZiBvcHRpb25zLmJ1aWxkPy5jc3NFeHRlbnNpb24gPT09IFwic3RyaW5nXCJcbiAgICAgID8gb3B0aW9ucy5idWlsZC5jc3NFeHRlbnNpb25cbiAgICAgIDogREVGQVVMVF9DT05GSUcuQlVJTEQuY3NzRXh0ZW5zaW9uO1xuICBjb25zdCBjc3NNb2R1bGVFeHRlbnNpb24gPVxuICAgIHR5cGVvZiBvcHRpb25zLmJ1aWxkPy5jc3NNb2R1bGVFeHRlbnNpb24gPT09IFwic3RyaW5nXCJcbiAgICAgID8gb3B0aW9ucy5idWlsZC5jc3NNb2R1bGVFeHRlbnNpb25cbiAgICAgIDogREVGQVVMVF9DT05GSUcuQlVJTEQuY3NzTW9kdWxlRXh0ZW5zaW9uO1xuICBjb25zdCBodG1sRXh0ZW5zaW9uID1cbiAgICB0eXBlb2Ygb3B0aW9ucy5idWlsZD8uaHRtbEV4dGVuc2lvbiA9PT0gXCJzdHJpbmdcIlxuICAgICAgPyBvcHRpb25zLmJ1aWxkLmh0bWxFeHRlbnNpb25cbiAgICAgIDogREVGQVVMVF9DT05GSUcuQlVJTEQuaHRtbEV4dGVuc2lvbjtcbiAgY29uc3QganNvbkV4dGVuc2lvbiA9XG4gICAgdHlwZW9mIG9wdGlvbnMuYnVpbGQ/Lmpzb25FeHRlbnNpb24gPT09IFwic3RyaW5nXCJcbiAgICAgID8gb3B0aW9ucy5idWlsZC5qc29uRXh0ZW5zaW9uXG4gICAgICA6IERFRkFVTFRfQ09ORklHLkJVSUxELmpzb25FeHRlbnNpb247XG4gIGNvbnN0IHJzY0V4dGVuc2lvbiA9XG4gICAgdHlwZW9mIG9wdGlvbnMuYnVpbGQ/LnJzY0V4dGVuc2lvbiA9PT0gXCJzdHJpbmdcIlxuICAgICAgPyBvcHRpb25zLmJ1aWxkLnJzY0V4dGVuc2lvblxuICAgICAgOiBERUZBVUxUX0NPTkZJRy5CVUlMRC5yc2NFeHRlbnNpb247XG5cbiAgY29uc3QgcnNjT3V0cHV0UGF0aCA9XG4gICAgdHlwZW9mIG9wdGlvbnMuYnVpbGQ/LnJzY091dHB1dFBhdGggPT09IFwic3RyaW5nXCJcbiAgICAgID8gb3B0aW9ucy5idWlsZC5yc2NPdXRwdXRQYXRoXG4gICAgICA6IERFRkFVTFRfQ09ORklHLkJVSUxELnJzY091dHB1dFBhdGg7XG4gIGNvbnN0IGh0bWxPdXRwdXRQYXRoID1cbiAgICB0eXBlb2Ygb3B0aW9ucy5idWlsZD8uaHRtbE91dHB1dFBhdGggPT09IFwic3RyaW5nXCJcbiAgICAgID8gb3B0aW9ucy5idWlsZC5odG1sT3V0cHV0UGF0aFxuICAgICAgOiBERUZBVUxUX0NPTkZJRy5CVUlMRC5odG1sT3V0cHV0UGF0aDtcblxuICBjb25zdCBtb2R1bGVQYXR0ZXJuID0gcmVzb2x2ZVJlZ0V4cChcbiAgICBvcHRpb25zLmF1dG9EaXNjb3Zlcj8ubW9kdWxlUGF0dGVybixcbiAgICBERUZBVUxUX0NPTkZJRy5BVVRPX0RJU0NPVkVSLm1vZHVsZVBhdHRlcm5cbiAgKTtcblxuICBjb25zdCBqc29uUGF0dGVybiA9IHJlc29sdmVSZWdFeHAoXG4gICAgb3B0aW9ucy5hdXRvRGlzY292ZXI/Lmpzb25QYXR0ZXJuLFxuICAgIERFRkFVTFRfQ09ORklHLkFVVE9fRElTQ09WRVIuanNvblBhdHRlcm5cbiAgKTtcblxuICBjb25zdCBjc3NQYXR0ZXJuID0gcmVzb2x2ZVJlZ0V4cChcbiAgICBvcHRpb25zLmF1dG9EaXNjb3Zlcj8uY3NzUGF0dGVybixcbiAgICBERUZBVUxUX0NPTkZJRy5BVVRPX0RJU0NPVkVSLmNzc1BhdHRlcm5cbiAgKTtcblxuICBjb25zdCBodG1sUGF0dGVybiA9IHJlc29sdmVSZWdFeHAoXG4gICAgb3B0aW9ucy5hdXRvRGlzY292ZXI/Lmh0bWxQYXR0ZXJuLFxuICAgIERFRkFVTFRfQ09ORklHLkFVVE9fRElTQ09WRVIuaHRtbFBhdHRlcm5cbiAgKTtcblxuICBjb25zdCByc2NQYXR0ZXJuID0gcmVzb2x2ZVJlZ0V4cChcbiAgICBvcHRpb25zLmF1dG9EaXNjb3Zlcj8ucnNjUGF0dGVybixcbiAgICBERUZBVUxUX0NPTkZJRy5BVVRPX0RJU0NPVkVSLnJzY1BhdHRlcm5cbiAgKTtcblxuICBjb25zdCBjbGllbnRQYXR0ZXJuID0gcmVzb2x2ZVJlZ0V4cChcbiAgICBvcHRpb25zLmF1dG9EaXNjb3Zlcj8uY2xpZW50UGF0dGVybixcbiAgICBERUZBVUxUX0NPTkZJRy5BVVRPX0RJU0NPVkVSLmNsaWVudFBhdHRlcm5cbiAgKTtcblxuICBjb25zdCBzZXJ2ZXJQYXR0ZXJuID0gcmVzb2x2ZVJlZ0V4cChcbiAgICBvcHRpb25zLmF1dG9EaXNjb3Zlcj8uc2VydmVyUGF0dGVybixcbiAgICBERUZBVUxUX0NPTkZJRy5BVVRPX0RJU0NPVkVSLnNlcnZlclBhdHRlcm5cbiAgKTtcblxuICBjb25zdCBub2RlUGF0dGVybiA9IHJlc29sdmVSZWdFeHAoXG4gICAgb3B0aW9ucy5hdXRvRGlzY292ZXI/Lm5vZGVQYXR0ZXJuLFxuICAgIERFRkFVTFRfQ09ORklHLkFVVE9fRElTQ09WRVIubm9kZU9ubHlcbiAgKTtcblxuICBjb25zdCBwcm9wc1BhdHRlcm4gPSByZXNvbHZlUmVnRXhwKFxuICAgIG9wdGlvbnMuYXV0b0Rpc2NvdmVyPy5wcm9wc1BhdHRlcm4sXG4gICAgREVGQVVMVF9DT05GSUcuQVVUT19ESVNDT1ZFUi5wcm9wc1BhdHRlcm5cbiAgKTtcblxuICBjb25zdCBwYWdlUGF0dGVybiA9IHJlc29sdmVSZWdFeHAoXG4gICAgb3B0aW9ucy5hdXRvRGlzY292ZXI/LnBhZ2VQYXR0ZXJuLFxuICAgIERFRkFVTFRfQ09ORklHLkFVVE9fRElTQ09WRVIucGFnZVBhdHRlcm5cbiAgKTtcblxuICBjb25zdCBjc3NNb2R1bGVQYXR0ZXJuID0gcmVzb2x2ZVJlZ0V4cChcbiAgICBvcHRpb25zLmF1dG9EaXNjb3Zlcj8uY3NzTW9kdWxlUGF0dGVybixcbiAgICBERUZBVUxUX0NPTkZJRy5BVVRPX0RJU0NPVkVSLmNzc01vZHVsZVBhdHRlcm5cbiAgKTtcblxuICBjb25zdCB2ZW5kb3JQYXR0ZXJuID0gcmVzb2x2ZVJlZ0V4cChcbiAgICBvcHRpb25zLmF1dG9EaXNjb3Zlcj8udmVuZG9yUGF0dGVybixcbiAgICBERUZBVUxUX0NPTkZJRy5BVVRPX0RJU0NPVkVSLnZlbmRvclBhdHRlcm5cbiAgKTtcblxuICBjb25zdCB2aXJ0dWFsUGF0dGVybiA9IHJlc29sdmVSZWdFeHAoXG4gICAgb3B0aW9ucy5hdXRvRGlzY292ZXI/LnZpcnR1YWxQYXR0ZXJuLFxuICAgIERFRkFVTFRfQ09ORklHLkFVVE9fRElTQ09WRVIudmlydHVhbFBhdHRlcm5cbiAgKTtcblxuICBjb25zdCBkb3RQYXR0ZXJuID0gcmVzb2x2ZVJlZ0V4cChcbiAgICBvcHRpb25zLmF1dG9EaXNjb3Zlcj8uZG90UGF0dGVybixcbiAgICBCQVNFX1BBVFRFUk5TLkRPVF9GSUxFU1xuICApO1xuXG4gIC8qKiBMb2FkZXIgb3B0aW9ucyAqL1xuICBjb25zdCBzZXJ2ZXJEaXJlY3RpdmUgPSByZXNvbHZlUmVnRXhwKFxuICAgIG9wdGlvbnMubG9hZGVyPy5zZXJ2ZXJEaXJlY3RpdmUsXG4gICAgREVGQVVMVF9MT0FERVJfQ09ORklHLnNlcnZlckRpcmVjdGl2ZVxuICApO1xuICBjb25zdCBjbGllbnREaXJlY3RpdmUgPSByZXNvbHZlUmVnRXhwKFxuICAgIG9wdGlvbnMubG9hZGVyPy5jbGllbnREaXJlY3RpdmUsXG4gICAgREVGQVVMVF9MT0FERVJfQ09ORklHLmNsaWVudERpcmVjdGl2ZVxuICApO1xuICBjb25zdCBpc1NlcnZlckZ1bmN0aW9uQ29kZSA9IHJlc29sdmVEaXJlY3RpdmVNYXRjaGVyKFxuICAgIHNlcnZlckRpcmVjdGl2ZSxcbiAgICAoY29kZTogc3RyaW5nLCBtb2R1bGVJZD86IHN0cmluZykgPT5cbiAgICAgIGNvZGUubWF0Y2goc2VydmVyRGlyZWN0aXZlKSAhPSBudWxsIHx8XG4gICAgICAodHlwZW9mIG1vZHVsZUlkID09PSBcInN0cmluZ1wiICYmIHNlcnZlclBhdHRlcm4udGVzdChtb2R1bGVJZCkpIHx8XG4gICAgICBmYWxzZVxuICApO1xuXG4gIGNvbnN0IGlzQ2xpZW50Q29tcG9uZW50Q29kZSA9IHJlc29sdmVEaXJlY3RpdmVNYXRjaGVyKFxuICAgIGNsaWVudERpcmVjdGl2ZSxcbiAgICAoY29kZTogc3RyaW5nLCBtb2R1bGVJZD86IHN0cmluZykgPT5cbiAgICAgIGNvZGUubWF0Y2goY2xpZW50RGlyZWN0aXZlKSAhPSBudWxsIHx8XG4gICAgICAodHlwZW9mIG1vZHVsZUlkID09PSBcInN0cmluZ1wiICYmIGNsaWVudFBhdHRlcm4udGVzdChtb2R1bGVJZCkpIHx8XG4gICAgICBmYWxzZVxuICApO1xuXG4gIGNvbnN0IGlzQ2xpZW50Q29tcG9uZW50QnlDb2RlID0gcmVzb2x2ZURpcmVjdGl2ZU1hdGNoZXIoXG4gICAgY2xpZW50RGlyZWN0aXZlLFxuICAgIChjb2RlOiBzdHJpbmcpID0+IGNvZGUubWF0Y2goY2xpZW50RGlyZWN0aXZlKSAhPSBudWxsIHx8IGZhbHNlXG4gICk7XG5cbiAgY29uc3QgaXNDbGllbnRDb21wb25lbnRCeU5hbWUgPSAobW9kdWxlSWQ6IHN0cmluZywgX3RyYW5zZm9ybWVkTW9kdWxlSWQ/OiBzdHJpbmcpID0+IFxuICAgICh0eXBlb2YgbW9kdWxlSWQgPT09IFwic3RyaW5nXCIgJiYgY2xpZW50UGF0dGVybi50ZXN0KG1vZHVsZUlkKSkgfHwgZmFsc2U7XG5cbiAgY29uc3QgaGFzaE9wdGlvbiA9IG9wdGlvbnMuYnVpbGQ/Lmhhc2ggPz8gREVGQVVMVF9DT05GSUcuQlVJTEQuaGFzaDtcblxuICAvLyBVc2VyLWZhY2luZyBoYXNoIGZ1bmN0aW9uIHRoYXQgY2FuIHRha2Ugc291cmNlIGNvbnRlbnQgb3IgZmlsZW5hbWVcbiAgY29uc3QgaGFzaCA9IChpbnB1dDogc3RyaW5nIHwgbnVsbCwgX3NzcjogYm9vbGVhbiwgc291cmNlQ29udGVudD86IHN0cmluZykgPT4ge1xuICAgIGlmICghaW5wdXQpIHJldHVybiBcIlwiO1xuICAgIGlmIChuZXcgUmVnRXhwKEJBU0VfUEFUVEVSTlMuRVhULk5PREUpLnRlc3QoaW5wdXQpKSB7XG4gICAgICByZXR1cm4gaW5wdXQ7XG4gICAgfVxuICAgIFxuICAgIC8vIENSSVRJQ0FMOiBOZXZlciBoYXNoIG5vZGVfbW9kdWxlcyBmaWxlcyAtIFZpdGUvUm9sbHVwIGhhbmRsZXMgdGhvc2VcbiAgICBpZiAoaW5wdXQuaW5jbHVkZXMoXCJub2RlX21vZHVsZXNcIikpIHtcbiAgICAgIHJldHVybiBpbnB1dDtcbiAgICB9XG4gICAgXG4gICAgLy8gQ1JJVElDQUw6IE5ldmVyIGhhc2ggdmlydHVhbCBtb2R1bGVzIChfdmlydHVhbCBvciBtYXRjaGluZyB2aXJ0dWFsUGF0dGVybikgLSBWaXRlIGhhbmRsZXMgdGhvc2VcbiAgICBjb25zdCB2aXJ0dWFsUGF0dGVybiA9IHJlc29sdmVSZWdFeHAoXG4gICAgICBvcHRpb25zLmF1dG9EaXNjb3Zlcj8udmlydHVhbFBhdHRlcm4sXG4gICAgICBERUZBVUxUX0NPTkZJRy5BVVRPX0RJU0NPVkVSLnZpcnR1YWxQYXR0ZXJuXG4gICAgKTtcbiAgICBpZiAoaW5wdXQuaW5jbHVkZXMoXCJfdmlydHVhbFwiKSB8fCAodmlydHVhbFBhdHRlcm4gJiYgdmlydHVhbFBhdHRlcm4udGVzdChpbnB1dCkpKSB7XG4gICAgICByZXR1cm4gaW5wdXQ7XG4gICAgfVxuICAgIFxuICAgIC8vIENoZWNrIGlmIGhhc2hpbmcgaXMgZGlzYWJsZWRcbiAgICBpZiAoaGFzaE9wdGlvbiA9PT0gXCJmYWxzZVwiKSB7XG4gICAgICByZXR1cm4gaW5wdXQ7XG4gICAgfVxuICAgIFxuICAgIC8vIFNraXAgb3V0cHV0cyB0aGF0IGFyZSBhZGRyZXNzZWQgYnkgY2Fub25pY2FsIHBhdGggcmF0aGVyIHRoYW4gdmlhIHRoZVxuICAgIC8vIGJ1aWxkIG1hbmlmZXN0LCBzbyByZW5hbWluZyB0aGVtIHdvdWxkIGJyZWFrIHRoZWlyIGNhbGxlcnM6XG4gICAgLy8gICAtIGh0bWxQYXR0ZXJuIC8gcnNjUGF0dGVybjogU1NHIG91dHB1dHMgYXQgZml4ZWQgcm91dGUgVVJMc1xuICAgIC8vICAgLSBwYWdlUGF0dGVybiAvIHByb3BzUGF0dGVybiAvIHNlcnZlclBhdHRlcm46IFNTUiBlbnRyeSBtb2R1bGVzIHRoZVxuICAgIC8vICAgICBydW50aW1lIGltcG9ydHMgYnkgbGl0ZXJhbCBwYXRoIChzZWUgcmVzb2x2ZVBhZ2VBbmRQcm9wcyksIG5vdFxuICAgIC8vICAgICBicm93c2VyLXNlcnZlZCBhc3NldHMgc28gY2FjaGluZyBpc24ndCBhIGNvbmNlcm4gZWl0aGVyXG4gICAgLy8gRXZlcnl0aGluZyBlbHNlIGZhbGxzIHRocm91Z2ggdG8gY29udGVudC1iYXNlZCBoYXNoaW5nIHNvIHByb2QgZGVwbG95c1xuICAgIC8vIGJ1c3QgYnJvd3Nlci9DRE4gY2FjaGVzLiBDcm9zcy1lbnZpcm9ubWVudCBoYXNoIGNvbnNpc3RlbmN5IGlzIHByb3ZpZGVkXG4gICAgLy8gYnkgaGFuZGxlU3NyRW50cnlOYW1lIC8gaGFuZGxlU3NyQXNzZXROYW1lIGluIHJlc29sdmVVc2VyQ29uZmlnLnRzXG4gICAgLy8gKHdoaWNoIGZlZWQgc291cmNlQ29udGVudCB0aHJvdWdoIHRvIHRoaXMgZnVuY3Rpb24pIGFuZCB0aGVcbiAgICAvLyBhdWdtZW50Q2h1bmtIYXNoIHBsdWdpbi5cbiAgICBpZiAoXG4gICAgICBodG1sUGF0dGVybi50ZXN0KGlucHV0KSB8fFxuICAgICAgcnNjUGF0dGVybi50ZXN0KGlucHV0KSB8fFxuICAgICAgcGFnZVBhdHRlcm4udGVzdChpbnB1dCkgfHxcbiAgICAgIHByb3BzUGF0dGVybi50ZXN0KGlucHV0KSB8fFxuICAgICAgc2VydmVyUGF0dGVybi50ZXN0KGlucHV0KVxuICAgICkge1xuICAgICAgcmV0dXJuIGlucHV0O1xuICAgIH1cblxuICAgIC8vIERldGVybWluZSB3aGF0IHRvIGhhc2ggYmFzZWQgb24gYXZhaWxhYmxlIGlucHV0XG4gICAgbGV0IGNvbnRlbnRUb0hhc2g6IHN0cmluZztcbiAgICBcbiAgICBpZiAoc291cmNlQ29udGVudCkge1xuICAgICAgLy8gVXNlIHByb3ZpZGVkIHNvdXJjZSBjb250ZW50IChwcmVmZXJyZWQpXG4gICAgICBjb250ZW50VG9IYXNoID0gc291cmNlQ29udGVudDtcbiAgICB9IGVsc2Uge1xuICAgICAgICAgICAvLyBUcnkgdG8gcmVhZCBzb3VyY2UgZmlsZSBjb250ZW50XG4gICAgIHRyeSB7XG4gICAgICAgY29uc3Qgc291cmNlUGF0aCA9IHJlc29sdmUocHJvamVjdFJvb3QsIGlucHV0KTtcbiAgICAgICBpZiAoZXhpc3RzU3luYyhzb3VyY2VQYXRoKSkge1xuICAgICAgICAgY29udGVudFRvSGFzaCA9IHJlYWRGaWxlU3luYyhzb3VyY2VQYXRoLCAndXRmLTgnKTtcbiAgICAgICAgIC8vIERlYnVnIGxvZ2dpbmdcbiAgICAgICAgIGlmIChvcHRpb25zLnZlcmJvc2UpIHtcbiAgICAgICAgICAgbG9nZ2VyLmluZm8oYFtoYXNoXSBSZWFkaW5nIHNvdXJjZTogJHtzb3VyY2VQYXRofSAoJHtjb250ZW50VG9IYXNoLmxlbmd0aH0gY2hhcnMpYCk7XG4gICAgICAgICB9XG4gICAgICAgfSBlbHNlIHtcbiAgICAgICAgIC8vIEZhbGxiYWNrIHRvIGZpbGVuYW1lXG4gICAgICAgICBjb250ZW50VG9IYXNoID0gaW5wdXQ7XG4gICAgICAgICBpZiAob3B0aW9ucy52ZXJib3NlKSB7XG4gICAgICAgICAgIGxvZ2dlci53YXJuKGBbaGFzaF0gRmlsZSBub3QgZm91bmQ6ICR7c291cmNlUGF0aH0sIHVzaW5nIGZpbGVuYW1lOiAke2lucHV0fWApO1xuICAgICAgICAgfVxuICAgICAgIH1cbiAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAvLyBGYWxsYmFjayB0byBmaWxlbmFtZVxuICAgICAgIGNvbnRlbnRUb0hhc2ggPSBpbnB1dDtcbiAgICAgICBpZiAob3B0aW9ucy52ZXJib3NlKSB7XG4gICAgICAgICBsb2dnZXIud2FybihgW2hhc2hdIEVycm9yIHJlYWRpbmcgJHtpbnB1dH06ICR7ZXJyb3J9YCk7XG4gICAgICAgfVxuICAgICB9XG4gICAgfVxuICAgIFxuICAgIC8vIEdlbmVyYXRlIGhhc2ggdXNpbmcgUm9sbHVwLWxpa2UgYWxnb3JpdGhtXG4gICAgY29uc3QgaGFzaENoYXJhY3RlcnMgPSB0eXBlb2YgaGFzaE9wdGlvbiA9PT0gJ29iamVjdCcgJiYgaGFzaE9wdGlvbj8uZm9ybWF0ID09PSAnaGV4JyA/ICdoZXgnIDogJ2Jhc2UzNic7XG4gICAgY29uc3QgY29udGVudEhhc2ggPSBjcmVhdGVSb2xsdXBMaWtlSGFzaChjb250ZW50VG9IYXNoLCBoYXNoQ2hhcmFjdGVycyk7XG4gICAgXG4gICAgLy8gQXBwbHkgbmFtaW5nIGxvZ2ljXG4gICAgY29uc3QgZXh0ZW5zaW9uSW5kZXggPSBpbnB1dC5sYXN0SW5kZXhPZihcIi5cIik7XG4gICAgaWYgKGV4dGVuc2lvbkluZGV4ICE9PSAtMSkge1xuICAgICAgY29uc3QgZXh0ZW5zaW9uID0gaW5wdXQuc2xpY2UoZXh0ZW5zaW9uSW5kZXgpO1xuICAgICAgY29uc3QgZmlsZW5hbWUgPSBpbnB1dC5zbGljZSgwLCBleHRlbnNpb25JbmRleCk7XG4gICAgICByZXR1cm4gZmlsZW5hbWUgKyBcIi1cIiArIGNvbnRlbnRIYXNoICsgZXh0ZW5zaW9uO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gaW5wdXQgKyBcIi1cIiArIGNvbnRlbnRIYXNoO1xuICAgIH1cbiAgfTtcblxuICAvLyBPdXRwdXQgcGF0aCByZXNvbHV0aW9uXG4gIGNvbnN0IGdldE91dHB1dFBhdGggPSAobjogc3RyaW5nIHwgbnVsbCwgaXNBc3NldDogYm9vbGVhbiA9IGZhbHNlKSA9PiB7XG4gICAgaWYgKCFuKSByZXR1cm4gXCJcIjtcbiAgICBsZXQgcGF0aCA9IGhhbmRsZVNlYXJjaFF1ZXJ5KG4pO1xuICAgIHBhdGggPSBwYXRoLnN0YXJ0c1dpdGgobW9kdWxlQmFzZSArIG1vZHVsZUJhc2VQYXRoKVxuICAgICAgPyBwYXRoLnNsaWNlKG1vZHVsZUJhc2UubGVuZ3RoICsgbW9kdWxlQmFzZVBhdGgubGVuZ3RoKVxuICAgICAgOiBwYXRoO1xuXG4gICAgLy8gRm9yIGFzc2V0cyB0aGF0IGFyZSBub3QgbW9kdWxlcywgcHJlc2VydmUgdGhlIG9yaWdpbmFsIGV4dGVuc2lvblxuICAgIC8vIE9ubHkgYXBwbHkgbW9kdWxlIHRyYW5zZm9ybWF0aW9ucyBpZiB0aGUgZmlsZSBhY3R1YWxseSBtYXRjaGVzIG1vZHVsZSBwYXR0ZXJuc1xuICAgIGlmIChpc0Fzc2V0KSB7XG4gICAgICAvLyBDaGVjayBpZiB0aGlzIGlzIGEgbW9kdWxlIGZpbGUgKEpTL1RTL0pTWC9UU1gpIC0gaWYgc28sIGFwcGx5IG1vZHVsZSB0cmFuc2Zvcm1hdGlvbnNcbiAgICAgIGNvbnN0IGlzTW9kdWxlRmlsZSA9IG1vZHVsZVBhdHRlcm4udGVzdChwYXRoKSB8fCBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpZW50UGF0dGVybi50ZXN0KHBhdGgpIHx8IFxuICAgICAgICAgICAgICAgICAgICAgICAgICBzZXJ2ZXJQYXR0ZXJuLnRlc3QocGF0aCkgfHxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvcHNQYXR0ZXJuLnRlc3QocGF0aCkgfHxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgcGFnZVBhdHRlcm4udGVzdChwYXRoKTtcbiAgICAgIFxuICAgICAgLy8gSWYgaXQncyBub3QgYSBtb2R1bGUgZmlsZSwgcHJlc2VydmUgdGhlIG9yaWdpbmFsIGV4dGVuc2lvblxuICAgICAgaWYgKCFpc01vZHVsZUZpbGUpIHtcbiAgICAgICAgcmV0dXJuIHBhdGg7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHZlbmRvclBhdHRlcm4udGVzdChwYXRoKSlcbiAgICAgIHJldHVybiByZWdpc3RlclBhdGgocGF0aCwgdmVuZG9yUGF0dGVybiwganNFeHRlbnNpb24pO1xuICAgIGlmIChjc3NNb2R1bGVQYXR0ZXJuLnRlc3QocGF0aCkpXG4gICAgICByZXR1cm4gcmVnaXN0ZXJQYXRoKHBhdGgsIGNzc01vZHVsZVBhdHRlcm4sIGNzc0V4dGVuc2lvbik7XG4gICAgaWYgKGNzc1BhdHRlcm4udGVzdChwYXRoKSlcbiAgICAgIHJldHVybiByZWdpc3RlclBhdGgocGF0aCwgY3NzUGF0dGVybiwgY3NzRXh0ZW5zaW9uKTtcbiAgICBpZiAoY2xpZW50UGF0dGVybi50ZXN0KHBhdGgpKVxuICAgICAgcmV0dXJuIHJlZ2lzdGVyUGF0aChwYXRoLCBjbGllbnRQYXR0ZXJuLCBqc0V4dGVuc2lvbik7XG4gICAgaWYgKGh0bWxQYXR0ZXJuLnRlc3QocGF0aCkpXG4gICAgICByZXR1cm4gcmVnaXN0ZXJQYXRoKHBhdGgsIGh0bWxQYXR0ZXJuLCBodG1sRXh0ZW5zaW9uKTtcbiAgICBpZiAoanNvblBhdHRlcm4udGVzdChwYXRoKSlcbiAgICAgIHJldHVybiByZWdpc3RlclBhdGgocGF0aCwganNvblBhdHRlcm4sIGpzb25FeHRlbnNpb24pO1xuICAgIGlmIChwcm9wc1BhdHRlcm4udGVzdChwYXRoKSlcbiAgICAgIHJldHVybiByZWdpc3RlclBhdGgocGF0aCwgcHJvcHNQYXR0ZXJuLCBqc0V4dGVuc2lvbik7XG4gICAgaWYgKHBhZ2VQYXR0ZXJuLnRlc3QocGF0aCkpXG4gICAgICByZXR1cm4gcmVnaXN0ZXJQYXRoKHBhdGgsIHBhZ2VQYXR0ZXJuLCBqc0V4dGVuc2lvbik7XG4gICAgaWYgKHNlcnZlclBhdHRlcm4udGVzdChwYXRoKSlcbiAgICAgIHJldHVybiByZWdpc3RlclBhdGgocGF0aCwgc2VydmVyUGF0dGVybiwganNFeHRlbnNpb24pO1xuICAgIGlmIChtb2R1bGVQYXR0ZXJuLnRlc3QocGF0aCkpXG4gICAgICByZXR1cm4gcmVnaXN0ZXJQYXRoKHBhdGgsIG1vZHVsZVBhdHRlcm4sIGpzRXh0ZW5zaW9uKTtcbiAgICBcbiAgICAvLyBGYWxsYmFjazogb25seSBhcHBseSBtb2R1bGUgcGF0dGVybiBpZiB3ZSdyZSBzdXJlIGl0J3MgYSBtb2R1bGVcbiAgICAvLyBGb3IgYXNzZXRzLCB3ZSd2ZSBhbHJlYWR5IHJldHVybmVkIGFib3ZlLCBzbyB0aGlzIGlzIHNhZmVcbiAgICByZXR1cm4gcmVnaXN0ZXJQYXRoKHBhdGgsIG1vZHVsZVBhdHRlcm4sIGpzRXh0ZW5zaW9uKTtcbiAgfTtcblxuICBjb25zdCBub3JtYWxpemVyID1cbiAgICBvcHRpb25zLm5vcm1hbGl6ZXIgPz9cbiAgICBjcmVhdGVJbnB1dE5vcm1hbGl6ZXIoe1xuICAgICAgcm9vdDogcHJvamVjdFJvb3QsXG4gICAgICBwcmVzZXJ2ZU1vZHVsZXNSb290OiBwcmVzZXJ2ZU1vZHVsZXNSb290U3RyaW5nLFxuICAgICAgcmVtb3ZlRXh0ZW5zaW9uOiB0cnVlLFxuICAgICAgbW9kdWxlQmFzZVBhdGgsXG4gICAgICBtb2R1bGVCYXNlVVJMLFxuICAgIH0pO1xuICAvLyBGaWxlIG5hbWluZyBmdW5jdGlvbnMgLSBkZWZpbmVkIGFzIHJlZ3VsYXIgZnVuY3Rpb25zIHRvIGF2b2lkIGNsb3N1cmUgaXNzdWVzXG4gIGZ1bmN0aW9uIGVudHJ5RmlsZShuOiBQcmVSZW5kZXJlZENodW5rLCBzc3I6IGJvb2xlYW4sIHNvdXJjZUNvbnRlbnQ/OiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGNvbnN0IG5vcm1hbGl6ZWROYW1lID0gbm9ybWFsaXplcihuLm5hbWUpWzBdO1xuICAgIGxldCBvdXRwdXRQYXRoID0gZ2V0T3V0cHV0UGF0aChub3JtYWxpemVkTmFtZSk7XG4gICAgXG4gICAgLy8gV2hlbiBwcmVzZXJ2ZU1vZHVsZXNSb290IGlzIHRydWUsIHByZXNlcnZlIHRoZSBzcmMvIHByZWZpeCBpbiBvdXRwdXQgcGF0aHNcbiAgICBpZiAocHJlc2VydmVNb2R1bGVzUm9vdCAmJiBuLm5hbWUuc3RhcnRzV2l0aChcInNyYy9cIikpIHtcbiAgICAgIC8vIElmIHRoZSBub3JtYWxpemVkIG5hbWUgZG9lc24ndCBoYXZlIHNyYy8sIGFkZCBpdCBiYWNrXG4gICAgICBpZiAoIW91dHB1dFBhdGguc3RhcnRzV2l0aChcInNyYy9cIikpIHtcbiAgICAgICAgLy8gSGFuZGxlIHRoZSBjYXNlIHdoZXJlIG91dHB1dFBhdGggc3RhcnRzIHdpdGggYSBzbGFzaFxuICAgICAgICBpZiAob3V0cHV0UGF0aC5zdGFydHNXaXRoKFwiL1wiKSkge1xuICAgICAgICAgIG91dHB1dFBhdGggPSBcInNyY1wiICsgb3V0cHV0UGF0aDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBvdXRwdXRQYXRoID0gXCJzcmMvXCIgKyBvdXRwdXRQYXRoO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIFxuICAgIHJldHVybiBoYXNoKG91dHB1dFBhdGgsIHNzciwgc291cmNlQ29udGVudCk7XG4gIH1cblxuICBmdW5jdGlvbiBjaHVua0ZpbGUobjogUHJlUmVuZGVyZWRDaHVuaywgc3NyOiBib29sZWFuLCBzb3VyY2VDb250ZW50Pzogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBjb25zdCBub3JtYWxpemVkTmFtZSA9IG5vcm1hbGl6ZXIobi5uYW1lKVswXTtcbiAgICBsZXQgb3V0cHV0UGF0aCA9IGdldE91dHB1dFBhdGgobm9ybWFsaXplZE5hbWUpO1xuICAgIFxuICAgIC8vIFdoZW4gcHJlc2VydmVNb2R1bGVzUm9vdCBpcyB0cnVlLCBwcmVzZXJ2ZSB0aGUgc3JjLyBwcmVmaXggaW4gb3V0cHV0IHBhdGhzXG4gICAgaWYgKHByZXNlcnZlTW9kdWxlc1Jvb3QgJiYgbi5uYW1lLnN0YXJ0c1dpdGgoXCJzcmMvXCIpKSB7XG4gICAgICAvLyBJZiB0aGUgbm9ybWFsaXplZCBuYW1lIGRvZXNuJ3QgaGF2ZSBzcmMvLCBhZGQgaXQgYmFja1xuICAgICAgaWYgKCFvdXRwdXRQYXRoLnN0YXJ0c1dpdGgoXCJzcmMvXCIpKSB7XG4gICAgICAgIC8vIEhhbmRsZSB0aGUgY2FzZSB3aGVyZSBvdXRwdXRQYXRoIHN0YXJ0cyB3aXRoIGEgc2xhc2hcbiAgICAgICAgaWYgKG91dHB1dFBhdGguc3RhcnRzV2l0aChcIi9cIikpIHtcbiAgICAgICAgICBvdXRwdXRQYXRoID0gXCJzcmNcIiArIG91dHB1dFBhdGg7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgb3V0cHV0UGF0aCA9IFwic3JjL1wiICsgb3V0cHV0UGF0aDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICBcbiAgICByZXR1cm4gaGFzaChvdXRwdXRQYXRoLCBzc3IsIHNvdXJjZUNvbnRlbnQpO1xuICB9XG5cbiAgZnVuY3Rpb24gYXNzZXRGaWxlKG46IFByZVJlbmRlcmVkQXNzZXQsIF9zc3I6IGJvb2xlYW4gPSBmYWxzZSk6IHN0cmluZyB7XG4gICAgLy8gUm9sbHVwIG1heSByZXBvcnQgdGhlIHNhbWUgYXNzZXQgdW5kZXIgc2V2ZXJhbCBgbmFtZXNgOyBkZWR1cGUgc28gd2VcbiAgICAvLyBkb24ndCBlbWl0IGludmFsaWQgY29tbWEtam9pbmVkIGZpbGVuYW1lcyBsaWtlIGBGb28uZW90LEZvby5lb3RgLiBXaGVuXG4gICAgLy8gbXVsdGlwbGUgZGlzdGluY3QgbmFtZXMgc3Vydml2ZSB0aGV5IGFsbCByZWZlcmVuY2UgdGhlIHNhbWUgZW1pdHRlZFxuICAgIC8vIGZpbGUsIHNvIHVzZSB0aGUgZmlyc3QgYXMgdGhlIGNhbm9uaWNhbCBvdXRwdXQgcGF0aC5cbiAgICBjb25zdCB1bmlxdWVOYW1lcyA9IEFycmF5LmZyb20obmV3IFNldChuLm5hbWVzKSk7XG4gICAgbGV0IGZpcnN0Tm