UNPKG

vite-plugin-react-server

Version:
567 lines (565 loc) 87.3 kB
/** * vite-plugin-react-server * Copyright (c) Nico Brinkkemper * MIT License */ import { handleError } from '../error/handleError.js'; import { shouldCausePanic } from '../error/panicThresholdHandler.js'; import { fileWriter } from './fileWriter.js'; import { createRenderMetrics } from '../metrics/createRenderMetrics.js'; import { createStreamMetrics } from '../metrics/createStreamMetrics.js'; function resolvePathWithManifest(path, manifest) { const entry = manifest[path]; if (entry && entry.file) { return entry.file; } return path; } const renderPages = (routes, handlerOptions, renderPage) => { const { autoDiscoveredFiles, cssFilesByPage, manifest = {}, ...options } = handlerOptions; const completedRoutes = /* @__PURE__ */ new Set(); const failedRoutes = /* @__PURE__ */ new Map(); const activeStreams = /* @__PURE__ */ new Map(); const results = /* @__PURE__ */ new Map(); if (!autoDiscoveredFiles.urlMap) { return async function* _renderPages() { yield { type: "error", error: new Error("No urlMap provided to renderPages"), route: "", failedRoutes: /* @__PURE__ */ new Map(), completedRoutes: /* @__PURE__ */ new Set(), results: /* @__PURE__ */ new Map() }; return { type: "error", error: new Error("No urlMap provided to renderPages"), route: "", failedRoutes: /* @__PURE__ */ new Map(), completedRoutes: /* @__PURE__ */ new Set(), results: /* @__PURE__ */ new Map() }; }(); } return async function* _renderPages() { for (const route of routes) { if (options.signal?.aborted) { yield { type: "error", error: options.signal.reason || new Error("Build aborted"), route, failedRoutes, completedRoutes, results }; return { type: "error", error: options.signal.reason || new Error("Build aborted"), route, failedRoutes, completedRoutes, results }; } const { page, props, root, html } = autoDiscoveredFiles.urlMap.get(route) || {}; if (!page) continue; try { const resolvedPagePath = page ? resolvePathWithManifest(page, manifest) : void 0; const resolvedPropsPath = props ? resolvePathWithManifest(props, manifest) : void 0; const resolvedRootPath = root ? resolvePathWithManifest(root, manifest) : void 0; const resolvedHtmlPath = html ? resolvePathWithManifest(html, manifest) : void 0; if (options.verbose) { options.logger?.info( `[renderPages] Resolved paths for route ${route}:` ); options.logger?.info(` page: ${page} -> ${resolvedPagePath}`); options.logger?.info(` props: ${props} -> ${resolvedPropsPath}`); options.logger?.info(` root: ${root} -> ${resolvedRootPath}`); options.logger?.info(` html: ${html} -> ${resolvedHtmlPath}`); } if (options.verbose) { options.logger?.info( `[renderPages] Global CSS: ${options.globalCss?.size} files` ); for (const [key, value] of options.globalCss?.entries() ?? []) { options.logger?.info( `[renderPages] Global CSS: ${key} -> ${value.as} (${value.children ? "inline" : "link"})` ); } options.logger?.info( `[renderPages] CSS files: ${cssFilesByPage.get(route)?.size} files` ); for (const [key, value] of cssFilesByPage.get(route)?.entries() ?? []) { options.logger?.info( `[renderPages] CSS file: ${key} -> ${value.as} (${value.children ? "inline" : "link"})` ); } } const renderPageWrapperOnEvent = (event) => { if (options.onEvent) { options.onEvent(event); } if (event.type === "route.error") { const detectedPanicError = handleError({ error: event.data.error, logger: options.logger, panicThreshold: event.data.panicThreshold, context: `route.error (${event.data.route})` }); if (detectedPanicError != null) { options.logger?.error( `[renderPages] Panic error for route ${event.data.route}: ${event.data.error.message}` ); failedRoutes.set(event.data.route, event.data.error); } else { options.logger?.warn( `[renderPages] Non-panic error for route ${event.data.route}: ${event.data.error.message}` ); } } }; const routeHandlerOptions = { ...options, manifest, route, pagePath: resolvedPagePath, propsPath: resolvedPropsPath, rootPath: resolvedRootPath, htmlPath: resolvedHtmlPath, cssFiles: cssFilesByPage.get(route) ?? /* @__PURE__ */ new Map(), // Ensure global CSS is available to Html component globalCss: options.globalCss ?? /* @__PURE__ */ new Map(), // Generate unique ID for this route id: `${route}-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`, // Override onEvent to use our wrapper onEvent: renderPageWrapperOnEvent }; const pageRenderer = renderPage(routeHandlerOptions); if (options.verbose) { options.logger?.info( `[renderPages] Starting to process route: ${route}` ); } for await (const result of pageRenderer) { if (options.verbose) { options.logger?.info( `[renderPages] Received result for route ${route}: ${result.type}` ); } if (result.type === "skip") { if (options.verbose) { options.logger?.info( `[renderPages] Skipping RSC for route ${route} due to error: ${result.reason}` ); } failedRoutes.set(route, result.reason); results.set(route, { type: "success", html: result.html, rsc: result.rsc, metrics: result.metrics }); try { const wrapperOnEvent = (event) => { if (options.onEvent) { options.onEvent(event); } if (event.type === "file.write.done" && event.data.route === route && event.data.fileType === "html") { const routeResult = results.get(route); if (routeResult && routeResult.type === "success") { const endTime = performance.now(); const htmlMetrics = createRenderMetrics({ route, type: routeResult.metrics.html.type, fromMainThread: routeResult.metrics.html.fromMainThread, fromRscWorker: routeResult.metrics.html.fromRscWorker, fromHtmlWorker: routeResult.metrics.html.fromHtmlWorker, fileSize: event.data.content.length, chunks: event.data.chunks || 0, processingTime: endTime - routeResult.metrics.html.streamMetrics.startTime, chunkRate: (event.data.chunks || 0) / ((endTime - routeResult.metrics.html.streamMetrics.startTime) / 1e3), fileName: event.data.fileName, outputPath: event.data.path, baseDir: event.data.baseDir, routePath: event.data.routePath, streamMetrics: createStreamMetrics({ ...routeResult.metrics.html.streamMetrics, chunks: event.data.chunks || 0, bytes: event.data.content.length, duration: endTime - routeResult.metrics.html.streamMetrics.startTime, endTime }) }); if (options.onMetrics) { options.onMetrics(htmlMetrics); } } } }; const rscWritePromise = fileWriter( result.rsc, "rsc", { ...options, route, onEvent: wrapperOnEvent, logger: options.logger }, options.signal ); const htmlWritePromise = fileWriter( result.html, "html", { ...options, route, onEvent: wrapperOnEvent, logger: options.logger }, options.signal ); await Promise.all([rscWritePromise, htmlWritePromise]); completedRoutes.add(route); if (options.verbose) { options.logger?.info( `[renderPages] Wrote HTML-only file for skipped route: ${route}` ); } } catch (writeError) { if (options.verbose) { options.logger?.error( `[renderPages] Failed to write HTML for skipped route ${route}: ${writeError}` ); } completedRoutes.delete(route); } continue; } if (result.type === "error") { if (options.verbose) { options.logger?.error( `[renderPages] Error for route ${route}: ${result.error}` ); } failedRoutes.set(route, result.error); yield { type: "error", error: result.error, route, failedRoutes, completedRoutes, results }; continue; } if (result.type === "success") { if (options.verbose) { options.logger?.info( `[renderPages] Success for route ${route}, starting file writes` ); } completedRoutes.add(route); results.set(route, { type: "success", html: result.html, rsc: result.rsc, metrics: result.metrics }); activeStreams.set(route, { html: result.html, rsc: result.rsc }); try { const wrapperOnEvent = (event) => { try { if (options.onEvent) { options.onEvent(event); } } catch (error) { options.logger?.error(`[renderPages] onEvent handler threw error: ${error.message}`); for (const [, streams] of activeStreams) { try { streams.html.abort("Build cancelled"); streams.rsc.abort("Build cancelled"); } catch (abortError) { } } if (options.rscWorker) { try { options.rscWorker.postMessage({ type: "SHUTDOWN", id: "*" }); } catch (shutdownError) { } } throw error; } if (event.type === "route.error") { const detectedPanicError = handleError({ error: event.data.error, logger: options.logger, panicThreshold: event.data.panicThreshold, context: `route.error (${event.data.route})` }); if (detectedPanicError != null) { options.logger?.error( `[renderPages] Panic error for route ${event.data.route}: ${event.data.error.message}` ); failedRoutes.set(event.data.route, event.data.error); } else { options.logger?.warn( `[renderPages] Non-panic error for route ${event.data.route}: ${event.data.error.message}` ); } } if (event.type === "file.write.done" && event.data.route === route) { const routeResult = results.get(route); if (routeResult && routeResult.type === "success") { if (event.data.fileType === "html") { const endTime = performance.now(); const htmlMetrics = createRenderMetrics({ route, type: routeResult.metrics.html.type, fromMainThread: routeResult.metrics.html.fromMainThread, fromRscWorker: routeResult.metrics.html.fromRscWorker, fromHtmlWorker: routeResult.metrics.html.fromHtmlWorker, fileSize: event.data.content.length, chunks: event.data.chunks || 0, processingTime: endTime - routeResult.metrics.html.streamMetrics.startTime, chunkRate: (event.data.chunks || 0) / ((endTime - routeResult.metrics.html.streamMetrics.startTime) / 1e3), fileName: event.data.fileName, outputPath: event.data.path, baseDir: event.data.baseDir, routePath: event.data.routePath, streamMetrics: createStreamMetrics({ ...routeResult.metrics.html.streamMetrics, chunks: event.data.chunks || 0, bytes: event.data.content.length, duration: endTime - routeResult.metrics.html.streamMetrics.startTime, endTime }) }); if (options.onMetrics) { options.onMetrics(htmlMetrics); } if (routeResult.metrics?.rscFull) { const rscFullEndTime = performance.now(); const rscFullMetrics = createRenderMetrics({ route, type: routeResult.metrics.rscFull.type, fromMainThread: routeResult.metrics.rscFull.fromMainThread, fromRscWorker: routeResult.metrics.rscFull.fromRscWorker, fromHtmlWorker: routeResult.metrics.rscFull.fromHtmlWorker, processingTime: rscFullEndTime - routeResult.metrics.rscFull.streamMetrics.startTime, chunks: routeResult.metrics.rscFull.streamMetrics.chunks, chunkRate: routeResult.metrics.rscFull.streamMetrics.chunks / ((rscFullEndTime - routeResult.metrics.rscFull.streamMetrics.startTime) / 1e3), fileName: event.data.fileName, outputPath: event.data.path, baseDir: event.data.baseDir, routePath: event.data.routePath, streamMetrics: createStreamMetrics({ ...routeResult.metrics.rscFull.streamMetrics, duration: rscFullEndTime - routeResult.metrics.rscFull.streamMetrics.startTime, endTime: rscFullEndTime }) // this stream is consumed by the html stream }); if (options.onMetrics) { options.onMetrics(rscFullMetrics); } } } else if (event.data.fileType === "rsc") { const rscEndTime = performance.now(); const rscMetrics = createRenderMetrics({ route, type: routeResult.metrics.rscHeadless.type, fromMainThread: routeResult.metrics.rscHeadless.fromMainThread, fromRscWorker: routeResult.metrics.rscHeadless.fromRscWorker, fromHtmlWorker: routeResult.metrics.rscHeadless.fromHtmlWorker, fileSize: event.data.content.length, chunks: event.data.chunks || 0, processingTime: rscEndTime - routeResult.metrics.rscHeadless.streamMetrics.startTime, chunkRate: (event.data.chunks || 0) / ((rscEndTime - routeResult.metrics.rscHeadless.streamMetrics.startTime) / 1e3), fileName: event.data.fileName, outputPath: event.data.path, baseDir: event.data.baseDir, routePath: event.data.routePath, streamMetrics: createStreamMetrics({ ...routeResult.metrics.rscHeadless.streamMetrics, chunks: event.data.chunks || 0, bytes: event.data.content.length, duration: rscEndTime - routeResult.metrics.rscHeadless.streamMetrics.startTime, endTime: rscEndTime }) }); if (options.onMetrics) { options.onMetrics(rscMetrics); } } } } }; const renderPageOnEvent = (event) => { wrapperOnEvent(event); }; const rscWritePromise = fileWriter( result.rsc, "rsc", { ...options, route, onEvent: renderPageOnEvent, logger: options.logger }, options.signal ); const htmlWritePromise = fileWriter( result.html, "html", { ...options, route, onEvent: renderPageOnEvent, logger: options.logger }, options.signal ); await Promise.all([rscWritePromise, htmlWritePromise]); activeStreams.delete(route); if (options.verbose) { options.logger?.info( `[renderPages] Successfully wrote files for route: ${route}` ); } } catch (error) { if (options.verbose) { options.logger?.error( `[renderPages] Failed to write files for route: ${route}: ${error}` ); } failedRoutes.set(route, error); completedRoutes.delete(route); results.delete(route); } yield { type: "success", route, completedRoutes, failedRoutes, results }; } } } catch (err) { const panicError = handleError({ error: err, logger: options.logger, panicThreshold: options.panicThreshold}); if (panicError != null) { yield { type: "error", error: panicError, route, failedRoutes, completedRoutes, results }; break; } try { results.delete(route); completedRoutes.delete(route); failedRoutes.set(route, err); } catch (cleanupError) { options.logger?.warn( `Failed to cleanup resources for route ${route}: ${cleanupError}` ); } yield { type: "success", // Change from error to success for non-panic route errors route, failedRoutes, completedRoutes, results }; if (options.panicThreshold === "none") { if (options.verbose) { options.logger.info( `[renderPages] Stopping render loop due to error with panicThreshold: "none"` ); } break; } } } if (options.verbose) { options.logger.info( `[renderPages] Final state - completedRoutes: ${completedRoutes.size}, failedRoutes: ${failedRoutes.size}` ); } yield { type: "success", route: "", // No specific route for final result completedRoutes, failedRoutes, results }; if (options.verbose) { options.logger.info(`[renderPages] Returning success result`); } if (options.verbose) { options.logger.info(`[renderPages] About to return success result`); } const shouldPanic = failedRoutes.size > 0 && shouldCausePanic( Array.from(failedRoutes.values())[0], // Check first failed route { panicThreshold: options.panicThreshold } ); if (options.verbose) { options.logger?.info( `[renderPages] Panic check - failedRoutes.size: ${failedRoutes.size}, panicThreshold: ${options.panicThreshold}, shouldPanic: ${shouldPanic}` ); } if (shouldPanic) { const firstError = Array.from(failedRoutes.values())[0]; if (options.verbose) { options.logger?.error( `[renderPages] Yielding panic error: ${firstError instanceof Error ? firstError.message : String(firstError)}` ); } yield { type: "error", error: firstError instanceof Error ? firstError : new Error(String(firstError)), route: "", // No specific route for global panic completedRoutes, failedRoutes, results }; return { type: "error", error: firstError instanceof Error ? firstError : new Error(String(firstError)), route: "", completedRoutes, failedRoutes, results }; } return { type: "success", completedRoutes, failedRoutes, results }; }(); }; export { renderPages }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVuZGVyUGFnZXMuanMiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3BsdWdpbi9yZWFjdC1zdGF0aWMvcmVuZGVyUGFnZXMudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiByZW5kZXJQYWdlcy50c1xuICpcbiAqIFBVUlBPU0U6IFJlbmRlcnMgbXVsdGlwbGUgcGFnZXMgaW4gcGFyYWxsZWwgd2l0aCBwcm9wZXIgZXJyb3IgaGFuZGxpbmdcbiAqXG4gKiBUaGlzIG1vZHVsZTpcbiAqIDEuIFRha2VzIGEgbGlzdCBvZiByb3V0ZXMgYW5kIHJlbmRlcnMgZWFjaCBwYWdlXG4gKiAyLiBIYW5kbGVzIGVycm9ycyBncmFjZWZ1bGx5IHdpdGggY29uZmlndXJhYmxlIHBhbmljIHRocmVzaG9sZHNcbiAqIDMuIENvbGxlY3RzIG1ldHJpY3MgZm9yIGVhY2ggcmVuZGVyZWQgcGFnZVxuICogNC4gU3VwcG9ydHMgcmV0cnlpbmcgZmFpbGVkIHJvdXRlcyB3aXRoIGZhbGxiYWNrIGNvbXBvbmVudHNcbiAqIDUuIFJldHJpZXMgZmFpbGVkIHJvdXRlcyB3aXRoIG5vLW9wIFBhZ2UgY29tcG9uZW50IGZvciBtaW5pbWFsIEhUTUwgc2hlbGxzXG4gKi9cbmltcG9ydCB0eXBlIHsgUmVuZGVyUGFnZXNSZXN1bHQsIFJlbmRlclBhZ2VSZXN1bHQgfSBmcm9tIFwiLi4vdHlwZXMuanNcIjtcbmltcG9ydCB0eXBlIHsgUmVuZGVyUGFnZXNGbiB9IGZyb20gXCIuL3R5cGVzLmpzXCI7XG5pbXBvcnQgeyBoYW5kbGVFcnJvciB9IGZyb20gXCIuLi9lcnJvci9oYW5kbGVFcnJvci5qc1wiO1xuaW1wb3J0IHsgc2hvdWxkQ2F1c2VQYW5pYyB9IGZyb20gXCIuLi9lcnJvci9wYW5pY1RocmVzaG9sZEhhbmRsZXIuanNcIjtcbmltcG9ydCB7IGZpbGVXcml0ZXIgfSBmcm9tIFwiLi9maWxlV3JpdGVyLmpzXCI7XG5cbmltcG9ydCB0eXBlIHsgTWFuaWZlc3QgfSBmcm9tIFwidml0ZVwiO1xuaW1wb3J0IHsgY3JlYXRlUmVuZGVyTWV0cmljcyB9IGZyb20gXCIuLi9tZXRyaWNzL2NyZWF0ZVJlbmRlck1ldHJpY3MuanNcIjtcbmltcG9ydCB7IGNyZWF0ZVN0cmVhbU1ldHJpY3MgfSBmcm9tIFwiLi4vbWV0cmljcy9jcmVhdGVTdHJlYW1NZXRyaWNzLmpzXCI7XG5cbmZ1bmN0aW9uIHJlc29sdmVQYXRoV2l0aE1hbmlmZXN0KHBhdGg6IHN0cmluZywgbWFuaWZlc3Q6IE1hbmlmZXN0KTogc3RyaW5nIHtcbiAgY29uc3QgZW50cnkgPSBtYW5pZmVzdFtwYXRoXTtcbiAgaWYgKGVudHJ5ICYmIGVudHJ5LmZpbGUpIHtcbiAgICByZXR1cm4gZW50cnkuZmlsZTtcbiAgfVxuICByZXR1cm4gcGF0aDtcbn1cblxuLyoqXG4gKiBSZW5kZXJzIGFsbCBwYWdlcyBmb3Igc3RhdGljIGdlbmVyYXRpb25cbiAqXG4gKiBUaGlzIGZ1bmN0aW9uOlxuICogMS4gSXRlcmF0ZXMgdGhyb3VnaCBhbGwgcm91dGVzIGluIHRoZSB1cmxNYXBcbiAqIDIuIFJlbmRlcnMgZWFjaCBwYWdlIHVzaW5nIHRoZSBwcm92aWRlZCByZW5kZXJQYWdlIGZ1bmN0aW9uXG4gKiAzLiBXcml0ZXMgYm90aCBSU0MgYW5kIEhUTUwgZmlsZXMgZm9yIGVhY2ggcm91dGVcbiAqIDQuIEhhbmRsZXMgZXJyb3JzIGFjY29yZGluZyB0byBwYW5pYyB0aHJlc2hvbGRcbiAqIDUuIFJldHJpZXMgZmFpbGVkIHJvdXRlcyB3aXRoIG5vLW9wIFBhZ2UgY29tcG9uZW50IGZvciBtaW5pbWFsIEhUTUwgc2hlbGxzXG4gKi9cbmV4cG9ydCBjb25zdCByZW5kZXJQYWdlczogUmVuZGVyUGFnZXNGbiA9IChcbiAgcm91dGVzLFxuICBoYW5kbGVyT3B0aW9ucyxcbiAgcmVuZGVyUGFnZVxuKSA9PiB7XG4gIGNvbnN0IHtcbiAgICBhdXRvRGlzY292ZXJlZEZpbGVzLFxuICAgIGNzc0ZpbGVzQnlQYWdlLFxuICAgIG1hbmlmZXN0ID0ge30sXG4gICAgLi4ub3B0aW9uc1xuICB9ID0gaGFuZGxlck9wdGlvbnM7XG4gIGNvbnN0IGNvbXBsZXRlZFJvdXRlcyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuICBjb25zdCBmYWlsZWRSb3V0ZXMgPSBuZXcgTWFwPHN0cmluZywgdW5rbm93bj4oKTtcbiAgY29uc3QgYWN0aXZlU3RyZWFtcyA9IG5ldyBNYXA8c3RyaW5nLCB7IGh0bWw6IHsgYWJvcnQ6IChyZWFzb24/OiB1bmtub3duKSA9PiB2b2lkIH0sIHJzYzogeyBhYm9ydDogKHJlYXNvbj86IHVua25vd24pID0+IHZvaWQgfSB9PigpO1xuXG4gIGNvbnN0IHJlc3VsdHMgPSBuZXcgTWFwPHN0cmluZywgUmVuZGVyUGFnZVJlc3VsdD4oKTtcblxuICBpZiAoIWF1dG9EaXNjb3ZlcmVkRmlsZXMudXJsTWFwKSB7XG4gICAgLy8gUmV0dXJuIGFuIGVycm9yIHJlc3VsdCBpbnN0ZWFkIG9mIHRocm93aW5nXG4gICAgcmV0dXJuIChhc3luYyBmdW5jdGlvbiogX3JlbmRlclBhZ2VzKCk6IEFzeW5jR2VuZXJhdG9yPFJlbmRlclBhZ2VzUmVzdWx0LCBSZW5kZXJQYWdlc1Jlc3VsdCwgdW5rbm93bj4ge1xuICAgICAgeWllbGQge1xuICAgICAgICB0eXBlOiBcImVycm9yXCIsXG4gICAgICAgIGVycm9yOiBuZXcgRXJyb3IoXCJObyB1cmxNYXAgcHJvdmlkZWQgdG8gcmVuZGVyUGFnZXNcIiksXG4gICAgICAgIHJvdXRlOiBcIlwiLFxuICAgICAgICBmYWlsZWRSb3V0ZXM6IG5ldyBNYXAoKSxcbiAgICAgICAgY29tcGxldGVkUm91dGVzOiBuZXcgU2V0KCksXG4gICAgICAgIHJlc3VsdHM6IG5ldyBNYXAoKSxcbiAgICAgIH0gc2F0aXNmaWVzIFJlbmRlclBhZ2VzUmVzdWx0O1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgdHlwZTogXCJlcnJvclwiLFxuICAgICAgICBlcnJvcjogbmV3IEVycm9yKFwiTm8gdXJsTWFwIHByb3ZpZGVkIHRvIHJlbmRlclBhZ2VzXCIpLFxuICAgICAgICByb3V0ZTogXCJcIixcbiAgICAgICAgZmFpbGVkUm91dGVzOiBuZXcgTWFwKCksXG4gICAgICAgIGNvbXBsZXRlZFJvdXRlczogbmV3IFNldCgpLFxuICAgICAgICByZXN1bHRzOiBuZXcgTWFwKCksXG4gICAgICB9IHNhdGlzZmllcyBSZW5kZXJQYWdlc1Jlc3VsdDtcbiAgICB9KSgpO1xuICB9XG5cbiAgcmV0dXJuIChhc3luYyBmdW5jdGlvbiogX3JlbmRlclBhZ2VzKCk6IEFzeW5jR2VuZXJhdG9yPFJlbmRlclBhZ2VzUmVzdWx0LCBSZW5kZXJQYWdlc1Jlc3VsdCwgdW5rbm93bj4ge1xuICAgIC8vIEZpcnN0IHBhc3M6IHJlbmRlciBhbGwgcm91dGVzIG5vcm1hbGx5XG4gICAgZm9yIChjb25zdCByb3V0ZSBvZiByb3V0ZXMpIHtcbiAgICAgIC8vIENoZWNrIGZvciBhYm9ydCBzaWduYWxcbiAgICAgIGlmIChvcHRpb25zLnNpZ25hbD8uYWJvcnRlZCkge1xuICAgICAgICB5aWVsZCB7XG4gICAgICAgICAgdHlwZTogXCJlcnJvclwiLFxuICAgICAgICAgIGVycm9yOiBvcHRpb25zLnNpZ25hbC5yZWFzb24gfHwgbmV3IEVycm9yKFwiQnVpbGQgYWJvcnRlZFwiKSxcbiAgICAgICAgICByb3V0ZTogcm91dGUsXG4gICAgICAgICAgZmFpbGVkUm91dGVzLFxuICAgICAgICAgIGNvbXBsZXRlZFJvdXRlcyxcbiAgICAgICAgICByZXN1bHRzLFxuICAgICAgICB9IHNhdGlzZmllcyBSZW5kZXJQYWdlc1Jlc3VsdDtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICB0eXBlOiBcImVycm9yXCIsXG4gICAgICAgICAgZXJyb3I6IG9wdGlvbnMuc2lnbmFsLnJlYXNvbiB8fCBuZXcgRXJyb3IoXCJCdWlsZCBhYm9ydGVkXCIpLFxuICAgICAgICAgIHJvdXRlOiByb3V0ZSxcbiAgICAgICAgICBmYWlsZWRSb3V0ZXMsXG4gICAgICAgICAgY29tcGxldGVkUm91dGVzLFxuICAgICAgICAgIHJlc3VsdHMsXG4gICAgICAgIH0gc2F0aXNmaWVzIFJlbmRlclBhZ2VzUmVzdWx0O1xuICAgICAgfVxuICAgICAgY29uc3QgeyBwYWdlLCBwcm9wcywgcm9vdCwgaHRtbCB9ID1cbiAgICAgICAgYXV0b0Rpc2NvdmVyZWRGaWxlcy51cmxNYXAuZ2V0KHJvdXRlKSB8fCB7fTtcbiAgICAgIGlmICghcGFnZSkgY29udGludWU7XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHJlc29sdmVkUGFnZVBhdGggPSBwYWdlXG4gICAgICAgICAgPyByZXNvbHZlUGF0aFdpdGhNYW5pZmVzdChwYWdlLCBtYW5pZmVzdClcbiAgICAgICAgICA6IHVuZGVmaW5lZDtcbiAgICAgICAgY29uc3QgcmVzb2x2ZWRQcm9wc1BhdGggPSBwcm9wc1xuICAgICAgICAgID8gcmVzb2x2ZVBhdGhXaXRoTWFuaWZlc3QocHJvcHMsIG1hbmlmZXN0KVxuICAgICAgICAgIDogdW5kZWZpbmVkO1xuICAgICAgICBjb25zdCByZXNvbHZlZFJvb3RQYXRoID0gcm9vdFxuICAgICAgICAgID8gcmVzb2x2ZVBhdGhXaXRoTWFuaWZlc3Qocm9vdCwgbWFuaWZlc3QpXG4gICAgICAgICAgOiB1bmRlZmluZWQ7XG4gICAgICAgIGNvbnN0IHJlc29sdmVkSHRtbFBhdGggPSBodG1sXG4gICAgICAgICAgPyByZXNvbHZlUGF0aFdpdGhNYW5pZmVzdChodG1sLCBtYW5pZmVzdClcbiAgICAgICAgICA6IHVuZGVmaW5lZDtcblxuICAgICAgICBpZiAob3B0aW9ucy52ZXJib3NlKSB7XG4gICAgICAgICAgb3B0aW9ucy5sb2dnZXI/LmluZm8oXG4gICAgICAgICAgICBgW3JlbmRlclBhZ2VzXSBSZXNvbHZlZCBwYXRocyBmb3Igcm91dGUgJHtyb3V0ZX06YFxuICAgICAgICAgICk7XG4gICAgICAgICAgb3B0aW9ucy5sb2dnZXI/LmluZm8oYCAgcGFnZTogJHtwYWdlfSAtPiAke3Jlc29sdmVkUGFnZVBhdGh9YCk7XG4gICAgICAgICAgb3B0aW9ucy5sb2dnZXI/LmluZm8oYCAgcHJvcHM6ICR7cHJvcHN9IC0+ICR7cmVzb2x2ZWRQcm9wc1BhdGh9YCk7XG4gICAgICAgICAgb3B0aW9ucy5sb2dnZXI/LmluZm8oYCAgcm9vdDogJHtyb290fSAtPiAke3Jlc29sdmVkUm9vdFBhdGh9YCk7XG4gICAgICAgICAgb3B0aW9ucy5sb2dnZXI/LmluZm8oYCAgaHRtbDogJHtodG1sfSAtPiAke3Jlc29sdmVkSHRtbFBhdGh9YCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG9wdGlvbnMudmVyYm9zZSkge1xuICAgICAgICAgIG9wdGlvbnMubG9nZ2VyPy5pbmZvKFxuICAgICAgICAgICAgYFtyZW5kZXJQYWdlc10gR2xvYmFsIENTUzogJHtvcHRpb25zLmdsb2JhbENzcz8uc2l6ZX0gZmlsZXNgXG4gICAgICAgICAgKTtcbiAgICAgICAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBvcHRpb25zLmdsb2JhbENzcz8uZW50cmllcygpID8/IFtdKSB7XG4gICAgICAgICAgICBvcHRpb25zLmxvZ2dlcj8uaW5mbyhcbiAgICAgICAgICAgICAgYFtyZW5kZXJQYWdlc10gR2xvYmFsIENTUzogJHtrZXl9IC0+ICR7dmFsdWUuYXN9ICgke1xuICAgICAgICAgICAgICAgIHZhbHVlLmNoaWxkcmVuID8gXCJpbmxpbmVcIiA6IFwibGlua1wiXG4gICAgICAgICAgICAgIH0pYFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgb3B0aW9ucy5sb2dnZXI/LmluZm8oXG4gICAgICAgICAgICBgW3JlbmRlclBhZ2VzXSBDU1MgZmlsZXM6ICR7Y3NzRmlsZXNCeVBhZ2UuZ2V0KHJvdXRlKT8uc2l6ZX0gZmlsZXNgXG4gICAgICAgICAgKTtcbiAgICAgICAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBjc3NGaWxlc0J5UGFnZS5nZXQocm91dGUpPy5lbnRyaWVzKCkgPz9cbiAgICAgICAgICAgIFtdKSB7XG4gICAgICAgICAgICBvcHRpb25zLmxvZ2dlcj8uaW5mbyhcbiAgICAgICAgICAgICAgYFtyZW5kZXJQYWdlc10gQ1NTIGZpbGU6ICR7a2V5fSAtPiAke3ZhbHVlLmFzfSAoJHtcbiAgICAgICAgICAgICAgICB2YWx1ZS5jaGlsZHJlbiA/IFwiaW5saW5lXCIgOiBcImxpbmtcIlxuICAgICAgICAgICAgICB9KWBcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gQ3JlYXRlIGEgd3JhcHBlciBvbkV2ZW50IHRoYXQgaGFuZGxlcyByb3V0ZS5lcnJvciBldmVudHMgaW4gcmVuZGVyUGFnZXNcbiAgICAgICAgY29uc3QgcmVuZGVyUGFnZVdyYXBwZXJPbkV2ZW50ID0gKGV2ZW50OiBhbnkpID0+IHtcbiAgICAgICAgICAvLyBDYWxsIHRoZSBvcmlnaW5hbCBvbkV2ZW50IGZpcnN0XG4gICAgICAgICAgaWYgKG9wdGlvbnMub25FdmVudCkge1xuICAgICAgICAgICAgb3B0aW9ucy5vbkV2ZW50KGV2ZW50KTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBIYW5kbGUgcm91dGUuZXJyb3IgZXZlbnRzIGhlcmUgaW4gcmVuZGVyUGFnZXNcbiAgICAgICAgICBpZiAoZXZlbnQudHlwZSA9PT0gXCJyb3V0ZS5lcnJvclwiKSB7XG4gICAgICAgICAgICAvLyBNYWtlIHBhbmljIGRlY2lzaW9uIGluIHRoZSBtYWluIHRocmVhZCBiYXNlZCBvbiBwYW5pY1RocmVzaG9sZFxuICAgICAgICAgICAgY29uc3QgZGV0ZWN0ZWRQYW5pY0Vycm9yID0gaGFuZGxlRXJyb3Ioe1xuICAgICAgICAgICAgICBlcnJvcjogZXZlbnQuZGF0YS5lcnJvcixcbiAgICAgICAgICAgICAgbG9nZ2VyOiBvcHRpb25zLmxvZ2dlcixcbiAgICAgICAgICAgICAgcGFuaWNUaHJlc2hvbGQ6IGV2ZW50LmRhdGEucGFuaWNUaHJlc2hvbGQsXG4gICAgICAgICAgICAgIGNvbnRleHQ6IGByb3V0ZS5lcnJvciAoJHtldmVudC5kYXRhLnJvdXRlfSlgLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGlmIChkZXRlY3RlZFBhbmljRXJyb3IgIT0gbnVsbCkge1xuICAgICAgICAgICAgICAvLyBUaGlzIGlzIGEgcGFuaWMgdGhyZXNob2xkIGVycm9yLCBhZGQgaXQgdG8gZmFpbGVkIHJvdXRlc1xuICAgICAgICAgICAgICBvcHRpb25zLmxvZ2dlcj8uZXJyb3IoXG4gICAgICAgICAgICAgICAgYFtyZW5kZXJQYWdlc10gUGFuaWMgZXJyb3IgZm9yIHJvdXRlICR7ZXZlbnQuZGF0YS5yb3V0ZX06ICR7ZXZlbnQuZGF0YS5lcnJvci5tZXNzYWdlfWBcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgZmFpbGVkUm91dGVzLnNldChldmVudC5kYXRhLnJvdXRlLCBldmVudC5kYXRhLmVycm9yKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIC8vIEZvciBub24tcGFuaWMgZXJyb3JzLCBqdXN0IGxvZyBhbmQgY29udGludWVcbiAgICAgICAgICAgICAgb3B0aW9ucy5sb2dnZXI/Lndhcm4oXG4gICAgICAgICAgICAgICAgYFtyZW5kZXJQYWdlc10gTm9uLXBhbmljIGVycm9yIGZvciByb3V0ZSAke2V2ZW50LmRhdGEucm91dGV9OiAke2V2ZW50LmRhdGEuZXJyb3IubWVzc2FnZX1gXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9O1xuXG4gICAgICAgIC8vIENyZWF0ZSB1bmlxdWUgaGFuZGxlciBvcHRpb25zIGZvciB0aGlzIHJvdXRlXG4gICAgICAgIGNvbnN0IHJvdXRlSGFuZGxlck9wdGlvbnMgPSB7XG4gICAgICAgICAgLi4ub3B0aW9ucyxcbiAgICAgICAgICBtYW5pZmVzdCxcbiAgICAgICAgICByb3V0ZSxcbiAgICAgICAgICBwYWdlUGF0aDogcmVzb2x2ZWRQYWdlUGF0aCBhcyBzdHJpbmcsXG4gICAgICAgICAgcHJvcHNQYXRoOiByZXNvbHZlZFByb3BzUGF0aCBhcyBzdHJpbmcsXG4gICAgICAgICAgcm9vdFBhdGg6IHJlc29sdmVkUm9vdFBhdGggYXMgc3RyaW5nLFxuICAgICAgICAgIGh0bWxQYXRoOiByZXNvbHZlZEh0bWxQYXRoIGFzIHN0cmluZyxcbiAgICAgICAgICBjc3NGaWxlczogY3NzRmlsZXNCeVBhZ2UuZ2V0KHJvdXRlKSA/PyBuZXcgTWFwKCksXG4gICAgICAgICAgLy8gRW5zdXJlIGdsb2JhbCBDU1MgaXMgYXZhaWxhYmxlIHRvIEh0bWwgY29tcG9uZW50XG4gICAgICAgICAgZ2xvYmFsQ3NzOiBvcHRpb25zLmdsb2JhbENzcyA/PyBuZXcgTWFwKCksXG4gICAgICAgICAgLy8gR2VuZXJhdGUgdW5pcXVlIElEIGZvciB0aGlzIHJvdXRlXG4gICAgICAgICAgaWQ6IGAke3JvdXRlfS0ke0RhdGUubm93KCl9LSR7TWF0aC5yYW5kb20oKVxuICAgICAgICAgICAgLnRvU3RyaW5nKDM2KVxuICAgICAgICAgICAgLnN1YnN0cmluZygyLCAxMSl9YCxcbiAgICAgICAgICAvLyBPdmVycmlkZSBvbkV2ZW50IHRvIHVzZSBvdXIgd3JhcHBlclxuICAgICAgICAgIG9uRXZlbnQ6IHJlbmRlclBhZ2VXcmFwcGVyT25FdmVudCxcbiAgICAgICAgfTtcblxuICAgICAgICBjb25zdCBwYWdlUmVuZGVyZXIgPSByZW5kZXJQYWdlKHJvdXRlSGFuZGxlck9wdGlvbnMpO1xuXG4gICAgICAgIGlmIChvcHRpb25zLnZlcmJvc2UpIHtcbiAgICAgICAgICBvcHRpb25zLmxvZ2dlcj8uaW5mbyhcbiAgICAgICAgICAgIGBbcmVuZGVyUGFnZXNdIFN0YXJ0aW5nIHRvIHByb2Nlc3Mgcm91dGU6ICR7cm91dGV9YFxuICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICBmb3IgYXdhaXQgKGNvbnN0IHJlc3VsdCBvZiBwYWdlUmVuZGVyZXIpIHtcbiAgICAgICAgICBpZiAob3B0aW9ucy52ZXJib3NlKSB7XG4gICAgICAgICAgICBvcHRpb25zLmxvZ2dlcj8uaW5mbyhcbiAgICAgICAgICAgICAgYFtyZW5kZXJQYWdlc10gUmVjZWl2ZWQgcmVzdWx0IGZvciByb3V0ZSAke3JvdXRlfTogJHtyZXN1bHQudHlwZX1gXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmIChyZXN1bHQudHlwZSA9PT0gXCJza2lwXCIpIHtcbiAgICAgICAgICAgIGlmIChvcHRpb25zLnZlcmJvc2UpIHtcbiAgICAgICAgICAgICAgb3B0aW9ucy5sb2dnZXI/LmluZm8oXG4gICAgICAgICAgICAgICAgYFtyZW5kZXJQYWdlc10gU2tpcHBpbmcgUlNDIGZvciByb3V0ZSAke3JvdXRlfSBkdWUgdG8gZXJyb3I6ICR7cmVzdWx0LnJlYXNvbn1gXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIEZvciBza2lwcGVkIHJvdXRlcywgd2Ugc3RpbGwgd2FudCB0byB3cml0ZSB0aGUgSFRNTCBmaWxlIChjbGllbnQtb25seSlcbiAgICAgICAgICAgIC8vIGJ1dCBza2lwIHRoZSBSU0MgZmlsZSBzaW5jZSB0aGVyZSB3YXMgYSBzZXJ2ZXIgZXJyb3JcbiAgICAgICAgICAgIGZhaWxlZFJvdXRlcy5zZXQocm91dGUsIHJlc3VsdC5yZWFzb24pO1xuXG4gICAgICAgICAgICAvLyBTdG9yZSB0aGUgcmVzdWx0IHdpdGggdGhlIHN0cmVhbXMgcHJvdmlkZWQgYnkgcmVuZGVyUGFnZVxuICAgICAgICAgICAgcmVzdWx0cy5zZXQocm91dGUsIHtcbiAgICAgICAgICAgICAgdHlwZTogXCJzdWNjZXNzXCIsXG4gICAgICAgICAgICAgIGh0bWw6IHJlc3VsdC5odG1sLFxuICAgICAgICAgICAgICByc2M6IHJlc3VsdC5yc2MsXG4gICAgICAgICAgICAgIG1ldHJpY3M6IHJlc3VsdC5tZXRyaWNzLFxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIC8vIFdyaXRlIGJvdGggSFRNTCBhbmQgUlNDIGZpbGVzIGZvciBza2lwcGVkIHJvdXRlcyAoUlNDIHdpbGwgYmUgZW1wdHkpXG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICBjb25zdCB3cmFwcGVyT25FdmVudCA9IChldmVudDogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKG9wdGlvbnMub25FdmVudCkge1xuICAgICAgICAgICAgICAgICAgb3B0aW9ucy5vbkV2ZW50KGV2ZW50KTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBIYW5kbGUgbWV0cmljcyBmb3IgSFRNTC1vbmx5IHdyaXRlc1xuICAgICAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICAgIGV2ZW50LnR5cGUgPT09IFwiZmlsZS53cml0ZS5kb25lXCIgJiZcbiAgICAgICAgICAgICAgICAgIGV2ZW50LmRhdGEucm91dGUgPT09IHJvdXRlICYmXG4gICAgICAgICAgICAgICAgICBldmVudC5kYXRhLmZpbGVUeXBlID09PSBcImh0bWxcIlxuICAgICAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgICAgY29uc3Qgcm91dGVSZXN1bHQgPSByZXN1bHRzLmdldChyb3V0ZSk7XG4gICAgICAgICAgICAgICAgICBpZiAocm91dGVSZXN1bHQgJiYgcm91dGVSZXN1bHQudHlwZSA9PT0gXCJzdWNjZXNzXCIpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgZW5kVGltZSA9IHBlcmZvcm1hbmNlLm5vdygpO1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBodG1sTWV0cmljcyA9IGNyZWF0ZVJlbmRlck1ldHJpY3Moe1xuICAgICAgICAgICAgICAgICAgICAgIHJvdXRlOiByb3V0ZSxcbiAgICAgICAgICAgICAgICAgICAgICB0eXBlOiByb3V0ZVJlc3VsdC5tZXRyaWNzLmh0bWwudHlwZSxcbiAgICAgICAgICAgICAgICAgICAgICBmcm9tTWFpblRocmVhZDogcm91dGVSZXN1bHQubWV0cmljcy5odG1sLmZyb21NYWluVGhyZWFkLFxuICAgICAgICAgICAgICAgICAgICAgIGZyb21Sc2NXb3JrZXI6IHJvdXRlUmVzdWx0Lm1ldHJpY3MuaHRtbC5mcm9tUnNjV29ya2VyLFxuICAgICAgICAgICAgICAgICAgICAgIGZyb21IdG1sV29ya2VyOiByb3V0ZVJlc3VsdC5tZXRyaWNzLmh0bWwuZnJvbUh0bWxXb3JrZXIsXG4gICAgICAgICAgICAgICAgICAgICAgZmlsZVNpemU6IGV2ZW50LmRhdGEuY29udGVudC5sZW5ndGgsXG4gICAgICAgICAgICAgICAgICAgICAgY2h1bmtzOiBldmVudC5kYXRhLmNodW5rcyB8fCAwLFxuICAgICAgICAgICAgICAgICAgICAgIHByb2Nlc3NpbmdUaW1lOlxuICAgICAgICAgICAgICAgICAgICAgICAgZW5kVGltZSAtXG4gICAgICAgICAgICAgICAgICAgICAgICByb3V0ZVJlc3VsdC5tZXRyaWNzLmh0bWwuc3RyZWFtTWV0cmljcy5zdGFydFRpbWUsXG4gICAgICAgICAgICAgICAgICAgICAgY2h1bmtSYXRlOlxuICAgICAgICAgICAgICAgICAgICAgICAgKGV2ZW50LmRhdGEuY2h1bmtzIHx8IDApIC9cbiAgICAgICAgICAgICAgICAgICAgICAgICgoZW5kVGltZSAtXG4gICAgICAgICAgICAgICAgICAgICAgICAgIHJvdXRlUmVzdWx0Lm1ldHJpY3MuaHRtbC5zdHJlYW1NZXRyaWNzLnN0YXJ0VGltZSkgL1xuICAgICAgICAgICAgICAgICAgICAgICAgICAxMDAwKSxcbiAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZTogZXZlbnQuZGF0YS5maWxlTmFtZSxcbiAgICAgICAgICAgICAgICAgICAgICBvdXRwdXRQYXRoOiBldmVudC5kYXRhLnBhdGgsXG4gICAgICAgICAgICAgICAgICAgICAgYmFzZURpcjogZXZlbnQuZGF0YS5iYXNlRGlyLFxuICAgICAgICAgICAgICAgICAgICAgIHJvdXRlUGF0aDogZXZlbnQuZGF0YS5yb3V0ZVBhdGgsXG4gICAgICAgICAgICAgICAgICAgICAgc3RyZWFtTWV0cmljczogY3JlYXRlU3RyZWFtTWV0cmljcyh7XG4gICAgICAgICAgICAgICAgICAgICAgICAuLi5yb3V0ZVJlc3VsdC5tZXRyaWNzLmh0bWwuc3RyZWFtTWV0cmljcyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGNodW5rczogZXZlbnQuZGF0YS5jaHVua3MgfHwgMCxcbiAgICAgICAgICAgICAgICAgICAgICAgIGJ5dGVzOiBldmVudC5kYXRhLmNvbnRlbnQubGVuZ3RoLFxuICAgICAgICAgICAgICAgICAgICAgICAgZHVyYXRpb246XG4gICAgICAgICAgICAgICAgICAgICAgICAgIGVuZFRpbWUgLVxuICAgICAgICAgICAgICAgICAgICAgICAgICByb3V0ZVJlc3VsdC5tZXRyaWNzLmh0bWwuc3RyZWFtTWV0cmljcy5zdGFydFRpbWUsXG4gICAgICAgICAgICAgICAgICAgICAgICBlbmRUaW1lOiBlbmRUaW1lLFxuICAgICAgICAgICAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgICAgICBpZiAob3B0aW9ucy5vbk1ldHJpY3MpIHtcbiAgICAgICAgICAgICAgICAgICAgICBvcHRpb25zLm9uTWV0cmljcyhodG1sTWV0cmljcyk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgICAgY29uc3QgcnNjV3JpdGVQcm9taXNlID0gZmlsZVdyaXRlcihcbiAgICAgICAgICAgICAgICByZXN1bHQucnNjIGFzIGFueSxcbiAgICAgICAgICAgICAgICBcInJzY1wiLFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgIC4uLm9wdGlvbnMsXG4gICAgICAgICAgICAgICAgICByb3V0ZSxcbiAgICAgICAgICAgICAgICAgIG9uRXZlbnQ6IHdyYXBwZXJPbkV2ZW50LFxuICAgICAgICAgICAgICAgICAgbG9nZ2VyOiBvcHRpb25zLmxvZ2dlcixcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIG9wdGlvbnMuc2lnbmFsXG4gICAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgICAgY29uc3QgaHRtbFdyaXRlUHJvbWlzZSA9IGZpbGVXcml0ZXIoXG4gICAgICAgICAgICAgICAgcmVzdWx0Lmh0bWwgYXMgYW55LFxuICAgICAgICAgICAgICAgIFwiaHRtbFwiLFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgIC4uLm9wdGlvbnMsXG4gICAgICAgICAgICAgICAgICByb3V0ZSxcbiAgICAgICAgICAgICAgICAgIG9uRXZlbnQ6IHdyYXBwZXJPbkV2ZW50LFxuICAgICAgICAgICAgICAgICAgbG9nZ2VyOiBvcHRpb25zLmxvZ2dlcixcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIG9wdGlvbnMuc2lnbmFsXG4gICAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgICAgLy8gV2FpdCBmb3IgYm90aCBSU0MgYW5kIEhUTUwgZmlsZXMgdG8gYmUgd3JpdHRlblxuICAgICAgICAgICAgICBhd2FpdCBQcm9taXNlLmFsbChbcnNjV3JpdGVQcm9taXNlLCBodG1sV3JpdGVQcm9taXNlXSk7XG5cbiAgICAgICAgICAgICAgY29tcGxldGVkUm91dGVzLmFkZChyb3V0ZSk7XG5cbiAgICAgICAgICAgICAgaWYgKG9wdGlvbnMudmVyYm9zZSkge1xuICAgICAgICAgICAgICAgIG9wdGlvbnMubG9nZ2VyPy5pbmZvKFxuICAgICAgICAgICAgICAgICAgYFtyZW5kZXJQYWdlc10gV3JvdGUgSFRNTC1vbmx5IGZpbGUgZm9yIHNraXBwZWQgcm91dGU6ICR7cm91dGV9YFxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gY2F0Y2ggKHdyaXRlRXJyb3IpIHtcbiAgICAgICAgICAgICAgaWYgKG9wdGlvbnMudmVyYm9zZSkge1xuICAgICAgICAgICAgICAgIG9wdGlvbnMubG9nZ2VyPy5lcnJvcihcbiAgICAgICAgICAgICAgICAgIGBbcmVuZGVyUGFnZXNdIEZhaWxlZCB0byB3cml0ZSBIVE1MIGZvciBza2lwcGVkIHJvdXRlICR7cm91dGV9OiAke3dyaXRlRXJyb3J9YFxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgLy8gUmVtb3ZlIGZyb20gY29tcGxldGVkIHJvdXRlcyBpZiBIVE1MIHdyaXRlIGZhaWxlZFxuICAgICAgICAgICAgICBjb21wbGV0ZWRSb3V0ZXMuZGVsZXRlKHJvdXRlKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKHJlc3VsdC50eXBlID09PSBcImVycm9yXCIpIHtcbiAgICAgICAgICAgIGlmIChvcHRpb25zLnZlcmJvc2UpIHtcbiAgICAgICAgICAgICAgb3B0aW9ucy5sb2dnZXI/LmVycm9yKFxuICAgICAgICAgICAgICAgIGBbcmVuZGVyUGFnZXNdIEVycm9yIGZvciByb3V0ZSAke3JvdXRlfTogJHtyZXN1bHQuZXJyb3J9YFxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZmFpbGVkUm91dGVzLnNldChyb3V0ZSwgcmVzdWx0LmVycm9yKTtcbiAgICAgICAgICAgIHlpZWxkIHtcbiAgICAgICAgICAgICAgdHlwZTogXCJlcnJvclwiLFxuICAgICAgICAgICAgICBlcnJvcjogcmVzdWx0LmVycm9yLFxuICAgICAgICAgICAgICByb3V0ZTogcm91dGUsXG4gICAgICAgICAgICAgIGZhaWxlZFJvdXRlcyxcbiAgICAgICAgICAgICAgY29tcGxldGVkUm91dGVzLFxuICAgICAgICAgICAgICByZXN1bHRzLFxuICAgICAgICAgICAgfSBzYXRpc2ZpZXMgUmVuZGVyUGFnZXNSZXN1bHQ7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBpZiAocmVzdWx0LnR5cGUgPT09IFwic3VjY2Vzc1wiKSB7XG4gICAgICAgICAgICBpZiAob3B0aW9ucy52ZXJib3NlKSB7XG4gICAgICAgICAgICAgIG9wdGlvbnMubG9nZ2VyPy5pbmZvKFxuICAgICAgICAgICAgICAgIGBbcmVuZGVyUGFnZXNdIFN1Y2Nlc3MgZm9yIHJvdXRlICR7cm91dGV9LCBzdGFydGluZyBmaWxlIHdyaXRlc2BcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbXBsZXRlZFJvdXRlcy5hZGQocm91dGUpO1xuICAgICAgICAgICAgLy8gU3RvcmUgdGhlIHJlc3VsdCB3aXRoIHRoZSBjb3JyZWN0IHR5cGUgc3RydWN0dXJlXG4gICAgICAgICAgICByZXN1bHRzLnNldChyb3V0ZSwge1xuICAgICAgICAgICAgICB0eXBlOiBcInN1Y2Nlc3NcIixcbiAgICAgICAgICAgICAgaHRtbDogcmVzdWx0Lmh0bWwsXG4gICAgICAgICAgICAgIHJzYzogcmVzdWx0LnJzYyxcbiAgICAgICAgICAgICAgbWV0cmljczogcmVzdWx0Lm1ldHJpY3MsXG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgLy8gU3RvcmUgYWN0aXZlIHN0cmVhbXMgZm9yIHBvdGVudGlhbCBhYm9ydGlvbiBkdXJpbmcgY2FuY2VsbGF0aW9uXG4gICAgICAgICAgICBhY3RpdmVTdHJlYW1zLnNldChyb3V0ZSwge1xuICAgICAgICAgICAgICBodG1sOiByZXN1bHQuaHRtbCxcbiAgICAgICAgICAgICAgcnNjOiByZXN1bHQucnNjLFxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIC8vIFdyaXRlIGZpbGVzIGRpcmVjdGx5IGluIHJlbmRlclBhZ2VzXG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAvLyBDcmVhdGUgYSB3cmFwcGVyIG9uRXZlbnQgdGhhdCBjYWxscyBib3RoIHRoZSByZW5kZXJQYWdlJ3MgZXZlbnQgaGFuZGxlciBhbmQgdGhlIG9yaWdpbmFsIG9uRXZlbnRcbiAgICAgICAgICAgICAgY29uc3Qgd3JhcHBlck9uRXZlbnQgPSAoZXZlbnQ6IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAvLyBDYWxsIHRoZSBvcmlnaW5hbCBvbkV2ZW50IGZpcnN0XG4gICAgICAgICAgICAgICAgICBpZiAob3B0aW9ucy5vbkV2ZW50KSB7XG4gICAgICAgICAgICAgICAgICAgIG9wdGlvbnMub25FdmVudChldmVudCk7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgIC8vIElmIG9uRXZlbnQgdGhyb3dzIGFuIGVycm9yIChlLmcuLCBidWlsZCBjYW5jZWxsYXRpb24pLCB3ZSBuZWVkIHRvIHN0b3AgcHJvY2Vzc2luZ1xuICAgICAgICAgICAgICAgICAgLy8gVGhpcyBwcmV2ZW50cyB0aGUgd29ya2VyIGZyb20gY29udGludWluZyB0byBwcm9jZXNzIGFmdGVyIHRoZSBidWlsZCBpcyBjYW5jZWxsZWRcbiAgICAgICAgICAgICAgICAgIG9wdGlvbnMubG9nZ2VyPy5lcnJvcihgW3JlbmRlclBhZ2VzXSBvbkV2ZW50IGhhbmRsZXIgdGhyZXcgZXJyb3I6ICR7KGVycm9yIGFzIEVycm9yKS5tZXNzYWdlfWApO1xuICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAvLyBGaXJzdCwgYWJvcnQgYWxsIGFjdGl2ZSBzdHJlYW1zIHRvIHN0b3AgZGF0YSBmbG93XG4gICAgICAgICAgICAgICAgICAvLyBUaGlzIGlzIGNyaXRpY2FsIHRvIHByZXZlbnQgdW5oYW5kbGVkIGVycm9ycyBhbmQgbWVtb3J5IGxlYWtzXG4gICAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IFssIHN0cmVhbXNdIG9mIGFjdGl2ZVN0cmVhbXMpIHtcbiAgICAgICAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAgICAgICBzdHJlYW1zLmh0bWwuYWJvcnQoXCJCdWlsZCBjYW5jZWxsZWRcIik7XG4gICAgICAgICAgICAgICAgICAgICAgc3RyZWFtcy5yc2MuYWJvcnQoXCJCdWlsZCBjYW5jZWxsZWRcIik7XG4gICAgICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGFib3J0RXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgICAvLyBTdHJlYW1zIG1heSBhbHJlYWR5IGJlIGNsb3NlZCwgaWdub3JlXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgICAgLy8gVGhlbiBzZW5kIHNodXRkb3duIHNpZ25hbCB0byB3b3JrZXIgdG8gcHJldmVudCBpdCBmcm9tIGNvbnRpbnVpbmcgdG8gcHJvY2Vzc1xuICAgICAgICAgICAgICAgICAgaWYgKG9wdGlvbnMucnNjV29ya2VyKSB7XG4gICAgICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgICAgb3B0aW9ucy5yc2NXb3JrZXIucG9zdE1lc3NhZ2Uoe1xuICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogXCJTSFVURE9XTlwiLFxuICAgICAgICAgICAgICAgICAgICAgICAgaWQ6IFwiKlwiLFxuICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChzaHV0ZG93bkVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgLy8gV29ya2VyIG1heSBhbHJlYWR5IGJlIHRlcm1pbmF0ZWQsIGlnbm9yZVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgIC8vIFJlLXRocm93IHRoZSBlcnJvciB0byBzdG9wIHRoZSBidWlsZCBwcm9jZXNzXG4gICAgICAgICAgICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBIYW5kbGUgcm91dGUuZXJyb3IgZXZlbnRzXG4gICAgICAgICAgICAgICAgaWYgKGV2ZW50LnR5cGUgPT09IFwicm91dGUuZXJyb3JcIikge1xuICAgICAgICAgICAgICAgICAgLy8gTWFrZSBwYW5pYyBkZWNpc2lvbiBpbiB0aGUgbWFpbiB0aHJlYWQgYmFzZWQgb24gcGFuaWNUaHJlc2hvbGRcbiAgICAgICAgICAgICAgICAgIGNvbnN0IGRldGVjdGVkUGFuaWNFcnJvciA9IGhhbmRsZUVycm9yKHtcbiAgICAgICAgICAgICAgICAgICAgZXJyb3I6IGV2ZW50LmRhdGEuZXJyb3IsXG4gICAgICAgICAgICAgICAgICAgIGxvZ2dlcjogb3B0aW9ucy5sb2dnZXIsXG4gICAgICAgICAgICAgICAgICAgIHBhbmljVGhyZXNob2xkOiBldmVudC5kYXRhLnBhbmljVGhyZXNob2xkLFxuICAgICAgICAgICAgICAgICAgICBjb250ZXh0OiBgcm91dGUuZXJyb3IgKCR7ZXZlbnQuZGF0YS5yb3V0ZX0pYCxcbiAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICBpZiAoZGV0ZWN0ZWRQYW5pY0Vycm9yICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gVGhpcyBpcyBhIHBhbmljIHRocmVzaG9sZCBlcnJvciwgYWRkIGl0IHRvIGZhaWxlZCByb3V0ZXMgdG8gYmUgeWllbGRlZFxuICAgICAgICAgICAgICAgICAgICBvcHRpb25zLmxvZ2dlcj8uZXJyb3IoXG4gICAgICAgICAgICAgICAgICAgICAgYFtyZW5kZXJQYWdlc10gUGFuaWMgZXJyb3IgZm9yIHJvdXRlICR7ZXZlbnQuZGF0YS5yb3V0ZX06ICR7ZXZlbnQuZGF0YS5lcnJvci5tZXNzYWdlfWBcbiAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICAgICAgZmFpbGVkUm91dGVzLnNldChldmVudC5kYXRhLnJvdXRlLCBldmVudC5kYXRhLmVycm9yKTtcbiAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIEZvciBub24tcGFuaWMgZXJyb3JzLCBqdXN0IGxvZyBhbmQgY29udGludWVcbiAgICAgICAgICAgICAgICAgICAgb3B0aW9ucy5sb2dnZXI/Lndhcm4oXG4gICAgICAgICAgICAgICAgICAgICAgYFtyZW5kZXJQYWdlc10gTm9uLXBhbmljIGVycm9yIGZvciByb3V0ZSAke2V2ZW50LmRhdGEucm91dGV9OiAke2V2ZW50LmRhdGEuZXJyb3IubWVzc2FnZX1gXG4gICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gSGFuZGxlIG1ldHJpY3MgY29sbGVjdGlvbiBoZXJlIHNpbmNlIHRoZSByZW5kZXJQYWdlIGZ1bmN0aW9uJ3MgZXZlbnQgaGFuZGxlciBpcyBub3QgYmVpbmcgY2FsbGVkXG4gICAgICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgICAgZXZlbnQudHlwZSA9PT0gXCJmaWxlLndyaXRlLmRvbmVcIiAmJlxuICAgICAgICAgICAgICAgICAgZXZlbnQuZGF0YS5yb3V0ZSA9PT0gcm91dGVcbiAgICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IHJvdXRlUmVzdWx0ID0gcmVzdWx0cy5nZXQocm91dGUpO1xuICAgICAgICAgICAgICAgICAgaWYgKHJvdXRlUmVzdWx0ICYmIHJvdXRlUmVzdWx0LnR5cGUgPT09IFwic3VjY2Vzc1wiKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChldmVudC5kYXRhLmZpbGVUeXBlID09PSBcImh0bWxcIikge1xuICAgICAgICAgICAgICAgICAgICAgIC8vIFVwZGF0ZSBIVE1MIG1ldHJpY3Mgd2l0aCBhY3R1YWwgZmlsZSBkYXRhXG4gICAgICAgICAgICAgICAgICAgICAgY29uc3QgZW5kVGltZSA9IHBlcmZvcm1hbmNlLm5vdygpO1xuICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGh0bWxNZXRyaWNzID0gY3JlYXRlUmVuZGVyTWV0cmljcyh7XG4gICAgICAgICAgICAgICAgICAgICAgICByb3V0ZTogcm91dGUsXG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiByb3V0ZVJlc3VsdC5tZXRyaWNzLmh0bWwudHlwZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGZyb21NYWluVGhyZWFkOiByb3V0ZVJlc3VsdC5tZXRyaWNzLmh0bWwuZnJvbU1haW5UaHJlYWQsXG4gICAgICAgICAgICAgICAgICAgICAgICBmcm9tUnNjV29ya2VyOiByb3V0ZVJlc3VsdC5tZXRyaWNzLmh0bWwuZnJvbVJzY1dvcmtlcixcbiAgICAgICAgICAgICAgICAgICAgICAgIGZyb21IdG1sV29ya2VyOiByb3V0ZVJlc3VsdC5tZXRyaWNzLmh0bWwuZnJvbUh0bWxXb3JrZXIsXG4gICAgICAgICAgICAgICAgICAgICAgICBmaWxlU2l6ZTogZXZlbnQuZGF0YS5jb250ZW50Lmxlbmd0aCxcbiAgICAgICAgICAgICAgICAgICAgICAgIGNodW5rczogZXZlbnQuZGF0YS5jaHVua3MgfHwgMCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHByb2Nlc3NpbmdUaW1lOlxuICAgICAgICAgICAgICAgICAgICAgICAgICBlbmRUaW1lIC1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgcm91dGVSZXN1bHQubWV0cmljcy5odG1sLnN0cmVhbU1ldHJpY3Muc3RhcnRUaW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgY2h1bmtSYXRlOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAoZXZlbnQuZGF0YS5jaHVua3MgfHwgMCkgL1xuICAgICAgICAgICAgICAgICAgICAgICAgICAoKGVuZFRpbWUgLVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdXRlUmVzdWx0Lm1ldHJpY3MuaHRtbC5zdHJlYW1NZXRyaWNzLnN0YXJ0VGltZSkgL1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDEwMDApLFxuICAgICAgICAgICAgICAgICAgICAgICAgZmlsZU5hbWU6IGV2ZW50LmRhdGEuZmlsZU5hbWUsXG4gICAgICAgICAgICAgICAgICAgICAgICBvdXRwdXRQYXRoOiBldmVudC5kYXRhLnBhdGgsXG4gICAgICAgICAgICAgICAgICAgICAgICBiYXNlRGlyOiBldmVudC5kYXRhLmJhc2VEaXIsXG4gICAgICAgICAgICAgICAgICAgICAgICByb3V0ZVBhdGg6IGV2ZW50LmRhdGEucm91dGVQYXRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgc3RyZWFtTWV0cmljczogY3JlYXRlU3RyZWFtTWV0cmljcyh7XG4gICAgICAgICAgICAgICAgICAgICAgICAgIC4uLnJvdXRlUmVzdWx0Lm1ldHJpY3MuaHRtbC5zdHJlYW1NZXRyaWNzLFxuICAgICAgICAgICAgICAgICAgICAgICAgICBjaHVua3M6IGV2ZW50LmRhdGEuY2h1bmtzIHx8IDAsXG4gICAgICAgICAgICAgICAgICAgICAgICAgIGJ5dGVzOiBldmVudC5kYXRhLmNvbnRlbnQubGVuZ3RoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICBkdXJhdGlvbjpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmRUaW1lIC1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3V0ZVJlc3VsdC5tZXRyaWNzLmh0bWwuc3RyZWFtTWV0cmljcy5zdGFydFRpbWUsXG4gICAgICAgICAgICAgICAgICAgICAgICAgIGVuZFRpbWU6IGVuZFRpbWUsXG4gICAgICAgICAgICAgICAgICAgICAgICB9KSxcbiAgICAgICAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgICAgICAgIGlmIChvcHRpb25zLm9uTWV0cmljcykge1xuICAgICAgICAgICAgICAgICAgICAgICAgb3B0aW9ucy5vbk1ldHJpY3MoaHRtbE1ldHJpY3MpO1xuICAgICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAgIC8vIEFsc28gZW1pdCBSU0MgRnVsbCBtZXRyaWNzICh0aGUgUlNDIGNodW5rcyBzZW50IHRvIEhUTUwgd29ya2VyKVxuICAgICAgICAgICAgICAgICAgICAgIC8vIE9ubHkgaWYgbWV0cmljcy5yc2NGdWxsIGV4aXN0cyAobWlnaHQgYmUgbWlzc2luZyBvbiBlcnJvcnMpXG4gICAgICAgICAgICAgICAgICAgICAgaWYgKHJvdXRlUmVzdWx0Lm1ldHJpY3M/LnJzY0Z1bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHJzY0Z1bGxFbmRUaW1lID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCByc2NGdWxsTWV0cmljcyA9IGNyZWF0ZVJlbmRlck1ldHJpY3Moe1xuICAgICAgICAgICAgICAgICAgICAgICAgICByb3V0ZTogcm91dGUsXG4gICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6IHJvdXRlUmVzdWx0Lm1ldHJpY3MucnNjRnVsbC50eXBlLFxuICAgICAgICAgICAgICAgICAgICAgICAgICBmcm9tTWFpblRocmVhZDpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3V0ZVJlc3VsdC5tZXRyaWNzLnJzY0Z1bGwuZnJ