@elurjs/kit 2.4.5 → 2.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +9 -3
- package/dist/lib/cli.cjs.map +1 -1
- package/dist/lib/cli.js.map +1 -1
- package/dist/lib/index.cjs +1 -1
- package/dist/lib/node-http-DRAUhO0c.js.map +1 -1
- package/dist/lib/node-http-DhxguYyz.cjs.map +1 -1
- package/package.json +5 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-http-DhxguYyz.cjs","names":[],"sources":["../../src/render/ssr-flag.ts","../../src/render/render-to-string.ts","../../src/build/document-shell.ts","../../src/action/error-store.ts","../../src/cache/policy.ts","../../src/ssr/render.ts","../../src/ssr/match.ts","../../src/action/origin.ts","../../src/action/server.ts","../../src/runtime/node-http.ts"],"sourcesContent":["// --- SSR flag utility ---\n//\n// Elur 2.6.0 (published on npm) does not export `_setSSR`/`_isSSR`. The\n// reactivity state lives on `globalThis[Symbol.for(\"@elurjs/core/reactivity-state\")]`\n// and exposes an `ssr` boolean that, when true, makes effects run a single\n// pass without subscribing — exactly what we need during server rendering.\n//\n// This module manipulates that flag directly so the kit does not depend on\n// private exports that may or may not be present in a given elur release.\n\nconst STATE_KEY = Symbol.for(\"@elurjs/core/reactivity-state\");\n\ntype ReactivityState = { ssr: boolean };\n\nfunction getState(): ReactivityState | undefined {\n return (globalThis as Record<symbol, unknown>)[STATE_KEY] as\n | ReactivityState\n | undefined;\n}\n\n/** Sets the SSR flag on the Elur reactivity state. No-op if state is absent. */\nexport function setSSR(value: boolean): void {\n const state = getState();\n if (state) state.ssr = value;\n}\n\n/** Reads the SSR flag from the Elur reactivity state. Defaults to false. */\nexport function isSSR(): boolean {\n return getState()?.ssr ?? false;\n}\n","import type { ElurTemplate } from \"@elurjs/core\";\nimport { renderToString as renderCoreTemplate } from \"@elurjs/core/server\";\nimport { setSSR } from \"./ssr-flag\";\n\n// --- Build-time / server rendering ---\n//\n// The Elur core ships a DOM-free `renderToString` (`@elurjs/core/server`)\n// that streams template output without ever touching a `document`. The kit used\n// to inject a Node-side DOM (happy-dom) as a fallback for legacy compatibility;\n// that fallback has been removed together with the happy-dom dependency.\n\n/**\n * Renders a Elur template to an HTML string in Node.\n *\n * Accepts a *factory* (not a template) because `html`` evaluates at call time.\n *\n * @param factory Thunk that builds the template, e.g. `() => Page({ data })`.\n * @returns Serialized HTML of the rendered template.\n */\nexport async function renderToString(\n factory: () => ElurTemplate,\n options: { markers?: \"none\" | \"hydration\" } = {},\n): Promise<string> {\n setSSR(true);\n try {\n return await renderCoreTemplate(factory(), {\n markers: options.markers ?? \"hydration\",\n });\n } finally {\n setSSR(false);\n }\n}\n","//\n// The <!DOCTYPE>, <head> and <body> wrapper — plus the serialized loader data\n// and the client entry — are injected here at build time.\n\nimport type { PageMetadata } from \"../types.js\";\nexport interface ShellOptions {\n /** Rendered inner HTML that goes inside `#app`. */\n body: string;\n /** `<title>` text. */\n title?: string;\n /** `<html lang>` attribute. */\n lang?: string;\n /** Additional attributes for the `<html>` element, e.g. `{ \"data-theme\": \"dark\" }`. */\n htmlAttributes?: Record<string, string>;\n /**\n * Inline scripts injected into `<head>`. They run synchronously while the\n * document parses — before the first paint and before the (deferred) client\n * bundle — so they are the right place for no-flash bootstrapping (e.g.\n * applying a stored theme before the page becomes visible).\n */\n headScripts?: string[];\n /**\n * Raw HTML strings injected into `<head>` — e.g. `<link rel=\"icon\">`,\n * `<link rel=\"manifest\">`, `<meta name=\"theme-color\">`. Each string is\n * rendered as-is inside `<head>`.\n */\n headLinks?: string[];\n /** Loader data serialized into `<script id=\"elur-data\">`. */\n data?: unknown;\n /** Per-page action names serialized into `<script id=\"elur-actions\">`. */\n actions?: Record<string, string[]>;\n /** Path to the client entry module, e.g. `/_elur/entry-client.js`. */\n clientEntry?: string;\n /** Page metadata emitted as `<meta>`, `<link>` and OG/Twitter tags in `<head>`. */\n metadata?: PageMetadata;\n /**\n * Whether the SSR render endpoint (`/__elur-js/render`) is available at\n * runtime. Defaults to `true`. When `false` (static deployments), the shell\n * emits `<meta name=\"elur:render-endpoint\" content=\"off\" />` so the client\n * router skips probing the endpoint entirely — preventing a storm of 404\n * requests on fully static sites.\n */\n renderEndpoint?: boolean;\n}\n\nconst HTML_ESCAPES: Record<string, string> = {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n '\"': \""\",\n \"'\": \"'\",\n};\n\nfunction escapeHtml(value: string): string {\n return value.replace(/[&<>\"']/g, (c) => HTML_ESCAPES[c]);\n}\n\n/**\n * Serializes data for embedding inside a `<script>` tag. Escapes `<` so a\n * `</script>` sequence in the data cannot break out of the tag.\n */\nfunction serializeData(data: unknown): string {\n return JSON.stringify(data ?? null).replace(/</g, \"\\\\u003c\");\n}\n\n/**\n * Builds the `<head>` tags for a `PageMetadata` object. Every tag is marked with\n * `data-elur-head` so the client-side router can replace them on navigation\n * without touching charset/viewport or user-supplied `headScripts`.\n */\nexport function buildHeadTags(metadata: PageMetadata, fallbackTitle: string): string {\n const tags: string[] = [];\n const title = metadata.title ?? fallbackTitle;\n if (metadata.title) {\n tags.push(`<title data-elur-head>${escapeHtml(title)}</title>`);\n }\n\n if (metadata.description) {\n tags.push(`<meta data-elur-head name=\"description\" content=\"${escapeHtml(metadata.description)}\" />`);\n }\n\n if (metadata.canonical) {\n tags.push(`<link data-elur-head rel=\"canonical\" href=\"${escapeHtml(metadata.canonical)}\" />`);\n }\n\n if (metadata.robots) {\n tags.push(`<meta data-elur-head name=\"robots\" content=\"${escapeHtml(metadata.robots)}\" />`);\n }\n\n const og = metadata.openGraph;\n if (og) {\n if (og.type) tags.push(`<meta data-elur-head property=\"og:type\" content=\"${escapeHtml(og.type)}\" />`);\n tags.push(`<meta data-elur-head property=\"og:title\" content=\"${escapeHtml(og.title ?? title)}\" />`);\n if (og.description ?? metadata.description) {\n tags.push(`<meta data-elur-head property=\"og:description\" content=\"${escapeHtml(og.description ?? metadata.description!)}\" />`);\n }\n if (og.url ?? metadata.canonical) {\n tags.push(`<meta data-elur-head property=\"og:url\" content=\"${escapeHtml(og.url ?? metadata.canonical!)}\" />`);\n }\n if (og.image) tags.push(`<meta data-elur-head property=\"og:image\" content=\"${escapeHtml(og.image)}\" />`);\n if (og.image && og.imageAlt) tags.push(`<meta data-elur-head property=\"og:image:alt\" content=\"${escapeHtml(og.imageAlt)}\" />`);\n if (og.image && og.imageWidth) tags.push(`<meta data-elur-head property=\"og:image:width\" content=\"${String(og.imageWidth)}\" />`);\n if (og.image && og.imageHeight) tags.push(`<meta data-elur-head property=\"og:image:height\" content=\"${String(og.imageHeight)}\" />`);\n if (og.image && og.imageType) tags.push(`<meta data-elur-head property=\"og:image:type\" content=\"${escapeHtml(og.imageType)}\" />`);\n if (og.siteName) tags.push(`<meta data-elur-head property=\"og:site_name\" content=\"${escapeHtml(og.siteName)}\" />`);\n if (og.locale) tags.push(`<meta data-elur-head property=\"og:locale\" content=\"${escapeHtml(og.locale)}\" />`);\n }\n\n const tw = metadata.twitter;\n if (tw) {\n if (tw.card) tags.push(`<meta data-elur-head name=\"twitter:card\" content=\"${escapeHtml(tw.card)}\" />`);\n if (tw.title ?? title) tags.push(`<meta data-elur-head name=\"twitter:title\" content=\"${escapeHtml(tw.title ?? title)}\" />`);\n if (tw.description ?? metadata.description) {\n tags.push(`<meta data-elur-head name=\"twitter:description\" content=\"${escapeHtml(tw.description ?? metadata.description!)}\" />`);\n }\n if (tw.image) tags.push(`<meta data-elur-head name=\"twitter:image\" content=\"${escapeHtml(tw.image)}\" />`);\n if (tw.image && tw.imageAlt) tags.push(`<meta data-elur-head name=\"twitter:image:alt\" content=\"${escapeHtml(tw.imageAlt)}\" />`);\n }\n\n if (metadata.other) {\n for (const [name, content] of Object.entries(metadata.other)) {\n tags.push(`<meta data-elur-head name=\"${escapeHtml(name)}\" content=\"${escapeHtml(content)}\" />`);\n }\n }\n\n return tags.map((t) => `\\n ${t}`).join(\"\");\n}\n\n/** Wraps rendered body HTML into a full HTML document. */\nexport function documentShell(opts: ShellOptions): string {\n const { body, title = \"Elur Kit App\", lang = \"es\", data, actions, clientEntry, htmlAttributes, headScripts, headLinks, metadata } = opts;\n\n const dataScript =\n data !== undefined\n ? `\\n <script type=\"application/json\" id=\"elur-data\">${serializeData(data)}</script>`\n : \"\";\n\n const actionsScript = actions && Object.keys(actions).length > 0\n ? `\\n <script type=\"application/json\" id=\"elur-actions\">${serializeData(actions)}</script>`\n : \"\";\n\n const entryScript = clientEntry\n ? `\\n <script type=\"module\" src=\"${escapeHtml(clientEntry)}\"></script>`\n : \"\";\n\n const htmlAttrs = htmlAttributes\n ? Object.entries(htmlAttributes)\n .filter(([, value]) => value !== undefined && value !== null && value !== \"\")\n .map(([key, value]) => ` ${escapeHtml(key)}=\"${escapeHtml(String(value))}\"`)\n .join(\"\")\n : \"\";\n\n const headScriptsHtml = headScripts\n ? headScripts\n .filter((script) => typeof script === \"string\" && script.trim().length > 0)\n .map((script) => {\n // If the script is already a complete <script> tag (e.g. JSON-LD),\n // render it as-is without wrapping.\n if (script.trimStart().startsWith(\"<script\")) {\n return `\\n ${script}`;\n }\n return `\\n <script>${script.replace(/<\\/script>/gi, \"<\\\\/script>\")}</script>`;\n })\n .join(\"\")\n : \"\";\n\n const headTags = metadata ? buildHeadTags(metadata, title) : \"\";\n const titleTag = metadata?.title\n ? \"\" // already emitted by buildHeadTags\n : `\\n <title>${escapeHtml(title)}</title>`;\n\n const headLinksHtml = headLinks\n ? headLinks\n .filter((link) => typeof link === \"string\" && link.trim().length > 0)\n .map((link) => `\\n ${link}`)\n .join(\"\")\n : \"\";\n\n const renderEndpointMeta =\n opts.renderEndpoint === false\n ? '\\n <meta name=\"elur:render-endpoint\" content=\"off\" />'\n : \"\";\n\n return `<!DOCTYPE html>\n<html lang=\"${escapeHtml(lang)}\"${htmlAttrs}>\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />${renderEndpointMeta}${titleTag}${headTags}${headLinksHtml}${headScriptsHtml}\n </head>\n <body>\n <div id=\"app\">${body}</div>${dataScript}${actionsScript}${entryScript}\n </body>\n</html>\n`;\n}\n","// --- Ephemeral action error store ---\n//\n// Action failures submitted via plain HTML forms (progressive enhancement)\n// need to be relayed back to the page so the user sees validation errors.\n//\n// Previously the failure data was serialized into a `?__elur_js_action_error=`\n// query param on the redirect. That leaks errors into browser history,\n// server logs and third-party Referer headers.\n//\n// Now we stash the failure in a short-lived in-memory store keyed by a random\n// id, set a small cookie `__elur_js_action_error=<id>` (Max-Age=15s, SameSite=Lax),\n// and the next render reads the cookie, fetches the payload, exposes it as\n// `props.form`, and clears the entry.\n//\n// The store is process-local, which is fine for the single-process SSR server\n// and the dev server. For multi-instance deployments the cookie carries the\n// payload directly when it fits (see `encodeActionErrorCookie`); the store is\n// only the overflow path for large payloads.\n\nimport { createHmac, randomBytes, timingSafeEqual } from \"node:crypto\";\n\nconst COOKIE_NAME = \"__elur_js_action_error\";\nconst MAX_COOKIE_SIZE = 3500; // bytes; leaves headroom under the 4KB cookie limit\nconst TTL_MS = 15_000;\n\n// HMAC key for signing action error cookies. In production this should be\n// set via ELUR_JS_ACTION_SECRET env var; otherwise we derive a per-process\n// key (sufficient for single-process dev/preview, but NOT for multi-instance).\nconst ACTION_SECRET =\n process.env.ELUR_JS_ACTION_SECRET ?? randomBytes(32).toString(\"hex\");\n\ninterface StoredError {\n data: unknown;\n status: number;\n expiresAt: number;\n}\n\nconst store = new Map<string, StoredError>();\n\n// Periodically purge expired entries so the map does not grow unbounded.\nlet sweepScheduled = false;\nfunction scheduleSweep(): void {\n if (sweepScheduled) return;\n sweepScheduled = true;\n setTimeout(() => {\n sweepScheduled = false;\n const now = Date.now();\n for (const [key, entry] of store) {\n if (entry.expiresAt <= now) store.delete(key);\n }\n }, TTL_MS).unref?.();\n}\n\n/**\n * Signs a payload with HMAC-SHA256 using the action secret.\n * Returns `signature.payload` (both hex/base64url).\n */\nfunction sign(payload: string): string {\n const sig = createHmac(\"sha256\", ACTION_SECRET).update(payload).digest(\"hex\");\n return `${sig}.${payload}`;\n}\n\n/**\n * Verifies a signed value and returns the payload if valid, or undefined.\n * Uses timingSafeEqual to prevent timing attacks.\n */\nfunction verify(value: string): string | undefined {\n const dotIndex = value.indexOf(\".\");\n if (dotIndex === -1) return undefined;\n const sig = value.slice(0, dotIndex);\n const payload = value.slice(dotIndex + 1);\n const expectedSig = createHmac(\"sha256\", ACTION_SECRET).update(payload).digest(\"hex\");\n if (sig.length !== expectedSig.length) return undefined;\n try {\n if (timingSafeEqual(Buffer.from(sig), Buffer.from(expectedSig))) {\n return payload;\n }\n } catch {\n // Length mismatch — invalid.\n }\n return undefined;\n}\n\n/**\n * Encodes an action failure for the redirect cookie. When the payload fits\n * inside the cookie limit, it is embedded directly as a signed base64url JSON\n * value. When it is too large, it is stored in memory and only a short signed\n * id is written to the cookie.\n *\n * The cookie is signed with HMAC-SHA256 to prevent forgery (A-20).\n *\n * @returns The cookie value to set on the redirect response.\n */\nexport function encodeActionErrorCookie(\n data: unknown,\n status: number,\n): { value: string; storeId?: string } {\n const payload = JSON.stringify({ d: data, s: status });\n const encoded = Buffer.from(payload, \"utf8\").toString(\"base64url\");\n const signed = sign(encoded);\n if (signed.length <= MAX_COOKIE_SIZE) {\n return { value: signed };\n }\n\n // Overflow: stash in memory and reference by signed id.\n const id = randomBytes(12).toString(\"hex\");\n store.set(id, { data, status, expiresAt: Date.now() + TTL_MS });\n scheduleSweep();\n return { value: sign(`id:${id}`), storeId: id };\n}\n\n/**\n * Decodes a cookie value (previously produced by `encodeActionErrorCookie`)\n * into the failure payload. Verifies the HMAC signature first, then resolves\n * in-memory overflow entries and deletes them after reading.\n */\nexport function decodeActionErrorCookie(value: string | undefined | null):\n | { data: unknown; status: number }\n | undefined {\n if (!value) return undefined;\n\n // Verify signature first.\n const verifiedPayload = verify(value);\n if (verifiedPayload === undefined) return undefined;\n\n // Check if it's an in-memory store reference.\n if (verifiedPayload.startsWith(\"id:\")) {\n const id = verifiedPayload.slice(3);\n const entry = store.get(id);\n if (!entry) return undefined;\n store.delete(id);\n if (entry.expiresAt <= Date.now()) return undefined;\n return { data: entry.data, status: entry.status };\n }\n\n try {\n const json = Buffer.from(verifiedPayload, \"base64url\").toString(\"utf8\");\n const parsed = JSON.parse(json) as { d: unknown; s: number };\n return { data: parsed.d, status: parsed.s };\n } catch {\n return undefined;\n }\n}\n\n/** Name of the cookie used to relay action errors. */\nexport const ACTION_ERROR_COOKIE = COOKIE_NAME;\n\n/** Builds the Set-Cookie header value that clears the error cookie. */\nexport function clearActionErrorCookieHeader(): string {\n return `${COOKIE_NAME}=; Path=/; Max-Age=0; SameSite=Lax`;\n}\n\n/** Builds the Set-Cookie header value that sets the error cookie. */\nexport function setActionErrorCookieHeader(value: string): string {\n return `${COOKIE_NAME}=${value}; Path=/; Max-Age=15; SameSite=Lax; HttpOnly`;\n}\n","// --- Cache policy per route (runtime-security §9.1) ---\n//\n// Authors can declare a cache policy in their page.data.ts:\n//\n// export const cache = {\n// mode: \"public\", // \"public\" | \"private\" | \"dynamic\"\n// revalidate: 60, // seconds\n// tags: [\"products\"], // for tag-based invalidation\n// };\n//\n// Default policy: \"dynamic\" (no public ISR caching).\n// Requests with Cookie/Authorization are never cached publicly.\n// Responses with Set-Cookie/private/no-store are never cached publicly.\n\n/** Cache mode for a route. */\nexport type CacheMode = \"public\" | \"private\" | \"dynamic\";\n\n/** Cache policy declared by the route's data module. */\nexport interface CachePolicy {\n mode: CacheMode;\n revalidate: number;\n tags?: string[];\n}\n\n/** Default cache policy when none is declared. */\nexport const DEFAULT_CACHE_POLICY: CachePolicy = {\n mode: \"dynamic\",\n revalidate: 0,\n};\n\n/**\n * Normalizes a raw cache export from a data module into a CachePolicy.\n * Returns the default policy if the input is invalid or missing.\n */\nexport function normalizeCachePolicy(raw: unknown): CachePolicy {\n if (!raw || typeof raw !== \"object\") return DEFAULT_CACHE_POLICY;\n const obj = raw as Record<string, unknown>;\n const mode = obj.mode;\n if (mode !== \"public\" && mode !== \"private\" && mode !== \"dynamic\") {\n return DEFAULT_CACHE_POLICY;\n }\n const revalidate = typeof obj.revalidate === \"number\" ? obj.revalidate : 0;\n const tags = Array.isArray(obj.tags) ? obj.tags.filter((t) => typeof t === \"string\") : undefined;\n return { mode, revalidate, tags };\n}\n\n/**\n * Determines whether a route's cache policy allows public caching for the\n * given request.\n *\n * Per §9.1:\n * - \"dynamic\" → never cache\n * - \"private\" → never cache publicly (requires private adapter)\n * - \"public\" → cache only if request has no Cookie/Authorization\n */\nexport function shouldCachePublic(\n policy: CachePolicy,\n request: Request,\n): boolean {\n if (policy.mode !== \"public\") return false;\n if (policy.revalidate <= 0) return false;\n if (request.headers.get(\"Cookie\")) return false;\n if (request.headers.get(\"Authorization\")) return false;\n return true;\n}\n","import type { ElurTemplate } from \"@elurjs/core\";\nimport { renderToString } from \"../render/render-to-string.js\";\nimport { documentShell, buildHeadTags } from \"../build/document-shell.js\";\nimport type { PageRoute, ScannedRoutes } from \"../router/route-scanner.js\";\nimport type { BuildConfig } from \"../build/build.js\";\nimport type { PageDataLoad, PageProps, RouteParams, PageMetadata, GenerateMetadata } from \"../types.js\";\nimport { existsSync } from \"node:fs\";\nimport { decodeActionErrorCookie, ACTION_ERROR_COOKIE } from \"../action/error-store.js\";\nimport { normalizeCachePolicy, type CachePolicy } from \"../cache/policy.js\";\n\nexport interface RenderPageOptions {\n route: PageRoute;\n params?: RouteParams;\n searchParams?: URLSearchParams;\n config: Pick<BuildConfig, \"lang\" | \"clientEntry\" | \"renderEndpoint\">;\n /** Custom module loader. Defaults to native dynamic import. */\n importer?: (path: string) => Promise<unknown>;\n /** Per-page action names exposed in the HTML shell. */\n actions?: Record<string, string[]>;\n /** Current request, used to hydrate data loaders that need cookies/headers. */\n request?: Request;\n}\n\nexport interface RenderPageResult {\n html: string;\n revalidate?: number;\n /**\n * `Set-Cookie` header value that clears the action error cookie, when the\n * page consumed a relayed action failure. The SSR server should append it to\n * the outgoing response so the cookie does not persist.\n */\n clearActionErrorCookie?: string;\n /** `<head>` tags (title, meta, OG, twitter) for the SPA router to merge. */\n head?: string;\n /** Resolved page title (from metadata or fallback). */\n resolvedTitle?: string;\n /**\n * When a loader or layout throws a `Response` (e.g. `throw new Response(...,\n * { status: 404 })`), it is captured here as a first-class response instead\n * of being treated as an internal error (A-22).\n */\n response?: Response;\n /** HTTP status code for the rendered page (e.g. 404 for not-found pages). */\n status?: number;\n /** Cache policy declared by the route (§9.1). */\n cachePolicy?: CachePolicy;\n}\n\nconst defaultImport = (path: string) => import(path);\n\n/**\n * Collects `<html>` attributes and head scripts declared by data loaders\n * (page and layouts) via top-level `htmlAttributes` / `headScripts` fields.\n */\nexport function collectShellExtras(\n pageData: unknown,\n layoutDataList: unknown[],\n): { htmlAttributes: Record<string, string>; headScripts: string[]; headLinks: string[] } {\n const htmlAttributes: Record<string, string> = {};\n const headScripts: string[] = [];\n const headLinks: string[] = [];\n const merge = (value: unknown) => {\n if (!value || typeof value !== \"object\") return;\n const attrs = (value as { htmlAttributes?: Record<string, string> }).htmlAttributes;\n if (attrs) Object.assign(htmlAttributes, attrs);\n const scripts = (value as { headScripts?: string[] }).headScripts;\n if (Array.isArray(scripts)) headScripts.push(...scripts);\n const links = (value as { headLinks?: string[] }).headLinks;\n if (Array.isArray(links)) headLinks.push(...links);\n };\n for (const layoutData of layoutDataList) merge(layoutData);\n merge(pageData);\n // Deduplicate headScripts and headLinks (e.g. from both layout and page data)\n const uniqueScripts = [...new Set(headScripts)];\n const uniqueLinks = [...new Set(headLinks)];\n return { htmlAttributes, headScripts: uniqueScripts, headLinks: uniqueLinks };\n}\n\nexport async function renderPage(options: RenderPageOptions): Promise<RenderPageResult> {\n const { route, params = {}, searchParams = new URLSearchParams(), config, importer = defaultImport, actions, request } = options;\n\n const pageModule = await importer(route.pagePath) as {\n default: (props: PageProps<unknown>) => ElurTemplate;\n generateMetadata?: GenerateMetadata;\n };\n const { default: PageComponent, generateMetadata } = pageModule;\n\n let data: unknown;\n let revalidate: number | undefined;\n let cachePolicy: import(\"../cache/policy.js\").CachePolicy | undefined;\n // Use a mutable container so TypeScript doesn't narrow the type after\n // the first `if (thrownResponse)` check.\n const thrown: { response: Response | undefined } = { response: undefined };\n if (route.dataPath) {\n const mod = await importer(route.dataPath) as {\n load?: PageDataLoad;\n revalidate?: number;\n cache?: unknown;\n };\n if (mod.load) {\n try {\n data = await mod.load({ params, searchParams, request });\n } catch (err) {\n if (err instanceof Response) {\n thrown.response = err;\n } else {\n throw err;\n }\n }\n }\n if (typeof mod.revalidate === \"number\") {\n revalidate = mod.revalidate;\n }\n // Read cache policy from the data module (§9.1).\n if (mod.cache) {\n cachePolicy = normalizeCachePolicy(mod.cache);\n if (cachePolicy.revalidate > 0) {\n revalidate = cachePolicy.revalidate;\n }\n }\n }\n\n // If a loader threw a Response (redirect, 404, etc.), return it as a\n // first-class response instead of rendering the page (A-22).\n if (thrown.response) {\n return { html: \"\", response: thrown.response, status: thrown.response.status };\n }\n\n // Relay an action failure previously stored in the ephemeral cookie so the\n // page can render validation errors via `props.form`. The cookie is cleared\n // on the outgoing response (see `clearActionErrorCookie` in the result).\n let form: unknown;\n let clearActionErrorCookie: string | undefined;\n if (request) {\n const cookieHeader = request.headers.get(\"Cookie\") ?? \"\";\n const match = cookieHeader.match(new RegExp(`(?:^|;\\\\s*)${ACTION_ERROR_COOKIE}=([^;]+)`));\n if (match) {\n const decoded = decodeActionErrorCookie(match[1]);\n if (decoded) {\n form = { __elur_js_action_error: true, status: decoded.status, data: decoded.data };\n clearActionErrorCookie = `${ACTION_ERROR_COOKIE}=; Path=/; Max-Age=0; SameSite=Lax`;\n }\n }\n }\n\n const props: PageProps<unknown> = {\n data: data ?? {},\n params,\n searchParams,\n form,\n };\n\n const layoutModules = await Promise.all(\n route.layouts.map(async (layoutPath) => importer(layoutPath)),\n );\n const layoutDataList = await Promise.all(\n route.layouts.map(async (layoutPath) => {\n const dataPath = layoutPath.replace(/layout\\.ts$/, \"layout.data.ts\");\n if (!existsSync(dataPath)) return undefined;\n const mod = (await importer(dataPath)) as { load?: PageDataLoad };\n if (mod.load) {\n try {\n return await mod.load({ params, searchParams, request });\n } catch (err) {\n if (err instanceof Response) {\n thrown.response = err;\n return undefined;\n }\n throw err;\n }\n }\n return undefined;\n }),\n );\n\n // If a layout loader threw a Response, return it as first-class (A-22).\n const layoutThrown = thrown.response as Response | undefined;\n if (layoutThrown) {\n return { html: \"\", response: layoutThrown, status: layoutThrown.status };\n }\n\n // Load slot modules if the route has them (v2.1 — Fix #2: Layout Slots).\n let slotTemplates: Record<string, ElurTemplate> | undefined;\n if (route.slots) {\n slotTemplates = {};\n for (const [slotName, slotPath] of Object.entries(route.slots)) {\n const slotMod = await importer(slotPath) as { default: (props: PageProps<unknown>) => ElurTemplate };\n slotTemplates[slotName] = slotMod.default(props);\n }\n }\n\n const body = await renderToString(() => {\n let template = PageComponent(props);\n for (let i = layoutModules.length - 1; i >= 0; i--) {\n const { default: Layout } = layoutModules[i] as {\n default: (props: { children: ElurTemplate; data?: unknown; slots?: Record<string, ElurTemplate> }) => ElurTemplate;\n };\n template = Layout({ children: template, data: layoutDataList[i], slots: slotTemplates });\n }\n return template;\n });\n\n const title = typeof data === \"object\" && data && \"title\" in data\n ? String((data as { title?: unknown }).title ?? \"Elur Kit\")\n : \"Elur Kit\";\n\n const { htmlAttributes, headScripts, headLinks } = collectShellExtras(data, layoutDataList);\n\n // Resolve page metadata. Priority: `generateMetadata` from page.ts > `metadata`\n // field in the page loader data > `metadata` field in layout loader data.\n let metadata: PageMetadata | undefined;\n if (typeof generateMetadata === \"function\") {\n metadata = await generateMetadata({ params, searchParams, request, data });\n }\n if (!metadata) {\n metadata = extractMetadata(data) ?? extractMetadataFromList(layoutDataList);\n }\n // The title from metadata takes precedence over the data.title fallback.\n const resolvedTitle = metadata?.title ?? title;\n\n const html = documentShell({\n title: resolvedTitle,\n lang: config.lang,\n body,\n data,\n actions,\n htmlAttributes,\n headScripts,\n headLinks,\n metadata,\n clientEntry: config.clientEntry,\n renderEndpoint: config.renderEndpoint,\n });\n\n const head = metadata ? buildHeadTags(metadata, resolvedTitle) : \"\";\n return { html, revalidate, clearActionErrorCookie, head, resolvedTitle, cachePolicy };\n}\n\n/** Extracts a `metadata` field from a loader data object, if present. */\nfunction extractMetadata(value: unknown): PageMetadata | undefined {\n if (value && typeof value === \"object\" && \"metadata\" in value) {\n const meta = (value as { metadata?: unknown }).metadata;\n if (meta && typeof meta === \"object\") return meta as PageMetadata;\n }\n return undefined;\n}\n\n/** Extracts metadata from the first layout data object that has one. */\nfunction extractMetadataFromList(list: unknown[]): PageMetadata | undefined {\n for (const item of list) {\n const meta = extractMetadata(item);\n if (meta) return meta;\n }\n return undefined;\n}\n\nexport interface RenderErrorPageOptions {\n routes: ScannedRoutes;\n status: 404 | 500;\n error?: unknown;\n config: Pick<BuildConfig, \"lang\" | \"clientEntry\" | \"renderEndpoint\">;\n actions?: Record<string, string[]>;\n importer?: (path: string) => Promise<unknown>;\n}\n\nexport async function renderErrorPage(\n options: RenderErrorPageOptions,\n): Promise<{ html: string; status: number } | undefined> {\n const route = options.status === 404 ? options.routes.error404 : options.routes.error500;\n if (!route) return undefined;\n\n try {\n const { html } = await renderPage({\n route,\n params: {},\n searchParams: new URLSearchParams(),\n config: options.config,\n actions: options.actions,\n importer: options.importer,\n });\n return { html, status: options.status };\n } catch (err) {\n console.error(`[render] error ${options.status} page failed`, err);\n return undefined;\n }\n}\n","import type { ApiRoute, PageRoute } from \"../router/route-scanner.js\";\n\nexport interface MatchResult {\n route: PageRoute;\n params: Record<string, string | string[]>;\n searchParams: URLSearchParams;\n}\n\n/**\n * Match a request pathname against a list of page routes.\n *\n * Routes are sorted by specificity (static > dynamic > catch-all) before\n * matching, so `/about` wins over `/:slug` even if the catch-all appears first.\n *\n * URL segments are safely decoded (plan §11.1, runtime-security §10).\n */\nexport function matchRoute(\n pathname: string,\n routes: PageRoute[],\n): MatchResult | undefined {\n const cleanPath = pathname.split(\"?\")[0];\n const requestSegments = cleanPath.split(\"/\").filter(Boolean).map(safeDecodeURIComponent);\n\n const sorted = [...routes].sort((a, b) => specificity(b.path) - specificity(a.path));\n\n for (const route of sorted) {\n const routeSegments = route.path.split(\"/\").filter(Boolean);\n const match = tryMatch(requestSegments, routeSegments, route.optionalCatchAll);\n if (match) {\n return { route, params: match, searchParams: new URLSearchParams() };\n }\n }\n\n return undefined;\n}\n\nexport interface ApiMatchResult<T = ApiRoute> {\n route: T;\n params: Record<string, string | string[]>;\n}\n\n/**\n * Match a request pathname against a list of API routes.\n */\nexport function matchApiRoute<T extends { path: string }>(pathname: string, routes: T[]): ApiMatchResult<T> | undefined {\n const cleanPath = pathname.split(\"?\")[0];\n const requestSegments = cleanPath.split(\"/\").filter(Boolean).map(safeDecodeURIComponent);\n\n const sorted = [...routes].sort((a, b) => specificity(b.path) - specificity(a.path));\n\n for (const route of sorted) {\n const routeSegments = route.path.split(\"/\").filter(Boolean);\n const match = tryMatch(requestSegments, routeSegments);\n if (match) {\n return { route, params: match };\n }\n }\n\n return undefined;\n}\n\n/**\n * Safely decodes a URI component. If decoding fails (malformed % sequences),\n * returns the original string rather than throwing (runtime-security §10).\n */\nfunction safeDecodeURIComponent(segment: string): string {\n try {\n return decodeURIComponent(segment);\n } catch {\n return segment;\n }\n}\n\nfunction specificity(path: string): number {\n return path.split(\"/\").filter(Boolean).reduce((score, segment) => {\n if (segment.endsWith(\"*\")) return score;\n if (segment.startsWith(\":\")) return score + 1;\n return score + 2;\n }, 0);\n}\n\nfunction tryMatch(\n requestSegments: string[],\n routeSegments: string[],\n optionalCatchAll = false,\n): Record<string, string | string[]> | undefined {\n const params: Record<string, string | string[]> = {};\n\n let i = 0;\n for (let r = 0; r < routeSegments.length; r++) {\n const routeSeg = routeSegments[r];\n\n if (routeSeg.endsWith(\"*\")) {\n // Catch-all consumes the rest of the request segments.\n const name = routeSeg.slice(1, -1);\n const rest = requestSegments.slice(i);\n // For optional catch-all, empty rest is OK.\n if (rest.length === 0 && !optionalCatchAll) return undefined;\n params[name] = rest.length > 0 ? rest : [];\n return params;\n }\n\n if (routeSeg.startsWith(\":\")) {\n const requestSeg = requestSegments[i];\n if (requestSeg === undefined) return undefined;\n params[routeSeg.slice(1)] = requestSeg;\n i++;\n continue;\n }\n\n if (routeSeg !== requestSegments[i]) {\n return undefined;\n }\n i++;\n }\n\n if (i !== requestSegments.length) return undefined;\n return params;\n}\n","// --- Origin verification (CSRF protection for server actions) ---\n//\n// Server actions accept POST requests from the browser. Without origin\n// verification, any third-party site could submit forged requests to\n// `/__elur-js/actions` on behalf of a logged-in user (CSRF).\n//\n// Strategy: compare the request's `Origin` (or `Referer` fallback) host against\n// the target `Host` header. Same-origin requests pass; cross-origin requests\n// are rejected with 403 unless the origin is explicitly allow-listed.\n//\n// Requests without `Origin` AND without `Referer` (e.g. curl, server-to-server)\n// are accepted by default for DX, unless `strictOrigin: true` is configured.\n\nexport interface OriginCheckOptions {\n /** Extra origins allowed to call actions (e.g. preview deployments). */\n allowedOrigins?: string[];\n /**\n * When true, requests missing both `Origin` and `Referer` are rejected.\n * Defaults to false so curl/server-to-server calls keep working.\n */\n strictOrigin?: boolean;\n}\n\n/**\n * Returns the host:port of a URL string, or undefined if it cannot be parsed.\n */\nfunction originOf(urlString: string | null | undefined): string | undefined {\n if (!urlString) return undefined;\n try {\n const url = new URL(urlString);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return undefined;\n return url.origin;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Verifies that a request originates from the same host (or an allow-listed\n * origin). Returns an error message when the request must be rejected, or\n * undefined when it is allowed.\n *\n * @param request The incoming Request to actions.\n * @param options Origin check configuration.\n */\nexport function verifyOrigin(\n request: Request,\n options: OriginCheckOptions = {},\n): string | undefined {\n const targetOrigin = originOf(request.url);\n if (!targetOrigin) return \"Invalid target URL\";\n\n const origin = request.headers.get(\"Origin\");\n const referer = request.headers.get(\"Referer\");\n if (!origin && !referer) {\n return options.strictOrigin\n ? \"Missing Origin and Referer headers\"\n : undefined;\n }\n\n const sourceOrigin = origin ? originOf(origin) : originOf(referer);\n if (!sourceOrigin) return origin ? \"Invalid Origin header\" : \"Invalid Referer header\";\n if (sourceOrigin === targetOrigin) return undefined;\n\n if (options.allowedOrigins?.some((allowed) => originOf(allowed) === sourceOrigin)) return undefined;\n\n return `Cross-origin request blocked: source \"${sourceOrigin}\" != target \"${targetOrigin}\"`;\n}\n\n/** Builds a 403 Response for a rejected origin. */\nexport function originForbidden(message: string): Response {\n return new Response(message, {\n status: 403,\n headers: { \"Content-Type\": \"text/plain; charset=utf-8\" },\n });\n}\n","import type { ActionRequest } from \"./index.js\";\nimport { isActionFailure, isRedirectResponse, publicErrorResponse } from \"../errors.js\";\nimport { verifyOrigin, originForbidden, type OriginCheckOptions } from \"./origin.js\";\nimport {\n encodeActionErrorCookie,\n setActionErrorCookieHeader,\n} from \"./error-store.js\";\n\n/**\n * Resolves a server action by name and optional page scope.\n */\nexport type ActionResolver = (\n name: string,\n page?: string,\n) => Promise<((...args: unknown[]) => unknown) | undefined>;\n\n/** Options shared by `handleActionRequest` callers for CSRF protection. */\nexport interface ActionSecurityOptions extends OriginCheckOptions {\n /** Maximum body size in bytes. Defaults to 1MB (1_048_576). */\n bodyLimit?: number;\n}\n\n/** Default body size limit: 1MB. */\nconst DEFAULT_BODY_LIMIT = 1_048_576;\n\n/**\n * Reads the request body as text, enforcing a maximum size.\n * Returns a 413 response if the body exceeds the limit.\n */\nasync function readBodyWithLimit(\n request: Request,\n limit: number,\n): Promise<{ ok: true; text: string } | { ok: false; response: Response }> {\n const contentLength = request.headers.get(\"Content-Length\");\n if (contentLength && parseInt(contentLength, 10) > limit) {\n return {\n ok: false,\n response: new Response(\"Request body too large\", {\n status: 413,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n // Read the body as a stream with a size cap to prevent memory exhaustion\n // from chunked transfer encoding without Content-Length.\n const reader = request.body?.getReader();\n if (!reader) {\n return { ok: true, text: \"\" };\n }\n const chunks: Uint8Array[] = [];\n let totalSize = 0;\n try {\n for (; ;) {\n const { done, value } = await reader.read();\n if (done) break;\n totalSize += value.byteLength;\n if (totalSize > limit) {\n try { reader.cancel(); } catch { /* ignore */ }\n return {\n ok: false,\n response: new Response(\"Request body too large\", {\n status: 413,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n chunks.push(value);\n }\n } finally {\n try { reader.releaseLock(); } catch { /* ignore */ }\n }\n const total = new Uint8Array(totalSize);\n let offset = 0;\n for (const chunk of chunks) {\n total.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return { ok: true, text: new TextDecoder().decode(total) };\n}\n\nfunction parseFormBody(body: string): Record<string, unknown> {\n const params = new URLSearchParams(body);\n const result: Record<string, unknown> = {};\n for (const [key, value] of params) {\n if (result[key] === undefined) {\n result[key] = value;\n } else if (Array.isArray(result[key])) {\n (result[key] as unknown[]).push(value);\n } else {\n result[key] = [result[key], value];\n }\n }\n return result;\n}\n\nasync function parseActionRequest(\n request: Request,\n bodyLimit: number = DEFAULT_BODY_LIMIT,\n): Promise<\n | { ok: true; name: string; page?: string; args: unknown[]; wantsJson: boolean }\n | { ok: false; response: Response }\n> {\n if (request.method !== \"POST\") {\n return {\n ok: false,\n response: new Response(\"Method not allowed\", {\n status: 405,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n\n const contentType = request.headers.get(\"Content-Type\") ?? \"\";\n const wantsJson = (request.headers.get(\"Accept\") ?? \"\").includes(\"application/json\");\n\n let name: string | undefined;\n let page: string | undefined;\n let args: unknown[] = [];\n\n if (contentType.includes(\"application/json\")) {\n const bodyResult = await readBodyWithLimit(request, bodyLimit);\n if (!bodyResult.ok) return { ok: false, response: bodyResult.response };\n let body: ActionRequest;\n try {\n body = JSON.parse(bodyResult.text) as ActionRequest;\n } catch {\n return {\n ok: false,\n response: new Response(\"Invalid JSON body\", {\n status: 400,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n name = body.name;\n page = body.page;\n args = Array.isArray(body.args) ? body.args : [];\n } else if (\n contentType.includes(\"application/x-www-form-urlencoded\") ||\n contentType.includes(\"multipart/form-data\")\n ) {\n // For multipart, use the native formData() parser after checking\n // Content-Length against the limit. For urlencoded, use our size-capped\n // reader to handle chunked encoding without Content-Length.\n if (contentType.includes(\"multipart/form-data\")) {\n const contentLength = request.headers.get(\"Content-Length\");\n if (contentLength && parseInt(contentLength, 10) > bodyLimit) {\n return {\n ok: false,\n response: new Response(\"Request body too large\", {\n status: 413,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n let form: FormData;\n try {\n form = await request.formData();\n } catch {\n return {\n ok: false,\n response: new Response(\"Invalid form body\", {\n status: 400,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n name = form.get(\"__elur_js_action_name\") as string | null ?? undefined;\n page = form.get(\"__elur_js_action_page\") as string | null ?? undefined;\n const input: Record<string, unknown> = {};\n for (const [key, value] of form) {\n if (key === \"__elur_js_action_name\" || key === \"__elur_js_action_page\") continue;\n input[key] = value;\n }\n args = [input];\n } else {\n const bodyResult = await readBodyWithLimit(request, bodyLimit);\n if (!bodyResult.ok) return { ok: false, response: bodyResult.response };\n const form = parseFormBody(bodyResult.text);\n name = form.__elur_js_action_name as string | undefined;\n page = form.__elur_js_action_page as string | undefined;\n const input: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(form)) {\n if (key === \"__elur_js_action_name\" || key === \"__elur_js_action_page\") continue;\n input[key] = value;\n }\n args = [input];\n }\n } else {\n // Try to parse a plain form body as a fallback for progressive enhancement.\n const bodyResult = await readBodyWithLimit(request, bodyLimit);\n if (!bodyResult.ok) return { ok: false, response: bodyResult.response };\n const form = parseFormBody(bodyResult.text);\n name = form.__elur_js_action_name as string | undefined;\n page = form.__elur_js_action_page as string | undefined;\n const input: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(form)) {\n if (key === \"__elur_js_action_name\" || key === \"__elur_js_action_page\") continue;\n input[key] = value;\n }\n args = [input];\n }\n\n if (!name || typeof name !== \"string\") {\n return {\n ok: false,\n response: new Response(\"Missing action name\", {\n status: 400,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n\n return { ok: true, name, page, args, wantsJson };\n}\n\n/**\n * Handles a POST request to the server action endpoint.\n *\n * Accepts both JSON requests (`{ name, page?, args }`) and HTML form submissions\n * for progressive enhancement. The provided resolver looks up the action\n * implementation, invokes it with the supplied arguments and returns the result\n * as JSON or redirects back to the request origin for form submissions.\n *\n * Origin verification (CSRF protection) runs before parsing the body: any\n * cross-origin POST is rejected with 403 unless its origin is allow-listed via\n * `security.allowedOrigins`.\n *\n * For progressive-enhancement form submissions that fail, the failure payload\n * is relayed back via a short-lived `__elur_js_action_error` cookie (SameSite=Lax,\n * Max-Age=15s) instead of a query param, so errors do not leak into browser\n * history, server logs or third-party Referer headers.\n */\nexport async function handleActionRequest(\n request: Request,\n resolveAction: ActionResolver,\n security: ActionSecurityOptions = {},\n): Promise<Response> {\n // CSRF: verify same-origin (or allow-listed) before doing any work.\n const originError = verifyOrigin(request, security);\n if (originError) return originForbidden(originError);\n\n const parsed = await parseActionRequest(request, security.bodyLimit ?? DEFAULT_BODY_LIMIT);\n if (!parsed.ok) return parsed.response;\n\n const { name, page, args, wantsJson } = parsed;\n\n try {\n const action = await resolveAction(name, page);\n if (!action) {\n const message = page ? `Action not found: ${name} (page: ${page})` : `Action not found: ${name}`;\n return new Response(message, {\n status: 404,\n headers: { \"Content-Type\": \"text/plain\" },\n });\n }\n\n const result = await action(...args);\n\n if (isActionFailure(result)) {\n if (wantsJson) {\n return new Response(JSON.stringify({ __elur_js_action_failure: true, status: result.status, data: result.data }), {\n status: result.status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n // Progressive enhancement: redirect back with the failure in a cookie.\n const referer = request.headers.get(\"Referer\") ?? \"/\";\n const url = new URL(referer, \"http://localhost\");\n const { value } = encodeActionErrorCookie(result.data, result.status);\n return new Response(null, {\n status: 303,\n headers: {\n Location: url.pathname + url.search,\n \"Content-Type\": \"text/plain\",\n \"Set-Cookie\": setActionErrorCookieHeader(value),\n },\n });\n }\n\n if (isRedirectResponse(result)) {\n if (wantsJson) {\n return new Response(\n JSON.stringify({ __elur_js_action_redirect: true, status: result.status, location: result.location }),\n {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n },\n );\n }\n return new Response(null, {\n status: result.status,\n headers: { Location: result.location, \"Content-Type\": \"text/plain\" },\n });\n }\n\n if (wantsJson) {\n return new Response(JSON.stringify(result ?? null), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n // For progressive enhancement (plain form POST), redirect back.\n const referer = request.headers.get(\"Referer\") ?? \"/\";\n return new Response(null, {\n status: 303,\n headers: {\n Location: typeof result === \"string\" ? result : referer,\n \"Content-Type\": \"text/plain\",\n },\n });\n } catch (err) {\n console.error(\"[elur-kit] Action error:\", err);\n return publicErrorResponse(err, { includeDetail: false });\n }\n}\n\nexport { verifyOrigin, originForbidden, type OriginCheckOptions } from \"./origin.js\";\nexport {\n decodeActionErrorCookie,\n clearActionErrorCookieHeader,\n setActionErrorCookieHeader,\n ACTION_ERROR_COOKIE,\n} from \"./error-store.js\";\n","import type { IncomingMessage } from \"node:http\";\n\n// Capture the global AbortController at module load time so it's immune to\n// test frameworks that replace or delete globalThis.AbortController.\nconst GlobalAbortController =\n (globalThis as { AbortController?: typeof AbortController }).AbortController ?? AbortController;\n\nexport function incomingMessageToRequest(req: IncomingMessage, body?: BodyInit | null): Request {\n const headers = new Headers();\n for (let index = 0; index < req.rawHeaders.length; index += 2) {\n headers.append(req.rawHeaders[index], req.rawHeaders[index + 1]);\n }\n\n const controller = new GlobalAbortController();\n req.once(\"aborted\", () => controller.abort());\n req.once(\"close\", () => {\n if (!req.complete) controller.abort();\n });\n\n const protocol = (req.socket as typeof req.socket & { encrypted?: boolean }).encrypted ? \"https\" : \"http\";\n const init: RequestInit = {\n method: req.method ?? \"GET\",\n headers,\n signal: controller.signal,\n };\n if (body !== undefined && body !== null && init.method !== \"GET\" && init.method !== \"HEAD\") init.body = body;\n\n return new Request(`${protocol}://${headers.get(\"host\") ?? \"localhost\"}${req.url ?? \"/\"}`, init);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"node-http-DhxguYyz.cjs","names":[],"sources":["../../src/render/ssr-flag.ts","../../src/render/render-to-string.ts","../../src/build/document-shell.ts","../../src/action/error-store.ts","../../src/cache/policy.ts","../../src/ssr/render.ts","../../src/ssr/match.ts","../../src/action/origin.ts","../../src/action/server.ts","../../src/runtime/node-http.ts"],"sourcesContent":["// --- SSR flag utility ---\n//\n// `@elurjs/core` does not export `_setSSR`/`_isSSR`. The reactivity state lives\n// on `globalThis[Symbol.for(\"@elurjs/core/reactivity-state\")]` and the kit owns\n// the `ssr` boolean on it: `renderToString` sets it to `true` while server\n// rendering so `isSSR()` reflects the current render mode for user code\n// (environment reads, client-only guards, ...).\n//\n// This module manipulates that flag directly so the kit does not depend on\n// private exports that may or may not be present in a given elur release.\n\nconst STATE_KEY = Symbol.for(\"@elurjs/core/reactivity-state\");\n\ntype ReactivityState = { ssr?: boolean };\n\nfunction getState(): ReactivityState | undefined {\n return (globalThis as Record<symbol, unknown>)[STATE_KEY] as\n | ReactivityState\n | undefined;\n}\n\n/** Sets the SSR flag on the Elur reactivity state. No-op if state is absent. */\nexport function setSSR(value: boolean): void {\n const state = getState();\n if (state) state.ssr = value;\n}\n\n/** Reads the SSR flag from the Elur reactivity state. Defaults to false. */\nexport function isSSR(): boolean {\n return getState()?.ssr ?? false;\n}\n","import type { ElurTemplate } from \"@elurjs/core\";\nimport { renderToString as renderCoreTemplate } from \"@elurjs/core/server\";\nimport { setSSR } from \"./ssr-flag\";\n\n// --- Build-time / server rendering ---\n//\n// The Elur core ships a DOM-free `renderToString` (`@elurjs/core/server`)\n// that streams template output without ever touching a `document`. The kit used\n// to inject a Node-side DOM (happy-dom) as a fallback for legacy compatibility;\n// that fallback has been removed together with the happy-dom dependency.\n\n/**\n * Renders a Elur template to an HTML string in Node.\n *\n * Accepts a *factory* (not a template) because `html`` evaluates at call time.\n *\n * @param factory Thunk that builds the template, e.g. `() => Page({ data })`.\n * @returns Serialized HTML of the rendered template.\n */\nexport async function renderToString(\n factory: () => ElurTemplate,\n options: { markers?: \"none\" | \"hydration\" } = {},\n): Promise<string> {\n setSSR(true);\n try {\n return await renderCoreTemplate(factory(), {\n markers: options.markers ?? \"hydration\",\n });\n } finally {\n setSSR(false);\n }\n}\n","//\n// The <!DOCTYPE>, <head> and <body> wrapper — plus the serialized loader data\n// and the client entry — are injected here at build time.\n\nimport type { PageMetadata } from \"../types.js\";\nexport interface ShellOptions {\n /** Rendered inner HTML that goes inside `#app`. */\n body: string;\n /** `<title>` text. */\n title?: string;\n /** `<html lang>` attribute. */\n lang?: string;\n /** Additional attributes for the `<html>` element, e.g. `{ \"data-theme\": \"dark\" }`. */\n htmlAttributes?: Record<string, string>;\n /**\n * Inline scripts injected into `<head>`. They run synchronously while the\n * document parses — before the first paint and before the (deferred) client\n * bundle — so they are the right place for no-flash bootstrapping (e.g.\n * applying a stored theme before the page becomes visible).\n */\n headScripts?: string[];\n /**\n * Raw HTML strings injected into `<head>` — e.g. `<link rel=\"icon\">`,\n * `<link rel=\"manifest\">`, `<meta name=\"theme-color\">`. Each string is\n * rendered as-is inside `<head>`.\n */\n headLinks?: string[];\n /** Loader data serialized into `<script id=\"elur-data\">`. */\n data?: unknown;\n /** Per-page action names serialized into `<script id=\"elur-actions\">`. */\n actions?: Record<string, string[]>;\n /** Path to the client entry module, e.g. `/_elur/entry-client.js`. */\n clientEntry?: string;\n /** Page metadata emitted as `<meta>`, `<link>` and OG/Twitter tags in `<head>`. */\n metadata?: PageMetadata;\n /**\n * Whether the SSR render endpoint (`/__elur-js/render`) is available at\n * runtime. Defaults to `true`. When `false` (static deployments), the shell\n * emits `<meta name=\"elur:render-endpoint\" content=\"off\" />` so the client\n * router skips probing the endpoint entirely — preventing a storm of 404\n * requests on fully static sites.\n */\n renderEndpoint?: boolean;\n}\n\nconst HTML_ESCAPES: Record<string, string> = {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n '\"': \""\",\n \"'\": \"'\",\n};\n\nfunction escapeHtml(value: string): string {\n return value.replace(/[&<>\"']/g, (c) => HTML_ESCAPES[c]);\n}\n\n/**\n * Serializes data for embedding inside a `<script>` tag. Escapes `<` so a\n * `</script>` sequence in the data cannot break out of the tag.\n */\nfunction serializeData(data: unknown): string {\n return JSON.stringify(data ?? null).replace(/</g, \"\\\\u003c\");\n}\n\n/**\n * Builds the `<head>` tags for a `PageMetadata` object. Every tag is marked with\n * `data-elur-head` so the client-side router can replace them on navigation\n * without touching charset/viewport or user-supplied `headScripts`.\n */\nexport function buildHeadTags(metadata: PageMetadata, fallbackTitle: string): string {\n const tags: string[] = [];\n const title = metadata.title ?? fallbackTitle;\n if (metadata.title) {\n tags.push(`<title data-elur-head>${escapeHtml(title)}</title>`);\n }\n\n if (metadata.description) {\n tags.push(`<meta data-elur-head name=\"description\" content=\"${escapeHtml(metadata.description)}\" />`);\n }\n\n if (metadata.canonical) {\n tags.push(`<link data-elur-head rel=\"canonical\" href=\"${escapeHtml(metadata.canonical)}\" />`);\n }\n\n if (metadata.robots) {\n tags.push(`<meta data-elur-head name=\"robots\" content=\"${escapeHtml(metadata.robots)}\" />`);\n }\n\n const og = metadata.openGraph;\n if (og) {\n if (og.type) tags.push(`<meta data-elur-head property=\"og:type\" content=\"${escapeHtml(og.type)}\" />`);\n tags.push(`<meta data-elur-head property=\"og:title\" content=\"${escapeHtml(og.title ?? title)}\" />`);\n if (og.description ?? metadata.description) {\n tags.push(`<meta data-elur-head property=\"og:description\" content=\"${escapeHtml(og.description ?? metadata.description!)}\" />`);\n }\n if (og.url ?? metadata.canonical) {\n tags.push(`<meta data-elur-head property=\"og:url\" content=\"${escapeHtml(og.url ?? metadata.canonical!)}\" />`);\n }\n if (og.image) tags.push(`<meta data-elur-head property=\"og:image\" content=\"${escapeHtml(og.image)}\" />`);\n if (og.image && og.imageAlt) tags.push(`<meta data-elur-head property=\"og:image:alt\" content=\"${escapeHtml(og.imageAlt)}\" />`);\n if (og.image && og.imageWidth) tags.push(`<meta data-elur-head property=\"og:image:width\" content=\"${String(og.imageWidth)}\" />`);\n if (og.image && og.imageHeight) tags.push(`<meta data-elur-head property=\"og:image:height\" content=\"${String(og.imageHeight)}\" />`);\n if (og.image && og.imageType) tags.push(`<meta data-elur-head property=\"og:image:type\" content=\"${escapeHtml(og.imageType)}\" />`);\n if (og.siteName) tags.push(`<meta data-elur-head property=\"og:site_name\" content=\"${escapeHtml(og.siteName)}\" />`);\n if (og.locale) tags.push(`<meta data-elur-head property=\"og:locale\" content=\"${escapeHtml(og.locale)}\" />`);\n }\n\n const tw = metadata.twitter;\n if (tw) {\n if (tw.card) tags.push(`<meta data-elur-head name=\"twitter:card\" content=\"${escapeHtml(tw.card)}\" />`);\n if (tw.title ?? title) tags.push(`<meta data-elur-head name=\"twitter:title\" content=\"${escapeHtml(tw.title ?? title)}\" />`);\n if (tw.description ?? metadata.description) {\n tags.push(`<meta data-elur-head name=\"twitter:description\" content=\"${escapeHtml(tw.description ?? metadata.description!)}\" />`);\n }\n if (tw.image) tags.push(`<meta data-elur-head name=\"twitter:image\" content=\"${escapeHtml(tw.image)}\" />`);\n if (tw.image && tw.imageAlt) tags.push(`<meta data-elur-head name=\"twitter:image:alt\" content=\"${escapeHtml(tw.imageAlt)}\" />`);\n }\n\n if (metadata.other) {\n for (const [name, content] of Object.entries(metadata.other)) {\n tags.push(`<meta data-elur-head name=\"${escapeHtml(name)}\" content=\"${escapeHtml(content)}\" />`);\n }\n }\n\n return tags.map((t) => `\\n ${t}`).join(\"\");\n}\n\n/** Wraps rendered body HTML into a full HTML document. */\nexport function documentShell(opts: ShellOptions): string {\n const { body, title = \"Elur Kit App\", lang = \"es\", data, actions, clientEntry, htmlAttributes, headScripts, headLinks, metadata } = opts;\n\n const dataScript =\n data !== undefined\n ? `\\n <script type=\"application/json\" id=\"elur-data\">${serializeData(data)}</script>`\n : \"\";\n\n const actionsScript = actions && Object.keys(actions).length > 0\n ? `\\n <script type=\"application/json\" id=\"elur-actions\">${serializeData(actions)}</script>`\n : \"\";\n\n const entryScript = clientEntry\n ? `\\n <script type=\"module\" src=\"${escapeHtml(clientEntry)}\"></script>`\n : \"\";\n\n const htmlAttrs = htmlAttributes\n ? Object.entries(htmlAttributes)\n .filter(([, value]) => value !== undefined && value !== null && value !== \"\")\n .map(([key, value]) => ` ${escapeHtml(key)}=\"${escapeHtml(String(value))}\"`)\n .join(\"\")\n : \"\";\n\n const headScriptsHtml = headScripts\n ? headScripts\n .filter((script) => typeof script === \"string\" && script.trim().length > 0)\n .map((script) => {\n // If the script is already a complete <script> tag (e.g. JSON-LD),\n // render it as-is without wrapping.\n if (script.trimStart().startsWith(\"<script\")) {\n return `\\n ${script}`;\n }\n return `\\n <script>${script.replace(/<\\/script>/gi, \"<\\\\/script>\")}</script>`;\n })\n .join(\"\")\n : \"\";\n\n const headTags = metadata ? buildHeadTags(metadata, title) : \"\";\n const titleTag = metadata?.title\n ? \"\" // already emitted by buildHeadTags\n : `\\n <title>${escapeHtml(title)}</title>`;\n\n const headLinksHtml = headLinks\n ? headLinks\n .filter((link) => typeof link === \"string\" && link.trim().length > 0)\n .map((link) => `\\n ${link}`)\n .join(\"\")\n : \"\";\n\n const renderEndpointMeta =\n opts.renderEndpoint === false\n ? '\\n <meta name=\"elur:render-endpoint\" content=\"off\" />'\n : \"\";\n\n return `<!DOCTYPE html>\n<html lang=\"${escapeHtml(lang)}\"${htmlAttrs}>\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />${renderEndpointMeta}${titleTag}${headTags}${headLinksHtml}${headScriptsHtml}\n </head>\n <body>\n <div id=\"app\">${body}</div>${dataScript}${actionsScript}${entryScript}\n </body>\n</html>\n`;\n}\n","// --- Ephemeral action error store ---\n//\n// Action failures submitted via plain HTML forms (progressive enhancement)\n// need to be relayed back to the page so the user sees validation errors.\n//\n// Previously the failure data was serialized into a `?__elur_js_action_error=`\n// query param on the redirect. That leaks errors into browser history,\n// server logs and third-party Referer headers.\n//\n// Now we stash the failure in a short-lived in-memory store keyed by a random\n// id, set a small cookie `__elur_js_action_error=<id>` (Max-Age=15s, SameSite=Lax),\n// and the next render reads the cookie, fetches the payload, exposes it as\n// `props.form`, and clears the entry.\n//\n// The store is process-local, which is fine for the single-process SSR server\n// and the dev server. For multi-instance deployments the cookie carries the\n// payload directly when it fits (see `encodeActionErrorCookie`); the store is\n// only the overflow path for large payloads.\n\nimport { createHmac, randomBytes, timingSafeEqual } from \"node:crypto\";\n\nconst COOKIE_NAME = \"__elur_js_action_error\";\nconst MAX_COOKIE_SIZE = 3500; // bytes; leaves headroom under the 4KB cookie limit\nconst TTL_MS = 15_000;\n\n// HMAC key for signing action error cookies. In production this should be\n// set via ELUR_JS_ACTION_SECRET env var; otherwise we derive a per-process\n// key (sufficient for single-process dev/preview, but NOT for multi-instance).\nconst ACTION_SECRET =\n process.env.ELUR_JS_ACTION_SECRET ?? randomBytes(32).toString(\"hex\");\n\ninterface StoredError {\n data: unknown;\n status: number;\n expiresAt: number;\n}\n\nconst store = new Map<string, StoredError>();\n\n// Periodically purge expired entries so the map does not grow unbounded.\nlet sweepScheduled = false;\nfunction scheduleSweep(): void {\n if (sweepScheduled) return;\n sweepScheduled = true;\n setTimeout(() => {\n sweepScheduled = false;\n const now = Date.now();\n for (const [key, entry] of store) {\n if (entry.expiresAt <= now) store.delete(key);\n }\n }, TTL_MS).unref?.();\n}\n\n/**\n * Signs a payload with HMAC-SHA256 using the action secret.\n * Returns `signature.payload` (both hex/base64url).\n */\nfunction sign(payload: string): string {\n const sig = createHmac(\"sha256\", ACTION_SECRET).update(payload).digest(\"hex\");\n return `${sig}.${payload}`;\n}\n\n/**\n * Verifies a signed value and returns the payload if valid, or undefined.\n * Uses timingSafeEqual to prevent timing attacks.\n */\nfunction verify(value: string): string | undefined {\n const dotIndex = value.indexOf(\".\");\n if (dotIndex === -1) return undefined;\n const sig = value.slice(0, dotIndex);\n const payload = value.slice(dotIndex + 1);\n const expectedSig = createHmac(\"sha256\", ACTION_SECRET).update(payload).digest(\"hex\");\n if (sig.length !== expectedSig.length) return undefined;\n try {\n if (timingSafeEqual(Buffer.from(sig), Buffer.from(expectedSig))) {\n return payload;\n }\n } catch {\n // Length mismatch — invalid.\n }\n return undefined;\n}\n\n/**\n * Encodes an action failure for the redirect cookie. When the payload fits\n * inside the cookie limit, it is embedded directly as a signed base64url JSON\n * value. When it is too large, it is stored in memory and only a short signed\n * id is written to the cookie.\n *\n * The cookie is signed with HMAC-SHA256 to prevent forgery (A-20).\n *\n * @returns The cookie value to set on the redirect response.\n */\nexport function encodeActionErrorCookie(\n data: unknown,\n status: number,\n): { value: string; storeId?: string } {\n const payload = JSON.stringify({ d: data, s: status });\n const encoded = Buffer.from(payload, \"utf8\").toString(\"base64url\");\n const signed = sign(encoded);\n if (signed.length <= MAX_COOKIE_SIZE) {\n return { value: signed };\n }\n\n // Overflow: stash in memory and reference by signed id.\n const id = randomBytes(12).toString(\"hex\");\n store.set(id, { data, status, expiresAt: Date.now() + TTL_MS });\n scheduleSweep();\n return { value: sign(`id:${id}`), storeId: id };\n}\n\n/**\n * Decodes a cookie value (previously produced by `encodeActionErrorCookie`)\n * into the failure payload. Verifies the HMAC signature first, then resolves\n * in-memory overflow entries and deletes them after reading.\n */\nexport function decodeActionErrorCookie(value: string | undefined | null):\n | { data: unknown; status: number }\n | undefined {\n if (!value) return undefined;\n\n // Verify signature first.\n const verifiedPayload = verify(value);\n if (verifiedPayload === undefined) return undefined;\n\n // Check if it's an in-memory store reference.\n if (verifiedPayload.startsWith(\"id:\")) {\n const id = verifiedPayload.slice(3);\n const entry = store.get(id);\n if (!entry) return undefined;\n store.delete(id);\n if (entry.expiresAt <= Date.now()) return undefined;\n return { data: entry.data, status: entry.status };\n }\n\n try {\n const json = Buffer.from(verifiedPayload, \"base64url\").toString(\"utf8\");\n const parsed = JSON.parse(json) as { d: unknown; s: number };\n return { data: parsed.d, status: parsed.s };\n } catch {\n return undefined;\n }\n}\n\n/** Name of the cookie used to relay action errors. */\nexport const ACTION_ERROR_COOKIE = COOKIE_NAME;\n\n/** Builds the Set-Cookie header value that clears the error cookie. */\nexport function clearActionErrorCookieHeader(): string {\n return `${COOKIE_NAME}=; Path=/; Max-Age=0; SameSite=Lax`;\n}\n\n/** Builds the Set-Cookie header value that sets the error cookie. */\nexport function setActionErrorCookieHeader(value: string): string {\n return `${COOKIE_NAME}=${value}; Path=/; Max-Age=15; SameSite=Lax; HttpOnly`;\n}\n","// --- Cache policy per route (runtime-security §9.1) ---\n//\n// Authors can declare a cache policy in their page.data.ts:\n//\n// export const cache = {\n// mode: \"public\", // \"public\" | \"private\" | \"dynamic\"\n// revalidate: 60, // seconds\n// tags: [\"products\"], // for tag-based invalidation\n// };\n//\n// Default policy: \"dynamic\" (no public ISR caching).\n// Requests with Cookie/Authorization are never cached publicly.\n// Responses with Set-Cookie/private/no-store are never cached publicly.\n\n/** Cache mode for a route. */\nexport type CacheMode = \"public\" | \"private\" | \"dynamic\";\n\n/** Cache policy declared by the route's data module. */\nexport interface CachePolicy {\n mode: CacheMode;\n revalidate: number;\n tags?: string[];\n}\n\n/** Default cache policy when none is declared. */\nexport const DEFAULT_CACHE_POLICY: CachePolicy = {\n mode: \"dynamic\",\n revalidate: 0,\n};\n\n/**\n * Normalizes a raw cache export from a data module into a CachePolicy.\n * Returns the default policy if the input is invalid or missing.\n */\nexport function normalizeCachePolicy(raw: unknown): CachePolicy {\n if (!raw || typeof raw !== \"object\") return DEFAULT_CACHE_POLICY;\n const obj = raw as Record<string, unknown>;\n const mode = obj.mode;\n if (mode !== \"public\" && mode !== \"private\" && mode !== \"dynamic\") {\n return DEFAULT_CACHE_POLICY;\n }\n const revalidate = typeof obj.revalidate === \"number\" ? obj.revalidate : 0;\n const tags = Array.isArray(obj.tags) ? obj.tags.filter((t) => typeof t === \"string\") : undefined;\n return { mode, revalidate, tags };\n}\n\n/**\n * Determines whether a route's cache policy allows public caching for the\n * given request.\n *\n * Per §9.1:\n * - \"dynamic\" → never cache\n * - \"private\" → never cache publicly (requires private adapter)\n * - \"public\" → cache only if request has no Cookie/Authorization\n */\nexport function shouldCachePublic(\n policy: CachePolicy,\n request: Request,\n): boolean {\n if (policy.mode !== \"public\") return false;\n if (policy.revalidate <= 0) return false;\n if (request.headers.get(\"Cookie\")) return false;\n if (request.headers.get(\"Authorization\")) return false;\n return true;\n}\n","import type { ElurTemplate } from \"@elurjs/core\";\nimport { renderToString } from \"../render/render-to-string.js\";\nimport { documentShell, buildHeadTags } from \"../build/document-shell.js\";\nimport type { PageRoute, ScannedRoutes } from \"../router/route-scanner.js\";\nimport type { BuildConfig } from \"../build/build.js\";\nimport type { PageDataLoad, PageProps, RouteParams, PageMetadata, GenerateMetadata } from \"../types.js\";\nimport { existsSync } from \"node:fs\";\nimport { decodeActionErrorCookie, ACTION_ERROR_COOKIE } from \"../action/error-store.js\";\nimport { normalizeCachePolicy, type CachePolicy } from \"../cache/policy.js\";\n\nexport interface RenderPageOptions {\n route: PageRoute;\n params?: RouteParams;\n searchParams?: URLSearchParams;\n config: Pick<BuildConfig, \"lang\" | \"clientEntry\" | \"renderEndpoint\">;\n /** Custom module loader. Defaults to native dynamic import. */\n importer?: (path: string) => Promise<unknown>;\n /** Per-page action names exposed in the HTML shell. */\n actions?: Record<string, string[]>;\n /** Current request, used to hydrate data loaders that need cookies/headers. */\n request?: Request;\n}\n\nexport interface RenderPageResult {\n html: string;\n revalidate?: number;\n /**\n * `Set-Cookie` header value that clears the action error cookie, when the\n * page consumed a relayed action failure. The SSR server should append it to\n * the outgoing response so the cookie does not persist.\n */\n clearActionErrorCookie?: string;\n /** `<head>` tags (title, meta, OG, twitter) for the SPA router to merge. */\n head?: string;\n /** Resolved page title (from metadata or fallback). */\n resolvedTitle?: string;\n /**\n * When a loader or layout throws a `Response` (e.g. `throw new Response(...,\n * { status: 404 })`), it is captured here as a first-class response instead\n * of being treated as an internal error (A-22).\n */\n response?: Response;\n /** HTTP status code for the rendered page (e.g. 404 for not-found pages). */\n status?: number;\n /** Cache policy declared by the route (§9.1). */\n cachePolicy?: CachePolicy;\n}\n\nconst defaultImport = (path: string) => import(path);\n\n/**\n * Collects `<html>` attributes and head scripts declared by data loaders\n * (page and layouts) via top-level `htmlAttributes` / `headScripts` fields.\n */\nexport function collectShellExtras(\n pageData: unknown,\n layoutDataList: unknown[],\n): { htmlAttributes: Record<string, string>; headScripts: string[]; headLinks: string[] } {\n const htmlAttributes: Record<string, string> = {};\n const headScripts: string[] = [];\n const headLinks: string[] = [];\n const merge = (value: unknown) => {\n if (!value || typeof value !== \"object\") return;\n const attrs = (value as { htmlAttributes?: Record<string, string> }).htmlAttributes;\n if (attrs) Object.assign(htmlAttributes, attrs);\n const scripts = (value as { headScripts?: string[] }).headScripts;\n if (Array.isArray(scripts)) headScripts.push(...scripts);\n const links = (value as { headLinks?: string[] }).headLinks;\n if (Array.isArray(links)) headLinks.push(...links);\n };\n for (const layoutData of layoutDataList) merge(layoutData);\n merge(pageData);\n // Deduplicate headScripts and headLinks (e.g. from both layout and page data)\n const uniqueScripts = [...new Set(headScripts)];\n const uniqueLinks = [...new Set(headLinks)];\n return { htmlAttributes, headScripts: uniqueScripts, headLinks: uniqueLinks };\n}\n\nexport async function renderPage(options: RenderPageOptions): Promise<RenderPageResult> {\n const { route, params = {}, searchParams = new URLSearchParams(), config, importer = defaultImport, actions, request } = options;\n\n const pageModule = await importer(route.pagePath) as {\n default: (props: PageProps<unknown>) => ElurTemplate;\n generateMetadata?: GenerateMetadata;\n };\n const { default: PageComponent, generateMetadata } = pageModule;\n\n let data: unknown;\n let revalidate: number | undefined;\n let cachePolicy: import(\"../cache/policy.js\").CachePolicy | undefined;\n // Use a mutable container so TypeScript doesn't narrow the type after\n // the first `if (thrownResponse)` check.\n const thrown: { response: Response | undefined } = { response: undefined };\n if (route.dataPath) {\n const mod = await importer(route.dataPath) as {\n load?: PageDataLoad;\n revalidate?: number;\n cache?: unknown;\n };\n if (mod.load) {\n try {\n data = await mod.load({ params, searchParams, request });\n } catch (err) {\n if (err instanceof Response) {\n thrown.response = err;\n } else {\n throw err;\n }\n }\n }\n if (typeof mod.revalidate === \"number\") {\n revalidate = mod.revalidate;\n }\n // Read cache policy from the data module (§9.1).\n if (mod.cache) {\n cachePolicy = normalizeCachePolicy(mod.cache);\n if (cachePolicy.revalidate > 0) {\n revalidate = cachePolicy.revalidate;\n }\n }\n }\n\n // If a loader threw a Response (redirect, 404, etc.), return it as a\n // first-class response instead of rendering the page (A-22).\n if (thrown.response) {\n return { html: \"\", response: thrown.response, status: thrown.response.status };\n }\n\n // Relay an action failure previously stored in the ephemeral cookie so the\n // page can render validation errors via `props.form`. The cookie is cleared\n // on the outgoing response (see `clearActionErrorCookie` in the result).\n let form: unknown;\n let clearActionErrorCookie: string | undefined;\n if (request) {\n const cookieHeader = request.headers.get(\"Cookie\") ?? \"\";\n const match = cookieHeader.match(new RegExp(`(?:^|;\\\\s*)${ACTION_ERROR_COOKIE}=([^;]+)`));\n if (match) {\n const decoded = decodeActionErrorCookie(match[1]);\n if (decoded) {\n form = { __elur_js_action_error: true, status: decoded.status, data: decoded.data };\n clearActionErrorCookie = `${ACTION_ERROR_COOKIE}=; Path=/; Max-Age=0; SameSite=Lax`;\n }\n }\n }\n\n const props: PageProps<unknown> = {\n data: data ?? {},\n params,\n searchParams,\n form,\n };\n\n const layoutModules = await Promise.all(\n route.layouts.map(async (layoutPath) => importer(layoutPath)),\n );\n const layoutDataList = await Promise.all(\n route.layouts.map(async (layoutPath) => {\n const dataPath = layoutPath.replace(/layout\\.ts$/, \"layout.data.ts\");\n if (!existsSync(dataPath)) return undefined;\n const mod = (await importer(dataPath)) as { load?: PageDataLoad };\n if (mod.load) {\n try {\n return await mod.load({ params, searchParams, request });\n } catch (err) {\n if (err instanceof Response) {\n thrown.response = err;\n return undefined;\n }\n throw err;\n }\n }\n return undefined;\n }),\n );\n\n // If a layout loader threw a Response, return it as first-class (A-22).\n const layoutThrown = thrown.response as Response | undefined;\n if (layoutThrown) {\n return { html: \"\", response: layoutThrown, status: layoutThrown.status };\n }\n\n // Load slot modules if the route has them (v2.1 — Fix #2: Layout Slots).\n let slotTemplates: Record<string, ElurTemplate> | undefined;\n if (route.slots) {\n slotTemplates = {};\n for (const [slotName, slotPath] of Object.entries(route.slots)) {\n const slotMod = await importer(slotPath) as { default: (props: PageProps<unknown>) => ElurTemplate };\n slotTemplates[slotName] = slotMod.default(props);\n }\n }\n\n const body = await renderToString(() => {\n let template = PageComponent(props);\n for (let i = layoutModules.length - 1; i >= 0; i--) {\n const { default: Layout } = layoutModules[i] as {\n default: (props: { children: ElurTemplate; data?: unknown; slots?: Record<string, ElurTemplate> }) => ElurTemplate;\n };\n template = Layout({ children: template, data: layoutDataList[i], slots: slotTemplates });\n }\n return template;\n });\n\n const title = typeof data === \"object\" && data && \"title\" in data\n ? String((data as { title?: unknown }).title ?? \"Elur Kit\")\n : \"Elur Kit\";\n\n const { htmlAttributes, headScripts, headLinks } = collectShellExtras(data, layoutDataList);\n\n // Resolve page metadata. Priority: `generateMetadata` from page.ts > `metadata`\n // field in the page loader data > `metadata` field in layout loader data.\n let metadata: PageMetadata | undefined;\n if (typeof generateMetadata === \"function\") {\n metadata = await generateMetadata({ params, searchParams, request, data });\n }\n if (!metadata) {\n metadata = extractMetadata(data) ?? extractMetadataFromList(layoutDataList);\n }\n // The title from metadata takes precedence over the data.title fallback.\n const resolvedTitle = metadata?.title ?? title;\n\n const html = documentShell({\n title: resolvedTitle,\n lang: config.lang,\n body,\n data,\n actions,\n htmlAttributes,\n headScripts,\n headLinks,\n metadata,\n clientEntry: config.clientEntry,\n renderEndpoint: config.renderEndpoint,\n });\n\n const head = metadata ? buildHeadTags(metadata, resolvedTitle) : \"\";\n return { html, revalidate, clearActionErrorCookie, head, resolvedTitle, cachePolicy };\n}\n\n/** Extracts a `metadata` field from a loader data object, if present. */\nfunction extractMetadata(value: unknown): PageMetadata | undefined {\n if (value && typeof value === \"object\" && \"metadata\" in value) {\n const meta = (value as { metadata?: unknown }).metadata;\n if (meta && typeof meta === \"object\") return meta as PageMetadata;\n }\n return undefined;\n}\n\n/** Extracts metadata from the first layout data object that has one. */\nfunction extractMetadataFromList(list: unknown[]): PageMetadata | undefined {\n for (const item of list) {\n const meta = extractMetadata(item);\n if (meta) return meta;\n }\n return undefined;\n}\n\nexport interface RenderErrorPageOptions {\n routes: ScannedRoutes;\n status: 404 | 500;\n error?: unknown;\n config: Pick<BuildConfig, \"lang\" | \"clientEntry\" | \"renderEndpoint\">;\n actions?: Record<string, string[]>;\n importer?: (path: string) => Promise<unknown>;\n}\n\nexport async function renderErrorPage(\n options: RenderErrorPageOptions,\n): Promise<{ html: string; status: number } | undefined> {\n const route = options.status === 404 ? options.routes.error404 : options.routes.error500;\n if (!route) return undefined;\n\n try {\n const { html } = await renderPage({\n route,\n params: {},\n searchParams: new URLSearchParams(),\n config: options.config,\n actions: options.actions,\n importer: options.importer,\n });\n return { html, status: options.status };\n } catch (err) {\n console.error(`[render] error ${options.status} page failed`, err);\n return undefined;\n }\n}\n","import type { ApiRoute, PageRoute } from \"../router/route-scanner.js\";\n\nexport interface MatchResult {\n route: PageRoute;\n params: Record<string, string | string[]>;\n searchParams: URLSearchParams;\n}\n\n/**\n * Match a request pathname against a list of page routes.\n *\n * Routes are sorted by specificity (static > dynamic > catch-all) before\n * matching, so `/about` wins over `/:slug` even if the catch-all appears first.\n *\n * URL segments are safely decoded (plan §11.1, runtime-security §10).\n */\nexport function matchRoute(\n pathname: string,\n routes: PageRoute[],\n): MatchResult | undefined {\n const cleanPath = pathname.split(\"?\")[0];\n const requestSegments = cleanPath.split(\"/\").filter(Boolean).map(safeDecodeURIComponent);\n\n const sorted = [...routes].sort((a, b) => specificity(b.path) - specificity(a.path));\n\n for (const route of sorted) {\n const routeSegments = route.path.split(\"/\").filter(Boolean);\n const match = tryMatch(requestSegments, routeSegments, route.optionalCatchAll);\n if (match) {\n return { route, params: match, searchParams: new URLSearchParams() };\n }\n }\n\n return undefined;\n}\n\nexport interface ApiMatchResult<T = ApiRoute> {\n route: T;\n params: Record<string, string | string[]>;\n}\n\n/**\n * Match a request pathname against a list of API routes.\n */\nexport function matchApiRoute<T extends { path: string }>(pathname: string, routes: T[]): ApiMatchResult<T> | undefined {\n const cleanPath = pathname.split(\"?\")[0];\n const requestSegments = cleanPath.split(\"/\").filter(Boolean).map(safeDecodeURIComponent);\n\n const sorted = [...routes].sort((a, b) => specificity(b.path) - specificity(a.path));\n\n for (const route of sorted) {\n const routeSegments = route.path.split(\"/\").filter(Boolean);\n const match = tryMatch(requestSegments, routeSegments);\n if (match) {\n return { route, params: match };\n }\n }\n\n return undefined;\n}\n\n/**\n * Safely decodes a URI component. If decoding fails (malformed % sequences),\n * returns the original string rather than throwing (runtime-security §10).\n */\nfunction safeDecodeURIComponent(segment: string): string {\n try {\n return decodeURIComponent(segment);\n } catch {\n return segment;\n }\n}\n\nfunction specificity(path: string): number {\n return path.split(\"/\").filter(Boolean).reduce((score, segment) => {\n if (segment.endsWith(\"*\")) return score;\n if (segment.startsWith(\":\")) return score + 1;\n return score + 2;\n }, 0);\n}\n\nfunction tryMatch(\n requestSegments: string[],\n routeSegments: string[],\n optionalCatchAll = false,\n): Record<string, string | string[]> | undefined {\n const params: Record<string, string | string[]> = {};\n\n let i = 0;\n for (let r = 0; r < routeSegments.length; r++) {\n const routeSeg = routeSegments[r];\n\n if (routeSeg.endsWith(\"*\")) {\n // Catch-all consumes the rest of the request segments.\n const name = routeSeg.slice(1, -1);\n const rest = requestSegments.slice(i);\n // For optional catch-all, empty rest is OK.\n if (rest.length === 0 && !optionalCatchAll) return undefined;\n params[name] = rest.length > 0 ? rest : [];\n return params;\n }\n\n if (routeSeg.startsWith(\":\")) {\n const requestSeg = requestSegments[i];\n if (requestSeg === undefined) return undefined;\n params[routeSeg.slice(1)] = requestSeg;\n i++;\n continue;\n }\n\n if (routeSeg !== requestSegments[i]) {\n return undefined;\n }\n i++;\n }\n\n if (i !== requestSegments.length) return undefined;\n return params;\n}\n","// --- Origin verification (CSRF protection for server actions) ---\n//\n// Server actions accept POST requests from the browser. Without origin\n// verification, any third-party site could submit forged requests to\n// `/__elur-js/actions` on behalf of a logged-in user (CSRF).\n//\n// Strategy: compare the request's `Origin` (or `Referer` fallback) host against\n// the target `Host` header. Same-origin requests pass; cross-origin requests\n// are rejected with 403 unless the origin is explicitly allow-listed.\n//\n// Requests without `Origin` AND without `Referer` (e.g. curl, server-to-server)\n// are accepted by default for DX, unless `strictOrigin: true` is configured.\n\nexport interface OriginCheckOptions {\n /** Extra origins allowed to call actions (e.g. preview deployments). */\n allowedOrigins?: string[];\n /**\n * When true, requests missing both `Origin` and `Referer` are rejected.\n * Defaults to false so curl/server-to-server calls keep working.\n */\n strictOrigin?: boolean;\n}\n\n/**\n * Returns the host:port of a URL string, or undefined if it cannot be parsed.\n */\nfunction originOf(urlString: string | null | undefined): string | undefined {\n if (!urlString) return undefined;\n try {\n const url = new URL(urlString);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return undefined;\n return url.origin;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Verifies that a request originates from the same host (or an allow-listed\n * origin). Returns an error message when the request must be rejected, or\n * undefined when it is allowed.\n *\n * @param request The incoming Request to actions.\n * @param options Origin check configuration.\n */\nexport function verifyOrigin(\n request: Request,\n options: OriginCheckOptions = {},\n): string | undefined {\n const targetOrigin = originOf(request.url);\n if (!targetOrigin) return \"Invalid target URL\";\n\n const origin = request.headers.get(\"Origin\");\n const referer = request.headers.get(\"Referer\");\n if (!origin && !referer) {\n return options.strictOrigin\n ? \"Missing Origin and Referer headers\"\n : undefined;\n }\n\n const sourceOrigin = origin ? originOf(origin) : originOf(referer);\n if (!sourceOrigin) return origin ? \"Invalid Origin header\" : \"Invalid Referer header\";\n if (sourceOrigin === targetOrigin) return undefined;\n\n if (options.allowedOrigins?.some((allowed) => originOf(allowed) === sourceOrigin)) return undefined;\n\n return `Cross-origin request blocked: source \"${sourceOrigin}\" != target \"${targetOrigin}\"`;\n}\n\n/** Builds a 403 Response for a rejected origin. */\nexport function originForbidden(message: string): Response {\n return new Response(message, {\n status: 403,\n headers: { \"Content-Type\": \"text/plain; charset=utf-8\" },\n });\n}\n","import type { ActionRequest } from \"./index.js\";\nimport { isActionFailure, isRedirectResponse, publicErrorResponse } from \"../errors.js\";\nimport { verifyOrigin, originForbidden, type OriginCheckOptions } from \"./origin.js\";\nimport {\n encodeActionErrorCookie,\n setActionErrorCookieHeader,\n} from \"./error-store.js\";\n\n/**\n * Resolves a server action by name and optional page scope.\n */\nexport type ActionResolver = (\n name: string,\n page?: string,\n) => Promise<((...args: unknown[]) => unknown) | undefined>;\n\n/** Options shared by `handleActionRequest` callers for CSRF protection. */\nexport interface ActionSecurityOptions extends OriginCheckOptions {\n /** Maximum body size in bytes. Defaults to 1MB (1_048_576). */\n bodyLimit?: number;\n}\n\n/** Default body size limit: 1MB. */\nconst DEFAULT_BODY_LIMIT = 1_048_576;\n\n/**\n * Reads the request body as text, enforcing a maximum size.\n * Returns a 413 response if the body exceeds the limit.\n */\nasync function readBodyWithLimit(\n request: Request,\n limit: number,\n): Promise<{ ok: true; text: string } | { ok: false; response: Response }> {\n const contentLength = request.headers.get(\"Content-Length\");\n if (contentLength && parseInt(contentLength, 10) > limit) {\n return {\n ok: false,\n response: new Response(\"Request body too large\", {\n status: 413,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n // Read the body as a stream with a size cap to prevent memory exhaustion\n // from chunked transfer encoding without Content-Length.\n const reader = request.body?.getReader();\n if (!reader) {\n return { ok: true, text: \"\" };\n }\n const chunks: Uint8Array[] = [];\n let totalSize = 0;\n try {\n for (; ;) {\n const { done, value } = await reader.read();\n if (done) break;\n totalSize += value.byteLength;\n if (totalSize > limit) {\n try { reader.cancel(); } catch { /* ignore */ }\n return {\n ok: false,\n response: new Response(\"Request body too large\", {\n status: 413,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n chunks.push(value);\n }\n } finally {\n try { reader.releaseLock(); } catch { /* ignore */ }\n }\n const total = new Uint8Array(totalSize);\n let offset = 0;\n for (const chunk of chunks) {\n total.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return { ok: true, text: new TextDecoder().decode(total) };\n}\n\nfunction parseFormBody(body: string): Record<string, unknown> {\n const params = new URLSearchParams(body);\n const result: Record<string, unknown> = {};\n for (const [key, value] of params) {\n if (result[key] === undefined) {\n result[key] = value;\n } else if (Array.isArray(result[key])) {\n (result[key] as unknown[]).push(value);\n } else {\n result[key] = [result[key], value];\n }\n }\n return result;\n}\n\nasync function parseActionRequest(\n request: Request,\n bodyLimit: number = DEFAULT_BODY_LIMIT,\n): Promise<\n | { ok: true; name: string; page?: string; args: unknown[]; wantsJson: boolean }\n | { ok: false; response: Response }\n> {\n if (request.method !== \"POST\") {\n return {\n ok: false,\n response: new Response(\"Method not allowed\", {\n status: 405,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n\n const contentType = request.headers.get(\"Content-Type\") ?? \"\";\n const wantsJson = (request.headers.get(\"Accept\") ?? \"\").includes(\"application/json\");\n\n let name: string | undefined;\n let page: string | undefined;\n let args: unknown[] = [];\n\n if (contentType.includes(\"application/json\")) {\n const bodyResult = await readBodyWithLimit(request, bodyLimit);\n if (!bodyResult.ok) return { ok: false, response: bodyResult.response };\n let body: ActionRequest;\n try {\n body = JSON.parse(bodyResult.text) as ActionRequest;\n } catch {\n return {\n ok: false,\n response: new Response(\"Invalid JSON body\", {\n status: 400,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n name = body.name;\n page = body.page;\n args = Array.isArray(body.args) ? body.args : [];\n } else if (\n contentType.includes(\"application/x-www-form-urlencoded\") ||\n contentType.includes(\"multipart/form-data\")\n ) {\n // For multipart, use the native formData() parser after checking\n // Content-Length against the limit. For urlencoded, use our size-capped\n // reader to handle chunked encoding without Content-Length.\n if (contentType.includes(\"multipart/form-data\")) {\n const contentLength = request.headers.get(\"Content-Length\");\n if (contentLength && parseInt(contentLength, 10) > bodyLimit) {\n return {\n ok: false,\n response: new Response(\"Request body too large\", {\n status: 413,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n let form: FormData;\n try {\n form = await request.formData();\n } catch {\n return {\n ok: false,\n response: new Response(\"Invalid form body\", {\n status: 400,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n name = form.get(\"__elur_js_action_name\") as string | null ?? undefined;\n page = form.get(\"__elur_js_action_page\") as string | null ?? undefined;\n const input: Record<string, unknown> = {};\n for (const [key, value] of form) {\n if (key === \"__elur_js_action_name\" || key === \"__elur_js_action_page\") continue;\n input[key] = value;\n }\n args = [input];\n } else {\n const bodyResult = await readBodyWithLimit(request, bodyLimit);\n if (!bodyResult.ok) return { ok: false, response: bodyResult.response };\n const form = parseFormBody(bodyResult.text);\n name = form.__elur_js_action_name as string | undefined;\n page = form.__elur_js_action_page as string | undefined;\n const input: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(form)) {\n if (key === \"__elur_js_action_name\" || key === \"__elur_js_action_page\") continue;\n input[key] = value;\n }\n args = [input];\n }\n } else {\n // Try to parse a plain form body as a fallback for progressive enhancement.\n const bodyResult = await readBodyWithLimit(request, bodyLimit);\n if (!bodyResult.ok) return { ok: false, response: bodyResult.response };\n const form = parseFormBody(bodyResult.text);\n name = form.__elur_js_action_name as string | undefined;\n page = form.__elur_js_action_page as string | undefined;\n const input: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(form)) {\n if (key === \"__elur_js_action_name\" || key === \"__elur_js_action_page\") continue;\n input[key] = value;\n }\n args = [input];\n }\n\n if (!name || typeof name !== \"string\") {\n return {\n ok: false,\n response: new Response(\"Missing action name\", {\n status: 400,\n headers: { \"Content-Type\": \"text/plain\" },\n }),\n };\n }\n\n return { ok: true, name, page, args, wantsJson };\n}\n\n/**\n * Handles a POST request to the server action endpoint.\n *\n * Accepts both JSON requests (`{ name, page?, args }`) and HTML form submissions\n * for progressive enhancement. The provided resolver looks up the action\n * implementation, invokes it with the supplied arguments and returns the result\n * as JSON or redirects back to the request origin for form submissions.\n *\n * Origin verification (CSRF protection) runs before parsing the body: any\n * cross-origin POST is rejected with 403 unless its origin is allow-listed via\n * `security.allowedOrigins`.\n *\n * For progressive-enhancement form submissions that fail, the failure payload\n * is relayed back via a short-lived `__elur_js_action_error` cookie (SameSite=Lax,\n * Max-Age=15s) instead of a query param, so errors do not leak into browser\n * history, server logs or third-party Referer headers.\n */\nexport async function handleActionRequest(\n request: Request,\n resolveAction: ActionResolver,\n security: ActionSecurityOptions = {},\n): Promise<Response> {\n // CSRF: verify same-origin (or allow-listed) before doing any work.\n const originError = verifyOrigin(request, security);\n if (originError) return originForbidden(originError);\n\n const parsed = await parseActionRequest(request, security.bodyLimit ?? DEFAULT_BODY_LIMIT);\n if (!parsed.ok) return parsed.response;\n\n const { name, page, args, wantsJson } = parsed;\n\n try {\n const action = await resolveAction(name, page);\n if (!action) {\n const message = page ? `Action not found: ${name} (page: ${page})` : `Action not found: ${name}`;\n return new Response(message, {\n status: 404,\n headers: { \"Content-Type\": \"text/plain\" },\n });\n }\n\n const result = await action(...args);\n\n if (isActionFailure(result)) {\n if (wantsJson) {\n return new Response(JSON.stringify({ __elur_js_action_failure: true, status: result.status, data: result.data }), {\n status: result.status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n // Progressive enhancement: redirect back with the failure in a cookie.\n const referer = request.headers.get(\"Referer\") ?? \"/\";\n const url = new URL(referer, \"http://localhost\");\n const { value } = encodeActionErrorCookie(result.data, result.status);\n return new Response(null, {\n status: 303,\n headers: {\n Location: url.pathname + url.search,\n \"Content-Type\": \"text/plain\",\n \"Set-Cookie\": setActionErrorCookieHeader(value),\n },\n });\n }\n\n if (isRedirectResponse(result)) {\n if (wantsJson) {\n return new Response(\n JSON.stringify({ __elur_js_action_redirect: true, status: result.status, location: result.location }),\n {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n },\n );\n }\n return new Response(null, {\n status: result.status,\n headers: { Location: result.location, \"Content-Type\": \"text/plain\" },\n });\n }\n\n if (wantsJson) {\n return new Response(JSON.stringify(result ?? null), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n // For progressive enhancement (plain form POST), redirect back.\n const referer = request.headers.get(\"Referer\") ?? \"/\";\n return new Response(null, {\n status: 303,\n headers: {\n Location: typeof result === \"string\" ? result : referer,\n \"Content-Type\": \"text/plain\",\n },\n });\n } catch (err) {\n console.error(\"[elur-kit] Action error:\", err);\n return publicErrorResponse(err, { includeDetail: false });\n }\n}\n\nexport { verifyOrigin, originForbidden, type OriginCheckOptions } from \"./origin.js\";\nexport {\n decodeActionErrorCookie,\n clearActionErrorCookieHeader,\n setActionErrorCookieHeader,\n ACTION_ERROR_COOKIE,\n} from \"./error-store.js\";\n","import type { IncomingMessage } from \"node:http\";\n\n// Capture the global AbortController at module load time so it's immune to\n// test frameworks that replace or delete globalThis.AbortController.\nconst GlobalAbortController =\n (globalThis as { AbortController?: typeof AbortController }).AbortController ?? AbortController;\n\nexport function incomingMessageToRequest(req: IncomingMessage, body?: BodyInit | null): Request {\n const headers = new Headers();\n for (let index = 0; index < req.rawHeaders.length; index += 2) {\n headers.append(req.rawHeaders[index], req.rawHeaders[index + 1]);\n }\n\n const controller = new GlobalAbortController();\n req.once(\"aborted\", () => controller.abort());\n req.once(\"close\", () => {\n if (!req.complete) controller.abort();\n });\n\n const protocol = (req.socket as typeof req.socket & { encrypted?: boolean }).encrypted ? \"https\" : \"http\";\n const init: RequestInit = {\n method: req.method ?? \"GET\",\n headers,\n signal: controller.signal,\n };\n if (body !== undefined && body !== null && init.method !== \"GET\" && init.method !== \"HEAD\") init.body = body;\n\n return new Request(`${protocol}://${headers.get(\"host\") ?? \"localhost\"}${req.url ?? \"/\"}`, init);\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elurjs/kit",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.6",
|
|
4
4
|
"description": "Full-stack framework for Elur — file-based routing, SSG and islands.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Deiver Vasquez",
|
|
@@ -262,7 +262,7 @@
|
|
|
262
262
|
"tsx": "^4.23.12"
|
|
263
263
|
},
|
|
264
264
|
"peerDependencies": {
|
|
265
|
-
"@elurjs/core": "^3.5.
|
|
265
|
+
"@elurjs/core": "^3.5.1",
|
|
266
266
|
"@elurjs/vite-plugin-elur": "^2.0.2",
|
|
267
267
|
"vite": "^7.0.0 || ^8.0.0"
|
|
268
268
|
},
|
|
@@ -281,12 +281,13 @@
|
|
|
281
281
|
}
|
|
282
282
|
},
|
|
283
283
|
"devDependencies": {
|
|
284
|
-
"@elurjs/core": "^3.5.
|
|
284
|
+
"@elurjs/core": "^3.5.1",
|
|
285
285
|
"@elurjs/vite-plugin-elur": "^2.0.2",
|
|
286
286
|
"@playwright/test": "^1.62.1",
|
|
287
287
|
"@types/node": "^22.20.1",
|
|
288
|
+
"happy-dom": "^20.11.6",
|
|
288
289
|
"terser": "^5.50.0",
|
|
289
290
|
"typescript": "^7.0.2",
|
|
290
291
|
"vite": "^8.2.1"
|
|
291
292
|
}
|
|
292
|
-
}
|
|
293
|
+
}
|