UNPKG

vite-plugin-react-server

Version:
1,002 lines (1,000 loc) 147 kB
/** * vite-plugin-react-server * Copyright (c) Nico Brinkkemper * MIT License */ import { workerData, parentPort } from 'node:worker_threads'; import { PassThrough } from 'node:stream'; import { createLogger } from 'vite'; import { join, relative } from 'node:path'; import { createRscWorkerLoader } from './createRscWorkerLoader.js'; import { handleRscRender } from './handleRscRender.server.js'; import { setMaxListenersOnPort } from '../../stream/setMaxListeners.js'; import { toError } from '../../error/toError.js'; import { decodeReply } from 'react-server-dom-esm/server'; import { createHandlers } from './handlers.js'; import { addModuleId, addCssFileContent, hmrState, clearAllCachedComponents, cssFiles, isModuleInvalidated, hasCachedComponent, getCachedComponent, clearCachedComponent, cacheComponent } from './state.server.js'; import { getRunner, getRpc } from './runnerInstance.js'; import { processInlineCssForState, combineCssFiles } from '../../helpers/createUnifiedCssProcessor.js'; import { routeToURL } from '../../utils/routeToURL.js'; import { DEFAULT_CONFIG } from '../../config/defaults.js'; import { resolvePageAndProps } from '../../helpers/resolvePageAndProps.js'; import { resolveComponent } from '../../helpers/resolveComponent.js'; import { handleError } from '../../error/handleError.js'; import { Root } from '../../components/root.js'; import { workerUserOptions } from './workerUserOptions.js'; import { hydrateUserOptions } from '../../helpers/hydrateUserOptions.js'; import { React } from '../../vendor/vendor.server.js'; import { sendMessage } from '../sendMessage.js'; import { createModuleResolutionMetrics } from '../../metrics/createModuleResolutionMetrics.js'; const logger = createLogger(workerData.resolvedConfig?.logLevel ?? "info"); const verbose = workerData.userOptions.verbose ?? false; process.on("uncaughtException", (error) => { if (verbose) { logger?.error(`[RSC-WORKER] Uncaught exception: ${error.message}`, { error }); } if (parentPort) { parentPort.postMessage({ type: "ERROR", id: "uncaught-exception", error: { message: error.message, stack: error.stack, name: error.name }, context: "Uncaught Exception in RSC Worker" }); } process.exit(1); }); const renderStates = /* @__PURE__ */ new Map(); let fallbackUserOptions = null; function getUserOptions(renderId) { if (renderId && renderStates.has(renderId)) { const renderState = renderStates.get(renderId); if (renderState.initialized) { return renderState.userOptions; } } if (fallbackUserOptions) { return fallbackUserOptions; } const userOptionsResult = hydrateUserOptions(workerData.userOptions); if (userOptionsResult.type === "error") { throw userOptionsResult.error; } fallbackUserOptions = userOptionsResult.userOptions; if (verbose) { logger?.info( `[FALLBACK] Using fallback userOptions for renderId: ${renderId || "none"}` ); logger?.info( `[FALLBACK] fallbackUserOptions.build: ${JSON.stringify( fallbackUserOptions.build )}` ); } return fallbackUserOptions; } function setRenderUserOptions(renderId, userOptions) { renderStates.set(renderId, { userOptions, initialized: true }); } function cleanupRenderState(renderId) { renderStates.delete(renderId); } const activeRenders = /* @__PURE__ */ new Map(); const activeStreamsByRoute = /* @__PURE__ */ new Map(); const headlessStreamElements = /* @__PURE__ */ new Map(); const headlessStreamErrors = /* @__PURE__ */ new Map(); function cleanupRender(id) { const rscStream = activeRenders.get(id); if (rscStream) { rscStream.destroy(); activeRenders.delete(id); for (const [route, streamId] of activeStreamsByRoute.entries()) { if (streamId === id) { activeStreamsByRoute.delete(route); break; } } } cleanupRenderState(id); } function clearHeadlessError(route) { headlessStreamErrors.delete(route); } async function loadComponentsWithCache(options) { const { pagePath, propsPath, rootPath, htmlPath, pageExportName = "Page", propsExportName = "props", rootExportName = "Root", htmlExportName = "Html", url, loader, verbose: verbose2, logger: logger2, panicThreshold = "none", resolvedPageProps } = options; const normalizeUrlForCache = (urlStr) => { if (urlStr.includes(".")) return urlStr; return urlStr.endsWith("/") ? urlStr : urlStr + "/"; }; const normalizedUrl = normalizeUrlForCache(url); let PageComponent; let pageProps = resolvedPageProps; let RootComponent; let HtmlComponent; if (verbose2) { logger2?.info( `[loadComponentsWithCache] pagePath=${pagePath}, propsPath=${propsPath}, url=${url}` ); } if (pagePath) { if (verbose2) { logger2?.info( `[loadComponentsWithCache] Loading page and props for pagePath=${pagePath}` ); } const pageId = `${pagePath}#${pageExportName}`; const isPageInvalidated = isModuleInvalidated(pagePath); let needToLoadProps = false; if (hasCachedComponent(pageId) && !isPageInvalidated) { PageComponent = getCachedComponent(pageId); if (verbose2) { logger2?.info( `[rsc-worker] Using cached Page component from: ${pagePath}` ); } if (propsPath && !resolvedPageProps) { const propsId = `${propsPath}#${propsExportName}@${normalizedUrl}`; if (hasCachedComponent(propsId)) { clearCachedComponent(propsId); } needToLoadProps = true; } else if (resolvedPageProps) { if (verbose2) { logger2?.info( `[rsc-worker] Using pre-resolved pageProps from main thread: ${Object.keys(resolvedPageProps).length} keys` ); } } } else { if (isPageInvalidated && hasCachedComponent(pageId)) { clearCachedComponent(pageId); if (verbose2) { logger2?.info( `[rsc-worker] Cleared invalidated Page component cache: ${pagePath}` ); } } if (propsPath && isPageInvalidated) { const propsId = `${propsPath}#${propsExportName}@${normalizedUrl}`; if (hasCachedComponent(propsId)) { clearCachedComponent(propsId); } } try { const pageAndPropsResult = await resolvePageAndProps({ pagePath, propsPath, pageExportName, propsExportName, url: normalizedUrl, loader, verbose: verbose2 || false, logger: logger2 }); if (verbose2) { logger2?.info( `[loadComponentsWithCache] pageAndPropsResult for ${pagePath}:`, pageAndPropsResult ); } if (pageAndPropsResult.type === "success") { PageComponent = pageAndPropsResult.PageComponent; if (!resolvedPageProps) { pageProps = pageAndPropsResult.pageProps; } cacheComponent(pageId, PageComponent); if (verbose2) { logger2?.info( `[rsc-worker] Loaded and cached PageComponent from: ${pagePath}` ); if (propsPath) { logger2?.info(`[rsc-worker] Loaded fresh pageProps from: ${propsPath}`); logger2?.info(`[rsc-worker] Loaded pageProps:`, pageProps); } } } else { const pageError = pageAndPropsResult.error ?? new Error( `[rsc-worker] Failed to load page module from ${pagePath}` ); const panicError = handleError({ error: pageError, logger: logger2, panicThreshold, critical: true, context: `rsc-worker: load page from ${pagePath}`, log: true }); throw panicError ?? pageError; } } catch (error) { const panicError = handleError({ error, logger: logger2, panicThreshold, critical: true, log: true }); throw panicError ?? error; } } if (needToLoadProps && propsPath) { if (verbose2) { logger2?.info( `[rsc-worker] Loading props separately for URL: ${url} (Page was cached)` ); } try { const pageAndPropsResult = await resolvePageAndProps({ pagePath, propsPath, pageExportName, propsExportName, url: normalizedUrl, loader, verbose: verbose2 || false, logger: logger2 }); if (pageAndPropsResult.type === "success") { pageProps = pageAndPropsResult.pageProps; if (verbose2) { logger2?.info( `[rsc-worker] Loaded fresh pageProps for URL: ${url}` ); } } else { if (verbose2) { logger2?.warn( `[rsc-worker] Failed to load props for URL ${url}: ${pageAndPropsResult.error?.message}` ); } pageProps = {}; } } catch (error) { if (verbose2) { logger2?.error( `[rsc-worker] Error loading props for URL ${url}`, { error } ); } pageProps = {}; } } } if (rootPath) { const rootId = `${rootPath}#${rootExportName}`; const isRootInvalidated = isModuleInvalidated(rootPath); if (hasCachedComponent(rootId) && !isRootInvalidated) { RootComponent = getCachedComponent(rootId); if (verbose2) { logger2?.info( `[rsc-worker] Using cached Root component from: ${rootPath}` ); } } else { if (isRootInvalidated && hasCachedComponent(rootId)) { clearCachedComponent(rootId); if (verbose2) { logger2?.info( `[rsc-worker] Cleared invalidated Root component cache: ${rootPath}` ); } } const rootResult = await resolveComponent({ componentPath: rootPath, exportName: rootExportName, loader }); if (rootResult.type === "success") { RootComponent = rootResult.component; cacheComponent(rootId, RootComponent); if (verbose2) { logger2?.info( `[rsc-worker] Loaded and cached Root component from: ${rootPath}` ); logger2?.info( `[rsc-worker] Root component type: ${typeof RootComponent}, isSymbol: ${typeof RootComponent === "symbol"}, keys: ${RootComponent ? Object.keys(RootComponent) : "null"}` ); } } else { const rootError = rootResult.error ?? new Error( `[rsc-worker] Failed to load Root component from ${rootPath}` ); const panicError = handleError({ error: rootError, logger: logger2, panicThreshold, critical: true, log: true }); throw panicError ?? rootError; } } } else { RootComponent = Root; if (verbose2) { logger2?.info(`[rsc-worker] Using built-in default Root component`); } } if (verbose2) { logger2?.info(`[rsc-worker] Html component resolution: htmlPath='${htmlPath}' (type: ${typeof htmlPath})`); } if (htmlPath === "") { HtmlComponent = React.Fragment; if (verbose2) { logger2?.info(`[rsc-worker] Using headless Html component (empty string)`); } } else if (htmlPath === void 0) { if (verbose2) { logger2?.info(`[rsc-worker] htmlPath is undefined, using default Html component`); } try { const { Html } = await import('../../components/html.js'); HtmlComponent = Html; if (verbose2) { logger2?.info(`[rsc-worker] Successfully loaded default Html component`); } } catch (error) { logger2?.warn( `[rsc-worker] Error loading default Html component: ${error instanceof Error ? error.message : String(error)}` ); HtmlComponent = React.Fragment; } } else if (htmlPath) { if (verbose2) { logger2?.info(`[rsc-worker] Attempting to load custom Html component from: ${htmlPath}`); } const htmlId = `${htmlPath}#${htmlExportName}`; const isHtmlInvalidated = isModuleInvalidated(htmlPath); if (hasCachedComponent(htmlId) && !isHtmlInvalidated) { HtmlComponent = getCachedComponent(htmlId); if (verbose2) { logger2?.info( `[rsc-worker] Using cached Html component from: ${htmlPath}` ); } } else { if (isHtmlInvalidated && hasCachedComponent(htmlId)) { clearCachedComponent(htmlId); if (verbose2) { logger2?.info( `[rsc-worker] Cleared invalidated Html component cache: ${htmlPath}` ); } } if (verbose2) { logger2?.info(`[rsc-worker] Component not cached, calling resolveComponent with path: ${htmlPath}, exportName: ${htmlExportName}`); } const htmlResult = await resolveComponent({ componentPath: htmlPath, exportName: htmlExportName, loader }); if (htmlResult.type === "success") { HtmlComponent = htmlResult.component; cacheComponent(htmlId, HtmlComponent); if (verbose2) { logger2?.info( `[rsc-worker] Loaded and cached Html component from: ${htmlPath}` ); } } else { if (verbose2) { logger2?.warn( `[rsc-worker] Failed to load Html component from ${htmlPath}: ${htmlResult.error?.message || "Unknown error"}` ); logger2?.warn(`[rsc-worker] Html resolution error details:`, htmlResult.error); } HtmlComponent = React.Fragment; } } } else { try { const { Html } = await import('../../components/html.js'); HtmlComponent = Html; if (verbose2) { logger2?.info(`[rsc-worker] Using default Html component`); } } catch (error) { logger2?.warn( `[rsc-worker] Error loading default Html component: ${error}` ); } } return { PageComponent, pageProps, RootComponent, HtmlComponent }; } let storedFromWorker; let storedToWorker; async function messageHandler(msg) { if (msg.type === "INIT") { storedFromWorker = msg.dataPort; storedToWorker = msg.controlPort; if (storedFromWorker) { setMaxListenersOnPort(storedFromWorker, 500); } if (storedToWorker) { setMaxListenersOnPort(storedToWorker, 500); } } const effectiveHandlers = createHandlers(storedFromWorker, storedToWorker); try { if (verbose) { logger.info( `[rsc-worker] Received message: ${msg.type} for id: ${msg.id}` ); } switch (msg.type) { case "INIT": const currentStreamId = msg.options.id || msg.id; const renderUserOptions = workerUserOptions(msg.options); setRenderUserOptions(currentStreamId, renderUserOptions); if (verbose) { logger?.info( `[INIT] Set render state for renderId: ${currentStreamId}` ); logger?.info( `[INIT] renderUserOptions.build: ${JSON.stringify( renderUserOptions.build )}` ); } const userOptions = renderUserOptions; cleanupRender(currentStreamId); const isHeadless = msg.options.htmlPath === ""; const rscVariant = isHeadless ? "rsc-headless" : "rsc-full"; if (verbose) { logger.info( `[rsc-worker] Processing ${rscVariant} render for route: ${msg.options.route} with ID: ${currentStreamId}` ); } const rscStream = msg.rscStream || new PassThrough(); activeRenders.set(currentStreamId, rscStream); const moduleResolutionStartTime = performance.now(); const projectRoot = msg.options.projectRoot || userOptions.projectRoot || workerData.userOptions?.projectRoot || process.cwd(); const buildConfig = { server: msg.options.build?.server || userOptions.build?.server || DEFAULT_CONFIG.BUILD.server, client: msg.options.build?.client || userOptions.build?.client || DEFAULT_CONFIG.BUILD.client, static: msg.options.build?.static || userOptions.build?.static || DEFAULT_CONFIG.BUILD.static, outDir: msg.options.build?.outDir || userOptions.build?.outDir || DEFAULT_CONFIG.BUILD.outDir, assetsDir: msg.options.build?.assetsDir || userOptions.build?.assetsDir || DEFAULT_CONFIG.BUILD.assetsDir, rscOutputPath: msg.options.build?.rscOutputPath || userOptions.build?.rscOutputPath || DEFAULT_CONFIG.BUILD.rscOutputPath, htmlOutputPath: msg.options.build?.htmlOutputPath || userOptions.build?.htmlOutputPath || DEFAULT_CONFIG.BUILD.htmlOutputPath, preserveModulesRoot: msg.options.build?.preserveModulesRoot || userOptions.build?.preserveModulesRoot || DEFAULT_CONFIG.BUILD.preserveModulesRoot }; const serverManifest = workerData.serverManifest || msg.options.manifest || {}; if (verbose) { logger?.info( `[rsc-worker:${msg.options.route}] msg.options.build: ${JSON.stringify(msg.options.build)} msg.options.route userOptions.build: ${JSON.stringify(userOptions.build)} final buildConfig: ${JSON.stringify(buildConfig)}` ); } const loader = createRscWorkerLoader({ verbose: msg.options.verbose ?? workerData.userOptions.verbose, logger, projectRoot, manifest: serverManifest, build: buildConfig // bundle: workerData.bundle || {}, }); const url = msg.options.url || routeToURL( msg.options.route, userOptions.moduleBaseURL, userOptions.build?.rscOutputPath ?? DEFAULT_CONFIG.BUILD.rscOutputPath ); if (verbose) { logger?.info(`[rsc-worker:messageHandler] Component paths for route ${msg.options.route}:`); logger?.info(` pagePath: ${msg.options.pagePath}`); logger?.info(` propsPath: ${msg.options.propsPath}`); logger?.info(` rootPath: ${msg.options.rootPath}`); logger?.info(` htmlPath: ${msg.options.htmlPath}`); } const { PageComponent, pageProps, RootComponent, HtmlComponent } = await loadComponentsWithCache({ pagePath: msg.options.pagePath, propsPath: msg.options.propsPath, rootPath: msg.options.rootPath, htmlPath: msg.options.htmlPath, pageExportName: msg.options.pageExportName ?? userOptions.pageExportName, propsExportName: msg.options.propsExportName ?? userOptions.propsExportName, rootExportName: msg.options.rootExportName ?? userOptions.rootExportName, htmlExportName: msg.options.htmlExportName ?? userOptions.htmlExportName, url, loader, verbose, logger, panicThreshold: msg.options.panicThreshold, resolvedPageProps: msg.options.resolvedPageProps // Pre-resolved from main thread }); if (verbose) { logger.info( `[rsc-worker] Loaded components for route ${msg.options.route}:` ); logger.info(`[rsc-worker] - PageComponent: ${typeof PageComponent}`); logger.info(`[rsc-worker] - pageProps: ${JSON.stringify(pageProps)}`); logger.info(`[rsc-worker] - RootComponent: ${typeof RootComponent}`); logger.info(`[rsc-worker] - HtmlComponent: ${typeof HtmlComponent}`); } const moduleResolutionTime = performance.now() - moduleResolutionStartTime; if (effectiveHandlers.onMetrics) { const moduleResolutionMetric = createModuleResolutionMetrics({ route: msg.options.route, workerType: "rsc", resolutionTime: moduleResolutionTime, fromMainThread: false, fromRscWorker: true, fromHtmlWorker: false, description: `Module resolution for route ${msg.options.route}` }); effectiveHandlers.onMetrics(msg.id, moduleResolutionMetric); } const rpc = getRpc(); if (rpc && msg.options.pagePath) { try { const cssFileUserOptions = getUserOptions(); const collected = await rpc("collectCss", [msg.options.pagePath, projectRoot]) || []; for (const { id, code } of collected) { addCssFileContent(id, code, cssFileUserOptions); } if (verbose) { logger?.info( `[rsc-worker] runner CSS bridge: collected ${collected.length} file(s) for ${msg.options.pagePath}` ); } } catch (err) { if (verbose) { logger?.warn( `[rsc-worker] runner CSS bridge failed: ${String(err)}` ); } } } const messageCssFiles = new Map(msg.options.cssFiles || []); processInlineCssForState( messageCssFiles, addCssFileContent, userOptions ); const combinedCssFiles = combineCssFiles(cssFiles, messageCssFiles); const handlerOptions = { route: msg.options.route, url, pageProps, PageComponent, RootComponent, HtmlComponent, cssFiles: combinedCssFiles, globalCss: msg.options.globalCss ?? /* @__PURE__ */ new Map(), manifest: msg.options.manifest || {}, projectRoot: userOptions.projectRoot || workerData.userOptions?.projectRoot || process.cwd(), moduleBase: userOptions.moduleBase || workerData.userOptions?.moduleBase || DEFAULT_CONFIG.MODULE_BASE, moduleBasePath: userOptions.moduleBasePath || workerData.userOptions?.moduleBasePath || DEFAULT_CONFIG.MODULE_BASE_PATH, moduleBaseURL: userOptions.moduleBaseURL || workerData.userOptions?.moduleBaseURL || DEFAULT_CONFIG.MODULE_BASE_URL, moduleRootPath: userOptions.moduleRootPath || workerData.userOptions?.moduleRootPath, verbose: msg.options.verbose || verbose, logger, panicThreshold: msg.options.panicThreshold || "none", rscTimeout: msg.options.rscTimeout, serverPipeableStreamOptions: (() => { const options = msg.options.serverPipeableStreamOptions || workerData.userOptions?.serverPipeableStreamOptions; if (verbose) { logger.info( `[rsc-worker] ${isHeadless ? "Headless" : "Full"} stream serverPipeableStreamOptions: ${JSON.stringify( options )}` ); } return options; })() }; let result; const headlessError = headlessStreamErrors.get(msg.options.route); const shouldUseFallback = headlessError && !isHeadless; if (isHeadless) { headlessStreamErrors.delete(msg.options.route); } const shouldReuseElements = !isHeadless && msg.options.reuseHeadlessStreamId; let reusableElements = null; if (shouldReuseElements && msg.options.reuseHeadlessStreamId) { reusableElements = headlessStreamElements.get( msg.options.reuseHeadlessStreamId ); if (verbose) { if (reusableElements) { logger.info( `[rsc-worker] Found reusable elements from headless stream ${msg.options.reuseHeadlessStreamId}` ); } else if (msg.options.reuseHeadlessStreamId) { logger.info( `[rsc-worker] No reusable elements found for headless stream ${msg.options.reuseHeadlessStreamId}` ); } } } const isStaticGeneration = msg.options.build && (msg.options.build.outDir || msg.options.build.static); if (verbose) { logger.info( `[rsc-worker] Route: ${msg.options.route}, isHeadless: ${isHeadless}, isStaticGeneration: ${isStaticGeneration}` ); logger.info( `[rsc-worker] msg.options.build: ${JSON.stringify( msg.options.build )}` ); } if (shouldUseFallback) { if (verbose) { logger.info( `[rsc-worker] Using fallback components due to previous error: ${headlessError.message}` ); } const fallbackHandlerOptions = { ...handlerOptions, PageComponent: React.Fragment // Keep the original HtmlComponent to ensure bootstrapScripts are rendered }; result = handleRscRender( { ...fallbackHandlerOptions, id: currentStreamId, reuseHeadlessStreamId: msg.options.reuseHeadlessStreamId }, effectiveHandlers, void 0, headlessStreamElements, headlessStreamErrors ); } else { try { if (isHeadless) { if (verbose) { logger.info( `[rsc-worker] Creating headless RSC stream for route ${msg.options.route} (no HTML wrapper)` ); } const headlessHandlerOptions = { ...handlerOptions, HtmlComponent: React.Fragment // Headless RSC - no HTML wrapper }; result = handleRscRender( { ...headlessHandlerOptions, id: currentStreamId, reuseHeadlessStreamId: msg.options.reuseHeadlessStreamId }, effectiveHandlers, void 0, headlessStreamElements, headlessStreamErrors ); } else { if (shouldReuseElements && reusableElements) { if (verbose) { logger.info( `[rsc-worker] Creating full RSC stream with reused elements from headless stream for route ${msg.options.route}` ); } const reuseHandlerOptions = { ...handlerOptions, PageComponent: () => reusableElements // Reuse the headless stream's elements }; result = handleRscRender( { ...reuseHandlerOptions, id: currentStreamId, reuseHeadlessStreamId: msg.options.reuseHeadlessStreamId }, effectiveHandlers, void 0, headlessStreamElements, headlessStreamErrors ); } else { if (verbose) { logger.info( `[rsc-worker] Creating full RSC stream for route ${msg.options.route} (no element reuse)` ); } result = handleRscRender( { ...handlerOptions, id: currentStreamId, reuseHeadlessStreamId: msg.options.reuseHeadlessStreamId }, effectiveHandlers, void 0, headlessStreamElements, headlessStreamErrors ); } } } catch (error) { if (verbose) { logger.warn( `[rsc-worker] Original PageComponent failed during creation for route ${msg.options.route}: ${error}` ); } const fallbackHandlerOptions = { ...handlerOptions, PageComponent: React.Fragment // Keep the original HtmlComponent to ensure bootstrapScripts are rendered }; result = handleRscRender( { ...fallbackHandlerOptions, id: currentStreamId, reuseHeadlessStreamId: msg.options.reuseHeadlessStreamId }, effectiveHandlers, void 0, headlessStreamElements, headlessStreamErrors ); } } if (verbose) { logger.info( `[rsc-worker] Processing single-use stream ${currentStreamId} for route: ${msg.options.route}` ); } clearHeadlessError(msg.options.route); return result; case "RESOLVE_COMPONENTS": { const resolutionStartTime = performance.now(); const resolveUserOptions = getUserOptions(msg.id); try { if (verbose) { logger.info( `[rsc-worker] Resolving components for route: ${msg.route}` ); logger.info(`[rsc-worker] pagePath: ${msg.pagePath}`); logger.info(`[rsc-worker] propsPath: ${msg.propsPath}`); logger.info(`[rsc-worker] rootPath: ${msg.rootPath}`); logger.info(`[rsc-worker] htmlPath: ${msg.htmlPath}`); } if (verbose) { logger?.info( `[rsc-worker:${msg.route}] pagePath=${msg.pagePath}, propsPath=${msg.propsPath}` ); } const buildConfig2 = { server: resolveUserOptions.build?.server || "server", client: resolveUserOptions.build?.client || "client", static: resolveUserOptions.build?.static || "static", outDir: resolveUserOptions.build?.outDir || "dist" }; if (verbose) { logger?.info( `[RESOLVE_COMPONENTS] Using outDir: ${buildConfig2.outDir}` ); logger?.info( `[RESOLVE_COMPONENTS] renderId: ${msg.id}, hasRenderState: ${renderStates.has(msg.id)}` ); if (renderStates.has(msg.id)) { const renderState = renderStates.get(msg.id); logger?.info( `[RESOLVE_COMPONENTS] renderState.initialized: ${renderState.initialized}` ); logger?.info( `[RESOLVE_COMPONENTS] renderState.userOptions.build: ${JSON.stringify( renderState.userOptions.build )}` ); } } const loader2 = createRscWorkerLoader({ verbose, logger, projectRoot: workerData.userOptions?.projectRoot, manifest: workerData.serverManifest || {}, build: buildConfig2 }); await loadComponentsWithCache({ pagePath: msg.pagePath, propsPath: msg.propsPath, rootPath: msg.rootPath, htmlPath: msg.htmlPath, pageExportName: msg.pageExportName || "default", propsExportName: msg.propsExportName || "props", rootExportName: msg.rootExportName || "Root", htmlExportName: msg.htmlExportName || "Html", url: `/${msg.route}`, // Simple URL for component resolution loader: loader2, verbose, logger }); const resolutionTime = performance.now() - resolutionStartTime; if (verbose) { logger.info( `[rsc-worker] Components resolved for route: ${msg.route} in ${resolutionTime.toFixed(2)}ms` ); } const response = { type: "COMPONENTS_RESOLVED", id: msg.id, route: msg.route, resolutionTime }; parentPort?.postMessage(response); } catch (error) { logger.error( `[rsc-worker] Failed to resolve components for route ${msg.route}: ${error}` ); effectiveHandlers.onError(msg.id, toError(error), { route: msg.route, context: `Component resolution failed for route ${msg.route}` }); } return; } case "SERVER_ACTION": { try { const serverActionUserOptions = getUserOptions(); const [filePath2, exportName] = msg.id.split("#"); if (!filePath2 || !exportName) { throw new Error( `Invalid server action ID format: ${msg.id}. Expected format: "path/to/file.ts#exportName"` ); } const actionPath = filePath2.startsWith( serverActionUserOptions.moduleBasePath ) ? filePath2.slice(serverActionUserOptions.moduleBasePath.length) : filePath2; const fullPath = join( workerData.userOptions?.projectRoot, actionPath ); const module = await import(fullPath); const action = module[exportName]; if (typeof action !== "function") { throw new Error(`Server action not found: ${msg.id}`); } let decodedArgs = msg.args; if (msg.args.length === 1 && typeof msg.args[0] === "string") { try { const moduleBasePath = serverActionUserOptions.moduleBasePath ?? "/"; decodedArgs = await decodeReply(msg.args[0], moduleBasePath); if (verbose) { logger?.info(`[rsc-worker] Decoded server action args: ${JSON.stringify(decodedArgs)}`); } } catch { if (verbose) { logger?.info(`[rsc-worker] Using raw server action args`); } } } const result2 = await action(...decodedArgs); effectiveHandlers.onServerActionResponse?.(msg.id, result2); } catch (error) { const errorMessage = toError(error).message; effectiveHandlers.onServerActionResponse?.( msg.id, void 0, errorMessage ); } return; } case "INITIALIZED_REACT_LOADER": case "INITIALIZED_CSS_LOADER": case "INITIALIZED_ENV_LOADER": return; case "HMR_UPDATE": const filePath = msg.path || msg.id; const hmrProjectRoot = workerData.userOptions?.projectRoot || process.cwd(); const normalizedPath = filePath.startsWith(hmrProjectRoot) ? relative(hmrProjectRoot, filePath) : filePath; hmrState.set(normalizedPath, { timestamp: msg.timestamp || Date.now(), invalidated: true, routes: msg.routes || [] }); if (verbose) { logger?.info( `[rsc-worker] HMR_UPDATE: Invalidated module ${normalizedPath} (from ${filePath})` ); } if (headlessStreamElements.size > 0) { if (verbose) { logger?.info( `[rsc-worker] HMR_UPDATE: Clearing ${headlessStreamElements.size} cached headless stream elements` ); } headlessStreamElements.clear(); } clearAllCachedComponents(); if (verbose) { logger?.info( `[rsc-worker] HMR_UPDATE: Cleared all cached components from temporaryReferences` ); } const runner = getRunner(); if (runner) { runner.clearCache(); if (verbose) { logger?.info( `[rsc-worker] HMR_UPDATE: runner cache cleared (trigger: ${filePath})` ); } } headlessStreamErrors.clear(); effectiveHandlers.onHmrUpdate(normalizedPath, msg.routes || []); return; case "ABORT": return; case "HMR_CLEANUP": hmrState.delete(msg.id); effectiveHandlers.onHmrAccept(msg.id, msg.routes || []); return; case "CSS_FILE": if (msg.id) { const cssFileUserOptions = getUserOptions(); addCssFileContent(msg.id, msg.content, cssFileUserOptions); } effectiveHandlers.onCssFile?.(msg.id, msg.content); return; case "SERVER_MODULE": addModuleId(msg.id, msg.url); effectiveHandlers.onServerModule?.(msg.id, msg.url, msg.source); return; case "MODULE_REQUEST": { const { id, path } = msg; try { const module = await import(join(workerData.userOptions?.projectRoot, path)); effectiveHandlers.onServerModule?.(id, path, module); } catch (error) { effectiveHandlers.onError(id, toError(error)); } return; } case "SHUTDOWN": { effectiveHandlers.onShutdown?.(msg.id); if (parentPort) { sendMessage( { type: "SHUTDOWN_COMPLETE", id: msg.id }, parentPort ); } return; } default: { logger.info(`Unknown message: ${msg.type}`); return; } } } catch (error) { effectiveHandlers.onError("worker/rsc", toError(error)); effectiveHandlers.onEnd?.("worker/rsc"); effectiveHandlers.onShutdown?.("*"); } } export { messageHandler }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVzc2FnZUhhbmRsZXIuc2VydmVyLmpzIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wbHVnaW4vd29ya2VyL3JzYy9tZXNzYWdlSGFuZGxlci5zZXJ2ZXIudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHBhcmVudFBvcnQsIHdvcmtlckRhdGEgfSBmcm9tIFwibm9kZTp3b3JrZXJfdGhyZWFkc1wiO1xuaW1wb3J0IHsgUGFzc1Rocm91Z2ggfSBmcm9tIFwibm9kZTpzdHJlYW1cIjtcbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciB9IGZyb20gXCJ2aXRlXCI7XG5pbXBvcnQgeyBqb2luLCByZWxhdGl2ZSB9IGZyb20gXCJub2RlOnBhdGhcIjtcbmltcG9ydCB7IGNyZWF0ZVJzY1dvcmtlckxvYWRlciB9IGZyb20gXCIuL2NyZWF0ZVJzY1dvcmtlckxvYWRlci5qc1wiO1xuaW1wb3J0IHsgaGFuZGxlUnNjUmVuZGVyIH0gZnJvbSBcIi4vaGFuZGxlUnNjUmVuZGVyLnNlcnZlci5qc1wiO1xuaW1wb3J0IHsgc2V0TWF4TGlzdGVuZXJzT25Qb3J0IH0gZnJvbSBcIi4uLy4uL3N0cmVhbS9zZXRNYXhMaXN0ZW5lcnMuanNcIjtcbmltcG9ydCB0eXBlIHtcbiAgUnNjV29ya2VySW5wdXRNZXNzYWdlLFxuICBDb21wb25lbnRzUmVzb2x2ZWRNZXNzYWdlLFxufSBmcm9tIFwiLi90eXBlcy5qc1wiO1xuaW1wb3J0IHsgdG9FcnJvciB9IGZyb20gXCIuLi8uLi9lcnJvci90b0Vycm9yLmpzXCI7XG4vLyBJbXBvcnQgZGVjb2RlUmVwbHkgZm9yIHNlcnZlciBhY3Rpb24gYXJndW1lbnQgZGVjb2RpbmdcbmltcG9ydCB7IGRlY29kZVJlcGx5IH0gZnJvbSBcInJlYWN0LXNlcnZlci1kb20tZXNtL3NlcnZlclwiO1xuXG5pbXBvcnQgeyBjcmVhdGVIYW5kbGVycyB9IGZyb20gXCIuL2hhbmRsZXJzLmpzXCI7XG5pbXBvcnQge1xuICBhZGRDc3NGaWxlQ29udGVudCxcbiAgYWRkTW9kdWxlSWQsXG4gIGNzc0ZpbGVzLFxuICBobXJTdGF0ZSxcbiAgY2FjaGVDb21wb25lbnQsXG4gIGdldENhY2hlZENvbXBvbmVudCxcbiAgaGFzQ2FjaGVkQ29tcG9uZW50LFxuICBjbGVhckNhY2hlZENvbXBvbmVudCxcbiAgaXNNb2R1bGVJbnZhbGlkYXRlZCxcbiAgY2xlYXJBbGxDYWNoZWRDb21wb25lbnRzLFxufSBmcm9tIFwiLi9zdGF0ZS5zZXJ2ZXIuanNcIjtcbmltcG9ydCB7IGdldFJ1bm5lciwgZ2V0UnBjIH0gZnJvbSBcIi4vcnVubmVySW5zdGFuY2UuanNcIjtcbmltcG9ydCB7XG4gIGNvbWJpbmVDc3NGaWxlcyxcbiAgcHJvY2Vzc0lubGluZUNzc0ZvclN0YXRlLFxufSBmcm9tIFwiLi4vLi4vaGVscGVycy9jcmVhdGVVbmlmaWVkQ3NzUHJvY2Vzc29yLmpzXCI7XG5pbXBvcnQgeyByb3V0ZVRvVVJMIH0gZnJvbSBcIi4uLy4uL3V0aWxzL3JvdXRlVG9VUkwuanNcIjtcbmltcG9ydCB7IERFRkFVTFRfQ09ORklHIH0gZnJvbSBcIi4uLy4uL2NvbmZpZy9kZWZhdWx0cy5qc1wiO1xuaW1wb3J0IHsgcmVzb2x2ZVBhZ2VBbmRQcm9wcyB9IGZyb20gXCIuLi8uLi9oZWxwZXJzL3Jlc29sdmVQYWdlQW5kUHJvcHMuanNcIjtcbmltcG9ydCB7IHJlc29sdmVDb21wb25lbnQgfSBmcm9tIFwiLi4vLi4vaGVscGVycy9yZXNvbHZlQ29tcG9uZW50LmpzXCI7XG5pbXBvcnQgeyBoYW5kbGVFcnJvciB9IGZyb20gXCIuLi8uLi9lcnJvci9oYW5kbGVFcnJvci5qc1wiO1xuaW1wb3J0IHR5cGUgeyBQYW5pY1RocmVzaG9sZCB9IGZyb20gXCIuLi8uLi90eXBlcy5qc1wiO1xuaW1wb3J0IHsgUm9vdCBhcyBEZWZhdWx0Um9vdCB9IGZyb20gXCIuLi8uLi9jb21wb25lbnRzL3Jvb3QuanNcIjtcbmltcG9ydCB7IHdvcmtlclVzZXJPcHRpb25zIH0gZnJvbSBcIi4vd29ya2VyVXNlck9wdGlvbnMuanNcIjtcbmltcG9ydCB7IGh5ZHJhdGVVc2VyT3B0aW9ucyB9IGZyb20gXCIuLi8uLi9oZWxwZXJzL2h5ZHJhdGVVc2VyT3B0aW9ucy5qc1wiO1xuaW1wb3J0IHsgUmVhY3QgfSBmcm9tIFwiLi4vLi4vdmVuZG9yL3ZlbmRvci5zZXJ2ZXIuanNcIjtcbmltcG9ydCB7IHNlbmRNZXNzYWdlIH0gZnJvbSBcIi4uL3NlbmRNZXNzYWdlLmpzXCI7XG5pbXBvcnQgeyBjcmVhdGVNb2R1bGVSZXNvbHV0aW9uTWV0cmljcyB9IGZyb20gXCIuLi8uLi9tZXRyaWNzL2NyZWF0ZU1vZHVsZVJlc29sdXRpb25NZXRyaWNzLmpzXCI7XG5cbmNvbnN0IGxvZ2dlciA9IGNyZWF0ZUxvZ2dlcih3b3JrZXJEYXRhLnJlc29sdmVkQ29uZmlnPy5sb2dMZXZlbCA/PyBcImluZm9cIik7XG5jb25zdCB2ZXJib3NlID0gd29ya2VyRGF0YS51c2VyT3B0aW9ucy52ZXJib3NlID8/IGZhbHNlO1xuXG4vLyBBZGQgdW5jYXVnaHQgZXhjZXB0aW9uIGhhbmRsZXIgdG8gY2F0Y2ggUmVhY3QgcmVuZGVyaW5nIGVycm9yc1xucHJvY2Vzcy5vbihcInVuY2F1Z2h0RXhjZXB0aW9uXCIsIChlcnJvcikgPT4ge1xuICBpZiAodmVyYm9zZSkge1xuICAgIGxvZ2dlcj8uZXJyb3IoYFtSU0MtV09SS0VSXSBVbmNhdWdodCBleGNlcHRpb246ICR7ZXJyb3IubWVzc2FnZX1gLCB7XG4gICAgICBlcnJvcixcbiAgICB9KTtcbiAgfVxuXG4gIC8vIFNlbmQgZXJyb3IgdGhyb3VnaCBjb250cm9sIHBvcnQgaWYgd2UgaGF2ZSBhY3RpdmUgaGFuZGxlcnNcbiAgaWYgKHBhcmVudFBvcnQpIHtcbiAgICBwYXJlbnRQb3J0LnBvc3RNZXNzYWdlKHtcbiAgICAgIHR5cGU6IFwiRVJST1JcIixcbiAgICAgIGlkOiBcInVuY2F1Z2h0LWV4Y2VwdGlvblwiLFxuICAgICAgZXJyb3I6IHtcbiAgICAgICAgbWVzc2FnZTogZXJyb3IubWVzc2FnZSxcbiAgICAgICAgc3RhY2s6IGVycm9yLnN0YWNrLFxuICAgICAgICBuYW1lOiBlcnJvci5uYW1lLFxuICAgICAgfSxcbiAgICAgIGNvbnRleHQ6IFwiVW5jYXVnaHQgRXhjZXB0aW9uIGluIFJTQyBXb3JrZXJcIixcbiAgICB9KTtcbiAgfVxuXG4gIC8vIEV4aXQgdGhlIHdvcmtlciB3aXRoIGVycm9yIGNvZGVcbiAgcHJvY2Vzcy5leGl0KDEpO1xufSk7XG5cbi8vIFJlbmRlciBzdGF0ZSB0byB0cmFjayB1c2VyIG9wdGlvbnMgcGVyIHJlbmRlciBJRFxuY29uc3QgcmVuZGVyU3RhdGVzID0gbmV3IE1hcDxcbiAgc3RyaW5nLFxuICB7XG4gICAgdXNlck9wdGlvbnM6IGFueTtcbiAgICBpbml0aWFsaXplZDogYm9vbGVhbjtcbiAgfVxuPigpO1xuXG4vLyBDYWNoZSBmb3IgZmFsbGJhY2sgdXNlciBvcHRpb25zIHRvIGF2b2lkIHJlLWh5ZHJhdGluZ1xubGV0IGZhbGxiYWNrVXNlck9wdGlvbnM6IGFueSA9IG51bGw7XG5cbi8vIEhlbHBlciBmdW5jdGlvbiB0byBnZXQgdXNlciBvcHRpb25zIGZvciBhIHNwZWNpZmljIHJlbmRlclxuZnVuY3Rpb24gZ2V0VXNlck9wdGlvbnMocmVuZGVySWQ/OiBzdHJpbmcpIHtcbiAgLy8gSWYgd2UgaGF2ZSBhIHJlbmRlciBJRCBhbmQgaXQncyBpbml0aWFsaXplZCwgdXNlIHRoZSByZW5kZXItc3BlY2lmaWMgb3B0aW9uc1xuICBpZiAocmVuZGVySWQgJiYgcmVuZGVyU3RhdGVzLmhhcyhyZW5kZXJJZCkpIHtcbiAgICBjb25zdCByZW5kZXJTdGF0ZSA9IHJlbmRlclN0YXRlcy5nZXQocmVuZGVySWQpITtcbiAgICBpZiAocmVuZGVyU3RhdGUuaW5pdGlhbGl6ZWQpIHtcbiAgICAgIHJldHVybiByZW5kZXJTdGF0ZS51c2VyT3B0aW9ucztcbiAgICB9XG4gIH1cblxuICAvLyBVc2UgY2FjaGVkIGZhbGxiYWNrIGlmIGF2YWlsYWJsZVxuICBpZiAoZmFsbGJhY2tVc2VyT3B0aW9ucykge1xuICAgIHJldHVybiBmYWxsYmFja1VzZXJPcHRpb25zO1xuICB9XG5cbiAgLy8gRmFsbGJhY2sgdG8gd29ya2VyRGF0YSB1c2VyT3B0aW9ucyBpZiBubyByZW5kZXItc3BlY2lmaWMgb3B0aW9ucyBhcmUgYXZhaWxhYmxlXG4gIGNvbnN0IHVzZXJPcHRpb25zUmVzdWx0ID0gaHlkcmF0ZVVzZXJPcHRpb25zKHdvcmtlckRhdGEudXNlck9wdGlvbnMpO1xuICBpZiAodXNlck9wdGlvbnNSZXN1bHQudHlwZSA9PT0gXCJlcnJvclwiKSB7XG4gICAgdGhyb3cgdXNlck9wdGlvbnNSZXN1bHQuZXJyb3I7XG4gIH1cbiAgLy8gQ2FjaGUgdGhlIHJlc3VsdFxuICBmYWxsYmFja1VzZXJPcHRpb25zID0gdXNlck9wdGlvbnNSZXN1bHQudXNlck9wdGlvbnM7XG4gIGlmICh2ZXJib3NlKSB7XG4gICAgbG9nZ2VyPy5pbmZvKFxuICAgICAgYFtGQUxMQkFDS10gVXNpbmcgZmFsbGJhY2sgdXNlck9wdGlvbnMgZm9yIHJlbmRlcklkOiAke1xuICAgICAgICByZW5kZXJJZCB8fCBcIm5vbmVcIlxuICAgICAgfWBcbiAgICApO1xuICAgIGxvZ2dlcj8uaW5mbyhcbiAgICAgIGBbRkFMTEJBQ0tdIGZhbGxiYWNrVXNlck9wdGlvbnMuYnVpbGQ6ICR7SlNPTi5zdHJpbmdpZnkoXG4gICAgICAgIGZhbGxiYWNrVXNlck9wdGlvbnMuYnVpbGRcbiAgICAgICl9YFxuICAgICk7XG4gIH1cbiAgcmV0dXJuIGZhbGxiYWNrVXNlck9wdGlvbnM7XG59XG5cbi8vIEhlbHBlciBmdW5jdGlvbiB0byBzZXQgdXNlciBvcHRpb25zIGZvciBhIHNwZWNpZmljIHJlbmRlclxuZnVuY3Rpb24gc2V0UmVuZGVyVXNlck9wdGlvbnMocmVuZGVySWQ6IHN0cmluZywgdXNlck9wdGlvbnM6IGFueSkge1xuICByZW5kZXJTdGF0ZXMuc2V0KHJlbmRlcklkLCB7XG4gICAgdXNlck9wdGlvbnMsXG4gICAgaW5pdGlhbGl6ZWQ6IHRydWUsXG4gIH0pO1xufVxuXG4vLyBIZWxwZXIgZnVuY3Rpb24gdG8gY2xlYW51cCByZW5kZXIgc3RhdGVcbmZ1bmN0aW9uIGNsZWFudXBSZW5kZXJTdGF0ZShyZW5kZXJJZDogc3RyaW5nKSB7XG4gIHJlbmRlclN0YXRlcy5kZWxldGUocmVuZGVySWQpO1xufVxuXG4vLyBUcmFjayBhY3RpdmUgcmVuZGVycyAtIHN0b3JlIHRoZSBSU0Mgc3RyZWFtIGZvciBlYWNoIHVuaXF1ZSByZW5kZXIgKGlkKVxuY29uc3QgYWN0aXZlUmVuZGVycyA9IG5ldyBNYXA8c3RyaW5nLCBQYXNzVGhyb3VnaD4oKTtcblxuLy8gR2xvYmFsIHZhcmlhYmxlIHRvIHN0b3JlIHRoZSBjb3JyZWN0IGJ1aWxkIGNvbmZpZ3VyYXRpb24gZnJvbSBJTklUIGNhc2Vcbi8vIGxldCBjb3JyZWN0QnVpbGRDb25maWc6IGFueSA9IG51bGw7XG5cbi8vIFRyYWNrIGFjdGl2ZSBzdHJlYW1zIGJ5IHJvdXRlIGZvciByZXVzZVxuY29uc3QgYWN0aXZlU3RyZWFtc0J5Um91dGUgPSBuZXcgTWFwPHN0cmluZywgc3RyaW5nPigpOyAvLyByb3V0ZSAtPiBzdHJlYW1JZFxuXG4vLyBUcmFjayBoZWFkbGVzcyBzdHJlYW0gZWxlbWVudHMgYnkgc3RyZWFtIElEIGZvciByZXVzZSBpbiBmdWxsIHN0cmVhbXNcbmNvbnN0IGhlYWRsZXNzU3RyZWFtRWxlbWVudHMgPSBuZXcgTWFwPHN0cmluZywgYW55PigpOyAvLyBzdHJlYW1JZCAtPiBSZWFjdCBlbGVtZW50c1xuXG4vLyBUcmFjayBoZWFkbGVzcyBzdHJlYW0gZXJyb3JzIGJ5IHJvdXRlIGZvciBmdWxsIHN0cmVhbSBlcnJvciBwcm9wYWdhdGlvblxuY29uc3QgaGVhZGxlc3NTdHJlYW1FcnJvcnMgPSBuZXcgTWFwPHN0cmluZywgRXJyb3I+KCk7IC8vIHJvdXRlIC0+IGVycm9yXG5cblxuZnVuY3Rpb24gY2xlYW51cFJlbmRlcihpZDogc3RyaW5nKSB7XG4gIGNvbnN0IHJzY1N0cmVhbSA9IGFjdGl2ZVJlbmRlcnMuZ2V0KGlkKTtcbiAgaWYgKHJzY1N0cmVhbSkge1xuICAgIHJzY1N0cmVhbS5kZXN0cm95KCk7XG4gICAgYWN0aXZlUmVuZGVycy5kZWxldGUoaWQpO1xuXG4gICAgLy8gQ2xlYW4gdXAgcm91dGUgbWFwcGluZ1xuICAgIGZvciAoY29uc3QgW3JvdXRlLCBzdHJlYW1JZF0gb2YgYWN0aXZlU3RyZWFtc0J5Um91dGUuZW50cmllcygpKSB7XG4gICAgICBpZiAoc3RyZWFtSWQgPT09IGlkKSB7XG4gICAgICAgIGFjdGl2ZVN0cmVhbXNCeVJvdXRlLmRlbGV0ZShyb3V0ZSk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIENsZWFuIHVwIHJldXNhYmxlIGVsZW1lbnRzIG9ubHkgaWYgd2Uga25vdyB3aGljaCBoZWFkbGVzcyBzdHJlYW0gdGhpcyBmdWxsIHN0cmVhbSBjb25zdW1lZFxuICAgIC8vIFRoZSBjYWxsZXIgc2hvdWxkIHRlbGwgdXMgd2hpY2ggaGVhZGxlc3Mgc3RyZWFtIHRvIGNsZWFuIHVwLCBub3QgaW5mZXIgZnJvbSBJRCBwYXR0ZXJuc1xuICAgIC8vIE5vdGU6IEVsZW1lbnQgY2xlYW51cCBzaG91bGQgYmUgaGFuZGxlZCBleHBsaWNpdGx5IGJ5IHRoZSBzdHJlYW0gcmV1c2Ugc3lzdGVtXG4gIH1cblxuICAvLyBDbGVhbiB1cCByZW5kZXIgc3RhdGVcbiAgY2xlYW51cFJlbmRlclN0YXRlKGlkKTtcbn1cblxuZnVuY3Rpb24gY2xlYXJIZWFkbGVzc0Vycm9yKHJvdXRlOiBzdHJpbmcpIHtcbiAgaGVhZGxlc3NTdHJlYW1FcnJvcnMuZGVsZXRlKHJvdXRlKTtcbn1cblxuLyoqXG4gKiBIZWxwZXIgZnVuY3Rpb24gdG8gbG9hZCBjb21wb25lbnRzIHdpdGggY2FjaGluZ1xuICovXG5hc3luYyBmdW5jdGlvbiBsb2FkQ29tcG9uZW50c1dpdGhDYWNoZShvcHRpb25zOiB7XG4gIHBhZ2VQYXRoPzogc3RyaW5nO1xuICBwcm9wc1BhdGg/OiBzdHJpbmc7XG4gIHJvb3RQYXRoPzogc3RyaW5nO1xuICBodG1sUGF0aD86IHN0cmluZztcbiAgcGFnZUV4cG9ydE5hbWU/OiBzdHJpbmc7XG4gIHByb3BzRXhwb3J0TmFtZT86IHN0cmluZztcbiAgcm9vdEV4cG9ydE5hbWU/OiBzdHJpbmc7XG4gIGh0bWxFeHBvcnROYW1lPzogc3RyaW5nO1xuICB1cmw6IHN0cmluZztcbiAgbG9hZGVyOiBhbnk7XG4gIHZlcmJvc2U/OiBib29sZWFuO1xuICBsb2dnZXI/OiBhbnk7XG4gIHBhbmljVGhyZXNob2xkPzogUGFuaWNUaHJlc2hvbGQ7XG4gIHJlc29sdmVkUGFnZVByb3BzPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj47ICAvLyBQcmUtcmVzb2x2ZWQgcHJvcHMgZnJvbSBtYWluIHRocmVhZFxufSkge1xuICBjb25zdCB7XG4gICAgcGFnZVBhdGgsXG4gICAgcHJvcHNQYXRoLFxuICAgIHJvb3RQYXRoLFxuICAgIGh0bWxQYXRoLFxuICAgIHBhZ2VFeHBvcnROYW1lID0gXCJQYWdlXCIsXG4gICAgcHJvcHNFeHBvcnROYW1lID0gXCJwcm9wc1wiLFxuICAgIHJvb3RFeHBvcnROYW1lID0gXCJSb290XCIsXG4gICAgaHRtbEV4cG9ydE5hbWUgPSBcIkh0bWxcIixcbiAgICB1cmwsXG4gICAgbG9hZGVyLFxuICAgIHZlcmJvc2UsXG4gICAgbG9nZ2VyLFxuICAgIHBhbmljVGhyZXNob2xkID0gXCJub25lXCIsXG4gICAgcmVzb2x2ZWRQYWdlUHJvcHMsXG4gIH0gPSBvcHRpb25zO1xuICBcbiAgLy8gTm9ybWFsaXplIFVSTCBmb3IgY2FjaGUga2V5IC0gZW5zdXJlIHRyYWlsaW5nIHNsYXNoIGZvciBmb2xkZXIgcm91dGVzXG4gIC8vIFRoaXMgZW5zdXJlcyAvOG1tYy9sZXZlbHMgYW5kIC84bW1jL2xldmVscy8gdXNlIHRoZSBzYW1lIGNhY2hlIGtleVxuICBjb25zdCBub3JtYWxpemVVcmxGb3JDYWNoZSA9ICh1cmxTdHI6IHN0cmluZyk6IHN0cmluZyA9PiB7XG4gICAgLy8gRG9uJ3QgYWRkIHRyYWlsaW5nIHNsYXNoIHRvIHBhdGhzIHdpdGggZmlsZSBleHRlbnNpb25zXG4gICAgaWYgKHVybFN0ci5pbmNsdWRlcygnLicpKSByZXR1cm4gdXJsU3RyO1xuICAgIHJldHVybiB1cmxTdHIuZW5kc1dpdGgoJy8nKSA/IHVybFN0ciA6IHVybFN0ciArICcvJztcbiAgfTtcbiAgY29uc3Qgbm9ybWFsaXplZFVybCA9IG5vcm1hbGl6ZVVybEZvckNhY2hlKHVybCk7XG5cbiAgbGV0IFBhZ2VDb21wb25lbnQ6IGFueTtcbiAgbGV0IHBhZ2VQcm9wczogYW55ID0gcmVzb2x2ZWRQYWdlUHJvcHM7ICAvLyBVc2UgcHJlLXJlc29sdmVkIHByb3BzIGlmIGF2YWlsYWJsZVxuICBsZXQgUm9vdENvbXBvbmVudDogYW55O1xuICBsZXQgSHRtbENvbXBvbmVudDogYW55O1xuXG4gIC8vIExvYWQgcGFnZSBhbmQgcHJvcHMgdXNpbmcgdGhlIHVuaWZpZWQgaGVscGVyXG4gIGlmICh2ZXJib3NlKSB7XG4gICAgbG9nZ2VyPy5pbmZvKFxuICAgICAgYFtsb2FkQ29tcG9uZW50c1dpdGhDYWNoZV0gcGFnZVBhdGg9JHtwYWdlUGF0aH0sIHByb3BzUGF0aD0ke3Byb3BzUGF0aH0sIHVybD0ke3VybH1gXG4gICAgKTtcbiAgfVxuICBpZiAocGFnZVBhdGgpIHtcbiAgICBpZiAodmVyYm9zZSkge1xuICAgICAgbG9nZ2VyPy5pbmZvKFxuICAgICAgICBgW2xvYWRDb21wb25lbnRzV2l0aENhY2hlXSBMb2FkaW5nIHBhZ2UgYW5kIHByb3BzIGZvciBwYWdlUGF0aD0ke3BhZ2VQYXRofWBcbiAgICAgICk7XG4gICAgfVxuICAgIFxuICAgIC8vIENSSVRJQ0FMOiBDaGVjayBpZiBQYWdlIG1vZHVsZSBpcyBpbnZhbGlkYXRlZCBiZWZvcmUgdXNpbmcgY2FjaGVcbiAgICBjb25zdCBwYWdlSWQgPSBgJHtwYWdlUGF0aH0jJHtwYWdlRXhwb3J0TmFtZX1gO1xuICAgIGNvbnN0IGlzUGFnZUludmFsaWRhdGVkID0gaXNNb2R1bGVJbnZhbGlkYXRlZChwYWdlUGF0aCk7XG4gICAgXG4gICAgLy8gVHJhY2sgaWYgd2UgbmVlZCB0byBsb2FkIHByb3BzIHNlcGFyYXRlbHkgKHdoZW4gUGFnZSBpcyBjYWNoZWQgYnV0IHByb3BzIGZvciB0aGlzIFVSTCBhcmVuJ3QpXG4gICAgbGV0IG5lZWRUb0xvYWRQcm9wcyA9IGZhbHNlO1xuICAgIFxuICAgIC8vIENoZWNrIGNhY2hlIGZpcnN0LCBidXQgb25seSBpZiBub3QgaW52YWxpZGF0ZWQuXG4gICAgLy9cbiAgICAvLyBiZC01eHUgKDIwMjYtMDQtMzApOiB3ZSBjYWNoZSB0aGUgUGFnZSBjb21wb25lbnQgKGEgc3RhYmxlIGZ1bmN0aW9uIGZyb21cbiAgICAvLyB0aGUgc291cmNlIGZpbGUpIGJ1dCBOT1QgdGhlIHByb3BzIHJlc3VsdC4gUHJvcHMgZnVuY3Rpb25zIHJlYWQgbXV0YWJsZVxuICAgIC8vIHNlcnZlciBzdGF0ZSDigJQgREIgcm93cywgaW4tbWVtb3J5IHN0b3JlcywgcmVxdWVzdC1zY29wZWQgZGF0YSDigJQgYW5kIGFcbiAgICAvLyBjcm9zcy1yZXF1ZXN0IGNhY2hlIHNpbGVudGx5IHNlcnZlcyBzdGFsZSBkYXRhIGFmdGVyIGEgc2VydmVyIGFjdGlvblxuICAgIC8vIG11dGF0ZXMgdGhhdCBzdGF0ZS4gVGhlIHByZS1iZC01eHUgY29kZSBrZXllZCBwYWdlUHJvcHMgYnkgVVJMIGFuZCBvbmx5XG4gICAgLy8gaW52YWxpZGF0ZWQgb24gZmlsZSBjaGFuZ2UsIHNvIGUuZy4gZGVsZXRpbmcgYSB0b2RvIHZpYSBhIHNlcnZlciBhY3Rpb25cbiAgICAvLyB3YXMgY29ycmVjdGx5IHBlcnNpc3RlZCB0byB0aGUgREIgYnV0IHRoZSBuZXh0IC90b2Rvcy9pbmRleC5yc2Mgc3RpbGxcbiAgICAvLyByZXR1cm5lZCB0aGUgcHJlLWRlbGV0ZSBjYWNoZWQgcHJvcHMuIGRldjpyc2MgZGlkbid0IGdvIHRocm91Z2ggdGhpc1xuICAgIC8vIHdvcmtlciBwYXRoIHNvIGl0IGRpZG4ndCByZXBybyB0aGVyZS4gQWx3YXlzIHJlbG9hZCBwcm9wcyBpbiBkZXYuXG4gICAgaWYgKGhhc0NhY2hlZENvbXBvbmVudChwYWdlSWQpICYmICFpc1BhZ2VJbnZhbGlkYXRlZCkge1xuICAgICAgUGFnZUNvbXBvbmVudCA9IGdldENhY2hlZENvbXBvbmVudChwYWdlSWQpO1xuICAgICAgaWYgKHZlcmJvc2UpIHtcbiAgICAgICAgbG9nZ2VyPy5pbmZvKFxuICAgICAgICAgIGBbcnNjLXdvcmtlcl0gVXNpbmcgY2FjaGVkIFBhZ2UgY29tcG9uZW50IGZyb206ICR7cGFnZVBhdGh9YFxuICAgICAgICApO1xuICAgICAgfVxuXG4gICAgICBpZiAocHJvcHNQYXRoICYmICFyZXNvbHZlZFBhZ2VQcm9wcykge1xuICAgICAgICAvLyBQcm9wcyBhcmUgaW50ZW50aW9uYWxseSBOT1QgY2FjaGVkIGFjcm9zcyByZXF1ZXN0cyDigJQgc2VlIGNvbW1lbnRcbiAgICAgICAgLy8gYWJvdmUuIERyb3AgYW55IHByZS1leGlzdGluZyBjYWNoZSBlbnRyeSBmcm9tIG9sZGVyIHZlcnNpb25zLCB0aGVuXG4gICAgICAgIC8vIGZhbGwgdGhyb3VnaCB0byB0aGUgc2VwYXJhdGUtbG9hZCBwYXRoIGJl