UNPKG

vite-plugin-react-server

Version:
513 lines (511 loc) 91.1 kB
/** * vite-plugin-react-server * Copyright (c) Nico Brinkkemper * MIT License */ import { createLogger, defaultServerConditions, defaultClientConditions } from 'vite'; import { join, resolve } from 'node:path'; import { existsSync, readFileSync } from 'node:fs'; import { DEFAULT_CONFIG } from './defaults.js'; import { getNodeEnv } from './getNodeEnv.js'; import { getEnvValue, setEnvValue } from '../env/getEnvKey.js'; import { mergeClientPackagesNoExternal, mergeClientPackagesOptimizeDepsExclude } from '../clientPackages/applyConfig.js'; import { createRollupLikeHash } from './createRollupLikeHash.js'; const stashedUserConfig = {}; let originalConfig = null; const resolveUserConfig = function _resolveUserConfig({ condition, config, configEnv, userOptions, autoDiscoveredFiles, forceResolve = false, ssr = void 0 }) { if (!forceResolve && originalConfig == null) { originalConfig = config; } else if (originalConfig != null && config !== originalConfig) { forceResolve = true; } ssr = typeof ssr === "boolean" ? ssr : typeof config.build?.ssr === "boolean" ? config.build?.ssr : condition === "react-server" ? true : typeof configEnv.isSsrBuild === "boolean" ? configEnv.isSsrBuild : false; if (condition === "react-server" && !ssr) { const logger = config.customLogger ?? createLogger(); logger.warn( "react-server build should be ssr, but is was manually set to false. This may not work as expected." ); } const envDir = condition === "react-client" && ssr ? userOptions.build.client : condition === "react-client" ? userOptions.build.static : userOptions.build.server; const envId = `${envDir}${ssr ? "-ssr" : ""}`; if (stashedUserConfig[envId] && !forceResolve) { return { type: "success", userConfig: stashedUserConfig[envId] }; } const handleSsrEntryName = (info, input, fallback, ssr2) => { let sourceContent; if (input) { try { const sourcePath = resolve(userOptions.projectRoot, input); if (existsSync(sourcePath)) { sourceContent = readFileSync(sourcePath, "utf-8"); } } catch (error) { } } if (!ssr2 || !input) { if (typeof fallback === "function") { return fallback(info, false, sourceContent); } return userOptions.normalizer(info.name)[0]; } const normalized = userOptions.normalizer(input); let value = normalized[1]; if (value.startsWith(userOptions.moduleBasePath)) { value = value.slice(userOptions.moduleBasePath.length); } if (typeof fallback === "function") { return fallback(info, true, sourceContent); } return userOptions.normalizer(info.name)[0]; }; const handleSsrAssetName = (info, input, fallback, ssr2) => { if (info.source === "") { return ""; } const isCssFile = input?.endsWith(".css") || info.names?.some((name) => name.endsWith(".css")); if (!ssr2 || !input || isCssFile) { if (typeof fallback === "function") { return fallback(info, ssr2); } return userOptions.normalizer(info.names[0])[0]; } const normalized = userOptions.normalizer(input); let value = normalized[1]; if (value.startsWith(userOptions.moduleBasePath)) { value = value.slice(userOptions.moduleBasePath.length); } return fallback(info, ssr2); }; const userDefinedOutput = config.build?.rollupOptions?.output; const hasOtherOutput = Array.isArray(userDefinedOutput) && userDefinedOutput.length > 1; const hasValidOutput = userDefinedOutput && !hasOtherOutput; const hasObjectOutput = userDefinedOutput && !hasOtherOutput && typeof userDefinedOutput === "object" && userDefinedOutput !== null; const userDefinedAssetFileNames = hasObjectOutput ? "assetFileNames" in userDefinedOutput ? userDefinedOutput.assetFileNames : void 0 : ( // find the other asset file names hasOtherOutput ? userDefinedOutput.find((o) => o?.assetFileNames)?.assetFileNames : void 0 ); const userDefinedChunkFileNames = hasValidOutput ? "chunkFileNames" in userDefinedOutput ? userDefinedOutput.chunkFileNames : void 0 : void 0; const userDefinedEntryFileNames = hasValidOutput ? "entryFileNames" in userDefinedOutput ? userDefinedOutput.entryFileNames : void 0 : void 0; const stashedReturns = {}; const preserveModulesRootString = userOptions.build.preserveModulesRoot === false ? userOptions.moduleBase : ""; const shouldPreserveModules = ssr || condition === "react-server"; const pluginOutput = { // For static builds: false to bundle everything and prevent _virtual files // For SSR builds: use preserveModules with preserveModulesRoot set to preserve only module names (not paths) // This prevents _virtual files by flattening the output structure preserveModules: shouldPreserveModules, // For static builds: undefined (not needed when preserveModules is false) // For SSR builds: set to empty string to preserve only module names, preventing _virtual files preserveModulesRoot: shouldPreserveModules ? preserveModulesRootString === "" ? "" : preserveModulesRootString : void 0, entryFileNames: userDefinedEntryFileNames ?? ((info) => { const input = info.facadeModuleId ?? info.name + userOptions.build.moduleExtension; const inputId = input + (ssr ? "-ssr" : ""); if (!stashedReturns[inputId]) { const r = handleSsrEntryName( info, input, userOptions.build.entryFile, ssr ); stashedReturns[inputId] = r ?? info.name; } return stashedReturns[inputId].slice( Number(stashedReturns[inputId].startsWith("/")) ); }), assetFileNames: userDefinedAssetFileNames ?? ((info) => { const input = info.originalFileNames[0]; const inputId = input + (ssr ? "-ssr" : ""); if (!stashedReturns[inputId]) { const r = handleSsrAssetName( info, input, userOptions.build.assetFile, ssr ); stashedReturns[inputId] = r ?? join( userOptions.build.preserveModulesRoot === false && userOptions.build.assetsDir ? userOptions.build.assetsDir : "", userOptions.normalizer(input)[0] ); } return stashedReturns[inputId].slice( Number(stashedReturns[inputId].startsWith("/")) ); }), chunkFileNames: userDefinedChunkFileNames ?? ((info) => { const input = info.facadeModuleId ?? info.name + userOptions.autoDiscover.modulePattern.source; const inputId = input + (ssr ? "-ssr" : ""); if (!stashedReturns[inputId]) { const r = handleSsrEntryName( info, input, userOptions.build.chunkFile, ssr ); stashedReturns[inputId] = r ?? info.name; } return stashedReturns[inputId].slice( Number(stashedReturns[inputId].startsWith("/")) ); }), format: "esm", exports: "named" }; const newOutput = Array.isArray(config.build?.rollupOptions?.output) ? [...config.build?.rollupOptions?.output || null, pluginOutput] : typeof config.build?.rollupOptions?.output === "object" && config.build?.rollupOptions?.output !== null ? [config.build?.rollupOptions?.output, pluginOutput] : pluginOutput; const vitePrefix = config.envPrefix ?? DEFAULT_CONFIG.ENV_PREFIX; const primaryPrefix = typeof vitePrefix === "string" ? vitePrefix : vitePrefix[0]; const envBaseUrl = getEnvValue("BASE_URL", primaryPrefix); const effectiveModuleBaseURL = envBaseUrl != null && envBaseUrl !== "" ? envBaseUrl : userOptions.moduleBaseURL; const envPublicOrigin = getEnvValue("PUBLIC_ORIGIN", primaryPrefix); const effectivePublicOrigin = envPublicOrigin != null ? envPublicOrigin : userOptions.publicOrigin; const explicitMode = typeof config.mode === "string" && config.mode !== "" ? config.mode : void 0; const mode = explicitMode ?? getNodeEnv(); const effectiveProjectRoot = userOptions.projectRoot || config.root || ""; let effectiveModuleRootPath; if (configEnv.command === "serve") { effectiveModuleRootPath = userOptions.moduleBasePath || "/"; } else { effectiveModuleRootPath = join( effectiveProjectRoot, userOptions.build.outDir, userOptions.build.client, !userOptions.moduleBasePath.startsWith("/") ? "/" : "" ); if (!userOptions.build.preserveModulesRoot && !userOptions.moduleBasePath.startsWith(userOptions.moduleBase)) { effectiveModuleRootPath = join( effectiveModuleRootPath, userOptions.moduleBase, userOptions.moduleBasePath === "" ? "/" : userOptions.moduleBasePath ); } } const minify = config.build?.minify; const clientPackages = userOptions.clientPackages ?? []; const mergedNoExternal = mergeClientPackagesNoExternal( clientPackages, config.ssr?.noExternal ); const srrConfig = { ...config.ssr, target: config.ssr?.target ?? "node", noExternal: mergedNoExternal, optimizeDeps: { ...config.ssr?.optimizeDeps, include: config.ssr?.optimizeDeps?.include ?? [ "react", "react-dom", "react-server-dom-esm/client" ], exclude: mergeClientPackagesOptimizeDepsExclude( clientPackages, config.ssr?.optimizeDeps?.exclude ) }, resolve: { ...config.ssr?.resolve, externalConditions: config.ssr?.resolve?.externalConditions ?? [ "react-server" ] } }; let publicOrigin = effectivePublicOrigin ?? DEFAULT_CONFIG.PUBLIC_ORIGIN; const port = typeof config.server?.port === "number" ? config.server?.port : 5173; const strictPort = config.server?.strictPort ?? true; const host = typeof config.server?.host === "string" ? config.server?.host : "localhost"; const base = effectiveModuleBaseURL ?? config.base ?? DEFAULT_CONFIG.MODULE_BASE_URL; if (configEnv.command === "serve" && !configEnv.isPreview) { publicOrigin = ""; } const isDev = mode === "development" || configEnv.command === "serve"; const ssrDefine = { [`process.env.${primaryPrefix}BASE_URL`]: `"${base}"`, [`process.env.${primaryPrefix}PUBLIC_ORIGIN`]: `"${publicOrigin}"`, [`process.env.NODE_ENV`]: `"${mode}"`, [`process.env.VITE_DEV`]: isDev ? "true" : "false", [`process.env.VITE_PROD`]: isDev ? "false" : "true" }; const define = { ...config.define, // Standard Vite env vars [`import.meta.env.DEV`]: isDev ? "true" : "false", [`import.meta.env.PROD`]: isDev ? "false" : "true", [`import.meta.env.MODE`]: `"${mode}"`, [`import.meta.env.SSR`]: "false", // Will be overridden per-environment // Custom env vars [`import.meta.env.BASE_URL`]: `"${base}"`, [`import.meta.env.PUBLIC_ORIGIN`]: `"${publicOrigin}"`, ...ssrDefine }; if (!getEnvValue("BASE_URL", primaryPrefix)) { setEnvValue("BASE_URL", base, primaryPrefix); } if (!getEnvValue("PUBLIC_ORIGIN", primaryPrefix)) { setEnvValue("PUBLIC_ORIGIN", publicOrigin, primaryPrefix); } if (condition === "react-client") { const clientConfig = { ...config, root: effectiveProjectRoot, mode, base, envPrefix: vitePrefix, resolve: { ...config.resolve, // Per-environment conditions are the load-bearing fix for the // dev-server case where the process was started with a global // `--conditions react-server` (the conventional `dev:rsc` script). // Without this explicit override, Node's module resolver sees // `react-server` for every environment and the client graph // pulls the `react-server` build of `react/jsx-runtime` — which // does not export `jsx` / `jsxs` — and `@chakra-ui/react`-style // packages' transitive CJS deps (`hoist-non-react-statics`, // etc.) lose their interop shims because they get resolved // through the SSR conditions path. Spelling the conditions out // here scopes `react-server` to the server env only. conditions: ssr ? [...defaultServerConditions] : [...defaultClientConditions], // For static builds (browser/ESM): don't externalize anything - bundle everything // For client/server builds (SSR): externalize React modules as usual external: ssr ? config.resolve?.external ?? [ "react", "react-dom", "react-server-dom-esm/client" ] : void 0, // Bundle everything for static builds // Vite 6 environments honor `resolve.noExternal` per-env, while the // legacy `ssr.noExternal` doesn't propagate. Mirror clientPackages // here too so the SSR env (outputs dist/client/) bundles them in // alongside user-authored .client.tsx files. noExternal: mergedNoExternal }, define, ssr: srrConfig, server: { ...config.server, // common default for stricter server operations // and ensures tests that use a server will fail early // also, we can't set the public origin without a port port, strictPort, host }, // client build options build: { ...config.build, modulePreload: config.build?.modulePreload ?? false, emptyOutDir: config.build?.emptyOutDir ?? true, outDir: config.build?.outDir ?? join(userOptions.build.outDir, envDir), assetsDir: config.build?.assetsDir ?? userOptions.build.assetsDir, copyPublicDir: typeof config.build?.copyPublicDir === "boolean" ? config.build?.copyPublicDir : !ssr, // modern browsers target: config.build?.target ?? ["esnext"], minify, rollupOptions: { ...config.build?.rollupOptions, // Use HTML + client entries for non-SSR client builds (static) // and pure client module entries for SSR client builds. input: Object.fromEntries( Object.entries( ssr ? autoDiscoveredFiles.clientInputs : autoDiscoveredFiles.staticInputs ).map(([key, value]) => [ key, value.slice(Number(value.startsWith("/"))) ]) ), output: newOutput, preserveEntrySignatures: config.build?.rollupOptions?.preserveEntrySignatures ?? "exports-only", // For static builds (browser/ESM): bundle everything including node_modules to avoid _virtual files // For client/server builds (SSR): externalize node_modules as usual external: ssr ? (id, _parent, _isResolved) => { if (id.includes("_virtual/") || id.startsWith("_virtual")) { return false; } const userExternal = config.build?.rollupOptions?.external ?? ["fsevents"]; if (Array.isArray(userExternal)) { return userExternal.includes(id); } if (typeof userExternal === "function") { return userExternal(id, _parent, _isResolved ?? false); } return false; } : (() => { const userExternal = config.build?.rollupOptions?.external ?? ["fsevents"]; if (Array.isArray(userExternal)) { return userExternal.filter((ext) => typeof ext === "string" && ext === "fsevents"); } if (typeof userExternal === "function") { return (id) => { if (id === "fsevents") return true; return false; }; } return ["fsevents"]; })() }, ssr, manifest: config.build?.manifest ?? `.vite/manifest.json`, ssrManifest: config.build?.ssrManifest ?? false, ssrEmitAssets: config.build?.ssrEmitAssets ?? true, cssCodeSplit: typeof config.build?.cssCodeSplit === "boolean" ? config.build?.cssCodeSplit : true } }; stashedUserConfig[envId] = clientConfig; return { type: "success", userConfig: clientConfig }; } else { const serverConfig = { ...config, root: effectiveProjectRoot, mode, base: userOptions.moduleBaseURL, envPrefix: vitePrefix, resolve: { ...config.resolve, // The server env owns the `react-server` condition. Spelling it // out here (instead of relying on a process-wide // `--conditions react-server` flag the caller set in // `NODE_OPTIONS`) lets the client / ssr envs resolve the // default React build of `react/jsx-runtime` in the same Vite // process — which is what fixes the client interop regression // surfaced when a client component imports a client package // (Chakra, emotion, …) under a global `--conditions // react-server` shell. conditions: [ "react-server", ...defaultServerConditions.filter((c) => c !== "react-server") ], externalConditions: config.resolve?.externalConditions ?? [ "react-server" ], // Force whitelisted client packages into the server bundle so the // RSC transform can convert their `"use client"` modules to client // references. Without this, Vite's default SSR externalization // leaves them as bare `import` statements that Node loads at SSG // time straight from `node_modules`, bypassing every transform. noExternal: mergedNoExternal }, define, ssr: srrConfig, // server build options build: { ...config.build, modulePreload: config.build?.modulePreload ?? false, emptyOutDir: config.build?.emptyOutDir ?? true, outDir: config.build?.outDir ?? join(userOptions.build.outDir, envDir), target: config.build?.target ?? "esnext", // Use esnext for pure ESM - no helpers needed minify, ssr, manifest: config.build?.manifest ?? `.vite/manifest.json`, ssrManifest: config.build?.ssrManifest ?? false, ssrEmitAssets: typeof config.build?.ssrEmitAssets === "boolean" ? config.build?.ssrEmitAssets : true, copyPublicDir: typeof config.build?.copyPublicDir === "boolean" ? config.build?.copyPublicDir : !ssr, assetsDir: config.build?.assetsDir ?? userOptions.build.assetsDir, // Ensure CSS files are output to static directory cssCodeSplit: typeof config.build?.cssCodeSplit === "boolean" ? config.build?.cssCodeSplit : true, rollupOptions: { ...config.build?.rollupOptions, input: autoDiscoveredFiles.serverInputs, preserveEntrySignatures: config.build?.rollupOptions?.preserveEntrySignatures ?? "strict", // Externalize core React deps for the server bundle external: config.build?.rollupOptions?.external ?? [ "react", "react/jsx-runtime", "react/jsx-dev-runtime", "react-dom", "react-server-dom-esm/server", "react-server-dom-esm/server.node" ], // Use the same output configuration as client environment for consistent hashing // This ensures client components have the same module IDs across all environments output: newOutput, // Configure Rollup context for server builds to use react-server conditions context: "module", // Set Node.js conditions for server builds plugins: [ ...Array.isArray(config.build?.rollupOptions?.plugins) ? config.build.rollupOptions.plugins : config.build?.rollupOptions?.plugins ? [config.build.rollupOptions.plugins] : [], { name: "react-server-conditions", buildStart() { if (typeof this.environment.name !== "string") { this.warn( "Environment name is not a string, skipping react-server-conditions plugin" ); return; } if (this.environment.name === "server") { if (!process.env.NODE_OPTIONS?.includes("react-server")) { if (this.environment.config.define) { this.environment.config.define = { ...this.environment.config.define, "process.env.NODE_OPTIONS": `"${process.env.NODE_OPTIONS} --conditions react-server"` }; } else { this.environment.config.define = { "process.env.NODE_OPTIONS": `"${process.env.NODE_OPTIONS} --conditions react-server"` }; } } } else { if (process.env.NODE_OPTIONS?.includes("react-server")) { if (this.environment.config.define && this.environment.config.define["process.env.NODE_OPTIONS"] && this.environment.config.define["process.env.NODE_OPTIONS"].includes("react-server")) { this.environment.config.define = { ...this.environment.config.define, "process.env.NODE_OPTIONS": this.environment.config.define["process.env.NODE_OPTIONS"].replace(/--conditions[=\s]react-server/, "") }; } else { this.environment.config.define = { "process.env.NODE_OPTIONS": "" }; } } } } }, // Add content hash plugin for consistent hashing across environments { name: "vite:plugin-react-server/content-hash", augmentChunkHash(chunk) { if (chunk.facadeModuleId) { try { const sourcePath = resolve( userOptions.projectRoot, chunk.facadeModuleId ); if (existsSync(sourcePath)) { const sourceContent = readFileSync(sourcePath, "utf-8"); return createRollupLikeHash(sourceContent, "hex"); } } catch (error) { return chunk.facadeModuleId; } } return; } } ] } } }; stashedUserConfig[envId] = serverConfig; return { type: "success", userConfig: serverConfig }; } }; export { resolveUserConfig }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzb2x2ZVVzZXJDb25maWcuanMiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3BsdWdpbi9jb25maWcvcmVzb2x2ZVVzZXJDb25maWcudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgY3JlYXRlTG9nZ2VyLFxuICBkZWZhdWx0Q2xpZW50Q29uZGl0aW9ucyxcbiAgZGVmYXVsdFNlcnZlckNvbmRpdGlvbnMsXG4gIHR5cGUgQ29uZmlnRW52LFxuICB0eXBlIFVzZXJDb25maWcsXG59IGZyb20gXCJ2aXRlXCI7XG5pbXBvcnQgdHlwZSB7XG4gIFJlc29sdmVkVXNlckNvbmZpZyxcbiAgUmVzb2x2ZWRVc2VyT3B0aW9ucyxcbiAgQXV0b0Rpc2NvdmVyZWRGaWxlcyxcbn0gZnJvbSBcIi4uL3R5cGVzLmpzXCI7XG5pbXBvcnQgeyBqb2luLCByZXNvbHZlIH0gZnJvbSBcIm5vZGU6cGF0aFwiO1xuaW1wb3J0IHsgcmVhZEZpbGVTeW5jLCBleGlzdHNTeW5jIH0gZnJvbSBcIm5vZGU6ZnNcIjtcbmltcG9ydCB0eXBlIHsgT3V0cHV0T3B0aW9ucywgUHJlUmVuZGVyZWRBc3NldCwgUHJlUmVuZGVyZWRDaHVuayB9IGZyb20gXCJyb2xsdXBcIjtcbmltcG9ydCB7IERFRkFVTFRfQ09ORklHIH0gZnJvbSBcIi4vZGVmYXVsdHMuanNcIjtcbmltcG9ydCB7IGdldE5vZGVFbnYgfSBmcm9tIFwiLi9nZXROb2RlRW52LmpzXCI7XG5pbXBvcnQgeyBnZXRFbnZWYWx1ZSwgc2V0RW52VmFsdWUgfSBmcm9tIFwiLi4vZW52L2dldEVudktleS5qc1wiO1xuaW1wb3J0IHtcbiAgbWVyZ2VDbGllbnRQYWNrYWdlc05vRXh0ZXJuYWwsXG4gIG1lcmdlQ2xpZW50UGFja2FnZXNPcHRpbWl6ZURlcHNFeGNsdWRlLFxufSBmcm9tIFwiLi4vY2xpZW50UGFja2FnZXMvaW5kZXguanNcIjtcbmltcG9ydCB7IGNyZWF0ZVJvbGx1cExpa2VIYXNoIH0gZnJvbSBcIi4vY3JlYXRlUm9sbHVwTGlrZUhhc2guanNcIjtcblxuY29uc3Qgc3Rhc2hlZFVzZXJDb25maWc6IFJlY29yZDxzdHJpbmcsIFJlc29sdmVkVXNlckNvbmZpZyB8IG51bGw+ID0ge307XG5sZXQgb3JpZ2luYWxDb25maWc6IFVzZXJDb25maWcgfCBudWxsID0gbnVsbDtcbmV4cG9ydCB0eXBlIFJlc29sdmVVc2VyQ29uZmlnUHJvcHMgPSB7XG4gIGNvbmRpdGlvbjogXCJyZWFjdC1jbGllbnRcIiB8IFwicmVhY3Qtc2VydmVyXCI7XG4gIGNvbmZpZzogVXNlckNvbmZpZztcbiAgY29uZmlnRW52OiBDb25maWdFbnY7XG4gIHVzZXJPcHRpb25zOiBSZXNvbHZlZFVzZXJPcHRpb25zO1xuICBhdXRvRGlzY292ZXJlZEZpbGVzOiBBdXRvRGlzY292ZXJlZEZpbGVzO1xuICBmb3JjZVJlc29sdmU/OiBib29sZWFuO1xuICBzc3I/OiBib29sZWFuO1xufTtcblxuZXhwb3J0IHR5cGUgUmVzb2x2ZVVzZXJDb25maWdSZXR1cm4gPVxuICB8IHsgdHlwZTogXCJzdWNjZXNzXCI7IHVzZXJDb25maWc6IFJlc29sdmVkVXNlckNvbmZpZyB9XG4gIHwgeyB0eXBlOiBcImVycm9yXCI7IGVycm9yOiB1bmtub3duIH07XG5cbmV4cG9ydCB0eXBlIFJlc29sdmVVc2VyQ29uZmlnRm4gPSAoXG4gIHByb3BzOiBSZXNvbHZlVXNlckNvbmZpZ1Byb3BzXG4pID0+IFJlc29sdmVVc2VyQ29uZmlnUmV0dXJuO1xuXG5leHBvcnQgY29uc3QgcmVzb2x2ZVVzZXJDb25maWc6IFJlc29sdmVVc2VyQ29uZmlnRm4gPVxuICBmdW5jdGlvbiBfcmVzb2x2ZVVzZXJDb25maWcoe1xuICAgIGNvbmRpdGlvbixcbiAgICBjb25maWcsXG4gICAgY29uZmlnRW52LFxuICAgIHVzZXJPcHRpb25zLFxuICAgIGF1dG9EaXNjb3ZlcmVkRmlsZXMsXG4gICAgZm9yY2VSZXNvbHZlID0gZmFsc2UsXG4gICAgc3NyID0gdW5kZWZpbmVkLFxuICB9KSB7XG4gICAgaWYgKCFmb3JjZVJlc29sdmUgJiYgb3JpZ2luYWxDb25maWcgPT0gbnVsbCkge1xuICAgICAgb3JpZ2luYWxDb25maWcgPSBjb25maWc7XG4gICAgfSBlbHNlIGlmIChvcmlnaW5hbENvbmZpZyAhPSBudWxsICYmIGNvbmZpZyAhPT0gb3JpZ2luYWxDb25maWcpIHtcbiAgICAgIGZvcmNlUmVzb2x2ZSA9IHRydWU7XG4gICAgfVxuXG4gICAgc3NyID1cbiAgICAgIHR5cGVvZiBzc3IgPT09IFwiYm9vbGVhblwiXG4gICAgICAgID8gc3NyXG4gICAgICAgIDogdHlwZW9mIGNvbmZpZy5idWlsZD8uc3NyID09PSBcImJvb2xlYW5cIlxuICAgICAgICA/IGNvbmZpZy5idWlsZD8uc3NyXG4gICAgICAgIDogY29uZGl0aW9uID09PSBcInJlYWN0LXNlcnZlclwiXG4gICAgICAgID8gdHJ1ZVxuICAgICAgICA6IHR5cGVvZiBjb25maWdFbnYuaXNTc3JCdWlsZCA9PT0gXCJib29sZWFuXCJcbiAgICAgICAgPyBjb25maWdFbnYuaXNTc3JCdWlsZFxuICAgICAgICA6IGZhbHNlO1xuXG4gICAgaWYgKGNvbmRpdGlvbiA9PT0gXCJyZWFjdC1zZXJ2ZXJcIiAmJiAhc3NyKSB7XG4gICAgICBjb25zdCBsb2dnZXIgPSBjb25maWcuY3VzdG9tTG9nZ2VyID8/IGNyZWF0ZUxvZ2dlcigpO1xuICAgICAgbG9nZ2VyLndhcm4oXG4gICAgICAgIFwicmVhY3Qtc2VydmVyIGJ1aWxkIHNob3VsZCBiZSBzc3IsIGJ1dCBpcyB3YXMgbWFudWFsbHkgc2V0IHRvIGZhbHNlLiBUaGlzIG1heSBub3Qgd29yayBhcyBleHBlY3RlZC5cIlxuICAgICAgKTtcbiAgICB9XG4gICAgY29uc3QgZW52RGlyID1cbiAgICAgIGNvbmRpdGlvbiA9PT0gXCJyZWFjdC1jbGllbnRcIiAmJiBzc3JcbiAgICAgICAgPyB1c2VyT3B0aW9ucy5idWlsZC5jbGllbnRcbiAgICAgICAgOiBjb25kaXRpb24gPT09IFwicmVhY3QtY2xpZW50XCJcbiAgICAgICAgPyB1c2VyT3B0aW9ucy5idWlsZC5zdGF0aWNcbiAgICAgICAgOiB1c2VyT3B0aW9ucy5idWlsZC5zZXJ2ZXI7XG4gICAgY29uc3QgZW52SWQgPSBgJHtlbnZEaXJ9JHtzc3IgPyBcIi1zc3JcIiA6IFwiXCJ9YDtcblxuICAgIGlmIChzdGFzaGVkVXNlckNvbmZpZ1tlbnZJZF0gJiYgIWZvcmNlUmVzb2x2ZSkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgdHlwZTogXCJzdWNjZXNzXCIsXG4gICAgICAgIHVzZXJDb25maWc6IHN0YXNoZWRVc2VyQ29uZmlnW2VudklkXSxcbiAgICAgIH07XG4gICAgfVxuXG4gICAgLy8gR2V0IGV4aXN0aW5nIGlucHV0c1xuXG4gICAgY29uc3QgaGFuZGxlU3NyRW50cnlOYW1lID0gKFxuICAgICAgaW5mbzogUHJlUmVuZGVyZWRDaHVuayxcbiAgICAgIGlucHV0OiBzdHJpbmcgfCBudWxsLFxuICAgICAgZmFsbGJhY2s6IChcbiAgICAgICAgaW5mbzogUHJlUmVuZGVyZWRDaHVuayxcbiAgICAgICAgc3NyOiBib29sZWFuLFxuICAgICAgICBzb3VyY2VDb250ZW50Pzogc3RyaW5nXG4gICAgICApID0+IHN0cmluZyxcbiAgICAgIHNzcjogYm9vbGVhblxuICAgICkgPT4ge1xuICAgICAgLy8gUmVhZCBzb3VyY2UgY29udGVudCBmb3IgY29uc2lzdGVudCBoYXNoaW5nIGFjcm9zcyBhbGwgZW52aXJvbm1lbnRzXG4gICAgICBsZXQgc291cmNlQ29udGVudDogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICAgICAgaWYgKGlucHV0KSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3Qgc291cmNlUGF0aCA9IHJlc29sdmUodXNlck9wdGlvbnMucHJvamVjdFJvb3QsIGlucHV0KTtcbiAgICAgICAgICBpZiAoZXhpc3RzU3luYyhzb3VyY2VQYXRoKSkge1xuICAgICAgICAgICAgc291cmNlQ29udGVudCA9IHJlYWRGaWxlU3luYyhzb3VyY2VQYXRoLCBcInV0Zi04XCIpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAvLyBGYWxsYmFjayB0byBmaWxlbmFtZS1iYXNlZCBoYXNoaW5nIGlmIHNvdXJjZSBmaWxlIGNhbid0IGJlIHJlYWRcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAoIXNzciB8fCAhaW5wdXQpIHtcbiAgICAgICAgaWYgKHR5cGVvZiBmYWxsYmFjayA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgICAgcmV0dXJuIGZhbGxiYWNrKGluZm8sIGZhbHNlLCBzb3VyY2VDb250ZW50KTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdXNlck9wdGlvbnMubm9ybWFsaXplcihpbmZvLm5hbWUpWzBdO1xuICAgICAgfVxuICAgICAgY29uc3Qgbm9ybWFsaXplZCA9IHVzZXJPcHRpb25zLm5vcm1hbGl6ZXIoaW5wdXQpO1xuICAgICAgbGV0IHZhbHVlID0gbm9ybWFsaXplZFsxXTtcbiAgICAgIGlmICh2YWx1ZS5zdGFydHNXaXRoKHVzZXJPcHRpb25zLm1vZHVsZUJhc2VQYXRoKSkge1xuICAgICAgICB2YWx1ZSA9IHZhbHVlLnNsaWNlKHVzZXJPcHRpb25zLm1vZHVsZUJhc2VQYXRoLmxlbmd0aCk7XG4gICAgICB9XG5cbiAgICAgIC8vIEFwcGx5IHRoZSBzYW1lIGhhc2ggZnVuY3Rpb24gZm9yIHNlcnZlciBlbnZpcm9ubWVudCB0byBlbnN1cmUgY29uc2lzdGVuY3lcbiAgICAgIC8vIFRoaXMgZW5zdXJlcyB0aGF0IGNsaWVudCBjb21wb25lbnRzIGhhdmUgdGhlIHNhbWUgaGFzaCBhY3Jvc3MgYWxsIGVudmlyb25tZW50c1xuICAgICAgaWYgKHR5cGVvZiBmYWxsYmFjayA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgIHJldHVybiBmYWxsYmFjayhpbmZvLCB0cnVlLCBzb3VyY2VDb250ZW50KTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB1c2VyT3B0aW9ucy5ub3JtYWxpemVyKGluZm8ubmFtZSlbMF07XG4gICAgfTtcblxuICAgIGNvbnN0IGhhbmRsZVNzckFzc2V0TmFtZSA9IChcbiAgICAgIGluZm86IFByZVJlbmRlcmVkQXNzZXQsXG4gICAgICBpbnB1dDogc3RyaW5nIHwgbnVsbCxcbiAgICAgIGZhbGxiYWNrOiAoaW5mbzogUHJlUmVuZGVyZWRBc3NldCwgc3NyOiBib29sZWFuKSA9PiBzdHJpbmcsXG4gICAgICBzc3I6IGJvb2xlYW5cbiAgICApID0+IHtcbiAgICAgIGlmIChpbmZvLnNvdXJjZSA9PT0gXCJcIikge1xuICAgICAgICByZXR1cm4gXCJcIjtcbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgaWYgdGhpcyBpcyBhIENTUyBmaWxlXG4gICAgICBjb25zdCBpc0Nzc0ZpbGUgPVxuICAgICAgICBpbnB1dD8uZW5kc1dpdGgoXCIuY3NzXCIpIHx8XG4gICAgICAgIGluZm8ubmFtZXM/LnNvbWUoKG5hbWUpID0+IG5hbWUuZW5kc1dpdGgoXCIuY3NzXCIpKTtcblxuICAgICAgaWYgKCFzc3IgfHwgIWlucHV0IHx8IGlzQ3NzRmlsZSkge1xuICAgICAgICBpZiAodHlwZW9mIGZhbGxiYWNrID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgICByZXR1cm4gZmFsbGJhY2soaW5mbywgc3NyKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdXNlck9wdGlvbnMubm9ybWFsaXplcihpbmZvLm5hbWVzWzBdKVswXTtcbiAgICAgIH1cblxuICAgICAgLy8gRmlyc3QgY2hlY2sgaWYgd2UgaGF2ZSBhIHN0YXRpYyBtYW5pZmVzdCBlbnRyeSBmb3IgY29uc2lzdGVudCBtb2R1bGUgSUQgcmVzb2x1dGlvblxuICAgICAgY29uc3Qgbm9ybWFsaXplZCA9IHVzZXJPcHRpb25zLm5vcm1hbGl6ZXIoaW5wdXQpO1xuICAgICAgbGV0IHZhbHVlID0gbm9ybWFsaXplZFsxXTtcbiAgICAgIGlmICh2YWx1ZS5zdGFydHNXaXRoKHVzZXJPcHRpb25zLm1vZHVsZUJhc2VQYXRoKSkge1xuICAgICAgICB2YWx1ZSA9IHZhbHVlLnNsaWNlKHVzZXJPcHRpb25zLm1vZHVsZUJhc2VQYXRoLmxlbmd0aCk7XG4gICAgICB9XG5cbiAgICAgIC8vIE5vdGU6IHN0YXRpY01hbmlmZXN0IGlzIG5vdCBhdmFpbGFibGUgZHVyaW5nIGF1dG8tZGlzY292ZXJ5IHBoYXNlXG4gICAgICAvLyBJdCdzIGxvYWRlZCBsYXRlciBkdXJpbmcgdGhlIGJ1aWxkIHByb2Nlc3NcblxuICAgICAgLy8gRmFsbCBiYWNrIHRvIHRoZSB1c2VyJ3MgYXNzZXRGaWxlIGZ1bmN0aW9uIGZvciBjb25zaXN0ZW50IGJlaGF2aW9yXG4gICAgICByZXR1cm4gZmFsbGJhY2soaW5mbywgc3NyKTtcbiAgICB9O1xuICAgIGNvbnN0IHVzZXJEZWZpbmVkT3V0cHV0ID0gY29uZmlnLmJ1aWxkPy5yb2xsdXBPcHRpb25zPy5vdXRwdXQ7XG4gICAgY29uc3QgaGFzT3RoZXJPdXRwdXQgPVxuICAgICAgQXJyYXkuaXNBcnJheSh1c2VyRGVmaW5lZE91dHB1dCkgJiYgdXNlckRlZmluZWRPdXRwdXQubGVuZ3RoID4gMTtcbiAgICBjb25zdCBoYXNWYWxpZE91dHB1dCA9IHVzZXJEZWZpbmVkT3V0cHV0ICYmICFoYXNPdGhlck91dHB1dDtcbiAgICBjb25zdCBoYXNPYmplY3RPdXRwdXQgPVxuICAgICAgdXNlckRlZmluZWRPdXRwdXQgJiZcbiAgICAgICFoYXNPdGhlck91dHB1dCAmJlxuICAgICAgdHlwZW9mIHVzZXJEZWZpbmVkT3V0cHV0ID09PSBcIm9iamVjdFwiICYmXG4gICAgICB1c2VyRGVmaW5lZE91dHB1dCAhPT0gbnVsbDtcblxuICAgIGNvbnN0IHVzZXJEZWZpbmVkQXNzZXRGaWxlTmFtZXMgPSBoYXNPYmplY3RPdXRwdXRcbiAgICAgID8gXCJhc3NldEZpbGVOYW1lc1wiIGluIHVzZXJEZWZpbmVkT3V0cHV0XG4gICAgICAgID8gdXNlckRlZmluZWRPdXRwdXQuYXNzZXRGaWxlTmFtZXNcbiAgICAgICAgOiB1bmRlZmluZWRcbiAgICAgIDogLy8gZmluZCB0aGUgb3RoZXIgYXNzZXQgZmlsZSBuYW1lc1xuICAgICAgaGFzT3RoZXJPdXRwdXRcbiAgICAgID8gKHVzZXJEZWZpbmVkT3V0cHV0LmZpbmQoKG8pID0+IG8/LmFzc2V0RmlsZU5hbWVzKSBhcyBPdXRwdXRPcHRpb25zKVxuICAgICAgICAgID8uYXNzZXRGaWxlTmFtZXNcbiAgICAgIDogdW5kZWZpbmVkO1xuXG4gICAgY29uc3QgdXNlckRlZmluZWRDaHVua0ZpbGVOYW1lcyA9IGhhc1ZhbGlkT3V0cHV0XG4gICAgICA/IFwiY2h1bmtGaWxlTmFtZXNcIiBpbiB1c2VyRGVmaW5lZE91dHB1dFxuICAgICAgICA/IHVzZXJEZWZpbmVkT3V0cHV0LmNodW5rRmlsZU5hbWVzXG4gICAgICAgIDogdW5kZWZpbmVkXG4gICAgICA6IHVuZGVmaW5lZDtcbiAgICBjb25zdCB1c2VyRGVmaW5lZEVudHJ5RmlsZU5hbWVzID0gaGFzVmFsaWRPdXRwdXRcbiAgICAgID8gXCJlbnRyeUZpbGVOYW1lc1wiIGluIHVzZXJEZWZpbmVkT3V0cHV0XG4gICAgICAgID8gdXNlckRlZmluZWRPdXRwdXQuZW50cnlGaWxlTmFtZXNcbiAgICAgICAgOiB1bmRlZmluZWRcbiAgICAgIDogdW5kZWZpbmVkO1xuXG4gICAgY29uc3Qgc3Rhc2hlZFJldHVybnM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fTtcbiAgICAvLyBSb2xsdXAncyBwcmVzZXJ2ZU1vZHVsZXNSb290IHdvcmtzIGluIHJldmVyc2Ugb2Ygd2hhdCB5b3UnZCBleHBlY3Q6XG4gICAgLy8gLSBXaGVuIHVzZXIgd2FudHMgcHJlc2VydmF0aW9uICh0cnVlKTogcGFzcyB1bmRlZmluZWQgdG8gUm9sbHVwIChkb24ndCBzdHJpcCBhbnl0aGluZylcbiAgICAvLyAtIFdoZW4gdXNlciB3YW50cyBzdHJpcHBpbmcgKGZhbHNlKTogcGFzcyBtb2R1bGVCYXNlIHRvIFJvbGx1cCAoc3RyaXAgdGhpcyBwYXRoKVxuICAgIC8vIEZvciBzdGF0aWMgYnVpbGRzOiB1c2UgZW1wdHkgc3RyaW5nIHRvIHByZXNlcnZlIG9ubHkgbW9kdWxlIG5hbWVzIChub3QgcGF0aHMpIHRvIHByZXZlbnQgX3ZpcnR1YWwgZmlsZXNcbiAgICBjb25zdCBwcmVzZXJ2ZU1vZHVsZXNSb290U3RyaW5nID1cbiAgICAgIHVzZXJPcHRpb25zLmJ1aWxkLnByZXNlcnZlTW9kdWxlc1Jvb3QgPT09IGZhbHNlXG4gICAgICAgID8gdXNlck9wdGlvbnMubW9kdWxlQmFzZSAvLyBTdHJpcCBzcmMvIGZyb20gb3V0cHV0IHBhdGhzXG4gICAgICAgIDogXCJcIjsgLy8gS2VlcCBzcmMvIGluIG91dHB1dCBwYXRocyBieSBzZXR0aW5nIHJvb3QgdG8gZW1wdHkgc3RyaW5nXG4gICAgXG5cbiAgICAvLyBGb3Igc3RhdGljIGJ1aWxkcyAoYnJvd3Nlci9FU00pOiBidW5kbGUgZXZlcnl0aGluZyAtIG5vIG5lZWQgdG8gcHJlc2VydmUgbW9kdWxlcyBvciBub2RlX21vZHVsZXMgc3RydWN0dXJlXG4gICAgLy8gRm9yIGNsaWVudC9zZXJ2ZXIgZW52aXJvbm1lbnRzIChTU1IpOiBwcmVzZXJ2ZSBtb2R1bGVzIHRvIG1haW50YWluIG1vZHVsZSBzdHJ1Y3R1cmUgZm9yIHNlcnZlci1zaWRlIHJlbmRlcmluZ1xuICAgIC8vIFVzZSBwcmVzZXJ2ZU1vZHVsZXM6IHRydWUgZm9yIFNTUiwgYnV0IGZvciBzdGF0aWMgYnVpbGRzIHVzZSBmYWxzZSB0byBwcmV2ZW50IF92aXJ0dWFsIGZpbGVzXG4gICAgY29uc3Qgc2hvdWxkUHJlc2VydmVNb2R1bGVzID0gc3NyIHx8IGNvbmRpdGlvbiA9PT0gXCJyZWFjdC1zZXJ2ZXJcIjtcbiAgICBcbiAgICBjb25zdCBwbHVnaW5PdXRwdXQgPSB7XG4gICAgICAvLyBGb3Igc3RhdGljIGJ1aWxkczogZmFsc2UgdG8gYnVuZGxlIGV2ZXJ5dGhpbmcgYW5kIHByZXZlbnQgX3ZpcnR1YWwgZmlsZXNcbiAgICAgIC8vIEZvciBTU1IgYnVpbGRzOiB1c2UgcHJlc2VydmVNb2R1bGVzIHdpdGggcHJlc2VydmVNb2R1bGVzUm9vdCBzZXQgdG8gcHJlc2VydmUgb25seSBtb2R1bGUgbmFtZXMgKG5vdCBwYXRocylcbiAgICAgIC8vIFRoaXMgcHJldmVudHMgX3ZpcnR1YWwgZmlsZXMgYnkgZmxhdHRlbmluZyB0aGUgb3V0cHV0IHN0cnVjdHVyZVxuICAgICAgcHJlc2VydmVNb2R1bGVzOiBzaG91bGRQcmVzZXJ2ZU1vZHVsZXMsXG4gICAgICAvLyBGb3Igc3RhdGljIGJ1aWxkczogdW5kZWZpbmVkIChub3QgbmVlZGVkIHdoZW4gcHJlc2VydmVNb2R1bGVzIGlzIGZhbHNlKVxuICAgICAgLy8gRm9yIFNTUiBidWlsZHM6IHNldCB0byBlbXB0eSBzdHJpbmcgdG8gcHJlc2VydmUgb25seSBtb2R1bGUgbmFtZXMsIHByZXZlbnRpbmcgX3ZpcnR1YWwgZmlsZXNcbiAgICAgIHByZXNlcnZlTW9kdWxlc1Jvb3Q6IHNob3VsZFByZXNlcnZlTW9kdWxlcyA/IChwcmVzZXJ2ZU1vZHVsZXNSb290U3RyaW5nID09PSBcIlwiID8gXCJcIiA6IHByZXNlcnZlTW9kdWxlc1Jvb3RTdHJpbmcpIDogdW5kZWZpbmVkLFxuICAgICAgZW50cnlGaWxlTmFtZXM6XG4gICAgICAgIHVzZXJEZWZpbmVkRW50cnlGaWxlTmFtZXMgPz9cbiAgICAgICAgKChpbmZvKSA9PiB7XG4gICAgICAgICAgY29uc3QgaW5wdXQgPVxuICAgICAgICAgICAgaW5mby5mYWNhZGVNb2R1bGVJZCA/P1xuICAgICAgICAgICAgaW5mby5uYW1lICsgdXNlck9wdGlvbnMuYnVpbGQubW9kdWxlRXh0ZW5zaW9uO1xuICAgICAgICAgIGNvbnN0IGlucHV0SWQgPSBpbnB1dCArIChzc3IgPyBcIi1zc3JcIiA6IFwiXCIpO1xuICAgICAgICAgIGlmICghc3Rhc2hlZFJldHVybnNbaW5wdXRJZF0pIHtcbiAgICAgICAgICAgIGNvbnN0IHIgPSBoYW5kbGVTc3JFbnRyeU5hbWUoXG4gICAgICAgICAgICAgIGluZm8sXG4gICAgICAgICAgICAgIGlucHV0LFxuICAgICAgICAgICAgICB1c2VyT3B0aW9ucy5idWlsZC5lbnRyeUZpbGUsXG4gICAgICAgICAgICAgIHNzclxuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgc3Rhc2hlZFJldHVybnNbaW5wdXRJZF0gPSByID8/IGluZm8ubmFtZTtcbiAgICAgICAgICB9XG4gICAgICAgICAgLy8gaW4gdGhlIGNhc2Ugb2YgZW1wdHkgYmFzZVBhdGgsIGl0IHdpbGwgbm90IGJlIHNsaWNlZCBmcm9tIHRoZSBwYXRoLCBzbywgd2UgbmVlZCB0byBzbGljZSBpdCBoZXJlXG4gICAgICAgICAgLy8gYXQgdGhlIGxhc3QgcG9zc2libGUgbW9tZW50IGFzIHRvIG5vdCBjb25mdXNlIHRoZSByZXN0IG9mIHRoZSBsb2dpYyBhcm91bmQgdGhlIGJhc2VQYXRoXG4gICAgICAgICAgcmV0dXJuIHN0YXNoZWRSZXR1cm5zW2lucHV0SWRdLnNsaWNlKFxuICAgICAgICAgICAgTnVtYmVyKHN0YXNoZWRSZXR1cm5zW2lucHV0SWRdLnN0YXJ0c1dpdGgoXCIvXCIpKVxuICAgICAgICAgICk7XG4gICAgICAgIH0pLFxuICAgICAgYXNzZXRGaWxlTmFtZXM6XG4gICAgICAgIHVzZXJEZWZpbmVkQXNzZXRGaWxlTmFtZXMgPz9cbiAgICAgICAgKChpbmZvKSA9PiB7XG4gICAgICAgICAgY29uc3QgaW5wdXQgPSBpbmZvLm9yaWdpbmFsRmlsZU5hbWVzWzBdO1xuICAgICAgICAgIGNvbnN0IGlucHV0SWQgPSBpbnB1dCArIChzc3IgPyBcIi1zc3JcIiA6IFwiXCIpO1xuXG4gICAgICAgICAgaWYgKCFzdGFzaGVkUmV0dXJuc1tpbnB1dElkXSkge1xuICAgICAgICAgICAgY29uc3QgciA9IGhhbmRsZVNzckFzc2V0TmFtZShcbiAgICAgICAgICAgICAgaW5mbyxcbiAgICAgICAgICAgICAgaW5wdXQsXG4gICAgICAgICAgICAgIHVzZXJPcHRpb25zLmJ1aWxkLmFzc2V0RmlsZSxcbiAgICAgICAgICAgICAgc3NyXG4gICAgICAgICAgICApO1xuXG4gICAgICAgICAgICBzdGFzaGVkUmV0dXJuc1tpbnB1dElkXSA9XG4gICAgICAgICAgICAgIHIgPz9cbiAgICAgICAgICAgICAgam9pbihcbiAgICAgICAgICAgICAgICB1c2VyT3B0aW9ucy5idWlsZC5wcmVzZXJ2ZU1vZHVsZXNSb290ID09PSBmYWxzZSAmJlxuICAgICAgICAgICAgICAgICAgdXNlck9wdGlvbnMuYnVpbGQuYXNzZXRzRGlyXG4gICAgICAgICAgICAgICAgICA/IHVzZXJPcHRpb25zLmJ1aWxkLmFzc2V0c0RpclxuICAgICAgICAgICAgICAgICAgOiBcIlwiLFxuICAgICAgICAgICAgICAgIHVzZXJPcHRpb25zLm5vcm1hbGl6ZXIoaW5wdXQpWzBdXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICAgIC8vIGluIHRoZSBjYXNlIG9mIGVtcHR5IGJhc2VQYXRoLCBpdCB3aWxsIG5vdCBiZSBzbGljZWQgZnJvbSB0aGUgcGF0aCwgc28sIHdlIG5lZWQgdG8gc2xpY2UgaXQgaGVyZVxuICAgICAgICAgIC8vIGF0IHRoZSBsYXN0IHBvc3NpYmxlIG1vbWVudCBhcyB0byBub3QgY29uZnVzZSB0aGUgcmVzdCBvZiB0aGUgbG9naWMgYXJvdW5kIHRoZSBiYXNlUGF0aFxuICAgICAgICAgIHJldHVybiBzdGFzaGVkUmV0dXJuc1tpbnB1dElkXS5zbGljZShcbiAgICAgICAgICAgIE51bWJlcihzdGFzaGVkUmV0dXJuc1tpbnB1dElkXS5zdGFydHNXaXRoKFwiL1wiKSlcbiAgICAgICAgICApO1xuICAgICAgICB9KSxcbiAgICAgIGNodW5rRmlsZU5hbWVzOlxuICAgICAgICB1c2VyRGVmaW5lZENodW5rRmlsZU5hbWVzID8/XG4gICAgICAgICgoaW5mbykgPT4ge1xuICAgICAgICAgIGNvbnN0IGlucHV0ID1cbiAgICAgICAgICAgIGluZm8uZmFjYWRlTW9kdWxlSWQgPz9cbiAgICAgICAgICAgIGluZm8ubmFtZSArIHVzZXJPcHRpb25zLmF1dG9EaXNjb3Zlci5tb2R1bGVQYXR0ZXJuLnNvdXJjZTtcbiAgICAgICAgICBjb25zdCBpbnB1dElkID0gaW5wdXQgKyAoc3NyID8gXCItc3NyXCIgOiBcIlwiKTtcblxuICAgICAgICAgIGlmICghc3Rhc2hlZFJldHVybnNbaW5wdXRJZF0pIHtcbiAgICAgICAgICAgIGNvbnN0IHIgPSBoYW5kbGVTc3JFbnRyeU5hbWUoXG4gICAgICAgICAgICAgIGluZm8sXG4gICAgICAgICAgICAgIGlucHV0LFxuICAgICAgICAgICAgICB1c2VyT3B0aW9ucy5idWlsZC5jaHVua0ZpbGUsXG4gICAgICAgICAgICAgIHNzclxuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgc3Rhc2hlZFJldHVybnNbaW5wdXRJZF0gPSByID8/IGluZm8ubmFtZTtcbiAgICAgICAgICB9XG4gICAgICAgICAgLy8gaW4gdGhlIGNhc2Ugb2YgZW1wdHkgYmFzZVBhdGgsIGl0IHdpbGwgbm90IGJlIHNsaWNlZCBmcm9tIHRoZSBwYXRoLCBzbywgd2UgbmVlZCB0byBzbGljZSBpdCBoZXJlXG4gICAgICAgICAgLy8gYXQgdGhlIGxhc3QgcG9zc2libGUgbW9tZW50IGFzIHRvIG5vdCBjb25mdXNlIHRoZSByZXN0IG9mIHRoZSBsb2dpYyBhcm91bmQgdGhlIGJhc2VQYXRoXG4gICAgICAgICAgcmV0dXJuIHN0YXNoZWRSZXR1cm5zW2lucHV0SWRdLnNsaWNlKFxuICAgICAgICAgICAgTnVtYmVyKHN0YXNoZWRSZXR1cm5zW2lucHV0SWRdLnN0YXJ0c1dpdGgoXCIvXCIpKVxuICAgICAgICAgICk7XG4gICAgICAgIH0pLFxuICAgICAgZm9ybWF0OiBcImVzbVwiLFxuICAgICAgZXhwb3J0czogXCJuYW1lZFwiLFxuICAgIH0gc2F0aXNmaWVzIE91dHB1dE9wdGlvbnM7XG5cbiAgICBjb25zdCBuZXdPdXRwdXQgPSBBcnJheS5pc0FycmF5KGNvbmZpZy5idWlsZD8ucm9sbHVwT3B0aW9ucz8ub3V0cHV0KVxuICAgICAgPyBbLi4uKGNvbmZpZy5idWlsZD8ucm9sbHVwT3B0aW9ucz8ub3V0cHV0IHx8IG51bGwpLCBwbHVnaW5PdXRwdXRdXG4gICAgICA6IHR5cGVvZiBjb25maWcuYnVpbGQ/LnJvbGx1cE9wdGlvbnM/Lm91dHB1dCA9PT0gXCJvYmplY3RcIiAmJlxuICAgICAgICBjb25maWcuYnVpbGQ/LnJvbGx1cE9wdGlvbnM/Lm91dHB1dCAhPT0gbnVsbFxuICAgICAgPyBbY29uZmlnLmJ1aWxkPy5yb2xsdXBPcHRpb25zPy5vdXRwdXQsIHBsdWdpbk91dHB1dF1cbiAgICAgIDogcGx1Z2luT3V0cHV0O1xuICAgIFxuICAgIGNvbnN0IHZpdGVQcmVmaXggPSBjb25maWcuZW52UHJlZml4ID8/IERFRkFVTFRfQ09ORklHLkVOVl9QUkVGSVg7XG5cbiAgICAvLyBHZXQgZW52aXJvbm1lbnQgdmFyaWFibGVzIChlbnYgdmFycyB0YWtlIHByZWNlZGVuY2Ugb3ZlciBjb25maWcpXG4gICAgY29uc3QgcHJpbWFyeVByZWZpeCA9XG4gICAgICB0eXBlb2Ygdml0ZVByZWZpeCA9PT0gXCJzdHJpbmdcIiA/IHZpdGVQcmVmaXggOiB2aXRlUHJlZml4WzBdO1xuICAgIGNvbnN0IGVudkJhc2VVcmwgPSBnZXRFbnZWYWx1ZShcIkJBU0VfVVJMXCIsIHByaW1hcnlQcmVmaXgpO1xuICAgIGNvbnN0IGVmZmVjdGl2ZU1vZHVsZUJhc2VVUkwgPVxuICAgICAgZW52QmFzZVVybCAhPSBudWxsICYmIGVudkJhc2VVcmwgIT09IFwiXCJcbiAgICAgICAgPyBlbnZCYXNlVXJsXG4gICAgICAgIDogdXNlck9wdGlvbnMubW9kdWxlQmFzZVVSTDtcblxuICAgIGNvbnN0IGVudlB1YmxpY09yaWdpbiA9IGdldEVudlZhbHVlKFwiUFVCTElDX09SSUdJTlwiLCBwcmltYXJ5UHJlZml4KTtcbiAgICBjb25zdCBlZmZlY3RpdmVQdWJsaWNPcmlnaW4gPVxuICAgICAgZW52UHVibGljT3JpZ2luICE9IG51bGwgPyBlbnZQdWJsaWNPcmlnaW4gOiB1c2VyT3B0aW9ucy5wdWJsaWNPcmlnaW47XG5cbiAgICAvLyBTaW5nbGUgc291cmNlIG9mIHRydXRoIGZvciB0aGUgYnVpbGQvcnVudGltZSBtb2RlLlxuICAgIC8vXG4gICAgLy8gUnVsZSAocGVyIG1haW50YWluZXIpOiBpZiB0aGUgdXNlciBFWFBMSUNJVExZIHNldCB0aGUgbW9kZSwgaG9ub3IgaXQ7XG4gICAgLy8gb3RoZXJ3aXNlIG1pcnJvciBOT0RFX0VOVi4gTm8gdGhyb3dpbmcgb24gYSBcImNvbnRyYWRpY3Rpb25cIiDigJQgZXhwbGljaXRcbiAgICAvLyBpbnRlbnQgYWx3YXlzIHdpbnMuXG4gICAgLy9cbiAgICAvLyBXaGF0IGNvdW50cyBhcyBcImV4cGxpY2l0XCIgKGVtcGlyaWNhbGx5IHZlcmlmaWVkIGFnYWluc3QgVml0ZSA2KTpcbiAgICAvLyAgIC0gYGNvbmZpZy5tb2RlYCBpcyB1bmRlZmluZWQgdW5sZXNzIHRoZSB1c2VyIHBhc3NlcyBgLS1tb2RlIDxtPmAgKFZpdGVcbiAgICAvLyAgICAgcHJvcGFnYXRlcyB0aGUgQ0xJIGFyZyBpbnRvIGBjb25maWcubW9kZWApIE9SIGF1dGhvcnMgYG1vZGVgIGluIHRoZWlyXG4gICAgLy8gICAgIHZpdGUgY29uZmlnLiBJdCBpcyBOT1QgcHJlLXBvcHVsYXRlZCB3aXRoIHRoZSBjb21tYW5kIGRlZmF1bHQsIHNvIGl0c1xuICAgIC8vICAgICBtZXJlIHByZXNlbmNlIGlzIGEgcmVsaWFibGUgXCJ1c2VyIHNldCB0aGlzXCIgc2lnbmFsLlxuICAgIC8vICAgLSBgY29uZmlnRW52Lm1vZGVgLCBieSBjb250cmFzdCwgSVMgcHJlLXBvcHVsYXRlZCB3aXRoIHRoZSBjb21tYW5kXG4gICAgLy8gICAgIGRlZmF1bHQgKFwicHJvZHVjdGlvblwiIGZvciBgdml0ZSBidWlsZGAsIFwiZGV2ZWxvcG1lbnRcIiBmb3Igc2VydmUpLCBzb1xuICAgIC8vICAgICBpdCBjYW5ub3QgZGlzdGluZ3Vpc2ggZXhwbGljaXQgaW50ZW50IGFuZCBtdXN0IG5vdCBnYXRlIHRoaXMgY2hvaWNlLlxuICAgIC8vXG4gICAgLy8gV2hlbiBubyBleHBsaWNpdCBtb2RlIGlzIGdpdmVuIHdlIG1pcnJvciBOT0RFX0VOViAobm9ybWFsaXplZCBieVxuICAgIC8vIGdldE5vZGVFbnYpLiBUaGlzIG1ha2VzIGBOT0RFX0VOVj1kZXZlbG9wbWVudCB2aXRlIGJ1aWxkYCBwcm9kdWNlIGEgZGV2XG4gICAgLy8gYnVpbGQgZXZlbiB0aG91Z2ggVml0ZSdzIGNvbW1hbmQgZGVmYXVsdCBtb2RlIGlzIFwicHJvZHVjdGlvblwiLlxuICAgIGNvbnN0IGV4cGxpY2l0TW9kZSA9XG4gICAgICB0eXBlb2YgY29uZmlnLm1vZGUgPT09IFwic3RyaW5nXCIgJiYgY29uZmlnLm1vZGUgIT09IFwiXCJcbiAgICAgICAgPyBjb25maWcubW9kZVxuICAgICAgICA6IHVuZGVmaW5lZDtcbiAgICBjb25zdCBtb2RlID0gZXhwbGljaXRNb2RlID8/IGdldE5vZGVFbnYoKTtcblxuICAgIC8vIENhbGN1bGF0ZSBlZmZlY3RpdmUgdmFsdWVzIGJhc2VkIG9uIGNvbW1hbmQgYW5kIGVudmlyb25tZW50XG4gICAgLy8gUHJpb3JpdGl6ZSB1c2VyT3B0aW9ucy5wcm9qZWN0Um9vdCB3aGVuIGV4cGxpY2l0bHkgc2V0LCByZWdhcmRsZXNzIG9mIGNvbmZpZy5yb290XG4gICAgLy8gVGhpcyBlbnN1cmVzIEVudmlyb25tZW50IEFQSSBlbnZpcm9ubWVudHMgdXNlIHRoZWlyIHNwZWNpZmljIHByb2plY3Qgcm9vdHNcbiAgICBjb25zdCBlZmZlY3RpdmVQcm9qZWN0Um9vdCA9XG4gICAgICB1c2VyT3B0aW9ucy5wcm9qZWN0Um9vdCB8fCBjb25maWcucm9vdCB8fCBcIlwiO1xuXG4gICAgLy8gQ2FsY3VsYXRlIG1vZHVsZVJvb3RQYXRoIGJhc2VkIG9uIGNvbW1hbmQgYW5kIGVudmlyb25tZW50XG4gICAgLy8gRHVyaW5nIHNlcnZlIChkZXZlbG9wbWVudCk6IHVzZSBtb2R1bGVCYXNlUGF0aCAoc291cmNlIHBhdGhzKVxuICAgIC8vIER1cmluZyBidWlsZCAocHJvZHVjdGlvbik6IHVzZSBidWlsZCBvdXRwdXQgcGF0aHNcbiAgICBsZXQgZWZmZWN0aXZlTW9kdWxlUm9vdFBhdGg6IHN0cmluZztcbiAgICBpZiAoY29uZmlnRW52LmNvbW1hbmQgPT09IFwic2VydmVcIikge1xuICAgICAgLy8gSW4gZGV2ZWxvcG1lbnQvc2VydmUgbW9kZSwgdXNlIG1vZHVsZUJhc2VQYXRoIGZvciBzb3VyY2UgcGF0aHNcbiAgICAgIC8vIHByZXNlcnZlIG1vZHVsZSByb290LCBpZiB0cnVlLCBzZXQgYXMgbW9kdWxlQmFzZVBhdGhcbiAgICAgIGVmZmVjdGl2ZU1vZHVsZVJvb3RQYXRoID0gdXNlck9wdGlvbnMubW9kdWxlQmFzZVBhdGggfHwgXCIvXCI7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIEluIHByb2R1Y3Rpb24vYnVpbGQgbW9kZSwgdXNlIGJ1aWxkIG91dHB1dCBwYXRoc1xuICAgICAgLy8gVGhlIFJTQyBzdHJlYW0gY29udGFpbnMgcmVmZXJlbmNlcyBsaWtlIFwic3JjL2NvbXBvbmVudHMvTGluay5jbGllbnQudHN4XCJcbiAgICAgIC8vIEJ1dCB0aGUgY29tcGlsZWQgZmlsZXMgYXJlIGluIGRpc3QvY2xpZW50L2NvbXBvbmVudHMvTGluay5jbGllbnQtQ0UtSkdScWEuanNcbiAgICAgIC8vIHRoZSBtYW5pZmVzdCBtYXBzIHRoZW0gZnJvbSB0aGUgY2xpZW50IGZvbGRlciwgc28gdGhpcyB0aGUgY29ycmVjdCByb290IGZvciBhIGJ1aWxkXG4gICAgICBlZmZlY3RpdmVNb2R1bGVSb290UGF0aCA9IGpvaW4oXG4gICAgICAgIGVmZmVjdGl2ZVByb2plY3RSb290LFxuICAgICAgICB1c2VyT3B0aW9ucy5idWlsZC5vdXREaXIsXG4gICAgICAgIHVzZXJPcHRpb25zLmJ1aWxkLmNsaWVudCxcbiAgICAgICAgIXVzZXJPcHRpb25zLm1vZHVsZUJhc2VQYXRoLnN0YXJ0c1dpdGgoXCIvXCIpID8gXCIvXCIgOiBcIlwiXG4gICAgICApO1xuICAgICAgaWYgKFxuICAgICAgICAhdXNlck9wdGlvbnMuYnVpbGQucHJlc2VydmVNb2R1bGVzUm9vdCAmJlxuICAgICAgICAhdXNlck9wdGlvbnMubW9kdWxlQmFzZVBhdGguc3RhcnRzV2l0aCh1c2VyT3B0aW9ucy5tb2R1bGVCYXNlKVxuICAgICAgKSB7XG4gICAgICAgIGVmZmVjdGl2ZU1vZHVsZVJvb3RQYXRoID0gam9pbihcbiAgICAgICAgICBlZmZlY3RpdmVNb2R1bGVSb290UGF0aCxcbiAgICAgICAgICB1c2VyT3B0aW9ucy5tb2R1bGVCYXNlLFxuICAgICAgICAgIHVzZXJPcHRpb25zLm1vZHVsZUJhc2VQYXRoID09PSBcIlwiID8gXCIvXCIgOiB1c2VyT3B0aW9ucy5tb2R1bGVCYXNlUGF0aFxuICAgICAgICApO1xuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IG1pbmlmeSA9IGNvbmZpZy5idWlsZD8ubWluaWZ5O1xuXG4gICAgLy8gUGFja2FnZXMgdGhhdCBpbnRlcm5hbGx5IHVzZSB0aGUgcGVyLWZpbGUgYFwidXNlIGNsaWVudFwiYCBjb252ZW50aW9uXG4gICAgLy8gKGUuZy4gQGNoYWtyYS11aS9yZWFjdCkuIFRoZSBkaXNjb3ZlcnkgcGx1Z2luIChyZWdpc3RlcmVkIGZpcnN0IGluXG4gICAgLy8gYm90aCBvcmNoZXN0cmF0b3JzKSBwb3B1bGF0ZXMgdGhpcyBsaXN0IHdpdGggYXV0by1kZXRlY3RlZCBwYWNrYWdlc1xuICAgIC8vIGJlZm9yZSB0aGlzIGhvb2sgcnVuczsgdXNlcnMgY2FuIGFsc28gYWRkIHRvIGl0IG1hbnVhbGx5IHZpYSB0aGVcbiAgICAvLyBgY2xpZW50UGFja2FnZXNgIG9wdGlvbi5cbiAgICAvL1xuICAgIC8vIFRocmVlIHRoaW5ncyBoYXBwZW4gaGVyZTpcbiAgICAvLyAgIDEuIGBvcHRpbWl6ZURlcHMuZXhjbHVkZWAga2VlcHMgZXNidWlsZCdzIHByZS1idW5kbGUgZnJvbSBzdHJpcHBpbmdcbiAgICAvLyAgICAgIHRoZSBwZXItZmlsZSBgXCJ1c2UgY2xpZW50XCJgIGRpcmVjdGl2ZXMgYmVmb3JlIG91ciB0cmFuc2Zvcm0uXG4gICAgLy8gICAyLiBgbm9FeHRlcm5hbGAgKGxlZ2FjeSBgc3NyLm5vRXh0ZXJuYWxgIEFORCBWaXRlLTYgZW52aXJvbm1lbnQtXG4gICAgLy8gICAgICBhd2FyZSBgcmVzb2x2ZS5ub0V4dGVybmFsYCkgbWFrZXMgUm9sbHVwIGlubGluZSB0aGVzZSBwYWNrYWdlc1xuICAgIC8vICAgICAgaW50byB0aGUgc2VydmVyIGJ1bmRsZSwgd2hlcmUgb3VyIHRyYW5zZm9ybSBjb252ZXJ0cyBlYWNoXG4gICAgLy8gICAgICBgXCJ1c2UgY2xpZW50XCJgIG1vZHVsZSB0byBhIGByZWdpc3RlckNsaWVudFJlZmVyZW5jZWAgc3R1Yi5cbiAgICAvLyAgIDMuIEVhY2ggYFwidXNlIGNsaWVudFwiYCBtb2R1bGUgZW1pdHMgYXMgYSBjaHVuayB1bmRlclxuICAgIC8vICAgICAgYGRpc3QvY2xpZW50L25vZGVfbW9kdWxlcy88cGtnPi8uLi5gIHRoYW5rcyB0byBWaXRlJ3MgbmF0dXJhbFxuICAgIC8vICAgICAgcHJlc2VydmVkLW1vZHVsZXMgaGFuZGxpbmcg4oCUIHRob3NlIHBhdGhzIG1hdGNoIHRoZSBtb2R1bGVJRHNcbiAgICAvLyAgICAgIHdlIGdlbmVyYXRlIGluIGBjcmVhdGVUcmFuc2Zvcm1lclBsdWdpbmAsIHNvIHRoZSBodG1sLXdvcmtlclxuICAgIC8vICAgICAgY2FuIHJlc29sdmUgY2xpZW50IHJlZnMgYXQgU1NHLXJlbmRlciB0aW1lLlxuICAgIGNvbnN0IGNsaWVudFBhY2thZ2VzOiByZWFkb25seSBzdHJpbmdbXSA9XG4gICAgICAodXNlck9wdGlvbnMgYXMgeyBjbGllbnRQYWNrYWdlcz86IHJlYWRvbmx5IHN0cmluZ1tdIH0pLmNsaWVudFBhY2thZ2VzID8/XG4gICAgICBbXTtcbiAgICBjb25zdCBtZXJnZWROb0V4dGVybmFsID0gbWVyZ2VDbGllbnRQYWNrYWdlc05vRXh0ZXJuYWwoXG4gICAgICBjbGllbnRQYWNrYWdlcyxcbiAgICAgIGNvbmZpZy5zc3I/Lm5vRXh0ZXJuYWxcbiAgICApO1xuICAgIGNvbnN0IHNyckNvbmZpZyA9IHtcbiAgICAgIC4uLmNvbmZpZy5zc3IsXG4gICAgICB0YXJnZXQ6IGNvbmZpZy5zc3I/LnRhcmdldCA/PyBcIm5vZGVcIixcbiAgICAgIG5vRXh0ZXJuYWw6IG1lcmdlZE5vRXh0ZXJuYWwsXG4gICAgICBvcHRpbWl6ZURlcHM6IHtcbiAgICAgICAgLi4uY29uZmlnLnNzcj8ub3B0aW1pemVEZXBzLFxuICAgICAgICBpbmNsdWRlOiBjb25maWcuc3NyPy5vcHRpbWl6ZURlcHM/LmluY2x1ZGUgPz8gW1xuICAgICAgICAgIFwicmVhY3RcIixcbiAgICAgICAgICBcInJlYWN0LWRvbVwiLFxuICAgICAgICAgIFwicmVhY3Qtc2VydmVyLWRvbS1lc20vY2xpZW50XCIsXG4gICAgICAgIF0sXG4gICAgICAgIGV4Y2x1ZGU6IG1lcmdlQ2xpZW50UGFja2FnZXNPcHRpbWl6ZURlcHNFeGNsdWRlKFxuICAgICAgICAgIGNsaWVudFBhY2thZ2VzLFxuICAgICAgICAgIGNvbmZpZy5zc3I/Lm9wdGltaXplRGVwcz8uZXhjbHVkZVxuICAgICAgICApLFxuICAgICAgfSxcbiAgICAgIHJlc29sdmU6IHtcbiAgICAgICAgLi4uY29uZmlnLnNzcj8ucmVzb2x2ZSxcbiAgICAgICAgZXh0ZXJuYWxDb25kaXRpb25zOiBjb25maWcuc3NyPy5yZXNvbHZlPy5leHRlcm5hbENvbmRpdGlvbnMgPz8gW1xuICAgICAgICAgIFwicmVhY3Qtc2VydmVyXCIsXG4gICAgICAgIF0sXG4gICAgICB9LFxuICAgIH07XG4gICAgbGV0IHB1YmxpY09yaWdpbiA9IGVmZmVjdGl2ZVB1YmxpY09yaWdpbiA/PyBERUZBVUxUX0NPTkZJRy5QVUJMSUNfT1JJR0lOO1xuICAgIGNvbnN0IHBvcnQgPVxuICAgICAgdHlwZW9mIGNvbmZpZy5zZXJ2ZXI/LnBvcnQgPT09IFwibnVtYmVyXCIgPyBjb25maWcuc2VydmVyPy5wb3J0IDogNTE3MztcbiAgICBjb25zdCBzdHJpY3RQb3J0ID0gY29uZmlnLnNlcnZlcj8uc3RyaWN0UG9ydCA/PyB0cnVlO1xuICAgIGNvbnN0IGhvc3QgPVxuICAgICAgdHlwZW9mIGNvbmZpZy5zZXJ2ZXI/Lmhvc3QgPT09IFwic3RyaW5nXCJcbiAgICAgICAgPyBjb25maWcuc2VydmVyPy5ob3N0XG4gICAgICAgIDogXCJsb2NhbGhvc3RcIjtcbiAgICBjb25zdCBiYXNlID1cbiAgICAgIGVmZmVjdGl2ZU1vZHVsZUJhc2VVUkwgPz8gY29uZmlnLmJhc2UgPz8gREVGQVVMVF9DT05GSUcuTU9EVUxFX0JBU0VfVVJMO1xuICAgIGlmIChjb25maWdFbnYuY29tbWFuZCA9PT0gXCJzZXJ2ZVwiICYmICFjb25maWdFbnYuaXNQcmV2aWV3KSB7XG4gICAgICAvLyBJbiBkZXYgbW9kZSwgdXNlIGVtcHR5IHB1YmxpY09yaWdpbiBzbyB0aGUgY2xpZW50IHVzZXMgd2luZG93LmxvY2F0aW9uLm9yaWdpbi5cbiAgICAgIC8vIFRoaXMgYXZvaWRzIGhhcmRjb2RpbmcgYSBwb3J0IHRoYXQgbWF5IGNoYW5nZSBpZiB0aGUgY29uZmlndXJlZCBwb3J0IGlzIHRha2VuLlxuICAgICAgcHVibGljT3JpZ2luID0gXCJcIjtcbiAgICB9XG4gICAgLy8gVXNlIHRoZSBzaW5nbGUgYXV0aG9yaXRhdGl2ZSBgbW9kZWAgcmVzb2x2ZWQgYWJvdmUgKE5PVCBjb25maWdFbnYubW9kZSlcbiAgICAvLyBzbyB0aGUgUmVhY3QgYnVpbGQgZGVmaW5lIGFuZCB2cHJzJ3MgaW50ZXJuYWwgbW9kZSBjYW4gbmV2ZXIgZGl2ZXJnZSDigJRcbiAgICAvLyBpbiBwYXJ0aWN1bGFyIHNvIGEgY29uZmlnLWZpbGUgYG1vZGVgIG9yIGBOT0RFX0VOVmAgcHJvcGFnYXRlcyBpbnRvIHRoZVxuICAgIC8vIGBwcm9jZXNzLmVudi5OT0RFX0VOVmAgLyBgaW1wb3J0Lm1ldGEuZW52Lk1PREVgIGRlZmluZXMgdGhhdCBzZWxlY3QgdGhlXG4gICAgLy8gZGV2LXZzLXByb2QgUmVhY3QgYnVpbGQuXG4gICAgY29uc3QgaXNEZXYgPSBtb2RlID09PSAnZGV2ZWxvcG1lbnQnIHx8IGNvbmZpZ0Vudi5jb21tYW5kID09PSAnc2VydmUnO1xuICAgIGNvbnN0IHNzckRlZmluZSA9IHtcbiAgICAgIFtgcHJvY2Vzcy5lbnYuJHtwcmltYXJ5UHJlZml4fUJBU0VfVVJMYF06IGBcIiR7YmFzZX1cImAsXG4gICAgICBbYHByb2Nlc3MuZW52LiR7cHJpbWFyeVByZWZpeH1QVUJMSUNfT1JJR0lOYF06IGBcIiR7cHVibGljT3JpZ2lufVwiYCxcbiAgICAgIFtgcHJvY2Vzcy5lbnYuTk9ERV9FTlZgXTogYFwiJHttb2RlfVwiYCxcbiAgICAgIFtgcHJvY2Vzcy5lbnYuVklURV9ERVZgXTogaXNEZXYgPyAndHJ1ZScgOiAnZmFsc2UnLFxuICAgICAgW2Bwcm9jZXNzLmVudi5WSVRFX1BST0RgXTogaXNEZXYgPyAnZmFsc2UnIDogJ3RydWUnLFxuICAgIH07XG4gICAgY29uc3QgZGVmaW5lID0ge1xuICAgICAgLi4uY29uZmlnLmRlZmluZSxcbiAgICAgIC8vIFN0YW5kYXJkIFZpdGUgZW52IHZhcnNcbiAgICAgIFtgaW1wb3J0Lm1ldGEuZW52LkRFVmBdOiBpc0RldiA/ICd0cnVlJyA6ICdmYWxzZScsXG4gICAgICBbYGltcG9ydC5tZXRhLmVudi5QUk9EYF06IGlzRGV2ID8gJ2ZhbHNlJyA6ICd0cnVlJyxcbiAgICAgIFtgaW1wb3J0Lm1ldGEuZW52Lk1PREVgXTogYFwiJHttb2RlfVwiYCxcbiAgICAgIFtgaW1wb3J0Lm1ldGEuZW52LlNTUmBdOiAnZmFsc2UnLCAvLyBXaWxsIGJlIG92ZXJyaWRkZW4gcGVyLWVudmlyb25tZW50XG4gICAgICAvLyBDdXN0b20gZW52IHZhcnNcbiAgICAgIFtgaW1wb3J0Lm1ldGEuZW52LkJBU0VfVVJMYF06IGBcIiR7YmFzZX1cImAsXG4gICAgICBbYGltcG9ydC5tZXRhLmVudi5QVUJMSUNfT1JJR0lOYF06IGBcIiR7cHVibGljT3JpZ2lufVwiYCxcbiAgICAgIC4uLnNzckRlZmluZSxcbiAgICB9O1xuICAgIC8vIFNldCBwcm9jZXNzLmVudiB2YWx1ZXMgdG8gZW5zdXJlIHRoZXkncmUgYXZhaWxhYmxlIGluIHByb2Nlc3MuZW52IGZvciBzZXJ2ZXItc2lkZSBjb2RlXG4gICAgaWYgKCFnZXRFbnZWYWx1ZShcIkJBU0VfVVJMXCIsIHByaW1hcnlQcmVmaXgpKSB7XG4gICAgICBzZXRFbnZWYWx1ZShcIkJBU0VfVVJMXCIsIGJhc2UsIHByaW1hcnlQcmVmaXgpO1xuICAgIH1cbiAgICBpZiAoIWdldEVudlZhbHVlKFwiUFVCTElDX09SSUdJTlwiLCBwcmltYXJ5UHJlZml4KSkge1xuICAgICAgc2V0RW52VmFsdWUoXCJQVUJMSUNfT1JJR0lOXCIsIHB1YmxpY09yaWdpbiwgcHJpbWFyeVByZWZpeCk7XG4gICAgfVxuXG4gICAgaWYgKGNvbmRpdGlvbiA9PT0gXCJyZWFjdC1jbGllbnRcIikge1xuICAgICAgLy8gY2xpZW50IHBsdWdpbiBidWlsZCBvcHRpb25zIChjbGllbnQgcGx1Z2luIHN0aWxsIG91dHB1dHMgc2VydmVyIGZpbGVzKVxuICAgICAgY29uc3QgY2xpZW50Q29uZmlnID0ge1xuICAgICAgICAuLi5jb25maWcsXG4gICAgICAgIHJvb3Q6IGVmZmVjdGl2ZVByb2plY3RSb290LFxuICAgICAgICBtb2RlOiBtb2RlLFxuICAgICAgICBiYXNlOiBiYXNlLFxuICAgICAgICBlbnZQcmVmaXg6IHZpdGVQcmVmaXgsXG4gICAgICAgIHJlc29sdmU6IHtcbiAgICAgICAgICAuL