UNPKG

vite-plugin-react-server

Version:
466 lines (464 loc) 66.1 kB
/** * 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