nukejs 0.0.9 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/build-common.ts"],
4
- "sourcesContent": ["/**\r\n * build-common.ts \u2014 Shared Build Logic\r\n *\r\n * Used by both build-node.ts and build-vercel.ts.\r\n *\r\n * Exports:\r\n * \u2014 types : AnalyzedRoute, ServerPage, BuiltPage,\r\n * PageAdapterOptions, PageBundleOptions\r\n * \u2014 utility helpers : walkFiles, analyzeFile, isServerComponent,\r\n * findPageLayouts, extractDefaultExportName\r\n * \u2014 collection : collectServerPages, collectGlobalClientRegistry,\r\n * buildPerPageRegistry\r\n * \u2014 template codegen : makeApiAdapterSource, makePageAdapterSource\r\n * \u2014 bundle ops : bundleApiHandler, bundlePageHandler,\r\n * bundleClientComponents, buildPages,\r\n * buildCombinedBundle, copyPublicFiles\r\n */\r\n\r\nimport fs from 'fs';\r\nimport path from 'path';\r\nimport { randomBytes } from 'node:crypto';\r\nimport { fileURLToPath, pathToFileURL } from 'url';\r\nimport { build } from 'esbuild';\r\nimport { findClientComponentsInTree } from './component-analyzer';\r\n\r\n// \u2500\u2500\u2500 Node built-in externals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * All Node.js built-in module names.\r\n * Used as the `external` list when bundling for Node so esbuild never tries\r\n * to inline them, which would produce broken `require()` shims in ESM output.\r\n */\r\nconst NODE_BUILTINS = [\r\n 'node:*',\r\n 'http', 'https', 'fs', 'path', 'url', 'crypto', 'stream', 'buffer',\r\n 'events', 'util', 'os', 'net', 'tls', 'child_process', 'worker_threads',\r\n 'cluster', 'dgram', 'dns', 'readline', 'zlib', 'assert', 'module',\r\n 'perf_hooks', 'string_decoder', 'timers', 'async_hooks', 'v8', 'vm',\r\n];\r\n\r\n// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nexport interface AnalyzedRoute {\r\n /** Regex string matching the URL path, e.g. '^/users/([^/]+)$' */\r\n srcRegex: string;\r\n /** Names of captured groups in srcRegex order */\r\n paramNames: string[];\r\n /**\r\n * Subset of paramNames that are catch-all ([...slug] or [[...path]]).\r\n * Their runtime values are string[] not string.\r\n */\r\n catchAllNames: string[];\r\n /** Function namespace path, e.g. '/api/users' or '/page/about' */\r\n funcPath: string;\r\n specificity: number;\r\n}\r\n\r\nexport interface ServerPage extends AnalyzedRoute {\r\n absPath: string;\r\n}\r\n\r\nexport interface BuiltPage extends ServerPage {\r\n bundleText: string;\r\n}\r\n\r\n// \u2500\u2500\u2500 File walker \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nexport function walkFiles(dir: string, base: string = dir): string[] {\r\n if (!fs.existsSync(dir)) return [];\r\n const results: string[] = [];\r\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\r\n const full = path.join(dir, entry.name);\r\n if (entry.isDirectory()) {\r\n results.push(...walkFiles(full, base));\r\n } else if (entry.name.endsWith('.ts') || entry.name.endsWith('.tsx')) {\r\n results.push(path.relative(base, full));\r\n }\r\n }\r\n return results;\r\n}\r\n\r\n// \u2500\u2500\u2500 Route analysis \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Parses dynamic-route segments from a relative file path and returns a regex,\r\n * captured param names, catch-all param names, a function path, and a\r\n * specificity score.\r\n *\r\n * Supported patterns per segment:\r\n * [[...name]] optional catch-all \u2192 regex (.*) \u2192 string[]\r\n * [...name] required catch-all \u2192 regex (.+) \u2192 string[]\r\n * [[name]] optional single \u2192 regex ([^/]*)? \u2192 string\r\n * [name] required single \u2192 regex ([^/]+) \u2192 string\r\n * literal static \u2192 escaped literal\r\n *\r\n * @param relPath Relative path from the dir root (e.g. 'users/[id].tsx').\r\n * @param prefix Namespace for funcPath ('api' | 'page').\r\n */\r\nexport function analyzeFile(relPath: string, prefix = 'api'): AnalyzedRoute {\r\n const normalized = relPath.replace(/\\\\/g, '/').replace(/\\.(tsx?)$/, '');\r\n let segments = normalized.split('/');\r\n if (segments.at(-1) === 'index') segments = segments.slice(0, -1);\r\n\r\n const paramNames: string[] = [];\r\n const catchAllNames: string[] = [];\r\n const regexParts: string[] = [];\r\n let specificity = 0;\r\n\r\n for (const seg of segments) {\r\n const optCatchAll = seg.match(/^\\[\\[\\.\\.\\.(.+)\\]\\]$/);\r\n if (optCatchAll) {\r\n paramNames.push(optCatchAll[1]);\r\n catchAllNames.push(optCatchAll[1]);\r\n regexParts.push('(.*)');\r\n specificity += 1;\r\n continue;\r\n }\r\n const catchAll = seg.match(/^\\[\\.\\.\\.(.+)\\]$/);\r\n if (catchAll) {\r\n paramNames.push(catchAll[1]);\r\n catchAllNames.push(catchAll[1]);\r\n regexParts.push('(.+)');\r\n specificity += 10;\r\n continue;\r\n }\r\n const optDynamic = seg.match(/^\\[\\[([^.][^\\]]*)\\]\\]$/);\r\n if (optDynamic) {\r\n paramNames.push(optDynamic[1]);\r\n regexParts.push('__OPT__([^/]*)'); // marker \u2014 resolved below\r\n specificity += 30;\r\n continue;\r\n }\r\n const dynamic = seg.match(/^\\[(.+)\\]$/);\r\n if (dynamic) {\r\n paramNames.push(dynamic[1]);\r\n regexParts.push('([^/]+)');\r\n specificity += 100;\r\n continue;\r\n }\r\n regexParts.push(seg.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'));\r\n specificity += 1000;\r\n }\r\n\r\n // Build the regex string.\r\n // __OPT__(...) markers indicate optional single segments where the preceding\r\n // slash must also be optional (e.g. users/[[id]] should match /users).\r\n let srcRegex: string;\r\n if (segments.length === 0) {\r\n srcRegex = '^/$';\r\n } else {\r\n let body = '';\r\n for (let i = 0; i < regexParts.length; i++) {\r\n const part = regexParts[i];\r\n if (part.startsWith('__OPT__')) {\r\n const cap = part.slice(7);\r\n // At position 0, ^/ already provides the leading slash\r\n body += i === 0 ? cap : `(?:/${cap})?`;\r\n } else {\r\n body += (i === 0 ? '' : '/') + part;\r\n }\r\n }\r\n srcRegex = '^/' + body + '$';\r\n }\r\n\r\n const funcSegments = normalized.split('/');\r\n if (funcSegments.at(-1) === 'index') funcSegments.pop();\r\n const funcPath = funcSegments.length === 0\r\n ? `/${prefix}/_index`\r\n : `/${prefix}/` + funcSegments.join('/');\r\n\r\n return { srcRegex, paramNames, catchAllNames, funcPath, specificity };\r\n}\r\n\r\n// \u2500\u2500\u2500 Server-component detection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Returns true when a file does NOT begin with a \"use client\" directive,\r\n * i.e. it is a server component.\r\n */\r\nexport function isServerComponent(filePath: string): boolean {\r\n const content = fs.readFileSync(filePath, 'utf-8');\r\n for (const line of content.split('\\n').slice(0, 5)) {\r\n const trimmed = line.trim();\r\n if (!trimmed || trimmed.startsWith('//') || trimmed.startsWith('/*')) continue;\r\n if (/^[\"']use client[\"'];?$/.test(trimmed)) return false;\r\n break;\r\n }\r\n return true;\r\n}\r\n\r\n// \u2500\u2500\u2500 Layout discovery \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Walks from the pages root to the directory containing `routeFilePath` and\r\n * returns every layout.tsx found, in outermost-first order.\r\n */\r\nexport function findPageLayouts(routeFilePath: string, pagesDir: string): string[] {\r\n const layouts: string[] = [];\r\n\r\n const rootLayout = path.join(pagesDir, 'layout.tsx');\r\n if (fs.existsSync(rootLayout)) layouts.push(rootLayout);\r\n\r\n const relativePath = path.relative(pagesDir, path.dirname(routeFilePath));\r\n if (!relativePath || relativePath === '.') return layouts;\r\n\r\n const segments = relativePath.split(path.sep).filter(Boolean);\r\n for (let i = 1; i <= segments.length; i++) {\r\n const layoutPath = path.join(pagesDir, ...segments.slice(0, i), 'layout.tsx');\r\n if (fs.existsSync(layoutPath)) layouts.push(layoutPath);\r\n }\r\n\r\n return layouts;\r\n}\r\n\r\n/**\r\n * Extracts the identifier used as the default export from a component file.\r\n * Returns null when no default export is found.\r\n *\r\n * Handles three formats so that components compiled by esbuild are recognised\r\n * alongside hand-written source files:\r\n * 1. Source: `export default function Foo` / `export default Foo`\r\n * 2. esbuild: `var Foo_default = Foo` (compiled arrow-function component)\r\n * 3. Re-export: `export { Foo as default }`\r\n */\r\nexport function extractDefaultExportName(filePath: string): string | null {\r\n const content = fs.readFileSync(filePath, 'utf-8');\r\n\r\n // Format 1 \u2013 source: `export default function Foo` or `export default Foo`\r\n let m = content.match(/export\\s+default\\s+(?:function\\s+)?(\\w+)/);\r\n if (m?.[1]) return m[1];\r\n\r\n // Format 2 \u2013 esbuild compiled: `var Foo_default = Foo`\r\n m = content.match(/var\\s+\\w+_default\\s*=\\s*(\\w+)/);\r\n if (m?.[1]) return m[1];\r\n\r\n // Format 3 \u2013 explicit re-export: `export { Foo as default }`\r\n m = content.match(/export\\s*\\{[^}]*\\b(\\w+)\\s+as\\s+default\\b[^}]*\\}/);\r\n if (m?.[1] && !m[1].endsWith('_default')) return m[1];\r\n\r\n return null;\r\n}\r\n\r\n// \u2500\u2500\u2500 Server page collection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Returns all server-component pages inside `pagesDir`, sorted most-specific\r\n * first so precise routes shadow catch-alls in routers.\r\n * layout.tsx files and \"use client\" files are excluded.\r\n */\r\nexport function collectServerPages(pagesDir: string): ServerPage[] {\r\n if (!fs.existsSync(pagesDir)) return [];\r\n return walkFiles(pagesDir)\r\n .filter(relPath => {\r\n if (path.basename(relPath, path.extname(relPath)) === 'layout') return false;\r\n return isServerComponent(path.join(pagesDir, relPath));\r\n })\r\n .map(relPath => ({\r\n ...analyzeFile(relPath, 'page'),\r\n absPath: path.join(pagesDir, relPath),\r\n }))\r\n .sort((a, b) => b.specificity - a.specificity);\r\n}\r\n\r\n/**\r\n * Walks every server page and its layout chain to collect all client component\r\n * IDs reachable anywhere in the app.\r\n */\r\nexport function collectGlobalClientRegistry(\r\n serverPages: ServerPage[],\r\n pagesDir: string,\r\n): Map<string, string> {\r\n const registry = new Map<string, string>();\r\n for (const { absPath } of serverPages) {\r\n for (const [id, p] of findClientComponentsInTree(absPath, pagesDir))\r\n registry.set(id, p);\r\n for (const layoutPath of findPageLayouts(absPath, pagesDir))\r\n for (const [id, p] of findClientComponentsInTree(layoutPath, pagesDir))\r\n registry.set(id, p);\r\n }\r\n return registry;\r\n}\r\n\r\n// \u2500\u2500\u2500 Per-page registry \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Builds the per-page client component registry (page + its layout chain)\r\n * and returns both the id\u2192path map and the name\u2192id map needed by\r\n * bundlePageHandler.\r\n */\r\nexport function buildPerPageRegistry(\r\n absPath: string,\r\n layoutPaths: string[],\r\n pagesDir: string,\r\n): { registry: Map<string, string>; clientComponentNames: Record<string, string> } {\r\n const registry = new Map<string, string>();\r\n\r\n for (const [id, p] of findClientComponentsInTree(absPath, pagesDir))\r\n registry.set(id, p);\r\n for (const lp of layoutPaths)\r\n for (const [id, p] of findClientComponentsInTree(lp, pagesDir))\r\n registry.set(id, p);\r\n\r\n const clientComponentNames: Record<string, string> = {};\r\n for (const [id, filePath] of registry) {\r\n const name = extractDefaultExportName(filePath);\r\n if (name) clientComponentNames[name] = id;\r\n }\r\n\r\n return { registry, clientComponentNames };\r\n}\r\n\r\n// \u2500\u2500\u2500 High-level page builder \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Runs both passes of the page build:\r\n *\r\n * Pass 1 \u2014 bundles all client components to `staticDir/__client-component/`\r\n * and collects pre-rendered HTML for each.\r\n * Pass 2 \u2014 bundles every server-component page into a self-contained ESM\r\n * handler and returns the results as `BuiltPage[]`.\r\n */\r\nexport async function buildPages(\r\n pagesDir: string,\r\n staticDir: string,\r\n): Promise<BuiltPage[]> {\r\n const serverPages = collectServerPages(pagesDir);\r\n\r\n if (fs.existsSync(pagesDir) && walkFiles(pagesDir).length > 0 && serverPages.length === 0) {\r\n console.warn(`\u26A0 Pages found in ${pagesDir} but none are server components`);\r\n }\r\n\r\n if (serverPages.length === 0) return [];\r\n\r\n const globalRegistry = collectGlobalClientRegistry(serverPages, pagesDir);\r\n const prerenderedHtml = await bundleClientComponents(globalRegistry, pagesDir, staticDir);\r\n const prerenderedRecord = Object.fromEntries(prerenderedHtml);\r\n\r\n const builtPages: BuiltPage[] = [];\r\n\r\n for (const page of serverPages) {\r\n console.log(` building ${page.absPath} \u2192 ${page.funcPath} [page]`);\r\n\r\n const layoutPaths = findPageLayouts(page.absPath, pagesDir);\r\n const { registry, clientComponentNames } = buildPerPageRegistry(page.absPath, layoutPaths, pagesDir);\r\n\r\n const bundleText = await bundlePageHandler({\r\n absPath: page.absPath,\r\n pagesDir,\r\n clientComponentNames,\r\n allClientIds: [...registry.keys()],\r\n layoutPaths,\r\n prerenderedHtml: prerenderedRecord,\r\n catchAllNames: page.catchAllNames,\r\n });\r\n\r\n builtPages.push({ ...page, bundleText });\r\n }\r\n\r\n return builtPages;\r\n}\r\n\r\n// \u2500\u2500\u2500 API adapter template \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Returns the TypeScript source for a thin HTTP adapter that wraps an API\r\n * route module and exposes a single `handler(req, res)` default export.\r\n */\r\nexport function makeApiAdapterSource(handlerFilename: string): string {\r\n return `\\\r\nimport type { IncomingMessage, ServerResponse } from 'http';\r\nimport * as mod from ${JSON.stringify('./' + handlerFilename)};\r\n\r\nfunction enhance(res: ServerResponse) {\r\n (res as any).json = function (data: any, status = 200) {\r\n this.statusCode = status;\r\n this.setHeader('Content-Type', 'application/json');\r\n this.end(JSON.stringify(data));\r\n };\r\n (res as any).status = function (code: number) { this.statusCode = code; return this; };\r\n return res;\r\n}\r\n\r\nasync function parseBody(req: IncomingMessage): Promise<any> {\r\n return new Promise((resolve, reject) => {\r\n let body = '';\r\n req.on('data', chunk => { body += chunk.toString(); });\r\n req.on('end', () => {\r\n try {\r\n resolve(body && req.headers['content-type']?.includes('application/json')\r\n ? JSON.parse(body) : body);\r\n } catch (e) { reject(e); }\r\n });\r\n req.on('error', reject);\r\n });\r\n}\r\n\r\nexport default async function handler(req: IncomingMessage, res: ServerResponse) {\r\n const method = (req.method || 'GET').toUpperCase();\r\n const apiRes = enhance(res);\r\n const apiReq = req as any;\r\n\r\n apiReq.body = await parseBody(req);\r\n apiReq.query = Object.fromEntries(new URL(req.url || '/', 'http://localhost').searchParams);\r\n apiReq.params = apiReq.query;\r\n\r\n const fn = (mod as any)[method] ?? (mod as any).default;\r\n if (typeof fn !== 'function') {\r\n (apiRes as any).json({ error: \\`Method \\${method} not allowed\\` }, 405);\r\n return;\r\n }\r\n await fn(apiReq, apiRes);\r\n}\r\n`;\r\n}\r\n\r\n// \u2500\u2500\u2500 Page adapter template \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nexport interface PageAdapterOptions {\r\n /** e.g. './home.tsx' \u2014 relative import for the page default export */\r\n pageImport: string;\r\n /** Newline-joined import statements for layout components */\r\n layoutImports: string;\r\n /** function-name \u2192 cc_id map, computed at build time */\r\n clientComponentNames: Record<string, string>;\r\n /** All client component IDs reachable from this page */\r\n allClientIds: string[];\r\n /** Comma-separated list of __layout_N__ identifiers */\r\n layoutArrayItems: string;\r\n /** Pre-rendered HTML per client component ID, computed at build time */\r\n prerenderedHtml: Record<string, string>;\r\n /** Catch-all param names whose runtime values are string[] not string */\r\n catchAllNames: string[];\r\n}\r\n\r\n/**\r\n * Returns the TypeScript source for a fully self-contained page handler.\r\n *\r\n * The adapter:\r\n * \u2022 Inlines the html-store so useHtml() works without external deps.\r\n * \u2022 Contains an async recursive renderer for server + client components.\r\n * \u2022 Client components are identified via the pre-computed CLIENT_COMPONENTS\r\n * map \u2014 no fs.readFileSync at runtime.\r\n * \u2022 Emits the full HTML document including the __n_data blob and bootstrap.\r\n */\r\nexport function makePageAdapterSource(opts: PageAdapterOptions): string {\r\n const {\r\n pageImport, layoutImports, clientComponentNames, allClientIds,\r\n layoutArrayItems, prerenderedHtml, catchAllNames,\r\n } = opts;\r\n\r\n return `\\\r\nimport type { IncomingMessage, ServerResponse } from 'http';\r\nimport { createElement as __createElement__ } from 'react';\r\nimport { renderToString as __renderToString__ } from 'react-dom/server';\r\nimport * as __page__ from ${pageImport};\r\n${layoutImports}\r\n\r\nconst CLIENT_COMPONENTS: Record<string, string> = ${JSON.stringify(clientComponentNames)};\r\nconst ALL_CLIENT_IDS: string[] = ${JSON.stringify(allClientIds)};\r\nconst PRERENDERED_HTML: Record<string, string> = ${JSON.stringify(prerenderedHtml)};\r\nconst CATCH_ALL_NAMES = new Set(${JSON.stringify(catchAllNames)});\r\n\r\n// \u2500\u2500\u2500 html-store (inlined) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\ntype TitleValue = string | ((prev: string) => string);\r\ninterface HtmlStore {\r\n titleOps: TitleValue[];\r\n htmlAttrs: Record<string, string | undefined>;\r\n bodyAttrs: Record<string, string | undefined>;\r\n meta: Record<string, string | undefined>[];\r\n link: Record<string, string | undefined>[];\r\n script: Record<string, any>[];\r\n style: { content?: string; media?: string }[];\r\n}\r\nconst __STORE_KEY__ = Symbol.for('__nukejs_html_store__');\r\nconst __getStore = (): HtmlStore | null => (globalThis as any)[__STORE_KEY__] ?? null;\r\nconst __setStore = (s: HtmlStore | null): void => { (globalThis as any)[__STORE_KEY__] = s; };\r\nconst __emptyStore = (): HtmlStore =>\r\n ({ titleOps: [], htmlAttrs: {}, bodyAttrs: {}, meta: [], link: [], script: [], style: [] });\r\nasync function runWithHtmlStore(fn: () => Promise<void>): Promise<HtmlStore> {\r\n __setStore(__emptyStore());\r\n try { await fn(); return { ...(__getStore() ?? __emptyStore()) }; }\r\n finally { __setStore(null); }\r\n}\r\nfunction resolveTitle(ops: TitleValue[], fallback = ''): string {\r\n let t = fallback;\r\n for (let i = ops.length - 1; i >= 0; i--) {\r\n const op = ops[i]; t = typeof op === 'string' ? op : op(t);\r\n }\r\n return t;\r\n}\r\n\r\n// \u2500\u2500\u2500 HTML helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nfunction escapeHtml(s: string): string {\r\n return String(s)\r\n .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;').replace(/'/g, '&#039;');\r\n}\r\nfunction escapeAttr(s: string): string {\r\n return String(s).replace(/&/g, '&amp;').replace(/\"/g, '&quot;');\r\n}\r\nfunction renderAttrs(attrs: Record<string, string | boolean | undefined>): string {\r\n return Object.entries(attrs)\r\n .filter(([, v]) => v !== undefined && v !== false)\r\n .map(([k, v]) => v === true ? k : \\`\\${k}=\"\\${escapeAttr(String(v))}\"\\`)\r\n .join(' ');\r\n}\r\nfunction openTag(tag: string, attrs: Record<string, string | undefined>): string {\r\n const s = renderAttrs(attrs as Record<string, string | boolean | undefined>);\r\n return s ? \\`<\\${tag} \\${s}>\\` : \\`<\\${tag}>\\`;\r\n}\r\nfunction renderMetaTag(tag: Record<string, string | undefined>): string {\r\n const key = (k: string) => k === 'httpEquiv' ? 'http-equiv' : k;\r\n const attrs: Record<string, string | undefined> = {};\r\n for (const [k, v] of Object.entries(tag)) if (v !== undefined) attrs[key(k)] = v;\r\n return \\` <meta \\${renderAttrs(attrs as any)} />\\`;\r\n}\r\nfunction renderLinkTag(tag: Record<string, string | undefined>): string {\r\n const key = (k: string) => k === 'hrefLang' ? 'hreflang' : k === 'crossOrigin' ? 'crossorigin' : k;\r\n const attrs: Record<string, string | undefined> = {};\r\n for (const [k, v] of Object.entries(tag)) if (v !== undefined) attrs[key(k)] = v;\r\n return \\` <link \\${renderAttrs(attrs as any)} />\\`;\r\n}\r\nfunction renderScriptTag(tag: any): string {\r\n const s = renderAttrs({ src: tag.src, type: tag.type, crossorigin: tag.crossOrigin,\r\n integrity: tag.integrity, defer: tag.defer, async: tag.async, nomodule: tag.noModule });\r\n return \\` \\${s ? \\`<script \\${s}>\\` : '<script>'}\\${tag.src ? '' : (tag.content ?? '')}</script>\\`;\r\n}\r\nfunction renderStyleTag(tag: any): string {\r\n const media = tag.media ? \\` media=\"\\${escapeAttr(tag.media)}\"\\` : '';\r\n return \\` <style\\${media}>\\${tag.content ?? ''}</style>\\`;\r\n}\r\n\r\n// \u2500\u2500\u2500 Renderer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nconst VOID_TAGS = new Set([\r\n 'area','base','br','col','embed','hr','img','input',\r\n 'link','meta','param','source','track','wbr',\r\n]);\r\n\r\nfunction serializeProps(value: any): any {\r\n if (value == null || typeof value !== 'object') return value;\r\n if (typeof value === 'function') return undefined;\r\n if (Array.isArray(value)) return value.map(serializeProps).filter((v: any) => v !== undefined);\r\n if ((value as any).$$typeof) {\r\n const { type, props: p } = value as any;\r\n if (typeof type === 'string') return { __re: 'html', tag: type, props: serializeProps(p) };\r\n if (typeof type === 'function') {\r\n const cid = CLIENT_COMPONENTS[type.name];\r\n if (cid) return { __re: 'client', componentId: cid, props: serializeProps(p) };\r\n }\r\n return undefined;\r\n }\r\n const out: any = {};\r\n for (const [k, v] of Object.entries(value as Record<string, any>)) {\r\n const s = serializeProps(v);\r\n if (s !== undefined) out[k] = s;\r\n }\r\n return out;\r\n}\r\n\r\nasync function renderNode(node: any, hydrated: Set<string>): Promise<string> {\r\n if (node == null || typeof node === 'boolean') return '';\r\n if (typeof node === 'string') return escapeHtml(node);\r\n if (typeof node === 'number') return String(node);\r\n if (Array.isArray(node)) return (await Promise.all(node.map(n => renderNode(n, hydrated)))).join('');\r\n\r\n const { type, props } = node as { type: any; props: Record<string, any> };\r\n if (!type) return '';\r\n\r\n if (type === Symbol.for('react.fragment')) return renderNode(props?.children ?? null, hydrated);\r\n\r\n if (typeof type === 'string') {\r\n const { children, dangerouslySetInnerHTML, ...rest } = props || {};\r\n const attrParts: string[] = [];\r\n for (const [k, v] of Object.entries(rest as Record<string, any>)) {\r\n const name = k === 'className' ? 'class' : k === 'htmlFor' ? 'for' : k;\r\n if (typeof v === 'boolean') { if (v) attrParts.push(name); continue; }\r\n if (v == null) continue;\r\n if (k === 'style' && typeof v === 'object') {\r\n const css = Object.entries(v as Record<string, any>)\r\n .map(([p, val]) => \\`\\${p.replace(/[A-Z]/g, m => \\`-\\${m.toLowerCase()}\\`)}:\\${escapeHtml(String(val))}\\`)\r\n .join(';');\r\n attrParts.push(\\`style=\"\\${css}\"\\`);\r\n continue;\r\n }\r\n attrParts.push(\\`\\${name}=\"\\${escapeHtml(String(v))}\"\\`);\r\n }\r\n const attrStr = attrParts.length ? ' ' + attrParts.join(' ') : '';\r\n if (VOID_TAGS.has(type)) return \\`<\\${type}\\${attrStr} />\\`;\r\n const inner = dangerouslySetInnerHTML\r\n ? (dangerouslySetInnerHTML as any).__html\r\n : await renderNode(children ?? null, hydrated);\r\n return \\`<\\${type}\\${attrStr}>\\${inner}</\\${type}>\\`;\r\n }\r\n\r\n if (typeof type === 'function') {\r\n const clientId = CLIENT_COMPONENTS[type.name];\r\n if (clientId) {\r\n hydrated.add(clientId);\r\n const serializedProps = serializeProps(props ?? {});\r\n let ssrHtml: string;\r\n try {\r\n ssrHtml = __renderToString__(__createElement__(type as any, props || {}));\r\n } catch {\r\n ssrHtml = PRERENDERED_HTML[clientId] ?? '';\r\n }\r\n return \\`<span data-hydrate-id=\"\\${clientId}\" data-hydrate-props=\"\\${escapeHtml(JSON.stringify(serializedProps))}\">\\${ssrHtml}</span>\\`;\r\n }\r\n const instance = type.prototype?.isReactComponent ? new (type as any)(props) : null;\r\n return renderNode(instance ? instance.render() : await (type as Function)(props || {}), hydrated);\r\n }\r\n\r\n return '';\r\n}\r\n\r\n// \u2500\u2500\u2500 Layout wrapping \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nconst LAYOUT_COMPONENTS: Array<(props: any) => any> = [${layoutArrayItems}];\r\n\r\nfunction wrapWithLayouts(element: any): any {\r\n let el = element;\r\n for (let i = LAYOUT_COMPONENTS.length - 1; i >= 0; i--)\r\n el = { type: LAYOUT_COMPONENTS[i], props: { children: el }, key: null, ref: null };\r\n return el;\r\n}\r\n\r\n// \u2500\u2500\u2500 Handler \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport default async function handler(req: IncomingMessage, res: ServerResponse): Promise<void> {\r\n try {\r\n const parsed = new URL(req.url || '/', 'http://localhost');\r\n const params: Record<string, string | string[]> = {};\r\n parsed.searchParams.forEach((_, k) => {\r\n params[k] = CATCH_ALL_NAMES.has(k)\r\n ? parsed.searchParams.getAll(k)\r\n : parsed.searchParams.get(k) as string;\r\n });\r\n const url = req.url || '/';\r\n\r\n const hydrated = new Set<string>();\r\n const wrapped = wrapWithLayouts({ type: __page__.default, props: params as any, key: null, ref: null });\r\n\r\n let appHtml = '';\r\n const store = await runWithHtmlStore(async () => { appHtml = await renderNode(wrapped, hydrated); });\r\n\r\n const pageTitle = resolveTitle(store.titleOps, 'NukeJS');\r\n const headScripts = store.script.filter((s: any) => (s.position ?? 'head') === 'head');\r\n const bodyScripts = store.script.filter((s: any) => s.position === 'body');\r\n const headLines = [\r\n ' <meta charset=\"utf-8\" />',\r\n ' <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />',\r\n \\` <title>\\${escapeHtml(pageTitle)}</title>\\`,\r\n ...(store.meta.length || store.link.length || store.style.length || headScripts.length ? [\r\n ' <!--n-head-->',\r\n ...store.meta.map(renderMetaTag),\r\n ...store.link.map(renderLinkTag),\r\n ...store.style.map(renderStyleTag),\r\n ...headScripts.map(renderScriptTag),\r\n ' <!--/n-head-->',\r\n ] : []),\r\n ];\r\n const bodyScriptLines = bodyScripts.length\r\n ? [' <!--n-body-scripts-->', ...bodyScripts.map(renderScriptTag), ' <!--/n-body-scripts-->']\r\n : [];\r\n const bodyScriptsHtml = bodyScriptLines.length ? '\\\\n' + bodyScriptLines.join('\\\\n') + '\\\\n' : '';\r\n\r\n const runtimeData = JSON.stringify({\r\n hydrateIds: [...hydrated], allIds: ALL_CLIENT_IDS, url, params, debug: 'silent',\r\n }).replace(/</g, '\\\\u003c').replace(/>/g, '\\\\u003e').replace(/&/g, '\\\\u0026');\r\n\r\n const html = \\`<!DOCTYPE html>\r\n\\${openTag('html', store.htmlAttrs)}\r\n<head>\r\n\\${headLines.join('\\\\n')}\r\n</head>\r\n\\${openTag('body', store.bodyAttrs)}\r\n <div id=\"app\">\\${appHtml}</div>\r\n\r\n <script id=\"__n_data\" type=\"application/json\">\\${runtimeData}</script>\r\n\r\n <script type=\"importmap\">\r\n {\r\n \"imports\": {\r\n \"react\": \"/__n.js\",\r\n \"react-dom/client\": \"/__n.js\",\r\n \"react/jsx-runtime\": \"/__n.js\",\r\n \"nukejs\": \"/__n.js\"\r\n }\r\n }\r\n </script>\r\n\r\n <script type=\"module\">\r\n const { initRuntime } = await import('nukejs');\r\n const data = JSON.parse(document.getElementById('__n_data').textContent);\r\n await initRuntime(data);\r\n </script>\r\n\\${bodyScriptsHtml}</body>\r\n</html>\\`;\r\n\r\n res.statusCode = 200;\r\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\r\n res.end(html);\r\n } catch (err: any) {\r\n console.error('[page render error]', err);\r\n res.statusCode = 500;\r\n res.setHeader('Content-Type', 'text/plain; charset=utf-8');\r\n res.end('Internal Server Error');\r\n }\r\n}\r\n`;\r\n}\r\n\r\n// \u2500\u2500\u2500 Bundle operations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Bundles an API route handler into a single self-contained ESM string.\r\n * node_modules are kept external \u2014 they exist at runtime on both Node and\r\n * Vercel (Vercel bundles them separately via the pages dispatcher).\r\n */\r\nexport async function bundleApiHandler(absPath: string): Promise<string> {\r\n const adapterName = `_api_adapter_${randomBytes(4).toString('hex')}.ts`;\r\n const adapterPath = path.join(path.dirname(absPath), adapterName);\r\n fs.writeFileSync(adapterPath, makeApiAdapterSource(path.basename(absPath)));\r\n\r\n let text: string;\r\n try {\r\n const result = await build({\r\n entryPoints: [adapterPath],\r\n bundle: true,\r\n format: 'esm',\r\n platform: 'node',\r\n target: 'node20',\r\n packages: 'external',\r\n write: false,\r\n });\r\n text = result.outputFiles[0].text;\r\n } finally {\r\n fs.unlinkSync(adapterPath);\r\n }\r\n return text;\r\n}\r\n\r\nexport interface PageBundleOptions {\r\n absPath: string;\r\n pagesDir: string;\r\n clientComponentNames: Record<string, string>;\r\n allClientIds: string[];\r\n layoutPaths: string[];\r\n prerenderedHtml: Record<string, string>;\r\n catchAllNames: string[];\r\n}\r\n\r\n/**\r\n * Bundles a server-component page into a single self-contained ESM string.\r\n * All npm packages are kept external \u2014 the Node production server has\r\n * node_modules available at runtime.\r\n */\r\nexport async function bundlePageHandler(opts: PageBundleOptions): Promise<string> {\r\n const {\r\n absPath, clientComponentNames, allClientIds,\r\n layoutPaths, prerenderedHtml, catchAllNames,\r\n } = opts;\r\n\r\n const adapterDir = path.dirname(absPath);\r\n const adapterPath = path.join(adapterDir, `_page_adapter_${randomBytes(4).toString('hex')}.ts`);\r\n\r\n const layoutImports = layoutPaths\r\n .map((lp, i) => {\r\n const rel = path.relative(adapterDir, lp).replace(/\\\\/g, '/');\r\n return `import __layout_${i}__ from ${JSON.stringify(rel.startsWith('.') ? rel : './' + rel)};`;\r\n })\r\n .join('\\n');\r\n\r\n fs.writeFileSync(adapterPath, makePageAdapterSource({\r\n pageImport: JSON.stringify('./' + path.basename(absPath)),\r\n layoutImports,\r\n clientComponentNames,\r\n allClientIds,\r\n layoutArrayItems: layoutPaths.map((_, i) => `__layout_${i}__`).join(', '),\r\n prerenderedHtml,\r\n catchAllNames,\r\n }));\r\n\r\n let text: string;\r\n try {\r\n const result = await build({\r\n entryPoints: [adapterPath],\r\n bundle: true,\r\n format: 'esm',\r\n platform: 'node',\r\n target: 'node20',\r\n jsx: 'automatic',\r\n packages: 'external',\r\n external: NODE_BUILTINS,\r\n define: { 'process.env.NODE_ENV': '\"production\"' },\r\n write: false,\r\n });\r\n text = result.outputFiles[0].text;\r\n } finally {\r\n fs.unlinkSync(adapterPath);\r\n }\r\n return text;\r\n}\r\n\r\n/**\r\n * Bundles every client component in `globalRegistry` to\r\n * `<staticDir>/__client-component/<id>.js` and pre-renders each to HTML.\r\n */\r\nexport async function bundleClientComponents(\r\n globalRegistry: Map<string, string>,\r\n pagesDir: string,\r\n staticDir: string,\r\n): Promise<Map<string, string>> {\r\n if (globalRegistry.size === 0) return new Map();\r\n\r\n const outDir = path.join(staticDir, '__client-component');\r\n fs.mkdirSync(outDir, { recursive: true });\r\n\r\n const prerendered = new Map<string, string>();\r\n\r\n for (const [id, filePath] of globalRegistry) {\r\n console.log(` bundling client ${id} (${path.relative(pagesDir, filePath)})`);\r\n\r\n // Browser bundle \u2014 served to the client for hydration\r\n const browserResult = await build({\r\n entryPoints: [filePath],\r\n bundle: true,\r\n format: 'esm',\r\n platform: 'browser',\r\n jsx: 'automatic',\r\n minify: true,\r\n external: ['react', 'react-dom/client', 'react/jsx-runtime'],\r\n define: { 'process.env.NODE_ENV': '\"production\"' },\r\n write: false,\r\n });\r\n fs.writeFileSync(path.join(outDir, `${id}.js`), browserResult.outputFiles[0].text);\r\n\r\n // SSR pre-render \u2014 bundle for Node, import, renderToString, discard\r\n const ssrTmp = path.join(\r\n path.dirname(filePath),\r\n `_ssr_${id}_${randomBytes(4).toString('hex')}.mjs`,\r\n );\r\n try {\r\n const ssrResult = await build({\r\n entryPoints: [filePath],\r\n bundle: true,\r\n format: 'esm',\r\n platform: 'node',\r\n target: 'node20',\r\n jsx: 'automatic',\r\n packages: 'external',\r\n define: { 'process.env.NODE_ENV': '\"production\"' },\r\n write: false,\r\n });\r\n fs.writeFileSync(ssrTmp, ssrResult.outputFiles[0].text);\r\n\r\n const { default: Component } = await import(pathToFileURL(ssrTmp).href);\r\n const { createElement } = await import('react');\r\n const { renderToString } = await import('react-dom/server');\r\n\r\n prerendered.set(id, renderToString(createElement(Component, {})));\r\n console.log(` prerendered ${id}`);\r\n } catch {\r\n prerendered.set(id, '');\r\n } finally {\r\n if (fs.existsSync(ssrTmp)) fs.unlinkSync(ssrTmp);\r\n }\r\n }\r\n\r\n console.log(` bundled ${globalRegistry.size} client component(s) \u2192 ${path.relative(process.cwd(), outDir)}/`);\r\n return prerendered;\r\n}\r\n\r\n/**\r\n * Builds the combined browser bundle (__n.js) that contains the full React\r\n * runtime + NukeJS client runtime in a single file.\r\n */\r\nexport async function buildCombinedBundle(staticDir: string): Promise<void> {\r\n const nukeDir = path.dirname(fileURLToPath(import.meta.url));\r\n const bundleFile = nukeDir.endsWith('dist') ? 'bundle' : 'bundle.ts';\r\n\r\n const result = await build({\r\n stdin: {\r\n contents: `\r\nimport React, {\r\n useState, useEffect, useContext, useReducer, useCallback, useMemo,\r\n useRef, useImperativeHandle, useLayoutEffect, useDebugValue,\r\n useDeferredValue, useTransition, useId, useSyncExternalStore,\r\n useInsertionEffect, createContext, forwardRef, memo, lazy,\r\n Suspense, Fragment, StrictMode, Component, PureComponent\r\n} from 'react';\r\nimport { jsx, jsxs } from 'react/jsx-runtime';\r\nimport { hydrateRoot, createRoot } from 'react-dom/client';\r\nexport { initRuntime, setupLocationChangeMonitor } from './${bundleFile}';\r\nexport {\r\n useState, useEffect, useContext, useReducer, useCallback, useMemo,\r\n useRef, useImperativeHandle, useLayoutEffect, useDebugValue,\r\n useDeferredValue, useTransition, useId, useSyncExternalStore,\r\n useInsertionEffect, createContext, forwardRef, memo, lazy,\r\n Suspense, Fragment, StrictMode, Component, PureComponent,\r\n hydrateRoot, createRoot, jsx, jsxs\r\n};\r\nexport default React;\r\n`,\r\n loader: 'ts',\r\n resolveDir: nukeDir,\r\n },\r\n bundle: true,\r\n write: false,\r\n treeShaking: true,\r\n minify: true,\r\n format: 'esm',\r\n jsx: 'automatic',\r\n alias: {\r\n react: path.dirname(fileURLToPath(import.meta.resolve('react/package.json'))),\r\n 'react-dom': path.dirname(fileURLToPath(import.meta.resolve('react-dom/package.json'))),\r\n },\r\n define: { 'process.env.NODE_ENV': '\"production\"' },\r\n });\r\n\r\n fs.writeFileSync(path.join(staticDir, '__n.js'), result.outputFiles[0].text);\r\n console.log(' built __n.js (react + runtime)');\r\n}\r\n\r\n// \u2500\u2500\u2500 Public file copying \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Recursively copies every file from `publicDir` into `destDir`, preserving\r\n * the directory structure. Skips silently when `publicDir` does not exist.\r\n */\r\nexport function copyPublicFiles(publicDir: string, destDir: string): void {\r\n if (!fs.existsSync(publicDir)) return;\r\n\r\n let count = 0;\r\n (function walk(src: string, dest: string) {\r\n fs.mkdirSync(dest, { recursive: true });\r\n for (const entry of fs.readdirSync(src, { withFileTypes: true })) {\r\n const s = path.join(src, entry.name);\r\n const d = path.join(dest, entry.name);\r\n if (entry.isDirectory()) { walk(s, d); } else { fs.copyFileSync(s, d); count++; }\r\n }\r\n })(publicDir, destDir);\r\n\r\n if (count > 0)\r\n console.log(` copied ${count} public file(s) \u2192 ${path.relative(process.cwd(), destDir)}/`);\r\n}"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\r\n * build-common.ts \u2014 Shared Build Logic\r\n *\r\n * Used by both build-node.ts and build-vercel.ts.\r\n *\r\n * Exports:\r\n * \u2014 types : AnalyzedRoute, ServerPage, BuiltPage,\r\n * PageAdapterOptions, PageBundleOptions\r\n * \u2014 utility helpers : walkFiles, analyzeFile, isServerComponent,\r\n * findPageLayouts, extractDefaultExportName\r\n * \u2014 collection : collectServerPages, collectGlobalClientRegistry,\r\n * buildPerPageRegistry\r\n * \u2014 template codegen : makeApiAdapterSource, makePageAdapterSource\r\n * \u2014 bundle ops : bundleApiHandler, bundlePageHandler,\r\n * bundleClientComponents, buildPages,\r\n * buildCombinedBundle, copyPublicFiles\r\n */\r\n\r\nimport fs from 'fs';\r\nimport path from 'path';\r\nimport { randomBytes } from 'node:crypto';\r\nimport { fileURLToPath, pathToFileURL } from 'url';\r\nimport { build } from 'esbuild';\r\nimport { findClientComponentsInTree } from './component-analyzer';\r\n\r\n// \u2500\u2500\u2500 Node built-in externals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * All Node.js built-in module names.\r\n * Used as the `external` list when bundling for Node so esbuild never tries\r\n * to inline them, which would produce broken `require()` shims in ESM output.\r\n */\r\nconst NODE_BUILTINS = [\r\n 'node:*',\r\n 'http', 'https', 'fs', 'path', 'url', 'crypto', 'stream', 'buffer',\r\n 'events', 'util', 'os', 'net', 'tls', 'child_process', 'worker_threads',\r\n 'cluster', 'dgram', 'dns', 'readline', 'zlib', 'assert', 'module',\r\n 'perf_hooks', 'string_decoder', 'timers', 'async_hooks', 'v8', 'vm',\r\n];\r\n\r\n// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nexport interface AnalyzedRoute {\r\n /** Regex string matching the URL path, e.g. '^/users/([^/]+)$' */\r\n srcRegex: string;\r\n /** Names of captured groups in srcRegex order */\r\n paramNames: string[];\r\n /**\r\n * Subset of paramNames that are catch-all ([...slug] or [[...path]]).\r\n * Their runtime values are string[] not string.\r\n */\r\n catchAllNames: string[];\r\n /** Function namespace path, e.g. '/api/users' or '/page/about' */\r\n funcPath: string;\r\n specificity: number;\r\n}\r\n\r\nexport interface ServerPage extends AnalyzedRoute {\r\n absPath: string;\r\n}\r\n\r\nexport interface BuiltPage extends ServerPage {\r\n bundleText: string;\r\n}\r\n\r\n// \u2500\u2500\u2500 File walker \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nexport function walkFiles(dir: string, base: string = dir): string[] {\r\n if (!fs.existsSync(dir)) return [];\r\n const results: string[] = [];\r\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\r\n const full = path.join(dir, entry.name);\r\n if (entry.isDirectory()) {\r\n results.push(...walkFiles(full, base));\r\n } else if (entry.name.endsWith('.ts') || entry.name.endsWith('.tsx')) {\r\n results.push(path.relative(base, full));\r\n }\r\n }\r\n return results;\r\n}\r\n\r\n// \u2500\u2500\u2500 Route analysis \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Parses dynamic-route segments from a relative file path and returns a regex,\r\n * captured param names, catch-all param names, a function path, and a\r\n * specificity score.\r\n *\r\n * Supported patterns per segment:\r\n * [[...name]] optional catch-all \u2192 regex (.*) \u2192 string[]\r\n * [...name] required catch-all \u2192 regex (.+) \u2192 string[]\r\n * [[name]] optional single \u2192 regex ([^/]*)? \u2192 string\r\n * [name] required single \u2192 regex ([^/]+) \u2192 string\r\n * literal static \u2192 escaped literal\r\n *\r\n * @param relPath Relative path from the dir root (e.g. 'users/[id].tsx').\r\n * @param prefix Namespace for funcPath ('api' | 'page').\r\n */\r\nexport function analyzeFile(relPath: string, prefix = 'api'): AnalyzedRoute {\r\n const normalized = relPath.replace(/\\\\/g, '/').replace(/\\.(tsx?)$/, '');\r\n let segments = normalized.split('/');\r\n if (segments.at(-1) === 'index') segments = segments.slice(0, -1);\r\n\r\n const paramNames: string[] = [];\r\n const catchAllNames: string[] = [];\r\n const regexParts: string[] = [];\r\n let specificity = 0;\r\n\r\n for (const seg of segments) {\r\n const optCatchAll = seg.match(/^\\[\\[\\.\\.\\.(.+)\\]\\]$/);\r\n if (optCatchAll) {\r\n paramNames.push(optCatchAll[1]);\r\n catchAllNames.push(optCatchAll[1]);\r\n regexParts.push('(.*)');\r\n specificity += 1;\r\n continue;\r\n }\r\n const catchAll = seg.match(/^\\[\\.\\.\\.(.+)\\]$/);\r\n if (catchAll) {\r\n paramNames.push(catchAll[1]);\r\n catchAllNames.push(catchAll[1]);\r\n regexParts.push('(.+)');\r\n specificity += 10;\r\n continue;\r\n }\r\n const optDynamic = seg.match(/^\\[\\[([^.][^\\]]*)\\]\\]$/);\r\n if (optDynamic) {\r\n paramNames.push(optDynamic[1]);\r\n regexParts.push('__OPT__([^/]*)'); // marker \u2014 resolved below\r\n specificity += 30;\r\n continue;\r\n }\r\n const dynamic = seg.match(/^\\[(.+)\\]$/);\r\n if (dynamic) {\r\n paramNames.push(dynamic[1]);\r\n regexParts.push('([^/]+)');\r\n specificity += 100;\r\n continue;\r\n }\r\n regexParts.push(seg.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'));\r\n specificity += 1000;\r\n }\r\n\r\n // Build the regex string.\r\n // __OPT__(...) markers indicate optional single segments where the preceding\r\n // slash must also be optional (e.g. users/[[id]] should match /users).\r\n let srcRegex: string;\r\n if (segments.length === 0) {\r\n srcRegex = '^/$';\r\n } else {\r\n let body = '';\r\n for (let i = 0; i < regexParts.length; i++) {\r\n const part = regexParts[i];\r\n if (part.startsWith('__OPT__')) {\r\n const cap = part.slice(7);\r\n // At position 0, ^/ already provides the leading slash\r\n body += i === 0 ? cap : `(?:/${cap})?`;\r\n } else {\r\n body += (i === 0 ? '' : '/') + part;\r\n }\r\n }\r\n srcRegex = '^/' + body + '$';\r\n }\r\n\r\n const funcSegments = normalized.split('/');\r\n if (funcSegments.at(-1) === 'index') funcSegments.pop();\r\n const funcPath = funcSegments.length === 0\r\n ? `/${prefix}/_index`\r\n : `/${prefix}/` + funcSegments.join('/');\r\n\r\n return { srcRegex, paramNames, catchAllNames, funcPath, specificity };\r\n}\r\n\r\n// \u2500\u2500\u2500 Server-component detection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Returns true when a file does NOT begin with a \"use client\" directive,\r\n * i.e. it is a server component.\r\n */\r\nexport function isServerComponent(filePath: string): boolean {\r\n const content = fs.readFileSync(filePath, 'utf-8');\r\n for (const line of content.split('\\n').slice(0, 5)) {\r\n const trimmed = line.trim();\r\n if (!trimmed || trimmed.startsWith('//') || trimmed.startsWith('/*')) continue;\r\n if (/^[\"']use client[\"'];?$/.test(trimmed)) return false;\r\n break;\r\n }\r\n return true;\r\n}\r\n\r\n// \u2500\u2500\u2500 Layout discovery \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Walks from the pages root to the directory containing `routeFilePath` and\r\n * returns every layout.tsx found, in outermost-first order.\r\n */\r\nexport function findPageLayouts(routeFilePath: string, pagesDir: string): string[] {\r\n const layouts: string[] = [];\r\n\r\n const rootLayout = path.join(pagesDir, 'layout.tsx');\r\n if (fs.existsSync(rootLayout)) layouts.push(rootLayout);\r\n\r\n const relativePath = path.relative(pagesDir, path.dirname(routeFilePath));\r\n if (!relativePath || relativePath === '.') return layouts;\r\n\r\n const segments = relativePath.split(path.sep).filter(Boolean);\r\n for (let i = 1; i <= segments.length; i++) {\r\n const layoutPath = path.join(pagesDir, ...segments.slice(0, i), 'layout.tsx');\r\n if (fs.existsSync(layoutPath)) layouts.push(layoutPath);\r\n }\r\n\r\n return layouts;\r\n}\r\n\r\n/**\r\n * Extracts the identifier used as the default export from a component file.\r\n * Returns null when no default export is found.\r\n *\r\n * Handles three formats so that components compiled by esbuild are recognised\r\n * alongside hand-written source files:\r\n * 1. Source: `export default function Foo` / `export default Foo`\r\n * 2. esbuild: `var Foo_default = Foo` (compiled arrow-function component)\r\n * 3. Re-export: `export { Foo as default }`\r\n */\r\nexport function extractDefaultExportName(filePath: string): string | null {\r\n const content = fs.readFileSync(filePath, 'utf-8');\r\n\r\n // Format 1 \u2013 source: `export default function Foo` or `export default Foo`\r\n let m = content.match(/export\\s+default\\s+(?:function\\s+)?(\\w+)/);\r\n if (m?.[1]) return m[1];\r\n\r\n // Format 2 \u2013 esbuild compiled: `var Foo_default = Foo`\r\n m = content.match(/var\\s+\\w+_default\\s*=\\s*(\\w+)/);\r\n if (m?.[1]) return m[1];\r\n\r\n // Format 3 \u2013 explicit re-export: `export { Foo as default }`\r\n m = content.match(/export\\s*\\{[^}]*\\b(\\w+)\\s+as\\s+default\\b[^}]*\\}/);\r\n if (m?.[1] && !m[1].endsWith('_default')) return m[1];\r\n\r\n return null;\r\n}\r\n\r\n// \u2500\u2500\u2500 Server page collection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Returns all server-component pages inside `pagesDir`, sorted most-specific\r\n * first so precise routes shadow catch-alls in routers.\r\n * layout.tsx files and \"use client\" files are excluded.\r\n */\r\nexport function collectServerPages(pagesDir: string): ServerPage[] {\r\n if (!fs.existsSync(pagesDir)) return [];\r\n return walkFiles(pagesDir)\r\n .filter(relPath => {\r\n if (path.basename(relPath, path.extname(relPath)) === 'layout') return false;\r\n return isServerComponent(path.join(pagesDir, relPath));\r\n })\r\n .map(relPath => ({\r\n ...analyzeFile(relPath, 'page'),\r\n absPath: path.join(pagesDir, relPath),\r\n }))\r\n .sort((a, b) => b.specificity - a.specificity);\r\n}\r\n\r\n/**\r\n * Walks every server page and its layout chain to collect all client component\r\n * IDs reachable anywhere in the app.\r\n */\r\nexport function collectGlobalClientRegistry(\r\n serverPages: ServerPage[],\r\n pagesDir: string,\r\n): Map<string, string> {\r\n const registry = new Map<string, string>();\r\n for (const { absPath } of serverPages) {\r\n for (const [id, p] of findClientComponentsInTree(absPath, pagesDir))\r\n registry.set(id, p);\r\n for (const layoutPath of findPageLayouts(absPath, pagesDir))\r\n for (const [id, p] of findClientComponentsInTree(layoutPath, pagesDir))\r\n registry.set(id, p);\r\n }\r\n return registry;\r\n}\r\n\r\n// \u2500\u2500\u2500 Per-page registry \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Builds the per-page client component registry (page + its layout chain)\r\n * and returns both the id\u2192path map and the name\u2192id map needed by\r\n * bundlePageHandler.\r\n */\r\nexport function buildPerPageRegistry(\r\n absPath: string,\r\n layoutPaths: string[],\r\n pagesDir: string,\r\n): { registry: Map<string, string>; clientComponentNames: Record<string, string> } {\r\n const registry = new Map<string, string>();\r\n\r\n for (const [id, p] of findClientComponentsInTree(absPath, pagesDir))\r\n registry.set(id, p);\r\n for (const lp of layoutPaths)\r\n for (const [id, p] of findClientComponentsInTree(lp, pagesDir))\r\n registry.set(id, p);\r\n\r\n const clientComponentNames: Record<string, string> = {};\r\n for (const [id, filePath] of registry) {\r\n const name = extractDefaultExportName(filePath);\r\n if (name) clientComponentNames[name] = id;\r\n }\r\n\r\n return { registry, clientComponentNames };\r\n}\r\n\r\n// \u2500\u2500\u2500 High-level page builder \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Runs both passes of the page build:\r\n *\r\n * Pass 1 \u2014 bundles all client components to `staticDir/__client-component/`\r\n * and collects pre-rendered HTML for each.\r\n * Pass 2 \u2014 bundles every server-component page into a self-contained ESM\r\n * handler and returns the results as `BuiltPage[]`.\r\n */\r\nexport async function buildPages(\r\n pagesDir: string,\r\n staticDir: string,\r\n): Promise<BuiltPage[]> {\r\n const serverPages = collectServerPages(pagesDir);\r\n\r\n if (fs.existsSync(pagesDir) && walkFiles(pagesDir).length > 0 && serverPages.length === 0) {\r\n console.warn(`\u26A0 Pages found in ${pagesDir} but none are server components`);\r\n }\r\n\r\n if (serverPages.length === 0) return [];\r\n\r\n const globalRegistry = collectGlobalClientRegistry(serverPages, pagesDir);\r\n const prerenderedHtml = await bundleClientComponents(globalRegistry, pagesDir, staticDir);\r\n const prerenderedRecord = Object.fromEntries(prerenderedHtml);\r\n\r\n const builtPages: BuiltPage[] = [];\r\n\r\n for (const page of serverPages) {\r\n console.log(` building ${page.absPath} \u2192 ${page.funcPath} [page]`);\r\n\r\n const layoutPaths = findPageLayouts(page.absPath, pagesDir);\r\n const { registry, clientComponentNames } = buildPerPageRegistry(page.absPath, layoutPaths, pagesDir);\r\n\r\n const bundleText = await bundlePageHandler({\r\n absPath: page.absPath,\r\n pagesDir,\r\n clientComponentNames,\r\n allClientIds: [...registry.keys()],\r\n layoutPaths,\r\n prerenderedHtml: prerenderedRecord,\r\n routeParamNames: page.paramNames,\r\n catchAllNames: page.catchAllNames,\r\n });\r\n\r\n builtPages.push({ ...page, bundleText });\r\n }\r\n\r\n return builtPages;\r\n}\r\n\r\n// \u2500\u2500\u2500 API adapter template \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Returns the TypeScript source for a thin HTTP adapter that wraps an API\r\n * route module and exposes a single `handler(req, res)` default export.\r\n */\r\nexport function makeApiAdapterSource(handlerFilename: string): string {\r\n return `\\\r\nimport type { IncomingMessage, ServerResponse } from 'http';\r\nimport * as mod from ${JSON.stringify('./' + handlerFilename)};\r\n\r\nfunction enhance(res: ServerResponse) {\r\n (res as any).json = function (data: any, status = 200) {\r\n this.statusCode = status;\r\n this.setHeader('Content-Type', 'application/json');\r\n this.end(JSON.stringify(data));\r\n };\r\n (res as any).status = function (code: number) { this.statusCode = code; return this; };\r\n return res;\r\n}\r\n\r\nasync function parseBody(req: IncomingMessage): Promise<any> {\r\n return new Promise((resolve, reject) => {\r\n let body = '';\r\n req.on('data', chunk => { body += chunk.toString(); });\r\n req.on('end', () => {\r\n try {\r\n resolve(body && req.headers['content-type']?.includes('application/json')\r\n ? JSON.parse(body) : body);\r\n } catch (e) { reject(e); }\r\n });\r\n req.on('error', reject);\r\n });\r\n}\r\n\r\nexport default async function handler(req: IncomingMessage, res: ServerResponse) {\r\n const method = (req.method || 'GET').toUpperCase();\r\n const apiRes = enhance(res);\r\n const apiReq = req as any;\r\n\r\n apiReq.body = await parseBody(req);\r\n // In production, route dynamic segments are injected as query-string keys by\r\n // the server entry, so params and query share the same parsed URL values.\r\n const qs = Object.fromEntries(new URL(req.url || '/', 'http://localhost').searchParams);\r\n apiReq.query = qs;\r\n apiReq.params = qs;\r\n\r\n const fn = (mod as any)[method] ?? (mod as any).default;\r\n if (typeof fn !== 'function') {\r\n (apiRes as any).json({ error: \\`Method \\${method} not allowed\\` }, 405);\r\n return;\r\n }\r\n await fn(apiReq, apiRes);\r\n}\r\n`;\r\n}\r\n\r\n// \u2500\u2500\u2500 Page adapter template \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nexport interface PageAdapterOptions {\r\n /** e.g. './home.tsx' \u2014 relative import for the page default export */\r\n pageImport: string;\r\n /** Newline-joined import statements for layout components */\r\n layoutImports: string;\r\n /** function-name \u2192 cc_id map, computed at build time */\r\n clientComponentNames: Record<string, string>;\r\n /** All client component IDs reachable from this page */\r\n allClientIds: string[];\r\n /** Comma-separated list of __layout_N__ identifiers */\r\n layoutArrayItems: string;\r\n /** Pre-rendered HTML per client component ID, computed at build time */\r\n prerenderedHtml: Record<string, string>;\r\n /**\r\n * All dynamic route param names for this page (e.g. ['id', 'slug']).\r\n * Used to distinguish route segments from real query-string params at runtime.\r\n */\r\n routeParamNames: string[];\r\n /** Subset of routeParamNames whose values are string[] (catch-all segments) */\r\n catchAllNames: string[];\r\n}\r\n\r\n/**\r\n * Returns the TypeScript source for a fully self-contained page handler.\r\n *\r\n * The adapter:\r\n * \u2022 Inlines the html-store so useHtml() works without external deps.\r\n * \u2022 Contains an async recursive renderer for server + client components.\r\n * \u2022 Client components are identified via the pre-computed CLIENT_COMPONENTS\r\n * map \u2014 no fs.readFileSync at runtime.\r\n * \u2022 Emits the full HTML document including the __n_data blob and bootstrap.\r\n */\r\nexport function makePageAdapterSource(opts: PageAdapterOptions): string {\r\n const {\r\n pageImport, layoutImports, clientComponentNames, allClientIds,\r\n layoutArrayItems, prerenderedHtml, routeParamNames, catchAllNames,\r\n } = opts;\r\n\r\n return `\\\r\nimport type { IncomingMessage, ServerResponse } from 'http';\r\nimport { createElement as __createElement__ } from 'react';\r\nimport { renderToString as __renderToString__ } from 'react-dom/server';\r\nimport * as __page__ from ${pageImport};\r\n${layoutImports}\r\n\r\nconst CLIENT_COMPONENTS: Record<string, string> = ${JSON.stringify(clientComponentNames)};\r\nconst ALL_CLIENT_IDS: string[] = ${JSON.stringify(allClientIds)};\r\nconst PRERENDERED_HTML: Record<string, string> = ${JSON.stringify(prerenderedHtml)};\r\n// ROUTE_PARAM_NAMES: the dynamic segments baked into this page's URL pattern.\r\n// Used to separate them from real user-supplied query params at runtime.\r\nconst ROUTE_PARAM_NAMES = new Set<string>(${JSON.stringify(routeParamNames)});\r\nconst CATCH_ALL_NAMES = new Set<string>(${JSON.stringify(catchAllNames)});\r\n\r\n// \u2500\u2500\u2500 html-store (inlined) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\ntype TitleValue = string | ((prev: string) => string);\r\ninterface HtmlStore {\r\n titleOps: TitleValue[];\r\n htmlAttrs: Record<string, string | undefined>;\r\n bodyAttrs: Record<string, string | undefined>;\r\n meta: Record<string, string | undefined>[];\r\n link: Record<string, string | undefined>[];\r\n script: Record<string, any>[];\r\n style: { content?: string; media?: string }[];\r\n}\r\nconst __STORE_KEY__ = Symbol.for('__nukejs_html_store__');\r\nconst __getStore = (): HtmlStore | null => (globalThis as any)[__STORE_KEY__] ?? null;\r\nconst __setStore = (s: HtmlStore | null): void => { (globalThis as any)[__STORE_KEY__] = s; };\r\nconst __emptyStore = (): HtmlStore =>\r\n ({ titleOps: [], htmlAttrs: {}, bodyAttrs: {}, meta: [], link: [], script: [], style: [] });\r\nasync function runWithHtmlStore(fn: () => Promise<void>): Promise<HtmlStore> {\r\n __setStore(__emptyStore());\r\n try { await fn(); return { ...(__getStore() ?? __emptyStore()) }; }\r\n finally { __setStore(null); }\r\n}\r\nfunction resolveTitle(ops: TitleValue[], fallback = ''): string {\r\n let t = fallback;\r\n for (let i = ops.length - 1; i >= 0; i--) {\r\n const op = ops[i]; t = typeof op === 'string' ? op : op(t);\r\n }\r\n return t;\r\n}\r\n\r\n// \u2500\u2500\u2500 request-store (inlined) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nconst SENSITIVE_HEADERS = new Set([\r\n 'cookie','authorization','proxy-authorization','set-cookie','x-api-key',\r\n]);\r\n// Flattens multi-value headers to strings; keeps all headers including credentials.\r\nfunction normaliseHeaders(raw: Record<string, string | string[] | undefined>): Record<string, string> {\r\n const out: Record<string, string> = {};\r\n for (const [k, v] of Object.entries(raw)) {\r\n if (v === undefined) continue;\r\n out[k] = Array.isArray(v) ? v.join(', ') : v;\r\n }\r\n return out;\r\n}\r\n// Same as normaliseHeaders but strips credentials before embedding in HTML.\r\nfunction sanitiseHeaders(raw: Record<string, string | string[] | undefined>): Record<string, string> {\r\n const out: Record<string, string> = {};\r\n for (const [k, v] of Object.entries(raw)) {\r\n if (SENSITIVE_HEADERS.has(k.toLowerCase()) || v === undefined) continue;\r\n out[k] = Array.isArray(v) ? v.join(', ') : v;\r\n }\r\n return out;\r\n}\r\nconst __REQ_KEY__ = Symbol.for('__nukejs_request_store__');\r\nconst __getReq = () => (globalThis as any)[__REQ_KEY__] ?? null;\r\nconst __setReq = (v: any) => { (globalThis as any)[__REQ_KEY__] = v; };\r\nasync function runWithRequestStore<T>(ctx: any, fn: () => Promise<T>): Promise<T> {\r\n __setReq(ctx);\r\n try { return await fn(); } finally { __setReq(null); }\r\n}\r\n\r\n// \u2500\u2500\u2500 HTML helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nfunction escapeHtml(s: string): string {\r\n return String(s)\r\n .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;').replace(/'/g, '&#039;');\r\n}\r\nfunction escapeAttr(s: string): string {\r\n return String(s).replace(/&/g, '&amp;').replace(/\"/g, '&quot;');\r\n}\r\nfunction renderAttrs(attrs: Record<string, string | boolean | undefined>): string {\r\n return Object.entries(attrs)\r\n .filter(([, v]) => v !== undefined && v !== false)\r\n .map(([k, v]) => v === true ? k : \\`\\${k}=\"\\${escapeAttr(String(v))}\"\\`)\r\n .join(' ');\r\n}\r\nfunction openTag(tag: string, attrs: Record<string, string | undefined>): string {\r\n const s = renderAttrs(attrs as Record<string, string | boolean | undefined>);\r\n return s ? \\`<\\${tag} \\${s}>\\` : \\`<\\${tag}>\\`;\r\n}\r\nfunction renderMetaTag(tag: Record<string, string | undefined>): string {\r\n const key = (k: string) => k === 'httpEquiv' ? 'http-equiv' : k;\r\n const attrs: Record<string, string | undefined> = {};\r\n for (const [k, v] of Object.entries(tag)) if (v !== undefined) attrs[key(k)] = v;\r\n return \\` <meta \\${renderAttrs(attrs as any)} />\\`;\r\n}\r\nfunction renderLinkTag(tag: Record<string, string | undefined>): string {\r\n const key = (k: string) => k === 'hrefLang' ? 'hreflang' : k === 'crossOrigin' ? 'crossorigin' : k;\r\n const attrs: Record<string, string | undefined> = {};\r\n for (const [k, v] of Object.entries(tag)) if (v !== undefined) attrs[key(k)] = v;\r\n return \\` <link \\${renderAttrs(attrs as any)} />\\`;\r\n}\r\nfunction renderScriptTag(tag: any): string {\r\n const s = renderAttrs({ src: tag.src, type: tag.type, crossorigin: tag.crossOrigin,\r\n integrity: tag.integrity, defer: tag.defer, async: tag.async, nomodule: tag.noModule });\r\n return \\` \\${s ? \\`<script \\${s}>\\` : '<script>'}\\${tag.src ? '' : (tag.content ?? '')}</script>\\`;\r\n}\r\nfunction renderStyleTag(tag: any): string {\r\n const media = tag.media ? \\` media=\"\\${escapeAttr(tag.media)}\"\\` : '';\r\n return \\` <style\\${media}>\\${tag.content ?? ''}</style>\\`;\r\n}\r\n\r\n// \u2500\u2500\u2500 Renderer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nconst VOID_TAGS = new Set([\r\n 'area','base','br','col','embed','hr','img','input',\r\n 'link','meta','param','source','track','wbr',\r\n]);\r\n\r\n// \u2500\u2500\u2500 Wrapper attribute helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nfunction isWrapperAttr(key: string): boolean {\r\n return (\r\n key === 'className' ||\r\n key === 'style' ||\r\n key === 'id' ||\r\n key.startsWith('data-') ||\r\n key.startsWith('aria-')\r\n );\r\n}\r\nfunction splitWrapperAttrs(props: any): { wrapperAttrs: Record<string, any>; componentProps: Record<string, any> } {\r\n const wrapperAttrs: Record<string, any> = {};\r\n const componentProps: Record<string, any> = {};\r\n for (const [key, value] of Object.entries((props || {}) as Record<string, any>)) {\r\n if (isWrapperAttr(key)) wrapperAttrs[key] = value;\r\n else componentProps[key] = value;\r\n }\r\n return { wrapperAttrs, componentProps };\r\n}\r\nfunction buildWrapperAttrString(attrs: Record<string, any>): string {\r\n const parts = Object.entries(attrs)\r\n .map(([key, value]) => {\r\n if (key === 'className') key = 'class';\r\n if (key === 'style' && typeof value === 'object') {\r\n const css = Object.entries(value as Record<string, any>)\r\n .map(([p, val]) => \\`\\${p.replace(/[A-Z]/g, m => \\`-\\${m.toLowerCase()}\\`)}:\\${escapeHtml(String(val))}\\`)\r\n .join(';');\r\n return \\`style=\"\\${css}\"\\`;\r\n }\r\n if (typeof value === 'boolean') return value ? key : '';\r\n if (value == null) return '';\r\n return \\`\\${key}=\"\\${escapeHtml(String(value))}\"\\`;\r\n })\r\n .filter(Boolean);\r\n return parts.length ? ' ' + parts.join(' ') : '';\r\n}\r\n\r\nfunction serializeProps(value: any): any {\r\n if (typeof value === 'function') return undefined; // must come before the object check\r\n if (value == null || typeof value !== 'object') return value;\r\n if (Array.isArray(value)) return value.map(serializeProps).filter((v: any) => v !== undefined);\r\n if ((value as any).$$typeof) {\r\n const { type, props: p } = value as any;\r\n if (typeof type === 'string') return { __re: 'html', tag: type, props: serializeProps(p) };\r\n if (typeof type === 'function') {\r\n const cid = CLIENT_COMPONENTS[type.name];\r\n if (cid) return { __re: 'client', componentId: cid, props: serializeProps(p) };\r\n }\r\n return undefined;\r\n }\r\n const out: any = {};\r\n for (const [k, v] of Object.entries(value as Record<string, any>)) {\r\n const s = serializeProps(v);\r\n if (s !== undefined) out[k] = s;\r\n }\r\n return out;\r\n}\r\n\r\nasync function renderNode(node: any, hydrated: Set<string>): Promise<string> {\r\n if (node == null || typeof node === 'boolean') return '';\r\n if (typeof node === 'string') return escapeHtml(node);\r\n if (typeof node === 'number') return String(node);\r\n if (Array.isArray(node)) return (await Promise.all(node.map(n => renderNode(n, hydrated)))).join('');\r\n\r\n const { type, props } = node as { type: any; props: Record<string, any> };\r\n if (!type) return '';\r\n\r\n if (type === Symbol.for('react.fragment')) return renderNode(props?.children ?? null, hydrated);\r\n\r\n if (typeof type === 'string') {\r\n const { children, dangerouslySetInnerHTML, ...rest } = props || {};\r\n const attrParts: string[] = [];\r\n for (const [k, v] of Object.entries(rest as Record<string, any>)) {\r\n const name = k === 'className' ? 'class' : k === 'htmlFor' ? 'for' : k;\r\n if (typeof v === 'boolean') { if (v) attrParts.push(name); continue; }\r\n if (v == null) continue;\r\n if (k === 'style' && typeof v === 'object') {\r\n const css = Object.entries(v as Record<string, any>)\r\n .map(([p, val]) => \\`\\${p.replace(/[A-Z]/g, m => \\`-\\${m.toLowerCase()}\\`)}:\\${escapeHtml(String(val))}\\`)\r\n .join(';');\r\n attrParts.push(\\`style=\"\\${css}\"\\`);\r\n continue;\r\n }\r\n attrParts.push(\\`\\${name}=\"\\${escapeHtml(String(v))}\"\\`);\r\n }\r\n const attrStr = attrParts.length ? ' ' + attrParts.join(' ') : '';\r\n if (VOID_TAGS.has(type)) return \\`<\\${type}\\${attrStr} />\\`;\r\n const inner = dangerouslySetInnerHTML\r\n ? (dangerouslySetInnerHTML as any).__html\r\n : await renderNode(children ?? null, hydrated);\r\n return \\`<\\${type}\\${attrStr}>\\${inner}</\\${type}>\\`;\r\n }\r\n\r\n if (typeof type === 'function') {\r\n const clientId = CLIENT_COMPONENTS[type.name];\r\n if (clientId) {\r\n hydrated.add(clientId);\r\n const { wrapperAttrs, componentProps } = splitWrapperAttrs(props);\r\n const wrapperAttrStr = buildWrapperAttrString(wrapperAttrs);\r\n const serializedProps = serializeProps(componentProps ?? {});\r\n let ssrHtml: string;\r\n try {\r\n ssrHtml = __renderToString__(__createElement__(type as any, componentProps || {}));\r\n } catch {\r\n ssrHtml = PRERENDERED_HTML[clientId] ?? '';\r\n }\r\n return \\`<span data-hydrate-id=\"\\${clientId}\"\\${wrapperAttrStr} data-hydrate-props=\"\\${escapeHtml(JSON.stringify(serializedProps))}\">\\${ssrHtml}</span>\\`;\r\n }\r\n const instance = type.prototype?.isReactComponent ? new (type as any)(props) : null;\r\n return renderNode(instance ? instance.render() : await (type as Function)(props || {}), hydrated);\r\n }\r\n\r\n return '';\r\n}\r\n\r\n// \u2500\u2500\u2500 Layout wrapping \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nconst LAYOUT_COMPONENTS: Array<(props: any) => any> = [${layoutArrayItems}];\r\n\r\nfunction wrapWithLayouts(element: any): any {\r\n let el = element;\r\n for (let i = LAYOUT_COMPONENTS.length - 1; i >= 0; i--)\r\n el = { type: LAYOUT_COMPONENTS[i], props: { children: el }, key: null, ref: null };\r\n return el;\r\n}\r\n\r\n// \u2500\u2500\u2500 Handler \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\nexport default async function handler(req: IncomingMessage, res: ServerResponse): Promise<void> {\r\n try {\r\n const parsed = new URL(req.url || '/', 'http://localhost');\r\n const url = req.url || '/';\r\n const pathname = parsed.pathname;\r\n\r\n // Route params are injected as query-string keys by the server entry.\r\n // Build 'params' only from known route segments, and 'query' from the rest.\r\n const params: Record<string, string | string[]> = {};\r\n ROUTE_PARAM_NAMES.forEach(k => {\r\n if (CATCH_ALL_NAMES.has(k)) {\r\n params[k] = parsed.searchParams.getAll(k);\r\n } else {\r\n const v = parsed.searchParams.get(k);\r\n if (v !== null) params[k] = v;\r\n }\r\n });\r\n\r\n const query: Record<string, string | string[]> = {};\r\n parsed.searchParams.forEach((_, k) => {\r\n if (!ROUTE_PARAM_NAMES.has(k)) {\r\n const all = parsed.searchParams.getAll(k);\r\n query[k] = all.length > 1 ? all : all[0];\r\n }\r\n });\r\n\r\n const rawHeaders = req.headers as Record<string, string | string[] | undefined>;\r\n // Full headers (including credentials) for server components via the request store.\r\n const normHeaders = normaliseHeaders(rawHeaders);\r\n // Stripped headers safe for embedding in the HTML document.\r\n const safeHeaders = sanitiseHeaders(rawHeaders);\r\n\r\n const hydrated = new Set<string>();\r\n // Merge query params into page props to match dev behaviour (ssr.ts mergedParams).\r\n const merged = { ...query, ...params } as any;\r\n const wrapped = wrapWithLayouts({ type: __page__.default, props: merged, key: null, ref: null });\r\n\r\n let appHtml = '';\r\n const store = await runWithRequestStore(\r\n { url, pathname, params, query, headers: normHeaders },\r\n () => runWithHtmlStore(async () => { appHtml = await renderNode(wrapped, hydrated); }),\r\n );\r\n\r\n const pageTitle = resolveTitle(store.titleOps, 'NukeJS');\r\n const headScripts = store.script.filter((s: any) => (s.position ?? 'head') === 'head');\r\n const bodyScripts = store.script.filter((s: any) => s.position === 'body');\r\n const headLines = [\r\n ' <meta charset=\"utf-8\" />',\r\n ' <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />',\r\n \\` <title>\\${escapeHtml(pageTitle)}</title>\\`,\r\n ...(store.meta.length || store.link.length || store.style.length || headScripts.length ? [\r\n ' <!--n-head-->',\r\n ...store.meta.map(renderMetaTag),\r\n ...store.link.map(renderLinkTag),\r\n ...store.style.map(renderStyleTag),\r\n ...headScripts.map(renderScriptTag),\r\n ' <!--/n-head-->',\r\n ] : []),\r\n ];\r\n const bodyScriptLines = bodyScripts.length\r\n ? [' <!--n-body-scripts-->', ...bodyScripts.map(renderScriptTag), ' <!--/n-body-scripts-->']\r\n : [];\r\n const bodyScriptsHtml = bodyScriptLines.length ? '\\\\n' + bodyScriptLines.join('\\\\n') + '\\\\n' : '';\r\n\r\n const runtimeData = JSON.stringify({\r\n hydrateIds: [...hydrated], allIds: ALL_CLIENT_IDS, url, params,\r\n query, headers: safeHeaders, debug: 'silent',\r\n }).replace(/</g, '\\\\u003c').replace(/>/g, '\\\\u003e').replace(/&/g, '\\\\u0026');\r\n\r\n const html = \\`<!DOCTYPE html>\r\n\\${openTag('html', store.htmlAttrs)}\r\n<head>\r\n\\${headLines.join('\\\\n')}\r\n</head>\r\n\\${openTag('body', store.bodyAttrs)}\r\n <div id=\"app\">\\${appHtml}</div>\r\n\r\n <script id=\"__n_data\" type=\"application/json\">\\${runtimeData}</script>\r\n\r\n <script type=\"importmap\">\r\n {\r\n \"imports\": {\r\n \"react\": \"/__n.js\",\r\n \"react-dom/client\": \"/__n.js\",\r\n \"react/jsx-runtime\": \"/__n.js\",\r\n \"nukejs\": \"/__n.js\"\r\n }\r\n }\r\n </script>\r\n\r\n <script type=\"module\">\r\n const { initRuntime } = await import('nukejs');\r\n const data = JSON.parse(document.getElementById('__n_data').textContent);\r\n await initRuntime(data);\r\n </script>\r\n\\${bodyScriptsHtml}</body>\r\n</html>\\`;\r\n\r\n res.statusCode = 200;\r\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\r\n res.end(html);\r\n } catch (err: any) {\r\n console.error('[page render error]', err);\r\n res.statusCode = 500;\r\n res.setHeader('Content-Type', 'text/plain; charset=utf-8');\r\n res.end('Internal Server Error');\r\n }\r\n}\r\n`;\r\n}\r\n\r\n// \u2500\u2500\u2500 Bundle operations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Bundles an API route handler into a single self-contained ESM string.\r\n * node_modules are kept external \u2014 they exist at runtime on both Node and\r\n * Vercel (Vercel bundles them separately via the pages dispatcher).\r\n */\r\nexport async function bundleApiHandler(absPath: string): Promise<string> {\r\n const adapterName = `_api_adapter_${randomBytes(4).toString('hex')}.ts`;\r\n const adapterPath = path.join(path.dirname(absPath), adapterName);\r\n fs.writeFileSync(adapterPath, makeApiAdapterSource(path.basename(absPath)));\r\n\r\n let text: string;\r\n try {\r\n const result = await build({\r\n entryPoints: [adapterPath],\r\n bundle: true,\r\n format: 'esm',\r\n platform: 'node',\r\n target: 'node20',\r\n packages: 'external',\r\n write: false,\r\n });\r\n text = result.outputFiles[0].text;\r\n } finally {\r\n fs.unlinkSync(adapterPath);\r\n }\r\n return text;\r\n}\r\n\r\nexport interface PageBundleOptions {\r\n absPath: string;\r\n pagesDir: string;\r\n clientComponentNames: Record<string, string>;\r\n allClientIds: string[];\r\n layoutPaths: string[];\r\n prerenderedHtml: Record<string, string>;\r\n routeParamNames: string[];\r\n catchAllNames: string[];\r\n}\r\n\r\n/**\r\n * Bundles a server-component page into a single self-contained ESM string.\r\n * All npm packages are kept external \u2014 the Node production server has\r\n * node_modules available at runtime.\r\n */\r\nexport async function bundlePageHandler(opts: PageBundleOptions): Promise<string> {\r\n const {\r\n absPath, clientComponentNames, allClientIds,\r\n layoutPaths, prerenderedHtml, routeParamNames, catchAllNames,\r\n } = opts;\r\n\r\n const adapterDir = path.dirname(absPath);\r\n const adapterPath = path.join(adapterDir, `_page_adapter_${randomBytes(4).toString('hex')}.ts`);\r\n\r\n const layoutImports = layoutPaths\r\n .map((lp, i) => {\r\n const rel = path.relative(adapterDir, lp).replace(/\\\\/g, '/');\r\n return `import __layout_${i}__ from ${JSON.stringify(rel.startsWith('.') ? rel : './' + rel)};`;\r\n })\r\n .join('\\n');\r\n\r\n fs.writeFileSync(adapterPath, makePageAdapterSource({\r\n pageImport: JSON.stringify('./' + path.basename(absPath)),\r\n layoutImports,\r\n clientComponentNames,\r\n allClientIds,\r\n layoutArrayItems: layoutPaths.map((_, i) => `__layout_${i}__`).join(', '),\r\n prerenderedHtml,\r\n routeParamNames,\r\n catchAllNames,\r\n }));\r\n\r\n let text: string;\r\n try {\r\n const result = await build({\r\n entryPoints: [adapterPath],\r\n bundle: true,\r\n format: 'esm',\r\n platform: 'node',\r\n target: 'node20',\r\n jsx: 'automatic',\r\n packages: 'external',\r\n external: NODE_BUILTINS,\r\n define: { 'process.env.NODE_ENV': '\"production\"' },\r\n write: false,\r\n });\r\n text = result.outputFiles[0].text;\r\n } finally {\r\n fs.unlinkSync(adapterPath);\r\n }\r\n return text;\r\n}\r\n\r\n/**\r\n * Bundles every client component in `globalRegistry` to\r\n * `<staticDir>/__client-component/<id>.js` and pre-renders each to HTML.\r\n */\r\nexport async function bundleClientComponents(\r\n globalRegistry: Map<string, string>,\r\n pagesDir: string,\r\n staticDir: string,\r\n): Promise<Map<string, string>> {\r\n if (globalRegistry.size === 0) return new Map();\r\n\r\n const outDir = path.join(staticDir, '__client-component');\r\n fs.mkdirSync(outDir, { recursive: true });\r\n\r\n const prerendered = new Map<string, string>();\r\n\r\n for (const [id, filePath] of globalRegistry) {\r\n console.log(` bundling client ${id} (${path.relative(pagesDir, filePath)})`);\r\n\r\n // Browser bundle \u2014 served to the client for hydration\r\n const browserResult = await build({\r\n entryPoints: [filePath],\r\n bundle: true,\r\n format: 'esm',\r\n platform: 'browser',\r\n jsx: 'automatic',\r\n minify: true,\r\n external: ['react', 'react-dom/client', 'react/jsx-runtime'],\r\n define: { 'process.env.NODE_ENV': '\"production\"' },\r\n write: false,\r\n });\r\n fs.writeFileSync(path.join(outDir, `${id}.js`), browserResult.outputFiles[0].text);\r\n\r\n // SSR pre-render \u2014 bundle for Node, import, renderToString, discard\r\n const ssrTmp = path.join(\r\n path.dirname(filePath),\r\n `_ssr_${id}_${randomBytes(4).toString('hex')}.mjs`,\r\n );\r\n try {\r\n const ssrResult = await build({\r\n entryPoints: [filePath],\r\n bundle: true,\r\n format: 'esm',\r\n platform: 'node',\r\n target: 'node20',\r\n jsx: 'automatic',\r\n packages: 'external',\r\n define: { 'process.env.NODE_ENV': '\"production\"' },\r\n write: false,\r\n });\r\n fs.writeFileSync(ssrTmp, ssrResult.outputFiles[0].text);\r\n\r\n const { default: Component } = await import(pathToFileURL(ssrTmp).href);\r\n const { createElement } = await import('react');\r\n const { renderToString } = await import('react-dom/server');\r\n\r\n prerendered.set(id, renderToString(createElement(Component, {})));\r\n console.log(` prerendered ${id}`);\r\n } catch {\r\n prerendered.set(id, '');\r\n } finally {\r\n if (fs.existsSync(ssrTmp)) fs.unlinkSync(ssrTmp);\r\n }\r\n }\r\n\r\n console.log(` bundled ${globalRegistry.size} client component(s) \u2192 ${path.relative(process.cwd(), outDir)}/`);\r\n return prerendered;\r\n}\r\n\r\n/**\r\n * Builds the combined browser bundle (__n.js) that contains the full React\r\n * runtime + NukeJS client runtime in a single file.\r\n */\r\nexport async function buildCombinedBundle(staticDir: string): Promise<void> {\r\n const nukeDir = path.dirname(fileURLToPath(import.meta.url));\r\n const bundleFile = nukeDir.endsWith('dist') ? 'bundle' : 'bundle.ts';\r\n\r\n const result = await build({\r\n stdin: {\r\n contents: `\r\nimport React, {\r\n useState, useEffect, useContext, useReducer, useCallback, useMemo,\r\n useRef, useImperativeHandle, useLayoutEffect, useDebugValue,\r\n useDeferredValue, useTransition, useId, useSyncExternalStore,\r\n useInsertionEffect, createContext, forwardRef, memo, lazy,\r\n Suspense, Fragment, StrictMode, Component, PureComponent\r\n} from 'react';\r\nimport { jsx, jsxs } from 'react/jsx-runtime';\r\nimport { hydrateRoot, createRoot } from 'react-dom/client';\r\nexport { initRuntime, setupLocationChangeMonitor } from './${bundleFile}';\r\nexport {\r\n useState, useEffect, useContext, useReducer, useCallback, useMemo,\r\n useRef, useImperativeHandle, useLayoutEffect, useDebugValue,\r\n useDeferredValue, useTransition, useId, useSyncExternalStore,\r\n useInsertionEffect, createContext, forwardRef, memo, lazy,\r\n Suspense, Fragment, StrictMode, Component, PureComponent,\r\n hydrateRoot, createRoot, jsx, jsxs\r\n};\r\nexport default React;\r\n`,\r\n loader: 'ts',\r\n resolveDir: nukeDir,\r\n },\r\n bundle: true,\r\n write: false,\r\n treeShaking: true,\r\n minify: true,\r\n format: 'esm',\r\n jsx: 'automatic',\r\n alias: {\r\n react: path.dirname(fileURLToPath(import.meta.resolve('react/package.json'))),\r\n 'react-dom': path.dirname(fileURLToPath(import.meta.resolve('react-dom/package.json'))),\r\n },\r\n define: { 'process.env.NODE_ENV': '\"production\"' },\r\n });\r\n\r\n fs.writeFileSync(path.join(staticDir, '__n.js'), result.outputFiles[0].text);\r\n console.log(' built __n.js (react + runtime)');\r\n}\r\n\r\n// \u2500\u2500\u2500 Public file copying \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Recursively copies every file from `publicDir` into `destDir`, preserving\r\n * the directory structure. Skips silently when `publicDir` does not exist.\r\n */\r\nexport function copyPublicFiles(publicDir: string, destDir: string): void {\r\n if (!fs.existsSync(publicDir)) return;\r\n\r\n let count = 0;\r\n (function walk(src: string, dest: string) {\r\n fs.mkdirSync(dest, { recursive: true });\r\n for (const entry of fs.readdirSync(src, { withFileTypes: true })) {\r\n const s = path.join(src, entry.name);\r\n const d = path.join(dest, entry.name);\r\n if (entry.isDirectory()) { walk(s, d); } else { fs.copyFileSync(s, d); count++; }\r\n }\r\n })(publicDir, destDir);\r\n\r\n if (count > 0)\r\n console.log(` copied ${count} public file(s) \u2192 ${path.relative(process.cwd(), destDir)}/`);\r\n}"],
5
+ "mappings": "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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yDAgOhB,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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",
6
6
  "names": []
7
7
  }
@@ -13,6 +13,10 @@ const OUT_DIR = path.resolve("dist");
13
13
  const API_DIR = path.join(OUT_DIR, "api");
14
14
  const PAGES_DIR_ = path.join(OUT_DIR, "pages");
15
15
  const STATIC_DIR = path.join(OUT_DIR, "static");
16
+ if (fs.existsSync(OUT_DIR)) {
17
+ fs.rmSync(OUT_DIR, { recursive: true, force: true });
18
+ console.log("\u{1F5D1}\uFE0F Cleaned dist/");
19
+ }
16
20
  for (const dir of [API_DIR, PAGES_DIR_, STATIC_DIR])
17
21
  fs.mkdirSync(dir, { recursive: true });
18
22
  const config = await loadConfig();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/build-node.ts"],
4
- "sourcesContent": ["/**\r\n * build-node.ts \u2014 Node.js Production Build\r\n *\r\n * Produces a self-contained dist/ directory that runs with:\r\n * node dist/index.mjs\r\n *\r\n * Output layout:\r\n * dist/\r\n * index.mjs \u2190 HTTP server entry point (routing: static \u2192 framework \u2192 api \u2192 pages)\r\n * manifest.json \u2190 Route \u2192 handler mapping\r\n * api/<route>.mjs \u2190 Bundled API handlers\r\n * pages/<route>.mjs \u2190 Bundled page handlers\r\n * static/ \u2190 __n.js, client components, public files\r\n */\r\n\r\nimport fs from 'fs';\r\nimport path from 'path';\r\n\r\nimport { loadConfig } from './config';\r\nimport {\r\n analyzeFile,\r\n walkFiles,\r\n buildPages,\r\n bundleApiHandler,\r\n buildCombinedBundle,\r\n copyPublicFiles,\r\n type AnalyzedRoute,\r\n} from './build-common';\r\n\r\n// \u2500\u2500\u2500 Output directories \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst OUT_DIR = path.resolve('dist');\r\nconst API_DIR = path.join(OUT_DIR, 'api');\r\nconst PAGES_DIR_ = path.join(OUT_DIR, 'pages');\r\nconst STATIC_DIR = path.join(OUT_DIR, 'static');\r\n\r\nfor (const dir of [API_DIR, PAGES_DIR_, STATIC_DIR])\r\n fs.mkdirSync(dir, { recursive: true });\r\n\r\n// \u2500\u2500\u2500 Config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst config = await loadConfig();\r\nconst SERVER_DIR = path.resolve(config.serverDir);\r\nconst PAGES_DIR = path.resolve('./app/pages');\r\nconst PUBLIC_DIR = path.resolve('./app/public');\r\n\r\n// \u2500\u2500\u2500 Route manifest \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\ninterface ManifestEntry {\r\n /** Regex string matching the URL path, e.g. '^/users/([^/]+)$' */\r\n srcRegex: string;\r\n /** Names of captured groups in srcRegex order */\r\n paramNames: string[];\r\n /** Subset of paramNames whose runtime values are string[] (catch-all params) */\r\n catchAllNames: string[];\r\n /** Path to the bundled handler relative to dist/, e.g. 'api/users/[id].mjs' */\r\n handler: string;\r\n type: 'api' | 'page';\r\n}\r\n\r\nconst manifest: ManifestEntry[] = [];\r\n\r\n// \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/** Converts a funcPath like '/api/users/[id]' to a filename 'users/[id].mjs'. */\r\nfunction funcPathToFilename(funcPath: string, prefix: 'api' | 'page'): string {\r\n return funcPath.replace(new RegExp(`^\\\\/${prefix}\\\\/`), '') + '.mjs';\r\n}\r\n\r\n// \u2500\u2500\u2500 API routes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst apiFiles = walkFiles(SERVER_DIR);\r\nif (apiFiles.length === 0) console.warn(`\u26A0 No server files found in ${SERVER_DIR}`);\r\n\r\nconst apiRoutes = apiFiles\r\n .map(relPath => ({ ...analyzeFile(relPath, 'api'), absPath: path.join(SERVER_DIR, relPath) }))\r\n .sort((a, b) => b.specificity - a.specificity);\r\n\r\nfor (const { srcRegex, paramNames, catchAllNames, funcPath, absPath } of apiRoutes) {\r\n console.log(` building ${path.relative(SERVER_DIR, absPath)} \u2192 ${funcPath}`);\r\n\r\n const filename = funcPathToFilename(funcPath, 'api');\r\n const outPath = path.join(API_DIR, filename);\r\n fs.mkdirSync(path.dirname(outPath), { recursive: true });\r\n fs.writeFileSync(outPath, await bundleApiHandler(absPath));\r\n\r\n manifest.push({ srcRegex, paramNames, catchAllNames, handler: path.join('api', filename), type: 'api' });\r\n}\r\n\r\n// \u2500\u2500\u2500 Page routes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst builtPages = await buildPages(PAGES_DIR, STATIC_DIR);\r\n\r\nfor (const { srcRegex, paramNames, catchAllNames, funcPath, bundleText } of builtPages) {\r\n const filename = funcPathToFilename(funcPath, 'page');\r\n const outPath = path.join(PAGES_DIR_, filename);\r\n fs.mkdirSync(path.dirname(outPath), { recursive: true });\r\n fs.writeFileSync(outPath, bundleText);\r\n\r\n manifest.push({ srcRegex, paramNames, catchAllNames, handler: path.join('pages', filename), type: 'page' });\r\n}\r\n\r\n// \u2500\u2500\u2500 Manifest \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n// Entries are already sorted most-specific first (both loops sort before\r\n// iterating), so the runtime can match top-to-bottom.\r\nfs.writeFileSync(\r\n path.join(OUT_DIR, 'manifest.json'),\r\n JSON.stringify({ routes: manifest }, null, 2),\r\n);\r\n\r\n// \u2500\u2500\u2500 Static assets \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nawait buildCombinedBundle(STATIC_DIR);\r\ncopyPublicFiles(PUBLIC_DIR, STATIC_DIR);\r\n\r\n// \u2500\u2500\u2500 Server entry \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n// A thin HTTP server that reads manifest.json at startup and dispatches\r\n// incoming requests to the correct pre-built handler module.\r\n\r\nconst MIME_MAP_ENTRIES = `\r\n '.html': 'text/html; charset=utf-8',\r\n '.htm': 'text/html; charset=utf-8',\r\n '.css': 'text/css; charset=utf-8',\r\n '.js': 'application/javascript; charset=utf-8',\r\n '.mjs': 'application/javascript; charset=utf-8',\r\n '.cjs': 'application/javascript; charset=utf-8',\r\n '.map': 'application/json; charset=utf-8',\r\n '.json': 'application/json; charset=utf-8',\r\n '.xml': 'application/xml; charset=utf-8',\r\n '.txt': 'text/plain; charset=utf-8',\r\n '.csv': 'text/csv; charset=utf-8',\r\n '.png': 'image/png',\r\n '.jpg': 'image/jpeg',\r\n '.jpeg': 'image/jpeg',\r\n '.gif': 'image/gif',\r\n '.webp': 'image/webp',\r\n '.avif': 'image/avif',\r\n '.svg': 'image/svg+xml',\r\n '.ico': 'image/x-icon',\r\n '.bmp': 'image/bmp',\r\n '.woff': 'font/woff',\r\n '.woff2':'font/woff2',\r\n '.ttf': 'font/ttf',\r\n '.otf': 'font/otf',\r\n '.mp4': 'video/mp4',\r\n '.webm': 'video/webm',\r\n '.mp3': 'audio/mpeg',\r\n '.wav': 'audio/wav',\r\n '.ogg': 'audio/ogg',\r\n '.pdf': 'application/pdf',\r\n '.wasm': 'application/wasm',\r\n`.trim();\r\n\r\nconst serverEntry = `\\\r\nimport http from 'http';\r\nimport path from 'path';\r\nimport fs from 'fs';\r\nimport { fileURLToPath, pathToFileURL } from 'url';\r\n\r\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\r\n\r\nconst { routes } = JSON.parse(fs.readFileSync(path.join(__dirname, 'manifest.json'), 'utf-8'));\r\nconst compiled = routes.map(r => ({ ...r, regex: new RegExp(r.srcRegex) }));\r\n\r\nconst STATIC_DIR = path.join(__dirname, 'static');\r\nconst MIME_MAP = { ${MIME_MAP_ENTRIES} };\r\n\r\nconst server = http.createServer(async (req, res) => {\r\n const url = req.url || '/';\r\n const clean = url.split('?')[0];\r\n\r\n // 1. Static files \u2014 app/public/ files are copied into STATIC_DIR last at\r\n // build time (after framework bundles), so they take priority over\r\n // framework files on name collision. path.join normalises '..' segments\r\n // before the startsWith guard, preventing directory traversal.\r\n {\r\n const candidate = path.join(STATIC_DIR, clean);\r\n const staticBase = STATIC_DIR.endsWith(path.sep) ? STATIC_DIR : STATIC_DIR + path.sep;\r\n if (\r\n candidate.startsWith(staticBase) &&\r\n candidate !== STATIC_DIR &&\r\n fs.existsSync(candidate) &&\r\n fs.statSync(candidate).isFile()\r\n ) {\r\n res.setHeader('Content-Type', MIME_MAP[path.extname(candidate)] ?? 'application/octet-stream');\r\n res.end(fs.readFileSync(candidate));\r\n return;\r\n }\r\n }\r\n\r\n // 2. Route dispatch \u2014 API routes appear before page routes in the manifest\r\n // (built in build-node.ts), so they are matched first.\r\n for (const { regex, paramNames, catchAllNames, handler } of compiled) {\r\n const m = clean.match(regex);\r\n if (!m) continue;\r\n\r\n const catchAllSet = new Set(catchAllNames);\r\n const qs = new URLSearchParams(Object.fromEntries(new URL(url, 'http://localhost').searchParams));\r\n paramNames.forEach((name, i) => {\r\n const raw = m[i + 1] ?? '';\r\n if (catchAllSet.has(name)) {\r\n raw.split('/').filter(Boolean).forEach(seg => qs.append(name, seg));\r\n } else {\r\n qs.set(name, raw);\r\n }\r\n });\r\n req.url = clean + (qs.toString() ? '?' + qs.toString() : '');\r\n\r\n const mod = await import(pathToFileURL(path.join(__dirname, handler)).href);\r\n await mod.default(req, res);\r\n return;\r\n }\r\n\r\n res.statusCode = 404;\r\n res.setHeader('Content-Type', 'text/plain');\r\n res.end('Not found');\r\n});\r\n\r\nconst PORT = Number(process.env.PORT ?? 3000);\r\nserver.listen(PORT, () => console.log('nukejs built server listening on http://localhost:' + PORT));\r\n`;\r\n\r\nfs.writeFileSync(path.join(OUT_DIR, 'index.mjs'), serverEntry);\r\n\r\nconsole.log(`\\n\u2713 Node build complete \u2014 ${manifest.length} route(s) \u2192 dist/`);\r\nconsole.log(' run with: node dist/index.mjs');"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\r\n * build-node.ts \u2014 Node.js Production Build\r\n *\r\n * Produces a self-contained dist/ directory that runs with:\r\n * node dist/index.mjs\r\n *\r\n * Output layout:\r\n * dist/\r\n * index.mjs \u2190 HTTP server entry point (routing: static \u2192 framework \u2192 api \u2192 pages)\r\n * manifest.json \u2190 Route \u2192 handler mapping\r\n * api/<route>.mjs \u2190 Bundled API handlers\r\n * pages/<route>.mjs \u2190 Bundled page handlers\r\n * static/ \u2190 __n.js, client components, public files\r\n */\r\n\r\nimport fs from 'fs';\r\nimport path from 'path';\r\n\r\nimport { loadConfig } from './config';\r\nimport {\r\n analyzeFile,\r\n walkFiles,\r\n buildPages,\r\n bundleApiHandler,\r\n buildCombinedBundle,\r\n copyPublicFiles,\r\n type AnalyzedRoute,\r\n} from './build-common';\r\n\r\n// \u2500\u2500\u2500 Output directories \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst OUT_DIR = path.resolve('dist');\r\nconst API_DIR = path.join(OUT_DIR, 'api');\r\nconst PAGES_DIR_ = path.join(OUT_DIR, 'pages');\r\nconst STATIC_DIR = path.join(OUT_DIR, 'static');\r\n\r\n// Clean the entire dist/ folder before building so stale bundles, removed\r\n// routes, and renamed pages don't linger in the output.\r\nif (fs.existsSync(OUT_DIR)) {\r\n fs.rmSync(OUT_DIR, { recursive: true, force: true });\r\n console.log('\uD83D\uDDD1\uFE0F Cleaned dist/');\r\n}\r\n\r\nfor (const dir of [API_DIR, PAGES_DIR_, STATIC_DIR])\r\n fs.mkdirSync(dir, { recursive: true });\r\n\r\n// \u2500\u2500\u2500 Config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst config = await loadConfig();\r\nconst SERVER_DIR = path.resolve(config.serverDir);\r\nconst PAGES_DIR = path.resolve('./app/pages');\r\nconst PUBLIC_DIR = path.resolve('./app/public');\r\n\r\n// \u2500\u2500\u2500 Route manifest \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\ninterface ManifestEntry {\r\n /** Regex string matching the URL path, e.g. '^/users/([^/]+)$' */\r\n srcRegex: string;\r\n /** Names of captured groups in srcRegex order */\r\n paramNames: string[];\r\n /** Subset of paramNames whose runtime values are string[] (catch-all params) */\r\n catchAllNames: string[];\r\n /** Path to the bundled handler relative to dist/, e.g. 'api/users/[id].mjs' */\r\n handler: string;\r\n type: 'api' | 'page';\r\n}\r\n\r\nconst manifest: ManifestEntry[] = [];\r\n\r\n// \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/** Converts a funcPath like '/api/users/[id]' to a filename 'users/[id].mjs'. */\r\nfunction funcPathToFilename(funcPath: string, prefix: 'api' | 'page'): string {\r\n return funcPath.replace(new RegExp(`^\\\\/${prefix}\\\\/`), '') + '.mjs';\r\n}\r\n\r\n// \u2500\u2500\u2500 API routes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst apiFiles = walkFiles(SERVER_DIR);\r\nif (apiFiles.length === 0) console.warn(`\u26A0 No server files found in ${SERVER_DIR}`);\r\n\r\nconst apiRoutes = apiFiles\r\n .map(relPath => ({ ...analyzeFile(relPath, 'api'), absPath: path.join(SERVER_DIR, relPath) }))\r\n .sort((a, b) => b.specificity - a.specificity);\r\n\r\nfor (const { srcRegex, paramNames, catchAllNames, funcPath, absPath } of apiRoutes) {\r\n console.log(` building ${path.relative(SERVER_DIR, absPath)} \u2192 ${funcPath}`);\r\n\r\n const filename = funcPathToFilename(funcPath, 'api');\r\n const outPath = path.join(API_DIR, filename);\r\n fs.mkdirSync(path.dirname(outPath), { recursive: true });\r\n fs.writeFileSync(outPath, await bundleApiHandler(absPath));\r\n\r\n manifest.push({ srcRegex, paramNames, catchAllNames, handler: path.join('api', filename), type: 'api' });\r\n}\r\n\r\n// \u2500\u2500\u2500 Page routes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst builtPages = await buildPages(PAGES_DIR, STATIC_DIR);\r\n\r\nfor (const { srcRegex, paramNames, catchAllNames, funcPath, bundleText } of builtPages) {\r\n const filename = funcPathToFilename(funcPath, 'page');\r\n const outPath = path.join(PAGES_DIR_, filename);\r\n fs.mkdirSync(path.dirname(outPath), { recursive: true });\r\n fs.writeFileSync(outPath, bundleText);\r\n\r\n manifest.push({ srcRegex, paramNames, catchAllNames, handler: path.join('pages', filename), type: 'page' });\r\n}\r\n\r\n// \u2500\u2500\u2500 Manifest \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n// Entries are already sorted most-specific first (both loops sort before\r\n// iterating), so the runtime can match top-to-bottom.\r\nfs.writeFileSync(\r\n path.join(OUT_DIR, 'manifest.json'),\r\n JSON.stringify({ routes: manifest }, null, 2),\r\n);\r\n\r\n// \u2500\u2500\u2500 Static assets \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nawait buildCombinedBundle(STATIC_DIR);\r\ncopyPublicFiles(PUBLIC_DIR, STATIC_DIR);\r\n\r\n// \u2500\u2500\u2500 Server entry \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n// A thin HTTP server that reads manifest.json at startup and dispatches\r\n// incoming requests to the correct pre-built handler module.\r\n\r\nconst MIME_MAP_ENTRIES = `\r\n '.html': 'text/html; charset=utf-8',\r\n '.htm': 'text/html; charset=utf-8',\r\n '.css': 'text/css; charset=utf-8',\r\n '.js': 'application/javascript; charset=utf-8',\r\n '.mjs': 'application/javascript; charset=utf-8',\r\n '.cjs': 'application/javascript; charset=utf-8',\r\n '.map': 'application/json; charset=utf-8',\r\n '.json': 'application/json; charset=utf-8',\r\n '.xml': 'application/xml; charset=utf-8',\r\n '.txt': 'text/plain; charset=utf-8',\r\n '.csv': 'text/csv; charset=utf-8',\r\n '.png': 'image/png',\r\n '.jpg': 'image/jpeg',\r\n '.jpeg': 'image/jpeg',\r\n '.gif': 'image/gif',\r\n '.webp': 'image/webp',\r\n '.avif': 'image/avif',\r\n '.svg': 'image/svg+xml',\r\n '.ico': 'image/x-icon',\r\n '.bmp': 'image/bmp',\r\n '.woff': 'font/woff',\r\n '.woff2':'font/woff2',\r\n '.ttf': 'font/ttf',\r\n '.otf': 'font/otf',\r\n '.mp4': 'video/mp4',\r\n '.webm': 'video/webm',\r\n '.mp3': 'audio/mpeg',\r\n '.wav': 'audio/wav',\r\n '.ogg': 'audio/ogg',\r\n '.pdf': 'application/pdf',\r\n '.wasm': 'application/wasm',\r\n`.trim();\r\n\r\nconst serverEntry = `\\\r\nimport http from 'http';\r\nimport path from 'path';\r\nimport fs from 'fs';\r\nimport { fileURLToPath, pathToFileURL } from 'url';\r\n\r\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\r\n\r\nconst { routes } = JSON.parse(fs.readFileSync(path.join(__dirname, 'manifest.json'), 'utf-8'));\r\nconst compiled = routes.map(r => ({ ...r, regex: new RegExp(r.srcRegex) }));\r\n\r\nconst STATIC_DIR = path.join(__dirname, 'static');\r\nconst MIME_MAP = { ${MIME_MAP_ENTRIES} };\r\n\r\nconst server = http.createServer(async (req, res) => {\r\n const url = req.url || '/';\r\n const clean = url.split('?')[0];\r\n\r\n // 1. Static files \u2014 app/public/ files are copied into STATIC_DIR last at\r\n // build time (after framework bundles), so they take priority over\r\n // framework files on name collision. path.join normalises '..' segments\r\n // before the startsWith guard, preventing directory traversal.\r\n {\r\n const candidate = path.join(STATIC_DIR, clean);\r\n const staticBase = STATIC_DIR.endsWith(path.sep) ? STATIC_DIR : STATIC_DIR + path.sep;\r\n if (\r\n candidate.startsWith(staticBase) &&\r\n candidate !== STATIC_DIR &&\r\n fs.existsSync(candidate) &&\r\n fs.statSync(candidate).isFile()\r\n ) {\r\n res.setHeader('Content-Type', MIME_MAP[path.extname(candidate)] ?? 'application/octet-stream');\r\n res.end(fs.readFileSync(candidate));\r\n return;\r\n }\r\n }\r\n\r\n // 2. Route dispatch \u2014 API routes appear before page routes in the manifest\r\n // (built in build-node.ts), so they are matched first.\r\n for (const { regex, paramNames, catchAllNames, handler } of compiled) {\r\n const m = clean.match(regex);\r\n if (!m) continue;\r\n\r\n const catchAllSet = new Set(catchAllNames);\r\n const qs = new URLSearchParams(Object.fromEntries(new URL(url, 'http://localhost').searchParams));\r\n paramNames.forEach((name, i) => {\r\n const raw = m[i + 1] ?? '';\r\n if (catchAllSet.has(name)) {\r\n raw.split('/').filter(Boolean).forEach(seg => qs.append(name, seg));\r\n } else {\r\n qs.set(name, raw);\r\n }\r\n });\r\n req.url = clean + (qs.toString() ? '?' + qs.toString() : '');\r\n\r\n const mod = await import(pathToFileURL(path.join(__dirname, handler)).href);\r\n await mod.default(req, res);\r\n return;\r\n }\r\n\r\n res.statusCode = 404;\r\n res.setHeader('Content-Type', 'text/plain');\r\n res.end('Not found');\r\n});\r\n\r\nconst PORT = Number(process.env.PORT ?? 3000);\r\nserver.listen(PORT, () => console.log('nukejs built server listening on http://localhost:' + PORT));\r\n`;\r\n\r\nfs.writeFileSync(path.join(OUT_DIR, 'index.mjs'), serverEntry);\r\n\r\nconsole.log(`\\n\u2713 Node build complete \u2014 ${manifest.length} route(s) \u2192 dist/`);\r\nconsole.log(' run with: node dist/index.mjs');"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -18,6 +18,10 @@ import {
18
18
  const OUTPUT_DIR = path.resolve(".vercel/output");
19
19
  const FUNCTIONS_DIR = path.join(OUTPUT_DIR, "functions");
20
20
  const STATIC_DIR = path.join(OUTPUT_DIR, "static");
21
+ if (fs.existsSync(OUTPUT_DIR)) {
22
+ fs.rmSync(OUTPUT_DIR, { recursive: true, force: true });
23
+ console.log("\u{1F5D1}\uFE0F Cleaned .vercel/output/");
24
+ }
21
25
  for (const dir of [FUNCTIONS_DIR, STATIC_DIR])
22
26
  fs.mkdirSync(dir, { recursive: true });
23
27
  const config = await loadConfig();
@@ -235,6 +239,7 @@ if (serverPages.length > 0) {
235
239
  allClientIds: [...registry.keys()],
236
240
  layoutArrayItems: layoutPaths.map((_, i) => `__layout_${i}__`).join(", "),
237
241
  prerenderedHtml: prerenderedRecord,
242
+ routeParamNames: page.paramNames,
238
243
  catchAllNames: page.catchAllNames
239
244
  })
240
245
  );
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/build-vercel.ts"],
4
- "sourcesContent": ["/**\r\n * build-vercel.ts \u2014 Vercel Production Build\r\n *\r\n * Produces a .vercel/output/ directory conforming to the Vercel Build Output\r\n * API v3. Two serverless functions are emitted:\r\n *\r\n * api.func/ \u2190 single dispatcher bundling all API route handlers\r\n * pages.func/ \u2190 single dispatcher bundling all SSR page handlers\r\n *\r\n * Static assets (React runtime, client components, public files) go to\r\n * .vercel/output/static/ and are served by Vercel's CDN directly.\r\n *\r\n * Notes on bundling strategy:\r\n * - npm packages are FULLY BUNDLED (no node_modules at Vercel runtime).\r\n * - Node built-ins are kept external (available in the nodejs20.x runtime).\r\n * - A createRequire banner lets CJS packages (mongoose, etc.) resolve Node\r\n * built-ins correctly inside the ESM output bundle.\r\n */\r\n\r\nimport fs from 'fs';\r\nimport path from 'path';\r\nimport { randomBytes } from 'node:crypto';\r\nimport { build } from 'esbuild';\r\n\r\nimport { loadConfig } from './config';\r\nimport {\r\n walkFiles,\r\n analyzeFile,\r\n collectServerPages,\r\n collectGlobalClientRegistry,\r\n bundleClientComponents,\r\n findPageLayouts,\r\n buildPerPageRegistry,\r\n makePageAdapterSource,\r\n buildCombinedBundle,\r\n copyPublicFiles,\r\n} from './build-common';\r\n\r\n// \u2500\u2500\u2500 Output directories \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst OUTPUT_DIR = path.resolve('.vercel/output');\r\nconst FUNCTIONS_DIR = path.join(OUTPUT_DIR, 'functions');\r\nconst STATIC_DIR = path.join(OUTPUT_DIR, 'static');\r\n\r\nfor (const dir of [FUNCTIONS_DIR, STATIC_DIR])\r\n fs.mkdirSync(dir, { recursive: true });\r\n\r\n// \u2500\u2500\u2500 Config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst config = await loadConfig();\r\nconst SERVER_DIR = path.resolve(config.serverDir);\r\nconst PAGES_DIR = path.resolve('./app/pages');\r\nconst PUBLIC_DIR = path.resolve('./app/public');\r\n\r\n// \u2500\u2500\u2500 Shared esbuild config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Node built-ins that should never be bundled.\r\n * npm packages are intentionally absent \u2014 they must be bundled because\r\n * Vercel serverless functions have no node_modules at runtime.\r\n */\r\nconst NODE_BUILTINS = [\r\n 'node:*',\r\n 'http', 'https', 'fs', 'path', 'url', 'crypto', 'stream', 'buffer',\r\n 'events', 'util', 'os', 'net', 'tls', 'child_process', 'worker_threads',\r\n 'cluster', 'dgram', 'dns', 'readline', 'zlib', 'assert', 'module',\r\n 'perf_hooks', 'string_decoder', 'timers', 'async_hooks', 'v8', 'vm',\r\n];\r\n\r\n/**\r\n * Banner injected at the top of every Vercel function bundle.\r\n *\r\n * Why it's needed: esbuild bundles CJS packages (mongoose, etc.) into ESM\r\n * output and replaces their require() calls with a __require2 shim. That\r\n * shim cannot resolve Node built-ins on its own inside an ESM module scope.\r\n * Injecting a real require (backed by createRequire) fixes the shim so that\r\n * dynamic require('crypto'), require('stream'), etc. work correctly.\r\n */\r\nconst CJS_COMPAT_BANNER = {\r\n js: `import { createRequire } from 'module';\\nconst require = createRequire(import.meta.url);`,\r\n};\r\n\r\n// \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\ntype VercelRoute = { src: string; dest: string } | { handle: 'filesystem' };\r\n\r\n/** Writes a bundled dispatcher into a Vercel .func directory. */\r\nfunction emitVercelFunction(name: string, bundleText: string): void {\r\n const funcDir = path.join(FUNCTIONS_DIR, `${name}.func`);\r\n fs.mkdirSync(funcDir, { recursive: true });\r\n fs.writeFileSync(path.join(funcDir, 'index.mjs'), bundleText);\r\n fs.writeFileSync(\r\n path.join(funcDir, '.vc-config.json'),\r\n JSON.stringify({ runtime: 'nodejs20.x', handler: 'index.mjs', launcherType: 'Nodejs' }, null, 2),\r\n );\r\n}\r\n\r\n// \u2500\u2500\u2500 API dispatcher source \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Generates a single TypeScript dispatcher that imports every API route module,\r\n * matches the incoming URL against each route's regex, injects captured params,\r\n * and calls the right HTTP-method export (GET, POST, \u2026) or default export.\r\n */\r\nfunction makeApiDispatcherSource(\r\n routes: Array<{ absPath: string; srcRegex: string; paramNames: string[] }>,\r\n): string {\r\n const imports = routes\r\n .map((r, i) => `import * as __api_${i}__ from ${JSON.stringify(r.absPath)};`)\r\n .join('\\n');\r\n\r\n const routeEntries = routes\r\n .map((r, i) =>\r\n ` { regex: ${JSON.stringify(r.srcRegex)}, params: ${JSON.stringify(r.paramNames)}, mod: __api_${i}__ },`,\r\n )\r\n .join('\\n');\r\n\r\n return `\\\r\nimport type { IncomingMessage, ServerResponse } from 'http';\r\n${imports}\r\n\r\nfunction enhance(res: ServerResponse) {\r\n (res as any).json = function(data: any, status = 200) {\r\n this.statusCode = status;\r\n this.setHeader('Content-Type', 'application/json');\r\n this.end(JSON.stringify(data));\r\n };\r\n (res as any).status = function(code: number) { this.statusCode = code; return this; };\r\n return res;\r\n}\r\n\r\nasync function parseBody(req: IncomingMessage): Promise<any> {\r\n return new Promise((resolve, reject) => {\r\n let body = '';\r\n req.on('data', (chunk: any) => { body += chunk.toString(); });\r\n req.on('end', () => {\r\n try {\r\n resolve(\r\n body && req.headers['content-type']?.includes('application/json')\r\n ? JSON.parse(body)\r\n : body,\r\n );\r\n } catch (e) { reject(e); }\r\n });\r\n req.on('error', reject);\r\n });\r\n}\r\n\r\nconst ROUTES = [\r\n${routeEntries}\r\n];\r\n\r\nexport default async function handler(req: IncomingMessage, res: ServerResponse) {\r\n const url = new URL(req.url || '/', 'http://localhost');\r\n const pathname = url.pathname;\r\n\r\n for (const route of ROUTES) {\r\n const m = pathname.match(new RegExp(route.regex));\r\n if (!m) continue;\r\n\r\n const method = (req.method || 'GET').toUpperCase();\r\n const apiRes = enhance(res);\r\n const apiReq = req as any;\r\n\r\n apiReq.body = await parseBody(req);\r\n apiReq.query = Object.fromEntries(url.searchParams);\r\n apiReq.params = {};\r\n route.params.forEach((name: string, i: number) => { apiReq.params[name] = m[i + 1]; });\r\n\r\n const fn = (route.mod as any)[method] ?? (route.mod as any)['default'];\r\n if (typeof fn !== 'function') {\r\n (apiRes as any).json({ error: \\`Method \\${method} not allowed\\` }, 405);\r\n return;\r\n }\r\n await fn(apiReq, apiRes);\r\n return;\r\n }\r\n\r\n res.statusCode = 404;\r\n res.setHeader('Content-Type', 'application/json');\r\n res.end(JSON.stringify({ error: 'Not Found' }));\r\n}\r\n`;\r\n}\r\n\r\n// \u2500\u2500\u2500 Pages dispatcher source \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Generates a TypeScript dispatcher that imports each page's pre-generated\r\n * adapter, matches the incoming URL, encodes captured dynamic params as\r\n * query-string values (catch-all params use repeated keys), then delegates\r\n * to the matching handler.\r\n */\r\nfunction makePagesDispatcherSource(\r\n routes: Array<{\r\n adapterPath: string;\r\n srcRegex: string;\r\n paramNames: string[];\r\n catchAllNames: string[];\r\n }>,\r\n): string {\r\n const imports = routes\r\n .map((r, i) => `import __page_${i}__ from ${JSON.stringify(r.adapterPath)};`)\r\n .join('\\n');\r\n\r\n const routeEntries = routes\r\n .map((r, i) =>\r\n ` { regex: ${JSON.stringify(r.srcRegex)}, params: ${JSON.stringify(r.paramNames)}, catchAll: ${JSON.stringify(r.catchAllNames)}, handler: __page_${i}__ },`,\r\n )\r\n .join('\\n');\r\n\r\n return `\\\r\nimport type { IncomingMessage, ServerResponse } from 'http';\r\n${imports}\r\n\r\nconst ROUTES: Array<{\r\n regex: string;\r\n params: string[];\r\n catchAll: string[];\r\n handler: (req: IncomingMessage, res: ServerResponse) => Promise<void>;\r\n}> = [\r\n${routeEntries}\r\n];\r\n\r\nexport default async function handler(req: IncomingMessage, res: ServerResponse) {\r\n const url = new URL(req.url || '/', 'http://localhost');\r\n const pathname = url.pathname;\r\n\r\n for (const route of ROUTES) {\r\n const m = pathname.match(new RegExp(route.regex));\r\n if (!m) continue;\r\n\r\n const catchAllSet = new Set(route.catchAll);\r\n route.params.forEach((name, i) => {\r\n const raw = m[i + 1] ?? '';\r\n if (catchAllSet.has(name)) {\r\n // Encode catch-all as repeated keys so the handler can getAll() \u2192 string[]\r\n raw.split('/').filter(Boolean).forEach(seg => url.searchParams.append(name, seg));\r\n } else {\r\n url.searchParams.set(name, raw);\r\n }\r\n });\r\n req.url = pathname + (url.search || '');\r\n\r\n return route.handler(req, res);\r\n }\r\n\r\n res.statusCode = 404;\r\n res.setHeader('Content-Type', 'text/plain; charset=utf-8');\r\n res.end('Not Found');\r\n}\r\n`;\r\n}\r\n\r\n// \u2500\u2500\u2500 Build API function \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst vercelRoutes: VercelRoute[] = [];\r\n\r\nconst apiFiles = walkFiles(SERVER_DIR);\r\nif (apiFiles.length === 0) console.warn(`\u26A0 No server files found in ${SERVER_DIR}`);\r\n\r\nconst apiRoutes = apiFiles\r\n .map(relPath => ({ ...analyzeFile(relPath, 'api'), absPath: path.join(SERVER_DIR, relPath) }))\r\n .sort((a, b) => b.specificity - a.specificity);\r\n\r\nif (apiRoutes.length > 0) {\r\n const dispatcherPath = path.join(SERVER_DIR, `_api_dispatcher_${randomBytes(4).toString('hex')}.ts`);\r\n fs.writeFileSync(dispatcherPath, makeApiDispatcherSource(apiRoutes));\r\n\r\n try {\r\n const result = await build({\r\n entryPoints: [dispatcherPath],\r\n bundle: true,\r\n format: 'esm',\r\n platform: 'node',\r\n target: 'node20',\r\n banner: CJS_COMPAT_BANNER,\r\n external: NODE_BUILTINS,\r\n write: false,\r\n });\r\n emitVercelFunction('api', result.outputFiles[0].text);\r\n console.log(` built API dispatcher \u2192 api.func (${apiRoutes.length} route(s))`);\r\n } finally {\r\n fs.unlinkSync(dispatcherPath);\r\n }\r\n\r\n // API routes are listed before pages in config.json so they win on any\r\n // URL collision. Static files in .vercel/output/static/ (app/public +\r\n // framework bundles) are served by Vercel's CDN before any route is checked.\r\n for (const { srcRegex } of apiRoutes)\r\n vercelRoutes.push({ src: srcRegex, dest: '/api' });\r\n}\r\n\r\n// \u2500\u2500\u2500 Build Pages function \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst serverPages = collectServerPages(PAGES_DIR);\r\n\r\nif (serverPages.length > 0) {\r\n // Pass 1 \u2014 bundle all client components to static files.\r\n const globalRegistry = collectGlobalClientRegistry(serverPages, PAGES_DIR);\r\n const prerenderedHtml = await bundleClientComponents(globalRegistry, PAGES_DIR, STATIC_DIR);\r\n const prerenderedRecord = Object.fromEntries(prerenderedHtml);\r\n\r\n // Pass 2 \u2014 write one temp adapter per page next to its source file (so\r\n // relative imports resolve correctly), then bundle everything in\r\n // one esbuild pass via the dispatcher.\r\n const tempAdapterPaths: string[] = [];\r\n\r\n for (const page of serverPages) {\r\n const adapterDir = path.dirname(page.absPath);\r\n const adapterPath = path.join(adapterDir, `_page_adapter_${randomBytes(4).toString('hex')}.ts`);\r\n\r\n const layoutPaths = findPageLayouts(page.absPath, PAGES_DIR);\r\n const { registry, clientComponentNames } = buildPerPageRegistry(page.absPath, layoutPaths, PAGES_DIR);\r\n\r\n const layoutImports = layoutPaths\r\n .map((lp, i) => {\r\n const rel = path.relative(adapterDir, lp).replace(/\\\\/g, '/');\r\n return `import __layout_${i}__ from ${JSON.stringify(rel.startsWith('.') ? rel : './' + rel)};`;\r\n })\r\n .join('\\n');\r\n\r\n fs.writeFileSync(\r\n adapterPath,\r\n makePageAdapterSource({\r\n pageImport: JSON.stringify('./' + path.basename(page.absPath)),\r\n layoutImports,\r\n clientComponentNames,\r\n allClientIds: [...registry.keys()],\r\n layoutArrayItems: layoutPaths.map((_, i) => `__layout_${i}__`).join(', '),\r\n prerenderedHtml: prerenderedRecord,\r\n catchAllNames: page.catchAllNames,\r\n }),\r\n );\r\n\r\n tempAdapterPaths.push(adapterPath);\r\n console.log(` prepared ${path.relative(PAGES_DIR, page.absPath)} \u2192 ${page.funcPath} [page]`);\r\n }\r\n\r\n const dispatcherRoutes = serverPages.map((page, i) => ({\r\n adapterPath: tempAdapterPaths[i],\r\n srcRegex: page.srcRegex,\r\n paramNames: page.paramNames,\r\n catchAllNames: page.catchAllNames,\r\n }));\r\n\r\n const dispatcherPath = path.join(PAGES_DIR, `_pages_dispatcher_${randomBytes(4).toString('hex')}.ts`);\r\n fs.writeFileSync(dispatcherPath, makePagesDispatcherSource(dispatcherRoutes));\r\n\r\n try {\r\n const result = await build({\r\n entryPoints: [dispatcherPath],\r\n bundle: true,\r\n format: 'esm',\r\n platform: 'node',\r\n target: 'node20',\r\n jsx: 'automatic',\r\n banner: CJS_COMPAT_BANNER,\r\n external: NODE_BUILTINS,\r\n define: { 'process.env.NODE_ENV': '\"production\"' },\r\n write: false,\r\n });\r\n emitVercelFunction('pages', result.outputFiles[0].text);\r\n console.log(` built Pages dispatcher \u2192 pages.func (${serverPages.length} page(s))`);\r\n } finally {\r\n fs.unlinkSync(dispatcherPath);\r\n for (const p of tempAdapterPaths) if (fs.existsSync(p)) fs.unlinkSync(p);\r\n }\r\n\r\n for (const { srcRegex } of serverPages)\r\n vercelRoutes.push({ src: srcRegex, dest: '/pages' });\r\n}\r\n\r\n// \u2500\u2500\u2500 Vercel config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n// `{ handle: 'filesystem' }` instructs Vercel's routing layer to check\r\n// .vercel/output/static/ BEFORE evaluating any of our dynamic route rules.\r\n// Without this, an optional catch-all like [[page]].tsx would intercept\r\n// /__n.js, /__react.js, and app/public/* before the CDN can serve them.\r\nfs.writeFileSync(\r\n path.join(OUTPUT_DIR, 'config.json'),\r\n JSON.stringify({ version: 3, routes: [{ handle: 'filesystem' }, ...vercelRoutes] }, null, 2),\r\n);\r\nfs.writeFileSync(\r\n path.resolve('vercel.json'),\r\n JSON.stringify({ runtime: 'nodejs20.x' }, null, 2),\r\n);\r\n\r\n// \u2500\u2500\u2500 Static assets \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nawait buildCombinedBundle(STATIC_DIR);\r\ncopyPublicFiles(PUBLIC_DIR, STATIC_DIR);\r\n\r\nconst fnCount = (apiRoutes.length > 0 ? 1 : 0) + (serverPages.length > 0 ? 1 : 0);\r\nconsole.log(`\\n\u2713 Vercel build complete \u2014 ${fnCount} function(s) \u2192 .vercel/output`);"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\r\n * build-vercel.ts \u2014 Vercel Production Build\r\n *\r\n * Produces a .vercel/output/ directory conforming to the Vercel Build Output\r\n * API v3. Two serverless functions are emitted:\r\n *\r\n * api.func/ \u2190 single dispatcher bundling all API route handlers\r\n * pages.func/ \u2190 single dispatcher bundling all SSR page handlers\r\n *\r\n * Static assets (React runtime, client components, public files) go to\r\n * .vercel/output/static/ and are served by Vercel's CDN directly.\r\n *\r\n * Notes on bundling strategy:\r\n * - npm packages are FULLY BUNDLED (no node_modules at Vercel runtime).\r\n * - Node built-ins are kept external (available in the nodejs20.x runtime).\r\n * - A createRequire banner lets CJS packages (mongoose, etc.) resolve Node\r\n * built-ins correctly inside the ESM output bundle.\r\n */\r\n\r\nimport fs from 'fs';\r\nimport path from 'path';\r\nimport { randomBytes } from 'node:crypto';\r\nimport { build } from 'esbuild';\r\n\r\nimport { loadConfig } from './config';\r\nimport {\r\n walkFiles,\r\n analyzeFile,\r\n collectServerPages,\r\n collectGlobalClientRegistry,\r\n bundleClientComponents,\r\n findPageLayouts,\r\n buildPerPageRegistry,\r\n makePageAdapterSource,\r\n buildCombinedBundle,\r\n copyPublicFiles,\r\n} from './build-common';\r\n\r\n// \u2500\u2500\u2500 Output directories \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst OUTPUT_DIR = path.resolve('.vercel/output');\r\nconst FUNCTIONS_DIR = path.join(OUTPUT_DIR, 'functions');\r\nconst STATIC_DIR = path.join(OUTPUT_DIR, 'static');\r\n\r\n// Clean the entire .vercel/output/ folder before building so stale function\r\n// bundles, removed routes, and renamed pages don't linger in the output.\r\nif (fs.existsSync(OUTPUT_DIR)) {\r\n fs.rmSync(OUTPUT_DIR, { recursive: true, force: true });\r\n console.log('\uD83D\uDDD1\uFE0F Cleaned .vercel/output/');\r\n}\r\n\r\nfor (const dir of [FUNCTIONS_DIR, STATIC_DIR])\r\n fs.mkdirSync(dir, { recursive: true });\r\n\r\n// \u2500\u2500\u2500 Config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst config = await loadConfig();\r\nconst SERVER_DIR = path.resolve(config.serverDir);\r\nconst PAGES_DIR = path.resolve('./app/pages');\r\nconst PUBLIC_DIR = path.resolve('./app/public');\r\n\r\n// \u2500\u2500\u2500 Shared esbuild config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Node built-ins that should never be bundled.\r\n * npm packages are intentionally absent \u2014 they must be bundled because\r\n * Vercel serverless functions have no node_modules at runtime.\r\n */\r\nconst NODE_BUILTINS = [\r\n 'node:*',\r\n 'http', 'https', 'fs', 'path', 'url', 'crypto', 'stream', 'buffer',\r\n 'events', 'util', 'os', 'net', 'tls', 'child_process', 'worker_threads',\r\n 'cluster', 'dgram', 'dns', 'readline', 'zlib', 'assert', 'module',\r\n 'perf_hooks', 'string_decoder', 'timers', 'async_hooks', 'v8', 'vm',\r\n];\r\n\r\n/**\r\n * Banner injected at the top of every Vercel function bundle.\r\n *\r\n * Why it's needed: esbuild bundles CJS packages (mongoose, etc.) into ESM\r\n * output and replaces their require() calls with a __require2 shim. That\r\n * shim cannot resolve Node built-ins on its own inside an ESM module scope.\r\n * Injecting a real require (backed by createRequire) fixes the shim so that\r\n * dynamic require('crypto'), require('stream'), etc. work correctly.\r\n */\r\nconst CJS_COMPAT_BANNER = {\r\n js: `import { createRequire } from 'module';\\nconst require = createRequire(import.meta.url);`,\r\n};\r\n\r\n// \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\ntype VercelRoute = { src: string; dest: string } | { handle: 'filesystem' };\r\n\r\n/** Writes a bundled dispatcher into a Vercel .func directory. */\r\nfunction emitVercelFunction(name: string, bundleText: string): void {\r\n const funcDir = path.join(FUNCTIONS_DIR, `${name}.func`);\r\n fs.mkdirSync(funcDir, { recursive: true });\r\n fs.writeFileSync(path.join(funcDir, 'index.mjs'), bundleText);\r\n fs.writeFileSync(\r\n path.join(funcDir, '.vc-config.json'),\r\n JSON.stringify({ runtime: 'nodejs20.x', handler: 'index.mjs', launcherType: 'Nodejs' }, null, 2),\r\n );\r\n}\r\n\r\n// \u2500\u2500\u2500 API dispatcher source \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Generates a single TypeScript dispatcher that imports every API route module,\r\n * matches the incoming URL against each route's regex, injects captured params,\r\n * and calls the right HTTP-method export (GET, POST, \u2026) or default export.\r\n */\r\nfunction makeApiDispatcherSource(\r\n routes: Array<{ absPath: string; srcRegex: string; paramNames: string[] }>,\r\n): string {\r\n const imports = routes\r\n .map((r, i) => `import * as __api_${i}__ from ${JSON.stringify(r.absPath)};`)\r\n .join('\\n');\r\n\r\n const routeEntries = routes\r\n .map((r, i) =>\r\n ` { regex: ${JSON.stringify(r.srcRegex)}, params: ${JSON.stringify(r.paramNames)}, mod: __api_${i}__ },`,\r\n )\r\n .join('\\n');\r\n\r\n return `\\\r\nimport type { IncomingMessage, ServerResponse } from 'http';\r\n${imports}\r\n\r\nfunction enhance(res: ServerResponse) {\r\n (res as any).json = function(data: any, status = 200) {\r\n this.statusCode = status;\r\n this.setHeader('Content-Type', 'application/json');\r\n this.end(JSON.stringify(data));\r\n };\r\n (res as any).status = function(code: number) { this.statusCode = code; return this; };\r\n return res;\r\n}\r\n\r\nasync function parseBody(req: IncomingMessage): Promise<any> {\r\n return new Promise((resolve, reject) => {\r\n let body = '';\r\n req.on('data', (chunk: any) => { body += chunk.toString(); });\r\n req.on('end', () => {\r\n try {\r\n resolve(\r\n body && req.headers['content-type']?.includes('application/json')\r\n ? JSON.parse(body)\r\n : body,\r\n );\r\n } catch (e) { reject(e); }\r\n });\r\n req.on('error', reject);\r\n });\r\n}\r\n\r\nconst ROUTES = [\r\n${routeEntries}\r\n];\r\n\r\nexport default async function handler(req: IncomingMessage, res: ServerResponse) {\r\n const url = new URL(req.url || '/', 'http://localhost');\r\n const pathname = url.pathname;\r\n\r\n for (const route of ROUTES) {\r\n const m = pathname.match(new RegExp(route.regex));\r\n if (!m) continue;\r\n\r\n const method = (req.method || 'GET').toUpperCase();\r\n const apiRes = enhance(res);\r\n const apiReq = req as any;\r\n\r\n apiReq.body = await parseBody(req);\r\n apiReq.query = Object.fromEntries(url.searchParams);\r\n apiReq.params = {};\r\n route.params.forEach((name: string, i: number) => { apiReq.params[name] = m[i + 1]; });\r\n\r\n const fn = (route.mod as any)[method] ?? (route.mod as any)['default'];\r\n if (typeof fn !== 'function') {\r\n (apiRes as any).json({ error: \\`Method \\${method} not allowed\\` }, 405);\r\n return;\r\n }\r\n await fn(apiReq, apiRes);\r\n return;\r\n }\r\n\r\n res.statusCode = 404;\r\n res.setHeader('Content-Type', 'application/json');\r\n res.end(JSON.stringify({ error: 'Not Found' }));\r\n}\r\n`;\r\n}\r\n\r\n// \u2500\u2500\u2500 Pages dispatcher source \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Generates a TypeScript dispatcher that imports each page's pre-generated\r\n * adapter, matches the incoming URL, encodes captured dynamic params as\r\n * query-string values (catch-all params use repeated keys), then delegates\r\n * to the matching handler.\r\n */\r\nfunction makePagesDispatcherSource(\r\n routes: Array<{\r\n adapterPath: string;\r\n srcRegex: string;\r\n paramNames: string[];\r\n catchAllNames: string[];\r\n }>,\r\n): string {\r\n const imports = routes\r\n .map((r, i) => `import __page_${i}__ from ${JSON.stringify(r.adapterPath)};`)\r\n .join('\\n');\r\n\r\n const routeEntries = routes\r\n .map((r, i) =>\r\n ` { regex: ${JSON.stringify(r.srcRegex)}, params: ${JSON.stringify(r.paramNames)}, catchAll: ${JSON.stringify(r.catchAllNames)}, handler: __page_${i}__ },`,\r\n )\r\n .join('\\n');\r\n\r\n return `\\\r\nimport type { IncomingMessage, ServerResponse } from 'http';\r\n${imports}\r\n\r\nconst ROUTES: Array<{\r\n regex: string;\r\n params: string[];\r\n catchAll: string[];\r\n handler: (req: IncomingMessage, res: ServerResponse) => Promise<void>;\r\n}> = [\r\n${routeEntries}\r\n];\r\n\r\nexport default async function handler(req: IncomingMessage, res: ServerResponse) {\r\n const url = new URL(req.url || '/', 'http://localhost');\r\n const pathname = url.pathname;\r\n\r\n for (const route of ROUTES) {\r\n const m = pathname.match(new RegExp(route.regex));\r\n if (!m) continue;\r\n\r\n const catchAllSet = new Set(route.catchAll);\r\n route.params.forEach((name, i) => {\r\n const raw = m[i + 1] ?? '';\r\n if (catchAllSet.has(name)) {\r\n // Encode catch-all as repeated keys so the handler can getAll() \u2192 string[]\r\n raw.split('/').filter(Boolean).forEach(seg => url.searchParams.append(name, seg));\r\n } else {\r\n url.searchParams.set(name, raw);\r\n }\r\n });\r\n req.url = pathname + (url.search || '');\r\n\r\n return route.handler(req, res);\r\n }\r\n\r\n res.statusCode = 404;\r\n res.setHeader('Content-Type', 'text/plain; charset=utf-8');\r\n res.end('Not Found');\r\n}\r\n`;\r\n}\r\n\r\n// \u2500\u2500\u2500 Build API function \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst vercelRoutes: VercelRoute[] = [];\r\n\r\nconst apiFiles = walkFiles(SERVER_DIR);\r\nif (apiFiles.length === 0) console.warn(`\u26A0 No server files found in ${SERVER_DIR}`);\r\n\r\nconst apiRoutes = apiFiles\r\n .map(relPath => ({ ...analyzeFile(relPath, 'api'), absPath: path.join(SERVER_DIR, relPath) }))\r\n .sort((a, b) => b.specificity - a.specificity);\r\n\r\nif (apiRoutes.length > 0) {\r\n const dispatcherPath = path.join(SERVER_DIR, `_api_dispatcher_${randomBytes(4).toString('hex')}.ts`);\r\n fs.writeFileSync(dispatcherPath, makeApiDispatcherSource(apiRoutes));\r\n\r\n try {\r\n const result = await build({\r\n entryPoints: [dispatcherPath],\r\n bundle: true,\r\n format: 'esm',\r\n platform: 'node',\r\n target: 'node20',\r\n banner: CJS_COMPAT_BANNER,\r\n external: NODE_BUILTINS,\r\n write: false,\r\n });\r\n emitVercelFunction('api', result.outputFiles[0].text);\r\n console.log(` built API dispatcher \u2192 api.func (${apiRoutes.length} route(s))`);\r\n } finally {\r\n fs.unlinkSync(dispatcherPath);\r\n }\r\n\r\n // API routes are listed before pages in config.json so they win on any\r\n // URL collision. Static files in .vercel/output/static/ (app/public +\r\n // framework bundles) are served by Vercel's CDN before any route is checked.\r\n for (const { srcRegex } of apiRoutes)\r\n vercelRoutes.push({ src: srcRegex, dest: '/api' });\r\n}\r\n\r\n// \u2500\u2500\u2500 Build Pages function \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst serverPages = collectServerPages(PAGES_DIR);\r\n\r\nif (serverPages.length > 0) {\r\n // Pass 1 \u2014 bundle all client components to static files.\r\n const globalRegistry = collectGlobalClientRegistry(serverPages, PAGES_DIR);\r\n const prerenderedHtml = await bundleClientComponents(globalRegistry, PAGES_DIR, STATIC_DIR);\r\n const prerenderedRecord = Object.fromEntries(prerenderedHtml);\r\n\r\n // Pass 2 \u2014 write one temp adapter per page next to its source file (so\r\n // relative imports resolve correctly), then bundle everything in\r\n // one esbuild pass via the dispatcher.\r\n const tempAdapterPaths: string[] = [];\r\n\r\n for (const page of serverPages) {\r\n const adapterDir = path.dirname(page.absPath);\r\n const adapterPath = path.join(adapterDir, `_page_adapter_${randomBytes(4).toString('hex')}.ts`);\r\n\r\n const layoutPaths = findPageLayouts(page.absPath, PAGES_DIR);\r\n const { registry, clientComponentNames } = buildPerPageRegistry(page.absPath, layoutPaths, PAGES_DIR);\r\n\r\n const layoutImports = layoutPaths\r\n .map((lp, i) => {\r\n const rel = path.relative(adapterDir, lp).replace(/\\\\/g, '/');\r\n return `import __layout_${i}__ from ${JSON.stringify(rel.startsWith('.') ? rel : './' + rel)};`;\r\n })\r\n .join('\\n');\r\n\r\n fs.writeFileSync(\r\n adapterPath,\r\n makePageAdapterSource({\r\n pageImport: JSON.stringify('./' + path.basename(page.absPath)),\r\n layoutImports,\r\n clientComponentNames,\r\n allClientIds: [...registry.keys()],\r\n layoutArrayItems: layoutPaths.map((_, i) => `__layout_${i}__`).join(', '),\r\n prerenderedHtml: prerenderedRecord,\r\n routeParamNames: page.paramNames,\r\n catchAllNames: page.catchAllNames,\r\n }),\r\n );\r\n\r\n tempAdapterPaths.push(adapterPath);\r\n console.log(` prepared ${path.relative(PAGES_DIR, page.absPath)} \u2192 ${page.funcPath} [page]`);\r\n }\r\n\r\n const dispatcherRoutes = serverPages.map((page, i) => ({\r\n adapterPath: tempAdapterPaths[i],\r\n srcRegex: page.srcRegex,\r\n paramNames: page.paramNames,\r\n catchAllNames: page.catchAllNames,\r\n }));\r\n\r\n const dispatcherPath = path.join(PAGES_DIR, `_pages_dispatcher_${randomBytes(4).toString('hex')}.ts`);\r\n fs.writeFileSync(dispatcherPath, makePagesDispatcherSource(dispatcherRoutes));\r\n\r\n try {\r\n const result = await build({\r\n entryPoints: [dispatcherPath],\r\n bundle: true,\r\n format: 'esm',\r\n platform: 'node',\r\n target: 'node20',\r\n jsx: 'automatic',\r\n banner: CJS_COMPAT_BANNER,\r\n external: NODE_BUILTINS,\r\n define: { 'process.env.NODE_ENV': '\"production\"' },\r\n write: false,\r\n });\r\n emitVercelFunction('pages', result.outputFiles[0].text);\r\n console.log(` built Pages dispatcher \u2192 pages.func (${serverPages.length} page(s))`);\r\n } finally {\r\n fs.unlinkSync(dispatcherPath);\r\n for (const p of tempAdapterPaths) if (fs.existsSync(p)) fs.unlinkSync(p);\r\n }\r\n\r\n for (const { srcRegex } of serverPages)\r\n vercelRoutes.push({ src: srcRegex, dest: '/pages' });\r\n}\r\n\r\n// \u2500\u2500\u2500 Vercel config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n// `{ handle: 'filesystem' }` instructs Vercel's routing layer to check\r\n// .vercel/output/static/ BEFORE evaluating any of our dynamic route rules.\r\n// Without this, an optional catch-all like [[page]].tsx would intercept\r\n// /__n.js, /__react.js, and app/public/* before the CDN can serve them.\r\nfs.writeFileSync(\r\n path.join(OUTPUT_DIR, 'config.json'),\r\n JSON.stringify({ version: 3, routes: [{ handle: 'filesystem' }, ...vercelRoutes] }, null, 2),\r\n);\r\nfs.writeFileSync(\r\n path.resolve('vercel.json'),\r\n JSON.stringify({ runtime: 'nodejs20.x' }, null, 2),\r\n);\r\n\r\n// \u2500\u2500\u2500 Static assets \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nawait buildCombinedBundle(STATIC_DIR);\r\ncopyPublicFiles(PUBLIC_DIR, STATIC_DIR);\r\n\r\nconst fnCount = (apiRoutes.length > 0 ? 1 : 0) + (serverPages.length > 0 ? 1 : 0);\r\nconsole.log(`\\n\u2713 Vercel build complete \u2014 ${fnCount} function(s) \u2192 .vercel/output`);"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/bundle.d.ts CHANGED
@@ -53,6 +53,13 @@ export interface RuntimeData {
53
53
  allIds: string[];
54
54
  url: string;
55
55
  params: Record<string, any>;
56
+ /** Query string parameters parsed from the URL. Multi-value keys are arrays. */
57
+ query: Record<string, string | string[]>;
58
+ /**
59
+ * Safe subset of the incoming request headers (cookie, authorization, and
60
+ * proxy-authorization are stripped before embedding in the HTML document).
61
+ */
62
+ headers: Record<string, string>;
56
63
  debug: ClientDebugLevel;
57
64
  }
58
65
  /**