UNPKG

vite-plugin-react-server

Version:
486 lines (484 loc) 91.5 kB
/** * 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