@elurjs/kit 2.4.10 → 2.5.1

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.
Files changed (188) hide show
  1. package/CHANGELOG.md +313 -0
  2. package/README.md +248 -15
  3. package/dist/lib/action/index.cjs +1 -1
  4. package/dist/lib/action/index.js +1 -1
  5. package/dist/lib/{action-DZHihU3Z.cjs → action-BiE4tEty.cjs} +2 -2
  6. package/dist/lib/{action-DZHihU3Z.cjs.map → action-BiE4tEty.cjs.map} +1 -1
  7. package/dist/lib/{action-BB6N5yD-.js → action-CXVk8rWt.js} +3 -3
  8. package/dist/lib/{action-BB6N5yD-.js.map → action-CXVk8rWt.js.map} +1 -1
  9. package/dist/lib/adapter-DNsjXvq5.js +160 -0
  10. package/dist/lib/adapter-DNsjXvq5.js.map +1 -0
  11. package/dist/lib/adapter-Dz2hzR36.cjs +3 -0
  12. package/dist/lib/adapter-Dz2hzR36.cjs.map +1 -0
  13. package/dist/lib/adapters/bun.cjs +2 -2
  14. package/dist/lib/adapters/bun.cjs.map +1 -1
  15. package/dist/lib/adapters/bun.js +17 -9
  16. package/dist/lib/adapters/bun.js.map +1 -1
  17. package/dist/lib/adapters/index.d.cts +34 -0
  18. package/dist/lib/adapters/index.d.ts +34 -0
  19. package/dist/lib/adapters/netlify.cjs +1 -1
  20. package/dist/lib/adapters/netlify.js +7 -7
  21. package/dist/lib/adapters/node.cjs +5 -5
  22. package/dist/lib/adapters/node.cjs.map +1 -1
  23. package/dist/lib/adapters/node.js +20 -12
  24. package/dist/lib/adapters/node.js.map +1 -1
  25. package/dist/lib/adapters/vercel.cjs +1 -1
  26. package/dist/lib/adapters/vercel.js +8 -8
  27. package/dist/lib/build/build.d.cts +40 -0
  28. package/dist/lib/build/build.d.ts +40 -0
  29. package/dist/lib/build/document-shell.d.cts +47 -1
  30. package/dist/lib/build/document-shell.d.ts +47 -1
  31. package/dist/lib/build/vite-build.d.cts +25 -2
  32. package/dist/lib/build/vite-build.d.ts +25 -2
  33. package/dist/lib/cache/adapter.d.cts +41 -0
  34. package/dist/lib/cache/adapter.d.ts +41 -0
  35. package/dist/lib/cache/index.cjs +2 -0
  36. package/dist/lib/cache/index.cjs.map +1 -0
  37. package/dist/lib/cache/index.d.cts +5 -0
  38. package/dist/lib/cache/index.d.ts +5 -0
  39. package/dist/lib/cache/index.js +113 -0
  40. package/dist/lib/cache/index.js.map +1 -0
  41. package/dist/lib/cache/invalidation.d.cts +36 -0
  42. package/dist/lib/cache/invalidation.d.ts +36 -0
  43. package/dist/lib/cache/redis-adapter.d.cts +49 -0
  44. package/dist/lib/cache/redis-adapter.d.ts +49 -0
  45. package/dist/lib/cache-DGhTYyqh.js +50 -0
  46. package/dist/lib/cache-DGhTYyqh.js.map +1 -0
  47. package/dist/lib/cache-X1n4Ossu.cjs +2 -0
  48. package/dist/lib/cache-X1n4Ossu.cjs.map +1 -0
  49. package/dist/lib/cli/output.d.cts +58 -0
  50. package/dist/lib/cli/output.d.ts +58 -0
  51. package/dist/lib/cli/ports.d.cts +20 -0
  52. package/dist/lib/cli/ports.d.ts +20 -0
  53. package/dist/lib/cli.cjs +2559 -1488
  54. package/dist/lib/cli.cjs.map +1 -1
  55. package/dist/lib/cli.d.cts +21 -0
  56. package/dist/lib/cli.d.ts +21 -0
  57. package/dist/lib/cli.js +2558 -1488
  58. package/dist/lib/cli.js.map +1 -1
  59. package/dist/lib/config/index.cjs +1 -1
  60. package/dist/lib/config/index.cjs.map +1 -1
  61. package/dist/lib/config/index.d.cts +79 -0
  62. package/dist/lib/config/index.d.ts +79 -0
  63. package/dist/lib/config/index.js +21 -14
  64. package/dist/lib/config/index.js.map +1 -1
  65. package/dist/lib/{errors-ePYxDlhU.js → errors-j4cg9VN3.js} +24 -30
  66. package/dist/lib/{errors-ePYxDlhU.js.map → errors-j4cg9VN3.js.map} +1 -1
  67. package/dist/lib/errors-kSl9yLkg.cjs +2 -0
  68. package/dist/lib/{errors-0X7pCFNU.cjs.map → errors-kSl9yLkg.cjs.map} +1 -1
  69. package/dist/lib/hydrate-BSLuI0Nh.cjs +2 -0
  70. package/dist/lib/hydrate-BSLuI0Nh.cjs.map +1 -0
  71. package/dist/lib/hydrate-BaS7-d8C.js +133 -0
  72. package/dist/lib/hydrate-BaS7-d8C.js.map +1 -0
  73. package/dist/lib/idiomorph.esm-BOOTOg1r.cjs +2 -0
  74. package/dist/lib/idiomorph.esm-BOOTOg1r.cjs.map +1 -0
  75. package/dist/lib/idiomorph.esm-CXuY-EHB.js +371 -0
  76. package/dist/lib/idiomorph.esm-CXuY-EHB.js.map +1 -0
  77. package/dist/lib/index.cjs +1 -1
  78. package/dist/lib/index.cjs.map +1 -1
  79. package/dist/lib/index.d.cts +8 -6
  80. package/dist/lib/index.d.ts +8 -6
  81. package/dist/lib/index.js +1 -1
  82. package/dist/lib/index.js.map +1 -1
  83. package/dist/lib/interpolation-plugin-C0dTd69H.cjs +58 -0
  84. package/dist/lib/interpolation-plugin-C0dTd69H.cjs.map +1 -0
  85. package/dist/lib/{interpolation-plugin-Wgb1j4pT.js → interpolation-plugin-K2qE69tF.js} +95 -126
  86. package/dist/lib/interpolation-plugin-K2qE69tF.js.map +1 -0
  87. package/dist/lib/invalidation-CFpNQUv6.cjs +2 -0
  88. package/dist/lib/invalidation-CFpNQUv6.cjs.map +1 -0
  89. package/dist/lib/invalidation-DXhdE9wj.js +64 -0
  90. package/dist/lib/invalidation-DXhdE9wj.js.map +1 -0
  91. package/dist/lib/island/generate-entry.d.cts +40 -2
  92. package/dist/lib/island/generate-entry.d.ts +40 -2
  93. package/dist/lib/island/hydrate.d.cts +19 -6
  94. package/dist/lib/island/hydrate.d.ts +19 -6
  95. package/dist/lib/island/index.cjs +1 -1
  96. package/dist/lib/island/index.d.cts +2 -1
  97. package/dist/lib/island/index.d.ts +2 -1
  98. package/dist/lib/island/index.js +1 -1
  99. package/dist/lib/island/island.d.cts +15 -0
  100. package/dist/lib/island/island.d.ts +15 -0
  101. package/dist/lib/island-CTBnsOhI.js +59 -0
  102. package/dist/lib/island-CTBnsOhI.js.map +1 -0
  103. package/dist/lib/island-kfQEKRe_.cjs +2 -0
  104. package/dist/lib/island-kfQEKRe_.cjs.map +1 -0
  105. package/dist/lib/node-http-DVFcYeBS.js +723 -0
  106. package/dist/lib/node-http-DVFcYeBS.js.map +1 -0
  107. package/dist/lib/node-http-Uct7DhmN.cjs +13 -0
  108. package/dist/lib/node-http-Uct7DhmN.cjs.map +1 -0
  109. package/dist/lib/rolldown-runtime-BIInC43l.cjs +1 -0
  110. package/dist/lib/rolldown-runtime-Dy4uBu1J.js +11 -0
  111. package/dist/lib/router/client.cjs +1 -1
  112. package/dist/lib/router/client.cjs.map +1 -1
  113. package/dist/lib/router/client.d.cts +55 -8
  114. package/dist/lib/router/client.d.ts +55 -8
  115. package/dist/lib/router/client.js +298 -97
  116. package/dist/lib/router/client.js.map +1 -1
  117. package/dist/lib/router/redirects.d.cts +34 -0
  118. package/dist/lib/router/redirects.d.ts +34 -0
  119. package/dist/lib/runtime/handler.d.cts +59 -0
  120. package/dist/lib/runtime/handler.d.ts +59 -0
  121. package/dist/lib/runtime/index.cjs +1 -1
  122. package/dist/lib/runtime/index.d.cts +2 -1
  123. package/dist/lib/runtime/index.d.ts +2 -1
  124. package/dist/lib/runtime/index.js +4 -4
  125. package/dist/lib/runtime/logger.d.cts +47 -0
  126. package/dist/lib/runtime/logger.d.ts +47 -0
  127. package/dist/lib/runtime/node-http.d.cts +11 -1
  128. package/dist/lib/runtime/node-http.d.ts +11 -1
  129. package/dist/lib/runtime-ChE4flX3.cjs +2 -0
  130. package/dist/lib/runtime-ChE4flX3.cjs.map +1 -0
  131. package/dist/lib/runtime-OMyz5yE-.js +905 -0
  132. package/dist/lib/runtime-OMyz5yE-.js.map +1 -0
  133. package/dist/lib/seo/index.cjs +1 -10
  134. package/dist/lib/seo/index.d.cts +1 -0
  135. package/dist/lib/seo/index.d.ts +1 -0
  136. package/dist/lib/seo/index.js +2 -41
  137. package/dist/lib/seo/sitemap-from-routes.d.cts +24 -0
  138. package/dist/lib/seo/sitemap-from-routes.d.ts +24 -0
  139. package/dist/lib/{shared-CfqOLsKj.cjs → shared-CP0Dc3cp.cjs} +72 -47
  140. package/dist/lib/shared-CP0Dc3cp.cjs.map +1 -0
  141. package/dist/lib/{shared-DU3iQgr7.js → shared-D8w-neq2.js} +87 -58
  142. package/dist/lib/shared-D8w-neq2.js.map +1 -0
  143. package/dist/lib/sitemap-from-routes-CaBoiGvE.js +77 -0
  144. package/dist/lib/sitemap-from-routes-CaBoiGvE.js.map +1 -0
  145. package/dist/lib/sitemap-from-routes-DbcLLL4G.cjs +13 -0
  146. package/dist/lib/sitemap-from-routes-DbcLLL4G.cjs.map +1 -0
  147. package/dist/lib/ssr/render.d.cts +8 -2
  148. package/dist/lib/ssr/render.d.ts +8 -2
  149. package/dist/lib/ssr/server.d.cts +7 -0
  150. package/dist/lib/ssr/server.d.ts +7 -0
  151. package/dist/lib/ssr/stream-response.d.cts +30 -0
  152. package/dist/lib/ssr/stream-response.d.ts +30 -0
  153. package/dist/lib/ssr/stream.d.cts +16 -1
  154. package/dist/lib/ssr/stream.d.ts +16 -1
  155. package/dist/lib/{stream-DmZjYqHm.cjs → stream-Cg-xYG82.cjs} +3 -3
  156. package/dist/lib/stream-Cg-xYG82.cjs.map +1 -0
  157. package/dist/lib/stream-D-ytogdu.js +106 -0
  158. package/dist/lib/stream-D-ytogdu.js.map +1 -0
  159. package/dist/lib/vite/index.cjs +1 -1
  160. package/dist/lib/vite/index.cjs.map +1 -1
  161. package/dist/lib/vite/index.d.cts +11 -0
  162. package/dist/lib/vite/index.d.ts +11 -0
  163. package/dist/lib/vite/index.js +101 -94
  164. package/dist/lib/vite/index.js.map +1 -1
  165. package/package.json +25 -8
  166. package/dist/lib/errors-0X7pCFNU.cjs +0 -2
  167. package/dist/lib/hydrate-CJbUWdXu.js +0 -126
  168. package/dist/lib/hydrate-CJbUWdXu.js.map +0 -1
  169. package/dist/lib/hydrate-D6MR24mz.cjs +0 -2
  170. package/dist/lib/hydrate-D6MR24mz.cjs.map +0 -1
  171. package/dist/lib/interpolation-plugin-TWDch8nn.cjs +0 -39
  172. package/dist/lib/interpolation-plugin-TWDch8nn.cjs.map +0 -1
  173. package/dist/lib/interpolation-plugin-Wgb1j4pT.js.map +0 -1
  174. package/dist/lib/node-http-DRAUhO0c.js +0 -617
  175. package/dist/lib/node-http-DRAUhO0c.js.map +0 -1
  176. package/dist/lib/node-http-DhxguYyz.cjs +0 -13
  177. package/dist/lib/node-http-DhxguYyz.cjs.map +0 -1
  178. package/dist/lib/runtime-CrH5X-Es.cjs +0 -2
  179. package/dist/lib/runtime-CrH5X-Es.cjs.map +0 -1
  180. package/dist/lib/runtime-Ds2-wH6P.js +0 -520
  181. package/dist/lib/runtime-Ds2-wH6P.js.map +0 -1
  182. package/dist/lib/seo/index.cjs.map +0 -1
  183. package/dist/lib/seo/index.js.map +0 -1
  184. package/dist/lib/shared-CfqOLsKj.cjs.map +0 -1
  185. package/dist/lib/shared-DU3iQgr7.js.map +0 -1
  186. package/dist/lib/stream-CpOG052V.js +0 -104
  187. package/dist/lib/stream-CpOG052V.js.map +0 -1
  188. package/dist/lib/stream-DmZjYqHm.cjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"node-http-DRAUhO0c.js","names":[],"sources":["../../src/render/ssr-flag.ts","../../src/render/render-to-string.ts","../../src/build/document-shell.ts","../../src/action/error-store.ts","../../src/cache/policy.ts","../../src/ssr/render.ts","../../src/ssr/match.ts","../../src/action/origin.ts","../../src/action/server.ts","../../src/runtime/node-http.ts"],"sourcesContent":["// --- SSR flag utility ---\n//\n// `@elurjs/core` does not export `_setSSR`/`_isSSR`. The reactivity state lives\n// on `globalThis[Symbol.for(\"@elurjs/core/reactivity-state\")]` and the kit owns\n// the `ssr` boolean on it: `renderToString` sets it to `true` while server\n// rendering so `isSSR()` reflects the current render mode for user code\n// (environment reads, client-only guards, ...).\n//\n// This module manipulates that flag directly so the kit does not depend on\n// private exports that may or may not be present in a given elur release.\n\nconst STATE_KEY = Symbol.for(\"@elurjs/core/reactivity-state\");\n\ntype ReactivityState = { ssr?: boolean };\n\nfunction getState(): ReactivityState | undefined {\n return (globalThis as Record<symbol, unknown>)[STATE_KEY] as\n | ReactivityState\n | undefined;\n}\n\n/** Sets the SSR flag on the Elur reactivity state. No-op if state is absent. */\nexport function setSSR(value: boolean): void {\n const state = getState();\n if (state) state.ssr = value;\n}\n\n/** Reads the SSR flag from the Elur reactivity state. Defaults to false. */\nexport function isSSR(): boolean {\n return getState()?.ssr ?? false;\n}\n","import type { ElurTemplate } from \"@elurjs/core\";\nimport { renderToString as renderCoreTemplate } from \"@elurjs/core/server\";\nimport { setSSR } from \"./ssr-flag\";\n\n// --- Build-time / server rendering ---\n//\n// The Elur core ships a DOM-free `renderToString` (`@elurjs/core/server`)\n// that streams template output without ever touching a `document`. The kit used\n// to inject a Node-side DOM (happy-dom) as a fallback for legacy compatibility;\n// that fallback has been removed together with the happy-dom dependency.\n\n/**\n * Renders a Elur template to an HTML string in Node.\n *\n * Accepts a *factory* (not a template) because `html`` evaluates at call time.\n *\n * @param factory Thunk that builds the template, e.g. `() => Page({ data })`.\n * @returns Serialized HTML of the rendered template.\n */\nexport async function renderToString(\n factory: () => ElurTemplate,\n options: { markers?: \"none\" | \"hydration\" } = {},\n): Promise<string> {\n setSSR(true);\n try {\n return await renderCoreTemplate(factory(), {\n markers: options.markers ?? \"hydration\",\n });\n } finally {\n setSSR(false);\n }\n}\n","//\n// The <!DOCTYPE>, <head> and <body> wrapper — plus the serialized loader data\n// and the client entry — are injected here at build time.\n\nimport type { PageMetadata } from \"../types.js\";\nexport interface ShellOptions {\n /** Rendered inner HTML that goes inside `#app`. */\n body: string;\n /** `<title>` text. */\n title?: string;\n /** `<html lang>` attribute. */\n lang?: string;\n /** Additional attributes for the `<html>` element, e.g. `{ \"data-theme\": \"dark\" }`. */\n htmlAttributes?: Record<string, string>;\n /**\n * Inline scripts injected into `<head>`. They run synchronously while the\n * document parses — before the first paint and before the (deferred) client\n * bundle — so they are the right place for no-flash bootstrapping (e.g.\n * applying a stored theme before the page becomes visible).\n */\n headScripts?: string[];\n /**\n * Raw HTML strings injected into `<head>` — e.g. `<link rel=\"icon\">`,\n * `<link rel=\"manifest\">`, `<meta name=\"theme-color\">`. Each string is\n * rendered as-is inside `<head>`.\n */\n headLinks?: string[];\n /** Loader data serialized into `<script id=\"elur-data\">`. */\n data?: unknown;\n /** Per-page action names serialized into `<script id=\"elur-actions\">`. */\n actions?: Record<string, string[]>;\n /** Path to the client entry module, e.g. `/_elur/entry-client.js`. */\n clientEntry?: string;\n /** Page metadata emitted as `<meta>`, `<link>` and OG/Twitter tags in `<head>`. */\n metadata?: PageMetadata;\n /**\n * Whether the SSR render endpoint (`/__elur-js/render`) is available at\n * runtime. Defaults to `true`. When `false` (static deployments), the shell\n * emits `<meta name=\"elur:render-endpoint\" content=\"off\" />` so the client\n * router skips probing the endpoint entirely — preventing a storm of 404\n * requests on fully static sites.\n */\n renderEndpoint?: boolean;\n}\n\nconst HTML_ESCAPES: Record<string, string> = {\n \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n '\"': \"&quot;\",\n \"'\": \"&#39;\",\n};\n\nfunction escapeHtml(value: string): string {\n return value.replace(/[&<>\"']/g, (c) => HTML_ESCAPES[c]);\n}\n\n/**\n * Serializes data for embedding inside a `<script>` tag. Escapes `<` so a\n * `</script>` sequence in the data cannot break out of the tag.\n */\nfunction serializeData(data: unknown): string {\n return JSON.stringify(data ?? null).replace(/</g, \"\\\\u003c\");\n}\n\n/**\n * Builds the `<head>` tags for a `PageMetadata` object. Every tag is marked with\n * `data-elur-head` so the client-side router can replace them on navigation\n * without touching charset/viewport or user-supplied `headScripts`.\n */\nexport function buildHeadTags(metadata: PageMetadata, fallbackTitle: string): string {\n const tags: string[] = [];\n const title = metadata.title ?? fallbackTitle;\n if (metadata.title) {\n tags.push(`<title data-elur-head>${escapeHtml(title)}</title>`);\n }\n\n if (metadata.description) {\n tags.push(`<meta data-elur-head name=\"description\" content=\"${escapeHtml(metadata.description)}\" />`);\n }\n\n if (metadata.canonical) {\n tags.push(`<link data-elur-head rel=\"canonical\" href=\"${escapeHtml(metadata.canonical)}\" />`);\n }\n\n if (metadata.robots) {\n tags.push(`<meta data-elur-head name=\"robots\" content=\"${escapeHtml(metadata.robots)}\" />`);\n }\n\n const og = metadata.openGraph;\n if (og) {\n if (og.type) tags.push(`<meta data-elur-head property=\"og:type\" content=\"${escapeHtml(og.type)}\" />`);\n tags.push(`<meta data-elur-head property=\"og:title\" content=\"${escapeHtml(og.title ?? title)}\" />`);\n if (og.description ?? metadata.description) {\n tags.push(`<meta data-elur-head property=\"og:description\" content=\"${escapeHtml(og.description ?? metadata.description!)}\" />`);\n }\n if (og.url ?? metadata.canonical) {\n tags.push(`<meta data-elur-head property=\"og:url\" content=\"${escapeHtml(og.url ?? metadata.canonical!)}\" />`);\n }\n if (og.image) tags.push(`<meta data-elur-head property=\"og:image\" content=\"${escapeHtml(og.image)}\" />`);\n if (og.image && og.imageAlt) tags.push(`<meta data-elur-head property=\"og:image:alt\" content=\"${escapeHtml(og.imageAlt)}\" />`);\n if (og.image && og.imageWidth) tags.push(`<meta data-elur-head property=\"og:image:width\" content=\"${String(og.imageWidth)}\" />`);\n if (og.image && og.imageHeight) tags.push(`<meta data-elur-head property=\"og:image:height\" content=\"${String(og.imageHeight)}\" />`);\n if (og.image && og.imageType) tags.push(`<meta data-elur-head property=\"og:image:type\" content=\"${escapeHtml(og.imageType)}\" />`);\n if (og.siteName) tags.push(`<meta data-elur-head property=\"og:site_name\" content=\"${escapeHtml(og.siteName)}\" />`);\n if (og.locale) tags.push(`<meta data-elur-head property=\"og:locale\" content=\"${escapeHtml(og.locale)}\" />`);\n }\n\n const tw = metadata.twitter;\n if (tw) {\n if (tw.card) tags.push(`<meta data-elur-head name=\"twitter:card\" content=\"${escapeHtml(tw.card)}\" />`);\n if (tw.title ?? title) tags.push(`<meta data-elur-head name=\"twitter:title\" content=\"${escapeHtml(tw.title ?? title)}\" />`);\n if (tw.description ?? metadata.description) {\n tags.push(`<meta data-elur-head name=\"twitter:description\" content=\"${escapeHtml(tw.description ?? metadata.description!)}\" />`);\n }\n if (tw.image) tags.push(`<meta data-elur-head name=\"twitter:image\" content=\"${escapeHtml(tw.image)}\" />`);\n if (tw.image && tw.imageAlt) tags.push(`<meta data-elur-head name=\"twitter:image:alt\" content=\"${escapeHtml(tw.imageAlt)}\" />`);\n }\n\n if (metadata.other) {\n for (const [name, content] of Object.entries(metadata.other)) {\n tags.push(`<meta data-elur-head name=\"${escapeHtml(name)}\" content=\"${escapeHtml(content)}\" />`);\n }\n }\n\n return tags.map((t) => `\\n ${t}`).join(\"\");\n}\n\n/** Wraps rendered body HTML into a full HTML document. */\nexport function documentShell(opts: ShellOptions): string {\n const { body, title = \"Elur Kit App\", lang = \"es\", data, actions, clientEntry, htmlAttributes, headScripts, headLinks, metadata } = opts;\n\n const dataScript =\n data !== undefined\n ? `\\n <script type=\"application/json\" id=\"elur-data\">${serializeData(data)}</script>`\n : \"\";\n\n const actionsScript = actions && Object.keys(actions).length > 0\n ? `\\n <script type=\"application/json\" id=\"elur-actions\">${serializeData(actions)}</script>`\n : \"\";\n\n const entryScript = clientEntry\n ? `\\n <script type=\"module\" src=\"${escapeHtml(clientEntry)}\"></script>`\n : \"\";\n\n const htmlAttrs = htmlAttributes\n ? Object.entries(htmlAttributes)\n .filter(([, value]) => value !== undefined && value !== null && value !== \"\")\n .map(([key, value]) => ` ${escapeHtml(key)}=\"${escapeHtml(String(value))}\"`)\n .join(\"\")\n : \"\";\n\n const headScriptsHtml = headScripts\n ? headScripts\n .filter((script) => typeof script === \"string\" && script.trim().length > 0)\n .map((script) => {\n // If the script is already a complete <script> tag (e.g. JSON-LD),\n // render it as-is without wrapping.\n if (script.trimStart().startsWith(\"<script\")) {\n return `\\n ${script}`;\n }\n return `\\n <script>${script.replace(/<\\/script>/gi, \"<\\\\/script>\")}</script>`;\n })\n .join(\"\")\n : \"\";\n\n const headTags = metadata ? buildHeadTags(metadata, title) : \"\";\n const titleTag = metadata?.title\n ? \"\" // already emitted by buildHeadTags\n : `\\n <title>${escapeHtml(title)}</title>`;\n\n const headLinksHtml = headLinks\n ? headLinks\n .filter((link) => typeof link === \"string\" && link.trim().length > 0)\n .map((link) => `\\n ${link}`)\n .join(\"\")\n : \"\";\n\n const renderEndpointMeta =\n opts.renderEndpoint === false\n ? '\\n <meta name=\"elur:render-endpoint\" content=\"off\" />'\n : \"\";\n\n return `<!DOCTYPE html>\n<html lang=\"${escapeHtml(lang)}\"${htmlAttrs}>\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />${renderEndpointMeta}${titleTag}${headTags}${headLinksHtml}${headScriptsHtml}\n </head>\n <body>\n <div id=\"app\">${body}</div>${dataScript}${actionsScript}${entryScript}\n </body>\n</html>\n`;\n}\n","// --- Ephemeral action error store ---\n//\n// Action failures submitted via plain HTML forms (progressive enhancement)\n// need to be relayed back to the page so the user sees validation errors.\n//\n// Previously the failure data was serialized into a `?__elur_js_action_error=`\n// query param on the redirect. That leaks errors into browser history,\n// server logs and third-party Referer headers.\n//\n// Now we stash the failure in a short-lived in-memory store keyed by a random\n// id, set a small cookie `__elur_js_action_error=<id>` (Max-Age=15s, SameSite=Lax),\n// and the next render reads the cookie, fetches the payload, exposes it as\n// `props.form`, and clears the entry.\n//\n// The store is process-local, which is fine for the single-process SSR server\n// and the dev server. For multi-instance deployments the cookie carries the\n// payload directly when it fits (see `encodeActionErrorCookie`); the store is\n// only the overflow path for large payloads.\n\nimport { createHmac, randomBytes, timingSafeEqual } from \"node:crypto\";\n\nconst COOKIE_NAME = \"__elur_js_action_error\";\nconst MAX_COOKIE_SIZE = 3500; // bytes; leaves headroom under the 4KB cookie limit\nconst TTL_MS = 15_000;\n\n// HMAC key for signing action error cookies. In production this should be\n// set via ELUR_JS_ACTION_SECRET env var; otherwise we derive a per-process\n// key (sufficient for single-process dev/preview, but NOT for multi-instance).\nconst ACTION_SECRET =\n process.env.ELUR_JS_ACTION_SECRET ?? randomBytes(32).toString(\"hex\");\n\ninterface StoredError {\n data: unknown;\n status: number;\n expiresAt: number;\n}\n\nconst store = new Map<string, StoredError>();\n\n// Periodically purge expired entries so the map does not grow unbounded.\nlet sweepScheduled = false;\nfunction scheduleSweep(): void {\n if (sweepScheduled) return;\n sweepScheduled = true;\n setTimeout(() => {\n sweepScheduled = false;\n const now = Date.now();\n for (const [key, entry] of store) {\n if (entry.expiresAt <= now) store.delete(key);\n }\n }, TTL_MS).unref?.();\n}\n\n/**\n * Signs a payload with HMAC-SHA256 using the action secret.\n * Returns `signature.payload` (both hex/base64url).\n */\nfunction sign(payload: string): string {\n const sig = createHmac(\"sha256\", ACTION_SECRET).update(payload).digest(\"hex\");\n return `${sig}.${payload}`;\n}\n\n/**\n * Verifies a signed value and returns the payload if valid, or undefined.\n * Uses timingSafeEqual to prevent timing attacks.\n */\nfunction verify(value: string): string | undefined {\n const dotIndex = value.indexOf(\".\");\n if (dotIndex === -1) return undefined;\n const sig = value.slice(0, dotIndex);\n const payload = value.slice(dotIndex + 1);\n const expectedSig = createHmac(\"sha256\", ACTION_SECRET).update(payload).digest(\"hex\");\n if (sig.length !== expectedSig.length) return undefined;\n try {\n if (timingSafeEqual(Buffer.from(sig), Buffer.from(expectedSig))) {\n return payload;\n }\n } catch {\n // Length mismatch — invalid.\n }\n return undefined;\n}\n\n/**\n * Encodes an action failure for the redirect cookie. When the payload fits\n * inside the cookie limit, it is embedded directly as a signed base64url JSON\n * value. When it is too large, it is stored in memory and only a short signed\n * id is written to the cookie.\n *\n * The cookie is signed with HMAC-SHA256 to prevent forgery (A-20).\n *\n * @returns The cookie value to set on the redirect response.\n */\nexport function encodeActionErrorCookie(\n data: unknown,\n status: number,\n): { value: string; storeId?: string } {\n const payload = JSON.stringify({ d: data, s: status });\n const encoded = Buffer.from(payload, \"utf8\").toString(\"base64url\");\n const signed = sign(encoded);\n if (signed.length <= MAX_COOKIE_SIZE) {\n return { value: signed };\n }\n\n // Overflow: stash in memory and reference by signed id.\n const id = randomBytes(12).toString(\"hex\");\n store.set(id, { data, status, expiresAt: Date.now() + TTL_MS });\n scheduleSweep();\n return { value: sign(`id:${id}`), storeId: id };\n}\n\n/**\n * Decodes a cookie value (previously produced by `encodeActionErrorCookie`)\n * into the failure payload. Verifies the HMAC signature first, then resolves\n * in-memory overflow entries and deletes them after reading.\n */\nexport function decodeActionErrorCookie(value: string | undefined | null):\n | { data: unknown; status: number }\n | undefined {\n if (!value) return undefined;\n\n // Verify signature first.\n const verifiedPayload = verify(value);\n if (verifiedPayload === undefined) return undefined;\n\n // Check if it's an in-memory store reference.\n if (verifiedPayload.startsWith(\"id:\")) {\n const id = verifiedPayload.slice(3);\n const entry = store.get(id);\n if (!entry) return undefined;\n store.delete(id);\n if (entry.expiresAt <= Date.now()) return undefined;\n return { data: entry.data, status: entry.status };\n }\n\n try {\n const json = Buffer.from(verifiedPayload, \"base64url\").toString(\"utf8\");\n const parsed = JSON.parse(json) as { d: unknown; s: number };\n return { data: parsed.d, status: parsed.s };\n } catch {\n return undefined;\n }\n}\n\n/** Name of the cookie used to relay action errors. */\nexport const ACTION_ERROR_COOKIE = COOKIE_NAME;\n\n/** Builds the Set-Cookie header value that clears the error cookie. */\nexport function clearActionErrorCookieHeader(): string {\n return `${COOKIE_NAME}=; Path=/; Max-Age=0; SameSite=Lax`;\n}\n\n/** Builds the Set-Cookie header value that sets the error cookie. */\nexport function setActionErrorCookieHeader(value: string): string {\n return `${COOKIE_NAME}=${value}; Path=/; Max-Age=15; SameSite=Lax; HttpOnly`;\n}\n","// --- Cache policy per route (runtime-security §9.1) ---\n//\n// Authors can declare a cache policy in their page.data.ts:\n//\n// export const cache = {\n// mode: \"public\", // \"public\" | \"private\" | \"dynamic\"\n// revalidate: 60, // seconds\n// tags: [\"products\"], // for tag-based invalidation\n// };\n//\n// Default policy: \"dynamic\" (no public ISR caching).\n// Requests with Cookie/Authorization are never cached publicly.\n// Responses with Set-Cookie/private/no-store are never cached publicly.\n\n/** Cache mode for a route. */\nexport type CacheMode = \"public\" | \"private\" | \"dynamic\";\n\n/** Cache policy declared by the route's data module. */\nexport interface CachePolicy {\n mode: CacheMode;\n revalidate: number;\n tags?: string[];\n}\n\n/** Default cache policy when none is declared. */\nexport const DEFAULT_CACHE_POLICY: CachePolicy = {\n mode: \"dynamic\",\n revalidate: 0,\n};\n\n/**\n * Normalizes a raw cache export from a data module into a CachePolicy.\n * Returns the default policy if the input is invalid or missing.\n */\nexport function normalizeCachePolicy(raw: unknown): CachePolicy {\n if (!raw || typeof raw !== \"object\") return DEFAULT_CACHE_POLICY;\n const obj = raw as Record<string, unknown>;\n const mode = obj.mode;\n if (mode !== \"public\" && mode !== \"private\" && mode !== \"dynamic\") {\n return DEFAULT_CACHE_POLICY;\n }\n const revalidate = typeof obj.revalidate === \"number\" ? obj.revalidate : 0;\n const tags = Array.isArray(obj.tags) ? obj.tags.filter((t) => typeof t === \"string\") : undefined;\n return { mode, revalidate, tags };\n}\n\n/**\n * Determines whether a route's cache policy allows public caching for the\n * given request.\n *\n * Per §9.1:\n * - \"dynamic\" → never cache\n * - \"private\" → never cache publicly (requires private adapter)\n * - \"public\" → cache only if request has no Cookie/Authorization\n */\nexport function shouldCachePublic(\n policy: CachePolicy,\n request: Request,\n): boolean {\n if (policy.mode !== \"public\") return false;\n if (policy.revalidate <= 0) return false;\n if (request.headers.get(\"Cookie\")) return false;\n if (request.headers.get(\"Authorization\")) return false;\n return true;\n}\n","import type { ElurTemplate } from \"@elurjs/core\";\nimport { renderToString } from \"../render/render-to-string.js\";\nimport { documentShell, buildHeadTags } from \"../build/document-shell.js\";\nimport type { PageRoute, ScannedRoutes } from \"../router/route-scanner.js\";\nimport type { BuildConfig } from \"../build/build.js\";\nimport type { PageDataLoad, PageProps, RouteParams, PageMetadata, GenerateMetadata } from \"../types.js\";\nimport { existsSync } from \"node:fs\";\nimport { decodeActionErrorCookie, ACTION_ERROR_COOKIE } from \"../action/error-store.js\";\nimport { normalizeCachePolicy, type CachePolicy } from \"../cache/policy.js\";\n\nexport interface RenderPageOptions {\n route: PageRoute;\n params?: RouteParams;\n searchParams?: URLSearchParams;\n config: Pick<BuildConfig, \"lang\" | \"clientEntry\" | \"renderEndpoint\">;\n /** Custom module loader. Defaults to native dynamic import. */\n importer?: (path: string) => Promise<unknown>;\n /** Per-page action names exposed in the HTML shell. */\n actions?: Record<string, string[]>;\n /** Current request, used to hydrate data loaders that need cookies/headers. */\n request?: Request;\n}\n\nexport interface RenderPageResult {\n html: string;\n revalidate?: number;\n /**\n * `Set-Cookie` header value that clears the action error cookie, when the\n * page consumed a relayed action failure. The SSR server should append it to\n * the outgoing response so the cookie does not persist.\n */\n clearActionErrorCookie?: string;\n /** `<head>` tags (title, meta, OG, twitter) for the SPA router to merge. */\n head?: string;\n /** Resolved page title (from metadata or fallback). */\n resolvedTitle?: string;\n /**\n * When a loader or layout throws a `Response` (e.g. `throw new Response(...,\n * { status: 404 })`), it is captured here as a first-class response instead\n * of being treated as an internal error (A-22).\n */\n response?: Response;\n /** HTTP status code for the rendered page (e.g. 404 for not-found pages). */\n status?: number;\n /** Cache policy declared by the route (§9.1). */\n cachePolicy?: CachePolicy;\n}\n\nconst defaultImport = (path: string) => import(path);\n\n/**\n * Collects `<html>` attributes and head scripts declared by data loaders\n * (page and layouts) via top-level `htmlAttributes` / `headScripts` fields.\n */\nexport function collectShellExtras(\n pageData: unknown,\n layoutDataList: unknown[],\n): { htmlAttributes: Record<string, string>; headScripts: string[]; headLinks: string[] } {\n const htmlAttributes: Record<string, string> = {};\n const headScripts: string[] = [];\n const headLinks: string[] = [];\n const merge = (value: unknown) => {\n if (!value || typeof value !== \"object\") return;\n const attrs = (value as { htmlAttributes?: Record<string, string> }).htmlAttributes;\n if (attrs) Object.assign(htmlAttributes, attrs);\n const scripts = (value as { headScripts?: string[] }).headScripts;\n if (Array.isArray(scripts)) headScripts.push(...scripts);\n const links = (value as { headLinks?: string[] }).headLinks;\n if (Array.isArray(links)) headLinks.push(...links);\n };\n for (const layoutData of layoutDataList) merge(layoutData);\n merge(pageData);\n // Deduplicate headScripts and headLinks (e.g. from both layout and page data)\n const uniqueScripts = [...new Set(headScripts)];\n const uniqueLinks = [...new Set(headLinks)];\n return { htmlAttributes, headScripts: uniqueScripts, headLinks: uniqueLinks };\n}\n\nexport async function renderPage(options: RenderPageOptions): Promise<RenderPageResult> {\n const { route, params = {}, searchParams = new URLSearchParams(), config, importer = defaultImport, actions, request } = options;\n\n const pageModule = await importer(route.pagePath) as {\n default: (props: PageProps<unknown>) => ElurTemplate;\n generateMetadata?: GenerateMetadata;\n };\n const { default: PageComponent, generateMetadata } = pageModule;\n\n let data: unknown;\n let revalidate: number | undefined;\n let cachePolicy: import(\"../cache/policy.js\").CachePolicy | undefined;\n // Use a mutable container so TypeScript doesn't narrow the type after\n // the first `if (thrownResponse)` check.\n const thrown: { response: Response | undefined } = { response: undefined };\n if (route.dataPath) {\n const mod = await importer(route.dataPath) as {\n load?: PageDataLoad;\n revalidate?: number;\n cache?: unknown;\n };\n if (mod.load) {\n try {\n data = await mod.load({ params, searchParams, request });\n } catch (err) {\n if (err instanceof Response) {\n thrown.response = err;\n } else {\n throw err;\n }\n }\n }\n if (typeof mod.revalidate === \"number\") {\n revalidate = mod.revalidate;\n }\n // Read cache policy from the data module (§9.1).\n if (mod.cache) {\n cachePolicy = normalizeCachePolicy(mod.cache);\n if (cachePolicy.revalidate > 0) {\n revalidate = cachePolicy.revalidate;\n }\n }\n }\n\n // If a loader threw a Response (redirect, 404, etc.), return it as a\n // first-class response instead of rendering the page (A-22).\n if (thrown.response) {\n return { html: \"\", response: thrown.response, status: thrown.response.status };\n }\n\n // Relay an action failure previously stored in the ephemeral cookie so the\n // page can render validation errors via `props.form`. The cookie is cleared\n // on the outgoing response (see `clearActionErrorCookie` in the result).\n let form: unknown;\n let clearActionErrorCookie: string | undefined;\n if (request) {\n const cookieHeader = request.headers.get(\"Cookie\") ?? \"\";\n const match = cookieHeader.match(new RegExp(`(?:^|;\\\\s*)${ACTION_ERROR_COOKIE}=([^;]+)`));\n if (match) {\n const decoded = decodeActionErrorCookie(match[1]);\n if (decoded) {\n form = { __elur_js_action_error: true, status: decoded.status, data: decoded.data };\n clearActionErrorCookie = `${ACTION_ERROR_COOKIE}=; Path=/; Max-Age=0; SameSite=Lax`;\n }\n }\n }\n\n const props: PageProps<unknown> = {\n data: data ?? {},\n params,\n searchParams,\n form,\n };\n\n const layoutModules = await Promise.all(\n route.layouts.map(async (layoutPath) => importer(layoutPath)),\n );\n const layoutDataList = await Promise.all(\n route.layouts.map(async (layoutPath) => {\n const dataPath = layoutPath.replace(/layout\\.ts$/, \"layout.data.ts\");\n if (!existsSync(dataPath)) return undefined;\n const mod = (await importer(dataPath)) as { load?: PageDataLoad };\n if (mod.load) {\n try {\n return await mod.load({ params, searchParams, request });\n } catch (err) {\n if (err instanceof Response) {\n thrown.response = err;\n return undefined;\n }\n throw err;\n }\n }\n return undefined;\n }),\n );\n\n // If a layout loader threw a Response, return it as first-class (A-22).\n const layoutThrown = thrown.response as Response | undefined;\n if (layoutThrown) {\n return { html: \"\", response: layoutThrown, status: layoutThrown.status };\n }\n\n // Load slot modules if the route has them (v2.1 — Fix #2: Layout Slots).\n let slotTemplates: Record<string, ElurTemplate> | undefined;\n if (route.slots) {\n slotTemplates = {};\n for (const [slotName, slotPath] of Object.entries(route.slots)) {\n const slotMod = await importer(slotPath) as { default: (props: PageProps<unknown>) => ElurTemplate };\n slotTemplates[slotName] = slotMod.default(props);\n }\n }\n\n const body = await renderToString(() => {\n let template = PageComponent(props);\n for (let i = layoutModules.length - 1; i >= 0; i--) {\n const { default: Layout } = layoutModules[i] as {\n default: (props: { children: ElurTemplate; data?: unknown; slots?: Record<string, ElurTemplate> }) => ElurTemplate;\n };\n template = Layout({ children: template, data: layoutDataList[i], slots: slotTemplates });\n }\n return template;\n });\n\n const title = typeof data === \"object\" && data && \"title\" in data\n ? String((data as { title?: unknown }).title ?? \"Elur Kit\")\n : \"Elur Kit\";\n\n const { htmlAttributes, headScripts, headLinks } = collectShellExtras(data, layoutDataList);\n\n // Resolve page metadata. Priority: `generateMetadata` from page.ts > `metadata`\n // field in the page loader data > `metadata` field in layout loader data.\n let metadata: PageMetadata | undefined;\n if (typeof generateMetadata === \"function\") {\n metadata = await generateMetadata({ params, searchParams, request, data });\n }\n if (!metadata) {\n metadata = extractMetadata(data) ?? extractMetadataFromList(layoutDataList);\n }\n // The title from metadata takes precedence over the data.title fallback.\n const resolvedTitle = metadata?.title ?? title;\n\n const html = documentShell({\n title: resolvedTitle,\n lang: config.lang,\n body,\n data,\n actions,\n htmlAttributes,\n headScripts,\n headLinks,\n metadata,\n clientEntry: config.clientEntry,\n renderEndpoint: config.renderEndpoint,\n });\n\n const head = metadata ? buildHeadTags(metadata, resolvedTitle) : \"\";\n return { html, revalidate, clearActionErrorCookie, head, resolvedTitle, cachePolicy };\n}\n\n/** Extracts a `metadata` field from a loader data object, if present. */\nfunction extractMetadata(value: unknown): PageMetadata | undefined {\n if (value && typeof value === \"object\" && \"metadata\" in value) {\n const meta = (value as { metadata?: unknown }).metadata;\n if (meta && typeof meta === \"object\") return meta as PageMetadata;\n }\n return undefined;\n}\n\n/** Extracts metadata from the first layout data object that has one. */\nfunction extractMetadataFromList(list: unknown[]): PageMetadata | undefined {\n for (const item of list) {\n const meta = extractMetadata(item);\n if (meta) return meta;\n }\n return undefined;\n}\n\nexport interface RenderErrorPageOptions {\n routes: ScannedRoutes;\n status: 404 | 500;\n error?: unknown;\n config: Pick<BuildConfig, \"lang\" | \"clientEntry\" | \"renderEndpoint\">;\n actions?: Record<string, string[]>;\n importer?: (path: string) => Promise<unknown>;\n}\n\nexport async function renderErrorPage(\n options: RenderErrorPageOptions,\n): Promise<{ html: string; status: number } | undefined> {\n const route = options.status === 404 ? options.routes.error404 : options.routes.error500;\n if (!route) return undefined;\n\n try {\n const { html } = await renderPage({\n route,\n params: {},\n searchParams: new URLSearchParams(),\n config: options.config,\n actions: options.actions,\n importer: options.importer,\n });\n return { html, status: options.status };\n } catch (err) {\n console.error(`[render] error ${options.status} page failed`, err);\n return undefined;\n }\n}\n","import type { ApiRoute, PageRoute } from \"../router/route-scanner.js\";\n\nexport interface MatchResult {\n route: PageRoute;\n params: Record<string, string | string[]>;\n searchParams: URLSearchParams;\n}\n\n/**\n * Match a request pathname against a list of page routes.\n *\n * Routes are sorted by specificity (static > dynamic > catch-all) before\n * matching, so `/about` wins over `/:slug` even if the catch-all appears first.\n *\n * URL segments are safely decoded (plan §11.1, runtime-security §10).\n */\nexport function matchRoute(\n pathname: string,\n routes: PageRoute[],\n): MatchResult | undefined {\n const cleanPath = pathname.split(\"?\")[0];\n const requestSegments = cleanPath.split(\"/\").filter(Boolean).map(safeDecodeURIComponent);\n\n const sorted = [...routes].sort((a, b) => specificity(b.path) - specificity(a.path));\n\n for (const route of sorted) {\n const routeSegments = route.path.split(\"/\").filter(Boolean);\n const match = tryMatch(requestSegments, routeSegments, route.optionalCatchAll);\n if (match) {\n return { route, params: match, searchParams: new URLSearchParams() };\n }\n }\n\n return undefined;\n}\n\nexport interface ApiMatchResult<T = ApiRoute> {\n route: T;\n params: Record<string, string | string[]>;\n}\n\n/**\n * Match a request pathname against a list of API routes.\n */\nexport function matchApiRoute<T extends { path: string }>(pathname: string, routes: T[]): ApiMatchResult<T> | undefined {\n const cleanPath = pathname.split(\"?\")[0];\n const requestSegments = cleanPath.split(\"/\").filter(Boolean).map(safeDecodeURIComponent);\n\n const sorted = [...routes].sort((a, b) => specificity(b.path) - specificity(a.path));\n\n for (const route of sorted) {\n const routeSegments = route.path.split(\"/\").filter(Boolean);\n const match = tryMatch(requestSegments, routeSegments);\n if (match) {\n return { route, params: match };\n }\n }\n\n return undefined;\n}\n\n/**\n * Safely decodes a URI component. If decoding fails (malformed % sequences),\n * returns the original string rather than throwing (runtime-security §10).\n */\nfunction safeDecodeURIComponent(segment: string): string {\n try {\n return decodeURIComponent(segment);\n } catch {\n return segment;\n }\n}\n\nfunction specificity(path: string): number {\n return path.split(\"/\").filter(Boolean).reduce((score, segment) => {\n if (segment.endsWith(\"*\")) return score;\n if (segment.startsWith(\":\")) return score + 1;\n return score + 2;\n }, 0);\n}\n\nfunction tryMatch(\n requestSegments: string[],\n routeSegments: string[],\n optionalCatchAll = false,\n): Record<string, string | string[]> | undefined {\n const params: Record<string, string | string[]> = {};\n\n let i = 0;\n for (let r = 0; r < routeSegments.length; r++) {\n const routeSeg = routeSegments[r];\n\n if (routeSeg.endsWith(\"*\")) {\n // Catch-all consumes the rest of the request segments.\n const name = routeSeg.slice(1, -1);\n const rest = requestSegments.slice(i);\n // For optional catch-all, empty rest is OK.\n if (rest.length === 0 && !optionalCatchAll) return undefined;\n params[name] = rest.length > 0 ? rest : [];\n return params;\n }\n\n if (routeSeg.startsWith(\":\")) {\n const requestSeg = requestSegments[i];\n if (requestSeg === undefined) return undefined;\n params[routeSeg.slice(1)] = requestSeg;\n i++;\n continue;\n }\n\n if (routeSeg !== requestSegments[i]) {\n return undefined;\n }\n i++;\n }\n\n if (i !== requestSegments.length) return undefined;\n return params;\n}\n","// --- Origin verification (CSRF protection for server actions) ---\n//\n// Server actions accept POST requests from the browser. Without origin\n// verification, any third-party site could submit forged requests to\n// `/__elur-js/actions` on behalf of a logged-in user (CSRF).\n//\n// Strategy: compare the request's `Origin` (or `Referer` fallback) host against\n// the target `Host` header. Same-origin requests pass; cross-origin requests\n// are rejected with 403 unless the origin is explicitly allow-listed.\n//\n// Requests without `Origin` AND without `Referer` (e.g. curl, server-to-server)\n// are accepted by default for DX, unless `strictOrigin: true` is configured.\n\nexport interface OriginCheckOptions {\n /** Extra origins allowed to call actions (e.g. preview deployments). */\n allowedOrigins?: string[];\n /**\n * When true, requests missing both `Origin` and `Referer` are rejected.\n * Defaults to false so curl/server-to-server calls keep working.\n */\n strictOrigin?: boolean;\n}\n\n/**\n * Returns the host:port of a URL string, or undefined if it cannot be parsed.\n */\nfunction originOf(urlString: string | null | undefined): string | undefined {\n if (!urlString) return undefined;\n try {\n const url = new URL(urlString);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return undefined;\n return url.origin;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Verifies that a request originates from the same host (or an allow-listed\n * origin). Returns an error message when the request must be rejected, or\n * undefined when it is allowed.\n *\n * @param request The incoming Request to actions.\n * @param options Origin check configuration.\n */\nexport function verifyOrigin(\n request: Request,\n options: OriginCheckOptions = {},\n): string | undefined {\n const targetOrigin = originOf(request.url);\n if (!targetOrigin) return \"Invalid target URL\";\n\n const origin = request.headers.get(\"Origin\");\n const referer = request.headers.get(\"Referer\");\n if (!origin && !referer) {\n return options.strictOrigin\n ? \"Missing Origin and Referer headers\"\n : undefined;\n }\n\n const sourceOrigin = origin ? originOf(origin) : originOf(referer);\n if (!sourceOrigin) return origin ? \"Invalid Origin header\" : \"Invalid Referer header\";\n if (sourceOrigin === targetOrigin) return undefined;\n\n if (options.allowedOrigins?.some((allowed) => originOf(allowed) === sourceOrigin)) return undefined;\n\n return `Cross-origin request blocked: source \"${sourceOrigin}\" != target \"${targetOrigin}\"`;\n}\n\n/** Builds a 403 Response for a rejected origin. */\nexport function originForbidden(message: string): Response {\n return new Response(message, {\n status: 403,\n headers: { \"Content-Type\": \"text/plain; charset=utf-8\" },\n });\n}\n","import type { ActionRequest } from \"./index.js\";\nimport { isActionFailure, isRedirectResponse, publicErrorResponse } from \"../errors.js\";\nimport { verifyOrigin, originForbidden, type OriginCheckOptions } from \"./origin.js\";\nimport {\n encodeActionErrorCookie,\n setActionErrorCookieHeader,\n} from \"./error-store.js\";\n\n/**\n * Resolves a server action by name and optional page scope.\n */\nexport type ActionResolver = (\n name: string,\n page?: string,\n) => Promise<((...args: unknown[]) => unknown) | undefined>;\n\n/** Options shared by `handleActionRequest` callers for CSRF protection. */\nexport interface ActionSecurityOptions extends OriginCheckOptions {\n /** Maximum body size in bytes. Defaults to 1MB (1_048_576). */\n bodyLimit?: number;\n}\n\n/** Default body size limit: 1MB. */\nconst DEFAULT_BODY_LIMIT = 1_048_576;\n\n/**\n * Reads the request body as text, enforcing a maximum size.\n * Returns a 413 response if the body exceeds the limit.\n */\nasync function readBodyWithLimit(\n request: Request,\n limit: number,\n): Promise<{ ok: true; text: string } | { ok: false; response: Response }> {\n const contentLength = request.headers.get(\"Content-Length\");\n if (contentLength && parseInt(contentLength, 10) > limit) {\n return {\n ok: false,\n response: new Response(\"Request body too large\", {\n status: 413,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n // Read the body as a stream with a size cap to prevent memory exhaustion\n // from chunked transfer encoding without Content-Length.\n const reader = request.body?.getReader();\n if (!reader) {\n return { ok: true, text: \"\" };\n }\n const chunks: Uint8Array[] = [];\n let totalSize = 0;\n try {\n for (; ;) {\n const { done, value } = await reader.read();\n if (done) break;\n totalSize += value.byteLength;\n if (totalSize > limit) {\n try { reader.cancel(); } catch { /* ignore */ }\n return {\n ok: false,\n response: new Response(\"Request body too large\", {\n status: 413,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n chunks.push(value);\n }\n } finally {\n try { reader.releaseLock(); } catch { /* ignore */ }\n }\n const total = new Uint8Array(totalSize);\n let offset = 0;\n for (const chunk of chunks) {\n total.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return { ok: true, text: new TextDecoder().decode(total) };\n}\n\nfunction parseFormBody(body: string): Record<string, unknown> {\n const params = new URLSearchParams(body);\n const result: Record<string, unknown> = {};\n for (const [key, value] of params) {\n if (result[key] === undefined) {\n result[key] = value;\n } else if (Array.isArray(result[key])) {\n (result[key] as unknown[]).push(value);\n } else {\n result[key] = [result[key], value];\n }\n }\n return result;\n}\n\nasync function parseActionRequest(\n request: Request,\n bodyLimit: number = DEFAULT_BODY_LIMIT,\n): Promise<\n | { ok: true; name: string; page?: string; args: unknown[]; wantsJson: boolean }\n | { ok: false; response: Response }\n> {\n if (request.method !== \"POST\") {\n return {\n ok: false,\n response: new Response(\"Method not allowed\", {\n status: 405,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n\n const contentType = request.headers.get(\"Content-Type\") ?? \"\";\n const wantsJson = (request.headers.get(\"Accept\") ?? \"\").includes(\"application/json\");\n\n let name: string | undefined;\n let page: string | undefined;\n let args: unknown[] = [];\n\n if (contentType.includes(\"application/json\")) {\n const bodyResult = await readBodyWithLimit(request, bodyLimit);\n if (!bodyResult.ok) return { ok: false, response: bodyResult.response };\n let body: ActionRequest;\n try {\n body = JSON.parse(bodyResult.text) as ActionRequest;\n } catch {\n return {\n ok: false,\n response: new Response(\"Invalid JSON body\", {\n status: 400,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n name = body.name;\n page = body.page;\n args = Array.isArray(body.args) ? body.args : [];\n } else if (\n contentType.includes(\"application/x-www-form-urlencoded\") ||\n contentType.includes(\"multipart/form-data\")\n ) {\n // For multipart, use the native formData() parser after checking\n // Content-Length against the limit. For urlencoded, use our size-capped\n // reader to handle chunked encoding without Content-Length.\n if (contentType.includes(\"multipart/form-data\")) {\n const contentLength = request.headers.get(\"Content-Length\");\n if (contentLength && parseInt(contentLength, 10) > bodyLimit) {\n return {\n ok: false,\n response: new Response(\"Request body too large\", {\n status: 413,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n let form: FormData;\n try {\n form = await request.formData();\n } catch {\n return {\n ok: false,\n response: new Response(\"Invalid form body\", {\n status: 400,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n name = form.get(\"__elur_js_action_name\") as string | null ?? undefined;\n page = form.get(\"__elur_js_action_page\") as string | null ?? undefined;\n const input: Record<string, unknown> = {};\n for (const [key, value] of form) {\n if (key === \"__elur_js_action_name\" || key === \"__elur_js_action_page\") continue;\n input[key] = value;\n }\n args = [input];\n } else {\n const bodyResult = await readBodyWithLimit(request, bodyLimit);\n if (!bodyResult.ok) return { ok: false, response: bodyResult.response };\n const form = parseFormBody(bodyResult.text);\n name = form.__elur_js_action_name as string | undefined;\n page = form.__elur_js_action_page as string | undefined;\n const input: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(form)) {\n if (key === \"__elur_js_action_name\" || key === \"__elur_js_action_page\") continue;\n input[key] = value;\n }\n args = [input];\n }\n } else {\n // Try to parse a plain form body as a fallback for progressive enhancement.\n const bodyResult = await readBodyWithLimit(request, bodyLimit);\n if (!bodyResult.ok) return { ok: false, response: bodyResult.response };\n const form = parseFormBody(bodyResult.text);\n name = form.__elur_js_action_name as string | undefined;\n page = form.__elur_js_action_page as string | undefined;\n const input: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(form)) {\n if (key === \"__elur_js_action_name\" || key === \"__elur_js_action_page\") continue;\n input[key] = value;\n }\n args = [input];\n }\n\n if (!name || typeof name !== \"string\") {\n return {\n ok: false,\n response: new Response(\"Missing action name\", {\n status: 400,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n\n return { ok: true, name, page, args, wantsJson };\n}\n\n/**\n * Handles a POST request to the server action endpoint.\n *\n * Accepts both JSON requests (`{ name, page?, args }`) and HTML form submissions\n * for progressive enhancement. The provided resolver looks up the action\n * implementation, invokes it with the supplied arguments and returns the result\n * as JSON or redirects back to the request origin for form submissions.\n *\n * Origin verification (CSRF protection) runs before parsing the body: any\n * cross-origin POST is rejected with 403 unless its origin is allow-listed via\n * `security.allowedOrigins`.\n *\n * For progressive-enhancement form submissions that fail, the failure payload\n * is relayed back via a short-lived `__elur_js_action_error` cookie (SameSite=Lax,\n * Max-Age=15s) instead of a query param, so errors do not leak into browser\n * history, server logs or third-party Referer headers.\n */\nexport async function handleActionRequest(\n request: Request,\n resolveAction: ActionResolver,\n security: ActionSecurityOptions = {},\n): Promise<Response> {\n // CSRF: verify same-origin (or allow-listed) before doing any work.\n const originError = verifyOrigin(request, security);\n if (originError) return originForbidden(originError);\n\n const parsed = await parseActionRequest(request, security.bodyLimit ?? DEFAULT_BODY_LIMIT);\n if (!parsed.ok) return parsed.response;\n\n const { name, page, args, wantsJson } = parsed;\n\n try {\n const action = await resolveAction(name, page);\n if (!action) {\n const message = page ? `Action not found: ${name} (page: ${page})` : `Action not found: ${name}`;\n return new Response(message, {\n status: 404,\n headers: { \"Content-Type\": \"text/plain\" },\n });\n }\n\n const result = await action(...args);\n\n if (isActionFailure(result)) {\n if (wantsJson) {\n return new Response(JSON.stringify({ __elur_js_action_failure: true, status: result.status, data: result.data }), {\n status: result.status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n // Progressive enhancement: redirect back with the failure in a cookie.\n const referer = request.headers.get(\"Referer\") ?? \"/\";\n const url = new URL(referer, \"http://localhost\");\n const { value } = encodeActionErrorCookie(result.data, result.status);\n return new Response(null, {\n status: 303,\n headers: {\n Location: url.pathname + url.search,\n \"Content-Type\": \"text/plain\",\n \"Set-Cookie\": setActionErrorCookieHeader(value),\n },\n });\n }\n\n if (isRedirectResponse(result)) {\n if (wantsJson) {\n return new Response(\n JSON.stringify({ __elur_js_action_redirect: true, status: result.status, location: result.location }),\n {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n },\n );\n }\n return new Response(null, {\n status: result.status,\n headers: { Location: result.location, \"Content-Type\": \"text/plain\" },\n });\n }\n\n if (wantsJson) {\n return new Response(JSON.stringify(result ?? null), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n // For progressive enhancement (plain form POST), redirect back.\n const referer = request.headers.get(\"Referer\") ?? \"/\";\n return new Response(null, {\n status: 303,\n headers: {\n Location: typeof result === \"string\" ? result : referer,\n \"Content-Type\": \"text/plain\",\n },\n });\n } catch (err) {\n console.error(\"[elur-kit] Action error:\", err);\n return publicErrorResponse(err, { includeDetail: false });\n }\n}\n\nexport { verifyOrigin, originForbidden, type OriginCheckOptions } from \"./origin.js\";\nexport {\n decodeActionErrorCookie,\n clearActionErrorCookieHeader,\n setActionErrorCookieHeader,\n ACTION_ERROR_COOKIE,\n} from \"./error-store.js\";\n","import type { IncomingMessage } from \"node:http\";\n\n// Capture the global AbortController at module load time so it's immune to\n// test frameworks that replace or delete globalThis.AbortController.\nconst GlobalAbortController =\n (globalThis as { AbortController?: typeof AbortController }).AbortController ?? AbortController;\n\nexport function incomingMessageToRequest(req: IncomingMessage, body?: BodyInit | null): Request {\n const headers = new Headers();\n for (let index = 0; index < req.rawHeaders.length; index += 2) {\n headers.append(req.rawHeaders[index], req.rawHeaders[index + 1]);\n }\n\n const controller = new GlobalAbortController();\n req.once(\"aborted\", () => controller.abort());\n req.once(\"close\", () => {\n if (!req.complete) controller.abort();\n });\n\n const protocol = (req.socket as typeof req.socket & { encrypted?: boolean }).encrypted ? \"https\" : \"http\";\n const init: RequestInit = {\n method: req.method ?? \"GET\",\n headers,\n signal: controller.signal,\n };\n if (body !== undefined && body !== null && init.method !== \"GET\" && init.method !== \"HEAD\") init.body = body;\n\n return new Request(`${protocol}://${headers.get(\"host\") ?? \"localhost\"}${req.url ?? \"/\"}`, init);\n}\n"],"mappings":"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"}
@@ -1,13 +0,0 @@
1
- const e=require("./errors-0X7pCFNU.cjs");let t=require("@elurjs/core/server"),n=require("node:fs"),r=require("node:crypto");var i=Symbol.for(`@elurjs/core/reactivity-state`);function a(){return globalThis[i]}function o(e){let t=a();t&&(t.ssr=e)}function s(){return a()?.ssr??!1}async function c(e,n={}){o(!0);try{return await(0,t.renderToString)(e(),{markers:n.markers??`hydration`})}finally{o(!1)}}var l={"&":`&amp;`,"<":`&lt;`,">":`&gt;`,'"':`&quot;`,"'":`&#39;`};function u(e){return e.replace(/[&<>"']/g,e=>l[e])}function d(e){return JSON.stringify(e??null).replace(/</g,`\\u003c`)}function f(e,t){let n=[],r=e.title??t;e.title&&n.push(`<title data-elur-head>${u(r)}</title>`),e.description&&n.push(`<meta data-elur-head name="description" content="${u(e.description)}" />`),e.canonical&&n.push(`<link data-elur-head rel="canonical" href="${u(e.canonical)}" />`),e.robots&&n.push(`<meta data-elur-head name="robots" content="${u(e.robots)}" />`);let i=e.openGraph;i&&(i.type&&n.push(`<meta data-elur-head property="og:type" content="${u(i.type)}" />`),n.push(`<meta data-elur-head property="og:title" content="${u(i.title??r)}" />`),(i.description??e.description)&&n.push(`<meta data-elur-head property="og:description" content="${u(i.description??e.description)}" />`),(i.url??e.canonical)&&n.push(`<meta data-elur-head property="og:url" content="${u(i.url??e.canonical)}" />`),i.image&&n.push(`<meta data-elur-head property="og:image" content="${u(i.image)}" />`),i.image&&i.imageAlt&&n.push(`<meta data-elur-head property="og:image:alt" content="${u(i.imageAlt)}" />`),i.image&&i.imageWidth&&n.push(`<meta data-elur-head property="og:image:width" content="${String(i.imageWidth)}" />`),i.image&&i.imageHeight&&n.push(`<meta data-elur-head property="og:image:height" content="${String(i.imageHeight)}" />`),i.image&&i.imageType&&n.push(`<meta data-elur-head property="og:image:type" content="${u(i.imageType)}" />`),i.siteName&&n.push(`<meta data-elur-head property="og:site_name" content="${u(i.siteName)}" />`),i.locale&&n.push(`<meta data-elur-head property="og:locale" content="${u(i.locale)}" />`));let a=e.twitter;if(a&&(a.card&&n.push(`<meta data-elur-head name="twitter:card" content="${u(a.card)}" />`),(a.title??r)&&n.push(`<meta data-elur-head name="twitter:title" content="${u(a.title??r)}" />`),(a.description??e.description)&&n.push(`<meta data-elur-head name="twitter:description" content="${u(a.description??e.description)}" />`),a.image&&n.push(`<meta data-elur-head name="twitter:image" content="${u(a.image)}" />`),a.image&&a.imageAlt&&n.push(`<meta data-elur-head name="twitter:image:alt" content="${u(a.imageAlt)}" />`)),e.other)for(let[t,r]of Object.entries(e.other))n.push(`<meta data-elur-head name="${u(t)}" content="${u(r)}" />`);return n.map(e=>`\n ${e}`).join(``)}function p(e){let{body:t,title:n=`Elur Kit App`,lang:r=`es`,data:i,actions:a,clientEntry:o,htmlAttributes:s,headScripts:c,headLinks:l,metadata:p}=e,m=i===void 0?``:`\n <script type="application/json" id="elur-data">${d(i)}<\/script>`,h=a&&Object.keys(a).length>0?`\n <script type="application/json" id="elur-actions">${d(a)}<\/script>`:``,g=o?`\n <script type="module" src="${u(o)}"><\/script>`:``,_=s?Object.entries(s).filter(([,e])=>e!=null&&e!==``).map(([e,t])=>` ${u(e)}="${u(String(t))}"`).join(``):``,v=c?c.filter(e=>typeof e==`string`&&e.trim().length>0).map(e=>e.trimStart().startsWith(`<script`)?`\n ${e}`:`\n <script>${e.replace(/<\/script>/gi,`<\\/script>`)}<\/script>`).join(``):``,y=p?f(p,n):``,b=p?.title?``:`\n <title>${u(n)}</title>`,x=l?l.filter(e=>typeof e==`string`&&e.trim().length>0).map(e=>`\n ${e}`).join(``):``,S=e.renderEndpoint===!1?`
2
- <meta name="elur:render-endpoint" content="off" />`:``;return`<!DOCTYPE html>
3
- <html lang="${u(r)}"${_}>
4
- <head>
5
- <meta charset="utf-8" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1" />${S}${b}${y}${x}${v}
7
- </head>
8
- <body>
9
- <div id="app">${t}</div>${m}${h}${g}
10
- </body>
11
- </html>
12
- `}var m=`__elur_js_action_error`,h=3500,g=15e3,_=process.env.ELUR_JS_ACTION_SECRET??(0,r.randomBytes)(32).toString(`hex`),v=new Map,y=!1;function b(){y||(y=!0,setTimeout(()=>{y=!1;let e=Date.now();for(let[t,n]of v)n.expiresAt<=e&&v.delete(t)},g).unref?.())}function x(e){return`${(0,r.createHmac)(`sha256`,_).update(e).digest(`hex`)}.${e}`}function S(e){let t=e.indexOf(`.`);if(t===-1)return;let n=e.slice(0,t),i=e.slice(t+1),a=(0,r.createHmac)(`sha256`,_).update(i).digest(`hex`);if(n.length===a.length)try{if((0,r.timingSafeEqual)(Buffer.from(n),Buffer.from(a)))return i}catch{}}function C(e,t){let n=JSON.stringify({d:e,s:t}),i=x(Buffer.from(n,`utf8`).toString(`base64url`));if(i.length<=h)return{value:i};let a=(0,r.randomBytes)(12).toString(`hex`);return v.set(a,{data:e,status:t,expiresAt:Date.now()+g}),b(),{value:x(`id:${a}`),storeId:a}}function w(e){if(!e)return;let t=S(e);if(t!==void 0){if(t.startsWith(`id:`)){let e=t.slice(3),n=v.get(e);return!n||(v.delete(e),n.expiresAt<=Date.now())?void 0:{data:n.data,status:n.status}}try{let e=Buffer.from(t,`base64url`).toString(`utf8`),n=JSON.parse(e);return{data:n.d,status:n.s}}catch{return}}}var T=m;function E(){return`${m}=; Path=/; Max-Age=0; SameSite=Lax`}function D(e){return`${m}=${e}; Path=/; Max-Age=15; SameSite=Lax; HttpOnly`}var O={mode:`dynamic`,revalidate:0};function k(e){if(!e||typeof e!=`object`)return O;let t=e,n=t.mode;return n!==`public`&&n!==`private`&&n!==`dynamic`?O:{mode:n,revalidate:typeof t.revalidate==`number`?t.revalidate:0,tags:Array.isArray(t.tags)?t.tags.filter(e=>typeof e==`string`):void 0}}function A(e,t){return!(e.mode!==`public`||e.revalidate<=0||t.headers.get(`Cookie`)||t.headers.get(`Authorization`))}var j=e=>import(e);function M(e,t){let n={},r=[],i=[],a=e=>{if(!e||typeof e!=`object`)return;let t=e.htmlAttributes;t&&Object.assign(n,t);let a=e.headScripts;Array.isArray(a)&&r.push(...a);let o=e.headLinks;Array.isArray(o)&&i.push(...o)};for(let e of t)a(e);return a(e),{htmlAttributes:n,headScripts:[...new Set(r)],headLinks:[...new Set(i)]}}async function N(e){let{route:t,params:r={},searchParams:i=new URLSearchParams,config:a,importer:o=j,actions:s,request:l}=e,{default:u,generateMetadata:d}=await o(t.pagePath),m,h,g,_={response:void 0};if(t.dataPath){let e=await o(t.dataPath);if(e.load)try{m=await e.load({params:r,searchParams:i,request:l})}catch(e){if(e instanceof Response)_.response=e;else throw e}typeof e.revalidate==`number`&&(h=e.revalidate),e.cache&&(g=k(e.cache),g.revalidate>0&&(h=g.revalidate))}if(_.response)return{html:``,response:_.response,status:_.response.status};let v,y;if(l){let e=(l.headers.get(`Cookie`)??``).match(RegExp(`(?:^|;\\s*)${T}=([^;]+)`));if(e){let t=w(e[1]);t&&(v={__elur_js_action_error:!0,status:t.status,data:t.data},y=`${T}=; Path=/; Max-Age=0; SameSite=Lax`)}}let b={data:m??{},params:r,searchParams:i,form:v},x=await Promise.all(t.layouts.map(async e=>o(e))),S=await Promise.all(t.layouts.map(async e=>{let t=e.replace(/layout\.ts$/,`layout.data.ts`);if(!(0,n.existsSync)(t))return;let a=await o(t);if(a.load)try{return await a.load({params:r,searchParams:i,request:l})}catch(e){if(e instanceof Response){_.response=e;return}throw e}})),C=_.response;if(C)return{html:``,response:C,status:C.status};let E;if(t.slots){E={};for(let[e,n]of Object.entries(t.slots)){let t=await o(n);E[e]=t.default(b)}}let D=await c(()=>{let e=u(b);for(let t=x.length-1;t>=0;t--){let{default:n}=x[t];e=n({children:e,data:S[t],slots:E})}return e}),O=typeof m==`object`&&m&&`title`in m?String(m.title??`Elur Kit`):`Elur Kit`,{htmlAttributes:A,headScripts:N,headLinks:I}=M(m,S),L;typeof d==`function`&&(L=await d({params:r,searchParams:i,request:l,data:m})),L||=P(m)??F(S);let R=L?.title??O,z=p({title:R,lang:a.lang,body:D,data:m,actions:s,htmlAttributes:A,headScripts:N,headLinks:I,metadata:L,clientEntry:a.clientEntry,renderEndpoint:a.renderEndpoint}),B=L?f(L,R):``;return{html:z,revalidate:h,clearActionErrorCookie:y,head:B,resolvedTitle:R,cachePolicy:g}}function P(e){if(e&&typeof e==`object`&&`metadata`in e){let t=e.metadata;if(t&&typeof t==`object`)return t}}function F(e){for(let t of e){let e=P(t);if(e)return e}}async function I(e){let t=e.status===404?e.routes.error404:e.routes.error500;if(t)try{let{html:n}=await N({route:t,params:{},searchParams:new URLSearchParams,config:e.config,actions:e.actions,importer:e.importer});return{html:n,status:e.status}}catch(t){console.error(`[render] error ${e.status} page failed`,t);return}}function L(e,t){let n=e.split(`?`)[0].split(`/`).filter(Boolean).map(z),r=[...t].sort((e,t)=>B(t.path)-B(e.path));for(let e of r){let t=V(n,e.path.split(`/`).filter(Boolean),e.optionalCatchAll);if(t)return{route:e,params:t,searchParams:new URLSearchParams}}}function R(e,t){let n=e.split(`?`)[0].split(`/`).filter(Boolean).map(z),r=[...t].sort((e,t)=>B(t.path)-B(e.path));for(let e of r){let t=V(n,e.path.split(`/`).filter(Boolean));if(t)return{route:e,params:t}}}function z(e){try{return decodeURIComponent(e)}catch{return e}}function B(e){return e.split(`/`).filter(Boolean).reduce((e,t)=>t.endsWith(`*`)?e:t.startsWith(`:`)?e+1:e+2,0)}function V(e,t,n=!1){let r={},i=0;for(let a=0;a<t.length;a++){let o=t[a];if(o.endsWith(`*`)){let t=o.slice(1,-1),a=e.slice(i);return a.length===0&&!n?void 0:(r[t]=a.length>0?a:[],r)}if(o.startsWith(`:`)){let t=e[i];if(t===void 0)return;r[o.slice(1)]=t,i++;continue}if(o!==e[i])return;i++}if(i===e.length)return r}function H(e){if(e)try{let t=new URL(e);return t.protocol!==`http:`&&t.protocol!==`https:`?void 0:t.origin}catch{return}}function U(e,t={}){let n=H(e.url);if(!n)return`Invalid target URL`;let r=e.headers.get(`Origin`),i=e.headers.get(`Referer`);if(!r&&!i)return t.strictOrigin?`Missing Origin and Referer headers`:void 0;let a=H(r||i);if(!a)return r?`Invalid Origin header`:`Invalid Referer header`;if(a!==n&&!t.allowedOrigins?.some(e=>H(e)===a))return`Cross-origin request blocked: source "${a}" != target "${n}"`}function W(e){return new Response(e,{status:403,headers:{"Content-Type":`text/plain; charset=utf-8`}})}var G=1048576;async function K(e,t){let n=e.headers.get(`Content-Length`);if(n&&parseInt(n,10)>t)return{ok:!1,response:new Response(`Request body too large`,{status:413,headers:{"Content-Type":`text/plain`}})};let r=e.body?.getReader();if(!r)return{ok:!0,text:``};let i=[],a=0;try{for(;;){let{done:e,value:n}=await r.read();if(e)break;if(a+=n.byteLength,a>t){try{r.cancel()}catch{}return{ok:!1,response:new Response(`Request body too large`,{status:413,headers:{"Content-Type":`text/plain`}})}}i.push(n)}}finally{try{r.releaseLock()}catch{}}let o=new Uint8Array(a),s=0;for(let e of i)o.set(e,s),s+=e.byteLength;return{ok:!0,text:new TextDecoder().decode(o)}}function q(e){let t=new URLSearchParams(e),n={};for(let[e,r]of t)n[e]===void 0?n[e]=r:Array.isArray(n[e])?n[e].push(r):n[e]=[n[e],r];return n}async function J(e,t=G){if(e.method!==`POST`)return{ok:!1,response:new Response(`Method not allowed`,{status:405,headers:{"Content-Type":`text/plain`}})};let n=e.headers.get(`Content-Type`)??``,r=(e.headers.get(`Accept`)??``).includes(`application/json`),i,a,o=[];if(n.includes(`application/json`)){let n=await K(e,t);if(!n.ok)return{ok:!1,response:n.response};let r;try{r=JSON.parse(n.text)}catch{return{ok:!1,response:new Response(`Invalid JSON body`,{status:400,headers:{"Content-Type":`text/plain`}})}}i=r.name,a=r.page,o=Array.isArray(r.args)?r.args:[]}else if(n.includes(`application/x-www-form-urlencoded`)||n.includes(`multipart/form-data`)){if(n.includes(`multipart/form-data`)){let n=e.headers.get(`Content-Length`);if(n&&parseInt(n,10)>t)return{ok:!1,response:new Response(`Request body too large`,{status:413,headers:{"Content-Type":`text/plain`}})};let r;try{r=await e.formData()}catch{return{ok:!1,response:new Response(`Invalid form body`,{status:400,headers:{"Content-Type":`text/plain`}})}}i=r.get(`__elur_js_action_name`)??void 0,a=r.get(`__elur_js_action_page`)??void 0;let s={};for(let[e,t]of r)e!==`__elur_js_action_name`&&e!==`__elur_js_action_page`&&(s[e]=t);o=[s]}else{let n=await K(e,t);if(!n.ok)return{ok:!1,response:n.response};let r=q(n.text);i=r.__elur_js_action_name,a=r.__elur_js_action_page;let s={};for(let[e,t]of Object.entries(r))e!==`__elur_js_action_name`&&e!==`__elur_js_action_page`&&(s[e]=t);o=[s]}}else{let n=await K(e,t);if(!n.ok)return{ok:!1,response:n.response};let r=q(n.text);i=r.__elur_js_action_name,a=r.__elur_js_action_page;let s={};for(let[e,t]of Object.entries(r))e!==`__elur_js_action_name`&&e!==`__elur_js_action_page`&&(s[e]=t);o=[s]}return!i||typeof i!=`string`?{ok:!1,response:new Response(`Missing action name`,{status:400,headers:{"Content-Type":`text/plain`}})}:{ok:!0,name:i,page:a,args:o,wantsJson:r}}async function Y(t,n,r={}){let i=U(t,r);if(i)return W(i);let a=await J(t,r.bodyLimit??G);if(!a.ok)return a.response;let{name:o,page:s,args:c,wantsJson:l}=a;try{let r=await n(o,s);if(!r){let e=s?`Action not found: ${o} (page: ${s})`:`Action not found: ${o}`;return new Response(e,{status:404,headers:{"Content-Type":`text/plain`}})}let i=await r(...c);if(e.a(i)){if(l)return new Response(JSON.stringify({__elur_js_action_failure:!0,status:i.status,data:i.data}),{status:i.status,headers:{"Content-Type":`application/json`}});let e=t.headers.get(`Referer`)??`/`,n=new URL(e,`http://localhost`),{value:r}=C(i.data,i.status);return new Response(null,{status:303,headers:{Location:n.pathname+n.search,"Content-Type":`text/plain`,"Set-Cookie":D(r)}})}if(e.o(i))return l?new Response(JSON.stringify({__elur_js_action_redirect:!0,status:i.status,location:i.location}),{status:200,headers:{"Content-Type":`application/json`}}):new Response(null,{status:i.status,headers:{Location:i.location,"Content-Type":`text/plain`}});if(l)return new Response(JSON.stringify(i??null),{status:200,headers:{"Content-Type":`application/json`}});let a=t.headers.get(`Referer`)??`/`;return new Response(null,{status:303,headers:{Location:typeof i==`string`?i:a,"Content-Type":`text/plain`}})}catch(t){return console.error(`[elur-kit] Action error:`,t),e.s(t,{includeDetail:!1})}}var X=globalThis.AbortController??AbortController;function Z(e,t){let n=new Headers;for(let t=0;t<e.rawHeaders.length;t+=2)n.append(e.rawHeaders[t],e.rawHeaders[t+1]);let r=new X;e.once(`aborted`,()=>r.abort()),e.once(`close`,()=>{e.complete||r.abort()});let i=e.socket.encrypted?`https`:`http`,a={method:e.method??`GET`,headers:n,signal:r.signal};return t!=null&&a.method!==`GET`&&a.method!==`HEAD`&&(a.body=t),new Request(`${i}://${n.get(`host`)??`localhost`}${e.url??`/`}`,a)}Object.defineProperty(exports,"_",{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return R}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return I}}),Object.defineProperty(exports,"d",{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,"f",{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,"g",{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,"h",{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return U}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return N}}),Object.defineProperty(exports,"m",{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return Y}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return L}}),Object.defineProperty(exports,"p",{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return W}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return Z}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,"v",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"y",{enumerable:!0,get:function(){return s}});
13
- //# sourceMappingURL=node-http-DhxguYyz.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"node-http-DhxguYyz.cjs","names":[],"sources":["../../src/render/ssr-flag.ts","../../src/render/render-to-string.ts","../../src/build/document-shell.ts","../../src/action/error-store.ts","../../src/cache/policy.ts","../../src/ssr/render.ts","../../src/ssr/match.ts","../../src/action/origin.ts","../../src/action/server.ts","../../src/runtime/node-http.ts"],"sourcesContent":["// --- SSR flag utility ---\n//\n// `@elurjs/core` does not export `_setSSR`/`_isSSR`. The reactivity state lives\n// on `globalThis[Symbol.for(\"@elurjs/core/reactivity-state\")]` and the kit owns\n// the `ssr` boolean on it: `renderToString` sets it to `true` while server\n// rendering so `isSSR()` reflects the current render mode for user code\n// (environment reads, client-only guards, ...).\n//\n// This module manipulates that flag directly so the kit does not depend on\n// private exports that may or may not be present in a given elur release.\n\nconst STATE_KEY = Symbol.for(\"@elurjs/core/reactivity-state\");\n\ntype ReactivityState = { ssr?: boolean };\n\nfunction getState(): ReactivityState | undefined {\n return (globalThis as Record<symbol, unknown>)[STATE_KEY] as\n | ReactivityState\n | undefined;\n}\n\n/** Sets the SSR flag on the Elur reactivity state. No-op if state is absent. */\nexport function setSSR(value: boolean): void {\n const state = getState();\n if (state) state.ssr = value;\n}\n\n/** Reads the SSR flag from the Elur reactivity state. Defaults to false. */\nexport function isSSR(): boolean {\n return getState()?.ssr ?? false;\n}\n","import type { ElurTemplate } from \"@elurjs/core\";\nimport { renderToString as renderCoreTemplate } from \"@elurjs/core/server\";\nimport { setSSR } from \"./ssr-flag\";\n\n// --- Build-time / server rendering ---\n//\n// The Elur core ships a DOM-free `renderToString` (`@elurjs/core/server`)\n// that streams template output without ever touching a `document`. The kit used\n// to inject a Node-side DOM (happy-dom) as a fallback for legacy compatibility;\n// that fallback has been removed together with the happy-dom dependency.\n\n/**\n * Renders a Elur template to an HTML string in Node.\n *\n * Accepts a *factory* (not a template) because `html`` evaluates at call time.\n *\n * @param factory Thunk that builds the template, e.g. `() => Page({ data })`.\n * @returns Serialized HTML of the rendered template.\n */\nexport async function renderToString(\n factory: () => ElurTemplate,\n options: { markers?: \"none\" | \"hydration\" } = {},\n): Promise<string> {\n setSSR(true);\n try {\n return await renderCoreTemplate(factory(), {\n markers: options.markers ?? \"hydration\",\n });\n } finally {\n setSSR(false);\n }\n}\n","//\n// The <!DOCTYPE>, <head> and <body> wrapper — plus the serialized loader data\n// and the client entry — are injected here at build time.\n\nimport type { PageMetadata } from \"../types.js\";\nexport interface ShellOptions {\n /** Rendered inner HTML that goes inside `#app`. */\n body: string;\n /** `<title>` text. */\n title?: string;\n /** `<html lang>` attribute. */\n lang?: string;\n /** Additional attributes for the `<html>` element, e.g. `{ \"data-theme\": \"dark\" }`. */\n htmlAttributes?: Record<string, string>;\n /**\n * Inline scripts injected into `<head>`. They run synchronously while the\n * document parses — before the first paint and before the (deferred) client\n * bundle — so they are the right place for no-flash bootstrapping (e.g.\n * applying a stored theme before the page becomes visible).\n */\n headScripts?: string[];\n /**\n * Raw HTML strings injected into `<head>` — e.g. `<link rel=\"icon\">`,\n * `<link rel=\"manifest\">`, `<meta name=\"theme-color\">`. Each string is\n * rendered as-is inside `<head>`.\n */\n headLinks?: string[];\n /** Loader data serialized into `<script id=\"elur-data\">`. */\n data?: unknown;\n /** Per-page action names serialized into `<script id=\"elur-actions\">`. */\n actions?: Record<string, string[]>;\n /** Path to the client entry module, e.g. `/_elur/entry-client.js`. */\n clientEntry?: string;\n /** Page metadata emitted as `<meta>`, `<link>` and OG/Twitter tags in `<head>`. */\n metadata?: PageMetadata;\n /**\n * Whether the SSR render endpoint (`/__elur-js/render`) is available at\n * runtime. Defaults to `true`. When `false` (static deployments), the shell\n * emits `<meta name=\"elur:render-endpoint\" content=\"off\" />` so the client\n * router skips probing the endpoint entirely — preventing a storm of 404\n * requests on fully static sites.\n */\n renderEndpoint?: boolean;\n}\n\nconst HTML_ESCAPES: Record<string, string> = {\n \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n '\"': \"&quot;\",\n \"'\": \"&#39;\",\n};\n\nfunction escapeHtml(value: string): string {\n return value.replace(/[&<>\"']/g, (c) => HTML_ESCAPES[c]);\n}\n\n/**\n * Serializes data for embedding inside a `<script>` tag. Escapes `<` so a\n * `</script>` sequence in the data cannot break out of the tag.\n */\nfunction serializeData(data: unknown): string {\n return JSON.stringify(data ?? null).replace(/</g, \"\\\\u003c\");\n}\n\n/**\n * Builds the `<head>` tags for a `PageMetadata` object. Every tag is marked with\n * `data-elur-head` so the client-side router can replace them on navigation\n * without touching charset/viewport or user-supplied `headScripts`.\n */\nexport function buildHeadTags(metadata: PageMetadata, fallbackTitle: string): string {\n const tags: string[] = [];\n const title = metadata.title ?? fallbackTitle;\n if (metadata.title) {\n tags.push(`<title data-elur-head>${escapeHtml(title)}</title>`);\n }\n\n if (metadata.description) {\n tags.push(`<meta data-elur-head name=\"description\" content=\"${escapeHtml(metadata.description)}\" />`);\n }\n\n if (metadata.canonical) {\n tags.push(`<link data-elur-head rel=\"canonical\" href=\"${escapeHtml(metadata.canonical)}\" />`);\n }\n\n if (metadata.robots) {\n tags.push(`<meta data-elur-head name=\"robots\" content=\"${escapeHtml(metadata.robots)}\" />`);\n }\n\n const og = metadata.openGraph;\n if (og) {\n if (og.type) tags.push(`<meta data-elur-head property=\"og:type\" content=\"${escapeHtml(og.type)}\" />`);\n tags.push(`<meta data-elur-head property=\"og:title\" content=\"${escapeHtml(og.title ?? title)}\" />`);\n if (og.description ?? metadata.description) {\n tags.push(`<meta data-elur-head property=\"og:description\" content=\"${escapeHtml(og.description ?? metadata.description!)}\" />`);\n }\n if (og.url ?? metadata.canonical) {\n tags.push(`<meta data-elur-head property=\"og:url\" content=\"${escapeHtml(og.url ?? metadata.canonical!)}\" />`);\n }\n if (og.image) tags.push(`<meta data-elur-head property=\"og:image\" content=\"${escapeHtml(og.image)}\" />`);\n if (og.image && og.imageAlt) tags.push(`<meta data-elur-head property=\"og:image:alt\" content=\"${escapeHtml(og.imageAlt)}\" />`);\n if (og.image && og.imageWidth) tags.push(`<meta data-elur-head property=\"og:image:width\" content=\"${String(og.imageWidth)}\" />`);\n if (og.image && og.imageHeight) tags.push(`<meta data-elur-head property=\"og:image:height\" content=\"${String(og.imageHeight)}\" />`);\n if (og.image && og.imageType) tags.push(`<meta data-elur-head property=\"og:image:type\" content=\"${escapeHtml(og.imageType)}\" />`);\n if (og.siteName) tags.push(`<meta data-elur-head property=\"og:site_name\" content=\"${escapeHtml(og.siteName)}\" />`);\n if (og.locale) tags.push(`<meta data-elur-head property=\"og:locale\" content=\"${escapeHtml(og.locale)}\" />`);\n }\n\n const tw = metadata.twitter;\n if (tw) {\n if (tw.card) tags.push(`<meta data-elur-head name=\"twitter:card\" content=\"${escapeHtml(tw.card)}\" />`);\n if (tw.title ?? title) tags.push(`<meta data-elur-head name=\"twitter:title\" content=\"${escapeHtml(tw.title ?? title)}\" />`);\n if (tw.description ?? metadata.description) {\n tags.push(`<meta data-elur-head name=\"twitter:description\" content=\"${escapeHtml(tw.description ?? metadata.description!)}\" />`);\n }\n if (tw.image) tags.push(`<meta data-elur-head name=\"twitter:image\" content=\"${escapeHtml(tw.image)}\" />`);\n if (tw.image && tw.imageAlt) tags.push(`<meta data-elur-head name=\"twitter:image:alt\" content=\"${escapeHtml(tw.imageAlt)}\" />`);\n }\n\n if (metadata.other) {\n for (const [name, content] of Object.entries(metadata.other)) {\n tags.push(`<meta data-elur-head name=\"${escapeHtml(name)}\" content=\"${escapeHtml(content)}\" />`);\n }\n }\n\n return tags.map((t) => `\\n ${t}`).join(\"\");\n}\n\n/** Wraps rendered body HTML into a full HTML document. */\nexport function documentShell(opts: ShellOptions): string {\n const { body, title = \"Elur Kit App\", lang = \"es\", data, actions, clientEntry, htmlAttributes, headScripts, headLinks, metadata } = opts;\n\n const dataScript =\n data !== undefined\n ? `\\n <script type=\"application/json\" id=\"elur-data\">${serializeData(data)}</script>`\n : \"\";\n\n const actionsScript = actions && Object.keys(actions).length > 0\n ? `\\n <script type=\"application/json\" id=\"elur-actions\">${serializeData(actions)}</script>`\n : \"\";\n\n const entryScript = clientEntry\n ? `\\n <script type=\"module\" src=\"${escapeHtml(clientEntry)}\"></script>`\n : \"\";\n\n const htmlAttrs = htmlAttributes\n ? Object.entries(htmlAttributes)\n .filter(([, value]) => value !== undefined && value !== null && value !== \"\")\n .map(([key, value]) => ` ${escapeHtml(key)}=\"${escapeHtml(String(value))}\"`)\n .join(\"\")\n : \"\";\n\n const headScriptsHtml = headScripts\n ? headScripts\n .filter((script) => typeof script === \"string\" && script.trim().length > 0)\n .map((script) => {\n // If the script is already a complete <script> tag (e.g. JSON-LD),\n // render it as-is without wrapping.\n if (script.trimStart().startsWith(\"<script\")) {\n return `\\n ${script}`;\n }\n return `\\n <script>${script.replace(/<\\/script>/gi, \"<\\\\/script>\")}</script>`;\n })\n .join(\"\")\n : \"\";\n\n const headTags = metadata ? buildHeadTags(metadata, title) : \"\";\n const titleTag = metadata?.title\n ? \"\" // already emitted by buildHeadTags\n : `\\n <title>${escapeHtml(title)}</title>`;\n\n const headLinksHtml = headLinks\n ? headLinks\n .filter((link) => typeof link === \"string\" && link.trim().length > 0)\n .map((link) => `\\n ${link}`)\n .join(\"\")\n : \"\";\n\n const renderEndpointMeta =\n opts.renderEndpoint === false\n ? '\\n <meta name=\"elur:render-endpoint\" content=\"off\" />'\n : \"\";\n\n return `<!DOCTYPE html>\n<html lang=\"${escapeHtml(lang)}\"${htmlAttrs}>\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />${renderEndpointMeta}${titleTag}${headTags}${headLinksHtml}${headScriptsHtml}\n </head>\n <body>\n <div id=\"app\">${body}</div>${dataScript}${actionsScript}${entryScript}\n </body>\n</html>\n`;\n}\n","// --- Ephemeral action error store ---\n//\n// Action failures submitted via plain HTML forms (progressive enhancement)\n// need to be relayed back to the page so the user sees validation errors.\n//\n// Previously the failure data was serialized into a `?__elur_js_action_error=`\n// query param on the redirect. That leaks errors into browser history,\n// server logs and third-party Referer headers.\n//\n// Now we stash the failure in a short-lived in-memory store keyed by a random\n// id, set a small cookie `__elur_js_action_error=<id>` (Max-Age=15s, SameSite=Lax),\n// and the next render reads the cookie, fetches the payload, exposes it as\n// `props.form`, and clears the entry.\n//\n// The store is process-local, which is fine for the single-process SSR server\n// and the dev server. For multi-instance deployments the cookie carries the\n// payload directly when it fits (see `encodeActionErrorCookie`); the store is\n// only the overflow path for large payloads.\n\nimport { createHmac, randomBytes, timingSafeEqual } from \"node:crypto\";\n\nconst COOKIE_NAME = \"__elur_js_action_error\";\nconst MAX_COOKIE_SIZE = 3500; // bytes; leaves headroom under the 4KB cookie limit\nconst TTL_MS = 15_000;\n\n// HMAC key for signing action error cookies. In production this should be\n// set via ELUR_JS_ACTION_SECRET env var; otherwise we derive a per-process\n// key (sufficient for single-process dev/preview, but NOT for multi-instance).\nconst ACTION_SECRET =\n process.env.ELUR_JS_ACTION_SECRET ?? randomBytes(32).toString(\"hex\");\n\ninterface StoredError {\n data: unknown;\n status: number;\n expiresAt: number;\n}\n\nconst store = new Map<string, StoredError>();\n\n// Periodically purge expired entries so the map does not grow unbounded.\nlet sweepScheduled = false;\nfunction scheduleSweep(): void {\n if (sweepScheduled) return;\n sweepScheduled = true;\n setTimeout(() => {\n sweepScheduled = false;\n const now = Date.now();\n for (const [key, entry] of store) {\n if (entry.expiresAt <= now) store.delete(key);\n }\n }, TTL_MS).unref?.();\n}\n\n/**\n * Signs a payload with HMAC-SHA256 using the action secret.\n * Returns `signature.payload` (both hex/base64url).\n */\nfunction sign(payload: string): string {\n const sig = createHmac(\"sha256\", ACTION_SECRET).update(payload).digest(\"hex\");\n return `${sig}.${payload}`;\n}\n\n/**\n * Verifies a signed value and returns the payload if valid, or undefined.\n * Uses timingSafeEqual to prevent timing attacks.\n */\nfunction verify(value: string): string | undefined {\n const dotIndex = value.indexOf(\".\");\n if (dotIndex === -1) return undefined;\n const sig = value.slice(0, dotIndex);\n const payload = value.slice(dotIndex + 1);\n const expectedSig = createHmac(\"sha256\", ACTION_SECRET).update(payload).digest(\"hex\");\n if (sig.length !== expectedSig.length) return undefined;\n try {\n if (timingSafeEqual(Buffer.from(sig), Buffer.from(expectedSig))) {\n return payload;\n }\n } catch {\n // Length mismatch — invalid.\n }\n return undefined;\n}\n\n/**\n * Encodes an action failure for the redirect cookie. When the payload fits\n * inside the cookie limit, it is embedded directly as a signed base64url JSON\n * value. When it is too large, it is stored in memory and only a short signed\n * id is written to the cookie.\n *\n * The cookie is signed with HMAC-SHA256 to prevent forgery (A-20).\n *\n * @returns The cookie value to set on the redirect response.\n */\nexport function encodeActionErrorCookie(\n data: unknown,\n status: number,\n): { value: string; storeId?: string } {\n const payload = JSON.stringify({ d: data, s: status });\n const encoded = Buffer.from(payload, \"utf8\").toString(\"base64url\");\n const signed = sign(encoded);\n if (signed.length <= MAX_COOKIE_SIZE) {\n return { value: signed };\n }\n\n // Overflow: stash in memory and reference by signed id.\n const id = randomBytes(12).toString(\"hex\");\n store.set(id, { data, status, expiresAt: Date.now() + TTL_MS });\n scheduleSweep();\n return { value: sign(`id:${id}`), storeId: id };\n}\n\n/**\n * Decodes a cookie value (previously produced by `encodeActionErrorCookie`)\n * into the failure payload. Verifies the HMAC signature first, then resolves\n * in-memory overflow entries and deletes them after reading.\n */\nexport function decodeActionErrorCookie(value: string | undefined | null):\n | { data: unknown; status: number }\n | undefined {\n if (!value) return undefined;\n\n // Verify signature first.\n const verifiedPayload = verify(value);\n if (verifiedPayload === undefined) return undefined;\n\n // Check if it's an in-memory store reference.\n if (verifiedPayload.startsWith(\"id:\")) {\n const id = verifiedPayload.slice(3);\n const entry = store.get(id);\n if (!entry) return undefined;\n store.delete(id);\n if (entry.expiresAt <= Date.now()) return undefined;\n return { data: entry.data, status: entry.status };\n }\n\n try {\n const json = Buffer.from(verifiedPayload, \"base64url\").toString(\"utf8\");\n const parsed = JSON.parse(json) as { d: unknown; s: number };\n return { data: parsed.d, status: parsed.s };\n } catch {\n return undefined;\n }\n}\n\n/** Name of the cookie used to relay action errors. */\nexport const ACTION_ERROR_COOKIE = COOKIE_NAME;\n\n/** Builds the Set-Cookie header value that clears the error cookie. */\nexport function clearActionErrorCookieHeader(): string {\n return `${COOKIE_NAME}=; Path=/; Max-Age=0; SameSite=Lax`;\n}\n\n/** Builds the Set-Cookie header value that sets the error cookie. */\nexport function setActionErrorCookieHeader(value: string): string {\n return `${COOKIE_NAME}=${value}; Path=/; Max-Age=15; SameSite=Lax; HttpOnly`;\n}\n","// --- Cache policy per route (runtime-security §9.1) ---\n//\n// Authors can declare a cache policy in their page.data.ts:\n//\n// export const cache = {\n// mode: \"public\", // \"public\" | \"private\" | \"dynamic\"\n// revalidate: 60, // seconds\n// tags: [\"products\"], // for tag-based invalidation\n// };\n//\n// Default policy: \"dynamic\" (no public ISR caching).\n// Requests with Cookie/Authorization are never cached publicly.\n// Responses with Set-Cookie/private/no-store are never cached publicly.\n\n/** Cache mode for a route. */\nexport type CacheMode = \"public\" | \"private\" | \"dynamic\";\n\n/** Cache policy declared by the route's data module. */\nexport interface CachePolicy {\n mode: CacheMode;\n revalidate: number;\n tags?: string[];\n}\n\n/** Default cache policy when none is declared. */\nexport const DEFAULT_CACHE_POLICY: CachePolicy = {\n mode: \"dynamic\",\n revalidate: 0,\n};\n\n/**\n * Normalizes a raw cache export from a data module into a CachePolicy.\n * Returns the default policy if the input is invalid or missing.\n */\nexport function normalizeCachePolicy(raw: unknown): CachePolicy {\n if (!raw || typeof raw !== \"object\") return DEFAULT_CACHE_POLICY;\n const obj = raw as Record<string, unknown>;\n const mode = obj.mode;\n if (mode !== \"public\" && mode !== \"private\" && mode !== \"dynamic\") {\n return DEFAULT_CACHE_POLICY;\n }\n const revalidate = typeof obj.revalidate === \"number\" ? obj.revalidate : 0;\n const tags = Array.isArray(obj.tags) ? obj.tags.filter((t) => typeof t === \"string\") : undefined;\n return { mode, revalidate, tags };\n}\n\n/**\n * Determines whether a route's cache policy allows public caching for the\n * given request.\n *\n * Per §9.1:\n * - \"dynamic\" → never cache\n * - \"private\" → never cache publicly (requires private adapter)\n * - \"public\" → cache only if request has no Cookie/Authorization\n */\nexport function shouldCachePublic(\n policy: CachePolicy,\n request: Request,\n): boolean {\n if (policy.mode !== \"public\") return false;\n if (policy.revalidate <= 0) return false;\n if (request.headers.get(\"Cookie\")) return false;\n if (request.headers.get(\"Authorization\")) return false;\n return true;\n}\n","import type { ElurTemplate } from \"@elurjs/core\";\nimport { renderToString } from \"../render/render-to-string.js\";\nimport { documentShell, buildHeadTags } from \"../build/document-shell.js\";\nimport type { PageRoute, ScannedRoutes } from \"../router/route-scanner.js\";\nimport type { BuildConfig } from \"../build/build.js\";\nimport type { PageDataLoad, PageProps, RouteParams, PageMetadata, GenerateMetadata } from \"../types.js\";\nimport { existsSync } from \"node:fs\";\nimport { decodeActionErrorCookie, ACTION_ERROR_COOKIE } from \"../action/error-store.js\";\nimport { normalizeCachePolicy, type CachePolicy } from \"../cache/policy.js\";\n\nexport interface RenderPageOptions {\n route: PageRoute;\n params?: RouteParams;\n searchParams?: URLSearchParams;\n config: Pick<BuildConfig, \"lang\" | \"clientEntry\" | \"renderEndpoint\">;\n /** Custom module loader. Defaults to native dynamic import. */\n importer?: (path: string) => Promise<unknown>;\n /** Per-page action names exposed in the HTML shell. */\n actions?: Record<string, string[]>;\n /** Current request, used to hydrate data loaders that need cookies/headers. */\n request?: Request;\n}\n\nexport interface RenderPageResult {\n html: string;\n revalidate?: number;\n /**\n * `Set-Cookie` header value that clears the action error cookie, when the\n * page consumed a relayed action failure. The SSR server should append it to\n * the outgoing response so the cookie does not persist.\n */\n clearActionErrorCookie?: string;\n /** `<head>` tags (title, meta, OG, twitter) for the SPA router to merge. */\n head?: string;\n /** Resolved page title (from metadata or fallback). */\n resolvedTitle?: string;\n /**\n * When a loader or layout throws a `Response` (e.g. `throw new Response(...,\n * { status: 404 })`), it is captured here as a first-class response instead\n * of being treated as an internal error (A-22).\n */\n response?: Response;\n /** HTTP status code for the rendered page (e.g. 404 for not-found pages). */\n status?: number;\n /** Cache policy declared by the route (§9.1). */\n cachePolicy?: CachePolicy;\n}\n\nconst defaultImport = (path: string) => import(path);\n\n/**\n * Collects `<html>` attributes and head scripts declared by data loaders\n * (page and layouts) via top-level `htmlAttributes` / `headScripts` fields.\n */\nexport function collectShellExtras(\n pageData: unknown,\n layoutDataList: unknown[],\n): { htmlAttributes: Record<string, string>; headScripts: string[]; headLinks: string[] } {\n const htmlAttributes: Record<string, string> = {};\n const headScripts: string[] = [];\n const headLinks: string[] = [];\n const merge = (value: unknown) => {\n if (!value || typeof value !== \"object\") return;\n const attrs = (value as { htmlAttributes?: Record<string, string> }).htmlAttributes;\n if (attrs) Object.assign(htmlAttributes, attrs);\n const scripts = (value as { headScripts?: string[] }).headScripts;\n if (Array.isArray(scripts)) headScripts.push(...scripts);\n const links = (value as { headLinks?: string[] }).headLinks;\n if (Array.isArray(links)) headLinks.push(...links);\n };\n for (const layoutData of layoutDataList) merge(layoutData);\n merge(pageData);\n // Deduplicate headScripts and headLinks (e.g. from both layout and page data)\n const uniqueScripts = [...new Set(headScripts)];\n const uniqueLinks = [...new Set(headLinks)];\n return { htmlAttributes, headScripts: uniqueScripts, headLinks: uniqueLinks };\n}\n\nexport async function renderPage(options: RenderPageOptions): Promise<RenderPageResult> {\n const { route, params = {}, searchParams = new URLSearchParams(), config, importer = defaultImport, actions, request } = options;\n\n const pageModule = await importer(route.pagePath) as {\n default: (props: PageProps<unknown>) => ElurTemplate;\n generateMetadata?: GenerateMetadata;\n };\n const { default: PageComponent, generateMetadata } = pageModule;\n\n let data: unknown;\n let revalidate: number | undefined;\n let cachePolicy: import(\"../cache/policy.js\").CachePolicy | undefined;\n // Use a mutable container so TypeScript doesn't narrow the type after\n // the first `if (thrownResponse)` check.\n const thrown: { response: Response | undefined } = { response: undefined };\n if (route.dataPath) {\n const mod = await importer(route.dataPath) as {\n load?: PageDataLoad;\n revalidate?: number;\n cache?: unknown;\n };\n if (mod.load) {\n try {\n data = await mod.load({ params, searchParams, request });\n } catch (err) {\n if (err instanceof Response) {\n thrown.response = err;\n } else {\n throw err;\n }\n }\n }\n if (typeof mod.revalidate === \"number\") {\n revalidate = mod.revalidate;\n }\n // Read cache policy from the data module (§9.1).\n if (mod.cache) {\n cachePolicy = normalizeCachePolicy(mod.cache);\n if (cachePolicy.revalidate > 0) {\n revalidate = cachePolicy.revalidate;\n }\n }\n }\n\n // If a loader threw a Response (redirect, 404, etc.), return it as a\n // first-class response instead of rendering the page (A-22).\n if (thrown.response) {\n return { html: \"\", response: thrown.response, status: thrown.response.status };\n }\n\n // Relay an action failure previously stored in the ephemeral cookie so the\n // page can render validation errors via `props.form`. The cookie is cleared\n // on the outgoing response (see `clearActionErrorCookie` in the result).\n let form: unknown;\n let clearActionErrorCookie: string | undefined;\n if (request) {\n const cookieHeader = request.headers.get(\"Cookie\") ?? \"\";\n const match = cookieHeader.match(new RegExp(`(?:^|;\\\\s*)${ACTION_ERROR_COOKIE}=([^;]+)`));\n if (match) {\n const decoded = decodeActionErrorCookie(match[1]);\n if (decoded) {\n form = { __elur_js_action_error: true, status: decoded.status, data: decoded.data };\n clearActionErrorCookie = `${ACTION_ERROR_COOKIE}=; Path=/; Max-Age=0; SameSite=Lax`;\n }\n }\n }\n\n const props: PageProps<unknown> = {\n data: data ?? {},\n params,\n searchParams,\n form,\n };\n\n const layoutModules = await Promise.all(\n route.layouts.map(async (layoutPath) => importer(layoutPath)),\n );\n const layoutDataList = await Promise.all(\n route.layouts.map(async (layoutPath) => {\n const dataPath = layoutPath.replace(/layout\\.ts$/, \"layout.data.ts\");\n if (!existsSync(dataPath)) return undefined;\n const mod = (await importer(dataPath)) as { load?: PageDataLoad };\n if (mod.load) {\n try {\n return await mod.load({ params, searchParams, request });\n } catch (err) {\n if (err instanceof Response) {\n thrown.response = err;\n return undefined;\n }\n throw err;\n }\n }\n return undefined;\n }),\n );\n\n // If a layout loader threw a Response, return it as first-class (A-22).\n const layoutThrown = thrown.response as Response | undefined;\n if (layoutThrown) {\n return { html: \"\", response: layoutThrown, status: layoutThrown.status };\n }\n\n // Load slot modules if the route has them (v2.1 — Fix #2: Layout Slots).\n let slotTemplates: Record<string, ElurTemplate> | undefined;\n if (route.slots) {\n slotTemplates = {};\n for (const [slotName, slotPath] of Object.entries(route.slots)) {\n const slotMod = await importer(slotPath) as { default: (props: PageProps<unknown>) => ElurTemplate };\n slotTemplates[slotName] = slotMod.default(props);\n }\n }\n\n const body = await renderToString(() => {\n let template = PageComponent(props);\n for (let i = layoutModules.length - 1; i >= 0; i--) {\n const { default: Layout } = layoutModules[i] as {\n default: (props: { children: ElurTemplate; data?: unknown; slots?: Record<string, ElurTemplate> }) => ElurTemplate;\n };\n template = Layout({ children: template, data: layoutDataList[i], slots: slotTemplates });\n }\n return template;\n });\n\n const title = typeof data === \"object\" && data && \"title\" in data\n ? String((data as { title?: unknown }).title ?? \"Elur Kit\")\n : \"Elur Kit\";\n\n const { htmlAttributes, headScripts, headLinks } = collectShellExtras(data, layoutDataList);\n\n // Resolve page metadata. Priority: `generateMetadata` from page.ts > `metadata`\n // field in the page loader data > `metadata` field in layout loader data.\n let metadata: PageMetadata | undefined;\n if (typeof generateMetadata === \"function\") {\n metadata = await generateMetadata({ params, searchParams, request, data });\n }\n if (!metadata) {\n metadata = extractMetadata(data) ?? extractMetadataFromList(layoutDataList);\n }\n // The title from metadata takes precedence over the data.title fallback.\n const resolvedTitle = metadata?.title ?? title;\n\n const html = documentShell({\n title: resolvedTitle,\n lang: config.lang,\n body,\n data,\n actions,\n htmlAttributes,\n headScripts,\n headLinks,\n metadata,\n clientEntry: config.clientEntry,\n renderEndpoint: config.renderEndpoint,\n });\n\n const head = metadata ? buildHeadTags(metadata, resolvedTitle) : \"\";\n return { html, revalidate, clearActionErrorCookie, head, resolvedTitle, cachePolicy };\n}\n\n/** Extracts a `metadata` field from a loader data object, if present. */\nfunction extractMetadata(value: unknown): PageMetadata | undefined {\n if (value && typeof value === \"object\" && \"metadata\" in value) {\n const meta = (value as { metadata?: unknown }).metadata;\n if (meta && typeof meta === \"object\") return meta as PageMetadata;\n }\n return undefined;\n}\n\n/** Extracts metadata from the first layout data object that has one. */\nfunction extractMetadataFromList(list: unknown[]): PageMetadata | undefined {\n for (const item of list) {\n const meta = extractMetadata(item);\n if (meta) return meta;\n }\n return undefined;\n}\n\nexport interface RenderErrorPageOptions {\n routes: ScannedRoutes;\n status: 404 | 500;\n error?: unknown;\n config: Pick<BuildConfig, \"lang\" | \"clientEntry\" | \"renderEndpoint\">;\n actions?: Record<string, string[]>;\n importer?: (path: string) => Promise<unknown>;\n}\n\nexport async function renderErrorPage(\n options: RenderErrorPageOptions,\n): Promise<{ html: string; status: number } | undefined> {\n const route = options.status === 404 ? options.routes.error404 : options.routes.error500;\n if (!route) return undefined;\n\n try {\n const { html } = await renderPage({\n route,\n params: {},\n searchParams: new URLSearchParams(),\n config: options.config,\n actions: options.actions,\n importer: options.importer,\n });\n return { html, status: options.status };\n } catch (err) {\n console.error(`[render] error ${options.status} page failed`, err);\n return undefined;\n }\n}\n","import type { ApiRoute, PageRoute } from \"../router/route-scanner.js\";\n\nexport interface MatchResult {\n route: PageRoute;\n params: Record<string, string | string[]>;\n searchParams: URLSearchParams;\n}\n\n/**\n * Match a request pathname against a list of page routes.\n *\n * Routes are sorted by specificity (static > dynamic > catch-all) before\n * matching, so `/about` wins over `/:slug` even if the catch-all appears first.\n *\n * URL segments are safely decoded (plan §11.1, runtime-security §10).\n */\nexport function matchRoute(\n pathname: string,\n routes: PageRoute[],\n): MatchResult | undefined {\n const cleanPath = pathname.split(\"?\")[0];\n const requestSegments = cleanPath.split(\"/\").filter(Boolean).map(safeDecodeURIComponent);\n\n const sorted = [...routes].sort((a, b) => specificity(b.path) - specificity(a.path));\n\n for (const route of sorted) {\n const routeSegments = route.path.split(\"/\").filter(Boolean);\n const match = tryMatch(requestSegments, routeSegments, route.optionalCatchAll);\n if (match) {\n return { route, params: match, searchParams: new URLSearchParams() };\n }\n }\n\n return undefined;\n}\n\nexport interface ApiMatchResult<T = ApiRoute> {\n route: T;\n params: Record<string, string | string[]>;\n}\n\n/**\n * Match a request pathname against a list of API routes.\n */\nexport function matchApiRoute<T extends { path: string }>(pathname: string, routes: T[]): ApiMatchResult<T> | undefined {\n const cleanPath = pathname.split(\"?\")[0];\n const requestSegments = cleanPath.split(\"/\").filter(Boolean).map(safeDecodeURIComponent);\n\n const sorted = [...routes].sort((a, b) => specificity(b.path) - specificity(a.path));\n\n for (const route of sorted) {\n const routeSegments = route.path.split(\"/\").filter(Boolean);\n const match = tryMatch(requestSegments, routeSegments);\n if (match) {\n return { route, params: match };\n }\n }\n\n return undefined;\n}\n\n/**\n * Safely decodes a URI component. If decoding fails (malformed % sequences),\n * returns the original string rather than throwing (runtime-security §10).\n */\nfunction safeDecodeURIComponent(segment: string): string {\n try {\n return decodeURIComponent(segment);\n } catch {\n return segment;\n }\n}\n\nfunction specificity(path: string): number {\n return path.split(\"/\").filter(Boolean).reduce((score, segment) => {\n if (segment.endsWith(\"*\")) return score;\n if (segment.startsWith(\":\")) return score + 1;\n return score + 2;\n }, 0);\n}\n\nfunction tryMatch(\n requestSegments: string[],\n routeSegments: string[],\n optionalCatchAll = false,\n): Record<string, string | string[]> | undefined {\n const params: Record<string, string | string[]> = {};\n\n let i = 0;\n for (let r = 0; r < routeSegments.length; r++) {\n const routeSeg = routeSegments[r];\n\n if (routeSeg.endsWith(\"*\")) {\n // Catch-all consumes the rest of the request segments.\n const name = routeSeg.slice(1, -1);\n const rest = requestSegments.slice(i);\n // For optional catch-all, empty rest is OK.\n if (rest.length === 0 && !optionalCatchAll) return undefined;\n params[name] = rest.length > 0 ? rest : [];\n return params;\n }\n\n if (routeSeg.startsWith(\":\")) {\n const requestSeg = requestSegments[i];\n if (requestSeg === undefined) return undefined;\n params[routeSeg.slice(1)] = requestSeg;\n i++;\n continue;\n }\n\n if (routeSeg !== requestSegments[i]) {\n return undefined;\n }\n i++;\n }\n\n if (i !== requestSegments.length) return undefined;\n return params;\n}\n","// --- Origin verification (CSRF protection for server actions) ---\n//\n// Server actions accept POST requests from the browser. Without origin\n// verification, any third-party site could submit forged requests to\n// `/__elur-js/actions` on behalf of a logged-in user (CSRF).\n//\n// Strategy: compare the request's `Origin` (or `Referer` fallback) host against\n// the target `Host` header. Same-origin requests pass; cross-origin requests\n// are rejected with 403 unless the origin is explicitly allow-listed.\n//\n// Requests without `Origin` AND without `Referer` (e.g. curl, server-to-server)\n// are accepted by default for DX, unless `strictOrigin: true` is configured.\n\nexport interface OriginCheckOptions {\n /** Extra origins allowed to call actions (e.g. preview deployments). */\n allowedOrigins?: string[];\n /**\n * When true, requests missing both `Origin` and `Referer` are rejected.\n * Defaults to false so curl/server-to-server calls keep working.\n */\n strictOrigin?: boolean;\n}\n\n/**\n * Returns the host:port of a URL string, or undefined if it cannot be parsed.\n */\nfunction originOf(urlString: string | null | undefined): string | undefined {\n if (!urlString) return undefined;\n try {\n const url = new URL(urlString);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return undefined;\n return url.origin;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Verifies that a request originates from the same host (or an allow-listed\n * origin). Returns an error message when the request must be rejected, or\n * undefined when it is allowed.\n *\n * @param request The incoming Request to actions.\n * @param options Origin check configuration.\n */\nexport function verifyOrigin(\n request: Request,\n options: OriginCheckOptions = {},\n): string | undefined {\n const targetOrigin = originOf(request.url);\n if (!targetOrigin) return \"Invalid target URL\";\n\n const origin = request.headers.get(\"Origin\");\n const referer = request.headers.get(\"Referer\");\n if (!origin && !referer) {\n return options.strictOrigin\n ? \"Missing Origin and Referer headers\"\n : undefined;\n }\n\n const sourceOrigin = origin ? originOf(origin) : originOf(referer);\n if (!sourceOrigin) return origin ? \"Invalid Origin header\" : \"Invalid Referer header\";\n if (sourceOrigin === targetOrigin) return undefined;\n\n if (options.allowedOrigins?.some((allowed) => originOf(allowed) === sourceOrigin)) return undefined;\n\n return `Cross-origin request blocked: source \"${sourceOrigin}\" != target \"${targetOrigin}\"`;\n}\n\n/** Builds a 403 Response for a rejected origin. */\nexport function originForbidden(message: string): Response {\n return new Response(message, {\n status: 403,\n headers: { \"Content-Type\": \"text/plain; charset=utf-8\" },\n });\n}\n","import type { ActionRequest } from \"./index.js\";\nimport { isActionFailure, isRedirectResponse, publicErrorResponse } from \"../errors.js\";\nimport { verifyOrigin, originForbidden, type OriginCheckOptions } from \"./origin.js\";\nimport {\n encodeActionErrorCookie,\n setActionErrorCookieHeader,\n} from \"./error-store.js\";\n\n/**\n * Resolves a server action by name and optional page scope.\n */\nexport type ActionResolver = (\n name: string,\n page?: string,\n) => Promise<((...args: unknown[]) => unknown) | undefined>;\n\n/** Options shared by `handleActionRequest` callers for CSRF protection. */\nexport interface ActionSecurityOptions extends OriginCheckOptions {\n /** Maximum body size in bytes. Defaults to 1MB (1_048_576). */\n bodyLimit?: number;\n}\n\n/** Default body size limit: 1MB. */\nconst DEFAULT_BODY_LIMIT = 1_048_576;\n\n/**\n * Reads the request body as text, enforcing a maximum size.\n * Returns a 413 response if the body exceeds the limit.\n */\nasync function readBodyWithLimit(\n request: Request,\n limit: number,\n): Promise<{ ok: true; text: string } | { ok: false; response: Response }> {\n const contentLength = request.headers.get(\"Content-Length\");\n if (contentLength && parseInt(contentLength, 10) > limit) {\n return {\n ok: false,\n response: new Response(\"Request body too large\", {\n status: 413,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n // Read the body as a stream with a size cap to prevent memory exhaustion\n // from chunked transfer encoding without Content-Length.\n const reader = request.body?.getReader();\n if (!reader) {\n return { ok: true, text: \"\" };\n }\n const chunks: Uint8Array[] = [];\n let totalSize = 0;\n try {\n for (; ;) {\n const { done, value } = await reader.read();\n if (done) break;\n totalSize += value.byteLength;\n if (totalSize > limit) {\n try { reader.cancel(); } catch { /* ignore */ }\n return {\n ok: false,\n response: new Response(\"Request body too large\", {\n status: 413,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n chunks.push(value);\n }\n } finally {\n try { reader.releaseLock(); } catch { /* ignore */ }\n }\n const total = new Uint8Array(totalSize);\n let offset = 0;\n for (const chunk of chunks) {\n total.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return { ok: true, text: new TextDecoder().decode(total) };\n}\n\nfunction parseFormBody(body: string): Record<string, unknown> {\n const params = new URLSearchParams(body);\n const result: Record<string, unknown> = {};\n for (const [key, value] of params) {\n if (result[key] === undefined) {\n result[key] = value;\n } else if (Array.isArray(result[key])) {\n (result[key] as unknown[]).push(value);\n } else {\n result[key] = [result[key], value];\n }\n }\n return result;\n}\n\nasync function parseActionRequest(\n request: Request,\n bodyLimit: number = DEFAULT_BODY_LIMIT,\n): Promise<\n | { ok: true; name: string; page?: string; args: unknown[]; wantsJson: boolean }\n | { ok: false; response: Response }\n> {\n if (request.method !== \"POST\") {\n return {\n ok: false,\n response: new Response(\"Method not allowed\", {\n status: 405,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n\n const contentType = request.headers.get(\"Content-Type\") ?? \"\";\n const wantsJson = (request.headers.get(\"Accept\") ?? \"\").includes(\"application/json\");\n\n let name: string | undefined;\n let page: string | undefined;\n let args: unknown[] = [];\n\n if (contentType.includes(\"application/json\")) {\n const bodyResult = await readBodyWithLimit(request, bodyLimit);\n if (!bodyResult.ok) return { ok: false, response: bodyResult.response };\n let body: ActionRequest;\n try {\n body = JSON.parse(bodyResult.text) as ActionRequest;\n } catch {\n return {\n ok: false,\n response: new Response(\"Invalid JSON body\", {\n status: 400,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n name = body.name;\n page = body.page;\n args = Array.isArray(body.args) ? body.args : [];\n } else if (\n contentType.includes(\"application/x-www-form-urlencoded\") ||\n contentType.includes(\"multipart/form-data\")\n ) {\n // For multipart, use the native formData() parser after checking\n // Content-Length against the limit. For urlencoded, use our size-capped\n // reader to handle chunked encoding without Content-Length.\n if (contentType.includes(\"multipart/form-data\")) {\n const contentLength = request.headers.get(\"Content-Length\");\n if (contentLength && parseInt(contentLength, 10) > bodyLimit) {\n return {\n ok: false,\n response: new Response(\"Request body too large\", {\n status: 413,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n let form: FormData;\n try {\n form = await request.formData();\n } catch {\n return {\n ok: false,\n response: new Response(\"Invalid form body\", {\n status: 400,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n name = form.get(\"__elur_js_action_name\") as string | null ?? undefined;\n page = form.get(\"__elur_js_action_page\") as string | null ?? undefined;\n const input: Record<string, unknown> = {};\n for (const [key, value] of form) {\n if (key === \"__elur_js_action_name\" || key === \"__elur_js_action_page\") continue;\n input[key] = value;\n }\n args = [input];\n } else {\n const bodyResult = await readBodyWithLimit(request, bodyLimit);\n if (!bodyResult.ok) return { ok: false, response: bodyResult.response };\n const form = parseFormBody(bodyResult.text);\n name = form.__elur_js_action_name as string | undefined;\n page = form.__elur_js_action_page as string | undefined;\n const input: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(form)) {\n if (key === \"__elur_js_action_name\" || key === \"__elur_js_action_page\") continue;\n input[key] = value;\n }\n args = [input];\n }\n } else {\n // Try to parse a plain form body as a fallback for progressive enhancement.\n const bodyResult = await readBodyWithLimit(request, bodyLimit);\n if (!bodyResult.ok) return { ok: false, response: bodyResult.response };\n const form = parseFormBody(bodyResult.text);\n name = form.__elur_js_action_name as string | undefined;\n page = form.__elur_js_action_page as string | undefined;\n const input: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(form)) {\n if (key === \"__elur_js_action_name\" || key === \"__elur_js_action_page\") continue;\n input[key] = value;\n }\n args = [input];\n }\n\n if (!name || typeof name !== \"string\") {\n return {\n ok: false,\n response: new Response(\"Missing action name\", {\n status: 400,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n\n return { ok: true, name, page, args, wantsJson };\n}\n\n/**\n * Handles a POST request to the server action endpoint.\n *\n * Accepts both JSON requests (`{ name, page?, args }`) and HTML form submissions\n * for progressive enhancement. The provided resolver looks up the action\n * implementation, invokes it with the supplied arguments and returns the result\n * as JSON or redirects back to the request origin for form submissions.\n *\n * Origin verification (CSRF protection) runs before parsing the body: any\n * cross-origin POST is rejected with 403 unless its origin is allow-listed via\n * `security.allowedOrigins`.\n *\n * For progressive-enhancement form submissions that fail, the failure payload\n * is relayed back via a short-lived `__elur_js_action_error` cookie (SameSite=Lax,\n * Max-Age=15s) instead of a query param, so errors do not leak into browser\n * history, server logs or third-party Referer headers.\n */\nexport async function handleActionRequest(\n request: Request,\n resolveAction: ActionResolver,\n security: ActionSecurityOptions = {},\n): Promise<Response> {\n // CSRF: verify same-origin (or allow-listed) before doing any work.\n const originError = verifyOrigin(request, security);\n if (originError) return originForbidden(originError);\n\n const parsed = await parseActionRequest(request, security.bodyLimit ?? DEFAULT_BODY_LIMIT);\n if (!parsed.ok) return parsed.response;\n\n const { name, page, args, wantsJson } = parsed;\n\n try {\n const action = await resolveAction(name, page);\n if (!action) {\n const message = page ? `Action not found: ${name} (page: ${page})` : `Action not found: ${name}`;\n return new Response(message, {\n status: 404,\n headers: { \"Content-Type\": \"text/plain\" },\n });\n }\n\n const result = await action(...args);\n\n if (isActionFailure(result)) {\n if (wantsJson) {\n return new Response(JSON.stringify({ __elur_js_action_failure: true, status: result.status, data: result.data }), {\n status: result.status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n // Progressive enhancement: redirect back with the failure in a cookie.\n const referer = request.headers.get(\"Referer\") ?? \"/\";\n const url = new URL(referer, \"http://localhost\");\n const { value } = encodeActionErrorCookie(result.data, result.status);\n return new Response(null, {\n status: 303,\n headers: {\n Location: url.pathname + url.search,\n \"Content-Type\": \"text/plain\",\n \"Set-Cookie\": setActionErrorCookieHeader(value),\n },\n });\n }\n\n if (isRedirectResponse(result)) {\n if (wantsJson) {\n return new Response(\n JSON.stringify({ __elur_js_action_redirect: true, status: result.status, location: result.location }),\n {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n },\n );\n }\n return new Response(null, {\n status: result.status,\n headers: { Location: result.location, \"Content-Type\": \"text/plain\" },\n });\n }\n\n if (wantsJson) {\n return new Response(JSON.stringify(result ?? null), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n // For progressive enhancement (plain form POST), redirect back.\n const referer = request.headers.get(\"Referer\") ?? \"/\";\n return new Response(null, {\n status: 303,\n headers: {\n Location: typeof result === \"string\" ? result : referer,\n \"Content-Type\": \"text/plain\",\n },\n });\n } catch (err) {\n console.error(\"[elur-kit] Action error:\", err);\n return publicErrorResponse(err, { includeDetail: false });\n }\n}\n\nexport { verifyOrigin, originForbidden, type OriginCheckOptions } from \"./origin.js\";\nexport {\n decodeActionErrorCookie,\n clearActionErrorCookieHeader,\n setActionErrorCookieHeader,\n ACTION_ERROR_COOKIE,\n} from \"./error-store.js\";\n","import type { IncomingMessage } from \"node:http\";\n\n// Capture the global AbortController at module load time so it's immune to\n// test frameworks that replace or delete globalThis.AbortController.\nconst GlobalAbortController =\n (globalThis as { AbortController?: typeof AbortController }).AbortController ?? AbortController;\n\nexport function incomingMessageToRequest(req: IncomingMessage, body?: BodyInit | null): Request {\n const headers = new Headers();\n for (let index = 0; index < req.rawHeaders.length; index += 2) {\n headers.append(req.rawHeaders[index], req.rawHeaders[index + 1]);\n }\n\n const controller = new GlobalAbortController();\n req.once(\"aborted\", () => controller.abort());\n req.once(\"close\", () => {\n if (!req.complete) controller.abort();\n });\n\n const protocol = (req.socket as typeof req.socket & { encrypted?: boolean }).encrypted ? \"https\" : \"http\";\n const init: RequestInit = {\n method: req.method ?? \"GET\",\n headers,\n signal: controller.signal,\n };\n if (body !== undefined && body !== null && init.method !== \"GET\" && init.method !== \"HEAD\") init.body = body;\n\n return new Request(`${protocol}://${headers.get(\"host\") ?? \"localhost\"}${req.url ?? \"/\"}`, init);\n}\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- const e=require("./errors-0X7pCFNU.cjs"),t=require("./node-http-DhxguYyz.cjs"),n=require("./scan-DXzYtmRQ.cjs"),r=require("./stream-DmZjYqHm.cjs");let i=require("node:fs/promises"),a=require("node:path"),o=require("node:crypto");function s(e,t){let n=(0,o.createHash)(`sha256`).update(t).digest(`hex`);return(0,a.join)(e,`${n}.html.json`)}async function c(e,t){let n=s(e,t);try{let e=await(0,i.readFile)(n,`utf8`),t=JSON.parse(e);if(Date.now()-t.generatedAt<t.revalidate*1e3)return t}catch{}}async function l(e,t,n,r){let c=s(e,t);await(0,i.mkdir)((0,a.dirname)(c),{recursive:!0});let l={html:n,generatedAt:Date.now(),revalidate:r},u=`${c}.${process.pid}.${(0,o.randomUUID)()}.tmp`;try{await(0,i.writeFile)(u,JSON.stringify(l),`utf8`),await(0,i.rename)(u,c)}finally{await(0,i.rm)(u,{force:!0})}}async function u(e){try{await(0,i.rm)(e,{recursive:!0,force:!0})}catch{}}function d(e,t){return t===e||t.startsWith(`${e}${a.sep}`)}function f(e){try{let t=decodeURIComponent(e);return t.includes(`\0`)||t.includes(`\\`)||/%(?:00|2e|2f|5c)/i.test(t)||t.split(`/`).some(e=>e===`..`)?null:t}catch{return null}}async function p(e,t){let n=f(t);if(n===null)return null;let r=(0,a.resolve)(e),o=n.replace(/^\/+/,``),s=(0,a.resolve)(r,o);if(!d(r,s))return null;try{(await(0,i.stat)(s)).isDirectory()&&(s=(0,a.resolve)(s,`index.html`))}catch{(n.endsWith(`/`)||(0,a.extname)(n)===``)&&(s=(0,a.resolve)(s,`index.html`))}if(!d(r,s))return null;try{let[e,t,n]=await Promise.all([(0,i.realpath)(r),(0,i.realpath)(s),(0,i.stat)(s)]);return!n.isFile()||!d(e,t)?null:t}catch{return null}}var m=class{cookies;constructor(e){this.cookies=g(e.headers.get(`Cookie`)??``)}get(e){return this.cookies[e]}getAll(){return{...this.cookies}}has(e){return e in this.cookies}},h=class{entries=[];set(e,t,n={}){this.entries.push(_(e,t,n))}clear(e,t={}){this.entries.push(_(e,``,{...t,maxAge:0,expires:new Date(0)}))}getAll(){return[...this.entries]}};function g(e){let t={};if(!e)return t;for(let n of e.split(`;`)){let e=n.indexOf(`=`);if(e===-1)continue;let r=n.slice(0,e).trim();t[r]=n.slice(e+1).trim()}return t}function _(e,t,n){let r=[`${e}=${t}`];return n.httpOnly&&r.push(`HttpOnly`),n.secure&&r.push(`Secure`),n.sameSite&&r.push(`SameSite=${n.sameSite}`),n.maxAge!==void 0&&r.push(`Max-Age=${n.maxAge}`),n.expires&&r.push(`Expires=${n.expires.toUTCString()}`),n.path&&r.push(`Path=${n.path}`),n.domain&&r.push(`Domain=${n.domain}`),r.join(`; `)}var v=class{request;url;config;routes;actions;publicActions;importer;renderEndpoint;noCache;cacheDir;defaultRevalidate;params;locals;cookies;signal;requestId;platform;route;response;constructor(e){this.request=e.request,this.url=new URL(e.request.url),this.config=e.config,this.routes=e.routes,this.actions=e.actions,this.publicActions=e.publicActions,this.importer=e.importer,this.renderEndpoint=e.renderEndpoint??!0,this.noCache=e.noCache??!1,this.cacheDir=e.cacheDir,this.defaultRevalidate=e.defaultRevalidate,this.params=e.params??{},this.locals=e.locals??{},this.cookies=new m(e.request),this.signal=e.signal??new AbortController().signal,this.requestId=e.requestId??(0,o.randomUUID)(),this.platform=e.platform,this.route=e.route,this.response={status:void 0,headers:new Headers,cookies:new h}}get pathname(){return this.url.pathname}get method(){return(this.request.method??`GET`).toUpperCase()}get wantsJson(){return(this.request.headers.get(`Accept`)??``).includes(`application/json`)}get searchParams(){return this.url.searchParams}get renderConfig(){return{lang:void 0,clientEntry:void 0,renderEndpoint:this.renderEndpoint}}applyToResponse(e){let t=new Headers(e.headers);for(let[e,n]of this.response.headers.entries())t.set(e,n);for(let e of this.response.cookies.getAll())t.append(`Set-Cookie`,e);let n=this.response.status??e.status;return new Response(e.body,{status:n,statusText:e.statusText,headers:t})}};function y(e,t=200,n){return new Response(e,{status:t,headers:{"Content-Type":`text/html; charset=utf-8`,...n}})}function b(e,t=200,n){return new Response(JSON.stringify(e),{status:t,headers:{"Content-Type":`application/json; charset=utf-8`,...n}})}function x(e,t=200,n){return new Response(e,{status:t,headers:{"Content-Type":`text/plain; charset=utf-8`,...n}})}function S(e=`Not Found`){return x(e,404)}function C(e){return x(`Method not allowed: ${e}`,405)}function w(e){return x(e,500)}function T(e){switch(e.slice(e.lastIndexOf(`.`)+1).toLowerCase()){case`html`:return`text/html; charset=utf-8`;case`js`:return`application/javascript; charset=utf-8`;case`mjs`:return`application/javascript; charset=utf-8`;case`css`:return`text/css; charset=utf-8`;case`json`:return`application/json; charset=utf-8`;case`svg`:return`image/svg+xml`;case`png`:return`image/png`;case`jpg`:case`jpeg`:return`image/jpeg`;case`webp`:return`image/webp`;case`avif`:return`image/avif`;case`ico`:return`image/x-icon`;case`woff`:return`font/woff`;case`woff2`:return`font/woff2`;case`wasm`:return`application/wasm`;case`txt`:return`text/plain; charset=utf-8`;default:return`application/octet-stream`}}async function E(e,t,n){let r=await p(e,t);if(!r)return null;try{let[e,t]=await Promise.all([(0,i.readFile)(r),(0,i.stat)(r)]),a=T(r),s=`"${(0,o.createHash)(`sha1`).update(e).digest(`hex`).slice(0,16)}"`,c=t.mtime.toUTCString(),l=n?.method===`HEAD`,u=e.byteLength,d={"Content-Type":a,"Content-Length":String(u),ETag:s,"Last-Modified":c,"Accept-Ranges":`bytes`},f=r.split(`/`).pop()??``;d[`Cache-Control`]=/[a-f0-9]{8,}\.(js|css|woff2?|wasm|png|jpg|jpeg|webp|avif|svg)$/i.test(f)?`public, max-age=31536000, immutable`:`public, max-age=0, must-revalidate`;let p=n?.headers.get(`If-None-Match`);if(p&&D(p,s))return new Response(null,{status:304,headers:d});let m=n?.headers.get(`If-Modified-Since`);if(m){let e=Date.parse(m);if(!isNaN(e)&&Math.floor(t.mtime.getTime()/1e3)<=Math.floor(e/1e3))return new Response(null,{status:304,headers:d})}let h=n?.headers.get(`Range`),g=n?.headers.get(`If-Range`);if(h&&(!g||O(g,s,t.mtime))){let t=k(h,u);if(t===null)return new Response(null,{status:416,headers:{...d,"Content-Range":`bytes */${u}`}});if(t){let[n,r]=t,i=e.subarray(n,r+1),a={...d,"Content-Length":String(i.byteLength),"Content-Range":`bytes ${n}-${r}/${u}`};return l?new Response(null,{status:206,headers:a}):new Response(i,{status:206,headers:a})}}return l?new Response(null,{status:200,headers:d}):new Response(e,{status:200,headers:d})}catch{return null}}function D(e,t){return e.split(`,`).map(e=>e.trim()).some(e=>e===`*`||e===t)}function O(e,t,n){if(e.startsWith(`"`)||e.startsWith(`W/`))return e===t;let r=Date.parse(e);return!isNaN(r)&&Math.floor(n.getTime()/1e3)<=Math.floor(r/1e3)}function k(e,t){let n=/^bytes=(\d*)-(\d*)$/.exec(e.trim());if(!n)return null;let r=n[1],i=n[2];if(r===``&&i===``)return null;if(r===``){let e=Number(i);if(!Number.isSafeInteger(e)||e<=0)return null;let n=Math.max(0,t-e);return t===0?void 0:[n,t-1]}let a=Number(r);if(!Number.isSafeInteger(a)||a<0||a>=t)return null;let o=i===``?t-1:Number(i);return!Number.isSafeInteger(o)||o<a?null:[a,Math.min(o,t-1)]}var A={noSniff:!0,referrerPolicy:`strict-origin-when-cross-origin`,frameAncestors:`SAMEORIGIN`};function j(e,t,n){if(e===!1)return{};let r={},i={...A,...e};if(i.noSniff&&(r[`X-Content-Type-Options`]=`nosniff`),i.referrerPolicy&&(r[`Referrer-Policy`]=i.referrerPolicy),i.contentSecurityPolicy){let e=i.contentSecurityPolicy;n&&(e=e.replace(/\bnonce\b/g,`'nonce-${n}'`)),r[`Content-Security-Policy`]=e}else if(i.frameAncestors){let e=i.frameAncestors;r[`X-Frame-Options`]=e===`NONE`?`DENY`:e===`SAMEORIGIN`?`SAMEORIGIN`:e}return i.hsts===!0&&t?r[`Strict-Transport-Security`]=`max-age=15552000; includeSubDomains`:typeof i.hsts==`string`&&(r[`Strict-Transport-Security`]=i.hsts),i.permissionsPolicy&&(r[`Permissions-Policy`]=i.permissionsPolicy),r}function M(e,t){if(Object.keys(t).length===0)return e;let n=new Headers(e.headers);for(let[e,r]of Object.entries(t))n.has(e)||n.set(e,r);return new Response(e.body,{status:e.status,statusText:e.statusText,headers:n})}function N(i,a,o){let s=n.t(a),u=o.lang??`es`,d=o.clientEntry,f=o.renderEndpoint??!0,p=o.noCache??!1,m=o.cacheDir,h=o.defaultRevalidate,g={lang:u,clientEntry:d,renderEndpoint:f},_=o.securityHeaders??{};function v(){return async(e,n)=>{let r=n?i.pages.some(e=>e.path===n)?n:t.o(n,i.pages)?.route.path??n:void 0,s=r?a[r]:Object.values(a).find(t=>t[e])??void 0,c=s?s[e]:void 0;if(!c)return;if(o.importer){let t=(await o.importer(c))[e];return typeof t==`function`?t:void 0}let l=(await import(c))[e];if(typeof l==`function`)return l}}let x=v();async function w(n){try{return await t.n(n,x)}catch(t){return console.error(`[elur-kit] action error:`,t),e.s(t,{includeDetail:p})}}async function T(t,n){let a=n.searchParams.get(`page`)??`/`,c=n.searchParams.get(`search`)??``,l=(t.headers.get(`Accept`)??``).includes(`application/json`);try{let{body:e,title:n}=await r.n({routes:i,pathname:a,searchParams:new URLSearchParams(c),config:g,actions:s,request:t,importer:o.importer});return l?b({title:n,body:e}):y(e)}catch(t){return t instanceof r.t?S(`Not Found`):t instanceof Response?t:(console.error(`[elur-kit] render endpoint error:`,t),e.s(t,{includeDetail:p}))}}async function D(n,r){let a=t.a(r,i.api);if(!a)return null;try{let e;e=o.importer?await o.importer(a.route.routePath):await import(a.route.routePath);let t=e[n.method??`GET`];return typeof t==`function`?await t(n,{params:a.params,locals:{}}):C(n.method??`GET`)}catch(t){return console.error(`[elur-kit] API route error:`,t),e.s(t,{includeDetail:p})}}async function O(e,t){let n=await E(o.staticRoot,e,t);if(n&&p){let e=n.headers.get(`Content-Type`)??``;if(e.includes(`text/html`)){let t=(await n.text()).replace(`<meta name="elur:render-endpoint" content="off" />`,``);return new Response(t,{status:n.status,headers:{"Content-Type":e,"Cache-Control":`no-store, must-revalidate`}})}return new Response(n.body,{status:n.status,headers:{...Object.fromEntries(n.headers.entries()),"Cache-Control":`no-store, must-revalidate`}})}if(n&&f&&(n.headers.get(`Content-Type`)??``).includes(`text/html`)){let e=Object.fromEntries(n.headers.entries());delete e[`content-length`];let t=await n.text();if(t.includes(`elur:render-endpoint" content="off"`)){let r=t.replace(`<meta name="elur:render-endpoint" content="off" />`,`<meta name="elur:render-endpoint" content="on" />`);return new Response(r,{status:n.status,headers:e})}return new Response(t,{status:n.status,headers:e})}return n}async function k(n,r){let a=t.o(r,i.pages);if(!a){let e=await t.c({routes:i,status:404,config:g,actions:s,importer:o.importer});return e?y(e.html,e.status):S(`Not found: ${r}`)}let u=!p&&m&&P(n);if(u&&m){let e=await c(m,r);if(e)return y(e.html)}try{let e=await t.l({route:a.route,params:a.params,searchParams:new URLSearchParams(n.url.split(`?`)[1]??``),config:g,actions:s,request:n,importer:o.importer});if(e.response)return e.response;if(u&&m&&F(e,n)){let t=e.revalidate??h??0;t>0&&await l(m,r,e.html,t)}return y(e.html)}catch(n){if(n instanceof Response)return n;console.error(`[elur-kit] SSR render error:`,n);let r=await t.c({routes:i,status:500,error:n,config:g,actions:s,importer:o.importer}).catch(()=>void 0);return r?y(r.html,r.status):e.s(n,{includeDetail:p})}}return async function(e){let t=new URL(e.url),n=t.pathname,r=t.protocol===`https:`,i=_===!1?{}:j(_,r);return n===`/__elur-js/actions`&&e.method===`POST`?M(await w(e),i):M(n===`/__elur-js/render`&&f?await T(e,t):await D(e,n)||await O(n,e)||await k(e,n),i)}}function P(e){return!(e.method!==`GET`&&e.method!==`HEAD`||e.headers.get(`Cookie`)||e.headers.get(`Authorization`))}function F(e,n){return e.html.includes(`__elur_js_action_error`)?!1:e.cachePolicy?t.u(e.cachePolicy,n):!(!e.revalidate||e.revalidate<=0||n.headers.get(`Cookie`)||n.headers.get(`Authorization`))}Object.defineProperty(exports,"_",{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,"d",{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,"f",{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,"g",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"h",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,"m",{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,"p",{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return N}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return S}});
2
- //# sourceMappingURL=runtime-CrH5X-Es.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"runtime-CrH5X-Es.cjs","names":[],"sources":["../../src/cache.ts","../../src/runtime/static.ts","../../src/runtime/context.ts","../../src/runtime/security-headers.ts","../../src/runtime/handler.ts"],"sourcesContent":["import { mkdir, readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { dirname, join } from \"node:path\";\n\nexport interface CacheEntry {\n html: string;\n generatedAt: number;\n revalidate: number;\n}\n\nexport interface CacheOptions {\n cacheDir: string;\n defaultRevalidate?: number;\n}\n\nfunction cachePath(cacheDir: string, pathname: string): string {\n const key = createHash(\"sha256\").update(pathname).digest(\"hex\");\n return join(cacheDir, `${key}.html.json`);\n}\n\nexport async function getCachedHtml(\n cacheDir: string,\n pathname: string,\n): Promise<CacheEntry | undefined> {\n const path = cachePath(cacheDir, pathname);\n try {\n const raw = await readFile(path, \"utf8\");\n const entry = JSON.parse(raw) as CacheEntry;\n if (Date.now() - entry.generatedAt < entry.revalidate * 1000) {\n return entry;\n }\n } catch {\n // cache miss or invalid\n }\n return undefined;\n}\n\nexport async function setCachedHtml(\n cacheDir: string,\n pathname: string,\n html: string,\n revalidate: number,\n): Promise<void> {\n const path = cachePath(cacheDir, pathname);\n await mkdir(dirname(path), { recursive: true });\n const entry: CacheEntry = { html, generatedAt: Date.now(), revalidate };\n const temporaryPath = `${path}.${process.pid}.${randomUUID()}.tmp`;\n try {\n await writeFile(temporaryPath, JSON.stringify(entry), \"utf8\");\n await rename(temporaryPath, path);\n } finally {\n await rm(temporaryPath, { force: true });\n }\n}\n\nexport async function isStale(cacheDir: string, pathname: string): Promise<boolean> {\n const path = cachePath(cacheDir, pathname);\n try {\n const raw = await readFile(path, \"utf8\");\n const entry = JSON.parse(raw) as CacheEntry;\n return Date.now() - entry.generatedAt >= entry.revalidate * 1000;\n } catch {\n return true;\n }\n}\n\nexport async function clearCache(cacheDir: string): Promise<void> {\n try {\n await rm(cacheDir, { recursive: true, force: true });\n } catch {\n // ignore\n }\n}\n","import { realpath, stat } from \"node:fs/promises\";\nimport { extname, resolve, sep } from \"node:path\";\n\nfunction isInside(root: string, candidate: string): boolean {\n return candidate === root || candidate.startsWith(`${root}${sep}`);\n}\n\nfunction decodePathname(pathname: string): string | null {\n try {\n const decoded = decodeURIComponent(pathname);\n if (decoded.includes(\"\\0\") || decoded.includes(\"\\\\\") || /%(?:00|2e|2f|5c)/i.test(decoded)) return null;\n if (decoded.split(\"/\").some((segment) => segment === \"..\")) return null;\n return decoded;\n } catch {\n return null;\n }\n}\n\nexport async function resolveStaticFile(root: string, pathname: string): Promise<string | null> {\n const decoded = decodePathname(pathname);\n if (decoded === null) return null;\n\n const resolvedRoot = resolve(root);\n const relativePath = decoded.replace(/^\\/+/, \"\");\n let candidate = resolve(resolvedRoot, relativePath);\n if (!isInside(resolvedRoot, candidate)) return null;\n\n try {\n const candidateStat = await stat(candidate);\n if (candidateStat.isDirectory()) candidate = resolve(candidate, \"index.html\");\n } catch {\n if (decoded.endsWith(\"/\") || extname(decoded) === \"\") candidate = resolve(candidate, \"index.html\");\n }\n\n if (!isInside(resolvedRoot, candidate)) return null;\n\n try {\n const [canonicalRoot, canonicalCandidate, candidateStat] = await Promise.all([\n realpath(resolvedRoot),\n realpath(candidate),\n stat(candidate),\n ]);\n if (!candidateStat.isFile() || !isInside(canonicalRoot, canonicalCandidate)) return null;\n return canonicalCandidate;\n } catch {\n return null;\n }\n}\n","import type { ResolvedElurConfig } from \"../config/index.js\";\nimport { randomUUID } from \"node:crypto\";\n\n// --- RequestContext: unified per-request runtime context ---\n//\n// Every runtime path (SSR server, CLI preview/dev, adapters, Vite plugin)\n// eventually funnels through a single Web handler that receives a Web Request\n// and returns a Web Response. RequestContext carries the resolved config,\n// route tables, action registry and request-scoped state so handlers do not\n// re-derive this information on every request.\n//\n// Design goals (runtime-security §4):\n// * One type used by every runtime entry point.\n// * No Node-specific APIs on the type — only Web standards.\n// * Carries per-request state: params, locals, cookies, signal, requestId.\n// * response.headers supports multiple Set-Cookie without collapsing them.\n// * signal aborts when the host disconnects (when the platform allows it).\n// * Middleware/loaders/actions share the same context or readonly views.\n\nexport interface RouteTable {\n pages: import(\"../router/route-scanner.js\").PageRoute[];\n api: import(\"../router/route-scanner.js\").ApiRoute[];\n error404?: import(\"../router/route-scanner.js\").PageRoute;\n error500?: import(\"../router/route-scanner.js\").PageRoute;\n}\n\n// --- CookieJar: read cookies from request, write to response ---\n\n/** Read-only access to request cookies. */\nexport interface CookieJar {\n /** Gets a cookie value by name, or undefined if not present. */\n get(name: string): string | undefined;\n /** Returns all cookie name-value pairs. */\n getAll(): Record<string, string>;\n /** Checks if a cookie exists. */\n has(name: string): boolean;\n}\n\n/** Write access to response cookies (Set-Cookie headers). */\nexport interface ResponseCookieJar {\n /** Sets a Set-Cookie header. */\n set(name: string, value: string, options?: CookieOptions): void;\n /** Removes a cookie by setting it expired. */\n clear(name: string, options?: CookieOptions): void;\n /** Returns all Set-Cookie header values accumulated so far. */\n getAll(): string[];\n}\n\nexport interface CookieOptions {\n httpOnly?: boolean;\n secure?: boolean;\n sameSite?: \"strict\" | \"lax\" | \"none\";\n maxAge?: number;\n expires?: Date;\n path?: string;\n domain?: string;\n}\n\n/** Mutable response state accumulated during the request lifecycle. */\nexport interface ResponseState {\n status?: number;\n headers: Headers;\n cookies: ResponseCookieJar;\n}\n\n// --- Cookie implementation ---\n\nclass RequestCookieJar implements CookieJar {\n private cookies: Record<string, string>;\n\n constructor(request: Request) {\n this.cookies = parseCookies(request.headers.get(\"Cookie\") ?? \"\");\n }\n\n get(name: string): string | undefined {\n return this.cookies[name];\n }\n\n getAll(): Record<string, string> {\n return { ...this.cookies };\n }\n\n has(name: string): boolean {\n return name in this.cookies;\n }\n}\n\nclass MutableResponseCookieJar implements ResponseCookieJar {\n private entries: string[] = [];\n\n set(name: string, value: string, options: CookieOptions = {}): void {\n this.entries.push(serializeCookie(name, value, options));\n }\n\n clear(name: string, options: CookieOptions = {}): void {\n this.entries.push(serializeCookie(name, \"\", { ...options, maxAge: 0, expires: new Date(0) }));\n }\n\n getAll(): string[] {\n return [...this.entries];\n }\n}\n\nfunction parseCookies(header: string): Record<string, string> {\n const result: Record<string, string> = {};\n if (!header) return result;\n for (const pair of header.split(\";\")) {\n const idx = pair.indexOf(\"=\");\n if (idx === -1) continue;\n const name = pair.slice(0, idx).trim();\n const value = pair.slice(idx + 1).trim();\n result[name] = value;\n }\n return result;\n}\n\nfunction serializeCookie(name: string, value: string, options: CookieOptions): string {\n const parts = [`${name}=${value}`];\n if (options.httpOnly) parts.push(\"HttpOnly\");\n if (options.secure) parts.push(\"Secure\");\n if (options.sameSite) parts.push(`SameSite=${options.sameSite}`);\n if (options.maxAge !== undefined) parts.push(`Max-Age=${options.maxAge}`);\n if (options.expires) parts.push(`Expires=${options.expires.toUTCString()}`);\n if (options.path) parts.push(`Path=${options.path}`);\n if (options.domain) parts.push(`Domain=${options.domain}`);\n return parts.join(\"; \");\n}\n\nexport interface RequestContextOptions {\n request: Request;\n config: ResolvedElurConfig;\n routes: RouteTable;\n actions: import(\"../action/scan.js\").ActionRegistry;\n /** Public action names serialized into the HTML shell. */\n publicActions: Record<string, string[]>;\n /** Optional module loader for adapter-bundled entries. */\n importer?: (path: string) => unknown | Promise<unknown>;\n /** Whether the render endpoint (/__elur-js/render) is available. */\n renderEndpoint?: boolean;\n /** Whether to bypass the ISR cache (dev mode). */\n noCache?: boolean;\n /** ISR cache directory (absolute). */\n cacheDir?: string;\n /** Default ISR revalidate interval in seconds. */\n defaultRevalidate?: number;\n /** Route params (populated after route matching). */\n params?: Record<string, string | string[] | undefined>;\n /** Per-request locals (populated by middleware). */\n locals?: Record<string, unknown>;\n /** Abort signal for the request (from host disconnect). */\n signal?: AbortSignal;\n /** Request ID (auto-generated if not provided). */\n requestId?: string;\n /** Platform-specific context (e.g. Vercel, Netlify). */\n platform?: unknown;\n /** Matched route (populated after route matching). */\n route?: import(\"../router/route-scanner.js\").PageRoute | import(\"../router/route-scanner.js\").ApiRoute;\n}\n\nexport class RequestContext {\n readonly request: Request;\n readonly url: URL;\n readonly config: ResolvedElurConfig;\n readonly routes: RouteTable;\n readonly actions: import(\"../action/scan.js\").ActionRegistry;\n readonly publicActions: Record<string, string[]>;\n readonly importer?: (path: string) => unknown | Promise<unknown>;\n readonly renderEndpoint: boolean;\n readonly noCache: boolean;\n readonly cacheDir?: string;\n readonly defaultRevalidate?: number;\n\n // Per-request state (runtime-security §4)\n /** Route params derived from the matched route. */\n params: Readonly<Record<string, string | string[] | undefined>>;\n /** Per-request locals, populated by middleware. Not global. */\n locals: Record<string, unknown>;\n /** Read-only access to request cookies. */\n readonly cookies: CookieJar;\n /** Abort signal (from host disconnect when platform allows). */\n readonly signal: AbortSignal;\n /** Unique request ID for logging/correlation. */\n readonly requestId: string;\n /** Platform-specific context (Vercel, Netlify, etc.). */\n readonly platform: unknown;\n /** Matched route after route matching. */\n route?: import(\"../router/route-scanner.js\").PageRoute | import(\"../router/route-scanner.js\").ApiRoute;\n /** Mutable response state accumulated during the request. */\n readonly response: ResponseState;\n\n constructor(options: RequestContextOptions) {\n this.request = options.request;\n this.url = new URL(options.request.url);\n this.config = options.config;\n this.routes = options.routes;\n this.actions = options.actions;\n this.publicActions = options.publicActions;\n this.importer = options.importer;\n this.renderEndpoint = options.renderEndpoint ?? true;\n this.noCache = options.noCache ?? false;\n this.cacheDir = options.cacheDir;\n this.defaultRevalidate = options.defaultRevalidate;\n\n // Per-request state\n this.params = options.params ?? {};\n this.locals = options.locals ?? {};\n this.cookies = new RequestCookieJar(options.request);\n this.signal = options.signal ?? new AbortController().signal;\n this.requestId = options.requestId ?? randomUUID();\n this.platform = options.platform;\n this.route = options.route;\n this.response = {\n status: undefined,\n headers: new Headers(),\n cookies: new MutableResponseCookieJar(),\n };\n }\n\n /** The pathname without a query string. */\n get pathname(): string {\n return this.url.pathname;\n }\n\n /** The HTTP method, uppercased. */\n get method(): string {\n return (this.request.method ?? \"GET\").toUpperCase();\n }\n\n /** Whether the request accepts JSON. */\n get wantsJson(): boolean {\n return (this.request.headers.get(\"Accept\") ?? \"\").includes(\"application/json\");\n }\n\n /** Search params from the request URL. */\n get searchParams(): URLSearchParams {\n return this.url.searchParams;\n }\n\n /** Render config passed to renderPage/renderErrorPage. */\n get renderConfig(): { lang?: string; clientEntry?: string; renderEndpoint?: boolean } {\n return {\n lang: undefined,\n clientEntry: undefined,\n renderEndpoint: this.renderEndpoint,\n };\n }\n\n /** Applies accumulated response state (headers, cookies, status) to a Response. */\n applyToResponse(response: Response): Response {\n const headers = new Headers(response.headers);\n // Merge accumulated headers\n for (const [key, value] of this.response.headers.entries()) {\n headers.set(key, value);\n }\n // Append Set-Cookie values (multiple allowed)\n for (const cookie of this.response.cookies.getAll()) {\n headers.append(\"Set-Cookie\", cookie);\n }\n const status = this.response.status ?? response.status;\n return new Response(response.body, {\n status,\n statusText: response.statusText,\n headers,\n });\n }\n}\n\n// --- ResponseBuilder: small helpers for consistent Web Responses ---\n\nexport function htmlResponse(body: string, status = 200, headers?: HeadersInit): Response {\n return new Response(body, {\n status,\n headers: { \"Content-Type\": \"text/html; charset=utf-8\", ...headers as Record<string, string> },\n });\n}\n\nexport function jsonResponse(data: unknown, status = 200, headers?: HeadersInit): Response {\n return new Response(JSON.stringify(data), {\n status,\n headers: { \"Content-Type\": \"application/json; charset=utf-8\", ...headers as Record<string, string> },\n });\n}\n\nexport function textResponse(body: string, status = 200, headers?: HeadersInit): Response {\n return new Response(body, {\n status,\n headers: { \"Content-Type\": \"text/plain; charset=utf-8\", ...headers as Record<string, string> },\n });\n}\n\nexport function notFound(body = \"Not Found\"): Response {\n return textResponse(body, 404);\n}\n\nexport function methodNotAllowed(method: string): Response {\n return textResponse(`Method not allowed: ${method}`, 405);\n}\n\nexport function serverError(body: string): Response {\n return textResponse(body, 500);\n}\n\n// --- Content-type guessing (shared by all static-serving paths) ---\n\nexport function guessContentType(filePath: string): string {\n switch (filePath.slice(filePath.lastIndexOf(\".\") + 1).toLowerCase()) {\n case \"html\": return \"text/html; charset=utf-8\";\n case \"js\": return \"application/javascript; charset=utf-8\";\n case \"mjs\": return \"application/javascript; charset=utf-8\";\n case \"css\": return \"text/css; charset=utf-8\";\n case \"json\": return \"application/json; charset=utf-8\";\n case \"svg\": return \"image/svg+xml\";\n case \"png\": return \"image/png\";\n case \"jpg\":\n case \"jpeg\": return \"image/jpeg\";\n case \"webp\": return \"image/webp\";\n case \"avif\": return \"image/avif\";\n case \"ico\": return \"image/x-icon\";\n case \"woff\": return \"font/woff\";\n case \"woff2\": return \"font/woff2\";\n case \"wasm\": return \"application/wasm\";\n case \"txt\": return \"text/plain; charset=utf-8\";\n default: return \"application/octet-stream\";\n }\n}\n\n// --- Static file serving as a Web handler (reuses resolveStaticFile) ---\n\nimport { readFile, stat } from \"node:fs/promises\";\nimport { createHash } from \"node:crypto\";\nimport { resolveStaticFile } from \"./static.js\";\n\n/**\n * Serves a static file from the root directory with full conditional and\n * range support:\n *\n * - ETag / Last-Modified with If-None-Match / If-Modified-Since → 304.\n * - `Range` with `If-Range` (ETag or date) → 206 with `Content-Range`.\n * - HEAD → same headers as GET without a body.\n * - Invalid/unsatisfiable ranges → 416 with a `Content-Range: bytes (asterisk)/size` header.\n *\n * Files with content hashes in their names (e.g. `app-abc123.js`) get\n * `Cache-Control: public, max-age=31536000, immutable`.\n *\n * @param root Static file root (absolute path).\n * @param pathname Request pathname.\n * @param request Optional request for conditional/range/HEAD handling.\n */\nexport async function serveStaticFile(\n root: string,\n pathname: string,\n request?: Request,\n): Promise<Response | null> {\n const filePath = await resolveStaticFile(root, pathname);\n if (!filePath) return null;\n try {\n const [data, stats] = await Promise.all([\n readFile(filePath),\n stat(filePath),\n ]);\n\n const contentType = guessContentType(filePath);\n const etag = `\"${createHash(\"sha1\").update(data).digest(\"hex\").slice(0, 16)}\"`;\n const lastModified = stats.mtime.toUTCString();\n const isHead = request?.method === \"HEAD\";\n const size = data.byteLength;\n\n const baseHeaders: Record<string, string> = {\n \"Content-Type\": contentType,\n \"Content-Length\": String(size),\n ETag: etag,\n \"Last-Modified\": lastModified,\n \"Accept-Ranges\": \"bytes\",\n };\n\n // Determine Cache-Control: hashed assets get immutable, others get a\n // short revalidation window.\n const baseName = filePath.split(\"/\").pop() ?? \"\";\n const isHashed = /[a-f0-9]{8,}\\.(js|css|woff2?|wasm|png|jpg|jpeg|webp|avif|svg)$/i.test(baseName);\n baseHeaders[\"Cache-Control\"] = isHashed\n ? \"public, max-age=31536000, immutable\"\n : \"public, max-age=0, must-revalidate\";\n\n // Conditional requests (If-None-Match takes precedence).\n const ifNoneMatch = request?.headers.get(\"If-None-Match\");\n if (ifNoneMatch && etagListMatches(ifNoneMatch, etag)) {\n return new Response(null, { status: 304, headers: baseHeaders });\n }\n const ifModifiedSince = request?.headers.get(\"If-Modified-Since\");\n if (ifModifiedSince) {\n const since = Date.parse(ifModifiedSince);\n if (!isNaN(since) && Math.floor(stats.mtime.getTime() / 1000) <= Math.floor(since / 1000)) {\n return new Response(null, { status: 304, headers: baseHeaders });\n }\n }\n\n // Range support with If-Range validation.\n const rangeHeader = request?.headers.get(\"Range\");\n const ifRange = request?.headers.get(\"If-Range\");\n if (rangeHeader && (!ifRange || ifRangeMatches(ifRange, etag, stats.mtime))) {\n const range = parseRange(rangeHeader, size);\n if (range === null) {\n return new Response(null, {\n status: 416,\n headers: { ...baseHeaders, \"Content-Range\": `bytes */${size}` },\n });\n }\n if (range) {\n const [start, end] = range;\n const chunk = data.subarray(start, end + 1);\n const headers: Record<string, string> = {\n ...baseHeaders,\n \"Content-Length\": String(chunk.byteLength),\n \"Content-Range\": `bytes ${start}-${end}/${size}`,\n };\n if (isHead) return new Response(null, { status: 206, headers });\n return new Response(chunk, { status: 206, headers });\n }\n }\n\n if (isHead) return new Response(null, { status: 200, headers: baseHeaders });\n return new Response(data, { status: 200, headers: baseHeaders });\n } catch {\n return null;\n }\n}\n\nfunction etagListMatches(ifNoneMatch: string, etag: string): boolean {\n return ifNoneMatch\n .split(\",\")\n .map((value) => value.trim())\n .some((value) => value === \"*\" || value === etag);\n}\n\nfunction ifRangeMatches(ifRange: string, etag: string, mtime: Date): boolean {\n if (ifRange.startsWith('\"') || ifRange.startsWith(\"W/\")) return ifRange === etag;\n const date = Date.parse(ifRange);\n return !isNaN(date) && Math.floor(mtime.getTime() / 1000) <= Math.floor(date / 1000);\n}\n\n/**\n * Parses a single `Range: bytes=...` header. Returns:\n * - `[start, end]` for a satisfiable range.\n * - `null` when the header is malformed or unsatisfiable (→ 416).\n * - `undefined` when the header is valid but the whole resource is requested\n * (e.g. `bytes=0-` for an empty file) — serve the full body.\n */\nfunction parseRange(rangeHeader: string, size: number): [number, number] | null | undefined {\n const match = /^bytes=(\\d*)-(\\d*)$/.exec(rangeHeader.trim());\n if (!match) return null;\n const startText = match[1];\n const endText = match[2];\n\n if (startText === \"\" && endText === \"\") return null;\n if (startText === \"\") {\n // Suffix range: last N bytes.\n const suffix = Number(endText);\n if (!Number.isSafeInteger(suffix) || suffix <= 0) return null;\n const start = Math.max(0, size - suffix);\n if (size === 0) return undefined;\n return [start, size - 1];\n }\n\n const start = Number(startText);\n if (!Number.isSafeInteger(start) || start < 0 || start >= size) return null;\n const end = endText === \"\" ? size - 1 : Number(endText);\n if (!Number.isSafeInteger(end) || end < start) return null;\n return [start, Math.min(end, size - 1)];\n}\n","// --- Security response headers (runtime-security §14) ---\n//\n// Applies configurable security headers to responses. Defaults are safe and\n// compatible: X-Content-Type-Options, Referrer-Policy, frame-ancestors.\n// HSTS is only applied under HTTPS or when explicitly configured.\n// CSP supports a \"nonce\" placeholder replaced per-request.\n// User-set headers on the response are never overwritten without explicit\n// merge rules.\n\nimport type { SecurityHeadersConfig } from \"../config/index.js\";\n\n/** Default security headers applied when `security.headers` is not `false`. */\nexport const DEFAULT_SECURITY_HEADERS: Required<\n Omit<SecurityHeadersConfig, \"contentSecurityPolicy\" | \"hsts\" | \"permissionsPolicy\">\n> = {\n noSniff: true,\n referrerPolicy: \"strict-origin-when-cross-origin\",\n frameAncestors: \"SAMEORIGIN\",\n};\n\n/**\n * Builds the security headers map from the resolved config.\n * Returns an empty map if headers are disabled.\n */\nexport function buildSecurityHeaders(\n config: SecurityHeadersConfig | false,\n isHttps: boolean,\n nonce?: string,\n): Record<string, string> {\n if (config === false) return {};\n\n const headers: Record<string, string> = {};\n const merged = { ...DEFAULT_SECURITY_HEADERS, ...config };\n\n if (merged.noSniff) {\n headers[\"X-Content-Type-Options\"] = \"nosniff\";\n }\n\n if (merged.referrerPolicy) {\n headers[\"Referrer-Policy\"] = merged.referrerPolicy;\n }\n\n // Frame policy: prefer CSP frame-ancestors if CSP is set, otherwise\n // X-Frame-Options for broader compatibility.\n if (merged.contentSecurityPolicy) {\n let csp = merged.contentSecurityPolicy;\n if (nonce) {\n csp = csp.replace(/\\bnonce\\b/g, `'nonce-${nonce}'`);\n }\n headers[\"Content-Security-Policy\"] = csp;\n } else if (merged.frameAncestors) {\n // Without CSP, use X-Frame-Options for frame protection.\n const fa = merged.frameAncestors;\n if (fa === \"NONE\") {\n headers[\"X-Frame-Options\"] = \"DENY\";\n } else if (fa === \"SAMEORIGIN\") {\n headers[\"X-Frame-Options\"] = \"SAMEORIGIN\";\n } else {\n headers[\"X-Frame-Options\"] = fa;\n }\n }\n\n // HSTS: only under HTTPS or when explicitly set as a string.\n if (merged.hsts === true && isHttps) {\n headers[\"Strict-Transport-Security\"] = \"max-age=15552000; includeSubDomains\";\n } else if (typeof merged.hsts === \"string\") {\n headers[\"Strict-Transport-Security\"] = merged.hsts;\n }\n\n if (merged.permissionsPolicy) {\n headers[\"Permissions-Policy\"] = merged.permissionsPolicy;\n }\n\n return headers;\n}\n\n/**\n * Applies security headers to an existing Response, preserving any\n * user-set headers unless overridden by security config.\n */\nexport function applySecurityHeaders(\n response: Response,\n headers: Record<string, string>,\n): Response {\n if (Object.keys(headers).length === 0) return response;\n\n const newHeaders = new Headers(response.headers);\n for (const [key, value] of Object.entries(headers)) {\n // Don't overwrite a header the response already set explicitly.\n if (!newHeaders.has(key)) {\n newHeaders.set(key, value);\n }\n }\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: newHeaders,\n });\n}\n","import { matchRoute, matchApiRoute } from \"../ssr/match.js\";\nimport { handleActionRequest, type ActionResolver } from \"../action/server.js\";\nimport { renderPage, renderErrorPage } from \"../ssr/render.js\";\nimport { renderPageBody, RouteNotFoundError } from \"../ssr/stream.js\";\nimport { actionNames } from \"../action/scan.js\";\nimport { serveStaticFile, htmlResponse, jsonResponse, notFound, methodNotAllowed } from \"./context.js\";\nimport { publicErrorResponse } from \"../errors.js\";\nimport { getCachedHtml, setCachedHtml } from \"../cache.js\";\nimport { shouldCachePublic, type CachePolicy } from \"../cache/policy.js\";\nimport { buildSecurityHeaders, applySecurityHeaders } from \"./security-headers.js\";\nimport type { SecurityHeadersConfig } from \"../config/index.js\";\n\n// --- Unified Web handler ---\n//\n// A single function that turns a Web Request into a Web Response. Every\n// runtime entry point (Node CLI, Bun adapter, Vercel, Netlify, Vite dev)\n// eventually calls this handler so behavior is identical across platforms.\n//\n// Responsibilities (in order):\n// 1. Server actions endpoint (/__elur-js/actions).\n// 2. SPA render endpoint (/__elur-js/render).\n// 3. API routes.\n// 4. Static files from the output directory.\n// 5. Dynamic SSR rendering for unmatched paths.\n// 6. 404 / 500 error pages.\n//\n// The handler is pure: it does not import Node HTTP types and can be used in\n// Bun, Deno, Cloudflare Workers, Vercel Edge, etc.\n\nexport interface WebHandlerOptions {\n /** Static file root (absolute path). Usually the build output directory. */\n staticRoot: string;\n /** Whether to bypass the ISR cache (dev mode). */\n noCache?: boolean;\n /** ISR cache directory (absolute). */\n cacheDir?: string;\n /** Default ISR revalidate interval in seconds. */\n defaultRevalidate?: number;\n /** Optional module loader for adapter-bundled entries. */\n importer?: (path: string) => Promise<unknown>;\n /** HTML lang attribute. */\n lang?: string;\n /** Client entry path. */\n clientEntry?: string;\n /** Whether the render endpoint exists. */\n renderEndpoint?: boolean;\n /** Security headers config (runtime-security §14). `false` disables. */\n securityHeaders?: SecurityHeadersConfig | false;\n}\n\nexport interface WebHandlerRouteTable {\n pages: import(\"../router/route-scanner.js\").PageRoute[];\n api: import(\"../router/route-scanner.js\").ApiRoute[];\n error404?: import(\"../router/route-scanner.js\").PageRoute;\n error500?: import(\"../router/route-scanner.js\").PageRoute;\n}\n\nexport interface WebHandlerActionRegistry {\n [pagePath: string]: Record<string, string>;\n}\n\nexport interface CreateWebHandlerResult {\n (request: Request): Promise<Response>;\n}\n\n/**\n * Create a unified Web handler from scanned routes, actions and options.\n *\n * The returned function is the single entry point for all runtimes.\n */\nexport function createWebHandler(\n routes: WebHandlerRouteTable,\n actions: WebHandlerActionRegistry,\n options: WebHandlerOptions,\n): CreateWebHandlerResult {\n const publicActions = actionNames(actions);\n const lang = options.lang ?? \"es\";\n const clientEntry = options.clientEntry;\n const renderEndpoint = options.renderEndpoint ?? true;\n const noCache = options.noCache ?? false;\n const cacheDir = options.cacheDir;\n const defaultRevalidate = options.defaultRevalidate;\n\n const renderConfig = { lang, clientEntry, renderEndpoint };\n const securityHeadersConfig = options.securityHeaders ?? {};\n\n function createActionResolver(): ActionResolver {\n return async (name: string, page?: string) => {\n const pageKey = page\n ? routes.pages.some((route) => route.path === page)\n ? page\n : (matchRoute(page, routes.pages)?.route.path ?? page)\n : undefined;\n const pageActions = pageKey ? actions[pageKey] : Object.values(actions).find((p) => p[name]) ?? undefined;\n const actionPath = pageActions ? pageActions[name] : undefined;\n if (!actionPath) return undefined;\n if (options.importer) {\n const mod = (await options.importer(actionPath)) as Record<string, unknown>;\n const action = mod[name];\n if (typeof action === \"function\") return action as (...args: unknown[]) => unknown;\n return undefined;\n }\n const mod = (await import(actionPath)) as Record<string, unknown>;\n const action = mod[name];\n if (typeof action === \"function\") return action as (...args: unknown[]) => unknown;\n return undefined;\n };\n }\n\n const actionResolver = createActionResolver();\n\n async function handleActions(request: Request): Promise<Response> {\n try {\n return await handleActionRequest(request, actionResolver);\n } catch (err) {\n console.error(\"[elur-kit] action error:\", err);\n return publicErrorResponse(err, { includeDetail: noCache });\n }\n }\n\n async function handleRenderEndpoint(request: Request, url: URL): Promise<Response> {\n const page = url.searchParams.get(\"page\") ?? \"/\";\n const search = url.searchParams.get(\"search\") ?? \"\";\n const wantsJson = (request.headers.get(\"Accept\") ?? \"\").includes(\"application/json\");\n try {\n const { body, title } = await renderPageBody({\n routes,\n pathname: page,\n searchParams: new URLSearchParams(search),\n config: renderConfig,\n actions: publicActions,\n request,\n importer: options.importer,\n });\n if (wantsJson) return jsonResponse({ title, body });\n return htmlResponse(body);\n } catch (err) {\n if (err instanceof RouteNotFoundError) return notFound(\"Not Found\");\n // A thrown Response from a loader is a first-class response (A-22).\n if (err instanceof Response) return err;\n console.error(\"[elur-kit] render endpoint error:\", err);\n return publicErrorResponse(err, { includeDetail: noCache });\n }\n }\n\n async function handleApiRoute(\n request: Request,\n pathname: string,\n ): Promise<Response | null> {\n const apiMatch = matchApiRoute(pathname, routes.api);\n if (!apiMatch) return null;\n try {\n let mod: Record<string, unknown>;\n if (options.importer) {\n mod = (await options.importer(apiMatch.route.routePath as unknown as string)) as Record<string, unknown>;\n } else {\n mod = (await import(apiMatch.route.routePath)) as Record<string, unknown>;\n }\n const handler = mod[request.method ?? \"GET\"];\n if (typeof handler !== \"function\") return methodNotAllowed(request.method ?? \"GET\");\n // Pass params and a writable locals object to the API handler\n // (runtime-security §4: params derived from the effective route).\n const ctx = { params: apiMatch.params, locals: {} as Record<string, unknown> };\n const response = (await (handler as (req: Request, ctx?: { params: Record<string, string | string[]>; locals: Record<string, unknown> }) => unknown)(request, ctx)) as Response;\n return response;\n } catch (err) {\n console.error(\"[elur-kit] API route error:\", err);\n return publicErrorResponse(err, { includeDetail: noCache });\n }\n }\n\n async function handleStatic(pathname: string, request: Request): Promise<Response | null> {\n const response = await serveStaticFile(options.staticRoot, pathname, request);\n if (response && noCache) {\n const ct = response.headers.get(\"Content-Type\") ?? \"\";\n if (ct.includes(\"text/html\")) {\n // Dev mode: strip the render-endpoint marker so the client router uses\n // the live /__elur-js/render endpoint for fast SPA navigation.\n const stripped = (await response.text())\n .replace('<meta name=\"elur:render-endpoint\" content=\"off\" />', \"\");\n return new Response(stripped, {\n status: response.status,\n headers: { \"Content-Type\": ct, \"Cache-Control\": \"no-store, must-revalidate\" },\n });\n }\n return new Response(response.body, {\n status: response.status,\n headers: { ...Object.fromEntries(response.headers.entries()), \"Cache-Control\": \"no-store, must-revalidate\" },\n });\n }\n if (response && renderEndpoint) {\n const ct = response.headers.get(\"Content-Type\") ?? \"\";\n if (ct.includes(\"text/html\")) {\n const headers = Object.fromEntries(response.headers.entries());\n delete headers[\"content-length\"];\n const body = await response.text();\n if (body.includes('elur:render-endpoint\" content=\"off\"')) {\n // The SSG build baked `render-endpoint content=\"off\"` so static\n // deployments never probe the endpoint. This server exposes\n // /__elur-js/render, so advertise it: SPA navigations fetch live\n // server-rendered content instead of the stale static file.\n const rewritten = body.replace(\n '<meta name=\"elur:render-endpoint\" content=\"off\" />',\n '<meta name=\"elur:render-endpoint\" content=\"on\" />',\n );\n return new Response(rewritten, { status: response.status, headers });\n }\n return new Response(body, { status: response.status, headers });\n }\n }\n return response;\n }\n\n async function handleDynamicRender(request: Request, pathname: string): Promise<Response> {\n const match = matchRoute(pathname, routes.pages);\n if (!match) {\n const errorResult = await renderErrorPage({\n routes,\n status: 404,\n config: renderConfig,\n actions: publicActions,\n importer: options.importer,\n });\n if (errorResult) return htmlResponse(errorResult.html, errorResult.status);\n return notFound(`Not found: ${pathname}`);\n }\n\n // ISR cache check (only when caching is enabled and the request is\n // cacheable — no cookies, no authorization header).\n const cacheable = !noCache && cacheDir && isCacheable(request);\n if (cacheable && cacheDir) {\n const cached = await getCachedHtml(cacheDir, pathname);\n if (cached) return htmlResponse(cached.html);\n }\n\n try {\n const result = await renderPage({\n route: match.route,\n params: match.params,\n searchParams: new URLSearchParams(request.url.split(\"?\")[1] ?? \"\"),\n config: renderConfig,\n actions: publicActions,\n request,\n importer: options.importer,\n });\n\n // If a loader threw a Response (redirect, 404, etc.), return it\n // as a first-class response (A-22).\n if (result.response) {\n return result.response;\n }\n\n if (cacheable && cacheDir && isResultCacheable(result, request)) {\n const revalidateSeconds = result.revalidate ?? defaultRevalidate ?? 0;\n if (revalidateSeconds > 0) {\n await setCachedHtml(cacheDir, pathname, result.html, revalidateSeconds);\n }\n }\n\n return htmlResponse(result.html);\n } catch (err) {\n // A thrown Response from a loader is a first-class response (A-22).\n if (err instanceof Response) return err;\n console.error(\"[elur-kit] SSR render error:\", err);\n const errorResult = await renderErrorPage({\n routes,\n status: 500,\n error: err,\n config: renderConfig,\n actions: publicActions,\n importer: options.importer,\n }).catch(() => undefined);\n if (errorResult) return htmlResponse(errorResult.html, errorResult.status);\n return publicErrorResponse(err, { includeDetail: noCache });\n }\n }\n\n return async function handler(request: Request): Promise<Response> {\n const url = new URL(request.url);\n const pathname = url.pathname;\n const isHttps = url.protocol === \"https:\";\n\n // Determine security headers (rebuild if nonce is needed).\n // HSTS is only applied under HTTPS; other headers apply always.\n const secHeaders = securityHeadersConfig === false\n ? {}\n : buildSecurityHeaders(securityHeadersConfig, isHttps);\n\n // 1. Server actions endpoint.\n if (pathname === \"/__elur-js/actions\" && request.method === \"POST\") {\n const response = await handleActions(request);\n return applySecurityHeaders(response, secHeaders);\n }\n\n // 2. SPA render endpoint.\n if (pathname === \"/__elur-js/render\" && renderEndpoint) {\n const response = await handleRenderEndpoint(request, url);\n return applySecurityHeaders(response, secHeaders);\n }\n\n // 3. API routes.\n const apiResponse = await handleApiRoute(request, pathname);\n if (apiResponse) return applySecurityHeaders(apiResponse, secHeaders);\n\n // 4. Static files.\n const staticResponse = await handleStatic(pathname, request);\n if (staticResponse) return applySecurityHeaders(staticResponse, secHeaders);\n\n // 5. Dynamic SSR rendering.\n const dynamicResponse = await handleDynamicRender(request, pathname);\n return applySecurityHeaders(dynamicResponse, secHeaders);\n };\n}\n\nfunction isCacheable(request: Request): boolean {\n if (request.method !== \"GET\" && request.method !== \"HEAD\") return false;\n if (request.headers.get(\"Cookie\")) return false;\n if (request.headers.get(\"Authorization\")) return false;\n return true;\n}\n\n/**\n * Checks whether a rendered page result is cacheable as public ISR.\n * Per runtime-security §9.1: uses the route's cache policy and checks\n * for personalized content markers.\n */\nfunction isResultCacheable(\n result: { revalidate?: number; html: string; cachePolicy?: CachePolicy },\n request: Request,\n): boolean {\n // If the HTML contains action error markers, it's personalized.\n if (result.html.includes(\"__elur_js_action_error\")) return false;\n // Use the route's cache policy if declared.\n if (result.cachePolicy) {\n return shouldCachePublic(result.cachePolicy, request);\n }\n // Fallback: cacheable only if revalidate > 0 and request is clean.\n if (!result.revalidate || result.revalidate <= 0) return false;\n if (request.headers.get(\"Cookie\")) return false;\n if (request.headers.get(\"Authorization\")) return false;\n return true;\n}\n"],"mappings":"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"}