@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-http-DVFcYeBS.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/ssr/render.ts","../../src/ssr/match.ts","../../src/action/origin.ts","../../src/action/server.ts","../../src/middleware/index.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 type SpeculationMode = \"prefetch\" | \"prerender\";\n\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 /**\n * Path to the client entry module, e.g. `/_elur/entry-client.js`. In split\n * builds this is the hydrate-only entry; callers gate it per page so it is\n * only emitted when the rendered body actually contains islands.\n */\n clientEntry?: string;\n /**\n * Path to the standalone client router module, e.g. `/_elur/router.js`\n * (split builds only). Emitted as a second `<script type=\"module\">` so pages\n * without islands still get SPA navigation without paying for the islands\n * entry.\n */\n routerEntry?: string;\n /**\n * Whether the client router is enabled for this page. When `false`, the\n * `elur:render-endpoint` meta is omitted entirely: no client router will run,\n * so there is nothing to advertise endpoint availability to.\n */\n routerEnabled?: boolean;\n /**\n * Speculation Rules API mode emitted as\n * `<script type=\"speculationrules\">` with document rules and\n * `eagerness: \"moderate\"`. Chromium-only progressive enhancement — other\n * browsers ignore the unknown script type. Only set this for static builds;\n * never apply to URLs reachable via server actions.\n */\n speculation?: SpeculationMode;\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\n/**\n * Explicit delimiters around the `#app` content. The streaming pipeline\n * (`createStreamingResponse`) and adapter render endpoints extract the page\n * body with these markers instead of parsing the shell layout by hand, so\n * changes to the shell markup never break extraction. They are HTML comments:\n * invisible, and ignored by hydration and the SPA router.\n */\nexport const APP_START_MARKER = \"<!--elur:app:start-->\";\nexport const APP_END_MARKER = \"<!--elur:app:end-->\";\n\n/**\n * Extracts the inner HTML of `#app` from a full document produced by\n * `documentShell`. Returns `undefined` when the markers are missing (e.g. a\n * hand-written document).\n */\nexport function extractAppBody(html: string): string | undefined {\n const start = html.indexOf(APP_START_MARKER);\n if (start < 0) return undefined;\n const end = html.indexOf(APP_END_MARKER, start + APP_START_MARKER.length);\n if (end < 0) return undefined;\n return html.slice(start + APP_START_MARKER.length, end);\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 */\nexport function 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/**\n * Document-level Speculation Rules (Chromium-only, ignored elsewhere).\n *\n * `href_matches: \"/*\"` scopes the rule to same-origin path links; the\n * `selector_matches` exclusions keep downloads, new-tab links, router-opt-outs\n * and explicit `data-no-speculation` links out of speculation. Actions are\n * POST endpoints reached through forms/`callAction`, never through document\n * links, so they are not speculated. `eagerness: \"moderate\"` speculates on\n * hover — the same trigger as the client router's prefetch.\n */\nfunction speculationRulesScript(mode: SpeculationMode): string {\n const rules = {\n [mode]: [\n {\n source: \"document\",\n where: {\n and: [\n { href_matches: \"/*\" },\n {\n not: {\n selector_matches:\n \"a[download], a[target], a[data-no-router], a[data-no-speculation]\",\n },\n },\n ],\n },\n eagerness: \"moderate\",\n },\n ],\n };\n return `\\n <script type=\"speculationrules\">${JSON.stringify(rules)}</script>`;\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, routerEntry, 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 // Every emitted module script also gets a <link rel=\"modulepreload\"> so the\n // fetch starts during HTML parsing instead of waiting for the deferred\n // script discovery (Fase 8.5 — paso 1).\n const modulePreload = (src: string) =>\n `\\n <link rel=\"modulepreload\" href=\"${escapeHtml(src)}\" />`;\n\n const preloads =\n (clientEntry ? modulePreload(clientEntry) : \"\") +\n (routerEntry ? modulePreload(routerEntry) : \"\");\n\n const entryScript = clientEntry\n ? `\\n <script type=\"module\" src=\"${escapeHtml(clientEntry)}\"></script>`\n : \"\";\n\n const routerScript = routerEntry\n ? `\\n <script type=\"module\" src=\"${escapeHtml(routerEntry)}\"></script>`\n : \"\";\n\n const speculationScript = opts.speculation\n ? speculationRulesScript(opts.speculation)\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 // The render-endpoint marker only exists for the client router; when the\n // router is disabled for the page there is nothing to advertise.\n const renderEndpointMeta =\n opts.renderEndpoint === false && opts.routerEnabled !== 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}${preloads}${speculationScript}\n </head>\n <body>\n <div id=\"app\">${APP_START_MARKER}${body}${APP_END_MARKER}</div>${dataScript}${actionsScript}${entryScript}${routerScript}\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","import type { ElurTemplate } from \"@elurjs/core\";\nimport { renderToString } from \"../render/render-to-string.js\";\nimport { documentShell, buildHeadTags } from \"../build/document-shell.js\";\nimport { ISLAND_MARKER_ATTR } from \"../island/island.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\" | \"router\" | \"js\">;\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 * Loader data as rendered into `<script id=\"elur-data\">`. Exposed so the\n * SPA render endpoint can ship it in the payload and the client router can\n * keep the serialized data fresh across navigations.\n */\n data?: unknown;\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 // --- 0% JS gating (Fase 8.2) ---\n // Scan the rendered body for island markers. A false positive (e.g. a\n // `data-elur-island` string inside user markdown) only loads the hydration\n // entry unnecessarily — benign. A false negative would mean dead islands in\n // production, which is why we scan output instead of tracking render context.\n const hasIslands = body.includes(ISLAND_MARKER_ATTR);\n\n // Decide which module scripts the shell emits. Three modes:\n // legacy (`js: \"legacy\"` or no router config at all): the combined\n // entry is emitted unconditionally — the pre-Fase-8 behavior.\n // split (router.entry set): entry-client hydrates islands only, the\n // router lives in its own chunk → emit router.js whenever the\n // router is enabled, and entry-client only when islands exist.\n // combined (router configured, no entry): the entry embeds the router\n // (single-input bundles) → emit it when there are islands or the\n // router is on; a page with neither ships 0 KB of JS.\n const routerCfg = config.router;\n const routerEnabled = routerCfg?.enabled !== false;\n let clientEntry: string | undefined;\n let routerEntry: string | undefined;\n if (!routerCfg || config.js === \"legacy\") {\n clientEntry = config.clientEntry;\n } else if (routerCfg.entry) {\n if (hasIslands) clientEntry = config.clientEntry;\n if (routerEnabled) routerEntry = routerCfg.entry;\n } else if (hasIslands || routerEnabled) {\n clientEntry = config.clientEntry;\n }\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,\n routerEntry,\n routerEnabled: routerCfg ? routerEnabled : undefined,\n speculation: routerCfg?.speculation,\n renderEndpoint: config.renderEndpoint,\n });\n\n const head = metadata ? buildHeadTags(metadata, resolvedTitle) : \"\";\n return { html, revalidate, clearActionErrorCookie, head, resolvedTitle, cachePolicy, data };\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\" | \"router\" | \"js\">;\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 type { ActionContext } from \"./define.js\";\nimport { defaultInvalidator } from \"../cache/invalidation.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 // defineAction() functions take (input, ctx) and carry __elurAction\n // metadata; legacy plain actions take (...args). Route params are not\n // known at this endpoint (actions resolve by page path), so params and\n // locals start empty — middleware/page context can fill them elsewhere.\n const actionMeta = (action as { __elurAction?: { invalidateTags?: readonly string[]; invalidatePaths?: readonly string[] } }).__elurAction;\n let result: unknown;\n if (actionMeta) {\n const ctx: ActionContext = {\n request,\n signal: request.signal,\n idempotencyKey: request.headers.get(\"Idempotency-Key\") ?? undefined,\n params: {},\n locals: {},\n };\n result = await action(args[0], ctx);\n } else {\n result = await action(...args);\n }\n\n // Cache invalidation (§9.4): actions defined with defineAction() declare\n // invalidateTags/invalidatePaths metadata; dispatch them to connected\n // cache adapters after a successful run (not on ActionFailure).\n if (!isActionFailure(result) && actionMeta) {\n const tags = actionMeta.invalidateTags ?? [];\n const paths = actionMeta.invalidatePaths ?? [];\n if (tags.length > 0 || paths.length > 0) {\n await defaultInvalidator.emit({ tags, paths, source: name });\n }\n }\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","// --- Middleware ---\n//\n// Convention: `src/middleware.ts` in the project root exports a default\n// function and an optional `config` with a `matcher` array.\n//\n// import type { Middleware } from \"@elurjs/kit\";\n//\n// export default function middleware(request: Request) {\n// if (!request.headers.get(\"Cookie\")?.includes(\"session=\")) {\n// return Response.redirect(new URL(\"/login\", request.url), 307);\n// }\n// }\n//\n// export const config = { matcher: [\"/dashboard/:path*\", \"/admin/:path*\"] };\n//\n// The middleware runs before routing. Return a `Response` to short-circuit\n// (redirect, rewrite, 401, etc.). Return `undefined` or nothing to continue.\n// Use `next()` to pass headers to the loader.\n\nimport { matchRoute } from \"../ssr/match.js\";\nimport type { PageRoute } from \"../router/route-scanner.js\";\n\n/** The middleware function signature. */\nexport type Middleware = (request: Request, context: MiddlewareContext) =>\n | Response\n | void\n | Promise<Response | void>;\n\n/** Context passed to the middleware function. */\nexport interface MiddlewareContext {\n /** Helper to continue to the next handler. Can attach headers, params, and locals. */\n next(options?: {\n headers?: Record<string, string>;\n params?: Record<string, string | string[]>;\n locals?: Record<string, unknown>;\n }): void;\n /** Matched route params (only available if the path matches a page route). */\n params?: Record<string, string | string[]>;\n /** Per-request locals (populated by middleware, available to loaders/actions). */\n locals?: Record<string, unknown>;\n}\n\n/** Configuration for the middleware module. */\nexport interface MiddlewareConfig {\n /** Path patterns that trigger the middleware. Supports `:param` and `:param*`. */\n matcher?: string[];\n}\n\nexport interface LoadedMiddleware {\n handler: Middleware;\n config: MiddlewareConfig;\n}\n\n/** Result of running middleware: either a response to short-circuit with, or continue. */\nexport type MiddlewareResult =\n | { kind: \"response\"; response: Response }\n | {\n kind: \"continue\";\n headers?: Record<string, string>;\n params?: Record<string, string | string[]>;\n locals?: Record<string, unknown>;\n };\n\n/**\n * Loads the user's `src/middleware.ts` module. Returns `null` if no middleware\n * file exists. Distinguishes \"file not found\" from \"file has errors\" (§6):\n * an import error is not silently treated as \"no middleware\".\n */\nexport async function loadMiddleware(root: string): Promise<LoadedMiddleware | null> {\n const candidates = [\n `${root}/src/middleware.ts`,\n `${root}/middleware.ts`,\n ];\n\n for (const path of candidates) {\n try {\n const mod = await import(path);\n const handler = (mod.default ?? mod.middleware) as Middleware | undefined;\n if (typeof handler !== \"function\") continue;\n const config = (mod.config ?? {}) as MiddlewareConfig;\n return { handler, config };\n } catch (err) {\n // Distinguish \"module not found\" from actual errors.\n // If the error is a module resolution error for this specific file,\n // it means the file doesn't exist — try the next candidate.\n // If it's a syntax/runtime error, rethrow so the user sees it.\n // Note: Bun's ResolveMessage is not `instanceof Error`, so match on the\n // message property instead of relying on the class hierarchy.\n const msg =\n typeof err === \"object\" && err !== null && \"message\" in err\n ? String((err as { message: unknown }).message)\n : String(err);\n if (\n msg.includes(\"Cannot find module\") ||\n msg.includes(\"Cannot find package\") ||\n msg.includes(\"ENOENT\") ||\n msg.includes(\"Module not found\")\n ) {\n // File doesn't exist — try next candidate.\n continue;\n }\n // Actual error in the middleware file — rethrow (§6).\n throw new Error(`[elur-kit] Error loading middleware: ${msg}`, { cause: err });\n }\n }\n\n return null;\n}\n\n/**\n * Checks if a pathname matches any of the middleware's matcher patterns.\n * If no matcher is configured, the middleware runs for every request.\n *\n * Catch-all patterns (`:param*`) match both the base path and any sub-paths,\n * e.g. `/dashboard/:path*` matches `/dashboard` and `/dashboard/settings/users`.\n */\nexport function matchesMiddleware(pathname: string, config: MiddlewareConfig): boolean {\n if (!config.matcher || config.matcher.length === 0) return true;\n\n const cleanPath = pathname.split(\"?\")[0];\n\n for (const pattern of config.matcher) {\n // Exact match.\n if (pattern === cleanPath) return true;\n\n // Check for catch-all: `/foo/:bar*` should also match `/foo`.\n const catchAllMatch = pattern.match(/^(.*)\\/:[\\w]+\\*$/);\n if (catchAllMatch) {\n const base = catchAllMatch[1];\n if (cleanPath === base) return true;\n }\n\n // Use matchRoute for param matching.\n const pseudoRoutes: PageRoute[] = [{\n path: pattern,\n pagePath: \"\",\n params: [],\n layouts: [],\n }];\n if (matchRoute(cleanPath, pseudoRoutes)) return true;\n }\n\n return false;\n}\n\n/**\n * Runs the middleware for a request. Returns the result indicating whether to\n * short-circuit with a response or continue with propagated headers/params/locals.\n *\n * Per §6: cleanup runs in `finally`, response short-circuits the pipeline,\n * headers/params/locals are propagated to downstream handlers.\n */\nexport async function runMiddleware(\n middleware: LoadedMiddleware,\n request: Request,\n params?: Record<string, string | string[]>,\n): Promise<MiddlewareResult> {\n let nextHeaders: Record<string, string> | undefined;\n let nextParams: Record<string, string | string[]> | undefined;\n let nextLocals: Record<string, unknown> | undefined;\n const cleanups: Array<() => void | Promise<void>> = [];\n\n const context: MiddlewareContext = {\n next(options) {\n if (options?.headers) nextHeaders = options.headers;\n if (options?.params) nextParams = options.params;\n if (options?.locals) nextLocals = options.locals;\n },\n params,\n locals: {},\n };\n\n try {\n const result = await middleware.handler(request, context);\n\n if (result instanceof Response) {\n return { kind: \"response\", response: result };\n }\n\n return {\n kind: \"continue\",\n headers: nextHeaders,\n params: nextParams ?? params,\n locals: nextLocals,\n };\n } finally {\n // Run any cleanup functions (§6). Errors in cleanup are logged but\n // do not propagate to the caller.\n for (const cleanup of cleanups) {\n try {\n await cleanup();\n } catch (err) {\n console.error(\"[elur-kit] middleware cleanup error:\", err);\n }\n }\n }\n}\n","import type { IncomingMessage, ServerResponse } 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\n/**\n * Writes a Web `Response` to a Node `ServerResponse`, streaming the body.\n *\n * Unlike `res.end(Buffer.from(await response.arrayBuffer()))`, this forwards\n * chunks as they are produced — required for streaming SSR responses to reach\n * the client progressively. Buffered (non-stream) bodies behave exactly as\n * before. Honors backpressure (`drain`) and cancels the upstream stream when\n * the client disconnects (`close` before `finish`).\n */\nexport async function sendWebResponse(res: ServerResponse, response: Response): Promise<void> {\n const headers = Object.fromEntries(response.headers.entries());\n if (response.body !== null) {\n // Chunk boundaries are decided as the body is read; a Content-Length\n // captured earlier would be wrong for streams. Transfer-Encoding is\n // managed by Node itself (it chunks when no length is set) — forwarding\n // it would duplicate the header (`chunked, chunked`).\n delete headers[\"content-length\"];\n delete headers[\"transfer-encoding\"];\n }\n res.writeHead(response.status, headers);\n\n const body = response.body;\n if (!body) {\n res.end();\n return;\n }\n\n const reader = body.getReader();\n let done = false;\n const onClose = () => {\n if (!done) void reader.cancel().catch(() => {});\n };\n res.once(\"close\", onClose);\n\n try {\n for (;;) {\n const { done: readDone, value } = await reader.read();\n if (readDone) break;\n if (value && value.byteLength > 0 && !res.write(value)) {\n // Socket buffer is full: wait for it to drain before reading more.\n await new Promise<void>((resolveDrain) => res.once(\"drain\", resolveDrain));\n }\n }\n done = true;\n res.end();\n } catch {\n done = true;\n // The client went away or the upstream stream failed: tear the socket\n // down instead of leaving a half-written response hanging.\n res.destroy();\n } finally {\n res.removeListener(\"close\", onClose);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ const e=require("./island-kfQEKRe_.cjs"),t=require("./invalidation-CFpNQUv6.cjs"),n=require("./errors-kSl9yLkg.cjs");let r=require("@elurjs/core/server"),i=require("node:fs"),a=require("node:crypto");var o=Symbol.for(`@elurjs/core/reactivity-state`);function s(){return globalThis[o]}function c(e){let t=s();t&&(t.ssr=e)}function l(){return s()?.ssr??!1}async function u(e,t={}){c(!0);try{return await(0,r.renderToString)(e(),{markers:t.markers??`hydration`})}finally{c(!1)}}var d={"&":`&amp;`,"<":`&lt;`,">":`&gt;`,'"':`&quot;`,"'":`&#39;`},f=`<!--elur:app:start-->`,p=`<!--elur:app:end-->`;function m(e){let t=e.indexOf(f);if(t<0)return;let n=e.indexOf(p,t+21);if(!(n<0))return e.slice(t+21,n)}function h(e){return e.replace(/[&<>"']/g,e=>d[e])}function g(e){return JSON.stringify(e??null).replace(/</g,`\\u003c`)}function _(e,t){let n=[],r=e.title??t;e.title&&n.push(`<title data-elur-head>${h(r)}</title>`),e.description&&n.push(`<meta data-elur-head name="description" content="${h(e.description)}" />`),e.canonical&&n.push(`<link data-elur-head rel="canonical" href="${h(e.canonical)}" />`),e.robots&&n.push(`<meta data-elur-head name="robots" content="${h(e.robots)}" />`);let i=e.openGraph;i&&(i.type&&n.push(`<meta data-elur-head property="og:type" content="${h(i.type)}" />`),n.push(`<meta data-elur-head property="og:title" content="${h(i.title??r)}" />`),(i.description??e.description)&&n.push(`<meta data-elur-head property="og:description" content="${h(i.description??e.description)}" />`),(i.url??e.canonical)&&n.push(`<meta data-elur-head property="og:url" content="${h(i.url??e.canonical)}" />`),i.image&&n.push(`<meta data-elur-head property="og:image" content="${h(i.image)}" />`),i.image&&i.imageAlt&&n.push(`<meta data-elur-head property="og:image:alt" content="${h(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="${h(i.imageType)}" />`),i.siteName&&n.push(`<meta data-elur-head property="og:site_name" content="${h(i.siteName)}" />`),i.locale&&n.push(`<meta data-elur-head property="og:locale" content="${h(i.locale)}" />`));let a=e.twitter;if(a&&(a.card&&n.push(`<meta data-elur-head name="twitter:card" content="${h(a.card)}" />`),(a.title??r)&&n.push(`<meta data-elur-head name="twitter:title" content="${h(a.title??r)}" />`),(a.description??e.description)&&n.push(`<meta data-elur-head name="twitter:description" content="${h(a.description??e.description)}" />`),a.image&&n.push(`<meta data-elur-head name="twitter:image" content="${h(a.image)}" />`),a.image&&a.imageAlt&&n.push(`<meta data-elur-head name="twitter:image:alt" content="${h(a.imageAlt)}" />`)),e.other)for(let[t,r]of Object.entries(e.other))n.push(`<meta data-elur-head name="${h(t)}" content="${h(r)}" />`);return n.map(e=>`\n ${e}`).join(``)}function v(e){return`\n <script type="speculationrules">${JSON.stringify({[e]:[{source:`document`,where:{and:[{href_matches:`/*`},{not:{selector_matches:`a[download], a[target], a[data-no-router], a[data-no-speculation]`}}]},eagerness:`moderate`}]})}<\/script>`}function y(e){let{body:t,title:n=`Elur Kit App`,lang:r=`es`,data:i,actions:a,clientEntry:o,routerEntry:s,htmlAttributes:c,headScripts:l,headLinks:u,metadata:d}=e,m=i===void 0?``:`\n <script type="application/json" id="elur-data">${g(i)}<\/script>`,y=a&&Object.keys(a).length>0?`\n <script type="application/json" id="elur-actions">${g(a)}<\/script>`:``,b=e=>`\n <link rel="modulepreload" href="${h(e)}" />`,x=(o?b(o):``)+(s?b(s):``),S=o?`\n <script type="module" src="${h(o)}"><\/script>`:``,C=s?`\n <script type="module" src="${h(s)}"><\/script>`:``,w=e.speculation?v(e.speculation):``,T=c?Object.entries(c).filter(([,e])=>e!=null&&e!==``).map(([e,t])=>` ${h(e)}="${h(String(t))}"`).join(``):``,E=l?l.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(``):``,D=d?_(d,n):``,O=d?.title?``:`\n <title>${h(n)}</title>`,k=u?u.filter(e=>typeof e==`string`&&e.trim().length>0).map(e=>`\n ${e}`).join(``):``,A=e.renderEndpoint===!1&&e.routerEnabled!==!1?`
2
+ <meta name="elur:render-endpoint" content="off" />`:``;return`<!DOCTYPE html>
3
+ <html lang="${h(r)}"${T}>
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />${A}${O}${D}${k}${E}${x}${w}
7
+ </head>
8
+ <body>
9
+ <div id="app">${f}${t}${p}</div>${m}${y}${S}${C}
10
+ </body>
11
+ </html>
12
+ `}var b=`__elur_js_action_error`,x=3500,S=15e3,C=process.env.ELUR_JS_ACTION_SECRET??(0,a.randomBytes)(32).toString(`hex`),w=new Map,T=!1;function E(){T||(T=!0,setTimeout(()=>{T=!1;let e=Date.now();for(let[t,n]of w)n.expiresAt<=e&&w.delete(t)},S).unref?.())}function D(e){return`${(0,a.createHmac)(`sha256`,C).update(e).digest(`hex`)}.${e}`}function O(e){let t=e.indexOf(`.`);if(t===-1)return;let n=e.slice(0,t),r=e.slice(t+1),i=(0,a.createHmac)(`sha256`,C).update(r).digest(`hex`);if(n.length===i.length)try{if((0,a.timingSafeEqual)(Buffer.from(n),Buffer.from(i)))return r}catch{}}function k(e,t){let n=JSON.stringify({d:e,s:t}),r=D(Buffer.from(n,`utf8`).toString(`base64url`));if(r.length<=x)return{value:r};let i=(0,a.randomBytes)(12).toString(`hex`);return w.set(i,{data:e,status:t,expiresAt:Date.now()+S}),E(),{value:D(`id:${i}`),storeId:i}}function A(e){if(!e)return;let t=O(e);if(t!==void 0){if(t.startsWith(`id:`)){let e=t.slice(3),n=w.get(e);return!n||(w.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 j=b;function M(){return`${b}=; Path=/; Max-Age=0; SameSite=Lax`}function N(e){return`${b}=${e}; Path=/; Max-Age=15; SameSite=Lax; HttpOnly`}var P=e=>import(e);function F(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 I(n){let{route:r,params:a={},searchParams:o=new URLSearchParams,config:s,importer:c=P,actions:l,request:d}=n,{default:f,generateMetadata:p}=await c(r.pagePath),m,h,g,v={response:void 0};if(r.dataPath){let e=await c(r.dataPath);if(e.load)try{m=await e.load({params:a,searchParams:o,request:d})}catch(e){if(e instanceof Response)v.response=e;else throw e}typeof e.revalidate==`number`&&(h=e.revalidate),e.cache&&(g=t.a(e.cache),g.revalidate>0&&(h=g.revalidate))}if(v.response)return{html:``,response:v.response,status:v.response.status};let b,x;if(d){let e=(d.headers.get(`Cookie`)??``).match(RegExp(`(?:^|;\\s*)${j}=([^;]+)`));if(e){let t=A(e[1]);t&&(b={__elur_js_action_error:!0,status:t.status,data:t.data},x=`${j}=; Path=/; Max-Age=0; SameSite=Lax`)}}let S={data:m??{},params:a,searchParams:o,form:b},C=await Promise.all(r.layouts.map(async e=>c(e))),w=await Promise.all(r.layouts.map(async e=>{let t=e.replace(/layout\.ts$/,`layout.data.ts`);if(!(0,i.existsSync)(t))return;let n=await c(t);if(n.load)try{return await n.load({params:a,searchParams:o,request:d})}catch(e){if(e instanceof Response){v.response=e;return}throw e}})),T=v.response;if(T)return{html:``,response:T,status:T.status};let E;if(r.slots){E={};for(let[e,t]of Object.entries(r.slots)){let n=await c(t);E[e]=n.default(S)}}let D=await u(()=>{let e=f(S);for(let t=C.length-1;t>=0;t--){let{default:n}=C[t];e=n({children:e,data:w[t],slots:E})}return e}),O=typeof m==`object`&&m&&`title`in m?String(m.title??`Elur Kit`):`Elur Kit`,{htmlAttributes:k,headScripts:M,headLinks:N}=F(m,w),I;typeof p==`function`&&(I=await p({params:a,searchParams:o,request:d,data:m})),I||=L(m)??R(w);let z=I?.title??O,B=D.includes(e.t),V=s.router,H=V?.enabled!==!1,U,W;!V||s.js===`legacy`?U=s.clientEntry:V.entry?(B&&(U=s.clientEntry),H&&(W=V.entry)):(B||H)&&(U=s.clientEntry);let G=y({title:z,lang:s.lang,body:D,data:m,actions:l,htmlAttributes:k,headScripts:M,headLinks:N,metadata:I,clientEntry:U,routerEntry:W,routerEnabled:V?H:void 0,speculation:V?.speculation,renderEndpoint:s.renderEndpoint}),K=I?_(I,z):``;return{html:G,revalidate:h,clearActionErrorCookie:x,head:K,resolvedTitle:z,cachePolicy:g,data:m}}function L(e){if(e&&typeof e==`object`&&`metadata`in e){let t=e.metadata;if(t&&typeof t==`object`)return t}}function R(e){for(let t of e){let e=L(t);if(e)return e}}async function z(e){let t=e.status===404?e.routes.error404:e.routes.error500;if(t)try{let{html:n}=await I({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 B(e,t){let n=e.split(`?`)[0].split(`/`).filter(Boolean).map(H),r=[...t].sort((e,t)=>U(t.path)-U(e.path));for(let e of r){let t=W(n,e.path.split(`/`).filter(Boolean),e.optionalCatchAll);if(t)return{route:e,params:t,searchParams:new URLSearchParams}}}function V(e,t){let n=e.split(`?`)[0].split(`/`).filter(Boolean).map(H),r=[...t].sort((e,t)=>U(t.path)-U(e.path));for(let e of r){let t=W(n,e.path.split(`/`).filter(Boolean));if(t)return{route:e,params:t}}}function H(e){try{return decodeURIComponent(e)}catch{return e}}function U(e){return e.split(`/`).filter(Boolean).reduce((e,t)=>t.endsWith(`*`)?e:t.startsWith(`:`)?e+1:e+2,0)}function W(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 G(e){if(e)try{let t=new URL(e);return t.protocol!==`http:`&&t.protocol!==`https:`?void 0:t.origin}catch{return}}function K(e,t={}){let n=G(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=G(r||i);if(!a)return r?`Invalid Origin header`:`Invalid Referer header`;if(a!==n&&!t.allowedOrigins?.some(e=>G(e)===a))return`Cross-origin request blocked: source "${a}" != target "${n}"`}function q(e){return new Response(e,{status:403,headers:{"Content-Type":`text/plain; charset=utf-8`}})}var J=1048576;async function Y(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 X(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 Z(e,t=J){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 Y(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 Y(e,t);if(!n.ok)return{ok:!1,response:n.response};let r=X(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 Y(e,t);if(!n.ok)return{ok:!1,response:n.response};let r=X(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 Q(e,r,i={}){let a=K(e,i);if(a)return q(a);let o=await Z(e,i.bodyLimit??J);if(!o.ok)return o.response;let{name:s,page:c,args:l,wantsJson:u}=o;try{let i=await r(s,c);if(!i){let e=c?`Action not found: ${s} (page: ${c})`:`Action not found: ${s}`;return new Response(e,{status:404,headers:{"Content-Type":`text/plain`}})}let a=i.__elurAction,o;if(a){let t={request:e,signal:e.signal,idempotencyKey:e.headers.get(`Idempotency-Key`)??void 0,params:{},locals:{}};o=await i(l[0],t)}else o=await i(...l);if(!n.a(o)&&a){let e=a.invalidateTags??[],n=a.invalidatePaths??[];(e.length>0||n.length>0)&&await t.r.emit({tags:e,paths:n,source:s})}if(n.a(o)){if(u)return new Response(JSON.stringify({__elur_js_action_failure:!0,status:o.status,data:o.data}),{status:o.status,headers:{"Content-Type":`application/json`}});let t=e.headers.get(`Referer`)??`/`,n=new URL(t,`http://localhost`),{value:r}=k(o.data,o.status);return new Response(null,{status:303,headers:{Location:n.pathname+n.search,"Content-Type":`text/plain`,"Set-Cookie":N(r)}})}if(n.o(o))return u?new Response(JSON.stringify({__elur_js_action_redirect:!0,status:o.status,location:o.location}),{status:200,headers:{"Content-Type":`application/json`}}):new Response(null,{status:o.status,headers:{Location:o.location,"Content-Type":`text/plain`}});if(u)return new Response(JSON.stringify(o??null),{status:200,headers:{"Content-Type":`application/json`}});let d=e.headers.get(`Referer`)??`/`;return new Response(null,{status:303,headers:{Location:typeof o==`string`?o:d,"Content-Type":`text/plain`}})}catch(e){return console.error(`[elur-kit] Action error:`,e),n.s(e,{includeDetail:!1})}}async function $(e){let t=[`${e}/src/middleware.ts`,`${e}/middleware.ts`];for(let e of t)try{let t=await import(e),n=t.default??t.middleware;if(typeof n!=`function`)continue;return{handler:n,config:t.config??{}}}catch(e){let t=typeof e==`object`&&e&&`message`in e?String(e.message):String(e);if(t.includes(`Cannot find module`)||t.includes(`Cannot find package`)||t.includes(`ENOENT`)||t.includes(`Module not found`))continue;throw Error(`[elur-kit] Error loading middleware: ${t}`,{cause:e})}return null}function ee(e,t){if(!t.matcher||t.matcher.length===0)return!0;let n=e.split(`?`)[0];for(let e of t.matcher){if(e===n)return!0;let t=e.match(/^(.*)\/:[\w]+\*$/);if(t&&n===t[1]||B(n,[{path:e,pagePath:``,params:[],layouts:[]}]))return!0}return!1}async function te(e,t,n){let r,i,a,o=[],s={next(e){e?.headers&&(r=e.headers),e?.params&&(i=e.params),e?.locals&&(a=e.locals)},params:n,locals:{}};try{let o=await e.handler(t,s);return o instanceof Response?{kind:`response`,response:o}:{kind:`continue`,headers:r,params:i??n,locals:a}}finally{for(let e of o)try{await e()}catch(e){console.error(`[elur-kit] middleware cleanup error:`,e)}}}var ne=globalThis.AbortController??AbortController;function re(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 ne;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)}async function ie(e,t){let n=Object.fromEntries(t.headers.entries());t.body!==null&&(delete n[`content-length`],delete n[`transfer-encoding`]),e.writeHead(t.status,n);let r=t.body;if(!r){e.end();return}let i=r.getReader(),a=!1,o=()=>{a||i.cancel().catch(()=>{})};e.once(`close`,o);try{for(;;){let{done:t,value:n}=await i.read();if(t)break;n&&n.byteLength>0&&!e.write(n)&&await new Promise(t=>e.once(`drain`,t))}a=!0,e.end()}catch{a=!0,e.destroy()}finally{e.removeListener(`close`,o)}}Object.defineProperty(exports,"C",{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,"E",{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,"S",{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,"T",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"_",{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return te}}),Object.defineProperty(exports,"b",{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return K}}),Object.defineProperty(exports,"d",{enumerable:!0,get:function(){return F}}),Object.defineProperty(exports,"f",{enumerable:!0,get:function(){return z}}),Object.defineProperty(exports,"g",{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,"h",{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return ee}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return V}}),Object.defineProperty(exports,"m",{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return ie}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return Q}}),Object.defineProperty(exports,"p",{enumerable:!0,get:function(){return I}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return $}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return q}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return re}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return B}}),Object.defineProperty(exports,"v",{enumerable:!0,get:function(){return N}}),Object.defineProperty(exports,"w",{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,"x",{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,"y",{enumerable:!0,get:function(){return p}});
13
+ //# sourceMappingURL=node-http-Uct7DhmN.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-http-Uct7DhmN.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/ssr/render.ts","../../src/ssr/match.ts","../../src/action/origin.ts","../../src/action/server.ts","../../src/middleware/index.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 type SpeculationMode = \"prefetch\" | \"prerender\";\n\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 /**\n * Path to the client entry module, e.g. `/_elur/entry-client.js`. In split\n * builds this is the hydrate-only entry; callers gate it per page so it is\n * only emitted when the rendered body actually contains islands.\n */\n clientEntry?: string;\n /**\n * Path to the standalone client router module, e.g. `/_elur/router.js`\n * (split builds only). Emitted as a second `<script type=\"module\">` so pages\n * without islands still get SPA navigation without paying for the islands\n * entry.\n */\n routerEntry?: string;\n /**\n * Whether the client router is enabled for this page. When `false`, the\n * `elur:render-endpoint` meta is omitted entirely: no client router will run,\n * so there is nothing to advertise endpoint availability to.\n */\n routerEnabled?: boolean;\n /**\n * Speculation Rules API mode emitted as\n * `<script type=\"speculationrules\">` with document rules and\n * `eagerness: \"moderate\"`. Chromium-only progressive enhancement — other\n * browsers ignore the unknown script type. Only set this for static builds;\n * never apply to URLs reachable via server actions.\n */\n speculation?: SpeculationMode;\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\n/**\n * Explicit delimiters around the `#app` content. The streaming pipeline\n * (`createStreamingResponse`) and adapter render endpoints extract the page\n * body with these markers instead of parsing the shell layout by hand, so\n * changes to the shell markup never break extraction. They are HTML comments:\n * invisible, and ignored by hydration and the SPA router.\n */\nexport const APP_START_MARKER = \"<!--elur:app:start-->\";\nexport const APP_END_MARKER = \"<!--elur:app:end-->\";\n\n/**\n * Extracts the inner HTML of `#app` from a full document produced by\n * `documentShell`. Returns `undefined` when the markers are missing (e.g. a\n * hand-written document).\n */\nexport function extractAppBody(html: string): string | undefined {\n const start = html.indexOf(APP_START_MARKER);\n if (start < 0) return undefined;\n const end = html.indexOf(APP_END_MARKER, start + APP_START_MARKER.length);\n if (end < 0) return undefined;\n return html.slice(start + APP_START_MARKER.length, end);\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 */\nexport function 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/**\n * Document-level Speculation Rules (Chromium-only, ignored elsewhere).\n *\n * `href_matches: \"/*\"` scopes the rule to same-origin path links; the\n * `selector_matches` exclusions keep downloads, new-tab links, router-opt-outs\n * and explicit `data-no-speculation` links out of speculation. Actions are\n * POST endpoints reached through forms/`callAction`, never through document\n * links, so they are not speculated. `eagerness: \"moderate\"` speculates on\n * hover — the same trigger as the client router's prefetch.\n */\nfunction speculationRulesScript(mode: SpeculationMode): string {\n const rules = {\n [mode]: [\n {\n source: \"document\",\n where: {\n and: [\n { href_matches: \"/*\" },\n {\n not: {\n selector_matches:\n \"a[download], a[target], a[data-no-router], a[data-no-speculation]\",\n },\n },\n ],\n },\n eagerness: \"moderate\",\n },\n ],\n };\n return `\\n <script type=\"speculationrules\">${JSON.stringify(rules)}</script>`;\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, routerEntry, 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 // Every emitted module script also gets a <link rel=\"modulepreload\"> so the\n // fetch starts during HTML parsing instead of waiting for the deferred\n // script discovery (Fase 8.5 — paso 1).\n const modulePreload = (src: string) =>\n `\\n <link rel=\"modulepreload\" href=\"${escapeHtml(src)}\" />`;\n\n const preloads =\n (clientEntry ? modulePreload(clientEntry) : \"\") +\n (routerEntry ? modulePreload(routerEntry) : \"\");\n\n const entryScript = clientEntry\n ? `\\n <script type=\"module\" src=\"${escapeHtml(clientEntry)}\"></script>`\n : \"\";\n\n const routerScript = routerEntry\n ? `\\n <script type=\"module\" src=\"${escapeHtml(routerEntry)}\"></script>`\n : \"\";\n\n const speculationScript = opts.speculation\n ? speculationRulesScript(opts.speculation)\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 // The render-endpoint marker only exists for the client router; when the\n // router is disabled for the page there is nothing to advertise.\n const renderEndpointMeta =\n opts.renderEndpoint === false && opts.routerEnabled !== 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}${preloads}${speculationScript}\n </head>\n <body>\n <div id=\"app\">${APP_START_MARKER}${body}${APP_END_MARKER}</div>${dataScript}${actionsScript}${entryScript}${routerScript}\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","import type { ElurTemplate } from \"@elurjs/core\";\nimport { renderToString } from \"../render/render-to-string.js\";\nimport { documentShell, buildHeadTags } from \"../build/document-shell.js\";\nimport { ISLAND_MARKER_ATTR } from \"../island/island.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\" | \"router\" | \"js\">;\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 * Loader data as rendered into `<script id=\"elur-data\">`. Exposed so the\n * SPA render endpoint can ship it in the payload and the client router can\n * keep the serialized data fresh across navigations.\n */\n data?: unknown;\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 // --- 0% JS gating (Fase 8.2) ---\n // Scan the rendered body for island markers. A false positive (e.g. a\n // `data-elur-island` string inside user markdown) only loads the hydration\n // entry unnecessarily — benign. A false negative would mean dead islands in\n // production, which is why we scan output instead of tracking render context.\n const hasIslands = body.includes(ISLAND_MARKER_ATTR);\n\n // Decide which module scripts the shell emits. Three modes:\n // legacy (`js: \"legacy\"` or no router config at all): the combined\n // entry is emitted unconditionally — the pre-Fase-8 behavior.\n // split (router.entry set): entry-client hydrates islands only, the\n // router lives in its own chunk → emit router.js whenever the\n // router is enabled, and entry-client only when islands exist.\n // combined (router configured, no entry): the entry embeds the router\n // (single-input bundles) → emit it when there are islands or the\n // router is on; a page with neither ships 0 KB of JS.\n const routerCfg = config.router;\n const routerEnabled = routerCfg?.enabled !== false;\n let clientEntry: string | undefined;\n let routerEntry: string | undefined;\n if (!routerCfg || config.js === \"legacy\") {\n clientEntry = config.clientEntry;\n } else if (routerCfg.entry) {\n if (hasIslands) clientEntry = config.clientEntry;\n if (routerEnabled) routerEntry = routerCfg.entry;\n } else if (hasIslands || routerEnabled) {\n clientEntry = config.clientEntry;\n }\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,\n routerEntry,\n routerEnabled: routerCfg ? routerEnabled : undefined,\n speculation: routerCfg?.speculation,\n renderEndpoint: config.renderEndpoint,\n });\n\n const head = metadata ? buildHeadTags(metadata, resolvedTitle) : \"\";\n return { html, revalidate, clearActionErrorCookie, head, resolvedTitle, cachePolicy, data };\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\" | \"router\" | \"js\">;\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 type { ActionContext } from \"./define.js\";\nimport { defaultInvalidator } from \"../cache/invalidation.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 // defineAction() functions take (input, ctx) and carry __elurAction\n // metadata; legacy plain actions take (...args). Route params are not\n // known at this endpoint (actions resolve by page path), so params and\n // locals start empty — middleware/page context can fill them elsewhere.\n const actionMeta = (action as { __elurAction?: { invalidateTags?: readonly string[]; invalidatePaths?: readonly string[] } }).__elurAction;\n let result: unknown;\n if (actionMeta) {\n const ctx: ActionContext = {\n request,\n signal: request.signal,\n idempotencyKey: request.headers.get(\"Idempotency-Key\") ?? undefined,\n params: {},\n locals: {},\n };\n result = await action(args[0], ctx);\n } else {\n result = await action(...args);\n }\n\n // Cache invalidation (§9.4): actions defined with defineAction() declare\n // invalidateTags/invalidatePaths metadata; dispatch them to connected\n // cache adapters after a successful run (not on ActionFailure).\n if (!isActionFailure(result) && actionMeta) {\n const tags = actionMeta.invalidateTags ?? [];\n const paths = actionMeta.invalidatePaths ?? [];\n if (tags.length > 0 || paths.length > 0) {\n await defaultInvalidator.emit({ tags, paths, source: name });\n }\n }\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","// --- Middleware ---\n//\n// Convention: `src/middleware.ts` in the project root exports a default\n// function and an optional `config` with a `matcher` array.\n//\n// import type { Middleware } from \"@elurjs/kit\";\n//\n// export default function middleware(request: Request) {\n// if (!request.headers.get(\"Cookie\")?.includes(\"session=\")) {\n// return Response.redirect(new URL(\"/login\", request.url), 307);\n// }\n// }\n//\n// export const config = { matcher: [\"/dashboard/:path*\", \"/admin/:path*\"] };\n//\n// The middleware runs before routing. Return a `Response` to short-circuit\n// (redirect, rewrite, 401, etc.). Return `undefined` or nothing to continue.\n// Use `next()` to pass headers to the loader.\n\nimport { matchRoute } from \"../ssr/match.js\";\nimport type { PageRoute } from \"../router/route-scanner.js\";\n\n/** The middleware function signature. */\nexport type Middleware = (request: Request, context: MiddlewareContext) =>\n | Response\n | void\n | Promise<Response | void>;\n\n/** Context passed to the middleware function. */\nexport interface MiddlewareContext {\n /** Helper to continue to the next handler. Can attach headers, params, and locals. */\n next(options?: {\n headers?: Record<string, string>;\n params?: Record<string, string | string[]>;\n locals?: Record<string, unknown>;\n }): void;\n /** Matched route params (only available if the path matches a page route). */\n params?: Record<string, string | string[]>;\n /** Per-request locals (populated by middleware, available to loaders/actions). */\n locals?: Record<string, unknown>;\n}\n\n/** Configuration for the middleware module. */\nexport interface MiddlewareConfig {\n /** Path patterns that trigger the middleware. Supports `:param` and `:param*`. */\n matcher?: string[];\n}\n\nexport interface LoadedMiddleware {\n handler: Middleware;\n config: MiddlewareConfig;\n}\n\n/** Result of running middleware: either a response to short-circuit with, or continue. */\nexport type MiddlewareResult =\n | { kind: \"response\"; response: Response }\n | {\n kind: \"continue\";\n headers?: Record<string, string>;\n params?: Record<string, string | string[]>;\n locals?: Record<string, unknown>;\n };\n\n/**\n * Loads the user's `src/middleware.ts` module. Returns `null` if no middleware\n * file exists. Distinguishes \"file not found\" from \"file has errors\" (§6):\n * an import error is not silently treated as \"no middleware\".\n */\nexport async function loadMiddleware(root: string): Promise<LoadedMiddleware | null> {\n const candidates = [\n `${root}/src/middleware.ts`,\n `${root}/middleware.ts`,\n ];\n\n for (const path of candidates) {\n try {\n const mod = await import(path);\n const handler = (mod.default ?? mod.middleware) as Middleware | undefined;\n if (typeof handler !== \"function\") continue;\n const config = (mod.config ?? {}) as MiddlewareConfig;\n return { handler, config };\n } catch (err) {\n // Distinguish \"module not found\" from actual errors.\n // If the error is a module resolution error for this specific file,\n // it means the file doesn't exist — try the next candidate.\n // If it's a syntax/runtime error, rethrow so the user sees it.\n // Note: Bun's ResolveMessage is not `instanceof Error`, so match on the\n // message property instead of relying on the class hierarchy.\n const msg =\n typeof err === \"object\" && err !== null && \"message\" in err\n ? String((err as { message: unknown }).message)\n : String(err);\n if (\n msg.includes(\"Cannot find module\") ||\n msg.includes(\"Cannot find package\") ||\n msg.includes(\"ENOENT\") ||\n msg.includes(\"Module not found\")\n ) {\n // File doesn't exist — try next candidate.\n continue;\n }\n // Actual error in the middleware file — rethrow (§6).\n throw new Error(`[elur-kit] Error loading middleware: ${msg}`, { cause: err });\n }\n }\n\n return null;\n}\n\n/**\n * Checks if a pathname matches any of the middleware's matcher patterns.\n * If no matcher is configured, the middleware runs for every request.\n *\n * Catch-all patterns (`:param*`) match both the base path and any sub-paths,\n * e.g. `/dashboard/:path*` matches `/dashboard` and `/dashboard/settings/users`.\n */\nexport function matchesMiddleware(pathname: string, config: MiddlewareConfig): boolean {\n if (!config.matcher || config.matcher.length === 0) return true;\n\n const cleanPath = pathname.split(\"?\")[0];\n\n for (const pattern of config.matcher) {\n // Exact match.\n if (pattern === cleanPath) return true;\n\n // Check for catch-all: `/foo/:bar*` should also match `/foo`.\n const catchAllMatch = pattern.match(/^(.*)\\/:[\\w]+\\*$/);\n if (catchAllMatch) {\n const base = catchAllMatch[1];\n if (cleanPath === base) return true;\n }\n\n // Use matchRoute for param matching.\n const pseudoRoutes: PageRoute[] = [{\n path: pattern,\n pagePath: \"\",\n params: [],\n layouts: [],\n }];\n if (matchRoute(cleanPath, pseudoRoutes)) return true;\n }\n\n return false;\n}\n\n/**\n * Runs the middleware for a request. Returns the result indicating whether to\n * short-circuit with a response or continue with propagated headers/params/locals.\n *\n * Per §6: cleanup runs in `finally`, response short-circuits the pipeline,\n * headers/params/locals are propagated to downstream handlers.\n */\nexport async function runMiddleware(\n middleware: LoadedMiddleware,\n request: Request,\n params?: Record<string, string | string[]>,\n): Promise<MiddlewareResult> {\n let nextHeaders: Record<string, string> | undefined;\n let nextParams: Record<string, string | string[]> | undefined;\n let nextLocals: Record<string, unknown> | undefined;\n const cleanups: Array<() => void | Promise<void>> = [];\n\n const context: MiddlewareContext = {\n next(options) {\n if (options?.headers) nextHeaders = options.headers;\n if (options?.params) nextParams = options.params;\n if (options?.locals) nextLocals = options.locals;\n },\n params,\n locals: {},\n };\n\n try {\n const result = await middleware.handler(request, context);\n\n if (result instanceof Response) {\n return { kind: \"response\", response: result };\n }\n\n return {\n kind: \"continue\",\n headers: nextHeaders,\n params: nextParams ?? params,\n locals: nextLocals,\n };\n } finally {\n // Run any cleanup functions (§6). Errors in cleanup are logged but\n // do not propagate to the caller.\n for (const cleanup of cleanups) {\n try {\n await cleanup();\n } catch (err) {\n console.error(\"[elur-kit] middleware cleanup error:\", err);\n }\n }\n }\n}\n","import type { IncomingMessage, ServerResponse } 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\n/**\n * Writes a Web `Response` to a Node `ServerResponse`, streaming the body.\n *\n * Unlike `res.end(Buffer.from(await response.arrayBuffer()))`, this forwards\n * chunks as they are produced — required for streaming SSR responses to reach\n * the client progressively. Buffered (non-stream) bodies behave exactly as\n * before. Honors backpressure (`drain`) and cancels the upstream stream when\n * the client disconnects (`close` before `finish`).\n */\nexport async function sendWebResponse(res: ServerResponse, response: Response): Promise<void> {\n const headers = Object.fromEntries(response.headers.entries());\n if (response.body !== null) {\n // Chunk boundaries are decided as the body is read; a Content-Length\n // captured earlier would be wrong for streams. Transfer-Encoding is\n // managed by Node itself (it chunks when no length is set) — forwarding\n // it would duplicate the header (`chunked, chunked`).\n delete headers[\"content-length\"];\n delete headers[\"transfer-encoding\"];\n }\n res.writeHead(response.status, headers);\n\n const body = response.body;\n if (!body) {\n res.end();\n return;\n }\n\n const reader = body.getReader();\n let done = false;\n const onClose = () => {\n if (!done) void reader.cancel().catch(() => {});\n };\n res.once(\"close\", onClose);\n\n try {\n for (;;) {\n const { done: readDone, value } = await reader.read();\n if (readDone) break;\n if (value && value.byteLength > 0 && !res.write(value)) {\n // Socket buffer is full: wait for it to drain before reading more.\n await new Promise<void>((resolveDrain) => res.once(\"drain\", resolveDrain));\n }\n }\n done = true;\n res.end();\n } catch {\n done = true;\n // The client went away or the upstream stream failed: tear the socket\n // down instead of leaving a half-written response hanging.\n res.destroy();\n } finally {\n res.removeListener(\"close\", onClose);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],enumerable:!0});return n||e(r,Symbol.toStringTag,{value:`Module`}),r};Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return t}});
@@ -0,0 +1,11 @@
1
+ //#region \0rolldown/runtime.js
2
+ var e = Object.defineProperty, t = (t, n) => {
3
+ let r = {};
4
+ for (var i in t) e(r, i, {
5
+ get: t[i],
6
+ enumerable: !0
7
+ });
8
+ return n || e(r, Symbol.toStringTag, { value: "Module" }), r;
9
+ };
10
+ //#endregion
11
+ export { t };
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=!0,t=null;function n(){return e?(t||=(async()=>{let t=new URL(`/__elur-js/render`,location.origin);t.searchParams.set(`page`,`/`);try{let n=await fetch(t.toString(),{headers:{Accept:`application/json`,"X-Elur-Probe":`1`}});return e=n.ok,n.ok}catch{return e=!1,!1}})(),t):Promise.resolve(!1)}function r(e){return e.tagName===`A`&&e.hostname===location.hostname&&e.target===``&&!e.getAttribute(`download`)&&!e.hasAttribute(`data-no-router`)}function i(e){return e.metaKey||e.ctrlKey||e.shiftKey||e.altKey}var a=3e4,o=new Map;function s(e,t){return e+t}function c(e){let t=o.get(e);if(t){if(Date.now()-t.ts>a){o.delete(e);return}return t.payload}}function l(e,t){o.set(e,{payload:t,ts:Date.now()})}async function u(t,r,i){let a=s(t,r),o=c(a);if(o)return o;if(e&&await n()){let e=await d(t,r,i);if(e)return l(a,e),e}let u=await f(t,r,i);return u&&l(a,u),u}async function d(e,t,n){let r=new URL(`/__elur-js/render`,location.origin);r.searchParams.set(`page`,e);let i=new URL(location.href);r.searchParams.set(`search`,t||i.search);let a;try{a=await fetch(r.toString(),{headers:{Accept:`application/json`},signal:n})}catch(e){e instanceof DOMException&&e.name;return}if(!a.ok)return;let o;try{o=await a.json()}catch{return}return o}async function f(e,t,n){let r=e+(t||``),i;try{i=await fetch(r,{headers:{Accept:`text/html`},signal:n})}catch(e){e instanceof DOMException&&e.name;return}if(!i.ok)return;let a;try{a=await i.text()}catch{return}let o=new DOMParser().parseFromString(a,`text/html`),s=o.getElementById(`app`);if(!s)return;let c=s.innerHTML,l=o.querySelectorAll(`[data-elur-head]`),u=``;for(let e of l)u+=e.outerHTML;let d=o.head.querySelectorAll(`link[rel='icon'], link[rel='apple-touch-icon'], link[rel='manifest'], meta[name='theme-color']`);for(let e of d){let t=e.getAttribute(`href`);t&&document.head.querySelector(`link[href="${t}"]`)||(u+=e.outerHTML)}let f=o.querySelector(`title`)?.textContent??void 0;return{body:c,head:u,title:f}}async function p(e,t=``){let n=s(e,t);if(o.has(n)){let e=o.get(n);if(Date.now()-e.ts<=a)return}await u(e,t)}function m(){return window.matchMedia?.(`(prefers-reduced-motion: reduce)`).matches??!1}function h(){return typeof document.startViewTransition==`function`}var g=null;function _(){g&&=(g.controller.abort(),null)}function v(e){let t=e.querySelectorAll(`link[rel="stylesheet"]`);for(let e of t){let t=e.getAttribute(`href`);if(t){if(document.head.querySelector(`link[rel="stylesheet"][href="${t}"]`)){e.remove();continue}e.setAttribute(`data-elur-hoisted`,``),document.head.appendChild(e)}}let n=e.querySelectorAll(`style`);for(let e of n){let t=e.textContent?.trim();if(t){if(Array.from(document.head.querySelectorAll(`style`)).find(e=>e.textContent?.trim()===t)){e.remove();continue}e.setAttribute(`data-elur-hoisted`,``),document.head.appendChild(e)}}}function y(e){let t=document.getElementById(`elur-route-announcer`);t||(t=document.createElement(`div`),t.id=`elur-route-announcer`,t.setAttribute(`aria-live`,`assertive`),t.setAttribute(`aria-atomic`,`true`),t.setAttribute(`role`,`status`),t.setAttribute(`style`,`position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden;`),document.body.appendChild(t)),t.textContent=``;let n=t,r=setTimeout(()=>{if(typeof document<`u`&&n){let t=document.title||e;n.textContent=t}},50);typeof r==`object`&&r&&`unref`in r&&r.unref()}function b(){let e=document.getElementById(`app`);e&&(e.hasAttribute(`tabindex`)||e.setAttribute(`tabindex`,`-1`),e.focus({preventScroll:!1}))}function x(e,t){let n=location.origin+e+(t||``),r=document.querySelector(`link[rel="canonical"]`);r||(r=document.createElement(`link`),r.rel=`canonical`,document.head.appendChild(r)),r.href=n;let i=document.querySelector(`meta[property="og:url"]`);i||(i=document.createElement(`meta`),i.setAttribute(`property`,`og:url`),document.head.appendChild(i)),i.content=n}async function S(e,t=``,n=!0){_();let r=new AbortController;g={controller:r,pathname:e};let i;try{i=await u(e,t,r.signal)}catch(e){if(r.signal.aborted)return!1;throw e}if(g&&g.controller!==r&&r.signal.aborted||(g=null,!i))return!1;let a=document.getElementById(`app`);if(!a)return!1;n&&history.replaceState({n:location.pathname,scroll:window.scrollY},``,location.href);let o=new URL(location.href),s=()=>{let r=[];a.querySelectorAll(`[data-scroll-preserve]`).forEach(e=>{r.push({el:e,top:e.scrollTop})}),v(a);let s=document.createElement(`template`);s.innerHTML=i.body,v(s.content),a.innerHTML=s.innerHTML,C(i.head,i.title),i.clearActionErrorCookie&&(document.cookie=i.clearActionErrorCookie),n&&history.pushState({n:e,scroll:0},``,e+(t||o.search));let c=n?0:history.state?.scroll??0;window.scrollTo(0,c);for(let e of r){let t=a.querySelector(`[data-scroll-preserve="${e.el.getAttribute(`data-scroll-preserve`)}"]`);t&&(t.scrollTop=e.top)}x(e,t),y(e),n&&b(),document.dispatchEvent(new CustomEvent(`elur:rendered`))};return h()&&!m()?document.startViewTransition(()=>s()):s(),!0}function C(e,t){if(document.querySelectorAll(`[data-elur-head]`).forEach(e=>e.remove()),e&&e.trim().length>0){let t=document.createElement(`template`);t.innerHTML=e;let n=t.content,r=n.querySelector(`title`);r&&(document.title=r.textContent??``,r.remove()),document.head.appendChild(n)}else t&&(document.title=t)}var w=new WeakSet;function T(){let e=e=>{let t=e.getAttribute(`href`);if(!t||t.startsWith(`#`)||t.startsWith(`mailto:`)||t.startsWith(`javascript:`))return null;let n=t.indexOf(`?`);return{path:n===-1?t:t.slice(0,n),search:n===-1?``:t.slice(n)}},t=t=>{if(w.has(t)||!r(t)||t.hasAttribute(`data-no-prefetch`))return;w.add(t);let n=()=>{let n=e(t);n&&p(n.path,n.search)};if(t.addEventListener(`pointerenter`,n,{once:!0}),t.addEventListener(`focus`,n,{once:!0}),t.dataset.prefetch===`viewport`&&`IntersectionObserver`in window){let n=new IntersectionObserver(t=>{for(let r of t){if(!r.isIntersecting)continue;let t=e(r.target);t&&p(t.path,t.search),n.disconnect()}},{rootMargin:`200px`,threshold:0});n.observe(t)}},n=()=>{let e=document.querySelectorAll(`a[href]`);for(let n of e)t(n)};n(),new MutationObserver(()=>n()).observe(document.body,{childList:!0,subtree:!0}),document.addEventListener(`elur:rendered`,n)}function E(){document.querySelector(`meta[name="elur:render-endpoint"]`)?.getAttribute(`content`)===`off`&&(e=!1);let t=document.getElementById(`app`);t&&v(t),document.addEventListener(`click`,async e=>{if(!(e instanceof MouseEvent)||i(e)||e.defaultPrevented)return;let t=e.target.closest(`a`);if(!t||!r(t))return;let n=t.getAttribute(`href`);if(!n||n.startsWith(`mailto:`)||n.startsWith(`javascript:`))return;if(n.startsWith(`#`)){if(n.length>1){let t=document.getElementById(n.slice(1));t&&(e.preventDefault(),t.scrollIntoView({behavior:`smooth`,block:`start`}),history.replaceState(null,``,n))}return}e.preventDefault();let a=n.indexOf(`?`);await S(a===-1?n:n.slice(0,a),a===-1?``:n.slice(a))||location.assign(n)}),window.addEventListener(`popstate`,e=>{S(e.state?.n??location.pathname,location.search,!1)}),T()}function D(){o.clear(),g=null,e=!0,t=null}exports.__resetRouterState=D,exports.hoistStyles=v,exports.navigateTo=S,exports.prefetch=p,exports.startClientRouter=E;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=`data-elur-persist`;function t(e,t){document.dispatchEvent(new CustomEvent(e,{detail:t}))}var n=!0,r=null;function i(){return n?(r||=(async()=>{let e=new URL(`/__elur-js/render`,location.origin);e.searchParams.set(`page`,`/`);try{let t=await fetch(e.toString(),{headers:{Accept:`application/json`,"X-Elur-Probe":`1`}});return n=t.ok,t.ok}catch{return n=!1,!1}})(),r):Promise.resolve(!1)}function a(e){return e.tagName===`A`&&e.hostname===location.hostname&&e.target===``&&!e.getAttribute(`download`)&&!e.hasAttribute(`data-no-router`)}function o(e){return e.metaKey||e.ctrlKey||e.shiftKey||e.altKey}var s=3e4,c=32,l=new Map;function u(e,t){return e+t}function d(e){let t=l.get(e);if(t){if(Date.now()-t.ts>s){l.delete(e);return}return l.delete(e),l.set(e,t),t.payload}}function f(e,t){for(l.delete(e),l.set(e,{payload:t,ts:Date.now()});l.size>c;){let e=l.keys().next().value;if(e===void 0)break;l.delete(e)}}function p(){let e=globalThis.navigator?.connection;return!(e?.saveData||e?.effectiveType===`slow-2g`||e?.effectiveType===`2g`)}async function m(e,t,r){let a=u(e,t),o=d(a);if(o)return{payload:o,fromCache:!0};if(n&&await i()){let n=await h(e,t,r);if(n)return f(a,n),{payload:n,fromCache:!1}}let s=await g(e,t,r);if(s)return f(a,s),{payload:s,fromCache:!1}}async function h(e,t,n){let r=new URL(`/__elur-js/render`,location.origin);r.searchParams.set(`page`,e);let i=new URL(location.href);r.searchParams.set(`search`,t||i.search);let a;try{a=await fetch(r.toString(),{headers:{Accept:`application/json`},signal:n})}catch(e){e instanceof DOMException&&e.name;return}if(!a.ok)return;let o;try{o=await a.json()}catch{return}return o}async function g(e,t,n){let r=e+(t||``),i;try{i=await fetch(r,{headers:{Accept:`text/html`},signal:n})}catch(e){e instanceof DOMException&&e.name;return}if(!i.ok)return;let a;try{a=await i.text()}catch{return}let o=new DOMParser().parseFromString(a,`text/html`),s=o.getElementById(`app`);if(!s)return;let c=s.innerHTML,l=o.querySelectorAll(`[data-elur-head]`),u=``;for(let e of l)u+=e.outerHTML;let d=o.head.querySelectorAll(`link[rel='icon'], link[rel='apple-touch-icon'], link[rel='manifest'], meta[name='theme-color']`);for(let e of d){let t=e.getAttribute(`href`);t&&document.head.querySelector(`link[href="${t}"]`)||(u+=e.outerHTML)}let f=o.querySelector(`title`)?.textContent??void 0,p=o.getElementById(`elur-data`),m=o.getElementById(`elur-actions`);return{body:c,head:u,title:f,data:p?.textContent??void 0,actions:m?.textContent??void 0}}async function _(e,t=``,n){if(n?.force!==!0&&!p())return;let r=u(e,t);if(l.has(r)){let e=l.get(r);if(Date.now()-e.ts<=s)return}await m(e,t)}function v(){return window.matchMedia?.(`(prefers-reduced-motion: reduce)`).matches??!1}function y(){return typeof document.startViewTransition==`function`}var b=null;function x(){b&&=(b.controller.abort(),null)}function S(e){let t=e.querySelectorAll(`link[rel="stylesheet"]`);for(let e of t){let t=e.getAttribute(`href`);if(t){if(document.head.querySelector(`link[rel="stylesheet"][href="${t}"]`)){e.remove();continue}e.setAttribute(`data-elur-hoisted`,``),document.head.appendChild(e)}}let n=e.querySelectorAll(`style`);for(let e of n){let t=e.textContent?.trim();if(t){if(Array.from(document.head.querySelectorAll(`style`)).find(e=>e.textContent?.trim()===t)){e.remove();continue}e.setAttribute(`data-elur-hoisted`,``),document.head.appendChild(e)}}}function C(e){let t=document.getElementById(`elur-route-announcer`);t||(t=document.createElement(`div`),t.id=`elur-route-announcer`,t.setAttribute(`aria-live`,`assertive`),t.setAttribute(`aria-atomic`,`true`),t.setAttribute(`role`,`status`),t.setAttribute(`style`,`position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden;`),document.body.appendChild(t)),t.textContent=``;let n=t,r=setTimeout(()=>{if(typeof document<`u`&&n){let t=document.title||e;n.textContent=t}},50);typeof r==`object`&&r&&`unref`in r&&r.unref()}function w(){let e=document.getElementById(`app`);e&&(e.hasAttribute(`tabindex`)||e.setAttribute(`tabindex`,`-1`),e.focus({preventScroll:!1}))}function T(e,t){let n=location.origin+e+(t||``),r=document.querySelector(`link[rel="canonical"]`);r||(r=document.createElement(`link`),r.rel=`canonical`,document.head.appendChild(r)),r.href=n;let i=document.querySelector(`meta[property="og:url"]`);i||(i=document.createElement(`meta`),i.setAttribute(`property`,`og:url`),document.head.appendChild(i)),i.content=n}function E(t,n){let r=t.querySelectorAll(`[${e}]`);if(r.length===0)return[];let i=[];for(let t of r){let r=t.getAttribute(e);if(!r||i.some(e=>e.oldEl.contains(t)))continue;let a=n.querySelector(`[${e}="${D(r)}"]`);a&&i.push({oldEl:t,newEl:a})}return i}function D(e){let t=globalThis.CSS?.escape;return t?t(e):e.replace(/["\\\]]/g,`\\$&`)}function O(e){for(let{oldEl:t,newEl:n}of e){k(t,n);let e=n.parentNode;e&&typeof e.moveBefore==`function`?(e.moveBefore(t,n),n.remove()):n.replaceWith(t)}}function k(e,t){let n=e=>(e.matches?.(`[data-elur-island]`)?[e]:[]).concat(Array.from(e.querySelectorAll(`[data-elur-island]`))),r=new Map;for(let e of n(t)){let t=e.getAttribute(`data-elur-island`);t&&r.set(t,e.getAttribute(`data-props`))}for(let t of n(e)){let e=t.getAttribute(`data-elur-island`);if(!e)continue;let n=r.get(e),i=t.getAttribute(`data-props`);n!==void 0&&n!==i&&t.dispatchEvent(new CustomEvent(`elur:persist-props-changed`,{bubbles:!0,detail:{name:e,props:n,previousProps:i}}))}}var A=null,j=new Set([``,`text/javascript`,`application/javascript`,`module`]);function M(e){A||=new Set(Array.from(document.querySelectorAll(`script[src]`)).map(e=>e.getAttribute(`src`)).filter(e=>!!e).map(e=>new URL(e,location.href).href));for(let t of Array.from(e.querySelectorAll(`script`))){if(t.hasAttribute(`data-elur-no-reload`)||!j.has(t.type))continue;let e=t.getAttribute(`src`);if(e){let t=new URL(e,location.href).href;if(A.has(t))continue;A.add(t)}let n=document.createElement(`script`);for(let e of Array.from(t.attributes))n.setAttribute(e.name,e.value);n.textContent=t.textContent,t.replaceWith(n)}}function N(e,t){let n=document.getElementById(e);if(t===void 0){n?.remove();return}if(n){n.textContent=t;return}let r=document.createElement(`script`);r.type=`application/json`,r.id=e,r.textContent=t,document.body.appendChild(r)}var P=!1;function F(t){return!!(t instanceof Element&&(t.hasAttribute(e)||typeof t.__elur_js_island_dispose==`function`))}async function I(e,t){let{Idiomorph:n}=await Promise.resolve().then(()=>require("../idiomorph.esm-BOOTOg1r.cjs")),r=document.createElement(`div`);r.appendChild(t),n.morph(e,r,{morphStyle:`innerHTML`,callbacks:{beforeNodeMorphed:e=>!F(e)&&void 0}})}async function L(n,r=``,i=!0){let a=!i;x();let o=new AbortController;b={controller:o,pathname:n};let s={pathname:n,search:r,popstate:a};t(`elur:navigate-start`,{...s,fromCache:d(u(n,r))!==void 0});let c;try{c=await m(n,r,o.signal)}catch(e){if(o.signal.aborted)return!1;throw t(`elur:navigate-error`,{...s,fromCache:!1}),e}if(b&&b.controller!==o&&o.signal.aborted)return!1;if(b=null,!c)return t(`elur:navigate-error`,{...s,fromCache:!1}),!1;let{payload:l,fromCache:f}=c,p=document.getElementById(`app`);if(!p)return t(`elur:navigate-error`,{...s,fromCache:f}),!1;i&&history.replaceState({n:location.pathname,scroll:window.scrollY},``,location.href);let h=new URL(location.href),g=async()=>{let t=[];p.querySelectorAll(`[data-scroll-preserve]`).forEach(e=>{t.push({el:e,top:e.scrollTop})}),S(p);let a=()=>{let e=document.createElement(`template`);return e.innerHTML=l.body,S(e.content),e.content},o=a(),c=E(p,o);if(document.dispatchEvent(new CustomEvent(`elur:before-render`,{detail:{...s,persisted:c.map(e=>e.oldEl)}})),O(c),P)try{await I(p,o)}catch{let t=a(),n=[];for(let r of t.querySelectorAll(`[${e}]`)){let t=r.getAttribute(e),i=c.find(n=>n.oldEl.getAttribute(e)===t)?.oldEl;i&&n.push({oldEl:i,newEl:r})}O(n),p.replaceChildren(t)}else p.replaceChildren(o);R(l.head,l.title),l.clearActionErrorCookie&&(document.cookie=l.clearActionErrorCookie),i&&history.pushState({n,scroll:0},``,n+(r||h.search));let u=i?0:history.state?.scroll??0;window.scrollTo(0,u);for(let e of t){let t=p.querySelector(`[data-scroll-preserve="${e.el.getAttribute(`data-scroll-preserve`)}"]`);t&&(t.scrollTop=e.top)}T(n,r),N(`elur-data`,l.data??void 0),N(`elur-actions`,l.actions??void 0),M(p),C(n),i&&w(),document.dispatchEvent(new CustomEvent(`elur:rendered`,{detail:s}))};if(y()&&!v()){let e=document.startViewTransition(()=>g());try{await e.updateCallbackDone}catch{}}else await g();return t(`elur:navigate-end`,{...s,fromCache:f}),!0}function R(e,t){if(document.querySelectorAll(`[data-elur-head]`).forEach(e=>e.remove()),e&&e.trim().length>0){let t=document.createElement(`template`);t.innerHTML=e;let n=t.content,r=n.querySelector(`title`);r&&(document.title=r.textContent??``,r.remove()),document.head.appendChild(n)}else t&&(document.title=t)}var z=new WeakSet;function B(){let e=e=>{let t=e.getAttribute(`href`);if(!t||t.startsWith(`#`)||t.startsWith(`mailto:`)||t.startsWith(`javascript:`))return null;let n=t.indexOf(`?`);return{path:n===-1?t:t.slice(0,n),search:n===-1?``:t.slice(n)}},t=t=>{if(z.has(t)||!a(t)||t.hasAttribute(`data-no-prefetch`))return;z.add(t);let n=()=>{let n=e(t);n&&_(n.path,n.search,{force:t.dataset.prefetch===`always`})};if(t.addEventListener(`pointerenter`,n,{once:!0}),t.addEventListener(`focus`,n,{once:!0}),t.addEventListener(`pointerdown`,n,{once:!0}),t.dataset.prefetch===`viewport`&&`IntersectionObserver`in window){let n=new IntersectionObserver(t=>{for(let r of t){if(!r.isIntersecting)continue;let t=e(r.target);t&&_(t.path,t.search),n.disconnect()}},{rootMargin:`200px`,threshold:0});n.observe(t)}},n=()=>{let e=document.querySelectorAll(`a[href]`);for(let n of e)t(n)};n(),new MutationObserver(()=>n()).observe(document.body,{childList:!0,subtree:!0}),document.addEventListener(`elur:rendered`,n)}var V=200,H=`elur-loading-indicator`;function U(){if(typeof document>`u`)return;let e=null,t=null,n=null,r=0,i=()=>v(),a=()=>{e&&(e.style.transform=`scaleX(${Math.min(r,100)/100})`)},o=()=>{e||(e=document.createElement(`div`),e.id=H,e.setAttribute(`aria-hidden`,`true`),Object.assign(e.style,{position:`fixed`,top:`0`,left:`0`,right:`0`,height:`2px`,background:`currentColor`,color:`var(--elur-loading-color, #4f7cff)`,transform:`scaleX(0)`,transformOrigin:`0 50%`,transition:i()?`none`:`transform 150ms ease-out, opacity 200ms ease-in`,zIndex:`2147483647`,pointerEvents:`none`,opacity:`1`}),document.body.appendChild(e),r=10,a(),i()||(n=setInterval(()=>{r+=(80-r)*.12,a()},150)))},s=()=>{if(t&&=(clearTimeout(t),null),n&&=(clearInterval(n),null),!e)return;r=100,a();let o=e;e=null,setTimeout(()=>{o.style.opacity=`0`,setTimeout(()=>o.remove(),i()?0:200)},120)};document.addEventListener(`elur:navigate-start`,e=>{e.detail?.fromCache||(s(),t=setTimeout(o,V))}),document.addEventListener(`elur:navigate-end`,s),document.addEventListener(`elur:navigate-error`,s)}var W=!1;function G(e={}){if(P=e.morph===!0,W)return;W=!0,`scrollRestoration`in history&&(history.scrollRestoration=`manual`),document.querySelector(`meta[name="elur:render-endpoint"]`)?.getAttribute(`content`)===`off`&&(n=!1);let t=document.getElementById(`app`);t&&S(t),document.addEventListener(`click`,async e=>{if(!(e instanceof MouseEvent)||o(e)||e.defaultPrevented)return;let t=e.target.closest(`a`);if(!t||!a(t))return;let n=t.getAttribute(`href`);if(!n||n.startsWith(`mailto:`)||n.startsWith(`javascript:`))return;if(n.startsWith(`#`)){if(n.length>1){let t=document.getElementById(n.slice(1));t&&(e.preventDefault(),t.scrollIntoView({behavior:`smooth`,block:`start`}),history.replaceState(null,``,n))}return}e.preventDefault();let r=n.indexOf(`?`);await L(r===-1?n:n.slice(0,r),r===-1?``:n.slice(r))||location.assign(n)}),window.addEventListener(`popstate`,e=>{L(e.state?.n??location.pathname,location.search,!1)}),e.prefetch!==!1&&B(),e.loadingIndicator===!0&&U()}function K(){l.clear(),b=null,n=!0,r=null,A=null,P=!1,W=!1}exports.__resetRouterState=K,exports.hoistStyles=S,exports.navigateTo=L,exports.prefetch=_,exports.startClientRouter=G;
2
2
  //# sourceMappingURL=client.cjs.map