vite-plugin-react-server
Version:
Vite plugin for React Server Components (RSC)
466 lines (464 loc) • 66.1 kB
JavaScript
/**
* vite-plugin-react-server
* Copyright (c) Nico Brinkkemper
* MIT License
*/
import { React } from '../vendor/vendor.server.js';
import { collectViteModuleGraphCss } from '../helpers/collectViteModuleGraphCss.js';
import { createRscStream } from '../stream/createRscStream.server.js';
import { createRenderToPipeableStreamHandler } from '../stream/createRenderToPipeableStreamHandler.server.js';
import { createWorker } from '../worker/createWorker.js';
import { serializedOptions } from '../helpers/serializeUserOptions.js';
import { requestInfo } from '../helpers/requestInfo.js';
import { getRouteFiles } from '../helpers/getRouteFiles.js';
import { handleServerAction } from './handleServerAction.server.js';
import { handleError } from '../error/handleError.js';
import { cleanupWorker } from '../helpers/workerCleanup.js';
import { mergeConfig } from 'vite';
import { resolvePageAndProps } from '../helpers/resolvePageAndProps.js';
import { Root } from '../components/root.js';
const configureReactServer = function _configureReactServer({
server,
autoDiscoveredFiles,
userOptions: _userOptions,
serverManifest,
resolvedConfig
}) {
const activeStreams = /* @__PURE__ */ new Set();
const activeControllers = /* @__PURE__ */ new Map();
let isRestarting = false;
const logger = server.config.customLogger || server.config.logger;
const {
Html: _UserHtmlComponent,
// loader config isn't important here, since that's used by the transformer
loader: _loaderConfig,
verbose,
// we can use these directly to create the handler
...userHandlerOptions
} = _userOptions;
server.config = mergeConfig(
server.config,
resolvedConfig
);
server.ws.on("restart", (path) => {
logger.info(
"[vite-plugin-react-server] 🔧 Plugin changed, preparing for restart:",
path
);
isRestarting = true;
for (const res of activeStreams) {
const controller = activeControllers.get(res);
if (controller) {
try {
controller.abort("Server restarting");
} catch (e) {
}
}
res.writeHead(503, {
"Content-Type": "text/x-component; charset=utf-8",
"Retry-After": "1"
});
res.end(
`0:E{"digest":"","name":"Error","message":"Server restarting...","stack":"","env":"Server"}`
);
}
activeStreams.clear();
activeControllers.clear();
setTimeout(() => {
if (isRestarting) {
isRestarting = false;
logger.info(
"[vite-plugin-react-server] ⏰ Restart timeout, resuming normal operation"
);
}
}, 5e3);
});
server.ws.on("full-reload", () => {
isRestarting = false;
logger.info("[vite-plugin-react-server] ✅ Server restart completed");
});
const loader = async (id) => {
const [moduleID, exportName] = id.split("#");
let fullModulePath;
if (moduleID.startsWith(_userOptions.projectRoot)) {
fullModulePath = moduleID;
} else {
const resolvedModuleID = moduleID.startsWith("/") ? moduleID.slice(1) : moduleID;
fullModulePath = `${_userOptions.projectRoot}/${resolvedModuleID}`;
}
const serverEnv = server.environments["server"];
let result;
if (serverEnv && "runner" in serverEnv && serverEnv.runner) {
result = await serverEnv.runner.import(fullModulePath);
} else {
result = await server.ssrLoadModule(fullModulePath);
}
if (result == null)
throw new Error(`Module "${moduleID}" does not have any exports`);
if (!Object.keys(result).length && exportName.length)
throw new Error(
`Module "${moduleID}" is a module, but does not have any exports so it can't find ${exportName}`
);
return result;
};
server.middlewares.use(async (req, res, next) => {
if (!req.url) {
return next();
}
const handlerOptions = {
...userHandlerOptions,
moduleBaseURL: server.config.base,
moduleBasePath: userHandlerOptions.moduleBasePath,
projectRoot: _userOptions.projectRoot,
css: userHandlerOptions.css,
loader,
verbose,
logger,
rscStream: res
};
const info = requestInfo(req, handlerOptions, "");
if (info.isServerActionRequest) {
return handleServerAction(req, res, server, handlerOptions);
}
if (!info.isRscRequest) {
return next();
}
if (isRestarting) {
res.writeHead(503, {
"Content-Type": "text/x-component; charset=utf-8",
"Retry-After": "1"
});
res.end(
`0:E{"digest":"","name":"Error","message":"Server restarting...","stack":"","env":"Server"}`
);
return;
}
let rscWorker;
try {
const routeFiles = await getRouteFiles(
info.route,
autoDiscoveredFiles,
_userOptions,
logger
);
if (routeFiles.type === "error") {
const panicError = handleError({
error: routeFiles.error,
logger,
panicThreshold: userHandlerOptions.panicThreshold,
critical: false,
context: "configureReactServer"
});
if (panicError != null) {
return next(panicError);
}
return next();
}
const pagePath = routeFiles.page;
const propsPath = routeFiles.props;
const rootPath = routeFiles.root;
if (!pagePath) {
if (verbose) {
logger.info(`No page found for route: ${info.route}, skipping`);
}
return next();
}
if (verbose) {
logger.info(
`Components resolved successfully for route: ${info.route}`
);
}
if (verbose) {
logger.info(
`PageComponent is valid, creating handler options for route: ${info.route}`
);
}
const handlerOptions2 = {
...userHandlerOptions,
route: info.route,
pagePath,
propsPath,
logger,
manifest: serverManifest,
server,
moduleBaseURL: server.config.base,
projectRoot: _userOptions.projectRoot,
loader,
verbose
};
let PageComponent = React.Fragment;
let RootComponent = Root;
let pageProps = {};
if (rootPath) {
try {
const rootExportName = userHandlerOptions.rootExportName || "Root";
const rootModule = await loader(rootPath);
if (rootModule && rootModule[rootExportName] && typeof rootModule[rootExportName] === "function") {
RootComponent = rootModule[rootExportName];
if (verbose) {
logger.info(`Loaded Root component for route ${info.route} from ${rootPath}#${rootExportName}`);
}
} else if (rootModule && rootModule["default"] && typeof rootModule["default"] === "function") {
RootComponent = rootModule["default"];
if (verbose) {
logger.info(`Loaded default export as Root component for route ${info.route}`);
}
} else {
if (verbose) {
logger.warn(`Root component not found in ${rootPath}, using React.Fragment`);
}
}
} catch (error) {
const panicError = handleError({
error,
logger,
panicThreshold: userHandlerOptions.panicThreshold,
critical: true,
context: `configureReactServer: load Root from ${rootPath}`,
log: true
});
if (panicError != null) {
return next(panicError);
}
}
}
if (pagePath) {
try {
const pageExportName = userHandlerOptions.pageExportName || "Page";
const pageModule = await loader(pagePath);
if (pageModule && pageModule[pageExportName] && typeof pageModule[pageExportName] === "function") {
PageComponent = pageModule[pageExportName];
if (verbose) {
logger.info(`Loaded Page component for route ${info.route} from ${pagePath}#${pageExportName}`);
}
} else if (pageModule && pageModule["default"] && typeof pageModule["default"] === "function") {
PageComponent = pageModule["default"];
if (verbose) {
logger.info(`Loaded default export as Page component for route ${info.route}`);
}
} else if (pageModule && typeof pageModule === "function") {
PageComponent = pageModule;
if (verbose) {
logger.info(`Loaded module as Page component for route ${info.route}`);
}
} else {
if (verbose) {
logger.warn(`Page component not found in ${pagePath}, using React.Fragment`);
}
}
} catch (error) {
const panicError = handleError({
error,
logger,
panicThreshold: userHandlerOptions.panicThreshold,
critical: true,
context: `configureReactServer: load Page from ${pagePath}`,
log: true
});
if (panicError != null) {
return next(panicError);
}
}
}
if (verbose) {
logger.info(`Collecting CSS files for route: ${info.route}`);
}
const serverEnv = server.environments["server"];
const moduleGraphForCss = serverEnv?.moduleGraph ?? server.moduleGraph;
const cssFilesResult = await collectViteModuleGraphCss({
moduleGraph: moduleGraphForCss,
parentUrl: pagePath,
handlerOptions: handlerOptions2
});
if (verbose) {
logger.info(`CSS collection completed for route: ${info.route}`);
}
if (cssFilesResult.type === "skip") {
if (verbose) {
logger.info(`CSS collection skipped for route: ${info.route}, continuing with RSC rendering`);
}
}
if (cssFilesResult.type === "error") {
return next(cssFilesResult.error);
}
const collectedCssFiles = cssFilesResult.type === "success" ? cssFilesResult.cssFiles : /* @__PURE__ */ new Map();
if (verbose) {
logger.info(`Creating RSC handler for route: ${info.route}`);
}
try {
if (verbose) {
logger.info(`[configureReactServer] Loading props for route ${info.route}, pagePath: ${pagePath}, propsPath: ${propsPath}, url: ${info.url}`);
}
const propsResult = await resolvePageAndProps({
pagePath,
propsPath,
pageExportName: userHandlerOptions.pageExportName,
propsExportName: userHandlerOptions.propsExportName,
url: info.url,
route: info.route,
moduleBaseURL: server.config.base,
loader,
verbose,
logger,
build: {
rscOutputPath: userHandlerOptions.build?.rscOutputPath || ".rsc"
}
});
if (verbose) {
logger.info(`[configureReactServer] Props resolution result type: ${propsResult.type}`);
}
if (propsResult.type === "success") {
pageProps = propsResult.pageProps || {};
if (verbose) {
logger.info(`[configureReactServer] Loaded props for route ${info.route}: ${JSON.stringify(pageProps, null, 2)}`);
}
} else if (propsResult.type === "skip") {
if (verbose) {
logger.info(`[configureReactServer] Props resolution skipped for route ${info.route}, using empty props`);
}
pageProps = {};
} else {
if (verbose) {
logger.warn(`[configureReactServer] Failed to load props for route ${info.route}: ${propsResult.error}`);
}
pageProps = {};
}
} catch (error) {
if (verbose) {
logger.warn(`[configureReactServer] Error loading props for route ${info.route}: ${error}`);
if (error instanceof Error) {
logger.warn(`[configureReactServer] Error stack: ${error.stack}`);
}
}
pageProps = {};
}
const useWorkerInDev = _userOptions.dev?.useRscWorker === true;
if (useWorkerInDev) {
try {
if (verbose) {
logger.info(`Creating RSC worker for route: ${info.route} (useRscWorker=true)`);
}
const workerResult = await createWorker({
projectRoot: _userOptions.projectRoot || server.config.root,
workerData: {
userOptions: serializedOptions(_userOptions, autoDiscoveredFiles),
resolvedConfig: server.config,
configEnv: { command: "serve", mode: "development" }
},
verbose,
logger
});
if (workerResult.type === "success") {
rscWorker = workerResult.worker;
if (verbose) {
logger.info(`RSC worker created successfully for route: ${info.route}`);
}
} else {
if (verbose) {
logger.warn(`RSC worker creation skipped for route: ${info.route}: ${workerResult.reason}`);
}
}
} catch (error) {
if (verbose) {
logger.warn(`Failed to create RSC worker for route: ${info.route}: ${error}`);
}
}
} else {
if (verbose) {
logger.info(`[dev:rsc] Using direct rendering (no worker) for proper HMR support`);
}
}
const rscResult = rscWorker ? createRscStream({
...handlerOptions2,
url: info.url,
pagePath,
propsPath,
rootPath,
// Pass the root path for worker to load
htmlPath: "",
// Empty string = headless RSC (no Html wrapper)
rscWorker,
cssFiles: collectedCssFiles,
globalCss: /* @__PURE__ */ new Map()
}) : createRenderToPipeableStreamHandler({
...handlerOptions2,
url: info.url,
PageComponent,
RootComponent,
HtmlComponent: React.Fragment,
// Headless stream - no Html wrapper
pageProps,
// Pass the loaded props
cssFiles: collectedCssFiles
});
if (verbose) {
logger.info(
`RSC handler created for route: ${info.route}, result type: ${typeof rscResult}, has pipe: ${typeof rscResult?.pipe}, has abort: ${typeof rscResult?.abort}`
);
}
if (rscResult && typeof rscResult.pipe === "function") {
if (verbose) {
logger.info(`Setting up RSC stream for route: ${info.route}`);
}
res.setHeader("Content-Type", "text/x-component; charset=utf-8");
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
res.setHeader("Pragma", "no-cache");
res.setHeader("Expires", "0");
const origin = req.headers.origin;
if (origin && (origin.includes("localhost") || origin.includes("127.0.0.1"))) {
res.setHeader("Access-Control-Allow-Origin", origin);
} else {
res.setHeader("Access-Control-Allow-Origin", "*");
}
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Accept, Content-Type");
res.setHeader("Access-Control-Max-Age", "86400");
rscResult.pipe(res);
activeControllers.set(res, rscResult);
} else {
if (verbose) {
logger.error(
`RSC handler failed for route: ${info.route}, invalid result: ${typeof rscResult}`
);
}
res.statusCode = 500;
res.end("Internal Server Error");
}
activeStreams.add(res);
res.on("close", () => {
activeStreams.delete(res);
const controller = activeControllers.get(res);
if (controller && typeof controller.abort === "function") {
try {
controller.abort();
} catch (error) {
}
}
activeControllers.delete(res);
cleanupWorker(rscWorker);
});
} catch (error) {
const panicError = handleError({
error,
logger,
panicThreshold: handlerOptions.panicThreshold,
critical: false,
log: true
});
if (panicError != null) {
return next(panicError);
}
if (!res.headersSent) {
res.statusCode = 500;
res.setHeader("Content-Type", "text/plain; charset=utf-8");
}
if (!res.writableEnded) {
const message = error instanceof Error ? error.message : String(error);
res.end(`RSC render failed: ${message}
`);
}
}
});
};
export { configureReactServer };
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlndXJlUmVhY3RTZXJ2ZXIuc2VydmVyLmpzIiwic291cmNlcyI6WyIuLi8uLi8uLi9wbHVnaW4vZGV2LXNlcnZlci9jb25maWd1cmVSZWFjdFNlcnZlci5zZXJ2ZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUgeyBTZXJ2ZXJSZXNwb25zZSB9IGZyb20gXCJodHRwXCI7XG5pbXBvcnQgeyBSZWFjdCB9IGZyb20gXCIuLi92ZW5kb3IvdmVuZG9yLnNlcnZlci5qc1wiO1xuaW1wb3J0IHsgY29sbGVjdFZpdGVNb2R1bGVHcmFwaENzcyB9IGZyb20gXCIuLi9oZWxwZXJzL2NvbGxlY3RWaXRlTW9kdWxlR3JhcGhDc3MuanNcIjtcbmltcG9ydCB7IGNyZWF0ZVJzY1N0cmVhbSB9IGZyb20gXCIuLi9zdHJlYW0vY3JlYXRlUnNjU3RyZWFtLnNlcnZlci5qc1wiO1xuaW1wb3J0IHsgY3JlYXRlUmVuZGVyVG9QaXBlYWJsZVN0cmVhbUhhbmRsZXIgfSBmcm9tIFwiLi4vc3RyZWFtL2NyZWF0ZVJlbmRlclRvUGlwZWFibGVTdHJlYW1IYW5kbGVyLnNlcnZlci5qc1wiO1xuaW1wb3J0IHsgY3JlYXRlV29ya2VyIH0gZnJvbSBcIi4uL3dvcmtlci9jcmVhdGVXb3JrZXIuanNcIjtcbmltcG9ydCB0eXBlIHsgV29ya2VyIH0gZnJvbSBcIm5vZGU6d29ya2VyX3RocmVhZHNcIjtcbmltcG9ydCB7IHNlcmlhbGl6ZWRPcHRpb25zIH0gZnJvbSBcIi4uL2hlbHBlcnMvc2VyaWFsaXplVXNlck9wdGlvbnMuanNcIjtcbmltcG9ydCB7IHJlcXVlc3RJbmZvIH0gZnJvbSBcIi4uL2hlbHBlcnMvcmVxdWVzdEluZm8uanNcIjtcbmltcG9ydCB7IGdldFJvdXRlRmlsZXMgfSBmcm9tIFwiLi4vaGVscGVycy9nZXRSb3V0ZUZpbGVzLmpzXCI7XG5pbXBvcnQgeyBoYW5kbGVTZXJ2ZXJBY3Rpb24gfSBmcm9tIFwiLi9oYW5kbGVTZXJ2ZXJBY3Rpb24uc2VydmVyLmpzXCI7XG5pbXBvcnQgdHlwZSB7IENvbmZpZ3VyZVJlYWN0U2VydmVyRm4gfSBmcm9tIFwiLi90eXBlcy5qc1wiO1xuaW1wb3J0IHsgaGFuZGxlRXJyb3IgfSBmcm9tIFwiLi4vZXJyb3IvaGFuZGxlRXJyb3IuanNcIjtcbmltcG9ydCB7IGNsZWFudXBXb3JrZXIgfSBmcm9tIFwiLi4vaGVscGVycy93b3JrZXJDbGVhbnVwLmpzXCI7XG5pbXBvcnQgeyBtZXJnZUNvbmZpZywgdHlwZSBSZXNvbHZlZENvbmZpZyB9IGZyb20gXCJ2aXRlXCI7XG5pbXBvcnQgeyByZXNvbHZlUGFnZUFuZFByb3BzIH0gZnJvbSBcIi4uL2hlbHBlcnMvcmVzb2x2ZVBhZ2VBbmRQcm9wcy5qc1wiO1xuaW1wb3J0IHsgUm9vdCBhcyBEZWZhdWx0Um9vdCB9IGZyb20gXCIuLi9jb21wb25lbnRzL3Jvb3QuanNcIjtcblxuZXhwb3J0IGNvbnN0IGNvbmZpZ3VyZVJlYWN0U2VydmVyOiBDb25maWd1cmVSZWFjdFNlcnZlckZuID1cbiAgZnVuY3Rpb24gX2NvbmZpZ3VyZVJlYWN0U2VydmVyKHtcbiAgICBzZXJ2ZXIsXG4gICAgYXV0b0Rpc2NvdmVyZWRGaWxlcyxcbiAgICB1c2VyT3B0aW9uczogX3VzZXJPcHRpb25zLFxuICAgIHNlcnZlck1hbmlmZXN0LFxuICAgIHJlc29sdmVkQ29uZmlnLFxuICB9KSB7XG4gICAgY29uc3QgYWN0aXZlU3RyZWFtcyA9IG5ldyBTZXQ8U2VydmVyUmVzcG9uc2U+KCk7XG4gICAgY29uc3QgYWN0aXZlQ29udHJvbGxlcnMgPSBuZXcgTWFwPFxuICAgICAgU2VydmVyUmVzcG9uc2UsXG4gICAgICB7IGFib3J0OiAocmVhc29uPzogdW5rbm93bikgPT4gdm9pZCB9XG4gICAgPigpO1xuICAgIGxldCBpc1Jlc3RhcnRpbmcgPSBmYWxzZTtcbiAgICBcbiAgICBcbiAgICBjb25zdCBsb2dnZXIgPSBzZXJ2ZXIuY29uZmlnLmN1c3RvbUxvZ2dlciB8fCBzZXJ2ZXIuY29uZmlnLmxvZ2dlcjtcbiAgICBjb25zdCB7XG4gICAgICBIdG1sOiBfVXNlckh0bWxDb21wb25lbnQsXG4gICAgICAvLyBsb2FkZXIgY29uZmlnIGlzbid0IGltcG9ydGFudCBoZXJlLCBzaW5jZSB0aGF0J3MgdXNlZCBieSB0aGUgdHJhbnNmb3JtZXJcbiAgICAgIGxvYWRlcjogX2xvYWRlckNvbmZpZyxcbiAgICAgIHZlcmJvc2UsXG4gICAgICAvLyB3ZSBjYW4gdXNlIHRoZXNlIGRpcmVjdGx5IHRvIGNyZWF0ZSB0aGUgaGFuZGxlclxuICAgICAgLi4udXNlckhhbmRsZXJPcHRpb25zXG4gICAgfSA9IF91c2VyT3B0aW9ucztcblxuICAgIHNlcnZlci5jb25maWcgPSBtZXJnZUNvbmZpZyhcbiAgICAgIHNlcnZlci5jb25maWcsXG4gICAgICByZXNvbHZlZENvbmZpZ1xuICAgICkgYXMgUmVzb2x2ZWRDb25maWc7XG5cblxuICAgIC8vIEhhbmRsZSBWaXRlIHNlcnZlciByZXN0YXJ0c1xuICAgIHNlcnZlci53cy5vbihcInJlc3RhcnRcIiwgKHBhdGgpID0+IHtcbiAgICAgIGxvZ2dlci5pbmZvKFxuICAgICAgICBcIlt2aXRlLXBsdWdpbi1yZWFjdC1zZXJ2ZXJdIPCflKcgUGx1Z2luIGNoYW5nZWQsIHByZXBhcmluZyBmb3IgcmVzdGFydDpcIixcbiAgICAgICAgcGF0aFxuICAgICAgKTtcblxuICAgICAgaXNSZXN0YXJ0aW5nID0gdHJ1ZTtcblxuICAgICAgLy8gQWJvcnQgYWxsIGFjdGl2ZSBzdHJlYW1zIGZpcnN0LCB0aGVuIGNsb3NlIHJlc3BvbnNlc1xuICAgICAgZm9yIChjb25zdCByZXMgb2YgYWN0aXZlU3RyZWFtcykge1xuICAgICAgICBjb25zdCBjb250cm9sbGVyID0gYWN0aXZlQ29udHJvbGxlcnMuZ2V0KHJlcyk7XG4gICAgICAgIGlmIChjb250cm9sbGVyKSB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnRyb2xsZXIuYWJvcnQoXCJTZXJ2ZXIgcmVzdGFydGluZ1wiKTtcbiAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAvLyBJZ25vcmUgYWJvcnQgZXJyb3JzXG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmVzLndyaXRlSGVhZCg1MDMsIHtcbiAgICAgICAgICBcIkNvbnRlbnQtVHlwZVwiOiBcInRleHQveC1jb21wb25lbnQ7IGNoYXJzZXQ9dXRmLThcIixcbiAgICAgICAgICBcIlJldHJ5LUFmdGVyXCI6IFwiMVwiLFxuICAgICAgICB9KTtcbiAgICAgICAgcmVzLmVuZChcbiAgICAgICAgICBgMDpFe1wiZGlnZXN0XCI6XCJcIixcIm5hbWVcIjpcIkVycm9yXCIsXCJtZXNzYWdlXCI6XCJTZXJ2ZXIgcmVzdGFydGluZy4uLlwiLFwic3RhY2tcIjpcIlwiLFwiZW52XCI6XCJTZXJ2ZXJcIn1gXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgICBhY3RpdmVTdHJlYW1zLmNsZWFyKCk7XG4gICAgICBhY3RpdmVDb250cm9sbGVycy5jbGVhcigpO1xuXG4gICAgICAvLyBGYWxsYmFjazogcmVzZXQgcmVzdGFydCBmbGFnIGFmdGVyIGEgdGltZW91dFxuICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgIGlmIChpc1Jlc3RhcnRpbmcpIHtcbiAgICAgICAgICBpc1Jlc3RhcnRpbmcgPSBmYWxzZTtcbiAgICAgICAgICBsb2dnZXIuaW5mbyhcbiAgICAgICAgICAgIFwiW3ZpdGUtcGx1Z2luLXJlYWN0LXNlcnZlcl0g4o+wIFJlc3RhcnQgdGltZW91dCwgcmVzdW1pbmcgbm9ybWFsIG9wZXJhdGlvblwiXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgfSwgNTAwMCk7IC8vIDUgc2Vjb25kIHRpbWVvdXRcbiAgICB9KTtcblxuICAgIC8vIEhhbmRsZSByZXN0YXJ0IGNvbXBsZXRpb25cbiAgICBzZXJ2ZXIud3Mub24oXCJmdWxsLXJlbG9hZFwiLCAoKSA9PiB7XG4gICAgICBpc1Jlc3RhcnRpbmcgPSBmYWxzZTtcbiAgICAgIGxvZ2dlci5pbmZvKFwiW3ZpdGUtcGx1Z2luLXJlYWN0LXNlcnZlcl0g4pyFIFNlcnZlciByZXN0YXJ0IGNvbXBsZXRlZFwiKTtcbiAgICB9KTtcblxuICAgIGNvbnN0IGxvYWRlciA9IGFzeW5jIChpZDogc3RyaW5nKSA9PiB7XG4gICAgICBjb25zdCBbbW9kdWxlSUQsIGV4cG9ydE5hbWVdID0gaWQuc3BsaXQoXCIjXCIpO1xuICAgICAgXG4gICAgICAvLyBEZXRlcm1pbmUgdGhlIGZ1bGwgbW9kdWxlIHBhdGhcbiAgICAgIGxldCBmdWxsTW9kdWxlUGF0aDogc3RyaW5nO1xuICAgICAgXG4gICAgICAvLyBDaGVjayBpZiBhbHJlYWR5IGFuIGFic29sdXRlIHBhdGggKGZyb20gc2VydmVyIGVudmlyb25tZW50IG1vZHVsZSBncmFwaClcbiAgICAgIGlmIChtb2R1bGVJRC5zdGFydHNXaXRoKF91c2VyT3B0aW9ucy5wcm9qZWN0Um9vdCkpIHtcbiAgICAgICAgZnVsbE1vZHVsZVBhdGggPSBtb2R1bGVJRDtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIFJlc29sdmUgcmVsYXRpdmUgcGF0aCBhZ2FpbnN0IHByb2plY3Qgcm9vdFxuICAgICAgICBjb25zdCByZXNvbHZlZE1vZHVsZUlEID0gbW9kdWxlSUQuc3RhcnRzV2l0aChcIi9cIikgXG4gICAgICAgICAgPyBtb2R1bGVJRC5zbGljZSgxKSBcbiAgICAgICAgICA6IG1vZHVsZUlEO1xuICAgICAgICBmdWxsTW9kdWxlUGF0aCA9IGAke191c2VyT3B0aW9ucy5wcm9qZWN0Um9vdH0vJHtyZXNvbHZlZE1vZHVsZUlEfWA7XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIFVzZSBzZXJ2ZXIgZW52aXJvbm1lbnQgcnVubmVyIGZvciBwcm9wZXIgcmVhY3Qtc2VydmVyIGNvbmRpdGlvbiBoYW5kbGluZ1xuICAgICAgLy8gVGhpcyBlbnN1cmVzIGNsaWVudCBjb21wb25lbnRzIGFyZSB0cmFuc2Zvcm1lZCB0byByZWdpc3RlckNsaWVudFJlZmVyZW5jZVxuICAgICAgY29uc3Qgc2VydmVyRW52ID0gc2VydmVyLmVudmlyb25tZW50c1snc2VydmVyJ107XG4gICAgICBsZXQgcmVzdWx0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICAgIFxuICAgICAgaWYgKHNlcnZlckVudiAmJiAncnVubmVyJyBpbiBzZXJ2ZXJFbnYgJiYgc2VydmVyRW52LnJ1bm5lcikge1xuICAgICAgICAvLyBWaXRlIDYgRW52aXJvbm1lbnQgQVBJOiB1c2Ugc2VydmVyIGVudmlyb25tZW50IHJ1bm5lciBmb3IgUlNDXG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IChzZXJ2ZXJFbnYucnVubmVyIGFzIHsgaW1wb3J0OiAodXJsOiBzdHJpbmcpID0+IFByb21pc2U8UmVjb3JkPHN0cmluZywgdW5rbm93bj4+IH0pLmltcG9ydChmdWxsTW9kdWxlUGF0aCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBGYWxsYmFjayB0byBzc3JMb2FkTW9kdWxlIChzaG91bGQgbm90IGhhcHBlbiBpbiBWaXRlIDYrKVxuICAgICAgICByZXN1bHQgPSBhd2FpdCBzZXJ2ZXIuc3NyTG9hZE1vZHVsZShmdWxsTW9kdWxlUGF0aCk7XG4gICAgICB9XG4gICAgICBpZiAocmVzdWx0ID09IG51bGwpXG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgTW9kdWxlIFxcXCIke21vZHVsZUlEfVxcXCIgZG9lcyBub3QgaGF2ZSBhbnkgZXhwb3J0c2ApO1xuXG4gICAgICBpZiAoIU9iamVjdC5rZXlzKHJlc3VsdCkubGVuZ3RoICYmIGV4cG9ydE5hbWUubGVuZ3RoKVxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgYE1vZHVsZSBcXFwiJHttb2R1bGVJRH1cXFwiIGlzIGEgbW9kdWxlLCBidXQgZG9lcyBub3QgaGF2ZSBhbnkgZXhwb3J0cyBzbyBpdCBjYW4ndCBmaW5kICR7ZXhwb3J0TmFtZX1gXG4gICAgICAgICk7XG5cbiAgICAgIC8vIEFsd2F5cyByZXR1cm4gdGhlIGZ1bGwgbW9kdWxlIC0gY2FsbGVycyB3aWxsIGV4dHJhY3QgdGhlIHNwZWNpZmljIGV4cG9ydCBpZiBuZWVkZWRcbiAgICAgIC8vIFRoaXMgaXMgY29uc2lzdGVudCB3aXRoIGhvdyByZXNvbHZlUGFnZSBhbmQgcmVzb2x2ZVByb3BzIHdvcmtcbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfTtcbiAgICBzZXJ2ZXIubWlkZGxld2FyZXMudXNlKGFzeW5jIChyZXEsIHJlcywgbmV4dCkgPT4ge1xuICAgICAgaWYgKCFyZXEudXJsKSB7XG4gICAgICAgIHJldHVybiBuZXh0KCk7XG4gICAgICB9XG4gICAgICBjb25zdCBoYW5kbGVyT3B0aW9ucyA9IHtcbiAgICAgICAgLi4udXNlckhhbmRsZXJPcHRpb25zLFxuICAgICAgICBtb2R1bGVCYXNlVVJMOiBzZXJ2ZXIuY29uZmlnLmJhc2UsXG4gICAgICAgIG1vZHVsZUJhc2VQYXRoOiB1c2VySGFuZGxlck9wdGlvbnMubW9kdWxlQmFzZVBhdGgsXG4gICAgICAgIHByb2plY3RSb290OiBfdXNlck9wdGlvbnMucHJvamVjdFJvb3QsXG4gICAgICAgIGNzczogdXNlckhhbmRsZXJPcHRpb25zLmNzcyxcbiAgICAgICAgbG9hZGVyOiBsb2FkZXIsXG4gICAgICAgIHZlcmJvc2UsXG4gICAgICAgIGxvZ2dlcixcbiAgICAgICAgcnNjU3RyZWFtOiByZXNcbiAgICAgIH07XG4gICAgICBjb25zdCBpbmZvID0gcmVxdWVzdEluZm8ocmVxLCBoYW5kbGVyT3B0aW9ucywgXCJcIik7XG5cbiAgICAgIC8vIEhhbmRsZSBzZXJ2ZXIgYWN0aW9uc1xuICAgICAgaWYgKGluZm8uaXNTZXJ2ZXJBY3Rpb25SZXF1ZXN0KSB7XG4gICAgICAgIHJldHVybiBoYW5kbGVTZXJ2ZXJBY3Rpb24ocmVxLCByZXMsIHNlcnZlciwgaGFuZGxlck9wdGlvbnMpO1xuICAgICAgfVxuICAgICAgaWYgKCFpbmZvLmlzUnNjUmVxdWVzdCkge1xuICAgICAgICByZXR1cm4gbmV4dCgpO1xuICAgICAgfVxuXG4gICAgICAvLyBJZiBzZXJ2ZXIgaXMgcmVzdGFydGluZywgcmV0dXJuIDUwMyBpbW1lZGlhdGVseVxuICAgICAgaWYgKGlzUmVzdGFydGluZykge1xuICAgICAgICByZXMud3JpdGVIZWFkKDUwMywge1xuICAgICAgICAgIFwiQ29udGVudC1UeXBlXCI6IFwidGV4dC94LWNvbXBvbmVudDsgY2hhcnNldD11dGYtOFwiLFxuICAgICAgICAgIFwiUmV0cnktQWZ0ZXJcIjogXCIxXCIsXG4gICAgICAgIH0pO1xuICAgICAgICByZXMuZW5kKFxuICAgICAgICAgIGAwOkV7XCJkaWdlc3RcIjpcIlwiLFwibmFtZVwiOlwiRXJyb3JcIixcIm1lc3NhZ2VcIjpcIlNlcnZlciByZXN0YXJ0aW5nLi4uXCIsXCJzdGFja1wiOlwiXCIsXCJlbnZcIjpcIlNlcnZlclwifWBcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICAvLyBDcmVhdGUgUlNDIHdvcmtlciBmb3IgY29uc2lzdGVudCBSU0Mgc3RyZWFtIGZvcm1hdHNcbiAgICAgIGxldCByc2NXb3JrZXI6IFdvcmtlciB8IHVuZGVmaW5lZDtcbiAgICAgIFxuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3Qgcm91dGVGaWxlcyA9IGF3YWl0IGdldFJvdXRlRmlsZXMoXG4gICAgICAgICAgaW5mby5yb3V0ZSxcbiAgICAgICAgICBhdXRvRGlzY292ZXJlZEZpbGVzLFxuICAgICAgICAgIF91c2VyT3B0aW9ucyxcbiAgICAgICAgICBsb2dnZXJcbiAgICAgICAgKTtcbiAgICAgICAgaWYgKHJvdXRlRmlsZXMudHlwZSA9PT0gXCJlcnJvclwiKSB7XG4gICAgICAgICAgY29uc3QgcGFuaWNFcnJvciA9IGhhbmRsZUVycm9yKHtcbiAgICAgICAgICAgIGVycm9yOiByb3V0ZUZpbGVzLmVycm9yLFxuICAgICAgICAgICAgbG9nZ2VyOiBsb2dnZXIsXG4gICAgICAgICAgICBwYW5pY1RocmVzaG9sZDogdXNlckhhbmRsZXJPcHRpb25zLnBhbmljVGhyZXNob2xkLFxuICAgICAgICAgICAgY3JpdGljYWw6IGZhbHNlLFxuICAgICAgICAgICAgY29udGV4dDogXCJjb25maWd1cmVSZWFjdFNlcnZlclwiLFxuICAgICAgICAgIH0pO1xuICAgICAgICAgIGlmIChwYW5pY0Vycm9yICE9IG51bGwpIHtcbiAgICAgICAgICAgIHJldHVybiBuZXh0KHBhbmljRXJyb3IpO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gbmV4dCgpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHBhZ2VQYXRoID0gcm91dGVGaWxlcy5wYWdlO1xuICAgICAgICBjb25zdCBwcm9wc1BhdGggPSByb3V0ZUZpbGVzLnByb3BzO1xuICAgICAgICBjb25zdCByb290UGF0aCA9IHJvdXRlRmlsZXMucm9vdDtcblxuICAgICAgICAvLyBDaGVjayBpZiB3ZSBoYXZlIGEgcGFnZSBwYXRoIC0gaWYgbm90LCBza2lwIHRoaXMgcm91dGVcbiAgICAgICAgaWYgKCFwYWdlUGF0aCkge1xuICAgICAgICAgIGlmICh2ZXJib3NlKSB7XG4gICAgICAgICAgICBsb2dnZXIuaW5mbyhgTm8gcGFnZSBmb3VuZCBmb3Igcm91dGU6ICR7aW5mby5yb3V0ZX0sIHNraXBwaW5nYCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBuZXh0KCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodmVyYm9zZSkge1xuICAgICAgICAgIGxvZ2dlci5pbmZvKFxuICAgICAgICAgICAgYENvbXBvbmVudHMgcmVzb2x2ZWQgc3VjY2Vzc2Z1bGx5IGZvciByb3V0ZTogJHtpbmZvLnJvdXRlfWBcbiAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHZlcmJvc2UpIHtcbiAgICAgICAgICBsb2dnZXIuaW5mbyhcbiAgICAgICAgICAgIGBQYWdlQ29tcG9uZW50IGlzIHZhbGlkLCBjcmVhdGluZyBoYW5kbGVyIG9wdGlvbnMgZm9yIHJvdXRlOiAke2luZm8ucm91dGV9YFxuICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBoYW5kbGVyT3B0aW9ucyA9IHtcbiAgICAgICAgICAuLi51c2VySGFuZGxlck9wdGlvbnMsXG4gICAgICAgICAgcm91dGU6IGluZm8ucm91dGUsXG4gICAgICAgICAgcGFnZVBhdGgsXG4gICAgICAgICAgcHJvcHNQYXRoLFxuICAgICAgICAgIGxvZ2dlcjogbG9nZ2VyLFxuICAgICAgICAgIG1hbmlmZXN0OiBzZXJ2ZXJNYW5pZmVzdCxcbiAgICAgICAgICBzZXJ2ZXIsXG4gICAgICAgICAgbW9kdWxlQmFzZVVSTDogc2VydmVyLmNvbmZpZy5iYXNlLFxuICAgICAgICAgIHByb2plY3RSb290OiBfdXNlck9wdGlvbnMucHJvamVjdFJvb3QsXG4gICAgICAgICAgbG9hZGVyOiBsb2FkZXIsXG4gICAgICAgICAgdmVyYm9zZTogdmVyYm9zZSxcbiAgICAgICAgfTtcblxuICAgICAgICAvLyBMb2FkIGFjdHVhbCBjb21wb25lbnRzIGZpcnN0IC0gdGhpcyByZWdpc3RlcnMgdGhlbSBpbiB0aGUgbW9kdWxlIGdyYXBoXG4gICAgICAgIC8vIHdoaWNoIGlzIHJlcXVpcmVkIGZvciBDU1MgY29sbGVjdGlvbiB0byB3b3JrXG4gICAgICAgIGxldCBQYWdlQ29tcG9uZW50OiBSZWFjdC5Db21wb25lbnRUeXBlPGFueT4gPSBSZWFjdC5GcmFnbWVudDtcbiAgICAgICAgbGV0IFJvb3RDb21wb25lbnQ6IFJlYWN0LkNvbXBvbmVudFR5cGU8YW55PiA9IERlZmF1bHRSb290O1xuICAgICAgICBsZXQgcGFnZVByb3BzOiBhbnkgPSB7fTtcbiAgICAgICAgXG4gICAgICAgIC8vIExvYWQgdGhlIFJvb3QgY29tcG9uZW50XG4gICAgICAgIGlmIChyb290UGF0aCkge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCByb290RXhwb3J0TmFtZSA9IHVzZXJIYW5kbGVyT3B0aW9ucy5yb290RXhwb3J0TmFtZSB8fCBcIlJvb3RcIjtcbiAgICAgICAgICAgIGNvbnN0IHJvb3RNb2R1bGUgPSBhd2FpdCBsb2FkZXIocm9vdFBhdGgpO1xuXG4gICAgICAgICAgICBpZiAocm9vdE1vZHVsZSAmJiByb290TW9kdWxlW3Jvb3RFeHBvcnROYW1lXSAmJiB0eXBlb2Ygcm9vdE1vZHVsZVtyb290RXhwb3J0TmFtZV0gPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgICAgUm9vdENvbXBvbmVudCA9IHJvb3RNb2R1bGVbcm9vdEV4cG9ydE5hbWVdIGFzIFJlYWN0LkNvbXBvbmVudFR5cGU8YW55PjtcbiAgICAgICAgICAgICAgaWYgKHZlcmJvc2UpIHtcbiAgICAgICAgICAgICAgICBsb2dnZXIuaW5mbyhgTG9hZGVkIFJvb3QgY29tcG9uZW50IGZvciByb3V0ZSAke2luZm8ucm91dGV9IGZyb20gJHtyb290UGF0aH0jJHtyb290RXhwb3J0TmFtZX1gKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIGlmIChyb290TW9kdWxlICYmIHJvb3RNb2R1bGVbJ2RlZmF1bHQnXSAmJiB0eXBlb2Ygcm9vdE1vZHVsZVsnZGVmYXVsdCddID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICAgIFJvb3RDb21wb25lbnQgPSByb290TW9kdWxlWydkZWZhdWx0J10gYXMgUmVhY3QuQ29tcG9uZW50VHlwZTxhbnk+O1xuICAgICAgICAgICAgICBpZiAodmVyYm9zZSkge1xuICAgICAgICAgICAgICAgIGxvZ2dlci5pbmZvKGBMb2FkZWQgZGVmYXVsdCBleHBvcnQgYXMgUm9vdCBjb21wb25lbnQgZm9yIHJvdXRlICR7aW5mby5yb3V0ZX1gKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgaWYgKHZlcmJvc2UpIHtcbiAgICAgICAgICAgICAgICBsb2dnZXIud2FybihgUm9vdCBjb21wb25lbnQgbm90IGZvdW5kIGluICR7cm9vdFBhdGh9LCB1c2luZyBSZWFjdC5GcmFnbWVudGApO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIC8vIEEgUm9vdC1tb2R1bGUgbG9hZCBmYWlsdXJlIG11c3Qgbm90IGJlIHNpbGVudGx5IHN3YWxsb3dlZCDigJQgdGhhdFxuICAgICAgICAgICAgLy8gaGlkZXMgcmVhbCBidWdzIGJlaGluZCB0aGUgRGVmYXVsdFJvb3QgZmFsbGJhY2suIExvZ1xuICAgICAgICAgICAgLy8gdW5jb25kaXRpb25hbGx5IGFuZCBob25vciBwYW5pY1RocmVzaG9sZCBsaWtlIHNpYmxpbmcgZXJyb3IgcGF0aHMuXG4gICAgICAgICAgICBjb25zdCBwYW5pY0Vycm9yID0gaGFuZGxlRXJyb3Ioe1xuICAgICAgICAgICAgICBlcnJvcixcbiAgICAgICAgICAgICAgbG9nZ2VyLFxuICAgICAgICAgICAgICBwYW5pY1RocmVzaG9sZDogdXNlckhhbmRsZXJPcHRpb25zLnBhbmljVGhyZXNob2xkLFxuICAgICAgICAgICAgICBjcml0aWNhbDogdHJ1ZSxcbiAgICAgICAgICAgICAgY29udGV4dDogYGNvbmZpZ3VyZVJlYWN0U2VydmVyOiBsb2FkIFJvb3QgZnJvbSAke3Jvb3RQYXRofWAsXG4gICAgICAgICAgICAgIGxvZzogdHJ1ZSxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgaWYgKHBhbmljRXJyb3IgIT0gbnVsbCkge1xuICAgICAgICAgICAgICByZXR1cm4gbmV4dChwYW5pY0Vycm9yKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBMb2FkIHRoZSBwYWdlIGNvbXBvbmVudCAocmVnaXN0ZXJzIGl0IGluIG1vZHVsZSBncmFwaCBmb3IgQ1NTIGNvbGxlY3Rpb24pXG4gICAgICAgIGlmIChwYWdlUGF0aCkge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCBwYWdlRXhwb3J0TmFtZSA9IHVzZXJIYW5kbGVyT3B0aW9ucy5wYWdlRXhwb3J0TmFtZSB8fCBcIlBhZ2VcIjtcbiAgICAgICAgICAgIGNvbnN0IHBhZ2VNb2R1bGUgPSBhd2FpdCBsb2FkZXIocGFnZVBhdGgpO1xuICAgICAgICAgICAgaWYgKHBhZ2VNb2R1bGUgJiYgcGFnZU1vZHVsZVtwYWdlRXhwb3J0TmFtZV0gJiYgdHlwZW9mIHBhZ2VNb2R1bGVbcGFnZUV4cG9ydE5hbWVdID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICAgIFBhZ2VDb21wb25lbnQgPSBwYWdlTW9kdWxlW3BhZ2VFeHBvcnROYW1lXSBhcyBSZWFjdC5Db21wb25lbnRUeXBlPGFueT47XG4gICAgICAgICAgICAgIGlmICh2ZXJib3NlKSB7XG4gICAgICAgICAgICAgICAgbG9nZ2VyLmluZm8oYExvYWRlZCBQYWdlIGNvbXBvbmVudCBmb3Igcm91dGUgJHtpbmZvLnJvdXRlfSBmcm9tICR7cGFnZVBhdGh9IyR7cGFnZUV4cG9ydE5hbWV9YCk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAocGFnZU1vZHVsZSAmJiBwYWdlTW9kdWxlWydkZWZhdWx0J10gJiYgdHlwZW9mIHBhZ2VNb2R1bGVbJ2RlZmF1bHQnXSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgICBQYWdlQ29tcG9uZW50ID0gcGFnZU1vZHVsZVsnZGVmYXVsdCddIGFzIFJlYWN0LkNvbXBvbmVudFR5cGU8YW55PjtcbiAgICAgICAgICAgICAgaWYgKHZlcmJvc2UpIHtcbiAgICAgICAgICAgICAgICBsb2dnZXIuaW5mbyhgTG9hZGVkIGRlZmF1bHQgZXhwb3J0IGFzIFBhZ2UgY29tcG9uZW50IGZvciByb3V0ZSAke2luZm8ucm91dGV9YCk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAocGFnZU1vZHVsZSAmJiB0eXBlb2YgcGFnZU1vZHVsZSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgICBQYWdlQ29tcG9uZW50ID0gcGFnZU1vZHVsZSBhcyBSZWFjdC5Db21wb25lbnRUeXBlPGFueT47XG4gICAgICAgICAgICAgIGlmICh2ZXJib3NlKSB7XG4gICAgICAgICAgICAgICAgbG9nZ2VyLmluZm8oYExvYWRlZCBtb2R1bGUgYXMgUGFnZSBjb21wb25lbnQgZm9yIHJvdXRlICR7aW5mby5yb3V0ZX1gKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgaWYgKHZlcmJvc2UpIHtcbiAgICAgICAgICAgICAgICBsb2dnZXIud2FybihgUGFnZSBjb21wb25lbnQgbm90IGZvdW5kIGluICR7cGFnZVBhdGh9LCB1c2luZyBSZWFjdC5GcmFnbWVudGApO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIC8vIEEgcGFnZS1tb2R1bGUgbG9hZCBmYWlsdXJlIHdhcyBwcmV2aW91c2x5IHN3YWxsb3dlZCB1bmRlclxuICAgICAgICAgICAgLy8gIXZlcmJvc2UsIGxlYXZpbmcgdGhlIHJvdXRlIHRvIHJlbmRlciBhcyBhIGJsYW5rIEZyYWdtZW50IHdpdGggbm9cbiAgICAgICAgICAgIC8vIGVycm9yIHN1cmZhY2VkIGFueXdoZXJlLiBMb2cgdW5jb25kaXRpb25hbGx5IGFuZCBob25vclxuICAgICAgICAgICAgLy8gcGFuaWNUaHJlc2hvbGQgbGlrZSBzaWJsaW5nIGVycm9yIHBhdGhzLlxuICAgICAgICAgICAgY29uc3QgcGFuaWNFcnJvciA9IGhhbmRsZUVycm9yKHtcbiAgICAgICAgICAgICAgZXJyb3IsXG4gICAgICAgICAgICAgIGxvZ2dlcixcbiAgICAgICAgICAgICAgcGFuaWNUaHJlc2hvbGQ6IHVzZXJIYW5kbGVyT3B0aW9ucy5wYW5pY1RocmVzaG9sZCxcbiAgICAgICAgICAgICAgY3JpdGljYWw6IHRydWUsXG4gICAgICAgICAgICAgIGNvbnRleHQ6IGBjb25maWd1cmVSZWFjdFNlcnZlcjogbG9hZCBQYWdlIGZyb20gJHtwYWdlUGF0aH1gLFxuICAgICAgICAgICAgICBsb2c6IHRydWUsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIGlmIChwYW5pY0Vycm9yICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIG5leHQocGFuaWNFcnJvcik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gTk9XIGNvbGxlY3QgQ1NTIC0gcGFnZSBpcyByZWdpc3RlcmVkIGluIG1vZHVsZSBncmFwaFxuICAgICAgICBpZiAodmVyYm9zZSkge1xuICAgICAgICAgIGxvZ2dlci5pbmZvKGBDb2xsZWN0aW5nIENTUyBmaWxlcyBmb3Igcm91dGU6ICR7aW5mby5yb3V0ZX1gKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHNlcnZlckVudiA9IHNlcnZlci5lbnZpcm9ubWVudHNbJ3NlcnZlciddO1xuICAgICAgICBjb25zdCBtb2R1bGVHcmFwaEZvckNzcyA9IHNlcnZlckVudj8ubW9kdWxlR3JhcGggPz8gc2VydmVyLm1vZHVsZUdyYXBoO1xuICAgICAgICBcbiAgICAgICAgY29uc3QgY3NzRmlsZXNSZXN1bHQgPSBhd2FpdCBjb2xsZWN0Vml0ZU1vZHVsZUdyYXBoQ3NzKHtcbiAgICAgICAgICBtb2R1bGVHcmFwaDogbW9kdWxlR3JhcGhGb3JDc3MsXG4gICAgICAgICAgcGFyZW50VXJsOiBwYWdlUGF0aCxcbiAgICAgICAgICBoYW5kbGVyT3B0aW9uczogaGFuZGxlck9wdGlvbnMsXG4gICAgICAgIH0pO1xuXG4gICAgICAgIGlmICh2ZXJib3NlKSB7XG4gICAgICAgICAgbG9nZ2VyLmluZm8oYENTUyBjb2xsZWN0aW9uIGNvbXBsZXRlZCBmb3Igcm91dGU6ICR7aW5mby5yb3V0ZX1gKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChjc3NGaWxlc1Jlc3VsdC50eXBlID09PSBcInNraXBcIikge1xuICAgICAgICAgIGlmICh2ZXJib3NlKSB7XG4gICAgICAgICAgICBsb2dnZXIuaW5mbyhgQ1NTIGNvbGxlY3Rpb24gc2tpcHBlZCBmb3Igcm91dGU6ICR7aW5mby5yb3V0ZX0sIGNvbnRpbnVpbmcgd2l0aCBSU0MgcmVuZGVyaW5nYCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmIChjc3NGaWxlc1Jlc3VsdC50eXBlID09PSBcImVycm9yXCIpIHtcbiAgICAgICAgICByZXR1cm4gbmV4dChjc3NGaWxlc1Jlc3VsdC5lcnJvcik7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBjb2xsZWN0ZWRDc3NGaWxlcyA9IGNzc0ZpbGVzUmVzdWx0LnR5cGUgPT09IFwic3VjY2Vzc1wiID8gY3NzRmlsZXNSZXN1bHQuY3NzRmlsZXMgOiBuZXcgTWFwKCk7XG5cbiAgICAgICAgaWYgKHZlcmJvc2UpIHtcbiAgICAgICAgICBsb2dnZXIuaW5mbyhgQ3JlYXRpbmcgUlNDIGhhbmRsZXIgZm9yIHJvdXRlOiAke2luZm8ucm91dGV9YCk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBMb2FkIHByb3BzIHVzaW5nIHRoZSByZXNvbHZlUGFnZUFuZFByb3BzIGhlbHBlclxuICAgICAgICB0cnkge1xuICAgICAgICAgIGlmICh2ZXJib3NlKSB7XG4gICAgICAgICAgICBsb2dnZXIuaW5mbyhgW2NvbmZpZ3VyZVJlYWN0U2VydmVyXSBMb2FkaW5nIHByb3BzIGZvciByb3V0ZSAke2luZm8ucm91dGV9LCBwYWdlUGF0aDogJHtwYWdlUGF0aH0sIHByb3BzUGF0aDogJHtwcm9wc1BhdGh9LCB1cmw6ICR7aW5mby51cmx9YCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIFxuICAgICAgICAgIGNvbnN0IHByb3BzUmVzdWx0ID0gYXdhaXQgcmVzb2x2ZVBhZ2VBbmRQcm9wcyh7XG4gICAgICAgICAgICBwYWdlUGF0aCxcbiAgICAgICAgICAgIHByb3BzUGF0aCxcbiAgICAgICAgICAgIHBhZ2VFeHBvcnROYW1lOiB1c2VySGFuZGxlck9wdGlvbnMucGFnZUV4cG9ydE5hbWUsXG4gICAgICAgICAgICBwcm9wc0V4cG9ydE5hbWU6IHVzZXJIYW5kbGVyT3B0aW9ucy5wcm9wc0V4cG9ydE5hbWUsXG4gICAgICAgICAgICB1cmw6IGluZm8udXJsLFxuICAgICAgICAgICAgcm91dGU6IGluZm8ucm91dGUsXG4gICAgICAgICAgICBtb2R1bGVCYXNlVVJMOiBzZXJ2ZXIuY29uZmlnLmJhc2UsXG4gICAgICAgICAgICBsb2FkZXIsXG4gICAgICAgICAgICB2ZXJib3NlLFxuICAgICAgICAgICAgbG9nZ2VyLFxuICAgICAgICAgICAgYnVpbGQ6IHtcbiAgICAgICAgICAgICAgcnNjT3V0cHV0UGF0aDogdXNlckhhbmRsZXJPcHRpb25zLmJ1aWxkPy5yc2NPdXRwdXRQYXRoIHx8IFwiLnJzY1wiLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9KTtcblxuICAgICAgICAgIGlmICh2ZXJib3NlKSB7XG4gICAgICAgICAgICBsb2dnZXIuaW5mbyhgW2NvbmZpZ3VyZVJlYWN0U2VydmVyXSBQcm9wcyByZXNvbHV0aW9uIHJlc3VsdCB0eXBlOiAke3Byb3BzUmVzdWx0LnR5cGV9YCk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKHByb3BzUmVzdWx0LnR5cGUgPT09IFwic3VjY2Vzc1wiKSB7XG4gICAgICAgICAgICBwYWdlUHJvcHMgPSBwcm9wc1Jlc3VsdC5wYWdlUHJvcHMgfHwge307XG4gICAgICAgICAgICBpZiAodmVyYm9zZSkge1xuICAgICAgICAgICAgICBsb2dnZXIuaW5mbyhgW2NvbmZpZ3VyZVJlYWN0U2VydmVyXSBMb2FkZWQgcHJvcHMgZm9yIHJvdXRlICR7aW5mby5yb3V0ZX06ICR7SlNPTi5zdHJpbmdpZnkocGFnZVByb3BzLCBudWxsLCAyKX1gKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2UgaWYgKHByb3BzUmVzdWx0LnR5cGUgPT09IFwic2tpcFwiKSB7XG4gICAgICAgICAgICBpZiAodmVyYm9zZSkge1xuICAgICAgICAgICAgICBsb2dnZXIuaW5mbyhgW2NvbmZpZ3VyZVJlYWN0U2VydmVyXSBQcm9wcyByZXNvbHV0aW9uIHNraXBwZWQgZm9yIHJvdXRlICR7aW5mby5yb3V0ZX0sIHVzaW5nIGVtcHR5IHByb3BzYCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBwYWdlUHJvcHMgPSB7fTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKHZlcmJvc2UpIHtcbiAgICAgICAgICAgICAgbG9nZ2VyLndhcm4oYFtjb25maWd1cmVSZWFjdFNlcnZlcl0gRmFpbGVkIHRvIGxvYWQgcHJvcHMgZm9yIHJvdXRlICR7aW5mby5yb3V0ZX06ICR7cHJvcHNSZXN1bHQuZXJyb3J9YCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBwYWdlUHJvcHMgPSB7fTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgaWYgKHZlcmJvc2UpIHtcbiAgICAgICAgICAgIGxvZ2dlci53YXJuKGBbY29uZmlndXJlUmVhY3RTZXJ2ZXJdIEVycm9yIGxvYWRpbmcgcHJvcHMgZm9yIHJvdXRlICR7aW5mby5yb3V0ZX06ICR7ZXJyb3J9YCk7XG4gICAgICAgICAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBFcnJvcikge1xuICAgICAgICAgICAgICBsb2dnZXIud2FybihgW2NvbmZpZ3VyZVJlYWN0U2VydmVyXSBFcnJvciBzdGFjazogJHtlcnJvci5zdGFja31gKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgLy8gQ29udGludWUgd2l0aCBlbXB0eSBwcm9wcyBpZiBsb2FkaW5nIGZhaWxzXG4gICAgICAgICAgcGFnZVByb3BzID0ge307XG4gICAgICAgIH1cblxuICAgICAgICAvLyBERVYgTU9ERSBPUFRJTUlaQVRJT046IFNraXAgd29ya2VyLCB1c2UgZGlyZWN0IHJlbmRlcmluZyBvbiBtYWluIHRocmVhZFxuICAgICAgICAvLyBcbiAgICAgICAgLy8gV2h5OiBJbiBkZXY6cnNjIG1vZGUsIHRoZSBtYWluIHRocmVhZCBhbHJlYWR5IGhhcyByZWFjdC1zZXJ2ZXIgY29uZGl0aW9uIGFuZCBcbiAgICAgICAgLy8gbG9hZHMgbW9kdWxlcyB2aWEgVml0ZSdzIGVudmlyb25tZW50IHJ1bm5lciB3aGljaCBoYW5kbGVzIEhNUiBhdXRvbWF0aWNhbGx5LlxuICAgICAgICAvLyBUaGUgd29ya2VyIHVzZXMgcmF3IGltcG9ydCgpIHdoaWNoIGJ5cGFzc2VzIFZpdGUncyBtb2R1bGUgZ3JhcGggYW5kIEhNUi5cbiAgICAgICAgLy9cbiAgICAgICAgLy8gQmVuZWZpdHMgb2YgZGlyZWN0IHJlbmRlcmluZyBpbiBkZXY6XG4gICAgICAgIC8vIC0gUHJvcGVyIEhNUjogZmlsZSBjaGFuZ2VzIGFyZSBwaWNrZWQgdXAgaW1tZWRpYXRlbHkgdmlhIFZpdGUncyBtb2R1bGUgZ3JhcGhcbiAgICAgICAgLy8gLSBObyBtb2R1bGUgY2FjaGluZyBpc3N1ZXM6IGVudmlyb25tZW50IHJ1bm5lciBoYW5kbGVzIGNhY2hlIGludmFsaWRhdGlvblxuICAgICAgICAvLyAtIFNpbXBsZXIgZGVidWdnaW5nOiBhbGwgY29kZSBydW5zIGluIG1haW4gdGhyZWFkXG4gICAgICAgIC8vXG4gICAgICAgIC8vIFRoZSB3b3JrZXIgaXMgc3RpbGwgdmFsdWFibGUgZm9yOlxuICAgICAgICAvLyAtIFByb2R1Y3Rpb24gYnVpbGRzIChpc29sYXRpb24sIGNvbnNpc3RlbnQgYmVoYXZpb3IpXG4gICAgICAgIC8vIC0gRnV0dXJlOiBSdW5uaW5nIFJTQyBpbiBkaWZmZXJlbnQgcnVudGltZXMgKHdvcmtlcmQsIGV0Yy4pXG4gICAgICAgIC8vXG4gICAgICAgIC8vIFVzZXJzIGNhbiBvcHQtaW4gdG8gd29ya2VyIGluIGRldiB2aWEgY29uZmlnIGlmIG5lZWRlZCBmb3IgdGVzdGluZyBwcm9kdWN0aW9uIGJlaGF2aW9yLlxuICAgICAgICBjb25zdCB1c2VXb3JrZXJJbkRldiA9IF91c2VyT3B0aW9ucy5kZXY/LnVzZVJzY1dvcmtlciA9PT0gdHJ1ZTtcbiAgICAgICAgXG4gICAgICAgIGlmICh1c2VXb3JrZXJJbkRldikge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBpZiAodmVyYm9zZSkge1xuICAgICAgICAgICAgICBsb2dnZXIuaW5mbyhgQ3JlYXRpbmcgUlNDIHdvcmtlciBmb3Igcm91dGU6ICR7aW5mby5yb3V0ZX0gKHVzZVJzY1dvcmtlcj10cnVlKWApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgXG4gICAgICAgICAgICBjb25zdCB3b3JrZXJSZXN1bHQgPSBhd2FpdCBjcmVhdGVXb3JrZXIoe1xuICAgICAgICAgICAgICBwcm9qZWN0Um9vdDogX3VzZXJPcHRpb25zLnByb2plY3RSb290IHx8IHNlcnZlci5jb25maWcucm9vdCxcbiAgICAgICAgICAgICAgd29ya2VyRGF0YToge1xuICAgICAgICAgICAgICAgIHVzZXJPcHRpb25zOiBzZXJpYWxpemVkT3B0aW9ucyhfdXNlck9wdGlvbnMsIGF1dG9EaXNjb3ZlcmVkRmlsZXMpLFxuICAgICAgICAgICAgICAgIHJlc29sdmVkQ29uZmlnOiBzZXJ2ZXIuY29uZmlnLFxuICAgICAgICAgICAgICAgIGNvbmZpZ0VudjogeyBjb21tYW5kOiBcInNlcnZlXCIsIG1vZGU6IFwiZGV2ZWxvcG1lbnRcIiB9LFxuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICB2ZXJib3NlLFxuICAgICAgICAgICAgICBsb2dnZXIsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgaWYgKHdvcmtlclJlc3VsdC50eXBlID09PSBcInN1Y2Nlc3NcIikge1xuICAgICAgICAgICAgICByc2NXb3JrZXIgPSB3b3JrZXJSZXN1bHQud29ya2VyO1xuICAgICAgICAgICAgICBpZiAodmVyYm9zZSkge1xuICAgICAgICAgICAgICAgIGxvZ2dlci5pbmZvKGBSU0Mgd29ya2VyIGNyZWF0ZWQgc3VjY2Vzc2Z1bGx5IGZvciByb3V0ZTogJHtpbmZvLnJvdXRlfWApO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBpZiAodmVyYm9zZSkge1xuICAgICAgICAgICAgICAgIGxvZ2dlci53YXJuKGBSU0Mgd29ya2VyIGNyZWF0aW9uIHNraXBwZWQgZm9yIHJvdXRlOiAke2luZm8ucm91dGV9OiAke3dvcmtlclJlc3VsdC5yZWFzb259YCk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgaWYgKHZlcmJvc2UpIHtcbiAgICAgICAgICAgICAgbG9nZ2VyLndhcm4oYEZhaWxlZCB0byBjcmVhdGUgUlNDIHdvcmtlciBmb3Igcm91dGU6ICR7aW5mby5yb3V0ZX06ICR7ZXJyb3J9YCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGlmICh2ZXJib3NlKSB7XG4gICAgICAgICAgICBsb2dnZXIuaW5mbyhgW2Rldjpyc2NdIFVzaW5nIGRpcmVjdCByZW5kZXJpbmcgKG5vIHdvcmtlcikgZm9yIHByb3BlciBITVIgc3VwcG9ydGApO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFVzZSB3b3JrZXItYmFzZWQgUlNDIHN0cmVhbSBpZiB3b3JrZXIgaXMgYXZhaWxhYmxlLCBvdGhlcndpc2UgZmFsbCBiYWNrIHRvIGRpcmVjdCByZW5kZXJpbmdcbiAgICAgICAgLy8gQ1JJVElDQUw6IEZvciBSU0MgcmVxdWVzdHMsIHVzZSBodG1sUGF0aDogXCJcIiBmb3IgaGVhZGxlc3MgbW9kZSAobm8gSHRtbCB3cmFwcGVyKVxuICAgICAgICAvLyBUaGlzIHByZXZlbnRzIGh5ZHJhdGlvbiBlcnJvcnMgd2hlcmUgPGh0bWw+IHdvdWxkIGJlIHJlbmRlcmVkIGluc2lkZSAjcm9vdCBkaXZcbiAgICAgICAgY29uc3QgcnNjUmVzdWx0ID0gcnNjV29ya2VyIFxuICAgICAgICAgID8gY3JlYXRlUnNjU3RyZWFtKHtcbiAgICAgICAgICAgICAgLi4uaGFuZGxlck9wdGlvbnMsXG4gICAgICAgICAgICAgIHVybDogaW5mby51cmwsXG4gICAgICAgICAgICAgIHBhZ2VQYXRoLFxuICAgICAgICAgICAgICBwcm9wc1BhdGgsXG4gICAgICAgICAgICAgIHJvb3RQYXRoLCAgLy8gUGFzcyB0aGUgcm9vdCBwYXRoIGZvciB3b3JrZXIgdG8gbG9hZFxuICAgICAgICAgICAgICBodG1sUGF0aDogXCJcIiwgIC8vIEVtcHR5IHN0cmluZyA9IGhlYWRsZXNzIFJTQyAobm8gSHRtbCB3cmFwcGVyKVxuICAgICAgICAgICAgICByc2NXb3JrZXIsXG4gICAgICAgICAgICAgIGNzc0ZpbGVzOiBjb2xsZWN0ZWRDc3NGaWxlcyxcbiAgICAgICAgICAgICAgZ2xvYmFsQ3NzOiBuZXcgTWFwKCksXG4gICAgICAgICAgICB9KVxuICAgICAgICAgIDogY3JlYXRlUmVuZGVyVG9QaXBlYWJsZVN0cmVhbUhhbmRsZXIoe1xuICAgICAgICAgICAgICAuLi5oYW5kbGVyT3B0aW9ucyxcbiAgICAgICAgICAgICAgdXJsOiBpbmZvLnVybCxcbiAgICAgICAgICAgICAgUGFnZUNvbXBvbmVudDogUGFnZUNvbXBvbmVudCBhcyBhbnksXG4gICAgICAgICAgICAgIFJvb3RDb21wb25lbnQ6IFJvb3RDb21wb25lbnQgYXMgYW55LFxuICAgICAgICAgICAgICBIdG1sQ29tcG9uZW50OiBSZWFjdC5GcmFnbWVudCwgIC8vIEhlYWRsZXNzIHN0cmVhbSAtIG5vIEh0bWwgd3JhcHBlclxuICAgICAgICAgICAgICBwYWdlUHJvcHM6IHBhZ2VQcm9wcywgIC8vIFBhc3MgdGhlIGxvYWRlZCBwcm9wc1xuICAgICAgICAgICAgICBjc3NGaWxlczogY29sbGVjdGVkQ3NzRmlsZXMsXG4gICAgICAgICAgICB9KTtcblxuICAgICAgICBpZiAodmVyYm9zZSkge1xuICAgICAgICAgIGxvZ2dlci5pbmZvKFxuICAgICAgICAgICAgYFJTQyBoYW5kbGVyIGNyZWF0ZWQgZm9yIHJvdXRlOiAke1xuICAgICAgICAgICAgICBpbmZvLnJvdXRlXG4gICAgICAgICAgICB9LCByZXN1bHQgdHlwZTogJHt0eXBlb2YgcnNjUmVzdWx0fSwgaGFzIHBpcGU6ICR7dHlwZW9mIHJzY1Jlc3VsdD8ucGlwZX0sIGhhcyBhYm9ydDogJHt0eXBlb2YgcnNjUmVzdWx0Py5hYm9ydH1gXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChyc2NSZXN1bHQgJiYgdHlwZW9mIHJzY1Jlc3VsdC5waXBlID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgICBpZiAodmVyYm9zZSkge1xuICAgICAgICAgICAgbG9nZ2VyLmluZm8oYFNldHRpbmcgdXAgUlNDIHN0cmVhbSBmb3Igcm91dGU6ICR7aW5mby5yb3V0ZX1gKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBzZXQgaGVhZGVyc1xuICAgICAgICAgIHJlcy5zZXRIZWFkZXIoXCJDb250ZW50LVR5cGVcIiwgXCJ0ZXh0L3gtY29tcG9uZW50OyBjaGFyc2V0PXV0Zi04XCIpO1xuICAgICAgICAgIFxuICAgICAgICAgIC8vIENSSVRJQ0FMOiBEaXNhYmxlIGNhY2hpbmcgaW4gZGV2ZWxvcG1lbnQgbW9kZVxuICAgICAgICAgIC8vIFdpdGhvdXQgdGhpcywgYnJvd3NlcnMgY2FjaGUgUlNDIHN0cmVhbXMgYW5kIGRvbid0IHNob3cgdXBkYXRlc1xuICAgICAgICAgIHJlcy5zZXRIZWFkZXIoXCJDYWNoZS1Db250cm9sXCIsIFwibm8tY2FjaGUsIG5vLXN0b3JlLCBtdXN0LXJldmFsaWRhdGVcIik7XG4gICAgICAgICAgcmVzLnNldEhlYWRlcihcIlByYWdtYVwiLCBcIm5vLWNhY2hlXCIpO1xuICAgICAgICAgIHJlcy5zZXRIZWFkZXIoXCJFeHBpcmVzXCIsIFwiMFwiKTtcbiAgICAgICAgICBcbiAgICAgICAgICAvLyBBZGQgQ09SUyBoZWFkZXJzIGZvciBSU0MgZmlsZXNcbiAgICAgICAgICBjb25zdCBvcmlnaW4gPSByZXEuaGVhZGVycy5vcmlnaW47XG4gICAgICAgICAgaWYgKG9yaWdpbiAmJiAob3JpZ2luLmluY2x1ZGVzKCdsb2NhbGhvc3QnKSB8fCBvcmlnaW4uaW5jbHVkZXMoJzEyNy4wLjAuMScpKSkge1xuICAgICAgICAgICAgcmVzLnNldEhlYWRlcihcIkFjY2Vzcy1Db250cm9sLUFsbG93LU9yaWdpblwiLCBvcmlnaW4pO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXMuc2V0SGVhZGVyKFwiQWNjZXNzLUNvbnRyb2wtQWxsb3ctT3JpZ2luXCIsIFwiKlwiKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmVzLnNldEhlYWRlcihcIkFjY2Vzcy1Db250cm9sLUFsbG93LU1ldGhvZHNcIiwgXCJHRVQsIE9QVElPTlNcIik7XG4gICAgICAgICAgcmVzLnNldEhlYWRlcihcIkFjY2Vzcy1Db250cm9sLUFsbG93LUhlYWRlcnNcIiwgXCJBY2NlcHQsIENvbnRlbnQtVHlwZVwiKTtcbiAgICAgICAgICByZXMuc2V0SGVhZGVyKFwiQWNjZXNzLUNvbnRyb2wtTWF4LUFnZVwiLCBcIjg2NDAwXCIpO1xuICAgICAgICAgIHJzY1Jlc3VsdC5waXBlKHJlcyk7XG5cbiAgICAgICAgICAvLyBTdG9yZSB0aGUgY29udHJvbGxlciBmb3IgcG90ZW50aWFsIGFib3J0IGR1cmluZyByZXN0YXJ0XG4gICAgICAgICAgYWN0aXZlQ29udHJvbGxlcnMuc2V0KHJlcywgcnNjUmVzdWx0KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBpZiAodmVyYm9zZSkge1xuICAgICAgICAgICAgbG9nZ2VyLmVycm9yKFxuICAgICAgICAgICAgICBgUlNDIGhhbmRsZXIgZmFpbGVkIGZvciByb3V0ZTogJHtcbiAgICAgICAgICAgICAgICBpbmZvLnJvdXRlXG4gICAgICAgICAgICAgIH0sIGludmFsaWQgcmVzdWx0OiAke3R5cGVvZiByc2NSZXN1bHR9YFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgLy8gSGFuZGxlIHRoZSBlcnJvciBjYXNlXG4gICAgICAgICAgcmVzLnN0YXR1c0NvZGUgPSA1MDA7XG4gICAgICAgICAgcmVzLmVuZChcIkludGVybmFsIFNlcnZlciBFcnJvclwiKTtcbiAgICAgICAgfVxuICAgICAgICBhY3RpdmVTdHJlYW1zLmFkZChyZXMpO1xuICAgICAgICByZXMub24oXCJjbG9zZVwiLCAoKSA9PiB7XG4gICAgICAgICAgYWN0aXZlU3RyZWFtcy5kZWxldGUocmVzKTtcbiAgICAgICAgICBcbiAgICAgICAgICAvLyBBYm9ydCB0aGUgUlNDIHN0cmVhbSB0byBjbGVhbiB1cCBNZXNzYWdlUG9ydHNcbiAgICAgICAgICBjb25zdCBjb250cm9sbGVyID0gYWN0aXZlQ29udHJvbGxlcnMuZ2V0KHJlcyk7XG4gICAgICAgICAgaWYgKGNvbnRyb2xsZXIgJiYgdHlwZW9mIGNvbnRyb2xsZXIuYWJvcnQgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgY29udHJvbGxlci5hYm9ydCgpO1xuICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgLy8gSWdub3JlIGNsZWFudXAgZXJyb3JzXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGFjdGl2ZUNvbnRyb2xsZXJzLmRlbGV0ZShyZXMpO1xuICAgICAgICAgIFxuICAgICAgICAgIC8vIENsZWFuIHVwIHdvcmtlciB3aGVuIHJlcXVlc3QgY29tcGxldGVzXG4gICAgICAgICAgY2xlYW51cFdvcmtlcihyc2NXb3JrZXIpO1xuICAgICAgICB9KTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIC8vIEFsd2F5cyBsb2cgUlNDIHN0cmVhbSBlcnJvcnMgKHJlZ2FyZGxlc3Mgb2YgdmVyYm9zZSkgc28gYSBtaXNjb25maWd1cmVkXG4gICAgICAgIC8vIGFwcCBzdXJmYWNlcyB0aGUgdW5kZXJseWluZyBlcnJvciBpbiB0aGUgZGV2IHNlcnZlciBsb2cgd2l0aG91dFxuICAgICAgICAvLyBmb3JjaW5nIHRoZSB1c2VyIHRvIGZsaXAgYHZlcmJvc2U6IHRydWVgLlxuICAgICAgICBjb25zdCBwYW5pY0Vycm9yID0gaGFuZGxlRXJyb3Ioe1xuICAgICAgICAgIGVycm9yLFxuICAgICAgICAgIGxvZ2dlcixcbiAgICAgICAgICBwYW5pY1RocmVzaG9sZDogaGFuZGxlck9wdGlvbnMucGFuaWNUaHJlc2hvbGQsXG4gICAgICAgICAgY3JpdGljYWw6IGZhbHNlLFxuICAgICAgICAgIGNvbnRleHQ6IFwiY29uZmlndXJlUmVhY3RTZXJ2ZXJcIixcbiAgICAgICAgICBsb2c6IHRydWUsXG4gICAgICAgIH0pO1xuICAgICAgICBpZiAocGFuaWNFcnJvciAhPSBudWxsKSB7XG4gICAgICAgICAgcmV0dXJuIG5leHQocGFuaWNFcnJvcik7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBTdXJmYWNlIHRoZSBmYWlsdXJlIHRvIHRoZSBIVFRQIGNsaWVudC4gUHJldmlvdXNseSB3ZSBzZXQgc3RhdHVzIDUwMFxuICAgICAgICAvLyBidXQgbmV2ZXIgZW5kZWQgdGhlIHJlc3BvbnNlLCBzbyB0aGUgcmVxdWVzdCB3b3VsZCBoYW5nIGFuZCBhbnlcbiAgICAgICAgLy8gYWxyZWFkeS1mbHVzaGVkIGJ5dGVzIChub25lIHlldCwgc2luY2Ugd2Ugc2V0IGhlYWRlcnMgcmlnaHQgYmVmb3JlXG4gICAgICAgIC8vIHBpcGUoKSkgd291bGQgYmUgYWxsIHRoZSBjYWxsZXIgc2F3LiBFbWl0IGEgdGV4dC9wbGFpbiA1MDAgd2l0aCB0aGVcbiAgICAgICAgLy8gZXJyb3IgbWVzc2FnZSBzbyBkZXYgY2xpZW50cyAoY3VybCwgdGhlIFJlYWN0IGZldGNoZXIpIGdldCBhIGNsZWFyXG4gICAgICAgIC8vIGZhaWx1cmUgaW5zdGVhZCBvZiBhbiBlbXB0eSAyMDAgLyBoYW5naW5nIHNvY2tldC5cbiAgICAgICAgaWYgKCFyZXMuaGVhZGVyc1NlbnQpIHtcbiAgICAgICAgICByZXMuc3RhdHVzQ29kZSA9IDUwMDtcbiAgICAgICAgICByZXMuc2V0SGVhZGVyKFwiQ29udGVudC1UeXBlXCIsIFwidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOFwiKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIXJlcy53cml0YWJsZUVuZGVkKSB7XG4gICAgICAgICAgY29uc3QgbWVzc2FnZSA9IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKTtcbiAgICAgICAgICByZXMuZW5kKGBSU0MgcmVuZGVyIGZhaWxlZDogJHttZXNzYWdlfVxcbmApO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gTm90ZTogV29ya2VyIGNsZWFudXAgaXMgaGFuZGxlZCBieSB0aGUgcmVzcG9uc2UgY2xvc2UgaGFuZGxlclxuICAgICAgfVxuICAgIH0pO1xuICB9O1xuIl0sIm5hbWVzIjpbImhhbmRsZXJPcHRpb25zIiwiRGVmYXVsdFJvb3QiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBa0JhLE1BQUEsb0JBQUEsR0FDWCxTQUFTLHFCQUFzQixDQUFBO0FBQUEsRUFDN0IsTUFBQTtBQUFBLEVBQ0EsbUJBQUE7QUFBQSxFQUNBLFdBQWEsRUFBQSxZQUFBO0FBQUEsRUFDYixjQUFBO0FBQUEsRUFDQTtBQUNGLENBQUcsRUFBQTtBQUNELEVBQU0sTUFBQSxhQUFBLHVCQUFvQixHQUFvQixFQUFBO0FBQzlDLEVBQU0sTUFBQSxpQkFBQSx1QkFBd0IsR0FHNUIsRUFBQTtBQUNGLEVBQUEsSUFBSSxZQUFlLEdBQUEsS0FBQTtBQUduQixFQUFBLE1BQU0sTUFBUyxHQUFBLE1BQUEsQ0FBTyxNQUFPLENBQUEsWUFBQSxJQUFnQixPQUFPLE1BQU8sQ0FBQSxNQUFBO0FBQzNELEVBQU0sTUFBQTtBQUFBLElBQ0osSUFBTSxFQUFBLGtCQUFBO0FBQUE7QUFBQSxJQUVOLE1BQVEsRUFBQSxhQUFBO0FBQUEsSUFDUixPQUFBO0FBQUE7QUFBQSxJQUVBLEdBQUc7QUFBQSxHQUNELEdBQUEsWUFBQTtBQUVKLEVBQUEsTUFBQSxDQUFPLE1BQVMsR0FBQSxXQUFBO0FBQUEsSUFDZCxNQUFPLENBQUEsTUFBQTtBQUFBLElBQ1A7QUFBQSxHQUNGO0FBSUEsRUFBQSxNQUFBLENBQU8sRUFBRyxDQUFBLEVBQUEsQ0FBRyxTQUFXLEVBQUEsQ0FBQyxJQUFTLEtBQUE7QUFDaEMsSUFBTyxNQUFBLENBQUEsSUFBQTtBQUFBLE1BQ0wsc0VBQUE7QUFBQSxNQUNBO0FBQUEsS0FDRjtBQUVBLElBQWUsWUFBQSxHQUFBLElBQUE7QUFHZixJQUFBLEtBQUEsTUFBVyxPQUFPLGFBQWUsRUFBQTtBQUMvQixNQUFNLE1BQUEsVUFBQSxHQUFhLGlCQUFrQixDQUFBLEdBQUEsQ0FBSSxHQUFHLENBQUE7QUFDNUMsTUFBQSxJQUFJLFVBQVksRUFBQTtBQUNkLFFBQUksSUFBQTtBQUNGLFVBQUEsVUFBQSxDQUFXLE1BQU0sbUJBQW1CLENBQUE7QUFBQSxpQkFDN0IsQ0FBRyxFQUFBO0FBQUE7QUFFWjtBQUdGLE1BQUEsR0FBQSxDQUFJLFVBQVUsR0FBSyxFQUFBO0FBQUEsUUFDakIsY0FBZ0IsRUFBQSxpQ0FBQTtBQUFBLFFBQ2hCLGFBQWUsRUFBQTtBQUFBLE9BQ2hCLENBQUE7QUFDRCxNQUFJLEdBQUEsQ0FBQSxHQUFBO0FBQUEsUUFDRixDQUFBLDBGQUFBO0FBQUEsT0FDRjtBQUFBO0FBRUYsSUFBQSxhQUFBLENBQWMsS0FBTSxFQUFBO0