{"version":3,"file":"node-http-C7mkR5Mo.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// Nix.js 2.6.0 (published on npm) does not export `_setSSR`/`_isSSR`. The\n// reactivity state lives on `globalThis[Symbol.for(\"@deijose/nix-js/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 nix-js release.\n\nconst STATE_KEY = Symbol.for(\"@deijose/nix-js/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 Nix.js 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 Nix.js reactivity state. Defaults to false. */\nexport function isSSR(): boolean {\n  return getState()?.ssr ?? false;\n}\n","import type { NixTemplate } from \"@deijose/nix-js\";\nimport { renderToString as renderCoreTemplate } from \"@deijose/nix-js/server\";\nimport { setSSR } from \"./ssr-flag\";\n\n// --- Build-time / server rendering ---\n//\n// The Nix.js core ships a DOM-free `renderToString` (`@deijose/nix-js/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 Nix.js 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: () => NixTemplate,\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=\"nix-js-data\">`. */\n  data?: unknown;\n  /** Per-page action names serialized into `<script id=\"nix-js-actions\">`. */\n  actions?: Record<string, string[]>;\n  /** Path to the client entry module, e.g. `/_nix-js/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 (`/__nix-js/render`) is available at\n   * runtime. Defaults to `true`. When `false` (static deployments), the shell\n   * emits `<meta name=\"nix-js:render-endpoint\" content=\"off\" />` so the client\n   * router skips probing the endpoint entirely — preventing a storm of 404\n   * requests on fully static sites.\n   */\n  renderEndpoint?: boolean;\n}\n\nconst HTML_ESCAPES: Record<string, string> = {\n  \"&\": \"&amp;\",\n  \"<\": \"&lt;\",\n  \">\": \"&gt;\",\n  '\"': \"&quot;\",\n  \"'\": \"&#39;\",\n};\n\nfunction escapeHtml(value: string): string {\n  return value.replace(/[&<>\"']/g, (c) => HTML_ESCAPES[c]);\n}\n\n/**\n * Serializes data for embedding inside a `<script>` tag. Escapes `<` so a\n * `</script>` sequence in the data cannot break out of the tag.\n */\nfunction serializeData(data: unknown): string {\n  return JSON.stringify(data ?? null).replace(/</g, \"\\\\u003c\");\n}\n\n/**\n * Builds the `<head>` tags for a `PageMetadata` object. Every tag is marked with\n * `data-nix-js-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-nix-js-head>${escapeHtml(title)}</title>`);\n  }\n\n  if (metadata.description) {\n    tags.push(`<meta data-nix-js-head name=\"description\" content=\"${escapeHtml(metadata.description)}\" />`);\n  }\n\n  if (metadata.canonical) {\n    tags.push(`<link data-nix-js-head rel=\"canonical\" href=\"${escapeHtml(metadata.canonical)}\" />`);\n  }\n\n  if (metadata.robots) {\n    tags.push(`<meta data-nix-js-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-nix-js-head property=\"og:type\" content=\"${escapeHtml(og.type)}\" />`);\n    tags.push(`<meta data-nix-js-head property=\"og:title\" content=\"${escapeHtml(og.title ?? title)}\" />`);\n    if (og.description ?? metadata.description) {\n      tags.push(`<meta data-nix-js-head property=\"og:description\" content=\"${escapeHtml(og.description ?? metadata.description!)}\" />`);\n    }\n    if (og.url ?? metadata.canonical) {\n      tags.push(`<meta data-nix-js-head property=\"og:url\" content=\"${escapeHtml(og.url ?? metadata.canonical!)}\" />`);\n    }\n    if (og.image) tags.push(`<meta data-nix-js-head property=\"og:image\" content=\"${escapeHtml(og.image)}\" />`);\n    if (og.image && og.imageAlt) tags.push(`<meta data-nix-js-head property=\"og:image:alt\" content=\"${escapeHtml(og.imageAlt)}\" />`);\n    if (og.image && og.imageWidth) tags.push(`<meta data-nix-js-head property=\"og:image:width\" content=\"${String(og.imageWidth)}\" />`);\n    if (og.image && og.imageHeight) tags.push(`<meta data-nix-js-head property=\"og:image:height\" content=\"${String(og.imageHeight)}\" />`);\n    if (og.image && og.imageType) tags.push(`<meta data-nix-js-head property=\"og:image:type\" content=\"${escapeHtml(og.imageType)}\" />`);\n    if (og.siteName) tags.push(`<meta data-nix-js-head property=\"og:site_name\" content=\"${escapeHtml(og.siteName)}\" />`);\n    if (og.locale) tags.push(`<meta data-nix-js-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-nix-js-head name=\"twitter:card\" content=\"${escapeHtml(tw.card)}\" />`);\n    if (tw.title ?? title) tags.push(`<meta data-nix-js-head name=\"twitter:title\" content=\"${escapeHtml(tw.title ?? title)}\" />`);\n    if (tw.description ?? metadata.description) {\n      tags.push(`<meta data-nix-js-head name=\"twitter:description\" content=\"${escapeHtml(tw.description ?? metadata.description!)}\" />`);\n    }\n    if (tw.image) tags.push(`<meta data-nix-js-head name=\"twitter:image\" content=\"${escapeHtml(tw.image)}\" />`);\n    if (tw.image && tw.imageAlt) tags.push(`<meta data-nix-js-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-nix-js-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 = \"Nix.js 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=\"nix-js-data\">${serializeData(data)}</script>`\n      : \"\";\n\n  const actionsScript = actions && Object.keys(actions).length > 0\n    ? `\\n    <script type=\"application/json\" id=\"nix-js-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=\"nix-js: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 `?__nix_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 `__nix_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 = \"__nix_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 NIX_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.NIX_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 { NixTemplate } from \"@deijose/nix-js\";\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>) => NixTemplate;\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 = { __nix_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, NixTemplate> | 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>) => NixTemplate };\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: NixTemplate; data?: unknown; slots?: Record<string, NixTemplate> }) => NixTemplate;\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 ?? \"Nix.js Kit\")\n    : \"Nix.js 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// `/__nix-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(\"__nix_js_action_name\") as string | null ?? undefined;\n      page = form.get(\"__nix_js_action_page\") as string | null ?? undefined;\n      const input: Record<string, unknown> = {};\n      for (const [key, value] of form) {\n        if (key === \"__nix_js_action_name\" || key === \"__nix_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.__nix_js_action_name as string | undefined;\n      page = form.__nix_js_action_page as string | undefined;\n      const input: Record<string, unknown> = {};\n      for (const [key, value] of Object.entries(form)) {\n        if (key === \"__nix_js_action_name\" || key === \"__nix_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.__nix_js_action_name as string | undefined;\n    page = form.__nix_js_action_page as string | undefined;\n    const input: Record<string, unknown> = {};\n    for (const [key, value] of Object.entries(form)) {\n      if (key === \"__nix_js_action_name\" || key === \"__nix_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 `__nix_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({ __nix_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({ __nix_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(\"[nix-js-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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"}