vite-plugin-react-server
Version:
Vite plugin for React Server Components (RSC)
513 lines (511 loc) • 91.1 kB
JavaScript
/**
* 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