@timber-js/app 0.2.0-alpha.209 → 0.2.0-alpha.210
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.
- package/dist/_chunks/{actions-BerlqoXA.js → actions-Rjk4htmA.js} +19 -25
- package/dist/_chunks/{actions-BerlqoXA.js.map → actions-Rjk4htmA.js.map} +1 -1
- package/dist/_chunks/als-registry-DaxkVjt5.js.map +1 -1
- package/dist/_chunks/{cache-api-CR23J_NC.js → cache-api-DGdYfNJn.js} +4 -4
- package/dist/_chunks/{cache-api-CR23J_NC.js.map → cache-api-DGdYfNJn.js.map} +1 -1
- package/dist/_chunks/{chains-CpFg56UB.js → chains-BoO51joc.js} +2 -2
- package/dist/_chunks/{chains-CpFg56UB.js.map → chains-BoO51joc.js.map} +1 -1
- package/dist/_chunks/{cli-check-C6Ev6wBO.js → cli-check-ajNY3B2e.js} +3 -3
- package/dist/_chunks/{cli-check-C6Ev6wBO.js.map → cli-check-ajNY3B2e.js.map} +1 -1
- package/dist/_chunks/{cli-schema-sync-CbT2AUUI.js → cli-schema-sync-D2eI8jEg.js} +2 -2
- package/dist/_chunks/{cli-schema-sync-CbT2AUUI.js.map → cli-schema-sync-D2eI8jEg.js.map} +1 -1
- package/dist/_chunks/{file-cache-Dw6BJPG7.js → codegen-Bps1sLKJ.js} +3 -29
- package/dist/_chunks/codegen-Bps1sLKJ.js.map +1 -0
- package/dist/_chunks/{convention-lint-jKTwKwPe.js → convention-lint-DLmhGsRS.js} +54 -316
- package/dist/_chunks/convention-lint-DLmhGsRS.js.map +1 -0
- package/dist/_chunks/{dev-server-C4WZdB7L.js → dev-server-v97rQH4b.js} +99 -9
- package/dist/_chunks/dev-server-v97rQH4b.js.map +1 -0
- package/dist/_chunks/{error-boundary-tA7kVfs4.js → error-boundary-DsNScGRM.js} +4 -4
- package/dist/_chunks/{error-boundary-tA7kVfs4.js.map → error-boundary-DsNScGRM.js.map} +1 -1
- package/dist/_chunks/{json-lossy-check-ip0Qi0MT.js → json-lossy-check-CVuRs2hG.js} +2 -2
- package/dist/_chunks/{json-lossy-check-ip0Qi0MT.js.map → json-lossy-check-CVuRs2hG.js.map} +1 -1
- package/dist/_chunks/{live-graph-Dv-JJCZw.js → live-graph-9cSnn_h9.js} +3 -3
- package/dist/_chunks/{live-graph-Dv-JJCZw.js.map → live-graph-9cSnn_h9.js.map} +1 -1
- package/dist/_chunks/{logger-DiDt5ppH.js → logger-BP0LN6vP.js} +17 -2
- package/dist/_chunks/{logger-DiDt5ppH.js.map → logger-BP0LN6vP.js.map} +1 -1
- package/dist/_chunks/metadata-routes-DSDjM_hJ.js.map +1 -1
- package/dist/_chunks/navigation-root-BQfo1-kG.js.map +1 -1
- package/dist/_chunks/{poison-scan-CpeT6_OJ.js → poison-scan-vGV7Re0B.js} +2 -2
- package/dist/_chunks/{poison-scan-CpeT6_OJ.js.map → poison-scan-vGV7Re0B.js.map} +1 -1
- package/dist/_chunks/{scanner-Bw0oq1HB.js → scanner-DmqdxzbW.js} +392 -7
- package/dist/_chunks/scanner-DmqdxzbW.js.map +1 -0
- package/dist/_chunks/segment-classify-C539Pa2O.js.map +1 -1
- package/dist/_chunks/{sizeof-UwzwB1uM.js → sizeof-BM1409x2.js} +2 -2
- package/dist/_chunks/{sizeof-UwzwB1uM.js.map → sizeof-BM1409x2.js.map} +1 -1
- package/dist/_chunks/{status-page-marker-gaihi0KZ.js → status-page-marker-BRX9Ib-d.js} +1 -45
- package/dist/_chunks/status-page-marker-BRX9Ib-d.js.map +1 -0
- package/dist/_chunks/{walkers-BXExhzzk.js → walkers-Czu2jXFq.js} +3 -3
- package/dist/_chunks/{walkers-BXExhzzk.js.map → walkers-Czu2jXFq.js.map} +1 -1
- package/dist/adapters/cloudflare-kv-cache.js +1 -1
- package/dist/analyze/crawl-entry.js +2 -2
- package/dist/analyze/graph-command.js +2 -2
- package/dist/cache/index.js +2 -2
- package/dist/cache/stores/memory.js +1 -1
- package/dist/cli.js +3 -3
- package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
- package/dist/client/browser-entry/router-init.d.ts.map +1 -1
- package/dist/client/error-boundary.js +1 -1
- package/dist/client/history.d.ts +0 -9
- package/dist/client/history.d.ts.map +1 -1
- package/dist/client/index.d.ts +2 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +9 -3
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.js +25 -38
- package/dist/client/internal.js.map +1 -1
- package/dist/client/link.d.ts +22 -0
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/navigation-api.d.ts +14 -2
- package/dist/client/navigation-api.d.ts.map +1 -1
- package/dist/client/navigation-root.d.ts +9 -1
- package/dist/client/navigation-root.d.ts.map +1 -1
- package/dist/client/navigation-transition.d.ts +6 -1
- package/dist/client/navigation-transition.d.ts.map +1 -1
- package/dist/client/react-root.d.ts.map +1 -1
- package/dist/client/router-effects.d.ts +10 -3
- package/dist/client/router-effects.d.ts.map +1 -1
- package/dist/client/router-pipeline.d.ts +3 -1
- package/dist/client/router-pipeline.d.ts.map +1 -1
- package/dist/client/router-types.d.ts +65 -9
- package/dist/client/router-types.d.ts.map +1 -1
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +0 -15
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/use-router.d.ts +13 -6
- package/dist/client/use-router.d.ts.map +1 -1
- package/dist/config-validation.d.ts +19 -2
- package/dist/config-validation.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -7
- package/dist/index.js.map +1 -1
- package/dist/routing/convention-lint.d.ts.map +1 -1
- package/dist/routing/export-detect.d.ts +19 -0
- package/dist/routing/export-detect.d.ts.map +1 -1
- package/dist/routing/index.js +3 -3
- package/dist/routing/manifest-codegen.d.ts.map +1 -1
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/types.d.ts +7 -0
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/server/action-handler.d.ts +1 -1
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/actions.d.ts +9 -4
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/csrf.d.ts +38 -19
- package/dist/server/csrf.d.ts.map +1 -1
- package/dist/server/error-boundary-wrapper.d.ts +8 -4
- package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
- package/dist/server/fallback-error.d.ts.map +1 -1
- package/dist/server/form-flash.d.ts +1 -1
- package/dist/server/index.js +2 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/internal.js +249 -16
- package/dist/server/internal.js.map +1 -1
- package/dist/server/logger.d.ts +16 -3
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/metadata-collector.d.ts +2 -0
- package/dist/server/metadata-collector.d.ts.map +1 -1
- package/dist/server/metadata-routes.d.ts +12 -1
- package/dist/server/metadata-routes.d.ts.map +1 -1
- package/dist/server/pipeline-helpers.d.ts +29 -1
- package/dist/server/pipeline-helpers.d.ts.map +1 -1
- package/dist/server/pipeline.d.ts +8 -1
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/route-matcher.d.ts +8 -0
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-entry/{wrap-action-dispatch.d.ts → action-dispatcher.d.ts} +18 -40
- package/dist/server/rsc-entry/action-dispatcher.d.ts.map +1 -0
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/safe-load.d.ts +5 -12
- package/dist/server/safe-load.d.ts.map +1 -1
- package/docs/api/30-api-server.mdx +1 -1
- package/docs/api/31-api-client.mdx +23 -20
- package/docs/api/34-api-config.mdx +4 -2
- package/package.json +1 -1
- package/src/client/browser-entry/action-dispatch.ts +28 -28
- package/src/client/browser-entry/post-hydration.ts +11 -1
- package/src/client/browser-entry/router-init.ts +10 -4
- package/src/client/history.ts +0 -22
- package/src/client/index.ts +2 -1
- package/src/client/link.tsx +30 -1
- package/src/client/navigation-api.ts +36 -3
- package/src/client/navigation-root.tsx +13 -1
- package/src/client/navigation-transition.ts +17 -7
- package/src/client/react-root.ts +11 -2
- package/src/client/router-effects.ts +11 -4
- package/src/client/router-pipeline.ts +4 -0
- package/src/client/router-types.ts +80 -6
- package/src/client/router.ts +36 -24
- package/src/client/segment-cache.ts +0 -65
- package/src/client/use-router.ts +25 -6
- package/src/config-validation.ts +121 -5
- package/src/index.ts +5 -8
- package/src/routing/convention-lint.ts +75 -0
- package/src/routing/export-detect.ts +88 -0
- package/src/routing/manifest-codegen.ts +6 -0
- package/src/routing/scanner.ts +9 -0
- package/src/routing/types.ts +7 -0
- package/src/server/action-handler.ts +14 -11
- package/src/server/actions.ts +37 -42
- package/src/server/als-registry.ts +1 -1
- package/src/server/csrf.ts +100 -72
- package/src/server/error-boundary-wrapper.ts +11 -12
- package/src/server/fallback-error.ts +13 -21
- package/src/server/form-flash.ts +1 -1
- package/src/server/logger.ts +19 -3
- package/src/server/metadata-collector.ts +4 -1
- package/src/server/metadata-routes.ts +23 -8
- package/src/server/pipeline-helpers.ts +89 -8
- package/src/server/pipeline.ts +27 -2
- package/src/server/route-element-builder.ts +1 -0
- package/src/server/route-matcher.ts +11 -0
- package/src/server/rsc-entry/{wrap-action-dispatch.ts → action-dispatcher.ts} +20 -62
- package/src/server/rsc-entry/index.ts +14 -27
- package/src/server/safe-load.ts +5 -12
- package/dist/_chunks/convention-lint-jKTwKwPe.js.map +0 -1
- package/dist/_chunks/dev-server-C4WZdB7L.js.map +0 -1
- package/dist/_chunks/file-cache-Dw6BJPG7.js.map +0 -1
- package/dist/_chunks/scanner-Bw0oq1HB.js.map +0 -1
- package/dist/_chunks/status-page-marker-gaihi0KZ.js.map +0 -1
- package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dev-server-C4WZdB7L.js","names":[],"sources":["../../../../node_modules/.pnpm/srvx@1.0.5/node_modules/srvx/dist/adapters/node.mjs","../../src/dev-tools/stack-classifier.ts","../../src/dev-tools/terminal.ts","../../src/dev-tools/overlay.ts","../../src/server/utils/escape-html.ts","../../src/dev-tools/dev-page-shell.ts","../../src/dev-tools/error-page.ts","../../src/config-validation.ts","../../src/plugins/graph-endpoint.ts","../../src/server/compress.ts","../../src/plugins/dev-server.ts"],"sourcesContent":["import { FastURL, lazyInherit } from \"../_chunks/_url.mjs\";\nimport { createWaitUntil, fmtURL, printListening, resolvePortAndHost, resolveTLSOptions } from \"../_chunks/_utils2.mjs\";\nimport { createBodyTooLargeError, limitBodyStream } from \"../body-limit.mjs\";\nimport { errorPlugin, gracefulShutdownPlugin, wrapFetch } from \"../_chunks/_plugins.mjs\";\nimport { HOST_RE, forwardedHopValue, resolveClientIP, trustedHops } from \"../_chunks/_trust-proxy.mjs\";\nimport nodeHTTP, { IncomingMessage, ServerResponse } from \"node:http\";\nimport { Duplex, PassThrough, Readable, addAbortSignal } from \"node:stream\";\nimport { pipeline as pipeline$1 } from \"node:stream/promises\";\nimport nodeHTTPS from \"node:https\";\nimport nodeHTTP2 from \"node:http2\";\nfunction sendNodeResponse(nodeRes, webRes) {\n\ttry {\n\t\treturn _sendNodeResponse(nodeRes, webRes, false) || Promise.resolve();\n\t} catch (error) {\n\t\treturn Promise.reject(error);\n\t}\n}\nfunction sendNodeResponseDetached(nodeRes, webRes, silent) {\n\ttry {\n\t\treturn _sendNodeResponse(nodeRes, webRes, true);\n\t} catch (error) {\n\t\thandleSendError(nodeRes, error, silent);\n\t}\n}\nfunction handleSendError(nodeRes, error, silent) {\n\tif (!silent) console.error(\"[srvx] Failed to send response:\", error);\n\tfailResponse(nodeRes);\n}\nfunction sendErrorResponse(nodeRes, error, silent) {\n\tif (!silent) console.error(\"[srvx] Unhandled error in fetch handler:\", error);\n\tfailResponse(nodeRes);\n}\nfunction failResponse(nodeRes) {\n\tif (nodeRes.writableEnded) return;\n\tif (nodeRes.headersSent) nodeRes.destroy();\n\telse {\n\t\tnodeRes.statusCode = 500;\n\t\tif (nodeRes.req?.httpVersion !== \"2.0\") nodeRes.statusMessage = \"\";\n\t\tnodeRes.end();\n\t}\n}\nfunction _sendNodeResponse(nodeRes, webRes, detached) {\n\tif (!webRes) {\n\t\tnodeRes.statusCode = 500;\n\t\treturn endNodeResponse(nodeRes, detached);\n\t}\n\tif (webRes._toNodeResponse) {\n\t\tconst res = webRes._toNodeResponse();\n\t\tif (res.body) {\n\t\t\tif (res.body instanceof ReadableStream) {\n\t\t\t\twriteHead(nodeRes, res.status, res.statusText, res.headers);\n\t\t\t\treturn streamBody(res.body, nodeRes);\n\t\t\t} else if (typeof res.body?.pipe === \"function\") return pipeBody(res.body, nodeRes, res.status, res.statusText, res.headers);\n\t\t\twriteHead(nodeRes, res.status, res.statusText, res.headers);\n\t\t\tnodeRes.write(res.body);\n\t\t} else writeHead(nodeRes, res.status, res.statusText, res.headers);\n\t\treturn endNodeResponse(nodeRes, detached);\n\t}\n\tconst rawHeaders = [];\n\tfor (const [key, value] of webRes.headers) rawHeaders.push(key, value);\n\twriteHead(nodeRes, webRes.status, webRes.statusText, rawHeaders);\n\treturn webRes.body ? streamBody(webRes.body, nodeRes) : endNodeResponse(nodeRes, detached);\n}\nfunction writeHead(nodeRes, status, statusText, rawHeaders) {\n\tif (!nodeRes.headersSent) {\n\t\tif (nodeRes.req?.httpVersion === \"2.0\") nodeRes.writeHead(status, rawHeaders);\n\t\telse nodeRes.writeHead(status, safeStatusText(statusText), rawHeaders);\n\t}\n}\nconst INVALID_REASON_PHRASE_RE = /[^\\t\\u0020-\\u007E\\u0080-\\u00FF]/g;\nfunction safeStatusText(statusText) {\n\treturn typeof statusText === \"string\" && statusText ? statusText.replace(INVALID_REASON_PHRASE_RE, \"\") : statusText;\n}\nfunction endNodeResponse(nodeRes, detached) {\n\tif (detached) {\n\t\tnodeRes.end();\n\t\treturn;\n\t}\n\treturn new Promise((resolve) => nodeRes.end(resolve));\n}\nfunction pipeBody(stream, nodeRes, status, statusText, headers) {\n\tif (nodeRes.destroyed) {\n\t\tstream.destroy?.();\n\t\treturn;\n\t}\n\tif (nodeRes.req?.method === \"HEAD\") {\n\t\tif (typeof stream.destroy === \"function\") stream.destroy();\n\t\telse stream.abort?.();\n\t\twriteHead(nodeRes, status, statusText, headers);\n\t\treturn endNodeResponse(nodeRes);\n\t}\n\tif (typeof stream.on !== \"function\" || typeof stream.destroy !== \"function\") {\n\t\twriteHead(nodeRes, status, statusText, headers);\n\t\tstream.pipe(nodeRes);\n\t\treturn new Promise((resolve) => nodeRes.on(\"close\", resolve));\n\t}\n\tif (stream.destroyed) {\n\t\twriteHead(nodeRes, 500, \"Internal Server Error\", []);\n\t\treturn endNodeResponse(nodeRes);\n\t}\n\treturn new Promise((resolve) => {\n\t\tfunction cleanup() {\n\t\t\tstream.off(\"error\", onEarlyError);\n\t\t\tstream.off(\"readable\", onReadable);\n\t\t\tnodeRes.off(\"close\", onResClose);\n\t\t}\n\t\tfunction onEarlyError() {\n\t\t\tcleanup();\n\t\t\tstream.destroy();\n\t\t\twriteHead(nodeRes, 500, \"Internal Server Error\", []);\n\t\t\tendNodeResponse(nodeRes).then(resolve);\n\t\t}\n\t\tfunction onReadable() {\n\t\t\tcleanup();\n\t\t\tif (nodeRes.destroyed) {\n\t\t\t\tstream.destroy();\n\t\t\t\treturn resolve();\n\t\t\t}\n\t\t\twriteHead(nodeRes, status, statusText, headers);\n\t\t\tpipeline$1(stream, nodeRes).catch(() => {}).then(() => resolve());\n\t\t}\n\t\tfunction onResClose() {\n\t\t\tcleanup();\n\t\t\tstream.destroy();\n\t\t\tresolve();\n\t\t}\n\t\tstream.once(\"error\", onEarlyError);\n\t\tstream.once(\"readable\", onReadable);\n\t\tnodeRes.once(\"close\", onResClose);\n\t});\n}\nfunction streamBody(stream, nodeRes) {\n\tif (nodeRes.destroyed) {\n\t\tstream.cancel().catch(() => {});\n\t\treturn;\n\t}\n\tif (nodeRes.req?.method === \"HEAD\") {\n\t\tstream.cancel().catch(() => {});\n\t\treturn endNodeResponse(nodeRes);\n\t}\n\tconst reader = stream.getReader();\n\tfunction streamCancel(error) {\n\t\treader.cancel(error).catch(() => {});\n\t\tif (error) nodeRes.destroy(error);\n\t}\n\tfunction streamHandle({ done, value }) {\n\t\ttry {\n\t\t\tif (done) nodeRes.end();\n\t\t\telse if (nodeRes.write(value)) reader.read().then(streamHandle, streamCancel);\n\t\t\telse nodeRes.once(\"drain\", () => reader.read().then(streamHandle, streamCancel));\n\t\t} catch (error) {\n\t\t\tstreamCancel(error instanceof Error ? error : void 0);\n\t\t}\n\t}\n\tnodeRes.on(\"close\", streamCancel);\n\tnodeRes.on(\"error\", streamCancel);\n\treader.read().then(streamHandle, streamCancel);\n\treturn reader.closed.catch(streamCancel).finally(() => {\n\t\tnodeRes.off(\"close\", streamCancel);\n\t\tnodeRes.off(\"error\", streamCancel);\n\t});\n}\nvar NodeRequestURL = class extends FastURL {\n\tconstructor({ req, hops = 0 }) {\n\t\tconst path = req.url || \"/\";\n\t\tconst trusted = hops > 0;\n\t\tconst forwardedHost = forwardedHopValue(req.headers[\"x-forwarded-host\"], hops);\n\t\tlet host = (forwardedHost && HOST_RE.test(forwardedHost) ? forwardedHost : void 0) || req.headers.host || req.headers[\":authority\"];\n\t\tif (host && !HOST_RE.test(host)) host = \"_invalid_\";\n\t\telse if (!host) {\n\t\t\tif (req.socket) host = `${req.socket.localFamily === \"IPv6\" ? \"[\" + req.socket.localAddress + \"]\" : req.socket.localAddress}:${req.socket?.localPort || \"80\"}`;\n\t\t\telse host = \"localhost\";\n\t\t}\n\t\tconst forwardedProto = forwardedHopValue(req.headers[\"x-forwarded-proto\"], hops);\n\t\tconst protocol = req.socket?.encrypted || forwardedProto === \"https\" || trusted && req.headers[\":scheme\"] === \"https\" ? \"https:\" : \"http:\";\n\t\tif (path[0] === \"/\") {\n\t\t\tconst qIndex = path.indexOf(\"?\");\n\t\t\tsuper({\n\t\t\t\tprotocol,\n\t\t\t\thost,\n\t\t\t\tpathname: qIndex === -1 ? path : path.slice(0, qIndex) || \"/\",\n\t\t\t\tsearch: qIndex === -1 ? \"\" : path.slice(qIndex) || \"\"\n\t\t\t});\n\t\t} else if (path === \"*\") super({\n\t\t\tprotocol,\n\t\t\thost,\n\t\t\tpathname: \"/*\",\n\t\t\tsearch: \"\"\n\t\t});\n\t\telse {\n\t\t\tconst target = URL.canParse(path) ? new URL(path) : void 0;\n\t\t\tif (target) {\n\t\t\t\tconst targetHost = target.host;\n\t\t\t\tconst targetPath = target.pathname;\n\t\t\t\tsuper({\n\t\t\t\t\tprotocol,\n\t\t\t\t\thost: targetHost ? HOST_RE.test(targetHost) ? targetHost : \"_invalid_\" : host,\n\t\t\t\t\tpathname: targetPath ? targetPath[0] === \"/\" ? targetPath : `/${targetPath}` : \"/\",\n\t\t\t\t\tsearch: target.search\n\t\t\t\t});\n\t\t\t} else super({\n\t\t\t\tprotocol,\n\t\t\t\thost,\n\t\t\t\tpathname: \"/\",\n\t\t\t\tsearch: \"\"\n\t\t\t});\n\t\t}\n\t}\n};\nfunction isValidAbsoluteForm(target) {\n\tif (!URL.canParse(target)) return false;\n\tconst url = new URL(target);\n\treturn (url.protocol === \"http:\" || url.protocol === \"https:\") && url.host !== \"\";\n}\nconst _nonJoinedHeaders = /* @__PURE__ */ new Set([\n\t\"age\",\n\t\"authorization\",\n\t\"content-length\",\n\t\"content-type\",\n\t\"etag\",\n\t\"expires\",\n\t\"from\",\n\t\"host\",\n\t\"if-modified-since\",\n\t\"if-unmodified-since\",\n\t\"last-modified\",\n\t\"location\",\n\t\"max-forwards\",\n\t\"proxy-authorization\",\n\t\"referer\",\n\t\"retry-after\",\n\t\"server\",\n\t\"user-agent\"\n]);\nconst _validHeaderNameRE = /^[!#$%&'*+\\-.^_`|~\\dA-Za-z]+$/;\nfunction _isRepeated(rawHeaders, lowerName) {\n\tlet seen = false;\n\tfor (let i = 0; i < rawHeaders.length; i += 2) {\n\t\tconst key = rawHeaders[i];\n\t\tif (key.length === lowerName.length && key.toLowerCase() === lowerName) {\n\t\t\tif (seen) return true;\n\t\t\tseen = true;\n\t\t}\n\t}\n\treturn false;\n}\nconst NodeRequestHeaders = /* @__PURE__ */ (() => {\n\tconst NativeHeaders = globalThis.Headers;\n\tclass Headers {\n\t\t#req;\n\t\t#headers;\n\t\tconstructor(req) {\n\t\t\tthis.#req = req;\n\t\t}\n\t\tstatic [Symbol.hasInstance](val) {\n\t\t\treturn val instanceof NativeHeaders;\n\t\t}\n\t\t_adopt(headers) {\n\t\t\tthis.#headers = headers;\n\t\t}\n\t\tget _headers() {\n\t\t\tif (!this.#headers) {\n\t\t\t\tconst headers = new NativeHeaders();\n\t\t\t\tconst rawHeaders = this.#req.rawHeaders;\n\t\t\t\tconst len = rawHeaders.length;\n\t\t\t\tfor (let i = 0; i < len; i += 2) {\n\t\t\t\t\tconst key = rawHeaders[i];\n\t\t\t\t\tif (key.charCodeAt(0) === 58) continue;\n\t\t\t\t\tconst value = rawHeaders[i + 1];\n\t\t\t\t\theaders.append(key, value);\n\t\t\t\t}\n\t\t\t\tthis.#headers = headers;\n\t\t\t}\n\t\t\treturn this.#headers;\n\t\t}\n\t\tget(name) {\n\t\t\tif (this.#headers) return this.#headers.get(name);\n\t\t\tconst lower = name.toLowerCase();\n\t\t\tif (lower.charCodeAt(0) === 58) return this._headers.get(name);\n\t\t\tconst value = this.#req.headers[lower];\n\t\t\tif (typeof value === \"string\") return _nonJoinedHeaders.has(lower) && _isRepeated(this.#req.rawHeaders, lower) ? this._headers.get(name) : value;\n\t\t\tif (Array.isArray(value)) return value.join(\", \");\n\t\t\treturn lower !== \"__proto__\" && _validHeaderNameRE.test(name) ? null : this._headers.get(name);\n\t\t}\n\t\thas(name) {\n\t\t\tif (this.#headers) return this.#headers.has(name);\n\t\t\tconst lower = name.toLowerCase();\n\t\t\tif (lower.charCodeAt(0) === 58) return this._headers.has(name);\n\t\t\tif (Object.hasOwn(this.#req.headers, lower)) return true;\n\t\t\treturn lower !== \"__proto__\" && _validHeaderNameRE.test(name) ? false : this._headers.has(name);\n\t\t}\n\t\tgetSetCookie() {\n\t\t\tif (this.#headers) return this.#headers.getSetCookie();\n\t\t\tconst value = this.#req.headers[\"set-cookie\"];\n\t\t\treturn Array.isArray(value) ? value.slice() : value ? [value] : [];\n\t\t}\n\t\tentries() {\n\t\t\treturn this._headers.entries();\n\t\t}\n\t\t[Symbol.iterator]() {\n\t\t\treturn this.entries();\n\t\t}\n\t}\n\tlazyInherit(Headers.prototype, NativeHeaders.prototype, \"_headers\");\n\tObject.setPrototypeOf(Headers, NativeHeaders);\n\tObject.setPrototypeOf(Headers.prototype, NativeHeaders.prototype);\n\treturn Headers;\n})();\nconst kNativeRequest = /* @__PURE__ */ Symbol.for(\"srvx.nativeRequest\");\nfunction bodyUnusable() {\n\treturn /* @__PURE__ */ new TypeError(\"Body is unusable: Body has already been read\");\n}\nfunction abortError() {\n\treturn new DOMException(\"The request was aborted.\", \"AbortError\");\n}\nfunction erroredStream(error) {\n\treturn new ReadableStream({ start(controller) {\n\t\tcontroller.error(error);\n\t} });\n}\nfunction isClientGone(req) {\n\treturn req.aborted || !!req.errored || req.destroyed && !req.complete;\n}\nfunction isBodySourceFinished(req) {\n\treturn isClientGone(req) || req.destroyed || req.readableEnded;\n}\nconst NodeRequest = /* @__PURE__ */ (() => {\n\tconst NativeRequest = getNativeRequest();\n\tclass Request {\n\t\truntime;\n\t\twaitUntil;\n\t\t#req;\n\t\t#url;\n\t\t#bodyStream;\n\t\t#bodyUsed = false;\n\t\t#request;\n\t\t#headers;\n\t\t#abortController;\n\t\t#maxRequestBodySize;\n\t\t#trustProxy;\n\t\t#ip;\n\t\t#ipResolved = false;\n\t\t#remoteAddress;\n\t\t#remoteResolved = false;\n\t\t#hops;\n\t\tconstructor(ctx) {\n\t\t\tthis.#req = ctx.req;\n\t\t\tthis.#maxRequestBodySize = ctx.maxRequestBodySize;\n\t\t\tthis.#trustProxy = ctx.trustProxy;\n\t\t\tthis.runtime = {\n\t\t\t\tname: \"node\",\n\t\t\t\tnode: ctx\n\t\t\t};\n\t\t}\n\t\tstatic [Symbol.hasInstance](val) {\n\t\t\treturn val instanceof NativeRequest;\n\t\t}\n\t\t#remoteAddr() {\n\t\t\tif (!this.#remoteResolved) {\n\t\t\t\tthis.#remoteResolved = true;\n\t\t\t\tthis.#remoteAddress = this.#req.socket?.remoteAddress;\n\t\t\t}\n\t\t\treturn this.#remoteAddress;\n\t\t}\n\t\t#resolveHops() {\n\t\t\tif (this.#hops === void 0) this.#hops = trustedHops(this.#trustProxy, this.#remoteAddr(), this.#req.headers[\"x-forwarded-for\"]);\n\t\t\treturn this.#hops;\n\t\t}\n\t\tget ip() {\n\t\t\tif (this.#ipResolved) return this.#ip;\n\t\t\tthis.#ipResolved = true;\n\t\t\treturn this.#ip = resolveClientIP(this.#trustProxy, this.#remoteAddr(), this.#req.headers[\"x-forwarded-for\"]);\n\t\t}\n\t\tget method() {\n\t\t\tif (this.#request) return this.#request.method;\n\t\t\treturn this.#req.method || \"GET\";\n\t\t}\n\t\tget _url() {\n\t\t\treturn this.#url ||= new NodeRequestURL({\n\t\t\t\treq: this.#req,\n\t\t\t\thops: this.#resolveHops()\n\t\t\t});\n\t\t}\n\t\tset _url(url) {\n\t\t\tthis.#url = url;\n\t\t}\n\t\tget url() {\n\t\t\tif (this.#request) return this.#request.url;\n\t\t\treturn this._url.href;\n\t\t}\n\t\tget headers() {\n\t\t\treturn this.#headers ||= new NodeRequestHeaders(this.#req);\n\t\t}\n\t\tget _abortController() {\n\t\t\tif (!this.#abortController) {\n\t\t\t\tthis.#abortController = new AbortController();\n\t\t\t\tconst { req, res } = this.runtime.node;\n\t\t\t\tconst abortController = this.#abortController;\n\t\t\t\tconst abort = (err) => abortController.abort?.(err);\n\t\t\t\tif (res) {\n\t\t\t\t\tconst onClose = () => {\n\t\t\t\t\t\tconst reqError = req.errored;\n\t\t\t\t\t\tif (reqError) abort(reqError);\n\t\t\t\t\t\telse if (!res.writableEnded) abort();\n\t\t\t\t\t};\n\t\t\t\t\tres.once(\"close\", onClose);\n\t\t\t\t\tif (res.destroyed || isClientGone(req)) onClose();\n\t\t\t\t} else {\n\t\t\t\t\tconst onClose = () => {\n\t\t\t\t\t\tif (!req.complete || req.aborted) abort();\n\t\t\t\t\t};\n\t\t\t\t\treq.once(\"close\", onClose);\n\t\t\t\t\tif (isClientGone(req)) onClose();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn this.#abortController;\n\t\t}\n\t\tget signal() {\n\t\t\treturn this.#request ? this.#request.signal : this._abortController.signal;\n\t\t}\n\t\t#hasBody() {\n\t\t\tconst method = this.method;\n\t\t\treturn method !== \"GET\" && method !== \"HEAD\";\n\t\t}\n\t\tget body() {\n\t\t\tif (this.#request) return this.#request.body;\n\t\t\tif (this.#bodyStream === void 0) {\n\t\t\t\tlet stream = null;\n\t\t\t\tif (this.#hasBody() && !this.#bodyUsed) {\n\t\t\t\t\tif (isBodySourceFinished(this.#req)) stream = erroredStream(this.#bodyError());\n\t\t\t\t\telse stream = Readable.toWeb(this.#req);\n\t\t\t\t}\n\t\t\t\tif (stream && this.#maxRequestBodySize !== void 0) stream = limitBodyStream(stream, this.#maxRequestBodySize);\n\t\t\t\tthis.#bodyStream = stream;\n\t\t\t}\n\t\t\treturn this.#bodyStream;\n\t\t}\n\t\tget bodyUsed() {\n\t\t\tif (this.#isBodyUsed()) return true;\n\t\t\treturn this.#request ? this.#request.bodyUsed : false;\n\t\t}\n\t\t#isBodyUsed() {\n\t\t\tif (!this.#bodyUsed && this.#bodyStream && Readable.isDisturbed(this.#bodyStream)) this.#bodyUsed = true;\n\t\t\treturn this.#bodyUsed;\n\t\t}\n\t\t#bodyError() {\n\t\t\tconst signal = this._abortController.signal;\n\t\t\tif (signal.aborted) return signal.reason;\n\t\t\treturn this.#req.errored || (isClientGone(this.#req) ? abortError() : bodyUnusable());\n\t\t}\n\t\t#readBuffered() {\n\t\t\tif (\"rawBody\" in this.#req && Buffer.isBuffer(this.#req.rawBody)) return readBody(this.#req, this.#maxRequestBodySize);\n\t\t\tif (isBodySourceFinished(this.#req)) return Promise.reject(this.#bodyError());\n\t\t\treturn readBody(this.#req, this.#maxRequestBodySize);\n\t\t}\n\t\ttext() {\n\t\t\tif (this.#isBodyUsed()) return Promise.reject(bodyUnusable());\n\t\t\tif (this.#request) return this.#request.text();\n\t\t\tif (!this.#hasBody()) return Promise.resolve(\"\");\n\t\t\tthis.#bodyUsed = true;\n\t\t\tif (this.#bodyStream !== void 0) try {\n\t\t\t\treturn new Response(this.#bodyStream).text();\n\t\t\t} catch (error) {\n\t\t\t\treturn Promise.reject(error);\n\t\t\t}\n\t\t\treturn this.#readBuffered().then((buf) => buf.toString());\n\t\t}\n\t\tjson() {\n\t\t\tif (this.#isBodyUsed()) return Promise.reject(bodyUnusable());\n\t\t\tif (this.#request) return this.#request.json();\n\t\t\tif (!this.#hasBody()) return Promise.resolve().then(() => JSON.parse(\"\"));\n\t\t\tthis.#bodyUsed = true;\n\t\t\tif (this.#bodyStream !== void 0) try {\n\t\t\t\treturn new Response(this.#bodyStream).json();\n\t\t\t} catch (error) {\n\t\t\t\treturn Promise.reject(error);\n\t\t\t}\n\t\t\treturn this.#readBuffered().then((buf) => JSON.parse(buf.toString()));\n\t\t}\n\t\tarrayBuffer() {\n\t\t\treturn this.#consumeNative(\"arrayBuffer\");\n\t\t}\n\t\tbytes() {\n\t\t\treturn this.#consumeNative(\"bytes\");\n\t\t}\n\t\tblob() {\n\t\t\treturn this.#consumeNative(\"blob\");\n\t\t}\n\t\tformData() {\n\t\t\treturn this.#consumeNative(\"formData\");\n\t\t}\n\t\t#consumeNative(method) {\n\t\t\tif (this.#isBodyUsed()) return Promise.reject(bodyUnusable());\n\t\t\ttry {\n\t\t\t\treturn this._request[method]();\n\t\t\t} catch (error) {\n\t\t\t\treturn Promise.reject(error);\n\t\t\t}\n\t\t}\n\t\tget _request() {\n\t\t\tif (!this.#request) {\n\t\t\t\tconst body = this.#isBodyUsed() ? null : this.body;\n\t\t\t\tthis.#request = new NativeRequest(this.url, {\n\t\t\t\t\tmethod: this.method,\n\t\t\t\t\theaders: this.headers,\n\t\t\t\t\tsignal: this._abortController.signal,\n\t\t\t\t\tbody,\n\t\t\t\t\tduplex: body ? \"half\" : void 0\n\t\t\t\t});\n\t\t\t\tthis.#headers._adopt(this.#request.headers);\n\t\t\t\tthis.#bodyStream = void 0;\n\t\t\t}\n\t\t\treturn this.#request;\n\t\t}\n\t}\n\tlazyInherit(Request.prototype, NativeRequest.prototype, \"_request\");\n\tObject.setPrototypeOf(Request.prototype, NativeRequest.prototype);\n\treturn Request;\n})();\nfunction patchGlobalRequest() {\n\tif (globalThis.Request._srvx) return globalThis.Request;\n\tconst NativeRequest = getNativeRequest();\n\tconst PatchedRequest = class Request extends NativeRequest {\n\t\tstatic _srvx = true;\n\t\tstatic [Symbol.hasInstance](instance) {\n\t\t\tif (this === PatchedRequest) return instance instanceof NativeRequest;\n\t\t\telse return Object.prototype.isPrototypeOf.call(this.prototype, instance);\n\t\t}\n\t\tconstructor(input, options) {\n\t\t\tif (typeof input === \"object\" && \"_request\" in input) input = input._request;\n\t\t\tsuper(input, options);\n\t\t}\n\t};\n\tglobalThis.Request = PatchedRequest;\n\treturn PatchedRequest;\n}\nfunction readBody(req, maxRequestBodySize) {\n\tif (\"rawBody\" in req && Buffer.isBuffer(req.rawBody)) {\n\t\tif (maxRequestBodySize !== void 0 && req.rawBody.length > maxRequestBodySize) return Promise.reject(createBodyTooLargeError(maxRequestBodySize));\n\t\treturn Promise.resolve(req.rawBody);\n\t}\n\treturn new Promise((resolve, reject) => {\n\t\tconst chunks = [];\n\t\tlet size = 0;\n\t\tconst cleanup = () => {\n\t\t\treq.off(\"data\", onData);\n\t\t\treq.off(\"end\", onEnd);\n\t\t\treq.off(\"error\", onError);\n\t\t\treq.off(\"close\", onClose);\n\t\t};\n\t\tconst onData = (chunk) => {\n\t\t\tif (maxRequestBodySize !== void 0) {\n\t\t\t\tsize += chunk.length;\n\t\t\t\tif (size > maxRequestBodySize) {\n\t\t\t\t\tcleanup();\n\t\t\t\t\treq.pause?.();\n\t\t\t\t\treject(createBodyTooLargeError(maxRequestBodySize));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tchunks.push(chunk);\n\t\t};\n\t\tconst onError = (err) => {\n\t\t\tcleanup();\n\t\t\treject(err);\n\t\t};\n\t\tconst onEnd = () => {\n\t\t\tcleanup();\n\t\t\tif (isClientGone(req)) {\n\t\t\t\treject(req.errored || abortError());\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tresolve(chunks.length === 1 ? chunks[0] : Buffer.concat(chunks));\n\t\t};\n\t\tconst onClose = () => {\n\t\t\tcleanup();\n\t\t\treject(req.errored || abortError());\n\t\t};\n\t\treq.on(\"data\", onData).once(\"end\", onEnd).once(\"error\", onError).once(\"close\", onClose);\n\t});\n}\nfunction getNativeRequest() {\n\tlet R = globalThis[kNativeRequest] || globalThis.Request;\n\twhile (R?._srvx) R = Object.getPrototypeOf(R);\n\treturn globalThis[kNativeRequest] ??= R;\n}\nconst NodeResponse = /* @__PURE__ */ (() => {\n\tconst NativeResponse = globalThis.Response;\n\tclass NodeResponse {\n\t\t#body;\n\t\t#init;\n\t\t#headers;\n\t\t#response;\n\t\tconstructor(body, init) {\n\t\t\tthis.#body = body;\n\t\t\tthis.#init = init;\n\t\t}\n\t\tstatic [Symbol.hasInstance](val) {\n\t\t\treturn val instanceof NativeResponse;\n\t\t}\n\t\tstatic json(data, init) {\n\t\t\tconst body = JSON.stringify(data);\n\t\t\tif (body === void 0) throw new TypeError(\"Value is not JSON serializable\");\n\t\t\tlet headers = init?.headers;\n\t\t\tif (!headers) headers = { \"content-type\": \"application/json\" };\n\t\t\telse {\n\t\t\t\tconst merged = new Headers(headers);\n\t\t\t\tif (!merged.has(\"content-type\")) merged.set(\"content-type\", \"application/json\");\n\t\t\t\theaders = merged;\n\t\t\t}\n\t\t\treturn new NodeResponse(body, init ? {\n\t\t\t\t...init,\n\t\t\t\theaders\n\t\t\t} : { headers });\n\t\t}\n\t\tget status() {\n\t\t\treturn this.#response?.status || this.#init?.status || 200;\n\t\t}\n\t\tget statusText() {\n\t\t\treturn this.#response?.statusText || this.#init?.statusText || \"\";\n\t\t}\n\t\tget headers() {\n\t\t\tif (this.#response) return this.#response.headers;\n\t\t\tif (this.#headers) return this.#headers;\n\t\t\treturn this.#headers = new Headers(this.#init?.headers);\n\t\t}\n\t\tget ok() {\n\t\t\tif (this.#response) return this.#response.ok;\n\t\t\tconst status = this.status;\n\t\t\treturn status >= 200 && status < 300;\n\t\t}\n\t\tget _response() {\n\t\t\tif (this.#response) return this.#response;\n\t\t\tlet body = this.#body;\n\t\t\tif (body && typeof body.pipe === \"function\" && !(body instanceof Readable)) {\n\t\t\t\tconst stream = new PassThrough();\n\t\t\t\tbody.pipe(stream);\n\t\t\t\tconst abort = body.abort;\n\t\t\t\tif (abort) stream.once(\"close\", () => abort());\n\t\t\t\tbody = stream;\n\t\t\t}\n\t\t\tthis.#response = new NativeResponse(body, this.#headers ? {\n\t\t\t\t...this.#init,\n\t\t\t\theaders: this.#headers\n\t\t\t} : this.#init);\n\t\t\tthis.#init = void 0;\n\t\t\tthis.#headers = void 0;\n\t\t\tthis.#body = void 0;\n\t\t\treturn this.#response;\n\t\t}\n\t\t_toNodeResponse() {\n\t\t\tconst status = this.status;\n\t\t\tconst statusText = this.statusText;\n\t\t\tlet body;\n\t\t\tlet contentType;\n\t\t\tlet contentLength;\n\t\t\tif (this.#response) body = this.#response.body;\n\t\t\telse if (this.#body != null) {\n\t\t\t\tif (this.#body instanceof ReadableStream) body = this.#body;\n\t\t\t\telse if (typeof this.#body === \"string\") {\n\t\t\t\t\tbody = this.#body;\n\t\t\t\t\tcontentType = \"text/plain; charset=UTF-8\";\n\t\t\t\t\tcontentLength = Buffer.byteLength(this.#body);\n\t\t\t\t} else if (this.#body instanceof ArrayBuffer) {\n\t\t\t\t\tbody = Buffer.from(this.#body);\n\t\t\t\t\tcontentLength = this.#body.byteLength;\n\t\t\t\t} else if (this.#body instanceof Uint8Array) {\n\t\t\t\t\tbody = this.#body;\n\t\t\t\t\tcontentLength = this.#body.byteLength;\n\t\t\t\t} else if (this.#body instanceof DataView) {\n\t\t\t\t\tbody = Buffer.from(this.#body.buffer, this.#body.byteOffset, this.#body.byteLength);\n\t\t\t\t\tcontentLength = this.#body.byteLength;\n\t\t\t\t} else if (this.#body instanceof Blob) {\n\t\t\t\t\tbody = this.#body.stream();\n\t\t\t\t\tcontentType = this.#body.type;\n\t\t\t\t\tcontentLength = this.#body.size;\n\t\t\t\t} else if (typeof this.#body.pipe === \"function\") body = this.#body;\n\t\t\t\telse body = this._response.body;\n\t\t\t}\n\t\t\tconst headers = [];\n\t\t\tconst initHeaders = this.#init?.headers;\n\t\t\tconst headerEntries = this.#response?.headers || this.#headers || (initHeaders ? Array.isArray(initHeaders) ? initHeaders : initHeaders?.entries ? initHeaders.entries() : Object.entries(initHeaders) : void 0);\n\t\t\tlet hasContentTypeHeader;\n\t\t\tlet hasContentLength;\n\t\t\tif (headerEntries) for (const [key, value] of headerEntries) {\n\t\t\t\tconst lowerKey = typeof key === \"string\" ? key.toLowerCase() : String(key);\n\t\t\t\tif (Array.isArray(value)) for (const v of value) headers.push(lowerKey, v);\n\t\t\t\telse headers.push(lowerKey, value);\n\t\t\t\tif (lowerKey === \"content-type\") hasContentTypeHeader = true;\n\t\t\t\telse if (lowerKey === \"content-length\") hasContentLength = true;\n\t\t\t}\n\t\t\tif (contentType && !hasContentTypeHeader) headers.push(\"content-type\", contentType);\n\t\t\tif (contentLength != null && !hasContentLength) headers.push(\"content-length\", String(contentLength));\n\t\t\tthis.#init = void 0;\n\t\t\tthis.#headers = void 0;\n\t\t\tthis.#response = void 0;\n\t\t\tthis.#body = void 0;\n\t\t\treturn {\n\t\t\t\tstatus,\n\t\t\t\tstatusText,\n\t\t\t\theaders,\n\t\t\t\tbody\n\t\t\t};\n\t\t}\n\t}\n\tlazyInherit(NodeResponse.prototype, NativeResponse.prototype, \"_response\");\n\tObject.setPrototypeOf(NodeResponse, NativeResponse);\n\tObject.setPrototypeOf(NodeResponse.prototype, NativeResponse.prototype);\n\treturn NodeResponse;\n})();\nfunction prematureCloseError() {\n\treturn Object.assign(/* @__PURE__ */ new Error(\"Connection closed before response was finished\"), { code: \"ERR_STREAM_PREMATURE_CLOSE\" });\n}\nvar WebRequestSocket = class extends Duplex {\n\t_httpMessage;\n\tautoSelectFamilyAttemptedAddresses = [];\n\tbufferSize = 0;\n\tbytesRead = 0;\n\tbytesWritten = 0;\n\tconnecting = false;\n\tpending = false;\n\treadyState = \"open\";\n\tremoteAddress = \"\";\n\tremoteFamily = \"\";\n\tremotePort = 0;\n\t#request;\n\t#timeoutTimer;\n\t#reqReader;\n\t#headersWritten;\n\t#_writeBody;\n\t#resBodyController;\n\t#resBodyClosed;\n\t_webResBody;\n\t#tos = 0;\n\tconstructor(request) {\n\t\tsuper({ allowHalfOpen: true });\n\t\tthis.#request = request;\n\t\tthis._webResBody = new ReadableStream({ start: (controller) => {\n\t\t\tthis.#resBodyController = controller;\n\t\t\tthis.#_writeBody = controller.enqueue.bind(controller);\n\t\t\tthis.once(\"finish\", () => {\n\t\t\t\tthis.readyState = \"closed\";\n\t\t\t\tthis.#resBodyClosed = true;\n\t\t\t\tcontroller.close();\n\t\t\t});\n\t\t} });\n\t\tif (request.signal) addAbortSignal(request.signal, this);\n\t}\n\tsetTimeout(ms, cb) {\n\t\tif (typeof ms !== \"number\" || !Number.isFinite(ms) || ms < 0) return this;\n\t\tif (cb) this.on(\"timeout\", cb);\n\t\tif (this.#timeoutTimer) clearTimeout(this.#timeoutTimer);\n\t\tif (ms > 0) this.#timeoutTimer = setTimeout(() => this.emit(\"timeout\"), ms);\n\t\treturn this;\n\t}\n\tsetNoDelay() {\n\t\treturn this;\n\t}\n\tsetKeepAlive() {\n\t\treturn this;\n\t}\n\tref() {\n\t\treturn this;\n\t}\n\tunref() {\n\t\treturn this;\n\t}\n\tdestroySoon() {\n\t\tthis.destroy();\n\t}\n\tconnect() {\n\t\treturn this;\n\t}\n\tresetAndDestroy() {\n\t\tthis.destroy();\n\t\treturn this;\n\t}\n\taddress() {\n\t\treturn {\n\t\t\taddress: \"\",\n\t\t\tfamily: \"\",\n\t\t\tport: 0\n\t\t};\n\t}\n\tbodyReader() {\n\t\ttry {\n\t\t\treturn this.#request.body?.getReader();\n\t\t} catch (error) {\n\t\t\tthis.emit(\"error\", error);\n\t\t}\n\t}\n\tgetTypeOfService() {\n\t\treturn this.#tos;\n\t}\n\tsetTypeOfService(tos) {\n\t\tthis.#tos = tos;\n\t\treturn this;\n\t}\n\t_read(_size) {\n\t\tconst reader = this.#reqReader ??= this.bodyReader();\n\t\tif (!reader) {\n\t\t\tthis.push(null);\n\t\t\treturn;\n\t\t}\n\t\treader.read().then((res) => this._onRead(res)).catch((error) => {\n\t\t\tthis.emit(\"error\", error);\n\t\t});\n\t}\n\t_onRead(res) {\n\t\tif (res.done) {\n\t\t\tthis.push(null);\n\t\t\treturn;\n\t\t}\n\t\tif (res.value) {\n\t\t\tthis.bytesRead += res.value.byteLength;\n\t\t\tthis.push(res.value);\n\t\t}\n\t}\n\t_write(chunk, encoding, callback) {\n\t\tif (this.#headersWritten) this.#_writeBody(typeof chunk === \"string\" ? Buffer.from(chunk, encoding) : chunk);\n\t\telse if (chunk?.length > 0) {\n\t\t\tthis.#headersWritten = true;\n\t\t\tconst headerEnd = chunk.indexOf(\"\\r\\n\\r\\n\");\n\t\t\tif (headerEnd === -1) throw new Error(\"Invalid HTTP headers chunk!\");\n\t\t\tif (headerEnd < chunk.length - 4) {\n\t\t\t\tthis._write(chunk.slice(headerEnd + 4), encoding, () => {\n\t\t\t\t\tcallback(null);\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tcallback(null);\n\t}\n\t_final(callback) {\n\t\tcallback(null);\n\t}\n\t_destroy(err, cb) {\n\t\tif (this.#timeoutTimer) clearTimeout(this.#timeoutTimer);\n\t\tif (this.#reqReader) this.#reqReader.cancel().catch((error) => {\n\t\t\tconsole.error(error);\n\t\t});\n\t\tif (!this.#resBodyClosed) {\n\t\t\tthis.#resBodyClosed = true;\n\t\t\ttry {\n\t\t\t\tthis.#resBodyController?.error(err ?? prematureCloseError());\n\t\t\t} catch {}\n\t\t}\n\t\tthis.readyState = \"closed\";\n\t\tcb(err ?? void 0);\n\t}\n};\nvar WebIncomingMessage = class extends IncomingMessage {\n\t#socket;\n\tconstructor(req, socket) {\n\t\tsuper(socket);\n\t\tthis.#socket = socket;\n\t\tthis.method = req.method;\n\t\tconst url = req._url ??= new FastURL(req.url);\n\t\tthis.url = url.pathname + url.search;\n\t\tthis.httpVersionMajor = 1;\n\t\tthis.httpVersionMinor = 1;\n\t\tthis.httpVersion = \"1.1\";\n\t\tconst headers = {};\n\t\tconst rawHeaders = this.rawHeaders;\n\t\tfor (const [key, value] of req.headers.entries()) {\n\t\t\tconst lowerKey = key.toLowerCase();\n\t\t\tif (lowerKey === \"set-cookie\") continue;\n\t\t\theaders[lowerKey] = value;\n\t\t\trawHeaders.push(key, value);\n\t\t}\n\t\tconst setCookie = req.headers.getSetCookie?.() ?? [];\n\t\tif (setCookie.length > 0) {\n\t\t\theaders[\"set-cookie\"] = setCookie;\n\t\t\tfor (const cookie of setCookie) rawHeaders.push(\"set-cookie\", cookie);\n\t\t}\n\t\tif (req.method !== \"GET\" && req.method !== \"HEAD\" && !headers[\"content-length\"] && !headers[\"transfer-encoding\"]) headers[\"transfer-encoding\"] = \"chunked\";\n\t\tthis.headers = headers;\n\t\tconst onData = (chunk) => {\n\t\t\tif (!this.push(chunk)) socket.pause();\n\t\t};\n\t\tsocket.on(\"data\", onData);\n\t\tsocket.once(\"end\", () => {\n\t\t\tthis.complete = true;\n\t\t\tthis.push(null);\n\t\t\tsocket.off(\"data\", onData);\n\t\t});\n\t}\n\t_read(_size) {\n\t\tthis.#socket.resume();\n\t}\n\t_destroy(_err, cb) {\n\t\tcb();\n\t}\n};\nfunction callNodeHandler(handler, req) {\n\tconst isMiddleware = handler.length > 2;\n\tconst nodeCtx = req.runtime?.node;\n\tif (!nodeCtx || !nodeCtx.req || !nodeCtx.res) throw new Error(\"Node.js runtime context is not available.\");\n\tconst { req: nodeReq, res: nodeRes } = nodeCtx;\n\tlet _headers;\n\tconst webRes = new NodeResponse(void 0, {\n\t\tget status() {\n\t\t\treturn nodeRes.statusCode;\n\t\t},\n\t\tget statusText() {\n\t\t\treturn nodeRes.statusMessage;\n\t\t},\n\t\tget headers() {\n\t\t\tif (!_headers) {\n\t\t\t\tconst headerEntries = [];\n\t\t\t\tconst rawHeaders = nodeRes.getHeaders();\n\t\t\t\tfor (const [name, value] of Object.entries(rawHeaders)) if (Array.isArray(value)) for (const v of value) headerEntries.push([name, v]);\n\t\t\t\telse if (value) headerEntries.push([name, String(value)]);\n\t\t\t\t_headers = new Headers(headerEntries);\n\t\t\t}\n\t\t\treturn _headers;\n\t\t}\n\t});\n\treturn new Promise((resolve, reject) => {\n\t\tnodeRes.once(\"close\", () => resolve(webRes));\n\t\tnodeRes.once(\"finish\", () => resolve(webRes));\n\t\tnodeRes.once(\"error\", (error) => reject(error));\n\t\tlet streamPromise;\n\t\tnodeRes.once(\"pipe\", (stream) => {\n\t\t\tstreamPromise = new Promise((resolve) => {\n\t\t\t\tstream.once(\"end\", () => resolve(webRes));\n\t\t\t\tstream.once(\"error\", (error) => reject(error));\n\t\t\t});\n\t\t});\n\t\ttry {\n\t\t\tif (isMiddleware) Promise.resolve(handler(nodeReq, nodeRes, (error) => error ? reject(error) : streamPromise || resolve(webRes))).catch((error) => reject(error));\n\t\t\telse Promise.resolve(handler(nodeReq, nodeRes)).then(() => streamPromise || webRes).catch((error) => reject(error));\n\t\t} catch (error) {\n\t\t\treject(error);\n\t\t}\n\t});\n}\nlet needDrainSymbol;\nfunction getNeedDrainSymbol(res) {\n\tneedDrainSymbol ??= Object.getOwnPropertySymbols(res).find((s) => s.description === \"kNeedDrain\");\n\treturn needDrainSymbol;\n}\nconst NULL_BODY_STATUSES = /* @__PURE__ */ new Set([\n\t101,\n\t204,\n\t205,\n\t304\n]);\nconst HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([\n\t\"connection\",\n\t\"keep-alive\",\n\t\"proxy-authenticate\",\n\t\"proxy-authorization\",\n\t\"te\",\n\t\"trailer\",\n\t\"transfer-encoding\",\n\t\"upgrade\"\n]);\nvar WebServerResponse = class extends ServerResponse {\n\t#socket;\n\t#socketError;\n\t#onHeadersSent;\n\tconstructor(req, socket) {\n\t\tsuper(req);\n\t\tthis.assignSocket(socket);\n\t\tthis.useChunkedEncodingByDefault = false;\n\t\tthis.once(\"finish\", () => {\n\t\t\tsocket.end();\n\t\t});\n\t\tthis.#socket = socket;\n\t\tsocket.once(\"error\", (err) => {\n\t\t\tthis.#socketError ??= err;\n\t\t});\n\t\tsocket.on(\"drain\", () => {\n\t\t\tconst kNeedDrain = getNeedDrainSymbol(this);\n\t\t\tif (kNeedDrain && !this[kNeedDrain]) return;\n\t\t\tif (this.destroyed || this.writableFinished) return;\n\t\t\tif (kNeedDrain) this[kNeedDrain] = false;\n\t\t\tthis.emit(\"drain\");\n\t\t});\n\t\tthis.waitToFinish = this.waitToFinish.bind(this);\n\t\tthis.waitForResponseHead = this.waitForResponseHead.bind(this);\n\t\tthis.toWebResponse = this.toWebResponse.bind(this);\n\t}\n\twriteHead(statusCode, statusMessage, headers) {\n\t\tconst result = typeof statusMessage === \"string\" ? super.writeHead(statusCode, statusMessage, stripTransferEncoding(headers)) : super.writeHead(statusCode, stripTransferEncoding(headers ?? statusMessage));\n\t\tthis.#onHeadersSent?.();\n\t\treturn result;\n\t}\n\tsetHeader(name, value) {\n\t\tif (typeof name === \"string\" && name.toLowerCase() === \"transfer-encoding\") return this;\n\t\treturn super.setHeader(name, value);\n\t}\n\tappendHeader(name, value) {\n\t\tif (typeof name === \"string\" && name.toLowerCase() === \"transfer-encoding\") return this;\n\t\treturn super.appendHeader(name, value);\n\t}\n\twaitToFinish() {\n\t\tif (this.writableFinished) return Promise.resolve();\n\t\tif (this.#socketError || this.#socket.destroyed) return Promise.reject(this.#socketError ?? prematureCloseError());\n\t\tif (this.writableEnded) return Promise.resolve();\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst socket = this.#socket;\n\t\t\tconst settle = (err) => {\n\t\t\t\tthis.removeListener(\"finish\", onFinish);\n\t\t\t\tthis.removeListener(\"error\", onError);\n\t\t\t\tsocket.removeListener(\"error\", onError);\n\t\t\t\tsocket.removeListener(\"close\", onClose);\n\t\t\t\tif (err) reject(err);\n\t\t\t\telse resolve();\n\t\t\t};\n\t\t\tconst onFinish = () => settle();\n\t\t\tconst onError = (err) => settle(err);\n\t\t\tconst onClose = () => {\n\t\t\t\tif (!this.writableFinished) settle(this.#socketError ?? prematureCloseError());\n\t\t\t};\n\t\t\tthis.on(\"finish\", onFinish);\n\t\t\tthis.on(\"error\", onError);\n\t\t\tsocket.on(\"error\", onError);\n\t\t\tsocket.on(\"close\", onClose);\n\t\t});\n\t}\n\twaitForResponseHead() {\n\t\tif (this.headersSent || this.writableEnded || this.writableFinished) return Promise.resolve();\n\t\tif (this.#socketError || this.#socket.destroyed) return Promise.reject(this.#socketError ?? prematureCloseError());\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst socket = this.#socket;\n\t\t\tconst settle = (err) => {\n\t\t\t\tthis.#onHeadersSent = void 0;\n\t\t\t\tthis.removeListener(\"finish\", onFinish);\n\t\t\t\tthis.removeListener(\"error\", onError);\n\t\t\t\tsocket.removeListener(\"error\", onError);\n\t\t\t\tsocket.removeListener(\"close\", onClose);\n\t\t\t\tif (err) reject(err);\n\t\t\t\telse resolve();\n\t\t\t};\n\t\t\tthis.#onHeadersSent = () => settle();\n\t\t\tconst onFinish = () => settle();\n\t\t\tconst onError = (err) => settle(err);\n\t\t\tconst onClose = () => {\n\t\t\t\tif (!this.headersSent && !this.writableFinished) settle(this.#socketError ?? prematureCloseError());\n\t\t\t};\n\t\t\tthis.on(\"finish\", onFinish);\n\t\t\tthis.on(\"error\", onError);\n\t\t\tsocket.on(\"error\", onError);\n\t\t\tsocket.on(\"close\", onClose);\n\t\t});\n\t}\n\tasync toWebResponse() {\n\t\tawait this.waitForResponseHead();\n\t\tconst headers = [];\n\t\tconst httpHeader = this._header?.split(\"\\r\\n\");\n\t\tconst connectionTokens = /* @__PURE__ */ new Set();\n\t\tfor (let i = 1; httpHeader && i < httpHeader.length; i++) {\n\t\t\tconst sepIndex = httpHeader[i].indexOf(\": \");\n\t\t\tif (sepIndex === -1) continue;\n\t\t\tconst key = httpHeader[i].slice(0, Math.max(0, sepIndex));\n\t\t\tconst value = httpHeader[i].slice(Math.max(0, sepIndex + 2));\n\t\t\tif (!key) continue;\n\t\t\tconst lowerKey = key.toLowerCase();\n\t\t\tif (lowerKey === \"connection\") for (const token of value.split(\",\")) {\n\t\t\t\tconst t = token.trim().toLowerCase();\n\t\t\t\tif (t) connectionTokens.add(t);\n\t\t\t}\n\t\t\tif (HOP_BY_HOP_HEADERS.has(lowerKey)) continue;\n\t\t\theaders.push([key, value]);\n\t\t}\n\t\tif (connectionTokens.size > 0) {\n\t\t\tfor (let i = headers.length - 1; i >= 0; i--) if (connectionTokens.has(headers[i][0].toLowerCase())) headers.splice(i, 1);\n\t\t}\n\t\tconst nullBody = NULL_BODY_STATUSES.has(this.statusCode);\n\t\treturn new Response(nullBody ? null : this.#socket._webResBody, {\n\t\t\tstatus: this.statusCode,\n\t\t\tstatusText: this.statusMessage,\n\t\t\theaders\n\t\t});\n\t}\n};\nfunction stripTransferEncoding(headers) {\n\tif (!headers || typeof headers !== \"object\") return headers;\n\tif (Array.isArray(headers)) {\n\t\tif (headers.length > 0 && Array.isArray(headers[0])) return headers.filter(([key]) => String(key).toLowerCase() !== \"transfer-encoding\");\n\t\tconst out = [];\n\t\tfor (let i = 0; i < headers.length; i += 2) {\n\t\t\tif (String(headers[i]).toLowerCase() === \"transfer-encoding\") continue;\n\t\t\tout.push(headers[i], headers[i + 1]);\n\t\t}\n\t\treturn out;\n\t}\n\tlet hasTransferEncoding = false;\n\tfor (const key in headers) if (key.toLowerCase() === \"transfer-encoding\") {\n\t\thasTransferEncoding = true;\n\t\tbreak;\n\t}\n\tif (!hasTransferEncoding) return headers;\n\tconst out = {};\n\tfor (const key in headers) {\n\t\tif (key.toLowerCase() === \"transfer-encoding\") continue;\n\t\tout[key] = headers[key];\n\t}\n\treturn out;\n}\nasync function fetchNodeHandler(handler, req) {\n\tconst nodeRuntime = req.runtime?.node;\n\tif (nodeRuntime && nodeRuntime.req && nodeRuntime.res) return await callNodeHandler(handler, req);\n\tconst socket = new WebRequestSocket(req);\n\tconst nodeReq = new WebIncomingMessage(req, socket);\n\tconst nodeRes = new WebServerResponse(nodeReq, socket);\n\ttry {\n\t\tif (handler.length > 2) await new Promise((resolve, reject) => {\n\t\t\tnodeRes.once(\"finish\", () => resolve());\n\t\t\tnodeRes.once(\"close\", () => resolve());\n\t\t\tnodeRes.once(\"error\", (error) => reject(error));\n\t\t\tPromise.resolve(handler(nodeReq, nodeRes, (error) => {\n\t\t\t\tif (error) return reject(error);\n\t\t\t\tif (!nodeRes.writableEnded) nodeRes.end();\n\t\t\t\tresolve();\n\t\t\t})).catch((error) => reject(error));\n\t\t});\n\t\telse await handler(nodeReq, nodeRes);\n\t\treturn await nodeRes.toWebResponse();\n\t} catch (error) {\n\t\tif (!(req.signal?.aborted || error?.name === \"AbortError\" || error?.code === \"ERR_STREAM_PREMATURE_CLOSE\")) console.error(error, { cause: {\n\t\t\treq,\n\t\t\thandler\n\t\t} });\n\t\treturn new Response(null, {\n\t\t\tstatus: 500,\n\t\t\tstatusText: \"Internal Server Error\"\n\t\t});\n\t}\n}\nfunction toNodeHandler(handler, options) {\n\tif (handler.__nodeHandler) return handler.__nodeHandler;\n\tfunction convertedNodeHandler(nodeReq, nodeRes) {\n\t\tconst res = handler(new NodeRequest({\n\t\t\treq: nodeReq,\n\t\t\tres: nodeRes,\n\t\t\tmaxRequestBodySize: options?.maxRequestBodySize,\n\t\t\ttrustProxy: options?.trustProxy\n\t\t}));\n\t\treturn typeof res?.then === \"function\" ? res.then((resolvedRes) => send(nodeRes, resolvedRes)) : send(nodeRes, res);\n\t}\n\tconvertedNodeHandler.__fetchHandler = handler;\n\tassignFnName(convertedNodeHandler, handler, \" (converted to Node handler)\");\n\treturn convertedNodeHandler;\n}\nfunction send(nodeRes, webRes) {\n\treturn sendNodeResponse(nodeRes, webRes).catch((error) => handleSendError(nodeRes, error));\n}\nfunction toFetchHandler(handler) {\n\tif (handler.__fetchHandler) return handler.__fetchHandler;\n\tfunction convertedNodeHandler(req) {\n\t\treturn fetchNodeHandler(handler, req);\n\t}\n\tconvertedNodeHandler.__nodeHandler = handler;\n\tassignFnName(convertedNodeHandler, handler, \" (converted to Web handler)\");\n\treturn convertedNodeHandler;\n}\nfunction assignFnName(target, source, suffix) {\n\tif (source.name) try {\n\t\tObject.defineProperty(target, \"name\", { value: `${source.name}${suffix}` });\n\t} catch {}\n}\nfunction serve(options) {\n\treturn new NodeServer(options);\n}\nvar NodeServer = class {\n\truntime = \"node\";\n\toptions;\n\tnode;\n\tserveOptions;\n\tfetch;\n\twaitUntil;\n\t#isSecure;\n\t#listeningPromise;\n\t#listenError;\n\t#listenErrorObserved;\n\t#wait;\n\tconstructor(options) {\n\t\tthis.options = {\n\t\t\t...options,\n\t\t\tmiddleware: [...options.middleware || []]\n\t\t};\n\t\tfor (const plugin of options.plugins || []) plugin(this);\n\t\terrorPlugin(this);\n\t\tconst fetchHandler = this.fetch = wrapFetch(this);\n\t\tconst handler = (nodeReq, nodeRes) => {\n\t\t\tconst reqUrl = nodeReq.url;\n\t\t\tif (reqUrl && reqUrl[0] !== \"/\" && reqUrl !== \"*\" && !isValidAbsoluteForm(reqUrl)) {\n\t\t\t\tnodeRes.statusCode = 400;\n\t\t\t\tnodeRes.end();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst request = new NodeRequest({\n\t\t\t\treq: nodeReq,\n\t\t\t\tres: nodeRes,\n\t\t\t\tmaxRequestBodySize: this.options.maxRequestBodySize,\n\t\t\t\ttrustProxy: this.options.trustProxy\n\t\t\t});\n\t\t\trequest.waitUntil = this.#wait?.waitUntil;\n\t\t\tlet res;\n\t\t\ttry {\n\t\t\t\tres = fetchHandler(request);\n\t\t\t} catch (error) {\n\t\t\t\treturn sendErrorResponse(nodeRes, error, this.options.silent);\n\t\t\t}\n\t\t\treturn typeof res?.then === \"function\" ? res.then((resolvedRes) => sendNodeResponseDetached(nodeRes, resolvedRes, this.options.silent), (error) => sendErrorResponse(nodeRes, error, this.options.silent)) : sendNodeResponseDetached(nodeRes, res, this.options.silent);\n\t\t};\n\t\tthis.node = {\n\t\t\thandler,\n\t\t\tserver: void 0\n\t\t};\n\t\tconst loader = globalThis.__srvxLoader__;\n\t\tif (loader) {\n\t\t\tloader({ server: this });\n\t\t\treturn;\n\t\t}\n\t\tgracefulShutdownPlugin(this);\n\t\tthis.#wait = createWaitUntil();\n\t\tthis.waitUntil = this.#wait.waitUntil;\n\t\tconst tls = resolveTLSOptions(this.options);\n\t\tconst { port, hostname: host } = resolvePortAndHost(this.options);\n\t\tthis.serveOptions = {\n\t\t\tport,\n\t\t\thost,\n\t\t\texclusive: !this.options.reusePort,\n\t\t\treusePort: this.options.reusePort,\n\t\t\t...tls,\n\t\t\t...this.options.node\n\t\t};\n\t\tlet server;\n\t\tthis.#isSecure = !!this.serveOptions.cert && this.options.protocol !== \"http\";\n\t\tif (this.options.node?.http2 ?? this.#isSecure) {\n\t\t\tif (this.#isSecure) server = nodeHTTP2.createSecureServer({\n\t\t\t\tallowHTTP1: true,\n\t\t\t\t...this.serveOptions\n\t\t\t}, handler);\n\t\t\telse throw new Error(\"node.http2 option requires tls certificate!\");\n\t\t} else if (this.#isSecure) server = nodeHTTPS.createServer(this.serveOptions, handler);\n\t\telse server = nodeHTTP.createServer(this.serveOptions, handler);\n\t\tthis.node.server = server;\n\t\tif (!options.manual) this.serve().catch((error) => this.#reportUnobservedListenError(error));\n\t}\n\t#reportUnobservedListenError(error) {\n\t\tif (this.#listenErrorObserved) return;\n\t\tif (!this.options.silent) console.error(\"[srvx] Failed to start server:\", error);\n\t\tconst process = globalThis.process;\n\t\tif (process && !process.exitCode) process.exitCode = 1;\n\t}\n\tserve() {\n\t\tif (this.#listeningPromise) return this.#listeningPromise.then(() => this);\n\t\tconst server = this.node?.server;\n\t\tif (!server) return Promise.reject(/* @__PURE__ */ new Error(\"Server not initialized\"));\n\t\tthis.#listenError = void 0;\n\t\tthis.#listeningPromise = new Promise((resolve, reject) => {\n\t\t\tconst onError = (error) => {\n\t\t\t\tserver.off(\"listening\", onListening);\n\t\t\t\tthis.#listenError = error;\n\t\t\t\tthis.#listeningPromise = void 0;\n\t\t\t\treject(error);\n\t\t\t};\n\t\t\tconst onListening = () => {\n\t\t\t\tserver.off(\"error\", onError);\n\t\t\t\tprintListening(this.options, this.url);\n\t\t\t\tresolve();\n\t\t\t};\n\t\t\tserver.once(\"error\", onError);\n\t\t\tserver.once(\"listening\", onListening);\n\t\t\tserver.listen(this.serveOptions);\n\t\t});\n\t\treturn this.#listeningPromise.then(() => this);\n\t}\n\tget url() {\n\t\tconst addr = this.node?.server?.address();\n\t\tif (!addr) return;\n\t\treturn typeof addr === \"string\" ? addr : fmtURL(addr.address, addr.port, this.#isSecure);\n\t}\n\tready() {\n\t\tthis.#listenErrorObserved = true;\n\t\tif (this.#listenError) return Promise.reject(this.#listenError);\n\t\treturn Promise.resolve(this.#listeningPromise).then(() => this);\n\t}\n\tasync close(closeAll) {\n\t\tawait Promise.all([this.#wait?.wait(), new Promise((resolve, reject) => {\n\t\t\tconst server = this.node?.server;\n\t\t\tif (server && closeAll && \"closeAllConnections\" in server) server.closeAllConnections();\n\t\t\tif (!server || !server.listening) return resolve();\n\t\t\tserver.close((error) => error ? reject(error) : resolve());\n\t\t})]);\n\t}\n};\nexport { NodeResponse as FastResponse, FastURL, NodeRequest, NodeResponse, fetchNodeHandler, patchGlobalRequest, sendNodeResponse, serve, toFetchHandler, toNodeHandler };\n","/**\n * Stack trace frame classifier — categorizes stack frames by origin.\n *\n * Provides `classifyFrame` (single frame) and `classifyStack` (full stack\n * string) as the shared primitives used by the error page, terminal\n * formatter, and overlay.\n *\n * Dev-only: this module is only imported by other dev-tools modules.\n */\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport type FrameType = 'app' | 'framework' | 'internal';\n\nexport interface ClassifiedFrame {\n raw: string;\n type: FrameType;\n file?: string;\n line?: number;\n col?: number;\n}\n\n// ─── Frame Parsing ──────────────────────────────────────────────────────────\n\n/** Parse file/line/col from a stack frame line. */\nfunction parseFrame(frameLine: string): { file?: string; line?: number; col?: number } {\n const parenMatch = /\\(([^)]+):(\\d+):(\\d+)\\)/.exec(frameLine);\n if (parenMatch) {\n return { file: parenMatch[1], line: Number(parenMatch[2]), col: Number(parenMatch[3]) };\n }\n const bareMatch = /at (\\/[^:]+):(\\d+):(\\d+)/.exec(frameLine);\n if (bareMatch) {\n return { file: bareMatch[1], line: Number(bareMatch[2]), col: Number(bareMatch[3]) };\n }\n return {};\n}\n\n// ─── Classification ─────────────────────────────────────────────────────────\n\n/**\n * Classify a single stack frame line by origin.\n *\n * - 'app': user application code (in project root, not node_modules)\n * - 'framework': timber-app internal code\n * - 'internal': node_modules, Node.js internals\n */\nexport function classifyFrame(frameLine: string, projectRoot: string): FrameType {\n const trimmed = frameLine.trim();\n\n if (trimmed.includes('packages/timber-app/')) return 'framework';\n if (trimmed.includes('node_modules/')) return 'internal';\n if (trimmed.startsWith('at node:') || trimmed.includes('(node:')) return 'internal';\n if (trimmed.includes(projectRoot)) return 'app';\n\n return 'internal';\n}\n\n/**\n * Classify all frames in a full stack trace string.\n *\n * Parses the stack, skips the first line (error message), filters to\n * lines starting with \"at \", and returns classified frames with optional\n * file/line/col metadata.\n */\nexport function classifyStack(stack: string, projectRoot: string): ClassifiedFrame[] {\n return stack\n .split('\\n')\n .slice(1) // skip first line (error message)\n .filter((line) => line.trim().startsWith('at '))\n .map((raw) => {\n const type = classifyFrame(raw, projectRoot);\n const { file, line, col } = parseFrame(raw);\n return { raw, type, file, line, col };\n });\n}\n","/**\n * Terminal error formatting — boxed, color-coded error output for dev mode.\n *\n * Produces a visually scannable error block with:\n * - Unicode box-drawing border around the error\n * - Phase badge and error message\n * - First app frame highlighted as the primary action item\n * - OSC 8 clickable file:line links (VSCode terminal, iTerm2, etc.)\n * - Internal/framework frames collapsed with a count\n * - Component stack (for React render errors)\n *\n * Dev-only: this module is only imported by dev-error-overlay.ts.\n *\n * Design doc: 21-dev-server.md §\"Error Overlay\"\n */\n\nimport { pathToFileURL } from 'node:url';\nimport type { InspectColor } from 'node:util';\nimport { styleText } from 'node:util';\nimport {\n extractComponentStack,\n parseFirstAppFrame,\n PHASE_LABELS,\n type ErrorPhase,\n} from './overlay.ts';\nimport { classifyStack } from './stack-classifier.ts';\n\n// ─── Styling ────────────────────────────────────────────────────────────────\n\ntype Format = InspectColor | readonly InspectColor[];\nconst noValidate = { validateStream: false } as const;\nconst style = (fmt: Format, text: string) => styleText(fmt, text, noValidate);\n\n// ─── OSC 8 Hyperlinks ──────────────────────────────────────────────────────\n\n/**\n * Wrap text in an OSC 8 hyperlink escape sequence.\n *\n * Terminals that support OSC 8 (VSCode, iTerm2, Windows Terminal, etc.)\n * render this as a clickable link. Others ignore the escape sequences\n * and show the text normally.\n *\n * Format: \\x1b]8;;URL\\x07TEXT\\x1b]8;;\\x07\n */\nfunction hyperlink(text: string, url: string): string {\n return `\\x1b]8;;${url}\\x07${text}\\x1b]8;;\\x07`;\n}\n\n/**\n * Format a file:line:col reference as a clickable terminal link.\n *\n * Uses file:// URLs so terminals open the file in the configured editor.\n * The link text is styled with cyan + underline for visibility.\n */\nfunction fileLink(filePath: string, line?: number, col?: number): string {\n const display = line ? `${filePath}:${line}${col ? `:${col}` : ''}` : filePath;\n // Use pathToFileURL for proper encoding of spaces, #, ?, and Windows\n // drive letters (TIM-792). Append :line:col after the URL.\n const base = pathToFileURL(filePath).href;\n const url = line ? `${base}:${line}${col ? `:${col}` : ''}` : base;\n return style(['cyan', 'underline'], hyperlink(display, url));\n}\n\n// ─── Box Drawing ────────────────────────────────────────────────────────────\n\nconst BOX = {\n topLeft: '╭',\n topRight: '╮',\n bottomLeft: '╰',\n bottomRight: '╯',\n horizontal: '─',\n vertical: '│',\n};\n\n/**\n * Wrap lines of text in a Unicode box with a colored left border.\n *\n * @param lines - Content lines (no ANSI length calculation — keeps it simple)\n * @param width - Box width (characters). Lines longer than this are not truncated.\n */\nfunction box(lines: string[], borderFormat: Format, width = 80): string {\n const bar = BOX.horizontal.repeat(width - 2);\n const output: string[] = [];\n\n output.push(style(borderFormat, `${BOX.topLeft}${bar}${BOX.topRight}`));\n for (const line of lines) {\n output.push(`${style(borderFormat, BOX.vertical)} ${line}`);\n }\n output.push(style(borderFormat, `${BOX.bottomLeft}${bar}${BOX.bottomRight}`));\n\n return output.join('\\n');\n}\n\n// Frame classification and parsing provided by stack-classifier.ts\n\n// ─── Public API ─────────────────────────────────────────────────────────────\n\n/**\n * Format an error for terminal output with a boxed layout.\n *\n * The output is designed to be scannable at a glance:\n * 1. Red box with phase badge and error message\n * 2. First app frame as a clickable link (the primary action item)\n * 3. App frames listed normally\n * 4. Internal/framework frames collapsed with count\n * 5. Component stack (if present)\n */\nexport function formatTerminalError(error: Error, phase: ErrorPhase, projectRoot: string): string {\n const sections: string[] = [];\n const componentStack = extractComponentStack(error);\n const loc = parseFirstAppFrame(error.stack ?? '', projectRoot);\n const frames = error.stack ? classifyStack(error.stack, projectRoot) : [];\n const appFrames = frames.filter((f) => f.type === 'app');\n const internalCount = frames.filter((f) => f.type !== 'app').length;\n\n // ── Box: Phase + Message ──────────────────────────────────────────\n const boxLines: string[] = [];\n boxLines.push(style(['red', 'bold'], `${PHASE_LABELS[phase]} Error`));\n boxLines.push('');\n\n // Error message — may be multi-line\n for (const msgLine of error.message.split('\\n')) {\n boxLines.push(style('red', msgLine));\n }\n\n // Primary file location (clickable)\n if (loc) {\n boxLines.push('');\n const relPath = loc.file.startsWith(projectRoot)\n ? loc.file.slice(projectRoot.length + 1)\n : loc.file;\n boxLines.push(\n `${style('bold', '→')} ${fileLink(loc.file, loc.line, loc.column)} ${style('dim', `(${relPath})`)}`\n );\n }\n\n sections.push(box(boxLines, 'red'));\n\n // ── Component Stack ───────────────────────────────────────────────\n if (componentStack) {\n sections.push('');\n sections.push(` ${style('bold', 'Component Stack:')}`);\n for (const csLine of componentStack.trim().split('\\n')) {\n sections.push(` ${style('dim', csLine.trim())}`);\n }\n }\n\n // ── App Frames ────────────────────────────────────────────────────\n if (appFrames.length > 0) {\n sections.push('');\n sections.push(` ${style('bold', 'Application Frames:')}`);\n for (let i = 0; i < appFrames.length; i++) {\n const f = appFrames[i]!;\n if (f.file && f.line) {\n const prefix = i === 0 ? style('bold', '▸') : ' ';\n sections.push(\n ` ${prefix} ${fileLink(f.file, f.line, f.col)} ${style('dim', extractFnName(f.raw))}`\n );\n } else {\n sections.push(` ${f.raw}`);\n }\n }\n }\n\n // ── Internal Frames (collapsed) ───────────────────────────────────\n if (internalCount > 0) {\n sections.push(\n ` ${style('dim', `… ${internalCount} internal frame${internalCount !== 1 ? 's' : ''} hidden`)}`\n );\n }\n\n sections.push('');\n return sections.join('\\n');\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\n/** Extract the function name from a stack frame line like \" at fnName (/path:1:2)\". */\nfunction extractFnName(frameLine: string): string {\n const match = /at\\s+(\\S+)\\s+\\(/.exec(frameLine.trim());\n return match ? match[1]! : '';\n}\n","/**\n * Dev error overlay — formats and sends errors to Vite's browser overlay and stderr.\n *\n * Integrates with Vite's built-in error overlay (`server.hot.send`) rather\n * than implementing a custom overlay.\n *\n * Stacks are shown as they arrive: Vite's module runner already reports\n * source positions for the code it runs, and a second mapping pass moves\n * them (TIM-1555).\n *\n * Design doc: 21-dev-server.md §\"Error Overlay\", §\"RSC Source-Mapping\"\n */\n\nimport type { ViteDevServer } from 'vite';\nimport { classifyFrame } from './stack-classifier.ts';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\n/** The phase of the pipeline where the error occurred. */\nexport type ErrorPhase =\n | 'module-transform'\n | 'proxy'\n | 'middleware'\n | 'access'\n | 'render'\n | 'handler';\n\n/** Labels for terminal output. */\nexport const PHASE_LABELS: Record<ErrorPhase, string> = {\n 'module-transform': 'Module Transform',\n 'proxy': 'Proxy',\n 'middleware': 'Middleware',\n 'access': 'Access Check',\n 'render': 'RSC Render',\n 'handler': 'Route Handler',\n};\n\n// ─── Frame Classification (re-exported from stack-classifier) ───────────────\n\nexport {\n classifyFrame,\n classifyStack,\n type FrameType,\n type ClassifiedFrame,\n} from './stack-classifier.ts';\n\n// ─── Component Stack Extraction ─────────────────────────────────────────────\n\n/**\n * Extract the React component stack from an error, if present.\n * React attaches this as `componentStack` during renderToReadableStream errors.\n */\nexport function extractComponentStack(error: unknown): string | null {\n if (\n error &&\n typeof error === 'object' &&\n 'componentStack' in error &&\n typeof (error as Record<string, unknown>).componentStack === 'string'\n ) {\n return (error as Record<string, unknown>).componentStack as string;\n }\n return null;\n}\n\n// ─── First App Frame Parsing ────────────────────────────────────────────────\n\ninterface SourceLocation {\n file: string;\n line: number;\n column: number;\n}\n\n/**\n * Parse the first application frame from a stack trace.\n * Returns file/line/column for the overlay's `loc` field.\n */\nexport function parseFirstAppFrame(stack: string, projectRoot: string): SourceLocation | null {\n const lines = stack.split('\\n');\n // Match patterns like:\n // at functionName (/absolute/path:line:column)\n // at /absolute/path:line:column\n const parenRegex = /\\(([^)]+):(\\d+):(\\d+)\\)/;\n const bareRegex = /at (\\/[^:]+):(\\d+):(\\d+)/;\n\n for (const line of lines) {\n if (classifyFrame(line, projectRoot) !== 'app') continue;\n\n const match = parenRegex.exec(line) ?? bareRegex.exec(line);\n if (!match) continue;\n\n const [, file, lineNum, col] = match;\n if (file && lineNum && col) {\n return { file, line: parseInt(lineNum, 10), column: parseInt(col, 10) };\n }\n }\n\n return null;\n}\n\n// ─── Error Phase Classification ─────────────────────────────────────────────\n\n/**\n * Classify the error phase by inspecting the error's stack trace.\n * Falls back to 'render' if no specific phase can be determined.\n */\nexport function classifyErrorPhase(error: Error, projectRoot: string): ErrorPhase {\n const stack = error.stack ?? '';\n\n // Check for React component stack (render error)\n if (extractComponentStack(error)) return 'render';\n\n // Check for specific file patterns in app frames\n const appRoot = projectRoot.replace(/\\/$/, '');\n if (stack.includes(`${appRoot}/app/`) || stack.includes('/app/')) {\n if (stack.includes('/middleware.ts') || stack.includes('/middleware.js')) return 'middleware';\n if (stack.includes('/access.ts') || stack.includes('/access.js')) return 'access';\n if (stack.includes('/route.ts') || stack.includes('/route.js')) return 'handler';\n }\n\n return 'render';\n}\n\n// ─── Terminal Formatting ────────────────────────────────────────────────────\n\n// formatTerminalError is implemented in the dedicated terminal formatter module\n// (terminal.ts) to keep this file under 500 lines.\n// Import for use in sendErrorToOverlay, and re-export for external consumers.\nimport { formatTerminalError as _formatTerminalError } from './terminal.ts';\nexport const formatTerminalError = _formatTerminalError;\n\n// ─── Overlay Integration ────────────────────────────────────────────────────\n\n/**\n * Send an error to Vite's browser overlay and log it to stderr.\n *\n * Reads `error` and never writes it: the pipeline may render the same object\n * on the fallback error page after reporting it here.\n *\n * The dev server remains running — errors are handled, not fatal.\n */\nexport function sendErrorToOverlay(\n server: ViteDevServer,\n error: Error,\n phase: ErrorPhase,\n projectRoot: string\n): void {\n // Log to stderr with frame dimming\n const formatted = formatTerminalError(error, phase, projectRoot);\n process.stderr.write(`${formatted}\\n`);\n\n // Build overlay payload\n const loc = parseFirstAppFrame(error.stack ?? '', projectRoot);\n const componentStack = extractComponentStack(error);\n\n let message = error.message;\n if (componentStack) {\n message = `${error.message}\\n\\nComponent Stack:\\n${componentStack.trim()}`;\n }\n\n // Send to browser via Vite's error overlay protocol\n try {\n server.hot.send({\n type: 'error',\n err: {\n message,\n stack: error.stack ?? '',\n id: loc?.file,\n plugin: `timber (${PHASE_LABELS[phase]})`,\n loc: loc ? { file: loc.file, line: loc.line, column: loc.column } : undefined,\n },\n });\n } catch (e) {\n console.debug('[timber] overlay error send failed:', e instanceof Error ? e.message : e);\n }\n}\n","/**\n * Shared HTML escaping utility.\n *\n * Used by both dev-only paths (error pages, 404 page) and production\n * paths (deny-renderer, RSC helpers). Lives in server/utils/ because\n * it's not dev-only.\n */\n\n/**\n * Escape a string for safe embedding in HTML content or attributes.\n *\n * Replaces `&`, `<`, `>`, and `\"` with their HTML entity equivalents.\n */\nexport function escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n","/**\n * Shared scaffold for dev-mode HTML pages (dev error page, dev 404 page).\n *\n * Both pages are self-contained HTML documents served when the normal\n * render pipeline can't produce a page. They share:\n * - The document scaffold (head, container, footer)\n * - Base CSS (layout, light/dark variables, common components)\n * - The HMR auto-reload script that watches Vite's WebSocket and reloads\n * when the developer fixes the error / adds the missing route\n *\n * Dev-only: this module is only imported by dev-mode code paths. It is\n * never included in production builds.\n *\n * Design doc: 21-dev-server.md §\"Error Overlay\", §\"Dev 404 Page\"\n */\n\nimport { escapeHtml } from '../server/utils/escape-html.ts';\n\n// ─── HMR Options ────────────────────────────────────────────────────────────\n\n/**\n * HMR connection options for the dev page auto-reload WebSocket.\n * Derived from Vite's resolved server config so the page can connect\n * to the correct HMR endpoint (TIM-789).\n */\nexport interface DevErrorHmrOptions {\n protocol?: string;\n host?: string;\n port?: number;\n path?: string;\n token?: string;\n}\n\n/**\n * Hooks the dev server hands the RSC environment, which cannot read the\n * Vite server itself (separate module graph). The RSC entry exports\n * `setDevHooks()`; the dev server calls it after importing the entry.\n *\n * Declared here, not in the RSC entry, so the dev-server plugin can name\n * the type without pulling the RSC entry into its type graph.\n */\nexport interface DevHooks {\n /** Forwards a pipeline error to the browser error overlay. */\n onPipelineError?: (error: Error, phase: string) => void;\n /**\n * The resolved HMR endpoint for the auto-reload WebSocket on the dev 404\n * page and the fallback error page (TIM-1067).\n */\n hmrOptions?: DevErrorHmrOptions;\n}\n\n/**\n * Extract HMR connection options from a Vite resolved config.\n * Used by the dev server to pass HMR config to the dev page generators\n * so the auto-reload WebSocket connects to the correct endpoint.\n */\nexport function extractHmrOptions(config: {\n server?: {\n hmr?:\n | { protocol?: string; host?: string; port?: number; clientPort?: number; path?: string }\n | boolean;\n };\n webSocketToken?: string;\n}): DevErrorHmrOptions | undefined {\n const hmr = config.server?.hmr;\n if (hmr === false) return undefined;\n const hmrOpts = typeof hmr === 'object' ? hmr : {};\n const opts: DevErrorHmrOptions = {\n protocol: hmrOpts.protocol,\n host: hmrOpts.host,\n port: hmrOpts.clientPort || hmrOpts.port,\n path: hmrOpts.path,\n token: config.webSocketToken,\n };\n // If no HMR fields were explicitly configured, return undefined so the\n // reload script derives everything from the browser's location, which\n // correctly includes the current port (TIM-805).\n if (!opts.protocol && !opts.host && !opts.port && !opts.path && !opts.token) {\n return undefined;\n }\n return opts;\n}\n\n// ─── Auto-Reload Script ─────────────────────────────────────────────────────\n\n/**\n * Build the inline auto-reload script for dev pages.\n *\n * The page is static HTML, not a Vite-managed module, so the normal HMR\n * update flow can't reach it. Instead we connect to Vite's HMR WebSocket\n * directly and reload on any update event.\n *\n * URL construction mirrors Vite's own client.mjs: explicitly configured\n * values win, and the browser's location fills any gaps at runtime. This\n * matters because Vite 8 requires a `?token=` on browser WS upgrades in\n * every config — a config with only a token still needs the host and port\n * from `location` (TIM-1067).\n */\nexport function buildReloadScript(hmrOptions?: DevErrorHmrOptions): string {\n // `<` is escaped as a unicode escape so config text can never close the\n // <script> block (same defense as escJsonForScript in error-page.ts, TIM-788).\n const opts = JSON.stringify(hmrOptions ?? {}).replace(/</g, '\\\\u003c');\n return ` (function() {\n try {\n var o = ${opts};\n var protocol = o.protocol || (location.protocol === 'https:' ? 'wss' : 'ws');\n var host = o.host || location.hostname;\n var port = o.port || location.port;\n var url = protocol + '://' + host + (port ? ':' + port : '') + (o.path || '')\n + (o.token ? '?token=' + encodeURIComponent(o.token) : '');\n var ws = new WebSocket(url, 'vite-hmr');\n ws.addEventListener('message', function(e) {\n try {\n var data = JSON.parse(e.data);\n if (data.type === 'full-reload' || data.type === 'update') {\n location.reload();\n }\n } catch (ex) {}\n });\n } catch (ex) {}\n })();`;\n}\n\n// ─── Page Shell ─────────────────────────────────────────────────────────────\n\nexport interface DevPageShellOptions {\n /** Page title — escaped by the shell. */\n title: string;\n /** Page-specific CSS appended after the shared base (badge colors, etc.). */\n extraCss?: string;\n /** Inner HTML of the container — must already be escaped by the caller. */\n bodyHtml: string;\n /** Hint text shown next to the timber logo in the footer. */\n footerHint: string;\n /** Additional inline script placed before the auto-reload script. */\n extraScript?: string;\n /** HMR connection options for the auto-reload WebSocket. */\n hmrOptions?: DevErrorHmrOptions;\n}\n\n/**\n * Render a self-contained dev page: scaffold + shared CSS + footer +\n * HMR auto-reload script.\n */\nexport function renderDevPageShell(opts: DevPageShellOptions): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>${escapeHtml(opts.title)}</title>\n <style>${SHARED_CSS}${opts.extraCss ?? ''}</style>\n</head>\n<body>\n <div class=\"container\">\n ${opts.bodyHtml}\n\n <footer class=\"footer\">\n <span class=\"timber-logo\">🪵 timber.js</span>\n <span class=\"footer-hint\">${opts.footerHint}</span>\n </footer>\n </div>\n\n <script>\n${opts.extraScript ?? ''}\n // Auto-reload when Vite HMR signals an update (file fixed, route added).\n${buildReloadScript(opts.hmrOptions)}\n </script>\n</body>\n</html>`;\n}\n\n// ─── Shared CSS ─────────────────────────────────────────────────────────────\n\n/**\n * Base CSS shared by all dev pages. Pages override the --badge-* variables\n * (and add their own component styles) via `extraCss`.\n */\nconst SHARED_CSS = `\n :root {\n --bg: #fff;\n --fg: #1a1a1a;\n --fg-dim: #6b7280;\n --border: #e5e7eb;\n --source-bg: #fafafa;\n --link: #2563eb;\n --code-bg: #f3f4f6;\n --btn-bg: #f3f4f6;\n --btn-fg: #374151;\n --btn-border: #d1d5db;\n --code-font: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;\n }\n\n @media (prefers-color-scheme: dark) {\n :root {\n --bg: #0a0a0a;\n --fg: #f5f5f5;\n --fg-dim: #9ca3af;\n --border: #27272a;\n --source-bg: #18181b;\n --link: #60a5fa;\n --code-bg: #27272a;\n --btn-bg: #27272a;\n --btn-fg: #e5e7eb;\n --btn-border: #3f3f46;\n }\n }\n\n * { margin: 0; padding: 0; box-sizing: border-box; }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n background: var(--bg);\n color: var(--fg);\n line-height: 1.6;\n padding: 2rem;\n }\n\n .container { max-width: 56rem; margin: 0 auto; }\n .header { margin-bottom: 1.5rem; }\n\n .badge {\n display: inline-block;\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n padding: 0.25rem 0.625rem;\n border-radius: 0.375rem;\n background: var(--badge-bg);\n color: var(--badge-fg);\n border: 1px solid var(--badge-border);\n margin-bottom: 0.75rem;\n }\n\n .message {\n font-size: 1.375rem;\n font-weight: 600;\n line-height: 1.3;\n word-break: break-word;\n }\n\n .hint {\n margin-top: 0.5rem;\n color: var(--fg-dim);\n font-size: 0.875rem;\n }\n\n .section {\n margin-bottom: 1rem;\n border: 1px solid var(--border);\n border-radius: 0.5rem;\n overflow: hidden;\n }\n\n .section-header {\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n padding: 0.5rem 0.75rem;\n background: var(--source-bg);\n border-bottom: 1px solid var(--border);\n color: var(--fg-dim);\n }\n\n .footer {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding-top: 1rem;\n border-top: 1px solid var(--border);\n font-size: 0.75rem;\n color: var(--fg-dim);\n }\n\n .timber-logo { font-weight: 600; white-space: nowrap; }\n`;\n","/**\n * Dev error page — self-contained HTML error page for dev server 500s.\n *\n * Generates a styled, self-contained HTML page when the RSC pipeline fails\n * and the Vite error overlay can't fire (e.g., first page load before HMR\n * WebSocket connects, RSC entry module crash, early pipeline errors).\n *\n * This is NOT a replacement for Vite's error overlay — it's the fallback\n * for when the overlay's transport (WebSocket) isn't available yet.\n *\n * Dev-only: this module is only imported by dev-server.ts (apply: 'serve')\n * and via dynamic import from fallback-error.ts in dev mode. It is never\n * included in production builds.\n *\n * Design doc: 21-dev-server.md §\"Error Overlay\"\n */\n\nimport { readFileSync } from 'node:fs';\nimport { extractComponentStack, parseFirstAppFrame, type ErrorPhase } from './overlay.ts';\nimport { classifyStack } from './stack-classifier.ts';\nimport { escapeHtml } from '../server/utils/escape-html.ts';\nimport { renderDevPageShell, type DevErrorHmrOptions } from './dev-page-shell.ts';\n\n// ─── Phase Labels ───────────────────────────────────────────────────────────\n\nconst PHASE_LABELS: Record<ErrorPhase, string> = {\n 'module-transform': 'Module Transform',\n 'proxy': 'Proxy',\n 'middleware': 'Middleware',\n 'access': 'Access Check',\n 'render': 'RSC Render',\n 'handler': 'Route Handler',\n};\n\nconst PHASE_HINTS: Record<ErrorPhase, string> = {\n 'module-transform':\n 'This error occurred while Vite was transforming a module. Check for syntax errors or missing imports.',\n 'proxy': 'This error occurred in proxy.ts. Check your proxy configuration.',\n 'middleware':\n 'This error occurred in a middleware.ts file. Check the middleware function for unhandled exceptions.',\n 'access': 'This error occurred in an access.ts file. Check your access control logic.',\n 'render':\n 'This error occurred while rendering a server component. Check the component for runtime errors.',\n 'handler': 'This error occurred in a route handler (route.ts). Check the handler function.',\n};\n\n// ─── Source Context ─────────────────────────────────────────────────────────\n\n/**\n * Try to read a few lines of source code around the error location.\n * Returns null if the file can't be read (e.g., virtual modules).\n *\n * Uses a top-level `node:fs` import — this module is dev-server-only and\n * Node-only. A bare `require()` here would throw in the ESM build and the\n * code frame would silently never render (TIM-1067, B48).\n */\nfunction readSourceContext(\n filePath: string,\n line: number,\n contextLines = 3\n): { lines: Array<{ num: number; text: string; highlight: boolean }>; startLine: number } | null {\n try {\n const content = readFileSync(filePath, 'utf-8');\n const allLines = content.split('\\n');\n\n const start = Math.max(0, line - 1 - contextLines);\n const end = Math.min(allLines.length, line + contextLines);\n\n return {\n startLine: start + 1,\n lines: allLines.slice(start, end).map((text: string, i: number) => ({\n num: start + 1 + i,\n text,\n highlight: start + 1 + i === line,\n })),\n };\n } catch (e) {\n console.debug('[timber] source context extraction failed:', e instanceof Error ? e.message : e);\n return null;\n }\n}\n\n// HTML escaping provided by server/utils/escape-html.ts\nconst esc = escapeHtml;\n\n/**\n * Escape a JSON string for safe embedding in an HTML `<script>` block.\n *\n * `JSON.stringify` does not escape `</script>`, so error text containing\n * that sequence breaks out of the script block — an XSS vector.\n * We replace all `<` with the equivalent unicode escape, which is valid in\n * JSON strings and prevents the HTML parser from seeing a closing\n * `</script>` tag.\n *\n * Security: TIM-788\n */\nfunction escJsonForScript(json: string): string {\n return json.replace(/</g, '\\\\u003c');\n}\n\n// ─── HTML Generation ────────────────────────────────────────────────────────\n\n/**\n * Generate a self-contained HTML error page for dev server 500 responses.\n *\n * The page includes:\n * - Error message and phase label\n * - Source code context around the first app frame (if readable)\n * - Component stack (for React render errors)\n * - Classified stack trace (app frames highlighted, internals collapsed)\n * - Copy button for the full error\n * - Dark/light mode via prefers-color-scheme\n * - Auto-reconnect script that watches for Vite HMR and reloads\n */\nexport function generateDevErrorPage(\n error: Error,\n phase: ErrorPhase,\n projectRoot: string,\n hmrOptions?: DevErrorHmrOptions\n): string {\n const message = error.message || 'Unknown error';\n const phaseLabel = PHASE_LABELS[phase];\n const phaseHint = PHASE_HINTS[phase];\n const componentStack = extractComponentStack(error);\n const loc = parseFirstAppFrame(error.stack ?? '', projectRoot);\n const frames = error.stack ? classifyStack(error.stack, projectRoot) : [];\n const appFrames = frames.filter((f) => f.type === 'app');\n const internalFrameCount = frames.filter((f) => f.type !== 'app').length;\n\n // Try to read source code context\n let sourceContext: ReturnType<typeof readSourceContext> = null;\n if (loc) {\n sourceContext = readSourceContext(loc.file, loc.line);\n }\n\n // Relative path for display\n const relPath = loc?.file.startsWith(projectRoot)\n ? loc.file.slice(projectRoot.length + 1)\n : loc?.file;\n\n const bodyHtml = `<header class=\"header\">\n <div class=\"badge\">${esc(phaseLabel)} Error</div>\n <h1 class=\"message\">${esc(message)}</h1>\n ${phaseHint ? `<p class=\"hint\">${esc(phaseHint)}</p>` : ''}\n </header>\n\n ${relPath ? `<div class=\"location\">${esc(relPath)}${loc ? `:${loc.line}:${loc.column}` : ''}</div>` : ''}\n\n ${\n sourceContext\n ? `<div class=\"source-context\">\n <div class=\"section-header\">Source</div>\n <pre class=\"source-code\"><code>${sourceContext.lines\n .map(\n (l) =>\n `<span class=\"source-line${l.highlight ? ' source-line-highlight' : ''}\"` +\n `><span class=\"line-num\">${l.num}</span>${esc(l.text)}</span>`\n )\n .join('\\n')}</code></pre>\n </div>`\n : ''\n }\n\n ${\n componentStack\n ? `<div class=\"section\">\n <div class=\"section-header\">Component Stack</div>\n <pre class=\"component-stack\">${esc(componentStack.trim())}</pre>\n </div>`\n : ''\n }\n\n ${\n appFrames.length > 0\n ? `<div class=\"section\">\n <div class=\"section-header\">Application Frames</div>\n <pre class=\"stack\">${appFrames.map((f) => esc(f.raw)).join('\\n')}</pre>\n </div>`\n : ''\n }\n\n ${\n internalFrameCount > 0\n ? `<details class=\"section internal-frames\">\n <summary class=\"section-header clickable\">${internalFrameCount} internal frame${internalFrameCount !== 1 ? 's' : ''}</summary>\n <pre class=\"stack dimmed\">${frames\n .filter((f) => f.type !== 'app')\n .map((f) => esc(f.raw))\n .join('\\n')}</pre>\n </details>`\n : ''\n }\n\n <div class=\"actions\">\n <button class=\"btn\" onclick=\"copyError()\">Copy Error</button>\n </div>`;\n\n const copyScript = ` function copyError() {\n var text = ${escJsonForScript(\n JSON.stringify(\n `${phaseLabel} Error: ${message}\\n\\n` +\n (relPath ? `File: ${relPath}${loc ? `:${loc.line}:${loc.column}` : ''}\\n\\n` : '') +\n (componentStack ? `Component Stack:\\n${componentStack.trim()}\\n\\n` : '') +\n `Stack Trace:\\n${error.stack ?? ''}`\n )\n )};\n navigator.clipboard.writeText(text).then(function() {\n var btn = document.querySelector('.btn');\n if (btn) { btn.textContent = 'Copied!'; setTimeout(function() { btn.textContent = 'Copy Error'; }, 1500); }\n });\n }\n`;\n\n return renderDevPageShell({\n title: `Error — ${phaseLabel} | timber.js`,\n extraCss: ERROR_CSS,\n bodyHtml,\n footerHint: 'Fix the error and save — the page will reload automatically via HMR.',\n extraScript: copyScript,\n hmrOptions,\n });\n}\n\n// ─── Page-Specific CSS ──────────────────────────────────────────────────────\n\nconst ERROR_CSS = `\n :root {\n --badge-bg: #fef2f2;\n --badge-fg: #991b1b;\n --badge-border: #fecaca;\n --highlight-bg: #fef2f2;\n --highlight-border: #ef4444;\n --line-num: #9ca3af;\n }\n\n @media (prefers-color-scheme: dark) {\n :root {\n --badge-bg: #450a0a;\n --badge-fg: #fca5a5;\n --badge-border: #7f1d1d;\n --highlight-bg: #450a0a;\n --highlight-border: #dc2626;\n --line-num: #6b7280;\n }\n }\n\n .location {\n font-family: var(--code-font);\n font-size: 0.8125rem;\n color: var(--link);\n margin-bottom: 1rem;\n padding: 0.5rem 0.75rem;\n background: var(--source-bg);\n border-radius: 0.375rem;\n border: 1px solid var(--border);\n }\n\n .source-context {\n margin-bottom: 1rem;\n border: 1px solid var(--border);\n border-radius: 0.5rem;\n overflow: hidden;\n }\n\n .source-code {\n overflow-x: auto;\n font-family: var(--code-font);\n font-size: 0.8125rem;\n line-height: 1.7;\n padding: 0;\n margin: 0;\n }\n\n .source-code code {\n display: block;\n }\n\n .source-line {\n display: block;\n padding: 0 0.75rem;\n border-left: 3px solid transparent;\n }\n\n .source-line-highlight {\n background: var(--highlight-bg);\n border-left-color: var(--highlight-border);\n }\n\n .line-num {\n display: inline-block;\n width: 3rem;\n text-align: right;\n margin-right: 1rem;\n color: var(--line-num);\n user-select: none;\n }\n\n .clickable { cursor: pointer; }\n .clickable:hover { background: var(--btn-bg); }\n\n .component-stack, .stack {\n font-family: var(--code-font);\n font-size: 0.8125rem;\n line-height: 1.7;\n padding: 0.75rem;\n overflow-x: auto;\n white-space: pre;\n margin: 0;\n }\n\n .dimmed { color: var(--fg-dim); }\n\n .actions {\n margin: 1.5rem 0;\n }\n\n .btn {\n font-family: inherit;\n font-size: 0.8125rem;\n font-weight: 500;\n padding: 0.5rem 1rem;\n border-radius: 0.375rem;\n border: 1px solid var(--btn-border);\n background: var(--btn-bg);\n color: var(--btn-fg);\n cursor: pointer;\n transition: background 0.15s;\n }\n\n .btn:hover { opacity: 0.85; }\n`;\n","/**\n * Config validation — validates timber.config.ts at startup.\n *\n * Runs in the plugin's configResolved hook (once, at startup/build).\n * Each check produces a clear error message with the invalid value,\n * what's expected, and how to fix it.\n *\n * Design doc: 18-build-system.md\n */\n\nimport { createRequire } from 'node:module';\nimport type { TimberUserConfig } from './config-types.ts';\nimport { DEFAULT_RENDER_TIMEOUT_MS, RENDER_TIMEOUT_CEILING_MS } from './server/render-timeout.ts';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface ConfigError {\n field: string;\n message: string;\n value?: unknown;\n suggestion?: string;\n}\n\n// ─── Known config keys ──────────────────────────────────────────────────────\n\n// `satisfies Record<keyof TimberUserConfig, true>` makes this drift-proof:\n// adding a key to TimberUserConfig without listing it here is a compile\n// error, so validateConfig can never reject a documented option (TIM-1066).\nconst KNOWN_CONFIG_KEYS = {\n output: true,\n debug: true,\n clientJavascript: true,\n adapter: true,\n allowedOrigins: true,\n csrf: true,\n limits: true,\n forms: true,\n pageExtensions: true,\n slowRequestMs: true,\n renderTimeoutMs: true,\n devBrowserLogs: true,\n dev: true,\n serverTiming: true,\n appDir: true,\n mdx: true,\n actionEncryption: true,\n reactCompiler: true,\n sitemap: true,\n buildDir: true,\n cache: true,\n clientSegmentCache: true,\n topLoader: true,\n budget: true,\n} satisfies Record<keyof TimberUserConfig, true>;\n\nconst knownKeys = new Set<string>(Object.keys(KNOWN_CONFIG_KEYS));\n\n// ─── Validation ─────────────────────────────────────────────────────────────\n\n/**\n * Validate a TimberUserConfig object.\n *\n * Returns an array of errors. Empty array means the config is valid.\n * Does not throw — the caller decides how to surface errors.\n */\nexport function validateConfig(config: TimberUserConfig): ConfigError[] {\n const errors: ConfigError[] = [];\n\n // output\n if (config.output !== undefined && config.output !== 'server' && config.output !== 'static') {\n errors.push({\n field: 'output',\n message: `Invalid output mode: \"${String(config.output)}\". Must be \"server\" or \"static\".`,\n value: config.output,\n suggestion: 'Use output: \"server\" (default) or output: \"static\" for static site generation.',\n });\n }\n\n // pageExtensions\n if (config.pageExtensions !== undefined) {\n if (!Array.isArray(config.pageExtensions)) {\n errors.push({\n field: 'pageExtensions',\n message: 'pageExtensions must be an array of strings.',\n value: config.pageExtensions,\n suggestion: 'Example: pageExtensions: [\"tsx\", \"ts\", \"jsx\", \"js\", \"mdx\"]',\n });\n } else {\n for (const ext of config.pageExtensions) {\n if (typeof ext !== 'string') {\n errors.push({\n field: 'pageExtensions',\n message: `pageExtensions contains a non-string value: ${JSON.stringify(ext)}`,\n value: ext,\n });\n break;\n }\n if (ext.startsWith('.')) {\n errors.push({\n field: 'pageExtensions',\n message: `pageExtensions should not include the leading dot: \"${ext}\"`,\n value: ext,\n suggestion: `Use \"${ext.slice(1)}\" instead of \"${ext}\".`,\n });\n break;\n }\n }\n }\n }\n\n // slowRequestMs\n if (config.slowRequestMs !== undefined) {\n if (typeof config.slowRequestMs !== 'number' || config.slowRequestMs < 0) {\n errors.push({\n field: 'slowRequestMs',\n message: `slowRequestMs must be a non-negative number (got ${JSON.stringify(config.slowRequestMs)}).`,\n value: config.slowRequestMs,\n suggestion: 'Use slowRequestMs: 3000 (default) or slowRequestMs: 0 to disable.',\n });\n }\n }\n\n // renderTimeoutMs\n if (config.renderTimeoutMs !== undefined) {\n if (typeof config.renderTimeoutMs !== 'number' || config.renderTimeoutMs < 0) {\n errors.push({\n field: 'renderTimeoutMs',\n message: `renderTimeoutMs must be a non-negative number (got ${JSON.stringify(config.renderTimeoutMs)}).`,\n value: config.renderTimeoutMs,\n suggestion: `Use renderTimeoutMs: ${DEFAULT_RENDER_TIMEOUT_MS} (default). Setting 0 uses a ${RENDER_TIMEOUT_CEILING_MS / 1000}s safety ceiling.`,\n });\n }\n }\n\n // serverTiming\n if (config.serverTiming !== undefined) {\n if (\n config.serverTiming !== 'detailed' &&\n config.serverTiming !== 'total' &&\n config.serverTiming !== false\n ) {\n errors.push({\n field: 'serverTiming',\n message: `Invalid serverTiming value: ${JSON.stringify(config.serverTiming)}.`,\n value: config.serverTiming,\n suggestion: 'Use \"detailed\", \"total\", or false.',\n });\n }\n }\n\n // devBrowserLogs\n if (config.devBrowserLogs !== undefined) {\n const valid = ['error', 'warn', 'info', 'none'];\n if (!valid.includes(config.devBrowserLogs)) {\n errors.push({\n field: 'devBrowserLogs',\n message: `Invalid devBrowserLogs value: ${JSON.stringify(config.devBrowserLogs)}.`,\n value: config.devBrowserLogs,\n suggestion: `Use one of: ${valid.map((v) => `\"${v}\"`).join(', ')}.`,\n });\n }\n }\n\n // sitemap\n if (config.sitemap != null && typeof config.sitemap === 'object') {\n if (config.sitemap.enabled && !config.sitemap.baseUrl) {\n errors.push({\n field: 'sitemap.baseUrl',\n message: 'sitemap.baseUrl is required when sitemap is enabled.',\n suggestion: 'Add sitemap: { enabled: true, baseUrl: \"https://example.com\" }.',\n });\n }\n if (\n config.sitemap.defaultPriority !== undefined &&\n (config.sitemap.defaultPriority < 0 || config.sitemap.defaultPriority > 1)\n ) {\n errors.push({\n field: 'sitemap.defaultPriority',\n message: `sitemap.defaultPriority must be between 0.0 and 1.0 (got ${config.sitemap.defaultPriority}).`,\n value: config.sitemap.defaultPriority,\n });\n }\n }\n\n // budget\n if (config.budget != null && typeof config.budget === 'object') {\n if (\n config.budget.firstLoadJs !== undefined &&\n (typeof config.budget.firstLoadJs !== 'number' || config.budget.firstLoadJs <= 0)\n ) {\n errors.push({\n field: 'budget.firstLoadJs',\n message: `budget.firstLoadJs must be a positive number in bytes (got ${JSON.stringify(config.budget.firstLoadJs)}).`,\n value: config.budget.firstLoadJs,\n suggestion: 'Example: budget: { firstLoadJs: 150_000 } for a ~150 kB limit.',\n });\n }\n }\n\n // cache\n if (config.cache != null && typeof config.cache === 'object') {\n if (\n config.cache.strictKeyDiscipline !== undefined &&\n typeof config.cache.strictKeyDiscipline !== 'boolean'\n ) {\n errors.push({\n field: 'cache.strictKeyDiscipline',\n message: `cache.strictKeyDiscipline must be a boolean (got ${JSON.stringify(config.cache.strictKeyDiscipline)}).`,\n value: config.cache.strictKeyDiscipline,\n suggestion:\n 'Use cache: { strictKeyDiscipline: true } to promote key-discipline warnings to errors.',\n });\n }\n }\n\n const MOVED_TO_CACHE_FILE = new Set(['cacheHandler', 'cdnPurge']);\n\n for (const key of Object.keys(config)) {\n if (!knownKeys.has(key)) {\n errors.push({\n field: key,\n message: `Unknown config option: \"${key}\".`,\n suggestion: MOVED_TO_CACHE_FILE.has(key)\n ? `\"${key}\" has moved to timber.cache.ts. Export cacheHandler as default, cdnPurge as a named export.`\n : `Check for typos. Known options: ${[...knownKeys].sort().join(', ')}.`,\n });\n }\n }\n\n return errors;\n}\n\n// ─── Formatting ─────────────────────────────────────────────────────────────\n\nconst RED = '\\x1b[31m';\nconst BOLD = '\\x1b[1m';\nconst DIM = '\\x1b[2m';\nconst RESET = '\\x1b[0m';\n\n/**\n * Format config errors for terminal output.\n */\nexport function formatConfigErrors(errors: ConfigError[]): string {\n if (errors.length === 0) return '';\n\n const lines: string[] = [];\n lines.push(\n `${RED}${BOLD}[timber]${RESET} ${RED}${errors.length} config error${errors.length !== 1 ? 's' : ''} in timber.config.ts:${RESET}`\n );\n\n for (const err of errors) {\n lines.push('');\n lines.push(` ${RED}✗${RESET} ${BOLD}${err.field}${RESET}: ${err.message}`);\n if (err.suggestion) {\n lines.push(` ${DIM}${err.suggestion}${RESET}`);\n }\n }\n\n return lines.join('\\n');\n}\n\n// ─── Virtual Module Name Mapping ────────────────────────────────────────────\n\nconst VIRTUAL_MODULE_NAMES: Record<string, string> = {\n 'virtual:timber-rsc-entry': 'RSC entry (server component handler)',\n 'virtual:timber-ssr-entry': 'SSR entry (HTML renderer)',\n 'virtual:timber-browser-entry': 'Browser entry (client hydration)',\n 'virtual:timber-config': 'Runtime config (timber.config.ts)',\n 'virtual:timber-route-manifest': 'Route manifest (app/ file tree)',\n 'virtual:timber-instrumentation': 'Instrumentation (instrumentation.ts)',\n 'virtual:timber-cache-handler': 'Cache handler (timber.cache.ts)',\n 'virtual:timber-build-manifest': 'Build manifest (asset mapping)',\n};\n\n/**\n * Add timber-specific context to an error message that references virtual modules.\n *\n * If the error message contains a `virtual:timber-*` ID, appends a\n * human-readable explanation. Does not replace the original message.\n */\nexport function addVirtualModuleContext(errorMessage: string): string {\n for (const [id, name] of Object.entries(VIRTUAL_MODULE_NAMES)) {\n if (errorMessage.includes(id)) {\n return `${errorMessage}\\n\\n [timber] This error references \"${id}\" — timber's ${name}.\\n This is an internal module. The issue is likely in your app code or timber configuration.`;\n }\n }\n return errorMessage;\n}\n\n// ─── Peer Dependency Check ──────────────────────────────────────────────────\n\ninterface PeerDepResult {\n name: string;\n status: 'ok' | 'missing';\n}\n\n/**\n * Required (non-optional) peer dependencies that must be installed.\n */\nconst REQUIRED_PEERS = ['react', 'react-dom', '@vitejs/plugin-react', '@vitejs/plugin-rsc'];\n\n/**\n * Check that required peer dependencies are installed.\n *\n * Uses require.resolve with a try/catch — no fs scanning.\n * Returns only the missing packages.\n */\nexport function checkPeerDependencies(projectRoot: string): PeerDepResult[] {\n const results: PeerDepResult[] = [];\n\n // Use createRequire from the project root to resolve as the user would.\n // `createRequire` MUST be a top-level ESM import — inline `require('node:module')`\n // throws \"Dynamic require not supported\" in ESM and the catch below would\n // mark every peer as missing. Same class of bug as TIM-796.\n const userRequire = createRequire(`${projectRoot}/package.json`);\n\n for (const name of REQUIRED_PEERS) {\n try {\n userRequire.resolve(name);\n results.push({ name, status: 'ok' });\n } catch {\n results.push({ name, status: 'missing' });\n }\n }\n\n return results;\n}\n\n/**\n * Format missing peer dependencies as an actionable warning.\n */\nexport function formatMissingPeers(results: PeerDepResult[]): string {\n const missing = results.filter((r) => r.status === 'missing');\n if (missing.length === 0) return '';\n\n const names = missing.map((r) => r.name);\n const lines: string[] = [];\n lines.push(`${RED}${BOLD}[timber]${RESET} ${RED}Missing required dependencies:${RESET}`);\n lines.push('');\n for (const name of names) {\n lines.push(` ${RED}✗${RESET} ${name}`);\n }\n lines.push('');\n lines.push(` ${DIM}Install with:${RESET}`);\n lines.push(` ${BOLD}pnpm add ${names.join(' ')}${RESET}`);\n\n return lines.join('\\n');\n}\n","/**\n * graph-endpoint — `GET /__timber/graph` on the dev server.\n *\n * The fast path of design/47: when a dev server runs, the env module\n * graphs are already resident and classification is a lookup. Serves\n * the `timber graph` CLI (delegation lands with the fast-path ticket)\n * and the VS Code extension, which additionally needs push updates on\n * HMR — something a CLI cannot provide.\n *\n * Dev-only by construction: registered from the timber-dev-server\n * plugin, which is `apply: 'serve'`; production builds contain none of\n * this. Same JSON contract as the CLI, produced by the same\n * classification module (analyze/live-graph.ts → classify/chains/\n * poison-scan) — never a second implementation.\n *\n * Endpoint hygiene (§10): requests are validated against DNS rebinding\n * consistent with Vite's own dev-endpoint protections — the Host\n * header must be a localhost form or an allowed host (Vite's\n * `server.allowedHosts` semantics, including leading-dot suffix\n * entries), and a present Origin header must pass the same check. A\n * CLI probe sends no Origin and passes; a browser on an attacker's\n * page either fails the Host check (rebinding: Host is the attacker's\n * domain) or the Origin check (direct cross-origin fetch).\n *\n * Push channel: `timber:graph-update` custom HMR events, one per\n * project-source watcher event. The channel just emits — debounce is\n * the consumer's concern (design/47 §\"Dev-server endpoint\").\n *\n * Design docs: 47-module-environment-tooling.md §10, 21-dev-server.md.\n */\n\nimport type { ViteDevServer } from 'vite';\nimport { normalizePath } from 'vite';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport { realpath, stat } from 'node:fs/promises';\nimport { isIP } from 'node:net';\nimport { isAbsolute, join, relative, resolve } from 'node:path';\nimport type { PluginContext } from '../plugin-context.ts';\nimport { buildSourceExtensions, isScannableSourceFile } from '../analyze/scan.ts';\nimport { GRAPH_JSON_VERSION } from '../analyze/graph-cache.ts';\n\nexport const GRAPH_ENDPOINT_PATH = '/__timber/graph';\n\n/** HMR custom event name for graph-affecting file changes. */\nexport const GRAPH_UPDATE_EVENT = 'timber:graph-update';\n\n/**\n * Host-header allowlist check, ported branch-for-branch from Vite's\n * host-validation middleware (`isHostAllowedWithoutCache`) so this\n * endpoint's reachability matches the rest of the dev server:\n *\n * - `allowedHosts: true` allows everything\n * - a MISSING Host header is allowed (Vite parity — HTTP/1.0 probes)\n * - any IP-literal host is allowed (`vite --host` LAN access sends\n * `Host: 192.168.x.x:port`; rejecting it would 403 this endpoint\n * while every other dev route works)\n * - `localhost` and `*.localhost` are always allowed\n * - allowedHosts entries: exact match, or `.suffix` subdomain entries\n */\nexport function isAllowedGraphHost(\n hostHeader: string | undefined,\n allowedHosts: true | string[] | undefined\n): boolean {\n if (allowedHosts === true) return true;\n if (hostHeader === undefined) return true;\n // Strip the port: `localhost:3000`, `[::1]:3000`, bare `[::1]`.\n let host = hostHeader;\n const bracket = host.match(/^\\[([^\\]]+)\\](?::\\d+)?$/);\n if (bracket) {\n // Bracketed form is only valid for IPv6 literals.\n return isIP(bracket[1]) === 6;\n }\n const colon = host.lastIndexOf(':');\n if (colon !== -1) host = host.slice(0, colon);\n host = host.toLowerCase();\n if (isIP(host) !== 0) return true;\n if (host === 'localhost' || host.endsWith('.localhost')) return true;\n for (const entry of allowedHosts ?? []) {\n const allowed = entry.toLowerCase();\n if (allowed.startsWith('.')) {\n if (host === allowed.slice(1) || host.endsWith(allowed)) return true;\n } else if (host === allowed) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Non-http(s) origins admitted without a host check: Vite's own\n * `file:` / `*-extension:` set (its host-validation regex), plus\n * `vscode-webview:` — VS Code webviews fetch with that Origin, and the\n * extension is this endpoint's primary consumer. Vite itself never\n * origin-checks plain HTTP GETs (only WebSocket upgrades), so this\n * layer is strictly additional; the additions keep it from rejecting\n * consumers Vite would serve.\n *\n * NOT admitted: `Origin: null`. Browsers serialize file:// pages'\n * opaque origin as the literal string `null` — but so do sandboxed\n * iframes and data: documents, exactly the untrusted embedders the\n * check exists to stop, and the header offers nothing to tell them\n * apart. file:// pages therefore cannot use this endpoint from a\n * browser; the `file:` scheme entry covers non-browser clients that\n * send a real scheme-form Origin (Vite admits the same set).\n */\nconst TRUSTED_ORIGIN_SCHEMES = /^(?:file|vscode-webview|.+-extension):/i;\n\n/** Origin check: absent is fine (CLI probe); present must pass the host allowlist. */\nexport function isAllowedGraphOrigin(\n originHeader: string | undefined,\n allowedHosts: true | string[] | undefined\n): boolean {\n if (originHeader === undefined) return true;\n if (TRUSTED_ORIGIN_SCHEMES.test(originHeader)) return true;\n let originUrl: URL;\n try {\n originUrl = new URL(originHeader);\n } catch {\n return false;\n }\n // An http(s) origin has a real host — hold it to the host allowlist,\n // NOT to the missing-Host leniency above (an Origin header is never\n // \"missing\" once present; an empty host must fail).\n if (originUrl.host === '') return false;\n // IP-literal leniency applies to the request's HOST header (LAN dev\n // access), never to Origin: a hostile page at http://203.0.113.10\n // sends that IP as its Origin, and admitting it (plus reflected\n // CORS) would let it read the whole module graph. Only loopback IP\n // origins pass; other IPs need an allowedHosts entry. (A developer\n // browsing the app over LAN fetches same-origin, which sends no\n // Origin header on GET — unaffected.)\n const bareHost = originUrl.hostname.replace(/^\\[|\\]$/g, '');\n if (isIP(bareHost) !== 0) {\n if (bareHost === '127.0.0.1' || bareHost === '::1') return true;\n if (allowedHosts === true) return true;\n return (allowedHosts ?? []).some((entry) => entry.toLowerCase() === bareHost);\n }\n return isAllowedGraphHost(originUrl.host, allowedHosts);\n}\n\nfunction sendJson(res: ServerResponse, status: number, body: unknown): void {\n res.statusCode = status;\n res.setHeader('content-type', 'application/json; charset=utf-8');\n res.end(JSON.stringify(body));\n}\n\n/**\n * Echo an ADMITTED origin back as CORS headers. A webview fetch from\n * `vscode-webview://…` passes the origin check but the browser rejects\n * the response body without `Access-Control-Allow-Origin` — the exact\n * transport design/47 specifies for the extension. Only the validated\n * origin is echoed (never `*`), with `Vary: Origin` for any cache in\n * between; unadmitted origins were already 403'd and get nothing.\n */\nfunction reflectCors(res: ServerResponse, origin: string | undefined): void {\n if (!origin) return;\n res.setHeader('Access-Control-Allow-Origin', origin);\n res.setHeader('Vary', 'Origin');\n}\n\n/**\n * Create the Connect middleware handling GET /__timber/graph.\n *\n * Query surface: `?file=<path>` (absolute or project-root-relative) or\n * `?all=1`. Anything else is a 400 — fail loudly, like the CLI.\n */\nexport function createGraphEndpointMiddleware(server: ViteDevServer, ctx: PluginContext) {\n const sourceExtensions = buildSourceExtensions(ctx.config.pageExtensions);\n return async (req: IncomingMessage, res: ServerResponse, next: () => void): Promise<void> => {\n const rawUrl = req.url;\n // Reserve the whole /__timber/ namespace: unknown paths under it\n // must 404 here, not fall through and render the app's 404 page.\n if (!rawUrl || !rawUrl.startsWith('/__timber/')) {\n next();\n return;\n }\n const url = new URL(rawUrl, 'http://placeholder.invalid');\n if (url.pathname !== GRAPH_ENDPOINT_PATH) {\n sendJson(res, 404, { error: `unknown /__timber/ endpoint: ${url.pathname}` });\n return;\n }\n\n if (req.method !== 'GET') {\n res.setHeader('Allow', 'GET');\n sendJson(res, 405, { error: 'GET only' });\n return;\n }\n\n // Host validation is skipped under server.https, as Vite skips it\n // (TLS cert matching already defeats DNS rebinding). The ORIGIN\n // check is not: it guards cross-site reads, which TLS does nothing\n // about — and since admitted origins get reflected CORS below,\n // skipping it under https would let any page read the endpoint.\n const origin = req.headers.origin as string | undefined;\n const allowedHosts = server.config.server.allowedHosts;\n if (!server.config.server.https && !isAllowedGraphHost(req.headers.host, allowedHosts)) {\n sendJson(res, 403, { error: 'host not allowed' });\n return;\n }\n if (!isAllowedGraphOrigin(origin, allowedHosts)) {\n sendJson(res, 403, { error: 'origin not allowed' });\n return;\n }\n // Past validation: reflect the admitted origin so webview/browser\n // consumers can actually READ the response.\n reflectCors(res, origin);\n\n try {\n // Lazy import: the analyze pipeline (oxc parse, chains) loads on\n // first query, not on every dev-server boot.\n const { classifyLiveFile, classifyLiveProject } = await import('../analyze/live-graph.ts');\n // Root must share the graphs' path space: by default Vite\n // resolves through symlinks and nodes carry realpaths, but under\n // resolve.preserveSymlinks the graphs keep the SYMLINK paths and\n // realpathing would break every lookup instead of fixing it —\n // honor the resolved setting (same rule as the crawl).\n const preserveSymlinks = server.config.resolve.preserveSymlinks;\n // Slash-normalized like every other frontend's paths — on\n // Windows a native-separator root beside slash-normalized module\n // paths would break consumers stripping the root prefix.\n const root = normalizePath(preserveSymlinks ? ctx.root : await realpath(ctx.root));\n // appDir must live in the same path space as root and the graph\n // nodes: on a symlinked project ctx.appDir still carries Vite's\n // unresolved prefix — and the appDir itself may be a symlink\n // (src/app -> elsewhere), so realpath the whole thing rather\n // than assuming the relative suffix is symlink-free. Under\n // preserveSymlinks the graphs keep logical paths and ctx.appDir\n // is already in that space.\n const appDir = normalizePath(\n preserveSymlinks\n ? ctx.appDir\n : await realpath(ctx.appDir).catch(() => join(root, relative(ctx.root, ctx.appDir)))\n );\n const liveCtx = {\n rsc: server.environments.rsc.moduleGraph,\n ssr: server.environments.ssr.moduleGraph,\n client: server.environments.client.moduleGraph,\n root,\n appDir,\n pageExtensions: ctx.config.pageExtensions,\n };\n\n const allParam = url.searchParams.get('all');\n if (allParam !== null && allParam !== '1') {\n // Fail loudly: `?all=0`/`?all=false` silently returning the\n // full project would hand API clients the wrong response shape.\n sendJson(res, 400, { error: `unsupported all value: ${allParam} (use ?all=1)` });\n return;\n }\n if (allParam === '1') {\n const report = await classifyLiveProject(liveCtx);\n // `cached: false` always — live answers are never cached, and\n // consumers switching between sources should not lose the\n // field. `stats` stays a static-scan extra (crawl bookkeeping\n // with no live equivalent) — documented in design/47.\n sendJson(res, 200, {\n version: GRAPH_JSON_VERSION,\n source: 'dev-server',\n cached: false,\n ...report,\n });\n return;\n }\n\n const fileParam = url.searchParams.get('file');\n if (!fileParam) {\n sendJson(res, 400, { error: 'missing ?file=<path> (or ?all=1)' });\n return;\n }\n // resolve() in BOTH branches: it collapses `..` segments, and\n // under preserveSymlinks the un-realpathed value is what gets\n // containment-checked — a raw absolute `/root/../outside/x.ts`\n // would pass the prefix check while stat/readFile escape the\n // project.\n // Slash-normalized so every comparison below shares the root's\n // path space (resolve/realpath emit backslashes on Windows).\n const absolute = normalizePath(\n isAbsolute(fileParam) ? resolve(fileParam) : resolve(root, fileParam)\n );\n // Target follows the root's path-space rule. realpath doubles as\n // the existence check in both branches — and the PHYSICAL path is\n // containment-checked even under preserveSymlinks: a symlink\n // inside the project pointing outside it would otherwise pass the\n // lexical prefix check while readFile followed it out of the\n // project. (Symlinks resolving within the project still work.)\n let target: string;\n try {\n const physical = normalizePath(await realpath(absolute));\n const rootPhysical = preserveSymlinks ? normalizePath(await realpath(root)) : root;\n if (\n physical !== rootPhysical &&\n !physical.startsWith(rootPhysical + '/') &&\n !physical.startsWith(rootPhysical + '\\\\')\n ) {\n sendJson(res, 404, { error: `not a project file under ${root}` });\n return;\n }\n if (preserveSymlinks) {\n // Segment-safe symlink check: the disk scan neither follows\n // file symlinks nor descends directory symlinks, so a path\n // reached through EITHER (link.ts, or link/a.ts via\n // link -> src) would 200 here while the ?all sweep omits it.\n // Symlinks ABOVE the root are fine (reaching the project via\n // one is the preserveSymlinks use case) — require only that\n // the path RELATIVE to the root is symlink-free, i.e. the\n // physical path equals the physical root plus that suffix.\n const suffix = absolute.slice(root.length);\n const expected = rootPhysical + suffix;\n if (physical !== expected) {\n sendJson(res, 404, { error: `not a project source file: ${absolute}` });\n return;\n }\n }\n target = normalizePath(preserveSymlinks ? absolute : physical);\n } catch {\n sendJson(res, 404, { error: `file not found: ${absolute}` });\n return;\n }\n if (target !== root && !target.startsWith(root + '/') && !target.startsWith(root + '\\\\')) {\n sendJson(res, 404, { error: `not a project file under ${root}` });\n return;\n }\n // Same admission rules as the CLI's scan (design/47: project\n // source files only) via the shared helper — without it, any\n // real path under the root (package.json, dist/generated.js,\n // types.d.ts, a nested node_modules file, a DIRECTORY named\n // foo.ts) would get a synthetic classification the static scan\n // would refuse, and the frontends would disagree.\n const targetStat = await stat(target).catch(() => null);\n if (\n !targetStat?.isFile() ||\n !isScannableSourceFile(normalizePath(target), normalizePath(root), sourceExtensions)\n ) {\n sendJson(res, 404, { error: `not a project source file: ${target}` });\n return;\n }\n const errors: import('../analyze/live-graph.ts').LiveGraphError[] = [];\n const module = await classifyLiveFile(target, liveCtx, undefined, errors);\n // Explicit field list, mirroring the CLI's single-file output —\n // a spread would leak `file` (redundant with target.resolved) and\n // silently drift the two frontends' key sets. `stats` is a\n // static-scan extra and deliberately absent here.\n sendJson(res, 200, {\n version: GRAPH_JSON_VERSION,\n source: 'dev-server',\n cached: false,\n target: { input: fileParam, resolved: target },\n kind: module.kind,\n pending: module.pending,\n subtreeUnknown: module.subtreeUnknown,\n envs: module.envs,\n boundary: module.boundary,\n chains: module.chains,\n poisonings: module.poisonings,\n errors,\n });\n } catch (error) {\n sendJson(res, 500, {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n };\n}\n\n/** What one hotUpdate invocation tells the emitter. */\nexport interface GraphHotUpdate {\n file: string;\n /** Vite's own hotUpdate vocabulary. */\n type: 'create' | 'update' | 'delete';\n /** Environment the hook ran for (rsc/ssr/client). */\n environment: string;\n}\n\n/** The push channel's two notification points for one edit. */\nexport interface GraphUpdateChannel {\n /** Feed from the plugin's hotUpdate hook (post graph invalidation). */\n onHotUpdate(update: GraphHotUpdate): void;\n /**\n * Feed from the plugin's transform hook (module id, may carry a\n * query suffix). Emits phase \"transformed\" for files with a pending\n * invalidation, once their import edges are actually rebuilt.\n */\n onTransform(id: string, environment: string): void;\n}\n\n/**\n * Accessor the channel uses to decide when a re-transforming module's\n * graph data is current: Vite stores the node's `transformResult` only\n * at the END of the transform request — after `vite:import-analysis`\n * has rebuilt the importer edges — so a non-null result is the\n * edges-are-current signal.\n */\nexport type IsNodeTransformed = (file: string, environment: string) => boolean;\n\n/**\n * Build the push channel: `timber:graph-update` custom HMR events per\n * project-source change. Consumers (the VS Code extension) re-query\n * and debounce on their side; the channel just emits. Non-source noise\n * (build output, dotdirs) is filtered by the same extension set the\n * scanners use.\n *\n * Two phases per edit, because one is not enough (codex review, PR\n * #1057): the `invalidated` event fires from the plugin's `hotUpdate`\n * hook — after Vite clears the module's transform result, but its\n * importer EDGES are only rebuilt when the module is next transformed.\n * The `transformed` event fires for exactly the files with a pending\n * invalidation, and only once the node's `transformResult` is set\n * again — the plugin transform hook itself runs BEFORE Vite's trailing\n * `vite:import-analysis` (which rebuilds the edges), so the hook only\n * marks the file \"settling\" and a short bounded timer confirms\n * completion via `isTransformed` before emitting. (A module nothing\n * re-requests never re-transforms — its `transformed` event waits for\n * the next render, which is also when the graph has new information.)\n */\nexport function makeGraphUpdateChannel(\n ctx: Pick<PluginContext, 'root' | 'config'>,\n send: (payload: { type: 'custom'; event: string; data: unknown }) => void,\n isTransformed?: IsNodeTransformed,\n // Maps a watcher/hook path into the module graphs' path space (the\n // dev-server passes a preserveSymlinks-aware realpath). Watcher\n // events can carry the symlink path while transform ids carry the\n // physical one — unkeyed by a common canonical form, the pending\n // entry would never match its own transform.\n canonicalize: (file: string) => string = (file) => file\n): GraphUpdateChannel {\n /** Files invalidated but not yet re-transformed, per environment. */\n const pending = new Set<string>();\n const key = (file: string, environment: string) => `${environment}\\0${file}`;\n // The root goes through the same canonicalization as the files it\n // contains — containment checks must compare in one path space.\n const root = normalizePath(canonicalize(ctx.root));\n const extensions = buildSourceExtensions(ctx.config.pageExtensions);\n\n const emitTransformed = (file: string, environment: string, structural?: boolean) => {\n send({\n type: 'custom',\n event: GRAPH_UPDATE_EVENT,\n data: structural\n ? { file, event: 'update', environment, phase: 'transformed', structural: true }\n : { file, event: 'update', environment, phase: 'transformed' },\n });\n };\n\n /**\n * Confirm the transform request finished (transformResult set again)\n * before emitting phase `transformed`. If confirmation never arrives\n * within the bounded schedule below, the pending key is RE-ARMED\n * rather than emitted — a `transformed` event over a still-stale\n * graph would be a false all-clear with no corrective event.\n */\n /**\n * Delays between confirmation re-checks: dense while a normal\n * transform request finishes, then exponential backoff for slow\n * import-analysis passes (large graphs, cold dep optimizer) — a\n * completed slow transform is CACHED, so no later transform hook may\n * ever fire for it and the tail checks are the only chance to emit.\n * Total window ≈ 16 s, then give up (bounded — the async-safety\n * rules ban open-ended polling) and re-arm the pending key.\n */\n const CONFIRM_DELAYS_MS = [\n ...Array.from({ length: 25 }, () => 20),\n 500,\n 1_000,\n 2_000,\n 4_000,\n 8_000,\n ];\n\n const confirmAndEmit = (file: string, environment: string, attempt = 0, structural = false) => {\n if (!isTransformed || isTransformed(file, environment)) {\n emitTransformed(file, environment, structural);\n return;\n }\n if (attempt >= CONFIRM_DELAYS_MS.length) {\n // Structural (virtual-id) updates have no pending-key cycle to\n // re-arm; project files wait for their next transform.\n if (!structural) pending.add(key(file, environment));\n return;\n }\n setTimeout(\n () => confirmAndEmit(file, environment, attempt + 1, structural),\n CONFIRM_DELAYS_MS[attempt]\n );\n };\n\n return {\n onHotUpdate(update) {\n // The scanner's FULL admission rules, not just the extension —\n // otherwise the channel pushes updates for files the endpoint\n // itself 404s (node_modules, dist, .d.ts, dot-dirs) and editor\n // consumers refresh into dead ends. Deletes canonicalize too:\n // the dev-server's canonicalizer falls back to resolving the\n // parent directory when the file itself is gone.\n const file = normalizePath(canonicalize(update.file));\n if (!isScannableSourceFile(file, root, extensions)) return;\n if (update.type === 'delete') {\n // Deleted files never re-transform; no second phase to track.\n pending.delete(key(file, update.environment));\n } else {\n pending.add(key(file, update.environment));\n }\n send({\n type: 'custom',\n event: GRAPH_UPDATE_EVENT,\n data: {\n file,\n event: update.type,\n environment: update.environment,\n phase: 'invalidated',\n },\n });\n },\n onTransform(id, environment) {\n const file = normalizePath(id.split('?')[0]);\n // Structural change: the route manifest re-transforms when routes\n // are added/removed (its dynamic-import edges are what connect\n // new route files to the graph). The new FILE itself may not\n // transform until visited, so per-file tracking cannot see this —\n // emit a structural notification, through the same confirmation\n // gate (the manifest's own edges are also rebuilt only by the\n // trailing import-analysis; the accessor resolves virtual ids).\n if (file.includes('virtual:timber-route-manifest')) {\n confirmAndEmit(file, environment, 25, true);\n return;\n }\n const pendingKey = key(file, environment);\n if (!pending.has(pendingKey)) return;\n pending.delete(pendingKey);\n confirmAndEmit(file, environment);\n },\n };\n}\n","// Response compression for self-hosted deployments (dev server, Nitro preview).\n//\n// Uses CompressionStream (Web Platform API) for gzip. Brotli is intentionally\n// left to CDNs and reverse proxies — at streaming-friendly quality levels its\n// ratio advantage is marginal, and node:zlib's brotli transform buffers output\n// internally, breaking streaming. Cloudflare Workers auto-compress at the edge —\n// this module is only used on Node.js/Bun.\n//\n// See design/25-production-deployments.md.\n//\n// This module is also emitted standalone as `_compress.mjs` by\n// `adapters/compress-module.ts`, which strips types but does not bundle. Its\n// imports must therefore be interpolated there — a new import that is not\n// fails the build with an explanatory error rather than resolving to nothing\n// on the deployed server.\n\nimport { RSC_CONTENT_TYPE } from '../shared/rsc-media-type.ts';\n\n// ─── Constants ────────────────────────────────────────────────────────────\n\n/**\n * MIME types that benefit from compression.\n * text/* is handled via prefix matching; these are the specific\n * application/* and image/* types that are compressible.\n */\nexport const COMPRESSIBLE_TYPES = new Set([\n 'text/html',\n 'text/css',\n 'text/plain',\n 'text/xml',\n 'text/javascript',\n RSC_CONTENT_TYPE,\n 'application/json',\n 'application/javascript',\n 'application/xml',\n 'application/xhtml+xml',\n 'application/rss+xml',\n 'application/atom+xml',\n 'image/svg+xml',\n]);\n\n/**\n * Status codes that should never be compressed (no body or special semantics).\n */\nconst NO_COMPRESS_STATUSES = new Set([204, 304]);\n\n// ─── Encoding Negotiation ─────────────────────────────────────────────────\n\n/**\n * Parse Accept-Encoding and return the best supported encoding.\n * Returns 'gzip' if the client accepts it, null otherwise.\n *\n * Brotli (br) is intentionally not handled at the application level.\n * At the streaming-friendly quality levels (0–4), brotli's compression\n * ratio advantage over gzip is marginal, and node:zlib's brotli transform\n * buffers output internally — turning smooth streaming responses into\n * large infrequent bursts. Brotli's real wins come from offline/static\n * compression at higher quality levels (5–11), which CDNs and reverse\n * proxies (Cloudflare, nginx, Caddy) apply on cached responses.\n *\n * See design/25-production-deployments.md.\n */\nexport function negotiateEncoding(acceptEncoding: string): 'gzip' | null {\n if (!acceptEncoding) return null;\n\n // Parse tokens with quality values from the Accept-Encoding header.\n // Per RFC 9110 §12.5.3, q=0 means \"not acceptable\" — the client explicitly\n // rejects that encoding. Tokens without a q-value default to q=1.\n // e.g. \"gzip;q=1.0, br;q=0\" → gzip is acceptable, br is rejected.\n const parts = acceptEncoding.split(',');\n for (const part of parts) {\n const [token, ...params] = part.split(';');\n const name = token.trim().toLowerCase();\n if (name !== 'gzip') continue;\n\n // Check for q=0 (explicitly disabled)\n let qValue = 1; // default quality is 1\n for (const param of params) {\n const trimmed = param.trim().toLowerCase();\n if (trimmed.startsWith('q=')) {\n qValue = parseFloat(trimmed.slice(2));\n if (Number.isNaN(qValue)) qValue = 1;\n break;\n }\n }\n\n if (qValue > 0) return 'gzip';\n }\n\n return null;\n}\n\n// ─── Compressibility Check ────────────────────────────────────────────────\n\n/**\n * Determine if a response should be compressed.\n *\n * Returns false for:\n * - Responses without a body (204, 304, null body)\n * - Already-encoded responses (Content-Encoding set)\n * - Non-compressible content types (images, binary)\n * - SSE streams (text/event-stream — must not be buffered)\n */\nexport function shouldCompress(response: Response): boolean {\n // No body to compress\n if (!response.body) return false;\n if (NO_COMPRESS_STATUSES.has(response.status)) return false;\n\n // Already compressed\n if (response.headers.has('Content-Encoding')) return false;\n\n // Check content type\n const contentType = response.headers.get('Content-Type');\n if (!contentType) return false;\n\n // Extract the MIME type (strip charset and other parameters)\n const mimeType = contentType.split(';')[0].trim().toLowerCase();\n\n // SSE must not be compressed — it relies on chunk-by-chunk delivery\n if (mimeType === 'text/event-stream') return false;\n\n return COMPRESSIBLE_TYPES.has(mimeType);\n}\n\n// ─── Compression ──────────────────────────────────────────────────────────\n\n/**\n * Compress a Web Response if the client supports it and the content is compressible.\n *\n * Returns the original response unchanged if compression is not applicable.\n * Returns a new Response with the compressed body, Content-Encoding, and Vary headers.\n *\n * The body is piped through a compression stream — no buffering of the full response.\n * This preserves streaming behavior for HTML shell + deferred Suspense chunks.\n */\nexport function compressResponse(request: Request, response: Response): Response {\n // Check if response is compressible\n if (!shouldCompress(response)) return response;\n\n // Negotiate encoding with the client\n const acceptEncoding = request.headers.get('Accept-Encoding') ?? '';\n const encoding = negotiateEncoding(acceptEncoding);\n if (!encoding) return response;\n\n // Compress the body stream with gzip via the Web Platform CompressionStream API.\n const compressedBody = compressWithGzip(response.body!);\n\n // Build new headers: copy originals, add compression headers, remove Content-Length\n // (compressed size is unknown until streaming completes).\n const headers = new Headers(response.headers);\n headers.set('Content-Encoding', encoding);\n headers.delete('Content-Length');\n\n // Append to Vary header (preserve existing Vary values)\n const existingVary = headers.get('Vary');\n if (existingVary) {\n if (!existingVary.toLowerCase().includes('accept-encoding')) {\n headers.set('Vary', `${existingVary}, Accept-Encoding`);\n }\n } else {\n headers.set('Vary', 'Accept-Encoding');\n }\n\n return new Response(compressedBody, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n}\n\n// ─── Gzip (node:zlib with Z_SYNC_FLUSH) ──────────────────────────────────\n//\n// Uses node:zlib's createGzip with Z_SYNC_FLUSH so each chunk is flushed\n// to the output immediately. The Web Platform CompressionStream API buffers\n// internally and does NOT flush per-chunk — this kills streaming because\n// the browser doesn't receive the HTML shell until the gzip stream closes\n// (i.e. after all Suspense boundaries resolve).\n//\n// Z_SYNC_FLUSH adds ~2–5% size overhead vs Z_NO_FLUSH but preserves\n// correct streaming behavior: the shell renders instantly, Suspense\n// fallbacks are visible immediately, and streamed content appears\n// progressively.\n\nimport { createGzip, constants } from 'node:zlib';\nimport { Readable, pipeline } from 'node:stream';\n\n/**\n * Compress a ReadableStream with gzip, flushing each chunk immediately.\n *\n * Uses node:zlib's createGzip with Z_SYNC_FLUSH to ensure each HTML chunk\n * (shell, Suspense resolution, RSC payload) is delivered to the browser\n * as soon as it's available — preserving streaming semantics.\n */\nfunction compressWithGzip(body: ReadableStream<Uint8Array>): ReadableStream<Uint8Array> {\n const gzip = createGzip({ flush: constants.Z_SYNC_FLUSH });\n const nodeInput = Readable.fromWeb(body as import('stream/web').ReadableStream);\n\n // pipeline() instead of bare pipe(): pipe() does not forward source errors,\n // so an erroring body (streaming route handler, dropped upstream fetch)\n // would emit an unhandled 'error' event and crash the process. pipeline()\n // destroys gzip on source error — the returned web stream rejects — and\n // tears down nodeInput when the consumer cancels the web stream, which\n // cancels the source body via fromWeb. The error reaches the consumer\n // through the web stream; the callback only marks it handled at the Node\n // layer.\n pipeline(nodeInput, gzip, () => {});\n\n return Readable.toWeb(gzip) as ReadableStream<Uint8Array>;\n}\n","/**\n * timber-dev-server — Vite sub-plugin for dev server request handling.\n *\n * Registers a configureServer middleware that intercepts requests and\n * routes them through the timber pipeline:\n * proxy.ts → canonicalize → route match → middleware → access → render → flush\n *\n * The RSC entry module is loaded via Vite's ssrLoadModule, which uses\n * Vite's dev module graph instead of built bundles. The full pipeline\n * (including proxy.ts) runs on every request.\n *\n * Design docs: 18-build-system.md §\"Dev Server\", 02-rendering-pipeline.md\n */\n\nimport type { Plugin, ViteDevServer, DevEnvironment } from 'vite';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { TLSSocket } from 'node:tls';\nimport { existsSync, realpathSync, statSync } from 'node:fs';\nimport { basename, dirname, join } from 'node:path';\nimport { sendNodeResponse } from 'srvx/node';\nimport type { PluginContext } from '../plugin-context.ts';\nimport {\n sendErrorToOverlay,\n classifyErrorPhase,\n parseFirstAppFrame,\n type ErrorPhase,\n} from '../dev-tools/overlay.ts';\nimport { generateDevErrorPage } from '../dev-tools/error-page.ts';\nimport {\n extractHmrOptions,\n type DevErrorHmrOptions,\n type DevHooks,\n} from '../dev-tools/dev-page-shell.ts';\nimport { addVirtualModuleContext } from '../config-validation.ts';\nimport { registerDevDiscovery } from './dev-discovery.ts';\nimport {\n createGraphEndpointMiddleware,\n makeGraphUpdateChannel,\n type GraphUpdateChannel,\n} from './graph-endpoint.ts';\nimport { compressResponse } from '../server/compress.ts';\nimport { isMetadataRouteServePath } from '../server/metadata-routes.ts';\nimport { canonicalize } from '../server/canonicalize.ts';\nimport { matchUrlParts } from '../server/tree-match.ts';\nimport type { RouteTree } from '../routing/types.ts';\n\n// ─── Constants ────────────────────────────────────────────────────────────\n\nconst RSC_ENTRY_ID = 'virtual:timber-rsc-entry';\n\n/**\n * Exit code used to signal the CLI supervisor that the dev server needs\n * a full process restart (e.g. timber.config.ts changed). The supervisor\n * in cli.ts respawns the worker on this code. 75 = EX_TEMPFAIL (sysexits).\n * See TIM-1444.\n */\nexport const RESTART_EXIT_CODE = 75;\n\n/**\n * Config file names that trigger a full dev server restart when changed.\n * See 21-dev-server.md §HMR Wiring — config is loaded once at startup.\n */\nconst CONFIG_FILE_NAMES = ['timber.config.ts', 'timber.config.js', 'timber.config.mjs'];\n\n/**\n * URL prefixes that are Vite-internal and should never be intercepted.\n * These are passed through to Vite's built-in middleware.\n */\nconst VITE_INTERNAL_PREFIXES = [\n '/@', // /@vite/client, /@fs/, /@id/\n '/__vite', // /__vite_hmr, /__vite_ping\n '/node_modules/',\n];\n\n/**\n * File extensions that indicate static asset requests.\n * These are passed through to Vite's static file serving.\n */\nconst ASSET_EXTENSIONS =\n /\\.(?:js|ts|tsx|jsx|css|map|json|svg|png|jpg|jpeg|gif|webp|avif|ico|woff|woff2|ttf|eot|mp4|webm|ogg|mp3|wav)(?:\\?.*)?$/;\n\n// ─── Plugin ───────────────────────────────────────────────────────────────\n\n/**\n * Create the timber-dev-server Vite plugin.\n *\n * Hook: configureServer (returns post-hook to register after Vite's middleware)\n */\nexport function timberDevServer(ctx: PluginContext): Plugin {\n // Set in configureServer, consumed by hotUpdate/transform — the\n // graph push channel emits through the server's HMR transport.\n let graphChannel: GraphUpdateChannel | null = null;\n\n return {\n name: 'timber-dev-server',\n\n // Only active in dev mode (command === 'serve'), not during build.\n // See 21-dev-server.md §Plugin Registration.\n apply: 'serve',\n\n /**\n * Push channel for the graph endpoint, phase 1: `invalidated`, one\n * per (file, environment). Emitted from hotUpdate — which runs\n * AFTER Vite invalidates the environment module graphs — never\n * from a raw watcher listener, which would race consumers into\n * querying pre-invalidation memberships (design/47 §\"Dev-server\n * endpoint\").\n */\n hotUpdate({ file, type }) {\n graphChannel?.onHotUpdate({ file, type, environment: this.environment.name });\n },\n\n /**\n * Push channel phase 2: `transformed`, when a pending-invalidated\n * module's import edges are actually rebuilt. A Set lookup for\n * everything without a pending invalidation — free on the hot\n * path. Returning nothing leaves the code untouched.\n */\n transform(_code, id) {\n graphChannel?.onTransform(id, this.environment.name);\n },\n\n /**\n * Register the dev server middleware and config file watcher.\n *\n * Registers as a pre-hook (no return value) so our middleware runs\n * before Vite's built-in SPA fallback / historyApiFallback. This\n * ensures we see the original URL (e.g. /blog) rather than a\n * rewritten /index.html. Vite-internal and asset requests are\n * filtered out explicitly and passed through to Vite.\n */\n async configureServer(server: ViteDevServer) {\n // Watch config files for changes. When running under the CLI\n // supervisor (__TIMBER_DEV_WORKER), exit the process so the\n // supervisor respawns with a fresh require cache. When running\n // via `vite dev` directly (no supervisor), fall back to Vite's\n // in-process server.restart() — it serves stale config, but at\n // least the server stays alive. See TIM-1444.\n const configPaths = CONFIG_FILE_NAMES.map((name) => join(ctx.root, name));\n server.watcher.add(configPaths);\n const configPathSet = new Set(configPaths);\n const hasSupervisor = Boolean(process.env.__TIMBER_DEV_WORKER);\n const onConfigChange = (filePath: string) => {\n if (!configPathSet.has(filePath)) return;\n if (hasSupervisor) {\n console.log(`\\n[timber] ${basename(filePath)} changed — restarting dev server...\\n`);\n process.exit(RESTART_EXIT_CODE);\n } else {\n console.log(\n `\\n[timber] ${basename(filePath)} changed — restarting dev server...` +\n `\\n[timber] Run \\`timber dev\\` instead of \\`vite dev\\` for guaranteed fresh config on restart.\\n`\n );\n server.restart();\n }\n };\n server.watcher.on('change', onConfigChange);\n server.watcher.on('add', onConfigChange);\n server.watcher.on('unlink', onConfigChange);\n\n // Listen for client-side errors forwarded from the browser.\n // The browser entry sends 'timber:client-error' events via HMR\n // for uncaught errors and unhandled rejections. We echo them back\n // as Vite's '{ type: \"error\" }' payload to trigger the overlay.\n listenForClientErrors(server, ctx.root);\n\n // GET /__timber/graph — module-environment classification from the\n // live env graphs, plus the timber:graph-update HMR push channel.\n // Registered before the timber middleware so /__timber/* requests\n // never reach the app pipeline. Dev-only by construction (this\n // plugin is apply: 'serve'). See design/47 §\"Dev-server endpoint\".\n server.middlewares.use(createGraphEndpointMiddleware(server, ctx));\n graphChannel = makeGraphUpdateChannel(\n ctx,\n (payload) => server.hot.send(payload),\n // \"Edges rebuilt\" signal: Vite sets a node's transformResult at\n // the END of the transform request, after vite:import-analysis\n // has updated the importer edges. Virtual ids (the route\n // manifest's structural updates) have no file index — resolve\n // them by id.\n (file, environment) => {\n const graph = server.environments[environment]?.moduleGraph;\n if (!graph) return false;\n const nodes = graph.getModulesByFile(file);\n if (nodes) {\n for (const node of nodes) {\n if (node.transformResult != null) return true;\n }\n return false;\n }\n return graph.getModuleById(file)?.transformResult != null;\n },\n // Canonicalize watcher/hook paths into the module graphs' path\n // space: under the default preserveSymlinks:false the graphs\n // carry realpaths while watcher events can carry symlink paths\n // — unkeyed by one canonical form, a pending entry would never\n // match its own transform. Deleted files realpath their parent\n // directory instead; virtual ids pass through untouched.\n (file) => {\n // Optional-chained: mock servers in unit tests carry a bare\n // config object without resolve options.\n if (server.config?.resolve?.preserveSymlinks || file.includes('virtual:')) return file;\n try {\n return realpathSync(file);\n } catch {\n try {\n return join(realpathSync(dirname(file)), basename(file));\n } catch {\n return file;\n }\n }\n }\n );\n\n // Pre-hook — registers middleware before Vite's internals\n server.middlewares.use(createTimberMiddleware(server, ctx));\n\n // Write this server's .timber/dev/<pid>-<port>-<token>.json record once\n // the server is listening so the graph CLI / VS Code extension can\n // discover it. See design/47-module-environment-tooling.md §5.\n registerDevDiscovery(server.httpServer, ctx.root, server.config);\n\n // Close the holding server and await the port release so Vite's\n // port probe and bind see a free port. configureServer is awaited\n // by Vite during createServer(), before startServer() runs\n // isPortAvailable() and httpServer.listen().\n // closeAllConnections() (in HoldingServer.close) kills keep-alive\n // sockets from the meta-refresh page so close() resolves promptly.\n // See design/21-dev-server.md §\"Startup Holding Server\", TIM-1419.\n if (ctx.holdingServer) {\n await ctx.holdingServer.close().catch(() => {});\n ctx.holdingServer = null;\n }\n\n // Log startup timing summary. configureServer runs on all plugins\n // before the server listens, so this captures the full cold start.\n ctx.timer.end('dev-server-setup');\n const summary = ctx.timer.formatSummary();\n if (summary) {\n console.log(summary);\n }\n },\n };\n}\n\n// ─── Middleware ────────────────────────────────────────────────────────────\n\n/**\n * Create the Connect middleware that routes requests through the timber pipeline.\n *\n * For route requests (HTML pages, API endpoints), the middleware:\n * 1. Loads the RSC entry via ssrLoadModule\n * 2. Converts the Node request to a Web Request\n * 3. Passes it through the RSC handler (which runs the full pipeline)\n * 4. Converts the Web Response back to a Node response\n *\n * For non-route requests (assets, Vite internals, HMR), the middleware\n * calls next() to let Vite handle them.\n */\nfunction createTimberMiddleware(server: ViteDevServer, ctx: PluginContext) {\n const projectRoot = ctx.root;\n // Extract HMR connection options once so the error page can construct\n // a correct WebSocket URL (TIM-789).\n const hmrOptions = extractHmrOptions(server.config);\n\n // Debug channel registry: buffers RSC debug rows per request and replays\n return async (req: IncomingMessage, res: ServerResponse, next: () => void): Promise<void> => {\n const url = req.url;\n if (!url) {\n next();\n return;\n }\n\n // Pass through Vite-internal requests\n if (isViteInternal(url)) {\n next();\n return;\n }\n\n // Files that exist in public/ are always served by Vite's static\n // middleware, regardless of extension. Without this, extensions like\n // .ico, .txt, .xml, .html either get caught by the metadata route\n // check or aren't in ASSET_EXTENSIONS, so they fall through to the\n // app router where a dynamic segment like [artistSlug] 404s them.\n if (isPublicFile(server, url)) {\n next();\n return;\n }\n\n // Pass through static asset requests — but not metadata routes like\n // /favicon.ico that match asset extensions but are handled by timber,\n // and not registered routes like app/feed.json/route.ts whose URL ends\n // in an asset extension. Production serves these through the RSC\n // handler, so letting them fall through to Vite would 404 in dev only\n // (TIM-1038).\n if (\n isAssetRequest(url) &&\n !isMetadataRouteServePath(url) &&\n !isRouteOwnedAsset(ctx.routeTree, server, url)\n ) {\n next();\n return;\n }\n\n // Step 1: Load the RSC entry module from the RSC environment.\n // The RSC entry runs in the 'rsc' Vite environment (separate module\n // graph with react-server conditions). In dev mode, this uses the\n // environment's module runner for HMR-aware loading.\n let handler: (req: Request) => Promise<Response>;\n try {\n const rscEnv = server.environments.rsc as DevEnvironment & {\n runner?: { import: (id: string) => Promise<any> };\n };\n // Duck-type check instead of isRunnableDevEnvironment() — the vite\n // import-based check fails across pnpm link boundaries where the\n // linked package resolves a different vite module instance.\n if (!rscEnv?.runner?.import) {\n throw new Error('[timber] RSC environment is not runnable');\n }\n const rscModule = await rscEnv.runner.import(RSC_ENTRY_ID);\n handler = rscModule.default as (req: Request) => Promise<Response>;\n\n // Wire pipeline errors into the browser error overlay, and plumb the\n // resolved HMR options into the RSC environment. The RSC entry can't\n // read the Vite server config (separate module graph), but its dev 404\n // page and fallback error page need the HMR endpoint (including Vite\n // 8's required WS token) for auto-reload (TIM-1067).\n const setDevHooks = rscModule.setDevHooks as ((hooks: DevHooks) => void) | undefined;\n setDevHooks?.({\n onPipelineError: (error) => {\n sendErrorToOverlay(server, error, classifyErrorPhase(error, projectRoot), projectRoot);\n },\n hmrOptions,\n });\n } catch (error) {\n // Module transform error — syntax error, missing import, etc.\n // Vite may already show its own overlay for these, but we still\n // log to stderr with frame dimming for the terminal.\n if (error instanceof Error) {\n // Add context for virtual:timber-* module errors\n addTimberContext(error);\n sendErrorToOverlay(server, error, 'module-transform', projectRoot);\n }\n respond500(res, error, 'module-transform', projectRoot, hmrOptions);\n return;\n }\n\n if (typeof handler !== 'function') {\n console.error('[timber] RSC entry module does not export a default function');\n next();\n return;\n }\n\n // Step 2: Run the pipeline.\n try {\n // Convert Node IncomingMessage → Web Request\n const webRequest = toWebRequest(req);\n\n // Check for a platform-specific request wrapper (e.g., Cloudflare\n // dev bindings). The cloudflareDevBindings() plugin stores a wrapper\n // function on the server instance using a well-known Symbol. The\n // wrapper runs the handler inside an ALS context so platform APIs\n // like getCloudflareBindings() work during dev.\n // See design/35-cloudflare-primitives.md §\"Dev Experience\".\n const wrapper = (server as any)[Symbol.for('timber:dev-request-wrapper')] as\n | (<T>(fn: () => T) => T)\n | undefined;\n\n // Run the full pipeline (wrapped if a platform dev plugin is active)\n const webResponse = wrapper\n ? await wrapper(() => handler(webRequest))\n : await handler(webRequest);\n\n // Compress the response if the client supports it.\n // In dev mode, compression is always enabled for parity with production.\n // See design/25-production-deployments.md.\n const finalResponse = compressResponse(webRequest, webResponse);\n\n // Flush headers eagerly so streaming responses (SSE, long-polling)\n // reach the client before the first body chunk. Node.js buffers\n // headers until write()/end() otherwise, which stalls EventSource\n // clients that wait for the response to open. srvx's writeHead\n // skips if headersSent is already true, so pre-flushing is safe.\n res.writeHead(finalResponse.status, [...finalResponse.headers].flat());\n res.flushHeaders();\n\n // Stream the body with backpressure and disconnect handling.\n // srvx skips its own writeHead since headers are already sent.\n await sendNodeResponse(res, finalResponse);\n } catch (error) {\n // An error the pipeline did not turn into a response, or one sending\n // the response threw — classify the phase, send to overlay, respond\n // 500. The pipeline answers render errors itself (an SsrStreamError\n // never gets here), so this sees the error as thrown. The dev server\n // remains running for recovery on file fix + HMR.\n if (error instanceof Error) {\n const phase = classifyErrorPhase(error, projectRoot);\n sendErrorToOverlay(server, error, phase, projectRoot);\n respond500(res, error, phase, projectRoot, hmrOptions);\n } else {\n process.stderr.write(`\\x1b[31m[timber] Dev server error:\\x1b[0m ${String(error)}\\n`);\n respond500(res, error, 'render', projectRoot, hmrOptions);\n }\n }\n };\n}\n\n/**\n * Send a 500 response without crashing the dev server.\n *\n * In dev mode, renders a styled HTML error page with source context,\n * classified stack trace, and auto-reload on HMR. Falls back to\n * text/plain if the HTML generator fails (must never crash).\n */\nfunction respond500(\n res: ServerResponse,\n error: unknown,\n phase: ErrorPhase,\n projectRoot: string,\n hmrOptions?: DevErrorHmrOptions\n): void {\n if (res.headersSent) return;\n\n // Try to render the rich HTML error page\n if (error instanceof Error) {\n try {\n const html = generateDevErrorPage(error, phase, projectRoot, hmrOptions);\n res.statusCode = 500;\n res.setHeader('content-type', 'text/html; charset=utf-8');\n res.end(html);\n return;\n } catch (e) {\n console.debug(\n '[timber] dev error page generation failed, falling back to text/plain:',\n e instanceof Error ? e.message : e\n );\n }\n }\n\n // Fallback: plain text (same as before)\n res.statusCode = 500;\n res.setHeader('content-type', 'text/plain');\n res.end(\n `[timber] Internal server error\\n\\n${error instanceof Error ? (error.stack ?? error.message) : String(error)}`\n );\n}\n\n/**\n * Add timber-specific context to an error's message if it references\n * internal virtual modules (virtual:timber-*). Mutates the error in place.\n */\nfunction addTimberContext(error: Error): void {\n const enriched = addVirtualModuleContext(error.message);\n if (enriched !== error.message) {\n error.message = enriched;\n }\n}\n\n// ─── Request/Response Conversion ──────────────────────────────────────────\n\n/**\n * Convert a Node IncomingMessage to a Web Request.\n *\n * Constructs the full URL from the Host header and request URL,\n * and forwards the method, headers, and body.\n *\n * The scheme comes from the socket's TLS state — with `server.https` the\n * browser sends `Origin: https://host`, and a hardcoded `http://` would\n * fail CSRF origin comparison on every POST. X-Forwarded-Proto is NOT\n * consulted: dev connections are direct, so the header would be\n * attacker-controlled. (TIM-1067, B32)\n *\n * Exported for tests.\n */\nexport function toWebRequest(nodeReq: IncomingMessage): Request {\n const protocol = (nodeReq.socket as TLSSocket | undefined)?.encrypted ? 'https' : 'http';\n // Under HTTP/2 there is no Host header — the authority lives in the\n // `:authority` pseudo-header.\n const host =\n nodeReq.headers.host ?? (nodeReq.headers[':authority'] as string | undefined) ?? 'localhost';\n const url = `${protocol}://${host}${nodeReq.url}`;\n\n const headers = new Headers();\n for (const [key, value] of Object.entries(nodeReq.headers)) {\n if (value === undefined) continue;\n // HTTP/2 pseudo-headers (:method, :path, :authority, :scheme) are not\n // valid Headers names — Headers.set() throws on them. They're already\n // reflected in the request URL/method above.\n if (key.startsWith(':')) continue;\n if (Array.isArray(value)) {\n for (const v of value) {\n headers.append(key, v);\n }\n } else {\n headers.set(key, value);\n }\n }\n // HTTP/2 requests carry the authority in `:authority`, not Host — but\n // downstream consumers (CSRF origin validation) read the Host header.\n if (!headers.has('host')) {\n headers.set('host', host);\n }\n\n const method = nodeReq.method ?? 'GET';\n const hasBody = method !== 'GET' && method !== 'HEAD';\n\n return new Request(url, {\n method,\n headers,\n body: hasBody ? nodeReadableToWebStream(nodeReq) : undefined,\n // @ts-expect-error — duplex is required for streaming request bodies\n duplex: hasBody ? 'half' : undefined,\n });\n}\n\n/**\n * Convert a Node Readable stream to a Web ReadableStream.\n */\nfunction nodeReadableToWebStream(nodeStream: IncomingMessage): ReadableStream<Uint8Array> {\n return new ReadableStream({\n start(controller) {\n nodeStream.on('data', (chunk: Buffer) => {\n controller.enqueue(new Uint8Array(chunk));\n });\n nodeStream.on('end', () => {\n controller.close();\n });\n nodeStream.on('error', (err) => {\n controller.error(err);\n });\n },\n });\n}\n\n// ─── URL Classification ──────────────────────────────────────────────────\n\n/**\n * Check if a URL is a Vite-internal request that should be passed through.\n */\nfunction isViteInternal(url: string): boolean {\n return VITE_INTERNAL_PREFIXES.some((prefix) => url.startsWith(prefix));\n}\n\n/**\n * Check if a URL looks like a static asset request.\n */\nfunction isAssetRequest(url: string): boolean {\n return ASSET_EXTENSIONS.test(url);\n}\n\n/**\n * Check if a URL maps to a real file in the project's public/ directory.\n * Vite serves public/ files as static assets — if a file exists there it\n * must be passed through to Vite regardless of extension.\n */\nfunction isPublicFile(server: ViteDevServer, url: string): boolean {\n const { publicDir } = server.config;\n if (!publicDir) return false;\n const pathname = url.split('?')[0];\n const relativePath = pathname.startsWith('/') ? pathname.slice(1) : pathname;\n if (!relativePath) return false;\n const fullPath = join(publicDir, relativePath);\n try {\n return statSync(fullPath).isFile();\n } catch {\n return false;\n }\n}\n\n/**\n * Check whether an asset-extension URL belongs to a scanned route rather\n * than a static file.\n *\n * Route handlers may live at paths ending in asset-like extensions\n * (app/feed.json/route.ts → /feed.json), which the asset bypass would\n * otherwise hand to Vite — 404ing in dev while working in production.\n * Uses the same canonicalization and tree walker as the runtime matcher\n * so dev classification agrees with what the pipeline would match.\n *\n * A file that actually exists on disk wins over the route match: the\n * production server serves public/ statics before the RSC handler (see\n * adapters/nitro.ts), and Vite serves root-relative source files (e.g.\n * /app/global.css) in dev. Without this, a broad catch-all route like\n * app/[...slug] would shadow every static asset. Joining the canonical\n * pathname is traversal-safe — canonicalize() resolves `..` segments and\n * rejects root escapes and encoded separators.\n *\n * Reads ctx.routeTree per request — the routing plugin rescans it on\n * file add/unlink/change, so newly created routes are picked up live.\n */\nfunction isRouteOwnedAsset(\n routeTree: RouteTree | null,\n server: ViteDevServer,\n url: string\n): boolean {\n if (!routeTree) return false;\n const rawPathname = url.split('?')[0];\n const result = canonicalize(rawPathname);\n // Malformed URLs can't match a route — let Vite handle them as before.\n if (!result.ok) return false;\n const parts = result.pathname === '/' ? [] : result.pathname.slice(1).split('/');\n if (matchUrlParts(routeTree.root, parts) === null) return false;\n\n // Real static files win over the route match (dev/prod parity: the\n // production server tries public/ before the RSC handler).\n const relativePath = result.pathname.slice(1);\n const { publicDir, root } = server.config;\n if (publicDir && existsSync(join(publicDir, relativePath))) return false;\n if (existsSync(join(root, relativePath))) return false;\n\n return true;\n}\n\n// ─── Client Error Listener ─────────────────────────────────────────────\n\ninterface ClientErrorPayload {\n message: string;\n stack: string;\n componentStack: string | null;\n}\n\n/**\n * Listen for client-side errors forwarded from the browser via HMR.\n *\n * The browser entry catches uncaught errors and unhandled rejections,\n * then sends them as 'timber:client-error' custom events. We parse\n * the first app frame for the overlay's loc field and forward the\n * error to Vite's overlay protocol.\n */\nfunction listenForClientErrors(server: ViteDevServer, projectRoot: string): void {\n server.hot.on('timber:client-error', (data: ClientErrorPayload) => {\n const loc = parseFirstAppFrame(data.stack, projectRoot);\n\n let message = data.message;\n if (data.componentStack) {\n message = `${data.message}\\n\\nComponent Stack:\\n${data.componentStack.trim()}`;\n }\n\n // Log to stderr\n const RED = '\\x1b[31m';\n const BOLD = '\\x1b[1m';\n const RESET = '\\x1b[0m';\n process.stderr.write(\n `${RED}${BOLD}[timber] Client Error${RESET}\\n${RED}${data.message}${RESET}\\n\\n`\n );\n\n // Forward to Vite's overlay\n try {\n server.hot.send({\n type: 'error',\n err: {\n message,\n stack: data.stack,\n id: loc?.file,\n plugin: 'timber (Client)',\n loc: loc ? { file: loc.file, line: loc.line, column: loc.column } : undefined,\n },\n });\n } catch (e) {\n console.debug('[timber] overlay send failed:', e instanceof Error ? e.message : e);\n }\n });\n}\n"],"x_google_ignoreList":[0],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"file-cache-Dw6BJPG7.js","names":[],"sources":["../../src/routing/codegen-shared.ts","../../src/routing/link-codegen.ts","../../src/routing/codegen.ts","../../src/routing/file-cache.ts"],"sourcesContent":["/**\n * Shared codegen helpers — import-path computation and codec chain type\n * builders.\n *\n * Extracted from `codegen.ts` so `link-codegen.ts` can use the same\n * helpers without a cyclic import.\n */\n\nimport { relative, posix } from 'node:path';\nimport type { ParamEntry } from './codegen-types.ts';\n\n/**\n * Compute a relative import specifier for a codec/page file, stripping\n * the .ts/.tsx extension and resolving against the codegen output dir.\n */\nexport function codecImportPath(codecFilePath: string, importBase: string | undefined): string {\n const absPath = codecFilePath.replace(/\\.(ts|tsx)$/, '');\n if (importBase) {\n return './' + relative(importBase, absPath).replace(/\\\\/g, '/');\n }\n return './' + posix.basename(absPath);\n}\n\n/**\n * Build a TypeScript type expression for a single param using the global\n * schema's InferCodecType. Falls back to the param's base type when\n * schema doesn't declare this param (schemas are opt-in per key).\n *\n * Uses `infer` to extract the codec value rather than direct indexing\n * because TypeScript doesn't narrow index validity inside a conditional\n * type's true branch — `typeof schema.segmentParams[K]` produces TS2538\n * even when the condition proves K is a valid key. In .d.ts files that\n * error is suppressed and the type silently becomes `any`.\n */\nexport function buildSchemaParamType(p: ParamEntry, fallback?: string): string {\n const key = JSON.stringify(p.bracketName);\n const fb = fallback ?? p.type;\n return `typeof schema['segmentParams'] extends Record<${key}, infer V> ? InferCodecType<V> : ${fb}`;\n}\n","/**\n * Typed `<Link>` codegen — interface augmentation generation.\n *\n * Extracted from `codegen.ts` to keep that file under the project's\n * 500-line cap. This module owns everything that emits the\n * `interface LinkFunction { ... }` augmentation blocks in the generated\n * `.timber/timber-routes.d.ts`.\n *\n * Two augmentation blocks are emitted:\n *\n * 1. **Per-route discriminated union** (`formatTypedLinkOverloads`) —\n * one call signature whose props is a union keyed on `href`. TS\n * narrows by literal href and reports prop errors against the\n * matched variant. See TIM-835 and `design/09-typescript.md`.\n *\n * 2. **Catch-all overloads** (`formatLinkCatchAllOverloads`) — external\n * href literals (`http://`, `mailto:`, etc.) and a computed-string\n * `<H extends string>` signature for runtime-computed paths.\n *\n * Block ordering is critical for error UX: per-route is emitted FIRST\n * so that, after TS's \"later overload set ordered first\" merge rule,\n * the discriminated union ends up LAST in resolution order — the\n * overload TS reports against on failure.\n */\n\nimport type { ParamEntry, RouteEntry } from './codegen-types.ts';\nimport { buildSchemaParamType } from './codegen-shared.ts';\nimport { classifyUrlSegment } from './segment-classify.ts';\n\n/**\n * The `searchParams` prop type, emitted identically on every Link variant\n * — per-route pattern, resolved template, and catch-all.\n *\n * TIM-1343: there is no per-route narrowing any more. `params.ts` is gone,\n * so codegen has no definition to read a `SearchParamsDefinition<T>` from.\n * Type safety moved to the value's construction site instead: callers write\n * `definition.buildSearchParams({ ... })`, which checks `Partial<T>` there\n * and returns a query STRING — the only shape that survives the RSC Flight\n * boundary. A plain record is the uncodec'd escape hatch. See `client/link.tsx` §\"searchParams prop shapes\".\n *\n * Declared once so the three emission sites cannot drift apart.\n */\nexport const SEARCH_PARAMS_PROP_TYPE = 'string | Record<string, unknown>';\n\n/** Shared Link base-props type literal used in every emitted call signature. */\nexport const LINK_BASE_PROPS_TYPE =\n \"Omit<import('react').AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> & { prefetch?: boolean; scroll?: boolean; preserveSearchParams?: true | string[]; onNavigate?: import('./client/link.js').OnNavigateHandler; children?: import('react').ReactNode }\";\n\n/**\n * Build a TypeScript template literal pattern for a dynamic route.\n * e.g. '/products/[id]' → '/products/${string}'\n * '/blog/[...slug]' → '/blog/${string}'\n * '/docs/[[...path]]' → '/docs/${string}' (also matches /docs)\n * '/[org]/[repo]' → '/${string}/${string}'\n */\nexport function buildResolvedPattern(route: RouteEntry): string | null {\n const parts = route.urlPath.split('/');\n const templateParts = parts.map((part) => {\n const seg = classifyUrlSegment(part);\n switch (seg.kind) {\n case 'catch-all':\n case 'optional-catch-all':\n // eslint-disable-next-line no-template-curly-in-string -- codegen output\n return '${string}';\n case 'dynamic': {\n const prefix = seg.prefix ?? '';\n const suffix = seg.suffix ?? '';\n // eslint-disable-next-line no-template-curly-in-string -- codegen output\n return `${prefix}\\${string}${suffix}`;\n }\n default:\n return part;\n }\n });\n return templateParts.join('/');\n}\n\n/**\n * Format the segmentParams type for Link overloads.\n *\n * Link params accept `string | number` for single dynamic segments\n * (convenience — values are stringified at runtime). Catch-all and\n * optional catch-all remain `string[]` / `string[] | undefined`.\n *\n * With a global schema, the schema codec's inferred type wins. Without\n * one, a URL segment is a raw string (widened to string | number for\n * Link convenience) — per-segment codec chains were deleted in TIM-1342.\n */\nexport function formatLinkParamsType(params: ParamEntry[], hasSchema?: boolean): string {\n if (params.length === 0) {\n return '{}';\n }\n\n const fields = params.map((p) => {\n const fallback = p.type === 'string' ? 'string | number' : p.type;\n const codecType = hasSchema ? buildSchemaParamType(p, fallback) : fallback;\n return `${p.name}: ${codecType}`;\n });\n return `{ ${fields.join('; ')} }`;\n}\n\n/**\n * Catch-all call signatures for `<Link>` — external hrefs and computed\n * `string` variables. Emitted from codegen in a SEPARATE\n * `declare module` block (declared AFTER the per-route block) so the TS\n * \"later overload set ordered first\" rule places these catch-all\n * signatures ahead of per-route in resolution order, leaving per-route\n * as the final overload whose error message is reported on failure.\n *\n * The conditional `string extends H ? ... : never` protection preserves\n * TIM-624's guarantee that unknown internal path literals don't match\n * the catch-all — typos like `<Link href=\"/typo\" />` still error.\n */\nexport function formatLinkCatchAllOverloads(): string[] {\n const lines: string[] = [];\n const baseProps = LINK_BASE_PROPS_TYPE;\n\n const catchAllSearchParams = SEARCH_PARAMS_PROP_TYPE;\n\n // ExternalHref inlined here rather than referenced as an exported\n // alias so the generated .d.ts stands alone without source imports.\n /* eslint-disable no-template-curly-in-string -- codegen output */\n const externalHref =\n '`http://${string}` | `https://${string}` | `mailto:${string}` | `tel:${string}` | `ftp://${string}` | `//${string}` | `#${string}` | `?${string}`';\n /* eslint-enable no-template-curly-in-string */\n\n lines.push(' // Typed Link overloads — catch-all (block 2 / emitted second)');\n lines.push(' interface LinkFunction {');\n\n // (1) External/literal-protocol hrefs.\n //\n // TIM-833: `segmentParams` is permissively typed as\n // `Record<string, unknown>` (not `never`) so generic wrappers that\n // forward a `LinkProps`-shaped object can compile when the wrapper\n // happens to bottom out at an external href. There are no dynamic\n // segments to interpolate for an external href, so this prop is\n // ignored at runtime — accepting an open-shape object instead of\n // forbidding it removes a footgun without changing behavior.\n lines.push(` (props: ${baseProps} & {`);\n lines.push(` href: ${externalHref}`);\n lines.push(` segmentParams?: Record<string, unknown>`);\n lines.push(` searchParams?: ${catchAllSearchParams}`);\n lines.push(` }): import('react').JSX.Element`);\n\n // (2) Computed/variable href — non-literal `string` only.\n //\n // `string extends H` is true only when H is the wide `string` type,\n // not a specific literal. For literal hrefs, this overload must NOT\n // be selectable so the per-route discriminated union (block 1) is\n // the resolution target.\n //\n // TIM-833 follow-up: the previous shape used `string extends H ? {...} : never`\n // on the WHOLE props type. For literal H, that collapsed `props` to\n // `never`, and JSX type inference then read `(never).children` as\n // `never` — producing the misleading\n // `'children' prop expects type 'never' which requires multiple\n // children, but only a single child was provided` (TS2745) error,\n // which buried the actual prop-mismatch message under a noise diagnostic.\n //\n // Fix: move the `: never` from the whole props onto just the `href`\n // field. The overload remains uncallable for literal H (since `\"/x\"`\n // is not assignable to `never`), but `children` retains its real\n // `ReactNode` type so JSX inference no longer collapses to never.\n // The diagnostic surface becomes: TS reports the per-route block's\n // error chain (the helpful \"Type 'number' is not assignable to type\n // 'string'\" message) WITHOUT a leading TS2745 noise line.\n lines.push(` <H extends string>(`);\n lines.push(` props: ${baseProps} & {`);\n lines.push(` href: string extends H ? H : never`);\n lines.push(` segmentParams?: Record<string, string | number | string[]>`);\n lines.push(` searchParams?: ${catchAllSearchParams}`);\n lines.push(` }`);\n lines.push(` ): import('react').JSX.Element`);\n\n lines.push(' }');\n return lines;\n}\n\n/**\n * Generate typed per-route Link call signatures via LinkFunction\n * interface merging.\n *\n * TIM-835: This emits a SINGLE call signature whose props is a\n * discriminated union keyed on `href`. TypeScript narrows the union by\n * the literal `href` at the call site, then checks the rest of the\n * props against the matched variant. When `segmentParams` or\n * `searchParams` is wrong, TS reports the error against the matched\n * variant — naming the user's actual `href` and the offending field.\n *\n * Before TIM-835, this function emitted N separate per-route overloads\n * (one per route, sometimes two for dynamic routes). When ALL overloads\n * failed, TS would pick an arbitrary failed overload (heuristically the\n * \"last tried\") to render the diagnostic, often pointing at a route\n * completely unrelated to the one the user wrote. The discriminated\n * union sidesteps overload-resolution heuristics entirely.\n *\n * Open property shapes (`Record<string, unknown>` instead of `never`)\n * for `segmentParams` on routes that don't declare them keep generic\n * `LinkProps`-spreading wrappers compiling. (Aligned with TIM-833.)\n *\n * TIM-832: this function still emits the per-route block FIRST and the\n * catch-all block follows, so per-route remains the LAST overload set in\n * resolution order — the one TS reports against on failure. With a\n * discriminated union there is only one call signature in this block,\n * so the resolved-template-vs-pattern ordering reduces to placing the\n * pattern variant before the resolved-template variant inside the union.\n */\nexport function formatTypedLinkOverloads(routes: RouteEntry[], hasSchema?: boolean): string[] {\n const lines: string[] = [];\n const baseProps = LINK_BASE_PROPS_TYPE;\n\n // Build the union variants. Each route contributes one variant for the\n // pattern href (e.g. '/products/[id]') and, for dynamic routes, an\n // additional variant for the resolved-template href (e.g.\n // `/products/${string}`).\n //\n // The PATTERN variant must be listed BEFORE the resolved-template\n // variant for the same route so that when the user writes the literal\n // pattern href (`<Link href=\"/products/[id]\" />`), TS narrows to the\n // pattern variant (which carries the typed `segmentParams` shape)\n // rather than the looser resolved-template variant. Without this\n // ordering, TS would silently match the resolved-template variant\n // first — swallowing typed-segmentParams type errors.\n const variants: string[] = [];\n for (const route of routes) {\n const hasDynamicParams = route.params.length > 0;\n // For routes with no dynamic params, accept an absent OR open-shape\n // segmentParams. `Record<string, unknown>` keeps generic spreads\n // compiling and gives a readable error if a non-object is passed.\n const paramsProp = hasDynamicParams\n ? `segmentParams: ${formatLinkParamsType(route.params, hasSchema)}`\n : 'segmentParams?: Record<string, unknown>';\n\n // TIM-1343: searchParams is no longer typed per-route. There is no\n // convention file to read a definition from, so codegen has nothing to\n // narrow with — the value is either a URLSearchParams built by\n // `definition.buildSearchParams()` (which type-checks its own\n // `Partial<T>` at that call) or a plain String()-coerced record.\n const searchParamsProp = `searchParams?: ${SEARCH_PARAMS_PROP_TYPE}`;\n\n // Pattern variant FIRST (more specific).\n variants.push(\n `${baseProps} & { href: '${route.urlPath}'; ${paramsProp}; ${searchParamsProp} }`\n );\n\n // Resolved-template variant SECOND (looser, matches interpolated hrefs).\n if (hasDynamicParams) {\n const templatePattern = buildResolvedPattern(route);\n if (templatePattern) {\n // TIM-835: `Record<string, never>` for segmentParams forbids ANY\n // provided keys on the resolved-template variant. Without this,\n // the resolved-template would shadow the pattern variant for\n // literal pattern hrefs (`<Link href=\"/products/[id]\" segmentParams={...} />`)\n // because both variants accept the literal `'/products/[id]'`\n // and the looser variant would silently swallow segmentParams\n // type errors.\n variants.push(\n `${baseProps} & { href: \\`${templatePattern}\\`; segmentParams?: Record<string, never>; ${searchParamsProp} }`\n );\n }\n }\n }\n\n lines.push(' interface LinkFunction {');\n if (variants.length === 0) {\n // No page routes — emit nothing. The catch-all block in the second\n // augmentation still provides external/computed-string signatures.\n lines.push(' }');\n return lines;\n }\n lines.push(' (');\n lines.push(' props:');\n for (const variant of variants) {\n lines.push(` | (${variant})`);\n }\n lines.push(` ): import('react').JSX.Element`);\n lines.push(' }');\n\n return lines;\n}\n","/**\n * Route map codegen.\n *\n * Walks the scanned RouteTree and generates a TypeScript declaration file\n * mapping every route to its params and searchParams shapes.\n *\n * This runs at build time and in dev (regenerated on file changes).\n * No runtime overhead — purely static type generation.\n */\n\nimport { existsSync } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport type { RouteTree, SegmentNode } from './types.ts';\nimport type { ParamEntry, RouteEntry } from './codegen-types.ts';\nimport { buildSchemaParamType } from './codegen-shared.ts';\nimport { effectiveUrlSegment, type UrlSegmentIdentity } from './segment-classify.ts';\nimport { formatLinkCatchAllOverloads, formatTypedLinkOverloads } from './link-codegen.ts';\n\n/** Options for route map generation. */\nexport interface CodegenOptions {\n /** Absolute path to the app/ directory. Required for page searchParams detection. */\n appDir?: string;\n /**\n * Absolute path to the directory where the .d.ts file will be written.\n * Used to compute correct relative import paths for page files.\n * Defaults to appDir when not provided (preserves backward compat for tests).\n */\n outputDir?: string;\n /**\n * Whether a global schema (app/schema.ts) exists.\n * Auto-detected from appDir when not explicitly set.\n */\n hasSchema?: boolean;\n}\n\n/**\n * Generate a TypeScript declaration file string from a scanned route tree.\n *\n * The output is a `declare module '@timber-js/app'` block containing the Routes\n * interface that maps every route path to its params and searchParams shape.\n */\nexport function generateRouteMap(tree: RouteTree, options: CodegenOptions = {}): string {\n // Auto-detect schema from appDir when not explicitly set\n const hasSchema = options.hasSchema ?? detectSchema(options.appDir);\n\n const routes: RouteEntry[] = [];\n collectRoutes(tree.root, [], '', routes, hasSchema, false);\n\n // Sort routes alphabetically for deterministic output\n routes.sort((a, b) => a.urlPath.localeCompare(b.urlPath));\n\n // When outputDir differs from appDir, import paths must be relative to outputDir\n const importBase = options.outputDir ?? options.appDir;\n\n return formatDeclarationFile(routes, importBase, hasSchema, options.appDir);\n}\n\n/** Schema file extensions to check, in priority order. */\nconst SCHEMA_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx'];\n\n/**\n * Detect whether app/schema.{ts,tsx,js,jsx} exists.\n */\nfunction detectSchema(appDir: string | undefined): boolean {\n if (!appDir) return false;\n return SCHEMA_EXTENSIONS.some((ext) => existsSync(join(appDir, `schema${ext}`)));\n}\n\n/**\n * Recursively walk the segment tree and collect route entries.\n *\n * A route entry is created for any segment that has a `page` or `route` file.\n * Params accumulate from ancestor dynamic segments.\n */\nfunction collectRoutes(\n node: SegmentNode,\n ancestorParams: ParamEntry[],\n parentTreePath: string,\n routes: RouteEntry[],\n hasSchema: boolean,\n insideSlot: boolean,\n insideIntercepting = false\n): void {\n // Build the tree path (includes groups, slots — uniquely identifies this segment).\n // Root node has empty segmentName, producing treePath = ''.\n const treePath = node.segmentName ? `${parentTreePath}/${node.segmentName}` : parentTreePath;\n // Accumulate params from this segment.\n //\n // The segment is read through `effectiveUrlSegment` for the same reason the\n // runtime coercer is: an intercepting node's own `segmentType` is\n // 'intercepting' and its `paramName` is unset — the param it binds lives in\n // the intercepted name (`(.)[albumSlug]` → `[albumSlug]`). Reading the node\n // directly emitted `{}` for a segment the router does bind and coerce, so\n // the types disagreed with the values for every intercepting page with a\n // dynamic intercepted segment, and for its descendants, which lost the\n // ancestor param too.\n const urlSegment = effectiveUrlSegment(node);\n const params = [...ancestorParams];\n if (urlSegment.paramName) {\n params.push({\n name: urlSegment.paramName,\n bracketName: bracketNameForSegment(urlSegment),\n type: paramTypeForSegment(urlSegment.segmentType),\n });\n }\n\n // Check if this segment is a leaf route (has page or route file)\n const isPage = !!node.page;\n const isApiRoute = !!node.route;\n\n // A segment that hosts code but is not itself addressable — a layout,\n // middleware, or access check above the leaf — still gets a `$segment`\n // module, so `getSegmentParams(SEGMENT_PATH)` is callable from it and needs\n // a matching typed overload. Emitting only leaves left those call sites on\n // the broad `Record<string, string | string[]>` signature, which since\n // TIM-1342 (which deleted `defineSegmentParams`) is their ONLY typed path.\n // design/41-global-params.md's Type Summary promises layouts and middleware\n // exact accumulated params.\n //\n // `isRoutable: false` keeps them out of the urlPath-keyed emissions — the\n // `Routes` interface and `<Link>` variants — where they would publish a URL\n // that no request can reach. They belong only to the segmentPath-keyed\n // APIs: `getSegmentParams`, `useSegmentParams`, `TimberSegmentParams`.\n const hostsCode = !!node.layout || !!node.middleware || !!node.access;\n\n if (isPage || isApiRoute || hostsCode) {\n const entry: RouteEntry = {\n urlPath: node.urlPath,\n segmentPath: treePath || '/',\n params,\n isApiRoute,\n isRoutable: isPage || isApiRoute,\n isSlotRoute: insideSlot,\n isInterceptingRoute: insideIntercepting,\n hasSchema,\n };\n\n routes.push(entry);\n }\n\n // Recurse into children. An intercepting subtree is flagged, and the flag is\n // sticky — the descendants are where the false URLs are. See\n // `RouteEntry.isInterceptingRoute` for what the flag drops and why dedupe\n // alone was not enough.\n for (const child of node.children) {\n const childIntercepting = insideIntercepting || child.segmentType === 'intercepting';\n collectRoutes(child, params, treePath, routes, hasSchema, insideSlot, childIntercepting);\n }\n\n // Recurse into slots (they share the parent's URL path, but may have their own pages)\n for (const slot of Object.values(node.slots)) {\n collectRoutes(slot, params, treePath, routes, hasSchema, true, insideIntercepting);\n }\n}\n\n/**\n * Dedupe route entries by urlPath for urlPath-keyed emissions (the Routes\n * interface and typed Link variants).\n *\n * Parallel-slot pages share their parent's urlPath; emitting one entry per\n * slot page produced duplicate interface keys (TS2300/TS2717 under\n * skipLibCheck:false) and duplicate overloads. The non-slot entry is the\n * canonical one; when only slot entries exist (parent has no page), the\n * lexicographically smallest segmentPath wins so output is independent of\n * slot scan order.\n *\n * The loser is discarded outright. It used to donate its `searchParams`\n * definition to the winner, because a slot could carry one the parent page\n * lacked; since TIM-1343 no route entry carries a searchParams definition at\n * all, there is nothing left to merge.\n *\n * segmentPath-keyed emissions (getSegmentParams, useSegmentParams,\n * TimberSegmentParams) must NOT use this — segmentPaths are already unique\n * and slot entries feed those APIs directly.\n */\nfunction dedupeRoutesByUrlPath(routes: RouteEntry[]): RouteEntry[] {\n const byPath = new Map<string, RouteEntry>();\n for (const route of routes) {\n const existing = byPath.get(route.urlPath);\n if (!existing) {\n byPath.set(route.urlPath, route);\n continue;\n }\n\n const routeWins =\n (existing.isSlotRoute && !route.isSlotRoute) ||\n (!!existing.isSlotRoute === !!route.isSlotRoute &&\n route.segmentPath.localeCompare(existing.segmentPath) < 0);\n byPath.set(route.urlPath, routeWins ? route : existing);\n }\n return [...byPath.values()];\n}\n\n/**\n * Determine the TypeScript type for a segment's param.\n */\n/**\n * Derive the bracket key for schema/codegen lookup.\n * For bare segments like `[id]` this is the segmentName itself.\n * For affixed segments like `img-[id].png` this is `[id]` — the bare bracket form.\n */\nfunction bracketNameForSegment(node: UrlSegmentIdentity): string {\n if (node.paramPrefix || node.paramSuffix) {\n return `[${node.paramName}]`;\n }\n return node.segmentName;\n}\n\nfunction paramTypeForSegment(segmentType: string): ParamEntry['type'] {\n switch (segmentType) {\n case 'catch-all':\n return 'string[]';\n case 'optional-catch-all':\n return 'string[] | undefined';\n default:\n return 'string';\n }\n}\n\n/**\n * Emit the schema-based type exports: TimberSegmentParams, TimberRoute,\n * AllSegmentParams, RoutesWithParams.\n *\n * These types use InferCodecType references so they stay in sync with\n * the schema without hardcoding resolved types.\n *\n * Design doc: design/41-global-params.md §Codegen Output\n */\nfunction formatSchemaTypes(routes: RouteEntry[]): string[] {\n const lines: string[] = [];\n\n lines.push('/** Union of all valid route patterns. */');\n lines.push('export type TimberRoute = keyof TimberSegmentParams;');\n lines.push('');\n lines.push('/** Maps each segment path to its accumulated segment params. */');\n lines.push('export interface TimberSegmentParams {');\n\n for (const route of routes) {\n if (route.params.length === 0) {\n lines.push(` '${route.segmentPath}': {};`);\n } else {\n lines.push(` '${route.segmentPath}': {`);\n for (const p of route.params) {\n lines.push(` ${p.name}: ${buildSchemaParamType(p)};`);\n }\n lines.push(' };');\n }\n }\n\n lines.push('}');\n lines.push('');\n\n // AllSegmentParams — flat union of all param types across all routes\n lines.push('/** All segment params (union of all route params). */');\n lines.push('export type AllSegmentParams = {');\n lines.push(\n ' [K in keyof TimberSegmentParams as TimberSegmentParams[K] extends Record<string, never>'\n );\n lines.push(' ? never');\n lines.push(' : K]: TimberSegmentParams[K];');\n lines.push('} extends infer U');\n lines.push(' ? { [K in keyof U[keyof U]]: U[keyof U][K] }');\n lines.push(' : never;');\n lines.push('');\n\n // RoutesWithParams — filter routes by required param keys\n lines.push(\n '/** Filters TimberRoute to routes whose accumulated params include all listed keys. */'\n );\n lines.push('export type RoutesWithParams<K extends keyof AllSegmentParams> = {');\n lines.push(' [R in TimberRoute]: K extends keyof TimberSegmentParams[R] ? R : never;');\n lines.push('}[TimberRoute];');\n\n return lines;\n}\n\n/**\n * Format the collected routes into a TypeScript declaration file.\n */\nfunction formatDeclarationFile(\n routes: RouteEntry[],\n importBase?: string,\n hasSchema?: boolean,\n appDir?: string\n): string {\n const lines: string[] = [];\n\n lines.push('// This file is auto-generated by timber.js route map codegen.');\n lines.push('// Do not edit manually. Regenerated on build and in dev mode.');\n lines.push('');\n // export {} makes this file a module, so all declare module blocks are\n // augmentations rather than ambient replacements. Without this, the\n // declare module blocks would replace the original module types entirely\n // (removing exports like bindUseQueryStates that aren't listed here).\n lines.push('export {};');\n lines.push('');\n\n if (hasSchema) {\n // Schema-based codegen: import schema and InferCodecType\n // Compute the schema import path relative to the output directory\n const schemaImport =\n appDir && importBase\n ? './' + relative(importBase, join(appDir, 'schema')).replace(/\\\\/g, '/')\n : '../app/schema';\n lines.push(`import type schema from '${schemaImport}';`);\n lines.push(\"import type { InferCodecType, SegmentParamCodec } from '@timber-js/app/params';\");\n lines.push('');\n // Collect all unique bracket keys from routes for key validation\n const allBracketKeys = new Set<string>();\n for (const route of routes) {\n for (const p of route.params) {\n allBracketKeys.add(p.bracketName);\n }\n }\n\n // Augment the ValidDynamicSegmentKeysRegistry so defineSchema constrains keys\n // to known filesystem segments. Unknown keys produce inline TS errors.\n if (allBracketKeys.size > 0) {\n lines.push(\"declare module '@timber-js/app/params' {\");\n lines.push(' interface ValidDynamicSegmentKeysRegistry {');\n for (const key of [...allBracketKeys].sort()) {\n lines.push(` ${JSON.stringify(key)}: true;`);\n }\n lines.push(' }');\n lines.push('}');\n lines.push('');\n }\n // Guard: detect when the schema or params imports silently degraded to\n // `any` because TypeScript could not resolve the import specifier.\n // In a .d.ts an unresolved import does NOT error — every downstream\n // type silently becomes `any`, which satisfies every assertion. Only\n // an explicit IsAny check catches it.\n lines.push('type _IsAny<T> = 0 extends 1 & T ? true : false;');\n lines.push('type _AssertSchemaNotAny<_T extends false = _IsAny<typeof schema>> = true;');\n lines.push('type _AssertCodecTypeNotAny<_T extends false = _IsAny<SegmentParamCodec>> = true;');\n lines.push('');\n // Type-level validation: each schema value must be a Codec or Standard Schema.\n // Running `pnpm run typecheck` will surface errors for invalid values.\n lines.push('type _AssertSchemaValues = {');\n lines.push(\n \" [K in keyof typeof schema['segmentParams']]: typeof schema['segmentParams'][K] extends SegmentParamCodec ? true : ['Error: value for', K, 'is not a valid Codec or Standard Schema'];\"\n );\n lines.push('};');\n // Generic constraint enforces that all values are `true` — if any value is\n // an error tuple, TS reports a real diagnostic instead of silently computing it.\n lines.push(\n 'type _EnforceSchemaValues<_T extends Record<string, true> = _AssertSchemaValues> = true;'\n );\n lines.push('');\n }\n\n // urlPath-keyed emissions use the deduped list (slot pages share their\n // parent's urlPath); segmentPath-keyed emissions below keep all entries.\n //\n // Intercepting subtrees are dropped outright rather than deduped: their\n // computed urlPath is not a route any request can reach, so a path that\n // happens NOT to collide with a real one would otherwise be published as a\n // real route. See `RouteEntry.isInterceptingRoute`.\n const uniqueByUrlPath = dedupeRoutesByUrlPath(\n routes.filter((r) => r.isRoutable && !r.isInterceptingRoute)\n );\n\n lines.push(\"declare module '@timber-js/app' {\");\n lines.push(' interface Routes {');\n\n // TIM-1343: no `searchParams` member. With `params.ts` gone there is no\n // convention file to read a definition from, so the framework has nothing\n // to type it with — and an always-`{}` member is a claim that every route\n // has no search params, which is false. Search-param types now come from\n // the definition object itself, wherever the app chooses to keep it.\n for (const route of uniqueByUrlPath) {\n const paramsType = formatParamsType(route.params, hasSchema);\n\n lines.push(` '${route.urlPath}': {`);\n lines.push(` segmentParams: ${paramsType}`);\n lines.push(` }`);\n }\n\n lines.push(' }');\n\n // When a global schema exists, emit segment param types inside the\n // @timber-js/app module augmentation so that declare module blocks\n // for @timber-js/app/server and @timber-js/app/client can reference\n // AllSegmentParams via `import type { AllSegmentParams } from '@timber-js/app'`.\n if (hasSchema) {\n lines.push('');\n lines.push(...formatSchemaTypes(routes).map((l) => ` ${l}`));\n }\n\n lines.push('}');\n lines.push('');\n\n // useQueryStates + typed Link are urlPath-keyed → deduped list.\n const pageRoutes = uniqueByUrlPath.filter((r) => !r.isApiRoute);\n // getSegmentParams / useSegmentParams are segmentPath-keyed → full list,\n // so slot segments keep their own typed overloads.\n const dynamicRoutes = routes.filter((r) => r.params.length > 0);\n\n // Generate @timber-js/app/server augmentation — typed getSegmentParams\n if (dynamicRoutes.length > 0) {\n lines.push(\"declare module '@timber-js/app/server' {\");\n if (hasSchema) {\n lines.push(\" import type { AllSegmentParams } from '@timber-js/app'\");\n }\n for (const route of dynamicRoutes) {\n const paramsType = formatParamsType(route.params, hasSchema);\n lines.push(\n ` export function getSegmentParams(segmentPath: '${route.segmentPath}'): ${paramsType}`\n );\n }\n lines.push(\n hasSchema\n ? ' export function getSegmentParams(): Partial<AllSegmentParams>'\n : ' export function getSegmentParams(): Record<string, string | string[]>'\n );\n lines.push('}');\n lines.push('');\n }\n\n // Generate overloads for @timber-js/app/client\n\n if (dynamicRoutes.length > 0 || pageRoutes.length > 0) {\n lines.push(\"declare module '@timber-js/app/client' {\");\n lines.push(\" import type { SearchParamsDefinition } from '@timber-js/app/search-params'\");\n if (hasSchema) {\n lines.push(\" import type { AllSegmentParams } from '@timber-js/app'\");\n }\n lines.push('');\n\n // useSegmentParams overloads\n if (dynamicRoutes.length > 0) {\n for (const route of dynamicRoutes) {\n const paramsType = formatParamsType(route.params, hasSchema);\n lines.push(\n ` export function useSegmentParams(segmentPath: '${route.segmentPath}'): ${paramsType}`\n );\n }\n lines.push(\n hasSchema\n ? ' export function useSegmentParams(): Partial<AllSegmentParams>'\n : ' export function useSegmentParams(): Record<string, string | string[]>'\n );\n lines.push('');\n }\n\n // Typed Link overloads — per-route with DIRECT types (no conditionals).\n // Direct types preserve TypeScript's excess property checking.\n //\n // TIM-832: per-route and catch-all are emitted as TWO separate\n // augmentation blocks. Per TS's merging rule \"later overload sets\n // ordered first\", the catch-all block (declared SECOND in this file)\n // ends up FIRST in the merged call-signature list at resolution time,\n // which puts the per-route block LAST — so its error message is the\n // one TypeScript reports when no overload matches. This gives users a\n // clear prop-mismatch error (e.g. \"'string | undefined' is not\n // assignable to 'string | number' on id\") instead of the old\n // confusing \"Type 'string' is not assignable to type 'never'\" cascade.\n if (pageRoutes.length > 0) {\n lines.push(' // Typed Link overloads — per-route (block 1 / emitted first)');\n lines.push(...formatTypedLinkOverloads(pageRoutes, hasSchema));\n lines.push('');\n }\n\n lines.push('}');\n lines.push('');\n }\n\n // TIM-832: catch-all block — emitted as a SEPARATE `declare module`\n // augmentation so TS's \"later overload set first\" rule orders it ahead\n // of the per-route block above at resolution time, leaving per-route as\n // the \"last overload\" whose error TypeScript reports.\n lines.push(\"declare module '@timber-js/app/client' {\");\n lines.push(\" import type { SearchParamsDefinition } from '@timber-js/app/search-params'\");\n lines.push('');\n lines.push(...formatLinkCatchAllOverloads());\n lines.push('}');\n lines.push('');\n\n return lines.join('\\n');\n}\n\n/**\n * Format the params type for a route entry.\n */\nfunction formatParamsType(params: ParamEntry[], hasSchema?: boolean): string {\n if (params.length === 0) {\n return '{}';\n }\n\n // Without a schema a URL segment is an uninterpreted string — that IS\n // its type. Per-segment codec chains used to narrow it here; TIM-1342\n // deleted them along with params.ts.\n const fields = params.map((p) => {\n const codecType = hasSchema ? buildSchemaParamType(p) : p.type;\n return `${p.name}: ${codecType}`;\n });\n return `{ ${fields.join('; ')} }`;\n}\n\n// TIM-1341: `useQueryStates` no longer has a route-string form, so the\n// generated `.d.ts` emits no overloads for it. Its source signature —\n// `useQueryStates(codecs, options?)` — is fully generic over the codec map\n// and needs no per-route augmentation. Reaching another route's codecs is\n// done by importing that route's `searchParams` definition from its\n// `params.ts` and calling `definition.useQueryStates()`, which is typed by\n// inference and cannot fail at runtime on an unpopulated registry.\n// See design/23-search-params.md §\"Client Access\".\n\n// Link overload formatters and helpers (`formatTypedLinkOverloads`,\n// `formatLinkCatchAllOverloads`, `formatLinkParamsType`,\n// `buildResolvedPattern`, `LINK_BASE_PROPS_TYPE`) were extracted to\n// `./link-codegen.ts` (TIM-835) to keep this file under the 500-line\n// cap. They are imported at the top of this file.\n","import { readFileSync, statSync } from 'node:fs';\n\nconst cache = new Map<string, { mtimeMs: number; size: number; content: string }>();\n\n/**\n * Read a file's text content, returning a cached copy when the file\n * hasn't been modified since the last read (checked via mtime + size).\n *\n * Falls back to a fresh read on any stat/read error.\n */\nexport function readFileCached(filePath: string): string {\n try {\n const stat = statSync(filePath);\n const entry = cache.get(filePath);\n if (entry && entry.mtimeMs === stat.mtimeMs && entry.size === stat.size) return entry.content;\n const content = readFileSync(filePath, 'utf-8');\n cache.set(filePath, { mtimeMs: stat.mtimeMs, size: stat.size, content });\n return content;\n } catch {\n cache.delete(filePath);\n return readFileSync(filePath, 'utf-8');\n }\n}\n"],"mappings":"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"}
|