@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,2 @@
1
+ const e=require("./node-http-Uct7DhmN.cjs"),t=require("./invalidation-CFpNQUv6.cjs"),n=require("./scan-DXzYtmRQ.cjs"),r=require("./stream-Cg-xYG82.cjs"),i=require("./capabilities-C0TDQoMY.cjs"),a=require("./errors-kSl9yLkg.cjs"),o=require("./adapter-Dz2hzR36.cjs");let s=require("node:fs/promises"),c=require("node:path"),l=require("node:crypto");var u=new(require("node:async_hooks")).AsyncLocalStorage;function d(e,t){return`<template id="${e}-tpl">${t}</template><script>(function(){var t=document.getElementById(${JSON.stringify(e+`-tpl`)});var f=document.getElementById(${JSON.stringify(e)});if(t&&f){f.replaceWith(t.content.cloneNode(true));}document.dispatchEvent(new CustomEvent("elur:rendered"));})();<\/script>`}function f(e){let t=`elur-stream-${(0,l.randomUUID)().slice(0,8)}`,n=u.getStore();return n&&n.boundaries.set(t,{promise:e.promise,children:e.children}),{__isElurTemplate:!0,mount(n){let r=typeof n==`string`?document.querySelector(n):n;if(!r)throw Error(`[elur-kit] streamBoundary(): container not found`);let i=e.fallback.mount(r);return e.promise.then(t=>{let n=e.children(t);r.innerHTML=``;let a=n.mount(r);i.__elurChildHandle=a}).catch(e=>{console.error(`[elur-kit] streamBoundary ${t} failed:`,e)}),{unmount(){let e=i.__elurChildHandle;e?.unmount&&e.unmount(),i.unmount()}}},_render(n,r){let i=e.fallback._render(n,r);return e.promise.then(e=>{}).catch(e=>{console.error(`[elur-kit] streamBoundary ${t} failed:`,e)}),i}}}var p={"Content-Type":`text/html; charset=utf-8`,"X-Accel-Buffering":`no`,"Cache-Control":`no-store`};function m(){return`<div role="alert" style="margin:2rem auto;max-width:32rem;padding:1rem 1.25rem;border:1px solid #e5484d;border-radius:8px;color:#b3373c;font-family:system-ui,sans-serif"><strong style="display:block;margin-bottom:.25rem">No se pudo cargar el contenido.</strong><span>Recarga la página para intentarlo de nuevo.</span></div>`}async function h(t){let{route:n,params:r,searchParams:i,config:a,actions:o,importer:s=_,request:c,signal:u}=t;if(!n.loadingPath){let t=await e.p({route:n,params:r,searchParams:i,config:a,actions:o,importer:s,request:c});return t.response?t.response:new Response(t.html,{headers:{"Content-Type":`text/html; charset=utf-8`}})}if(u?.aborted)return new Response(`Client Closed Request`,{status:499});let f=await s(n.loadingPath),h=await e.T(f.default),g=`elur-stream-${(0,l.randomUUID)().slice(0,8)}`,v=a.router,y=v?.enabled!==!1,b=v?.entry&&y&&a.js!==`legacy`?v.entry:void 0,x=e.S({title:`Loading...`,lang:a.lang,body:`<div id="${g}">${h}</div>`,data:{__elur_js_streaming:!0,page:n.path},actions:o,clientEntry:a.clientEntry,routerEntry:b,routerEnabled:v?y:void 0,renderEndpoint:a.renderEndpoint}),S=!1,C=new ReadableStream({async start(t){let l=new TextEncoder,f=()=>{S=!0;try{t.close()}catch{}};u?.addEventListener(`abort`,f,{once:!0});let p=e=>{S||t.enqueue(l.encode(e))};try{p(x);let t=await e.p({route:n,params:r,searchParams:i,config:a,actions:o,importer:s,request:c});if(S)return;if(t.response){let e=t.response.status,n=t.response.headers.get(`Location`);p(n&&(e===301||e===302||e===307||e===308)?`<script>window.location.href=${JSON.stringify(n)};<\/script>`:d(g,m())+`<script>console.error(${JSON.stringify(`Loader responded with status ${e}`)});<\/script>`);return}let l=e.C(t.html)??t.html.match(/<div id="app">([\s\S]*)<\/div>\s*(<script|$)/)?.[1]?.trim()??t.html;p(d(g,l))}catch(e){let t=e instanceof Error?e.message:String(e);p(d(g,m())+`<script>console.error(${JSON.stringify(t)});<\/script>`)}finally{u?.removeEventListener(`abort`,f),S||t.close()}},cancel(){S=!0}});return new Response(C,{headers:p})}async function g(t){let{route:n,params:r,searchParams:i,config:a,actions:o,importer:s=_,request:c}=t,l=await e.p({route:n,params:r,searchParams:i,config:a,actions:o,importer:s,request:c});return l.response?l.response:new Response(l.html,{headers:{"Content-Type":`text/html; charset=utf-8`}})}var _=e=>import(e);function v(e,t){return t===e||t.startsWith(`${e}${c.sep}`)}function y(e){try{let t=decodeURIComponent(e);return t.includes(`\0`)||t.includes(`\\`)||/%(?:00|2e|2f|5c)/i.test(t)||t.split(`/`).some(e=>e===`..`)?null:t}catch{return null}}async function b(e,t){let n=y(t);if(n===null)return null;let r=(0,c.resolve)(e),i=n.replace(/^\/+/,``),a=(0,c.resolve)(r,i);if(!v(r,a))return null;try{(await(0,s.stat)(a)).isDirectory()&&(a=(0,c.resolve)(a,`index.html`))}catch{(n.endsWith(`/`)||(0,c.extname)(n)===``)&&(a=(0,c.resolve)(a,`index.html`))}if(!v(r,a))return null;try{let[e,t,n]=await Promise.all([(0,s.realpath)(r),(0,s.realpath)(a),(0,s.stat)(a)]);return!n.isFile()||!v(e,t)?null:t}catch{return null}}var x=class{cookies;constructor(e){this.cookies=C(e.headers.get(`Cookie`)??``)}get(e){return this.cookies[e]}getAll(){return{...this.cookies}}has(e){return e in this.cookies}},S=class{entries=[];set(e,t,n={}){this.entries.push(w(e,t,n))}clear(e,t={}){this.entries.push(w(e,``,{...t,maxAge:0,expires:new Date(0)}))}getAll(){return[...this.entries]}};function C(e){let t={};if(!e)return t;for(let n of e.split(`;`)){let e=n.indexOf(`=`);if(e===-1)continue;let r=n.slice(0,e).trim();t[r]=n.slice(e+1).trim()}return t}function w(e,t,n){let r=[`${e}=${t}`];return n.httpOnly&&r.push(`HttpOnly`),n.secure&&r.push(`Secure`),n.sameSite&&r.push(`SameSite=${n.sameSite}`),n.maxAge!==void 0&&r.push(`Max-Age=${n.maxAge}`),n.expires&&r.push(`Expires=${n.expires.toUTCString()}`),n.path&&r.push(`Path=${n.path}`),n.domain&&r.push(`Domain=${n.domain}`),r.join(`; `)}var T=class{request;url;config;routes;actions;publicActions;importer;renderEndpoint;noCache;cacheDir;defaultRevalidate;params;locals;cookies;signal;requestId;platform;route;response;constructor(e){this.request=e.request,this.url=new URL(e.request.url),this.config=e.config,this.routes=e.routes,this.actions=e.actions,this.publicActions=e.publicActions,this.importer=e.importer,this.renderEndpoint=e.renderEndpoint??!0,this.noCache=e.noCache??!1,this.cacheDir=e.cacheDir,this.defaultRevalidate=e.defaultRevalidate,this.params=e.params??{},this.locals=e.locals??{},this.cookies=new x(e.request),this.signal=e.signal??new AbortController().signal,this.requestId=e.requestId??(0,l.randomUUID)(),this.platform=e.platform,this.route=e.route,this.response={status:void 0,headers:new Headers,cookies:new S}}get pathname(){return this.url.pathname}get method(){return(this.request.method??`GET`).toUpperCase()}get wantsJson(){return(this.request.headers.get(`Accept`)??``).includes(`application/json`)}get searchParams(){return this.url.searchParams}get renderConfig(){return{lang:void 0,clientEntry:void 0,renderEndpoint:this.renderEndpoint}}applyToResponse(e){let t=new Headers(e.headers);for(let[e,n]of this.response.headers.entries())t.set(e,n);for(let e of this.response.cookies.getAll())t.append(`Set-Cookie`,e);let n=this.response.status??e.status;return new Response(e.body,{status:n,statusText:e.statusText,headers:t})}};function E(e,t=200,n){return new Response(e,{status:t,headers:{"Content-Type":`text/html; charset=utf-8`,...n}})}function D(e,t=200,n){return new Response(JSON.stringify(e),{status:t,headers:{"Content-Type":`application/json; charset=utf-8`,...n}})}function O(e,t=200,n){return new Response(e,{status:t,headers:{"Content-Type":`text/plain; charset=utf-8`,...n}})}function k(e=`Not Found`){return O(e,404)}function A(e){return O(`Method not allowed: ${e}`,405)}function j(e){return O(e,500)}function M(e){switch(e.slice(e.lastIndexOf(`.`)+1).toLowerCase()){case`html`:return`text/html; charset=utf-8`;case`js`:return`application/javascript; charset=utf-8`;case`mjs`:return`application/javascript; charset=utf-8`;case`css`:return`text/css; charset=utf-8`;case`json`:return`application/json; charset=utf-8`;case`svg`:return`image/svg+xml`;case`png`:return`image/png`;case`jpg`:case`jpeg`:return`image/jpeg`;case`webp`:return`image/webp`;case`avif`:return`image/avif`;case`ico`:return`image/x-icon`;case`woff`:return`font/woff`;case`woff2`:return`font/woff2`;case`wasm`:return`application/wasm`;case`txt`:return`text/plain; charset=utf-8`;default:return`application/octet-stream`}}async function N(e,t,n){let r=await b(e,t);if(!r)return null;try{let[e,t]=await Promise.all([(0,s.readFile)(r),(0,s.stat)(r)]),i=M(r),a=`"${(0,l.createHash)(`sha1`).update(e).digest(`hex`).slice(0,16)}"`,o=t.mtime.toUTCString(),c=n?.method===`HEAD`,u=e.byteLength,d={"Content-Type":i,"Content-Length":String(u),ETag:a,"Last-Modified":o,"Accept-Ranges":`bytes`},f=r.split(`/`).pop()??``;d[`Cache-Control`]=/[a-f0-9]{8,}\.(js|css|woff2?|wasm|png|jpg|jpeg|webp|avif|svg)$/i.test(f)?`public, max-age=31536000, immutable`:`public, max-age=0, must-revalidate`;let p=n?.headers.get(`If-None-Match`);if(p&&P(p,a))return new Response(null,{status:304,headers:d});let m=n?.headers.get(`If-Modified-Since`);if(m){let e=Date.parse(m);if(!isNaN(e)&&Math.floor(t.mtime.getTime()/1e3)<=Math.floor(e/1e3))return new Response(null,{status:304,headers:d})}let h=n?.headers.get(`Range`),g=n?.headers.get(`If-Range`);if(h&&(!g||F(g,a,t.mtime))){let t=I(h,u);if(t===null)return new Response(null,{status:416,headers:{...d,"Content-Range":`bytes */${u}`}});if(t){let[n,r]=t,i=e.subarray(n,r+1),a={...d,"Content-Length":String(i.byteLength),"Content-Range":`bytes ${n}-${r}/${u}`};return c?new Response(null,{status:206,headers:a}):new Response(i,{status:206,headers:a})}}return c?new Response(null,{status:200,headers:d}):new Response(e,{status:200,headers:d})}catch{return null}}function P(e,t){return e.split(`,`).map(e=>e.trim()).some(e=>e===`*`||e===t)}function F(e,t,n){if(e.startsWith(`"`)||e.startsWith(`W/`))return e===t;let r=Date.parse(e);return!isNaN(r)&&Math.floor(n.getTime()/1e3)<=Math.floor(r/1e3)}function I(e,t){let n=/^bytes=(\d*)-(\d*)$/.exec(e.trim());if(!n)return null;let r=n[1],i=n[2];if(r===``&&i===``)return null;if(r===``){let e=Number(i);if(!Number.isSafeInteger(e)||e<=0)return null;let n=Math.max(0,t-e);return t===0?void 0:[n,t-1]}let a=Number(r);if(!Number.isSafeInteger(a)||a<0||a>=t)return null;let o=i===``?t-1:Number(i);return!Number.isSafeInteger(o)||o<a?null:[a,Math.min(o,t-1)]}var L={noSniff:!0,referrerPolicy:`strict-origin-when-cross-origin`,frameAncestors:`SAMEORIGIN`};function R(e,t,n){if(e===!1)return{};let r={},i={...L,...e};if(i.noSniff&&(r[`X-Content-Type-Options`]=`nosniff`),i.referrerPolicy&&(r[`Referrer-Policy`]=i.referrerPolicy),i.contentSecurityPolicy){let e=i.contentSecurityPolicy;n&&(e=e.replace(/\bnonce\b/g,`'nonce-${n}'`)),r[`Content-Security-Policy`]=e}else if(i.frameAncestors){let e=i.frameAncestors;r[`X-Frame-Options`]=e===`NONE`?`DENY`:e===`SAMEORIGIN`?`SAMEORIGIN`:e}return i.hsts===!0&&t?r[`Strict-Transport-Security`]=`max-age=15552000; includeSubDomains`:typeof i.hsts==`string`&&(r[`Strict-Transport-Security`]=i.hsts),i.permissionsPolicy&&(r[`Permissions-Policy`]=i.permissionsPolicy),r}function z(e,t){if(Object.keys(t).length===0)return e;let n=new Headers(e.headers);for(let[e,r]of Object.entries(t))n.has(e)||n.set(e,r);return new Response(e.body,{status:e.status,statusText:e.statusText,headers:n})}var B={debug:10,info:20,warn:30,error:40},ee=new Set([`cookie`,`authorization`,`set-cookie`,`x-api-key`,`x-auth-token`]),V=class{minLevel;requestId;timings=[];entries=[];constructor(e={}){this.minLevel=e.minLevel??(process.env.NODE_ENV===`production`?`info`:`debug`),this.requestId=e.requestId??(0,l.randomUUID)()}getRequestId(){return this.requestId}debug(e,t){this.log(`debug`,e,t)}info(e,t){this.log(`info`,e,t)}warn(e,t){this.log(`warn`,e,t)}error(e,t){this.log(`error`,e,t)}timing(e,t,n){this.timings.push({name:e,durationMs:t,description:n})}startTimer(e,t){let n=performance.now();return()=>{this.timing(e,performance.now()-n,t)}}getServerTimingHeader(){return this.timings.map(e=>{let t=e.description?`;desc="${e.description}"`:``;return`${e.name};dur=${e.durationMs.toFixed(1)}${t}`}).join(`, `)}getEntries(){return this.entries}log(e,t,n){if(B[e]<B[this.minLevel])return;let r={level:e,message:t,requestId:this.requestId,timestamp:new Date().toISOString(),fields:n?H(n):void 0};if(this.entries.push(r),process.env.NODE_ENV===`production`){let t=JSON.stringify(r);e===`error`?console.error(t):e===`warn`?console.warn(t):console.log(t)}else{let n=`${`[${e.toUpperCase()}]`} ${t}${r.fields?` `+JSON.stringify(r.fields):``}`;e===`error`?console.error(n):e===`warn`?console.warn(n):console.log(n)}}};function H(e){let t={};for(let[n,r]of Object.entries(e)){let e=n.toLowerCase();t[n]=ee.has(e)?`[REDACTED]`:typeof r==`object`&&r&&!Array.isArray(r)?H(r):r}return t}function U(e,t){return new V({minLevel:t,requestId:e?.headers.get(`X-Request-ID`)??void 0})}function W(e,t){for(let n of t){let t=q(e,n.from);if(t){let e=J(n.to,t),r=n.status??308;return new Response(null,{status:r,headers:{Location:e}})}}}function G(e,t){for(let n of t){let t=q(e,n.from);if(t)return J(n.to,t)}}function K(e,t){for(let n of t)if(q(e,n.path))return n.headers}function q(e,t){let n=e.split(`?`)[0].split(`/`).filter(Boolean),r=t.split(`/`).filter(Boolean),i={},a=0;for(let e=0;e<r.length;e++){let t=r[e];if(t===`*`)return i;if(t.endsWith(`*`)){let e=t.slice(1,-1);return i[e]=n.slice(a).join(`/`),i}if(t.startsWith(`:`)){let e=t.slice(1);if(n[a]===void 0)return;i[e]=n[a],a++;continue}if(t!==n[a])return;a++}if(a===n.length)return i}function J(e,t){return e.replace(/:(\w+)\*?/g,(e,n)=>t[n]??``)}function te(s,c,l){let u=n.t(c),d=l.lang??`es`,f=l.clientEntry,p=l.renderEndpoint??!0,m=l.noCache??!1,g=l.defaultRevalidate,_={lang:d,clientEntry:f,renderEndpoint:p,router:l.router?{enabled:l.router.enabled!==!1,entry:l.router.entry}:void 0,js:l.js},v=l.securityHeaders??{},y=l.redirects??[],b=l.rewrites??[],x=l.routeHeaders??[],S=l.capabilities??i.t,C=l.streaming===!0&&i.o(S),w=Z(l);w&&!X.has(w)&&(X.add(w),t.n(w));function T(){return async(t,n)=>{let r=n?s.pages.some(e=>e.path===n)?n:e.u(n,s.pages)?.route.path??n:void 0,i=r?c[r]:Object.values(c).find(e=>e[t])??void 0,a=i?i[t]:void 0;if(!a)return;if(l.importer){let e=(await l.importer(a))[t];return typeof e==`function`?e:void 0}let o=(await import(a))[t];if(typeof o==`function`)return o}}let O=T();async function j(t,n){let r=n.startTimer(`action`,`Server action`);try{return await e.o(t,O)}catch(e){return n.error(`[elur-kit] action error`,{path:new URL(t.url).pathname,method:t.method,error:Q(e),stack:$(e)}),a.s(e,{includeDetail:m})}finally{r()}}async function M(e,t,n){let i=t.searchParams.get(`page`)??`/`,o=t.searchParams.get(`search`)??``,c=(e.headers.get(`Accept`)??``).includes(`application/json`),d=n.startTimer(`render-endpoint`,`SPA render endpoint`);try{let t=await r.n({routes:s,pathname:i,searchParams:new URLSearchParams(o),config:_,actions:u,request:e,importer:l.importer});if(t.response)return t.response;let{body:n,title:a,head:d,clearActionErrorCookie:f,data:p,actions:m}=t;if(c){let e={};return f&&(e[`X-Elur-Action-Clear-Cookie`]=f),D({title:a,body:n,head:d??null,data:p??null,actions:m??null,clearActionErrorCookie:f??null},200,e)}return E(n,200,f?{"Set-Cookie":f}:void 0)}catch(e){return e instanceof r.t?k(`Not Found`):e instanceof Response?e:(n.error(`[elur-kit] render endpoint error`,{path:t.pathname,page:i,error:Q(e),stack:$(e)}),a.s(e,{includeDetail:m}))}finally{d()}}async function P(t,n,r,i){let o=e.l(n,s.api);if(!o)return null;let c=r.startTimer(`api`,`API route`);try{let e;e=l.importer?await l.importer(o.route.routePath):await import(o.route.routePath);let n=e[t.method??`GET`];return typeof n==`function`?await n(t,{params:o.params,locals:i??{}}):A(t.method??`GET`)}catch(e){return r.error(`[elur-kit] API route error`,{path:n,method:t.method,route:o.route.path,error:Q(e),stack:$(e)}),a.s(e,{includeDetail:m})}finally{c()}}async function F(e,t){let n=await N(l.staticRoot,e,t);if(n&&m){let e=n.headers.get(`Content-Type`)??``;if(e.includes(`text/html`)){let t=(await n.text()).replace(`<meta name="elur:render-endpoint" content="off" />`,``);return new Response(t,{status:n.status,headers:{"Content-Type":e,"Cache-Control":`no-store, must-revalidate`}})}return new Response(n.body,{status:n.status,headers:{...Object.fromEntries(n.headers.entries()),"Cache-Control":`no-store, must-revalidate`}})}if(n&&p&&(n.headers.get(`Content-Type`)??``).includes(`text/html`)){let e=Object.fromEntries(n.headers.entries());delete e[`content-length`];let t=await n.text();if(t.includes(`elur:render-endpoint" content="off"`)){let r=t.replace(`<meta name="elur:render-endpoint" content="off" />`,`<meta name="elur:render-endpoint" content="on" />`);return new Response(r,{status:n.status,headers:e})}return new Response(t,{status:n.status,headers:e})}return n}async function I(t,n,r){let i=e.u(n,s.pages);if(!i){let t=await e.f({routes:s,status:404,config:_,actions:u,importer:l.importer});return t?E(t.html,t.status):k(`Not found: ${n}`)}if(C&&i.route.loadingPath){let o=r.startTimer(`ssr`,`SSR stream shell`);try{return await h({route:i.route,params:i.params,searchParams:new URLSearchParams(t.url.split(`?`)[1]??``),config:_,actions:u,importer:l.importer,request:t,signal:t.signal})}catch(t){if(t instanceof Response)return t;r.error(`[elur-kit] SSR stream error`,{path:n,route:i.route.path,error:Q(t),stack:$(t)});let o=await e.f({routes:s,status:500,error:t,config:_,actions:u,importer:l.importer}).catch(()=>void 0);return o?E(o.html,o.status):a.s(t,{includeDetail:m})}finally{o()}}let c=!m&&w&&ne(t),d=c?o.n(n):void 0,f=async()=>{let n=await e.p({route:i.route,params:i.params,searchParams:new URLSearchParams(t.url.split(`?`)[1]??``),config:_,actions:u,request:t,importer:l.importer});if(n.response)return n.response;if(c&&w&&d&&re(n,t)){let e=n.revalidate??g??0;e>0&&await w.set(d,{html:n.html,generatedAt:Date.now(),revalidate:e},{revalidate:e,tags:n.cachePolicy?.tags})}return E(n.html)};if(c&&w&&d){let e=await w.get(d);if(e)return Date.now()-e.generatedAt>=e.revalidate*1e3&&f().catch(e=>{r.error(`[elur-kit] background cache revalidation failed`,{path:n,error:Q(e),stack:$(e)})}),E(e.html)}let p=r.startTimer(`ssr`,`SSR render`);try{return await f()}catch(t){if(t instanceof Response)return t;r.error(`[elur-kit] SSR render error`,{path:n,route:i.route.path,error:Q(t),stack:$(t)});let o=await e.f({routes:s,status:500,error:t,config:_,actions:u,importer:l.importer}).catch(()=>void 0);return o?E(o.html,o.status):a.s(t,{includeDetail:m})}finally{p()}}function L(e,t,n,r){let i=z(e,n),a=new Headers(i.headers);if(r)for(let[e,t]of Object.entries(r))a.set(e,t);let o=t.getServerTimingHeader();return o&&a.set(`Server-Timing`,o),a.set(`X-Request-ID`,t.getRequestId()),new Response(i.body,{status:i.status,statusText:i.statusText,headers:a})}return async function(t){let n=U(t,l.logLevel),r=new URL(t.url),i=r.pathname,o=r.protocol===`https:`,s=v===!1?{}:R(v,o);if(y.length>0){let e=W(i,y);if(e)return L(e,n,s,K(i,x))}let c=i;b.length>0&&(c=G(i,b)??i);let u=K(i,x);if(c===`/__elur-js/actions`&&t.method===`POST`)return L(await j(t,n),n,s,u);if(c===`/__elur-js/render`&&p)return L(await M(t,r,n),n,s,u);let d,f=l.middleware;if(f&&e.i(c,f.config)){let r;try{r=await e.a(f,t)}catch(e){return n.error(`[elur-kit] middleware error`,{path:c,error:Q(e),stack:$(e)}),L(a.s(e,{includeDetail:m}),n,s,u)}if(r.kind===`response`)return L(r.response,n,s,u);if(d=r.locals,r.headers){let e=new Headers(t.headers);for(let[t,n]of Object.entries(r.headers))e.set(t,n);t=new Request(t,{headers:e})}}return L(await P(t,c,n,d)||await F(c,t)||await I(t,c,n),n,s,u)}}var Y=new Map,X=new WeakSet;function Z(e){if(e.cacheAdapter)return e.cacheAdapter;if(!e.cacheDir)return;let t=Y.get(e.cacheDir);return t||(t=o.r({cacheDir:e.cacheDir}),Y.set(e.cacheDir,t)),t}function Q(e){return e instanceof Error?e.message:String(e)}function $(e){return e instanceof Error?e.stack:void 0}function ne(e){return!(e.method!==`GET`&&e.method!==`HEAD`||e.headers.get(`Cookie`)||e.headers.get(`Authorization`))}function re(e,n){return e.html.includes(`__elur_js_action_error`)?!1:e.cachePolicy?t.o(e.cachePolicy,n):!(!e.revalidate||e.revalidate<=0||n.headers.get(`Cookie`)||n.headers.get(`Authorization`))}Object.defineProperty(exports,"_",{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return z}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,"d",{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,"f",{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,"g",{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,"h",{enumerable:!0,get:function(){return O}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return L}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,"m",{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return V}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return R}}),Object.defineProperty(exports,"p",{enumerable:!0,get:function(){return N}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return U}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return te}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,"v",{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,"y",{enumerable:!0,get:function(){return f}});
2
+ //# sourceMappingURL=runtime-ChE4flX3.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-ChE4flX3.cjs","names":[],"sources":["../../src/middleware/stream-boundary.ts","../../src/ssr/stream-response.ts","../../src/runtime/static.ts","../../src/runtime/context.ts","../../src/runtime/security-headers.ts","../../src/runtime/logger.ts","../../src/router/redirects.ts","../../src/runtime/handler.ts"],"sourcesContent":["// --- Stream boundary (per-request, real Suspense streaming) ---\n//\n// `streamBoundary()` wraps a promise in a loading fallback. During SSR runtime,\n// the server emits the fallback HTML immediately, then streams a `<template>`\n// chunk with a replacement script that the browser executes to swap the\n// fallback for the resolved content in-place (real Suspense streaming).\n//\n// In SSG (build time), boundaries are resolved synchronously — the build waits\n// for all promises before writing the HTML, so no streaming occurs.\n//\n// Boundaries are tracked per-request via AsyncLocalStorage to avoid global\n// state leakage between concurrent requests.\n\nimport type { ElurTemplate } from \"@elurjs/core\";\nimport { randomUUID } from \"node:crypto\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\nexport interface StreamBoundaryOptions<T> {\n /** Fallback content shown while the promise resolves. */\n fallback: ElurTemplate;\n /** Promise that resolves to a ElurTemplate. */\n promise: Promise<T>;\n /** Renders the resolved value to a ElurTemplate. */\n children: (value: T) => ElurTemplate;\n}\n\n/** Per-request boundary registry. */\ninterface BoundaryContext {\n boundaries: Map<string, {\n promise: Promise<unknown>;\n children: (value: unknown) => ElurTemplate;\n }>;\n}\n\nconst boundaryALS = new AsyncLocalStorage<BoundaryContext>();\n\n/**\n * Gets the current per-request boundary context, if any.\n * Used by the streaming response to collect boundaries for later resolution.\n */\nexport function getCurrentBoundaryContext(): BoundaryContext | undefined {\n return boundaryALS.getStore();\n}\n\n/**\n * Runs a function within a per-request boundary context.\n * Used by the SSR streaming pipeline to collect boundaries.\n */\nexport function withBoundaryContext<T>(fn: () => T): T {\n const ctx: BoundaryContext = { boundaries: new Map() };\n return boundaryALS.run(ctx, fn);\n}\n\n/**\n * Builds the fallback HTML wrapper for a boundary ID.\n * The fallback content is wrapped in a `<div>` with the boundary ID so the\n * browser can find it and replace it when the resolved content arrives.\n *\n * (v2.1 — Fix #4: real Suspense streaming with `<template>` replacement)\n */\nexport function buildFallbackHtml(boundaryId: string, fallbackHtml: string): string {\n return `<div id=\"${boundaryId}\" style=\"display:contents\" data-elur-boundary=\"${boundaryId}\">${fallbackHtml}</div>`;\n}\n\n/**\n * Builds the resolved content chunk for a boundary ID.\n * Emits a `<template>` element with the resolved content, followed by a\n * `<script>` that replaces the fallback div with the template content\n * in-place. This is real Suspense streaming — the browser swaps the DOM\n * node without a full re-render.\n *\n * (v2.1 — Fix #4: real Suspense streaming with `<template>` replacement)\n */\nexport function buildResolvedChunk(boundaryId: string, resolvedHtml: string): string {\n // Escape the resolved HTML for safe embedding inside a <template> tag.\n // <template> content is inert (not parsed as DOM), so we store the raw\n // HTML and clone it via `content.cloneNode(true)`.\n return `<template id=\"${boundaryId}-tpl\">${resolvedHtml}</template>` +\n `<script>(function(){` +\n `var t=document.getElementById(${JSON.stringify(boundaryId + \"-tpl\")});` +\n `var f=document.getElementById(${JSON.stringify(boundaryId)});` +\n `if(t&&f){f.replaceWith(t.content.cloneNode(true));}` +\n `document.dispatchEvent(new CustomEvent(\"elur:rendered\"));` +\n `})();</script>`;\n}\n\n/**\n * Creates a stream boundary. During SSR, emits the fallback and registers the\n * promise for later resolution by the streaming pipeline. During SSG, the\n * build awaits all boundaries before writing HTML.\n *\n * The boundary ID is deterministic per-request via crypto.randomUUID().\n */\nexport function streamBoundary<T>(options: StreamBoundaryOptions<T>): ElurTemplate {\n const id = `elur-stream-${randomUUID().slice(0, 8)}`;\n const ctx = boundaryALS.getStore();\n\n // In SSR mode with a boundary context, register the promise for later.\n if (ctx) {\n ctx.boundaries.set(id, {\n promise: options.promise,\n children: options.children as (value: unknown) => ElurTemplate,\n });\n }\n\n return {\n __isElurTemplate: true as const,\n mount(container: Element | string) {\n const el = typeof container === \"string\" ? document.querySelector(container) : container;\n if (!el) throw new Error(\"[elur-kit] streamBoundary(): container not found\");\n // Render fallback initially.\n const handle = options.fallback.mount(el);\n // Attempt to resolve and swap (works in both SSR and client).\n options.promise\n .then((value) => {\n const content = options.children(value);\n el.innerHTML = \"\";\n const childHandle = content.mount(el);\n // Store the new handle for cleanup.\n (handle as any).__elurChildHandle = childHandle;\n })\n .catch((err) => {\n console.error(`[elur-kit] streamBoundary ${id} failed:`, err);\n });\n return {\n unmount() {\n const childHandle = (handle as any).__elurChildHandle;\n if (childHandle?.unmount) childHandle.unmount();\n handle.unmount();\n },\n };\n },\n _render(parent: Node, before: Node | null): () => void {\n // For SSR/build: render fallback inline. The promise resolution is\n // handled by the streaming pipeline when available.\n const dispose = options.fallback._render(parent, before);\n\n // Kick off the promise resolution in the background.\n options.promise\n .then((value) => {\n void value;\n })\n .catch((err) => {\n console.error(`[elur-kit] streamBoundary ${id} failed:`, err);\n });\n\n return dispose;\n },\n } as unknown as ElurTemplate;\n}\n","// --- Real streaming with ReadableStream (plan §10) ---\n//\n// Creates a Web Response with a ReadableStream that:\n// 1. Sends the document shell + loading fallback immediately.\n// 2. Runs the full page render (loaders included) in the background.\n// 3. Appends a resolved-content chunk with a deterministic boundary ID.\n// 4. Includes a swap script that replaces the loading boundary in-place.\n// 5. Cancels the stream and the background render when the client\n// disconnects (AbortSignal), cleaning up listeners.\n//\n// Response contract (mirrors what Next.js documents for self-hosted\n// streaming): `Content-Type: text/html` is sent early, the body is chunked\n// (no `Content-Length`), `X-Accel-Buffering: no` asks reverse proxies like\n// nginx not to buffer the stream, and `Cache-Control: no-store` keeps CDNs\n// from caching a half-sent dynamic stream. Streamed responses are never\n// written to the ISR cache — caching a stream mid-flight is unsound, so\n// routes served this way always render live.\n//\n// For adapters without streaming support, `createBufferedResponse()` provides\n// a fallback that buffers the full response and returns it as a single\n// Response (no streaming).\n\nimport type { ElurTemplate } from \"@elurjs/core\";\nimport { renderToString } from \"../render/render-to-string.js\";\nimport { documentShell, extractAppBody } from \"../build/document-shell.js\";\nimport type { PageRoute } from \"../router/route-scanner.js\";\nimport type { BuildConfig } from \"../build/build.js\";\nimport { renderPage } from \"./render.js\";\nimport { randomUUID } from \"node:crypto\";\nimport { buildResolvedChunk } from \"../middleware/stream-boundary.js\";\n\nexport interface StreamResponseOptions {\n route: PageRoute;\n params: Record<string, string | string[]>;\n searchParams: URLSearchParams;\n config: Pick<BuildConfig, \"lang\" | \"clientEntry\" | \"renderEndpoint\" | \"router\" | \"js\">;\n actions?: Record<string, string[]>;\n importer?: (path: string) => Promise<unknown>;\n request?: Request;\n /** AbortSignal from the host (client disconnect). */\n signal?: AbortSignal;\n}\n\n/** Standard headers for a streamed HTML response. */\nconst STREAM_HEADERS: Record<string, string> = {\n \"Content-Type\": \"text/html; charset=utf-8\",\n // Ask reverse proxies (nginx and friends) not to buffer the stream; without\n // this the client receives the whole response at once and streaming is\n // pointless. See https://nextjs.org/docs/app/guides/self-hosting#streaming-and-suspense\n \"X-Accel-Buffering\": \"no\",\n // A streamed dynamic page is rendered live per request: intermediaries and\n // browsers must not cache it.\n \"Cache-Control\": \"no-store\",\n};\n\n/**\n * Mid-stream error notice swapped into the loading boundary when the\n * background render fails after the shell was already sent. Inline styles\n * keep it self-contained (the page's CSS may assume the final layout).\n */\nfunction buildErrorNotice(): string {\n return `<div role=\"alert\" style=\"margin:2rem auto;max-width:32rem;padding:1rem 1.25rem;border:1px solid #e5484d;border-radius:8px;color:#b3373c;font-family:system-ui,sans-serif\">` +\n `<strong style=\"display:block;margin-bottom:.25rem\">No se pudo cargar el contenido.</strong>` +\n `<span>Recarga la página para intentarlo de nuevo.</span></div>`;\n}\n\n/**\n * Creates a streaming Response that sends the shell + loading fallback first,\n * then appends the resolved content.\n *\n * If the route has no loading boundary, falls back to a normal renderPage.\n */\nexport async function createStreamingResponse(\n options: StreamResponseOptions,\n): Promise<Response> {\n const { route, params, searchParams, config, actions, importer = defaultImport, request, signal } = options;\n\n // If no loading boundary, do a normal render (no streaming).\n if (!route.loadingPath) {\n const result = await renderPage({\n route,\n params,\n searchParams,\n config,\n actions,\n importer,\n request,\n });\n if (result.response) return result.response;\n return new Response(result.html, {\n headers: { \"Content-Type\": \"text/html; charset=utf-8\" },\n });\n }\n\n // The client already disconnected before we produced anything.\n if (signal?.aborted) {\n return new Response(\"Client Closed Request\", { status: 499 });\n }\n\n // Load the loading boundary component.\n const loadingMod = (await importer(route.loadingPath)) as { default: () => ElurTemplate };\n const loadingHtml = await renderToString(loadingMod.default);\n\n // Deterministic boundary ID for the swap.\n const boundaryId = `elur-stream-${randomUUID().slice(0, 8)}`;\n\n // Build the shell with the loading fallback. Streaming sends the shell\n // before the body is known, so the 0%-JS island scan cannot run here —\n // streamed routes always emit the client entry (they are few and usually\n // interactive anyway). The split router chunk is emitted when configured.\n const routerCfg = config.router;\n const routerEnabled = routerCfg?.enabled !== false;\n const routerEntry =\n routerCfg?.entry && routerEnabled && config.js !== \"legacy\"\n ? routerCfg.entry\n : undefined;\n const shellHtml = documentShell({\n title: \"Loading...\",\n lang: config.lang,\n body: `<div id=\"${boundaryId}\">${loadingHtml}</div>`,\n data: { __elur_js_streaming: true, page: route.path },\n actions,\n clientEntry: config.clientEntry,\n routerEntry,\n routerEnabled: routerCfg ? routerEnabled : undefined,\n renderEndpoint: config.renderEndpoint,\n });\n\n // Create a ReadableStream that sends the shell, then the resolved content.\n let aborted = false;\n const stream = new ReadableStream<Uint8Array>({\n async start(controller) {\n const encoder = new TextEncoder();\n\n const onAbort = () => {\n aborted = true;\n try {\n controller.close();\n } catch {\n // Already closed/errored — nothing to do.\n }\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n /** Enqueue unless the client went away mid-render. */\n const send = (html: string): void => {\n if (aborted) return;\n controller.enqueue(encoder.encode(html));\n };\n\n try {\n // Send the shell immediately.\n send(shellHtml);\n\n // Run the full page render in the background.\n const result = await renderPage({\n route,\n params,\n searchParams,\n config,\n actions,\n importer,\n request,\n });\n if (aborted) return;\n\n // If a loader threw a Response, send a redirect/error script.\n if (result.response) {\n const status = result.response.status;\n const location = result.response.headers.get(\"Location\");\n if (location && (status === 301 || status === 302 || status === 307 || status === 308)) {\n send(`<script>window.location.href=${JSON.stringify(location)};</script>`);\n } else {\n // A non-redirect thrown response (404, 403, ...): swap the loading\n // boundary for an error notice instead of leaving a spinner.\n send(\n buildResolvedChunk(boundaryId, buildErrorNotice()) +\n `<script>console.error(${JSON.stringify(`Loader responded with status ${status}`)});</script>`,\n );\n }\n return;\n }\n\n // Extract the inner body from the full render. The document shell\n // wraps the page in explicit markers; the regex fallback covers\n // documents assembled without them.\n const innerBody = extractAppBody(result.html)\n ?? result.html.match(/<div id=\"app\">([\\s\\S]*)<\\/div>\\s*(<script|$)/)?.[1]?.trim()\n ?? result.html;\n\n // Send a `<template>` chunk + replacement script that swaps the\n // loading boundary with the real content in-place.\n send(buildResolvedChunk(boundaryId, innerBody));\n } catch (err) {\n // The shell is already on the wire, so the error must arrive as a\n // chunk: swap the loading boundary for an error notice and log the\n // details to the console.\n const errorMsg = err instanceof Error ? err.message : String(err);\n send(\n buildResolvedChunk(boundaryId, buildErrorNotice()) +\n `<script>console.error(${JSON.stringify(errorMsg)});</script>`,\n );\n } finally {\n signal?.removeEventListener(\"abort\", onAbort);\n if (!aborted) {\n controller.close();\n }\n }\n },\n\n cancel() {\n // Client disconnected (the runtime cancelled the stream): mark aborted\n // so a render completing late never enqueues into a dead stream.\n aborted = true;\n },\n });\n\n return new Response(stream, {\n // No `Content-Length` and no explicit `Transfer-Encoding`: the host\n // runtime (Node, Bun, edge) chunks the body automatically when the\n // length is unknown. Setting Transfer-Encoding by hand produces a\n // duplicated `chunked, chunked` header under Node.\n headers: STREAM_HEADERS,\n });\n}\n\n/**\n * Buffered fallback for adapters without streaming support.\n * Renders the full page and returns it as a single Response.\n */\nexport async function createBufferedResponse(\n options: StreamResponseOptions,\n): Promise<Response> {\n const { route, params, searchParams, config, actions, importer = defaultImport, request } = options;\n\n const result = await renderPage({\n route,\n params,\n searchParams,\n config,\n actions,\n importer,\n request,\n });\n\n if (result.response) return result.response;\n\n return new Response(result.html, {\n headers: { \"Content-Type\": \"text/html; charset=utf-8\" },\n });\n}\n\nconst defaultImport = (path: string) => import(path);\n\n/**\n * Checks if the host runtime supports streaming responses.\n * Node, Bun, and modern edge runtimes do. Some serverless platforms may not.\n */\nexport { supportsStreaming } from \"../runtime/capabilities.js\";\n","import { realpath, stat } from \"node:fs/promises\";\nimport { extname, resolve, sep } from \"node:path\";\n\nfunction isInside(root: string, candidate: string): boolean {\n return candidate === root || candidate.startsWith(`${root}${sep}`);\n}\n\nfunction decodePathname(pathname: string): string | null {\n try {\n const decoded = decodeURIComponent(pathname);\n if (decoded.includes(\"\\0\") || decoded.includes(\"\\\\\") || /%(?:00|2e|2f|5c)/i.test(decoded)) return null;\n if (decoded.split(\"/\").some((segment) => segment === \"..\")) return null;\n return decoded;\n } catch {\n return null;\n }\n}\n\nexport async function resolveStaticFile(root: string, pathname: string): Promise<string | null> {\n const decoded = decodePathname(pathname);\n if (decoded === null) return null;\n\n const resolvedRoot = resolve(root);\n const relativePath = decoded.replace(/^\\/+/, \"\");\n let candidate = resolve(resolvedRoot, relativePath);\n if (!isInside(resolvedRoot, candidate)) return null;\n\n try {\n const candidateStat = await stat(candidate);\n if (candidateStat.isDirectory()) candidate = resolve(candidate, \"index.html\");\n } catch {\n if (decoded.endsWith(\"/\") || extname(decoded) === \"\") candidate = resolve(candidate, \"index.html\");\n }\n\n if (!isInside(resolvedRoot, candidate)) return null;\n\n try {\n const [canonicalRoot, canonicalCandidate, candidateStat] = await Promise.all([\n realpath(resolvedRoot),\n realpath(candidate),\n stat(candidate),\n ]);\n if (!candidateStat.isFile() || !isInside(canonicalRoot, canonicalCandidate)) return null;\n return canonicalCandidate;\n } catch {\n return null;\n }\n}\n","import type { ResolvedElurConfig } from \"../config/index.js\";\nimport { randomUUID } from \"node:crypto\";\n\n// --- RequestContext: unified per-request runtime context ---\n//\n// Every runtime path (SSR server, CLI preview/dev, adapters, Vite plugin)\n// eventually funnels through a single Web handler that receives a Web Request\n// and returns a Web Response. RequestContext carries the resolved config,\n// route tables, action registry and request-scoped state so handlers do not\n// re-derive this information on every request.\n//\n// Design goals (runtime-security §4):\n// * One type used by every runtime entry point.\n// * No Node-specific APIs on the type — only Web standards.\n// * Carries per-request state: params, locals, cookies, signal, requestId.\n// * response.headers supports multiple Set-Cookie without collapsing them.\n// * signal aborts when the host disconnects (when the platform allows it).\n// * Middleware/loaders/actions share the same context or readonly views.\n\nexport interface RouteTable {\n pages: import(\"../router/route-scanner.js\").PageRoute[];\n api: import(\"../router/route-scanner.js\").ApiRoute[];\n error404?: import(\"../router/route-scanner.js\").PageRoute;\n error500?: import(\"../router/route-scanner.js\").PageRoute;\n}\n\n// --- CookieJar: read cookies from request, write to response ---\n\n/** Read-only access to request cookies. */\nexport interface CookieJar {\n /** Gets a cookie value by name, or undefined if not present. */\n get(name: string): string | undefined;\n /** Returns all cookie name-value pairs. */\n getAll(): Record<string, string>;\n /** Checks if a cookie exists. */\n has(name: string): boolean;\n}\n\n/** Write access to response cookies (Set-Cookie headers). */\nexport interface ResponseCookieJar {\n /** Sets a Set-Cookie header. */\n set(name: string, value: string, options?: CookieOptions): void;\n /** Removes a cookie by setting it expired. */\n clear(name: string, options?: CookieOptions): void;\n /** Returns all Set-Cookie header values accumulated so far. */\n getAll(): string[];\n}\n\nexport interface CookieOptions {\n httpOnly?: boolean;\n secure?: boolean;\n sameSite?: \"strict\" | \"lax\" | \"none\";\n maxAge?: number;\n expires?: Date;\n path?: string;\n domain?: string;\n}\n\n/** Mutable response state accumulated during the request lifecycle. */\nexport interface ResponseState {\n status?: number;\n headers: Headers;\n cookies: ResponseCookieJar;\n}\n\n// --- Cookie implementation ---\n\nclass RequestCookieJar implements CookieJar {\n private cookies: Record<string, string>;\n\n constructor(request: Request) {\n this.cookies = parseCookies(request.headers.get(\"Cookie\") ?? \"\");\n }\n\n get(name: string): string | undefined {\n return this.cookies[name];\n }\n\n getAll(): Record<string, string> {\n return { ...this.cookies };\n }\n\n has(name: string): boolean {\n return name in this.cookies;\n }\n}\n\nclass MutableResponseCookieJar implements ResponseCookieJar {\n private entries: string[] = [];\n\n set(name: string, value: string, options: CookieOptions = {}): void {\n this.entries.push(serializeCookie(name, value, options));\n }\n\n clear(name: string, options: CookieOptions = {}): void {\n this.entries.push(serializeCookie(name, \"\", { ...options, maxAge: 0, expires: new Date(0) }));\n }\n\n getAll(): string[] {\n return [...this.entries];\n }\n}\n\nfunction parseCookies(header: string): Record<string, string> {\n const result: Record<string, string> = {};\n if (!header) return result;\n for (const pair of header.split(\";\")) {\n const idx = pair.indexOf(\"=\");\n if (idx === -1) continue;\n const name = pair.slice(0, idx).trim();\n const value = pair.slice(idx + 1).trim();\n result[name] = value;\n }\n return result;\n}\n\nfunction serializeCookie(name: string, value: string, options: CookieOptions): string {\n const parts = [`${name}=${value}`];\n if (options.httpOnly) parts.push(\"HttpOnly\");\n if (options.secure) parts.push(\"Secure\");\n if (options.sameSite) parts.push(`SameSite=${options.sameSite}`);\n if (options.maxAge !== undefined) parts.push(`Max-Age=${options.maxAge}`);\n if (options.expires) parts.push(`Expires=${options.expires.toUTCString()}`);\n if (options.path) parts.push(`Path=${options.path}`);\n if (options.domain) parts.push(`Domain=${options.domain}`);\n return parts.join(\"; \");\n}\n\nexport interface RequestContextOptions {\n request: Request;\n config: ResolvedElurConfig;\n routes: RouteTable;\n actions: import(\"../action/scan.js\").ActionRegistry;\n /** Public action names serialized into the HTML shell. */\n publicActions: Record<string, string[]>;\n /** Optional module loader for adapter-bundled entries. */\n importer?: (path: string) => unknown | Promise<unknown>;\n /** Whether the render endpoint (/__elur-js/render) is available. */\n renderEndpoint?: boolean;\n /** Whether to bypass the ISR cache (dev mode). */\n noCache?: boolean;\n /** ISR cache directory (absolute). */\n cacheDir?: string;\n /** Default ISR revalidate interval in seconds. */\n defaultRevalidate?: number;\n /** Route params (populated after route matching). */\n params?: Record<string, string | string[] | undefined>;\n /** Per-request locals (populated by middleware). */\n locals?: Record<string, unknown>;\n /** Abort signal for the request (from host disconnect). */\n signal?: AbortSignal;\n /** Request ID (auto-generated if not provided). */\n requestId?: string;\n /** Platform-specific context (e.g. Vercel, Netlify). */\n platform?: unknown;\n /** Matched route (populated after route matching). */\n route?: import(\"../router/route-scanner.js\").PageRoute | import(\"../router/route-scanner.js\").ApiRoute;\n}\n\nexport class RequestContext {\n readonly request: Request;\n readonly url: URL;\n readonly config: ResolvedElurConfig;\n readonly routes: RouteTable;\n readonly actions: import(\"../action/scan.js\").ActionRegistry;\n readonly publicActions: Record<string, string[]>;\n readonly importer?: (path: string) => unknown | Promise<unknown>;\n readonly renderEndpoint: boolean;\n readonly noCache: boolean;\n readonly cacheDir?: string;\n readonly defaultRevalidate?: number;\n\n // Per-request state (runtime-security §4)\n /** Route params derived from the matched route. */\n params: Readonly<Record<string, string | string[] | undefined>>;\n /** Per-request locals, populated by middleware. Not global. */\n locals: Record<string, unknown>;\n /** Read-only access to request cookies. */\n readonly cookies: CookieJar;\n /** Abort signal (from host disconnect when platform allows). */\n readonly signal: AbortSignal;\n /** Unique request ID for logging/correlation. */\n readonly requestId: string;\n /** Platform-specific context (Vercel, Netlify, etc.). */\n readonly platform: unknown;\n /** Matched route after route matching. */\n route?: import(\"../router/route-scanner.js\").PageRoute | import(\"../router/route-scanner.js\").ApiRoute;\n /** Mutable response state accumulated during the request. */\n readonly response: ResponseState;\n\n constructor(options: RequestContextOptions) {\n this.request = options.request;\n this.url = new URL(options.request.url);\n this.config = options.config;\n this.routes = options.routes;\n this.actions = options.actions;\n this.publicActions = options.publicActions;\n this.importer = options.importer;\n this.renderEndpoint = options.renderEndpoint ?? true;\n this.noCache = options.noCache ?? false;\n this.cacheDir = options.cacheDir;\n this.defaultRevalidate = options.defaultRevalidate;\n\n // Per-request state\n this.params = options.params ?? {};\n this.locals = options.locals ?? {};\n this.cookies = new RequestCookieJar(options.request);\n this.signal = options.signal ?? new AbortController().signal;\n this.requestId = options.requestId ?? randomUUID();\n this.platform = options.platform;\n this.route = options.route;\n this.response = {\n status: undefined,\n headers: new Headers(),\n cookies: new MutableResponseCookieJar(),\n };\n }\n\n /** The pathname without a query string. */\n get pathname(): string {\n return this.url.pathname;\n }\n\n /** The HTTP method, uppercased. */\n get method(): string {\n return (this.request.method ?? \"GET\").toUpperCase();\n }\n\n /** Whether the request accepts JSON. */\n get wantsJson(): boolean {\n return (this.request.headers.get(\"Accept\") ?? \"\").includes(\"application/json\");\n }\n\n /** Search params from the request URL. */\n get searchParams(): URLSearchParams {\n return this.url.searchParams;\n }\n\n /** Render config passed to renderPage/renderErrorPage. */\n get renderConfig(): { lang?: string; clientEntry?: string; renderEndpoint?: boolean } {\n return {\n lang: undefined,\n clientEntry: undefined,\n renderEndpoint: this.renderEndpoint,\n };\n }\n\n /** Applies accumulated response state (headers, cookies, status) to a Response. */\n applyToResponse(response: Response): Response {\n const headers = new Headers(response.headers);\n // Merge accumulated headers\n for (const [key, value] of this.response.headers.entries()) {\n headers.set(key, value);\n }\n // Append Set-Cookie values (multiple allowed)\n for (const cookie of this.response.cookies.getAll()) {\n headers.append(\"Set-Cookie\", cookie);\n }\n const status = this.response.status ?? response.status;\n return new Response(response.body, {\n status,\n statusText: response.statusText,\n headers,\n });\n }\n}\n\n// --- ResponseBuilder: small helpers for consistent Web Responses ---\n\nexport function htmlResponse(body: string, status = 200, headers?: HeadersInit): Response {\n return new Response(body, {\n status,\n headers: { \"Content-Type\": \"text/html; charset=utf-8\", ...headers as Record<string, string> },\n });\n}\n\nexport function jsonResponse(data: unknown, status = 200, headers?: HeadersInit): Response {\n return new Response(JSON.stringify(data), {\n status,\n headers: { \"Content-Type\": \"application/json; charset=utf-8\", ...headers as Record<string, string> },\n });\n}\n\nexport function textResponse(body: string, status = 200, headers?: HeadersInit): Response {\n return new Response(body, {\n status,\n headers: { \"Content-Type\": \"text/plain; charset=utf-8\", ...headers as Record<string, string> },\n });\n}\n\nexport function notFound(body = \"Not Found\"): Response {\n return textResponse(body, 404);\n}\n\nexport function methodNotAllowed(method: string): Response {\n return textResponse(`Method not allowed: ${method}`, 405);\n}\n\nexport function serverError(body: string): Response {\n return textResponse(body, 500);\n}\n\n// --- Content-type guessing (shared by all static-serving paths) ---\n\nexport function guessContentType(filePath: string): string {\n switch (filePath.slice(filePath.lastIndexOf(\".\") + 1).toLowerCase()) {\n case \"html\": return \"text/html; charset=utf-8\";\n case \"js\": return \"application/javascript; charset=utf-8\";\n case \"mjs\": return \"application/javascript; charset=utf-8\";\n case \"css\": return \"text/css; charset=utf-8\";\n case \"json\": return \"application/json; charset=utf-8\";\n case \"svg\": return \"image/svg+xml\";\n case \"png\": return \"image/png\";\n case \"jpg\":\n case \"jpeg\": return \"image/jpeg\";\n case \"webp\": return \"image/webp\";\n case \"avif\": return \"image/avif\";\n case \"ico\": return \"image/x-icon\";\n case \"woff\": return \"font/woff\";\n case \"woff2\": return \"font/woff2\";\n case \"wasm\": return \"application/wasm\";\n case \"txt\": return \"text/plain; charset=utf-8\";\n default: return \"application/octet-stream\";\n }\n}\n\n// --- Static file serving as a Web handler (reuses resolveStaticFile) ---\n\nimport { readFile, stat } from \"node:fs/promises\";\nimport { createHash } from \"node:crypto\";\nimport { resolveStaticFile } from \"./static.js\";\n\n/**\n * Serves a static file from the root directory with full conditional and\n * range support:\n *\n * - ETag / Last-Modified with If-None-Match / If-Modified-Since → 304.\n * - `Range` with `If-Range` (ETag or date) → 206 with `Content-Range`.\n * - HEAD → same headers as GET without a body.\n * - Invalid/unsatisfiable ranges → 416 with a `Content-Range: bytes (asterisk)/size` header.\n *\n * Files with content hashes in their names (e.g. `app-abc123.js`) get\n * `Cache-Control: public, max-age=31536000, immutable`.\n *\n * @param root Static file root (absolute path).\n * @param pathname Request pathname.\n * @param request Optional request for conditional/range/HEAD handling.\n */\nexport async function serveStaticFile(\n root: string,\n pathname: string,\n request?: Request,\n): Promise<Response | null> {\n const filePath = await resolveStaticFile(root, pathname);\n if (!filePath) return null;\n try {\n const [data, stats] = await Promise.all([\n readFile(filePath),\n stat(filePath),\n ]);\n\n const contentType = guessContentType(filePath);\n const etag = `\"${createHash(\"sha1\").update(data).digest(\"hex\").slice(0, 16)}\"`;\n const lastModified = stats.mtime.toUTCString();\n const isHead = request?.method === \"HEAD\";\n const size = data.byteLength;\n\n const baseHeaders: Record<string, string> = {\n \"Content-Type\": contentType,\n \"Content-Length\": String(size),\n ETag: etag,\n \"Last-Modified\": lastModified,\n \"Accept-Ranges\": \"bytes\",\n };\n\n // Determine Cache-Control: hashed assets get immutable, others get a\n // short revalidation window.\n const baseName = filePath.split(\"/\").pop() ?? \"\";\n const isHashed = /[a-f0-9]{8,}\\.(js|css|woff2?|wasm|png|jpg|jpeg|webp|avif|svg)$/i.test(baseName);\n baseHeaders[\"Cache-Control\"] = isHashed\n ? \"public, max-age=31536000, immutable\"\n : \"public, max-age=0, must-revalidate\";\n\n // Conditional requests (If-None-Match takes precedence).\n const ifNoneMatch = request?.headers.get(\"If-None-Match\");\n if (ifNoneMatch && etagListMatches(ifNoneMatch, etag)) {\n return new Response(null, { status: 304, headers: baseHeaders });\n }\n const ifModifiedSince = request?.headers.get(\"If-Modified-Since\");\n if (ifModifiedSince) {\n const since = Date.parse(ifModifiedSince);\n if (!isNaN(since) && Math.floor(stats.mtime.getTime() / 1000) <= Math.floor(since / 1000)) {\n return new Response(null, { status: 304, headers: baseHeaders });\n }\n }\n\n // Range support with If-Range validation.\n const rangeHeader = request?.headers.get(\"Range\");\n const ifRange = request?.headers.get(\"If-Range\");\n if (rangeHeader && (!ifRange || ifRangeMatches(ifRange, etag, stats.mtime))) {\n const range = parseRange(rangeHeader, size);\n if (range === null) {\n return new Response(null, {\n status: 416,\n headers: { ...baseHeaders, \"Content-Range\": `bytes */${size}` },\n });\n }\n if (range) {\n const [start, end] = range;\n const chunk = data.subarray(start, end + 1);\n const headers: Record<string, string> = {\n ...baseHeaders,\n \"Content-Length\": String(chunk.byteLength),\n \"Content-Range\": `bytes ${start}-${end}/${size}`,\n };\n if (isHead) return new Response(null, { status: 206, headers });\n return new Response(chunk, { status: 206, headers });\n }\n }\n\n if (isHead) return new Response(null, { status: 200, headers: baseHeaders });\n return new Response(data, { status: 200, headers: baseHeaders });\n } catch {\n return null;\n }\n}\n\nfunction etagListMatches(ifNoneMatch: string, etag: string): boolean {\n return ifNoneMatch\n .split(\",\")\n .map((value) => value.trim())\n .some((value) => value === \"*\" || value === etag);\n}\n\nfunction ifRangeMatches(ifRange: string, etag: string, mtime: Date): boolean {\n if (ifRange.startsWith('\"') || ifRange.startsWith(\"W/\")) return ifRange === etag;\n const date = Date.parse(ifRange);\n return !isNaN(date) && Math.floor(mtime.getTime() / 1000) <= Math.floor(date / 1000);\n}\n\n/**\n * Parses a single `Range: bytes=...` header. Returns:\n * - `[start, end]` for a satisfiable range.\n * - `null` when the header is malformed or unsatisfiable (→ 416).\n * - `undefined` when the header is valid but the whole resource is requested\n * (e.g. `bytes=0-` for an empty file) — serve the full body.\n */\nfunction parseRange(rangeHeader: string, size: number): [number, number] | null | undefined {\n const match = /^bytes=(\\d*)-(\\d*)$/.exec(rangeHeader.trim());\n if (!match) return null;\n const startText = match[1];\n const endText = match[2];\n\n if (startText === \"\" && endText === \"\") return null;\n if (startText === \"\") {\n // Suffix range: last N bytes.\n const suffix = Number(endText);\n if (!Number.isSafeInteger(suffix) || suffix <= 0) return null;\n const start = Math.max(0, size - suffix);\n if (size === 0) return undefined;\n return [start, size - 1];\n }\n\n const start = Number(startText);\n if (!Number.isSafeInteger(start) || start < 0 || start >= size) return null;\n const end = endText === \"\" ? size - 1 : Number(endText);\n if (!Number.isSafeInteger(end) || end < start) return null;\n return [start, Math.min(end, size - 1)];\n}\n","// --- Security response headers (runtime-security §14) ---\n//\n// Applies configurable security headers to responses. Defaults are safe and\n// compatible: X-Content-Type-Options, Referrer-Policy, frame-ancestors.\n// HSTS is only applied under HTTPS or when explicitly configured.\n// CSP supports a \"nonce\" placeholder replaced per-request.\n// User-set headers on the response are never overwritten without explicit\n// merge rules.\n\nimport type { SecurityHeadersConfig } from \"../config/index.js\";\n\n/** Default security headers applied when `security.headers` is not `false`. */\nexport const DEFAULT_SECURITY_HEADERS: Required<\n Omit<SecurityHeadersConfig, \"contentSecurityPolicy\" | \"hsts\" | \"permissionsPolicy\">\n> = {\n noSniff: true,\n referrerPolicy: \"strict-origin-when-cross-origin\",\n frameAncestors: \"SAMEORIGIN\",\n};\n\n/**\n * Builds the security headers map from the resolved config.\n * Returns an empty map if headers are disabled.\n */\nexport function buildSecurityHeaders(\n config: SecurityHeadersConfig | false,\n isHttps: boolean,\n nonce?: string,\n): Record<string, string> {\n if (config === false) return {};\n\n const headers: Record<string, string> = {};\n const merged = { ...DEFAULT_SECURITY_HEADERS, ...config };\n\n if (merged.noSniff) {\n headers[\"X-Content-Type-Options\"] = \"nosniff\";\n }\n\n if (merged.referrerPolicy) {\n headers[\"Referrer-Policy\"] = merged.referrerPolicy;\n }\n\n // Frame policy: prefer CSP frame-ancestors if CSP is set, otherwise\n // X-Frame-Options for broader compatibility.\n if (merged.contentSecurityPolicy) {\n let csp = merged.contentSecurityPolicy;\n if (nonce) {\n csp = csp.replace(/\\bnonce\\b/g, `'nonce-${nonce}'`);\n }\n headers[\"Content-Security-Policy\"] = csp;\n } else if (merged.frameAncestors) {\n // Without CSP, use X-Frame-Options for frame protection.\n const fa = merged.frameAncestors;\n if (fa === \"NONE\") {\n headers[\"X-Frame-Options\"] = \"DENY\";\n } else if (fa === \"SAMEORIGIN\") {\n headers[\"X-Frame-Options\"] = \"SAMEORIGIN\";\n } else {\n headers[\"X-Frame-Options\"] = fa;\n }\n }\n\n // HSTS: only under HTTPS or when explicitly set as a string.\n if (merged.hsts === true && isHttps) {\n headers[\"Strict-Transport-Security\"] = \"max-age=15552000; includeSubDomains\";\n } else if (typeof merged.hsts === \"string\") {\n headers[\"Strict-Transport-Security\"] = merged.hsts;\n }\n\n if (merged.permissionsPolicy) {\n headers[\"Permissions-Policy\"] = merged.permissionsPolicy;\n }\n\n return headers;\n}\n\n/**\n * Applies security headers to an existing Response, preserving any\n * user-set headers unless overridden by security config.\n */\nexport function applySecurityHeaders(\n response: Response,\n headers: Record<string, string>,\n): Response {\n if (Object.keys(headers).length === 0) return response;\n\n const newHeaders = new Headers(response.headers);\n for (const [key, value] of Object.entries(headers)) {\n // Don't overwrite a header the response already set explicitly.\n if (!newHeaders.has(key)) {\n newHeaders.set(key, value);\n }\n }\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: newHeaders,\n });\n}\n","// --- Structured logger with request ID and Server-Timing (plan §12.3) ---\n//\n// Provides a structured logger that:\n// - Generates a unique request ID per request.\n// - Attaches the request ID to all log entries.\n// - Supports structured fields (not just string messages).\n// - Redacts sensitive data (cookies, auth headers, tokens).\n// - Supports Server-Timing header accumulation.\n//\n// OpenTelemetry/analytics are external integrations — no automatic telemetry.\n\nimport { randomUUID } from \"node:crypto\";\n\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nconst LEVEL_PRIORITY: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n};\n\nconst SENSITIVE_HEADERS = new Set([\n \"cookie\",\n \"authorization\",\n \"set-cookie\",\n \"x-api-key\",\n \"x-auth-token\",\n]);\n\nexport interface LogEntry {\n level: LogLevel;\n message: string;\n requestId?: string;\n timestamp: string;\n fields?: Record<string, unknown>;\n}\n\nexport interface ServerTimingMetric {\n name: string;\n description?: string;\n durationMs: number;\n}\n\nexport class StructuredLogger {\n private minLevel: LogLevel;\n private requestId: string;\n private timings: ServerTimingMetric[] = [];\n private entries: LogEntry[] = [];\n\n constructor(options: { minLevel?: LogLevel; requestId?: string } = {}) {\n this.minLevel = options.minLevel ?? (process.env.NODE_ENV === \"production\" ? \"info\" : \"debug\");\n this.requestId = options.requestId ?? randomUUID();\n }\n\n /** Returns the request ID for this logger instance. */\n getRequestId(): string {\n return this.requestId;\n }\n\n /** Logs a debug message. */\n debug(message: string, fields?: Record<string, unknown>): void {\n this.log(\"debug\", message, fields);\n }\n\n /** Logs an info message. */\n info(message: string, fields?: Record<string, unknown>): void {\n this.log(\"info\", message, fields);\n }\n\n /** Logs a warning. */\n warn(message: string, fields?: Record<string, unknown>): void {\n this.log(\"warn\", message, fields);\n }\n\n /** Logs an error. */\n error(message: string, fields?: Record<string, unknown>): void {\n this.log(\"error\", message, fields);\n }\n\n /** Records a Server-Timing metric. */\n timing(name: string, durationMs: number, description?: string): void {\n this.timings.push({ name, durationMs, description });\n }\n\n /** Starts a timer and returns a function to stop it and record the timing. */\n startTimer(name: string, description?: string): () => void {\n const start = performance.now();\n return () => {\n this.timing(name, performance.now() - start, description);\n };\n }\n\n /** Returns the Server-Timing header value. */\n getServerTimingHeader(): string {\n return this.timings\n .map((t) => {\n const desc = t.description ? `;desc=\"${t.description}\"` : \"\";\n return `${t.name};dur=${t.durationMs.toFixed(1)}${desc}`;\n })\n .join(\", \");\n }\n\n /** Returns all log entries collected so far. */\n getEntries(): readonly LogEntry[] {\n return this.entries;\n }\n\n private log(level: LogLevel, message: string, fields?: Record<string, unknown>): void {\n if (LEVEL_PRIORITY[level] < LEVEL_PRIORITY[this.minLevel]) return;\n\n const entry: LogEntry = {\n level,\n message,\n requestId: this.requestId,\n timestamp: new Date().toISOString(),\n fields: fields ? redactSensitive(fields) : undefined,\n };\n\n this.entries.push(entry);\n\n // Output to console in development, structured JSON in production.\n if (process.env.NODE_ENV === \"production\") {\n const output = JSON.stringify(entry);\n if (level === \"error\") console.error(output);\n else if (level === \"warn\") console.warn(output);\n else console.log(output);\n } else {\n const prefix = `[${level.toUpperCase()}]`;\n const fieldsStr = entry.fields ? \" \" + JSON.stringify(entry.fields) : \"\";\n const output = `${prefix} ${message}${fieldsStr}`;\n if (level === \"error\") console.error(output);\n else if (level === \"warn\") console.warn(output);\n else console.log(output);\n }\n }\n}\n\n/**\n * Redacts sensitive fields from a log fields object.\n * Recursively redacts keys that match sensitive header names.\n */\nfunction redactSensitive(fields: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(fields)) {\n const lowerKey = key.toLowerCase();\n if (SENSITIVE_HEADERS.has(lowerKey)) {\n result[key] = \"[REDACTED]\";\n } else if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n result[key] = redactSensitive(value as Record<string, unknown>);\n } else {\n result[key] = value;\n }\n }\n return result;\n}\n\n/**\n * Creates a logger for a request, optionally using the request's\n * X-Request-ID header if present.\n */\nexport function createRequestLogger(request?: Request, minLevel?: LogLevel): StructuredLogger {\n const requestId = request?.headers.get(\"X-Request-ID\") ?? undefined;\n return new StructuredLogger({ minLevel, requestId });\n}\n","// --- Redirects, rewrites, and route headers (plan §11.1, §10) ---\n//\n// Authors can declare redirects and rewrites in their config:\n//\n// export default defineConfig({\n// redirects: [\n// { from: \"/old-blog/:slug\", to: \"/blog/:slug\", status: 301 },\n// ],\n// rewrites: [\n// { from: \"/api/legacy/*\", to: \"/api/v2/*\" },\n// ],\n// headers: [\n// { path: \"/admin/*\", headers: { \"X-Robots-Tag\": \"noindex\" } },\n// ],\n// });\n//\n// Redirects return a Response with the appropriate status and Location.\n// Rewrites change the pathname before routing (transparent to the user).\n// Route headers are applied to the response for matching paths.\n\nexport interface RedirectRule {\n /** Source path pattern (supports :param and *). */\n from: string;\n /** Destination path (supports :param interpolation). */\n to: string;\n /** HTTP status code (301, 302, 307, 308). Default: 308. */\n status?: 301 | 302 | 307 | 308;\n}\n\nexport interface RewriteRule {\n /** Source path pattern (supports :param and *). */\n from: string;\n /** Destination path (supports :param interpolation). */\n to: string;\n}\n\nexport interface RouteHeadersRule {\n /** Path pattern to match (supports :param and *). */\n path: string;\n /** Headers to apply to matching responses. */\n headers: Record<string, string>;\n}\n\n/**\n * Checks if a pathname matches a redirect rule and returns the redirect\n * Response if so.\n */\nexport function matchRedirect(\n pathname: string,\n rules: RedirectRule[],\n): Response | undefined {\n for (const rule of rules) {\n const params = matchPattern(pathname, rule.from);\n if (params) {\n const location = interpolatePath(rule.to, params);\n const status = rule.status ?? 308;\n return new Response(null, {\n status,\n headers: { Location: location },\n });\n }\n }\n return undefined;\n}\n\n/**\n * Checks if a pathname matches a rewrite rule and returns the rewritten\n * pathname if so.\n */\nexport function matchRewrite(\n pathname: string,\n rules: RewriteRule[],\n): string | undefined {\n for (const rule of rules) {\n const params = matchPattern(pathname, rule.from);\n if (params) {\n return interpolatePath(rule.to, params);\n }\n }\n return undefined;\n}\n\n/**\n * Returns headers that should be applied to a response for the given pathname.\n */\nexport function matchRouteHeaders(\n pathname: string,\n rules: RouteHeadersRule[],\n): Record<string, string> | undefined {\n for (const rule of rules) {\n if (matchPattern(pathname, rule.path)) {\n return rule.headers;\n }\n }\n return undefined;\n}\n\n/**\n * Matches a pathname against a pattern with :param and * wildcards.\n * Returns the extracted params, or undefined if no match.\n */\nfunction matchPattern(pathname: string, pattern: string): Record<string, string> | undefined {\n const cleanPath = pathname.split(\"?\")[0];\n const requestSegments = cleanPath.split(\"/\").filter(Boolean);\n const patternSegments = pattern.split(\"/\").filter(Boolean);\n const params: Record<string, string> = {};\n\n let i = 0;\n for (let r = 0; r < patternSegments.length; r++) {\n const seg = patternSegments[r];\n\n if (seg === \"*\") {\n // Wildcard matches everything remaining.\n return params;\n }\n\n if (seg.endsWith(\"*\")) {\n // Catch-all: :name* matches the rest as a single string.\n const name = seg.slice(1, -1);\n const rest = requestSegments.slice(i).join(\"/\");\n params[name] = rest;\n return params;\n }\n\n if (seg.startsWith(\":\")) {\n const name = seg.slice(1);\n if (requestSegments[i] === undefined) return undefined;\n params[name] = requestSegments[i];\n i++;\n continue;\n }\n\n if (seg !== requestSegments[i]) return undefined;\n i++;\n }\n\n if (i !== requestSegments.length) return undefined;\n return params;\n}\n\n/**\n * Interpolates :param placeholders in a path with actual values.\n */\nfunction interpolatePath(template: string, params: Record<string, string>): string {\n return template.replace(/:(\\w+)\\*?/g, (_match, name: string) => {\n return params[name] ?? \"\";\n });\n}\n","import { matchRoute, matchApiRoute } from \"../ssr/match.js\";\nimport { handleActionRequest, type ActionResolver } from \"../action/server.js\";\nimport { renderPage, renderErrorPage } from \"../ssr/render.js\";\nimport { renderPageBody, RouteNotFoundError } from \"../ssr/stream.js\";\nimport { actionNames } from \"../action/scan.js\";\nimport { serveStaticFile, htmlResponse, jsonResponse, notFound, methodNotAllowed } from \"./context.js\";\nimport { publicErrorResponse } from \"../errors.js\";\nimport { cacheKey, createFsCacheAdapter, type CacheAdapter } from \"../cache/adapter.js\";\nimport { connectCacheAdapter } from \"../cache/invalidation.js\";\nimport { shouldCachePublic, type CachePolicy } from \"../cache/policy.js\";\nimport { buildSecurityHeaders, applySecurityHeaders } from \"./security-headers.js\";\nimport { createRequestLogger, type LogLevel, type StructuredLogger } from \"./logger.js\";\nimport { matchRedirect, matchRewrite, matchRouteHeaders, type RedirectRule, type RewriteRule, type RouteHeadersRule } from \"../router/redirects.js\";\nimport { matchesMiddleware, runMiddleware, type LoadedMiddleware } from \"../middleware/index.js\";\nimport { createStreamingResponse } from \"../ssr/stream-response.js\";\nimport { supportsStreaming, DEFAULT_CAPABILITIES, type AdapterCapabilities } from \"./capabilities.js\";\nimport type { SecurityHeadersConfig } from \"../config/index.js\";\n\n// --- Unified Web handler ---\n//\n// A single function that turns a Web Request into a Web Response. Every\n// runtime entry point (Node CLI, Bun adapter, Vercel, Netlify, Vite dev)\n// eventually calls this handler so behavior is identical across platforms.\n//\n// Responsibilities (in order):\n// 0. Redirects and rewrites declared in the config.\n// 1. Server actions endpoint (/__elur-js/actions).\n// 2. SPA render endpoint (/__elur-js/render).\n// 3. API routes.\n// 4. Static files from the output directory.\n// 5. Dynamic SSR rendering for unmatched paths.\n// 6. 404 / 500 error pages.\n//\n// The handler is pure: it does not import Node HTTP types and can be used in\n// Bun, Deno, Cloudflare Workers, Vercel Edge, etc.\n\nexport interface WebHandlerOptions {\n /** Static file root (absolute path). Usually the build output directory. */\n staticRoot: string;\n /** Whether to bypass the ISR cache (dev mode). */\n noCache?: boolean;\n /** ISR cache directory (absolute). */\n cacheDir?: string;\n /** Default ISR revalidate interval in seconds. */\n defaultRevalidate?: number;\n /** Optional module loader for adapter-bundled entries. */\n importer?: (path: string) => Promise<unknown>;\n /** HTML lang attribute. */\n lang?: string;\n /** Client entry path. */\n clientEntry?: string;\n /** Whether the render endpoint exists. */\n renderEndpoint?: boolean;\n /** Security headers config (runtime-security §14). `false` disables. */\n securityHeaders?: SecurityHeadersConfig | false;\n /** Minimum log level for the per-request structured logger. */\n logLevel?: LogLevel;\n /**\n * Pluggable ISR cache adapter. When omitted and `cacheDir` is set, a\n * filesystem adapter is created and shared per `cacheDir` for the process.\n */\n cacheAdapter?: CacheAdapter;\n /** Redirect rules evaluated before any routing (first match wins). */\n redirects?: RedirectRule[];\n /** Rewrite rules: transparently change the pathname used for routing. */\n rewrites?: RewriteRule[];\n /** Extra response headers applied to matching request paths. */\n routeHeaders?: RouteHeadersRule[];\n /**\n * Opt-in streaming SSR (experimental). When `true`, dynamic routes with a\n * `loading` boundary are served as a real stream: the document shell plus\n * the loading fallback go out immediately, and the resolved content arrives\n * as a follow-up chunk that swaps the boundary in-place. Streamed responses\n * bypass the ISR cache (they always render live) and send\n * `Cache-Control: no-store` + `X-Accel-Buffering: no`. Routes without a\n * loading boundary render buffered exactly as before.\n */\n streaming?: boolean;\n /**\n * Host capabilities used to gate streaming. Defaults to\n * `DEFAULT_CAPABILITIES` (a full Node/Bun process). Adapters for hosts\n * without streaming support should pass their own capabilities so\n * `streaming: true` degrades to buffered rendering instead of breaking.\n */\n capabilities?: AdapterCapabilities;\n /**\n * User middleware (the project's `src/middleware.ts`, loaded by the caller\n * with `loadMiddleware`). Runs after redirects/rewrites and the internal\n * endpoints, before API/static/SSR routing. A returned Response\n * short-circuits the pipeline; `next({ headers, locals })` merges headers\n * into the downstream request and exposes `locals` to API routes.\n */\n middleware?: LoadedMiddleware;\n /**\n * Client router options affecting SSR output: `enabled` controls the\n * render-endpoint marker and whether a page without islands ships any JS;\n * `entry` is the public URL of the split router chunk (e.g.\n * `/_elur/router.js`) when the client bundle was built with separate\n * entry/router inputs.\n */\n router?: { enabled?: boolean; entry?: string };\n /**\n * Client JS mode. `\"legacy\"` restores the pre-0%-JS behavior: the combined\n * client entry is emitted unconditionally on every page.\n */\n js?: \"modern\" | \"legacy\";\n}\n\nexport interface WebHandlerRouteTable {\n pages: import(\"../router/route-scanner.js\").PageRoute[];\n api: import(\"../router/route-scanner.js\").ApiRoute[];\n error404?: import(\"../router/route-scanner.js\").PageRoute;\n error500?: import(\"../router/route-scanner.js\").PageRoute;\n}\n\nexport interface WebHandlerActionRegistry {\n [pagePath: string]: Record<string, string>;\n}\n\nexport interface CreateWebHandlerResult {\n (request: Request): Promise<Response>;\n}\n\n/**\n * Create a unified Web handler from scanned routes, actions and options.\n *\n * The returned function is the single entry point for all runtimes.\n */\nexport function createWebHandler(\n routes: WebHandlerRouteTable,\n actions: WebHandlerActionRegistry,\n options: WebHandlerOptions,\n): CreateWebHandlerResult {\n const publicActions = actionNames(actions);\n const lang = options.lang ?? \"es\";\n const clientEntry = options.clientEntry;\n const renderEndpoint = options.renderEndpoint ?? true;\n const noCache = options.noCache ?? false;\n const defaultRevalidate = options.defaultRevalidate;\n\n const renderConfig = {\n lang,\n clientEntry,\n renderEndpoint,\n router: options.router\n ? { enabled: options.router.enabled !== false, entry: options.router.entry }\n : undefined,\n js: options.js,\n };\n const securityHeadersConfig = options.securityHeaders ?? {};\n const redirectRules = options.redirects ?? [];\n const rewriteRules = options.rewrites ?? [];\n const routeHeaderRules = options.routeHeaders ?? [];\n const capabilities = options.capabilities ?? DEFAULT_CAPABILITIES;\n // Streaming is opt-in AND requires a host that can flush chunks as they are\n // produced; when either is missing every route renders buffered.\n const streamingEnabled = options.streaming === true && supportsStreaming(capabilities);\n const cacheAdapter = resolveCacheAdapter(options);\n if (cacheAdapter && !invalidatorConnectedAdapters.has(cacheAdapter)) {\n invalidatorConnectedAdapters.add(cacheAdapter);\n // The subscription lives for the process lifetime: defaultInvalidator is\n // a module-level singleton and dev/preview recreate the handler per\n // request, so connecting per call would leak listeners.\n connectCacheAdapter(cacheAdapter);\n }\n\n function createActionResolver(): ActionResolver {\n return async (name: string, page?: string) => {\n const pageKey = page\n ? routes.pages.some((route) => route.path === page)\n ? page\n : (matchRoute(page, routes.pages)?.route.path ?? page)\n : undefined;\n const pageActions = pageKey ? actions[pageKey] : Object.values(actions).find((p) => p[name]) ?? undefined;\n const actionPath = pageActions ? pageActions[name] : undefined;\n if (!actionPath) return undefined;\n if (options.importer) {\n const mod = (await options.importer(actionPath)) as Record<string, unknown>;\n const action = mod[name];\n if (typeof action === \"function\") return action as (...args: unknown[]) => unknown;\n return undefined;\n }\n const mod = (await import(actionPath)) as Record<string, unknown>;\n const action = mod[name];\n if (typeof action === \"function\") return action as (...args: unknown[]) => unknown;\n return undefined;\n };\n }\n\n const actionResolver = createActionResolver();\n\n async function handleActions(request: Request, logger: StructuredLogger): Promise<Response> {\n const stopTimer = logger.startTimer(\"action\", \"Server action\");\n try {\n return await handleActionRequest(request, actionResolver);\n } catch (err) {\n logger.error(\"[elur-kit] action error\", {\n path: new URL(request.url).pathname,\n method: request.method,\n error: errorMessage(err),\n stack: errorStack(err),\n });\n return publicErrorResponse(err, { includeDetail: noCache });\n } finally {\n stopTimer();\n }\n }\n\n async function handleRenderEndpoint(request: Request, url: URL, logger: StructuredLogger): Promise<Response> {\n const page = url.searchParams.get(\"page\") ?? \"/\";\n const search = url.searchParams.get(\"search\") ?? \"\";\n const wantsJson = (request.headers.get(\"Accept\") ?? \"\").includes(\"application/json\");\n const stopTimer = logger.startTimer(\"render-endpoint\", \"SPA render endpoint\");\n try {\n const result = await renderPageBody({\n routes,\n pathname: page,\n searchParams: new URLSearchParams(search),\n config: renderConfig,\n actions: publicActions,\n request,\n importer: options.importer,\n });\n // A thrown Response from a loader is a first-class response (A-22).\n if (result.response) return result.response;\n const { body, title, head, clearActionErrorCookie, data, actions } = result;\n if (wantsJson) {\n // The full SPA payload: head keeps OG/Twitter metadata fresh on\n // navigation, data/actions keep the inert JSON scripts in sync, and\n // the clear-cookie is also relayed as a header for parity with dev.\n const headers: Record<string, string> = {};\n if (clearActionErrorCookie) {\n headers[\"X-Elur-Action-Clear-Cookie\"] = clearActionErrorCookie;\n }\n // `?? null` keeps every key present in the wire shape — JSON.stringify\n // drops undefined values and the parity contract expects a stable\n // payload across runtimes.\n return jsonResponse(\n {\n title,\n body,\n head: head ?? null,\n data: data ?? null,\n actions: actions ?? null,\n clearActionErrorCookie: clearActionErrorCookie ?? null,\n },\n 200,\n headers,\n );\n }\n return htmlResponse(\n body,\n 200,\n clearActionErrorCookie ? { \"Set-Cookie\": clearActionErrorCookie } : undefined,\n );\n } catch (err) {\n if (err instanceof RouteNotFoundError) return notFound(\"Not Found\");\n // A thrown Response from a loader is a first-class response (A-22).\n if (err instanceof Response) return err;\n logger.error(\"[elur-kit] render endpoint error\", {\n path: url.pathname,\n page,\n error: errorMessage(err),\n stack: errorStack(err),\n });\n return publicErrorResponse(err, { includeDetail: noCache });\n } finally {\n stopTimer();\n }\n }\n\n async function handleApiRoute(\n request: Request,\n pathname: string,\n logger: StructuredLogger,\n middlewareLocals?: Record<string, unknown>,\n ): Promise<Response | null> {\n const apiMatch = matchApiRoute(pathname, routes.api);\n if (!apiMatch) return null;\n const stopTimer = logger.startTimer(\"api\", \"API route\");\n try {\n let mod: Record<string, unknown>;\n if (options.importer) {\n mod = (await options.importer(apiMatch.route.routePath as unknown as string)) as Record<string, unknown>;\n } else {\n mod = (await import(apiMatch.route.routePath)) as Record<string, unknown>;\n }\n const handler = mod[request.method ?? \"GET\"];\n if (typeof handler !== \"function\") return methodNotAllowed(request.method ?? \"GET\");\n // Pass params and a writable locals object to the API handler\n // (runtime-security §4: params derived from the effective route).\n // `locals` carries values published by the user middleware via next().\n const ctx = { params: apiMatch.params, locals: middlewareLocals ?? {} as Record<string, unknown> };\n const response = (await (handler as (req: Request, ctx?: { params: Record<string, string | string[]>; locals: Record<string, unknown> }) => unknown)(request, ctx)) as Response;\n return response;\n } catch (err) {\n logger.error(\"[elur-kit] API route error\", {\n path: pathname,\n method: request.method,\n route: apiMatch.route.path,\n error: errorMessage(err),\n stack: errorStack(err),\n });\n return publicErrorResponse(err, { includeDetail: noCache });\n } finally {\n stopTimer();\n }\n }\n\n async function handleStatic(pathname: string, request: Request): Promise<Response | null> {\n const response = await serveStaticFile(options.staticRoot, pathname, request);\n if (response && noCache) {\n const ct = response.headers.get(\"Content-Type\") ?? \"\";\n if (ct.includes(\"text/html\")) {\n // Dev mode: strip the render-endpoint marker so the client router uses\n // the live /__elur-js/render endpoint for fast SPA navigation.\n const stripped = (await response.text())\n .replace('<meta name=\"elur:render-endpoint\" content=\"off\" />', \"\");\n return new Response(stripped, {\n status: response.status,\n headers: { \"Content-Type\": ct, \"Cache-Control\": \"no-store, must-revalidate\" },\n });\n }\n return new Response(response.body, {\n status: response.status,\n headers: { ...Object.fromEntries(response.headers.entries()), \"Cache-Control\": \"no-store, must-revalidate\" },\n });\n }\n if (response && renderEndpoint) {\n const ct = response.headers.get(\"Content-Type\") ?? \"\";\n if (ct.includes(\"text/html\")) {\n const headers = Object.fromEntries(response.headers.entries());\n delete headers[\"content-length\"];\n const body = await response.text();\n if (body.includes('elur:render-endpoint\" content=\"off\"')) {\n // The SSG build baked `render-endpoint content=\"off\"` so static\n // deployments never probe the endpoint. This server exposes\n // /__elur-js/render, so advertise it: SPA navigations fetch live\n // server-rendered content instead of the stale static file.\n const rewritten = body.replace(\n '<meta name=\"elur:render-endpoint\" content=\"off\" />',\n '<meta name=\"elur:render-endpoint\" content=\"on\" />',\n );\n return new Response(rewritten, { status: response.status, headers });\n }\n return new Response(body, { status: response.status, headers });\n }\n }\n return response;\n }\n\n async function handleDynamicRender(request: Request, pathname: string, logger: StructuredLogger): Promise<Response> {\n const match = matchRoute(pathname, routes.pages);\n if (!match) {\n const errorResult = await renderErrorPage({\n routes,\n status: 404,\n config: renderConfig,\n actions: publicActions,\n importer: options.importer,\n });\n if (errorResult) return htmlResponse(errorResult.html, errorResult.status);\n return notFound(`Not found: ${pathname}`);\n }\n\n // Streaming SSR (opt-in): routes with a loading boundary are served as a\n // real stream — shell + fallback first, resolved content as a later chunk.\n // Streamed responses bypass the ISR cache entirely (a half-sent stream is\n // not cacheable; these pages render live on every request), so the cache\n // gates below only apply to the buffered path.\n if (streamingEnabled && match.route.loadingPath) {\n // The timer measures time-to-shell: the Response is returned once the\n // shell is ready while the background render continues streaming.\n const stopStreamTimer = logger.startTimer(\"ssr\", \"SSR stream shell\");\n try {\n return await createStreamingResponse({\n route: match.route,\n params: match.params,\n searchParams: new URLSearchParams(request.url.split(\"?\")[1] ?? \"\"),\n config: renderConfig,\n actions: publicActions,\n importer: options.importer,\n request,\n signal: request.signal,\n });\n } catch (err) {\n // A thrown Response from a loader is a first-class response (A-22).\n if (err instanceof Response) return err;\n logger.error(\"[elur-kit] SSR stream error\", {\n path: pathname,\n route: match.route.path,\n error: errorMessage(err),\n stack: errorStack(err),\n });\n const errorResult = await renderErrorPage({\n routes,\n status: 500,\n error: err,\n config: renderConfig,\n actions: publicActions,\n importer: options.importer,\n }).catch(() => undefined);\n if (errorResult) return htmlResponse(errorResult.html, errorResult.status);\n return publicErrorResponse(err, { includeDetail: noCache });\n } finally {\n stopStreamTimer();\n }\n }\n\n // ISR cache (only when caching is enabled and the request is cacheable —\n // no cookies, no authorization header). Pages are stored in the cache\n // adapter under cacheKey(pathname); the same key scheme is used by\n // path-based invalidation (connectCacheAdapter).\n const cacheable = !noCache && cacheAdapter && isCacheable(request);\n const pageCacheKey = cacheable ? cacheKey(pathname) : undefined;\n\n const renderAndStore = async (): Promise<Response> => {\n const result = await renderPage({\n route: match.route,\n params: match.params,\n searchParams: new URLSearchParams(request.url.split(\"?\")[1] ?? \"\"),\n config: renderConfig,\n actions: publicActions,\n request,\n importer: options.importer,\n });\n\n // If a loader threw a Response (redirect, 404, etc.), return it\n // as a first-class response (A-22).\n if (result.response) {\n return result.response;\n }\n\n if (cacheable && cacheAdapter && pageCacheKey && isResultCacheable(result, request)) {\n const revalidateSeconds = result.revalidate ?? defaultRevalidate ?? 0;\n if (revalidateSeconds > 0) {\n await cacheAdapter.set(\n pageCacheKey,\n { html: result.html, generatedAt: Date.now(), revalidate: revalidateSeconds },\n { revalidate: revalidateSeconds, tags: result.cachePolicy?.tags },\n );\n }\n }\n\n return htmlResponse(result.html);\n };\n\n if (cacheable && cacheAdapter && pageCacheKey) {\n const cached = await cacheAdapter.get(pageCacheKey);\n if (cached) {\n if (Date.now() - cached.generatedAt >= cached.revalidate * 1000) {\n // Stale-while-revalidate: serve the stale entry immediately and\n // refresh it in the background.\n renderAndStore().catch((err) => {\n logger.error(\"[elur-kit] background cache revalidation failed\", {\n path: pathname,\n error: errorMessage(err),\n stack: errorStack(err),\n });\n });\n }\n return htmlResponse(cached.html);\n }\n }\n\n const stopTimer = logger.startTimer(\"ssr\", \"SSR render\");\n try {\n return await renderAndStore();\n } catch (err) {\n // A thrown Response from a loader is a first-class response (A-22).\n if (err instanceof Response) return err;\n logger.error(\"[elur-kit] SSR render error\", {\n path: pathname,\n route: match.route.path,\n error: errorMessage(err),\n stack: errorStack(err),\n });\n const errorResult = await renderErrorPage({\n routes,\n status: 500,\n error: err,\n config: renderConfig,\n actions: publicActions,\n importer: options.importer,\n }).catch(() => undefined);\n if (errorResult) return htmlResponse(errorResult.html, errorResult.status);\n return publicErrorResponse(err, { includeDetail: noCache });\n } finally {\n stopTimer();\n }\n }\n\n // Applies security headers plus the per-request observability headers\n // (Server-Timing when there are metrics, X-Request-ID always) and any\n // configured route headers. Route headers may override security headers;\n // the observability headers are applied last so they always win.\n function finalizeResponse(\n response: Response,\n logger: StructuredLogger,\n secHeaders: Record<string, string>,\n routeHeaders?: Record<string, string>,\n ): Response {\n const secured = applySecurityHeaders(response, secHeaders);\n const headers = new Headers(secured.headers);\n if (routeHeaders) {\n for (const [key, value] of Object.entries(routeHeaders)) {\n headers.set(key, value);\n }\n }\n const timing = logger.getServerTimingHeader();\n if (timing) headers.set(\"Server-Timing\", timing);\n headers.set(\"X-Request-ID\", logger.getRequestId());\n return new Response(secured.body, {\n status: secured.status,\n statusText: secured.statusText,\n headers,\n });\n }\n\n return async function handler(request: Request): Promise<Response> {\n const logger = createRequestLogger(request, options.logLevel);\n const url = new URL(request.url);\n const originalPathname = url.pathname;\n const isHttps = url.protocol === \"https:\";\n\n // Determine security headers (rebuild if nonce is needed).\n // HSTS is only applied under HTTPS; other headers apply always.\n const secHeaders = securityHeadersConfig === false\n ? {}\n : buildSecurityHeaders(securityHeadersConfig, isHttps);\n\n // 0. Redirects, evaluated before any routing.\n if (redirectRules.length > 0) {\n const redirect = matchRedirect(originalPathname, redirectRules);\n if (redirect) {\n return finalizeResponse(redirect, logger, secHeaders, matchRouteHeaders(originalPathname, routeHeaderRules));\n }\n }\n\n // Rewrites change the pathname transparently: everything below (API\n // routes, static files, dynamic SSR and its ISR cache key) routes on the\n // rewritten path, while route headers keep matching the original URL the\n // user configured them for.\n let pathname = originalPathname;\n if (rewriteRules.length > 0) {\n pathname = matchRewrite(originalPathname, rewriteRules) ?? originalPathname;\n }\n const routeHeaders = matchRouteHeaders(originalPathname, routeHeaderRules);\n\n // 1. Server actions endpoint.\n if (pathname === \"/__elur-js/actions\" && request.method === \"POST\") {\n const response = await handleActions(request, logger);\n return finalizeResponse(response, logger, secHeaders, routeHeaders);\n }\n\n // 2. SPA render endpoint.\n if (pathname === \"/__elur-js/render\" && renderEndpoint) {\n const response = await handleRenderEndpoint(request, url, logger);\n return finalizeResponse(response, logger, secHeaders, routeHeaders);\n }\n\n // User middleware (src/middleware.ts) runs after redirects/rewrites and\n // the internal endpoints, before routing — same semantics as the legacy\n // createSsrServer pipeline. A returned Response short-circuits (through\n // finalizeResponse so security/observability headers still apply);\n // next({ headers }) merges into the downstream request and\n // next({ locals }) is exposed to API routes.\n let middlewareLocals: Record<string, unknown> | undefined;\n const middleware = options.middleware;\n if (middleware && matchesMiddleware(pathname, middleware.config)) {\n let mwResult;\n try {\n mwResult = await runMiddleware(middleware, request);\n } catch (err) {\n logger.error(\"[elur-kit] middleware error\", {\n path: pathname,\n error: errorMessage(err),\n stack: errorStack(err),\n });\n return finalizeResponse(\n publicErrorResponse(err, { includeDetail: noCache }),\n logger,\n secHeaders,\n routeHeaders,\n );\n }\n if (mwResult.kind === \"response\") {\n return finalizeResponse(mwResult.response, logger, secHeaders, routeHeaders);\n }\n middlewareLocals = mwResult.locals;\n if (mwResult.headers) {\n const merged = new Headers(request.headers);\n for (const [key, value] of Object.entries(mwResult.headers)) {\n merged.set(key, value);\n }\n request = new Request(request, { headers: merged });\n }\n }\n\n // 3. API routes.\n const apiResponse = await handleApiRoute(request, pathname, logger, middlewareLocals);\n if (apiResponse) return finalizeResponse(apiResponse, logger, secHeaders, routeHeaders);\n\n // 4. Static files.\n const staticResponse = await handleStatic(pathname, request);\n if (staticResponse) return finalizeResponse(staticResponse, logger, secHeaders, routeHeaders);\n\n // 5. Dynamic SSR rendering.\n const dynamicResponse = await handleDynamicRender(request, pathname, logger);\n return finalizeResponse(dynamicResponse, logger, secHeaders, routeHeaders);\n };\n}\n\n// Default filesystem adapters are shared per cacheDir and connected to the\n// invalidator once; both live for the process lifetime (dev/preview recreate\n// the handler per request, so per-call adapters would leak listeners).\nconst defaultCacheAdapters = new Map<string, CacheAdapter>();\nconst invalidatorConnectedAdapters = new WeakSet<CacheAdapter>();\n\nfunction resolveCacheAdapter(options: WebHandlerOptions): CacheAdapter | undefined {\n if (options.cacheAdapter) return options.cacheAdapter;\n if (!options.cacheDir) return undefined;\n let adapter = defaultCacheAdapters.get(options.cacheDir);\n if (!adapter) {\n adapter = createFsCacheAdapter({ cacheDir: options.cacheDir });\n defaultCacheAdapters.set(options.cacheDir, adapter);\n }\n return adapter;\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nfunction errorStack(err: unknown): string | undefined {\n return err instanceof Error ? err.stack : undefined;\n}\n\nfunction isCacheable(request: Request): boolean {\n if (request.method !== \"GET\" && request.method !== \"HEAD\") return false;\n if (request.headers.get(\"Cookie\")) return false;\n if (request.headers.get(\"Authorization\")) return false;\n return true;\n}\n\n/**\n * Checks whether a rendered page result is cacheable as public ISR.\n * Per runtime-security §9.1: uses the route's cache policy and checks\n * for personalized content markers.\n */\nfunction isResultCacheable(\n result: { revalidate?: number; html: string; cachePolicy?: CachePolicy },\n request: Request,\n): boolean {\n // If the HTML contains action error markers, it's personalized.\n if (result.html.includes(\"__elur_js_action_error\")) return false;\n // Use the route's cache policy if declared.\n if (result.cachePolicy) {\n return shouldCachePublic(result.cachePolicy, request);\n }\n // Fallback: cacheable only if revalidate > 0 and request is clean.\n if (!result.revalidate || result.revalidate <= 0) return false;\n if (request.headers.get(\"Cookie\")) return false;\n if (request.headers.get(\"Authorization\")) return false;\n return true;\n}\n"],"mappings":"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"}