import { brotliCompressSync, constants as zlibConstants } from "node:zlib";
/**
 * Shared request runtime used by BOTH the dev server and the production server.
 *
 * It owns the HTTP/WebSocket dispatch and the SSR document assembly, but knows
 * nothing about *how* modules or assets are produced — those come in via
 * `RuntimeDeps`. Dev wires in dynamic module loading + on-the-fly bundling;
 * prod wires in a static manifest + pre-built chunks on disk.
 */

import {
  bridgeRealtime,
  createContext,
  createCorsPreflightResponse,
  createRealtimeRegistry,
  csrfToken,
  verifyCsrf,
  escapeHtml,
  etag,
  isRoomDefinition,
  isWebSocketOriginAllowed,
  notModified,
  isSafeRequestPath,
  renderError,
  renderNotFound,
  withContextHeaders,
  withSecurityHeaders,
  resolveRequestUrl,
  tenantMiddleware,
  HealthRegistry,
  type Context,
  type Middleware,
  type Mode,
  type PageMeta,
  type RealtimeBus,
  type RealtimeConnectMeta,
  type SecurityConfig,
  type SeoConfig,
  type TFunction,
} from "@wrnexus/core";
import { requestHardening } from "@wrnexus/security";
import {
  createLivenessHandler,
  createOtlpTraceExporter,
  createReadinessHandler,
  createWebVitalsHandler,
  defaultMetrics,
  metricsMiddleware,
  traceMiddleware,
  webVitalsClient,
} from "@wrnexus/observability";
import type { Router } from "@wrnexus/router";
import {
  partialPrerender,
  renderDocument,
  streamPartialDocument,
  type RenderScript,
  type ScriptAsset,
} from "@wrnexus/ssr";
import {
  disposeRequestStores,
  renderStoreHydration,
  requestStoreContainer,
} from "@wrnexus/ssr/store-context";
import type { StoreDefinition } from "@wrnexus/store";
import type { ClientRuntimeDefinition } from "@wrnexus/plugin";
import { CacheCoordinator } from "@wrnexus/cache";
import { generateServiceWorker } from "@wrnexus/pwa";
import { collectScripts, usesMobileRuntime } from "./script-selection.ts";
export { collectScripts, usesMobileRuntime } from "./script-selection.ts";
import {
  ACCENT_COOKIE,
  THEME_COOKIE,
  activeThemeCssHref,
  resolveAccentName,
  resolveThemeName,
  type MobileConfig,
  type ObservabilityConfig,
  type PwaConfig,
  type ResolvedTheme,
  type TenancyConfig,
} from "@wrnexus/styles";
import {
  renderI18nData,
  makeT,
  resolveLang,
  translateHtml,
  type ResolvedI18n,
} from "@wrnexus/i18n";
import { runMiddleware } from "./pipeline.ts";
import { handleRpcRequest, isRpcPath } from "./rpc-dispatch.ts";
import type { ServiceImplementation, StreamImplementation } from "@wrnexus/rpc";
import type { HmrHub } from "./hmr.ts";
import type {
  DevToolbarConfig,
  DevToolbarPanel,
  DevToolbarPlatformSnapshot,
} from "@wrnexus/dev-toolbar/types";

import {
  createServerIssue,
  handleDevToolbarRoute,
  issueFromError,
  type DevToolbarCollector,
} from "@wrnexus/dev-toolbar/server";

/** HTTP methods we recognize as API handler exports. */
const HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"] as const;

/** A realtime module's `websocket` export: a bag of optional lifecycle hooks. */
export type WsHandler = Record<string, (...args: any[]) => unknown>;

/**
 * Per-connection socket data. A socket is either an app realtime connection or
 * an internal HMR connection — discriminated by `kind`.
 */
export type WsData =
  | { kind: "realtime"; handler: WsHandler } // legacy raw `websocket` export
  | { kind: "room"; meta: RealtimeConnectMeta } // `defineRoom` default export
  | { kind: "hmr"; baseUrl: string; headers: [string, string][] };

type RouteModule = Record<string, unknown>;
type ApiRegistry = Record<string, unknown>;
interface ActionEntry {
  run: (input: unknown, ctx: Context) => unknown | Promise<unknown>;
  schema?: {
    parse(input: unknown): { ok: boolean; value: unknown; errors: Record<string, string> };
  };
}
interface CsrBinding {
  id: string;
  method?: string;
  path: string;
  body?: string;
  expr?: string;
  helpers?: string;
}

/** Serves framework-owned assets under `/__wrnexus/*` (islands, reactive, hmr). */
export interface AssetServer {
  serve(pathname: string): Promise<Response | null>;
}

export interface RuntimeDeps {
  mode: Mode;
  /** When true, inject the live-reload client into rendered pages. */
  hmr: boolean;
  router: Router;
  /** Load a route module by absolute path (dev: dynamic import; prod: manifest). */
  loadModule(file: string): Promise<RouteModule>;
  /** Resolve the ordered middleware chain. */
  getMiddleware(): Promise<Middleware[]>;
  /** Serve `/__wrnexus/*` assets. */
  assets: AssetServer;
  /** When true, inject the global stylesheet link into every page head. */
  hasStyles?: boolean;
  /** When true, inject the Wire UI stylesheet link (`/__wrnexus/ui.css`). */
  hasUi?: boolean;
  /** Production build combined theme + UI stylesheet. */
  hasFrameworkStyles?: boolean;
  /** App stylesheet already contains theme + UI CSS and is the only CSS request needed. */
  stylesIncludeFramework?: boolean;
  stylesIncludeUi?: boolean;
  /** Resolved theme config: enables `/__wrnexus/theme.css` + `<html data-theme>`. */
  theme?: ResolvedTheme;
  /** Resolved i18n bundle: enables `ctx.t`, `<html lang>`, and `{t:key}` markers. */
  i18n?: ResolvedI18n;
  /** Small production stylesheets can be inlined to avoid a render-blocking request. */
  inlineStyles?: string;
  /** Production cache-busting version appended to framework asset URLs. */
  assetVersion?: string;
  /** Package browser runtimes resolved by the plugin system. */
  clientRuntimes?: ClientRuntimeDefinition[];
  /** Page navigation strategy. `document` disables same-origin link interception. */
  navigation?: { mode?: "auto" | "client" | "document" };
  /** Raw HTML appended to every page head (e.g. CDN framework links). */
  head?: string;
  /** Global SEO defaults. */
  seo?: SeoConfig;
  mobile?: MobileConfig;
  pwa?: PwaConfig | false;
  /** Framework security headers and CORS policy. */
  security?: SecurityConfig;
  /** Built-in request tracing and Server-Timing policy. */
  observability?: ObservabilityConfig;
  /** Dependency health checks used by `/readyz` and `/__wrnexus/ready`. */
  health?: HealthRegistry;
  /** Built-in tenant identity resolution. */
  tenancy?: TenancyConfig;
  /** Max request body size in bytes (413 above this). Default 10 MB. */
  maxBodyBytes?: number;
  /** HMR hub for browser live-update sockets (dev only). */
  hub?: HmrHub;
  /**
   * Cross-process realtime bus. When provided, room broadcasts/`toUser` sends are
   * bridged to it so they reach clients on every app process/instance sharing the
   * bus (use the Redis pub/sub driver). Enables realtime across multiple apps.
   */
  realtimeBus?: RealtimeBus;
  /** Shared first-class data/component/page caches. */
  cache?: CacheCoordinator;
  /** Final document transform supplied by the plugin render lifecycle. */
  renderHtml?: (html: string) => string | Promise<string>;

  devToolbar?: {
    config: DevToolbarConfig;
    collector: DevToolbarCollector;
    root: string;
    platform?: DevToolbarPlatformSnapshot;
    panels?: DevToolbarPanel[] | (() => DevToolbarPanel[] | Promise<DevToolbarPanel[]>);
  };
}

const DEV_TOOLBAR_SCRIPT =
  '<script type="module" src="/__wrnexus/dev-toolbar.js" data-wrnexus-dev-toolbar></script>';

function shouldEnableDevToolbar(mode: string, deps: RuntimeDeps): boolean {
  return (
    mode === "development" &&
    deps.devToolbar !== undefined &&
    deps.devToolbar.config.enabled !== false
  );
}

function tenantIdentityFromConfig(
  config: TenancyConfig,
): (ctx: Context) => Promise<{ id: string; slug?: string } | null> {
  return async (ctx) => {
    const host = ctx.url.hostname.toLowerCase();

    if (config.mode === "domain") return host ? { id: host, slug: host } : null;

    if (config.mode === "path") {
      const segments = ctx.url.pathname.split("/").filter(Boolean);
      const prefix = config.pathPrefix?.replace(/^\/+|\/+$/g, "");
      const slug = prefix ? (segments[0] === prefix ? segments[1] : undefined) : segments[0];
      return slug ? { id: slug, slug } : null;
    }

    if (config.mode === "subdomain" || config.mode === undefined) {
      const roots = config.rootDomains?.map((domain) => domain.toLowerCase()) ?? [];
      const root = roots.find((domain) => host === domain || host.endsWith(`.${domain}`));
      const slug = root ? host.slice(0, -(root.length + 1)) : host.split(".")[0];
      if (!slug || slug === host || slug === "www" || slug === "localhost") return null;
      return { id: slug, slug };
    }

    return null;
  };
}

function frameworkMiddleware(deps: RuntimeDeps): Middleware[] {
  const middleware: Middleware[] = [];

  if (deps.security?.requestLimits) {
    middleware.push(requestHardening(deps.security.requestLimits));
  }

  if (deps.observability && deps.observability.enabled !== false) {
    middleware.push(metricsMiddleware({ registry: defaultMetrics, includePath: false }));
    const traceExporter =
      deps.observability.exporter === "otlp" && deps.observability.endpoint
        ? createOtlpTraceExporter(deps.observability.endpoint, {
            serviceName: deps.observability.serviceName,
          })
        : undefined;
    middleware.push(
      traceMiddleware({
        serviceName: deps.observability.serviceName,
        sampleRate: deps.observability.sampleRate,
        serverTiming: deps.observability.serverTiming,
        exporter: traceExporter,
        onSpan:
          deps.observability.exporter === "console"
            ? (span) => {
                console.log(
                  `[wrnexus:trace] ${span.name} ${span.durationMs.toFixed(2)}ms trace=${span.traceId}`,
                );
              }
            : undefined,
        onExportError(error) {
          console.error("[wrnexus:trace] export failed", error);
        },
      }),
    );
  }

  if (deps.tenancy && Object.keys(deps.tenancy).length > 0 && deps.tenancy.mode !== "custom") {
    middleware.push(
      tenantMiddleware(tenantIdentityFromConfig(deps.tenancy), {
        required: deps.tenancy.required,
      }),
    );
  }

  return middleware;
}

function shouldReportNotFound(pathname: string): boolean {
  return !(
    pathname.startsWith("/__wrnexus/") ||
    pathname.startsWith("/.well-known/") ||
    pathname === "/favicon.ico" ||
    pathname.endsWith(".map")
  );
}

export function renderPwaClient(version = "2") {
  return `if ("serviceWorker" in navigator) {
  var swVersion = ${JSON.stringify(version)};
  var swUrl = "/sw.js?v=" + encodeURIComponent(swVersion);
  if (window.trustedTypes) {
    try {
      swUrl = window.trustedTypes.createPolicy("wrnexus-pwa", { createScriptURL: function(value) { return value; } }).createScriptURL(swUrl);
    } catch (_) {}
  }
  addEventListener("load", function () {
    var register = function () {
      navigator.serviceWorker.register(swUrl, { updateViaCache: "none" }).catch(function () {});
    };
    if ("requestIdleCallback" in window) window.requestIdleCallback(register, { timeout: 2000 });
    else setTimeout(register, 0);
  });
}`;
}

export const PWA_CLIENT = renderPwaClient();

export const PWA_DEV_CLEANUP_CLIENT = `if ("serviceWorker" in navigator && !sessionStorage.getItem("wrnexus-pwa-dev-cleaned")) {
  sessionStorage.setItem("wrnexus-pwa-dev-cleaned", "1");
  navigator.serviceWorker.getRegistrations().then(function (registrations) {
    return Promise.all(registrations.filter(function (registration) {
      return new URL(registration.active?.scriptURL || registration.installing?.scriptURL || registration.waiting?.scriptURL || location.origin, location.origin).pathname === "/sw.js";
    }).map(function (registration) { return registration.unregister(); }));
  }).catch(function () {});
  if (window.caches) caches.keys().then(function (keys) {
    return Promise.all(keys.filter(function (key) { return key.indexOf("wrnexus-pwa-") === 0; }).map(function (key) { return caches.delete(key); }));
  }).catch(function () {});
}`;

function renderPwaServiceWorker(pwa: PwaConfig): string {
  return generateServiceWorker(pwa);
}

const DEFAULT_PWA_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
  <rect width="512" height="512" rx="112" fill="#6366f1"/>
  <path d="M112 144h64l42 168 38-128h48l38 128 42-168h64l-72 224h-60l-36-117-36 117h-60z" fill="white"/>
</svg>`;

export const MOBILE_CLIENT = `const isNativeApp = window.Capacitor?.isNativePlatform?.() === true ||
  ["android", "ios"].includes(window.Capacitor?.getPlatform?.());
const nativeTarget = isNativeApp ? "mobile" : "browser";
const nativeCapabilities = new Map();
const registerNative = (name, adapters) => nativeCapabilities.set(name, adapters);
const unregisterNative = name => nativeCapabilities.delete(name);
const nativeAdapter = name => nativeCapabilities.get(name)?.[nativeTarget];
const nativeSupports = name => {
  const adapter = nativeAdapter(name);
  return !!adapter && (adapter.supported?.() ?? true);
};
const nativeRun = async (name, options) => {
  const adapter = nativeAdapter(name);
  if (!adapter || !(adapter.supported?.() ?? true)) throw new Error('Native capability "' + name + '" is unavailable on ' + nativeTarget);
  return adapter.run(options);
};
const cap = (plugin, method) => ({
  supported: () => typeof window.Capacitor?.Plugins?.[plugin]?.[method] === "function",
  run: options => window.Capacitor.Plugins[plugin][method](options)
});
const capWith = (plugin, method, transform) => ({
  supported: () => typeof window.Capacitor?.Plugins?.[plugin]?.[method] === "function",
  run: options => window.Capacitor.Plugins[plugin][method](transform(options))
});
registerNative("clipboard.write", {
  browser: { supported: () => !!navigator.clipboard?.writeText, run: o => navigator.clipboard.writeText(String(o?.text ?? "")) },
  mobile: capWith("Clipboard", "write", o => ({ string: String(o?.text ?? o?.string ?? "") }))
});
registerNative("share", {
  browser: { supported: () => typeof navigator.share === "function", run: o => navigator.share(o) },
  mobile: cap("Share", "share")
});
registerNative("geolocation", {
  browser: { supported: () => !!navigator.geolocation, run: o => new Promise((resolve, reject) => navigator.geolocation.getCurrentPosition(resolve, reject, o)) },
  mobile: cap("Geolocation", "getCurrentPosition")
});
registerNative("network", {
  browser: { run: () => ({ connected: navigator.onLine }) }, mobile: cap("Network", "getStatus")
});
registerNative("camera", {
  browser: { supported: () => !!navigator.mediaDevices?.getUserMedia, run: o => navigator.mediaDevices.getUserMedia({ video: o ?? true, audio: false }) },
  mobile: cap("Camera", "getPhoto")
});
registerNative("haptics", { mobile: cap("Haptics", "impact") });
nativeCapabilities.get("haptics").browser = { supported: () => typeof navigator.vibrate === "function", run: o => navigator.vibrate(o?.duration ?? 20) };
registerNative("storage.get", { browser: { run: o => ({ value: localStorage.getItem(String(o?.key ?? "")) }) }, mobile: cap("Preferences", "get") });
registerNative("storage.set", { browser: { run: o => localStorage.setItem(String(o?.key ?? ""), String(o?.value ?? "")) }, mobile: cap("Preferences", "set") });
registerNative("filesystem.read", {
  browser: { supported: () => "showOpenFilePicker" in window, run: async () => { const [handle] = await window.showOpenFilePicker(); return handle?.getFile(); } },
  mobile: cap("Filesystem", "readFile")
});
registerNative("filesystem.write", {
  browser: { supported: () => "showSaveFilePicker" in window, run: async o => { const handle = await window.showSaveFilePicker(); const writable = await handle.createWritable(); await writable.write(o?.data ?? ""); await writable.close(); } },
  mobile: cap("Filesystem", "writeFile")
});
registerNative("notifications.schedule", {
  browser: { supported: () => typeof Notification !== "undefined", run: async o => { if (Notification.permission === "default") await Notification.requestPermission(); if (Notification.permission !== "granted") throw new Error("Notification permission was not granted"); return new Notification(o?.title ?? "Notification", { body: o?.body }); } },
  mobile: capWith("LocalNotifications", "schedule", o => o?.notifications ? o : ({ notifications: [{ id: o?.id ?? Date.now() % 2147483647, title: o?.title ?? "Notification", body: o?.body ?? "", schedule: o?.schedule }] }))
});
registerNative("device.info", { browser: { run: () => ({ platform: "web", userAgent: navigator.userAgent, language: navigator.language }) }, mobile: cap("Device", "getInfo") });
window.__WrNexusNativePending?.forEach((adapters, name) => registerNative(name, adapters));
delete window.__WrNexusNativePending;
window.WrNexusNative = { target: nativeTarget, isMobile: isNativeApp, register: registerNative, unregister: unregisterNative, supports: nativeSupports, run: nativeRun };
const revealMobileOnly = root => {
  const elements = [];
  if (root instanceof Element) elements.push(root);
  root.querySelectorAll?.("[data-mobile-only],[data-native-only],[data-native-requires],[data-native-unsupported]").forEach(element => elements.push(element));
  elements.forEach(element => {
    if (element.matches("[data-mobile-only]")) element.hidden = !isNativeApp;
    const only = element.getAttribute("data-native-only");
    if (only) element.hidden = !((only === "mobile" && isNativeApp) || (["browser", "web"].includes(only) && !isNativeApp) || only === window.Capacitor?.getPlatform?.());
    const required = element.getAttribute("data-native-requires");
    if (required) element.hidden = !nativeSupports(required);
    const unsupported = element.getAttribute("data-native-unsupported");
    if (unsupported) element.hidden = nativeSupports(unsupported);
  });
};
revealMobileOnly(document);
new MutationObserver(records => {
  records.forEach(record => record.addedNodes.forEach(node => {
    revealMobileOnly(node);
    installNativeEvents(node);
  }));
}).observe(document.documentElement, { childList: true, subtree: true });

const handleNativeEvent = async event => {
  const eventSelector = "[data-on-wrnexus-browser-" + event.type + "],[data-on-wrnexus-mobile-" + event.type + "]";
  const actionSelector = event.type === "click" ? ",[data-native-browser],[data-native-mobile]" : "";
  const element = event.target.closest?.(eventSelector + actionSelector);
  if (!element) return;
  const platformEvent = "wrnexus-" + nativeTarget + "-" + event.type;
  if (element.hasAttribute("data-on-" + platformEvent)) element.dispatchEvent(new CustomEvent(platformEvent));
  if (event.type !== "click") return;
  const capability = element.getAttribute("data-native-" + nativeTarget);
  if (!capability) return;
  let options = {};
  const raw = element.getAttribute("data-native-options");
  if (raw) try { options = JSON.parse(raw); } catch (error) {
    element.dispatchEvent(new CustomEvent("wrnexus:native-error", { detail: error, bubbles: true }));
    return;
  }
  try {
    const result = await nativeRun(capability, options);
    element.dispatchEvent(new CustomEvent("wrnexus:native-success", { detail: { capability, result }, bubbles: true }));
  } catch (error) {
    element.dispatchEvent(new CustomEvent("wrnexus:native-error", { detail: { capability, error }, bubbles: true }));
  }
};
const nativeEventTypes = new Set();
const listenNativeEvent = type => {
  if (nativeEventTypes.has(type)) return;
  nativeEventTypes.add(type);
  document.addEventListener(type, handleNativeEvent, true);
};
const installNativeEvents = root => {
  const elements = [];
  if (root instanceof Element) elements.push(root);
  root.querySelectorAll?.("*").forEach(element => elements.push(element));
  elements.forEach(element => Array.from(element.attributes).forEach(attribute => {
    const match = /^data-on-wrnexus-(?:browser|mobile)-(.+)$/.exec(attribute.name);
    if (match) listenNativeEvent(match[1]);
  }));
};
listenNativeEvent("click");
installNativeEvents(document);

const openWebCamera = async image => {
  if (!navigator.mediaDevices?.getUserMedia) return false;
  let stream;
  try {
    stream = await navigator.mediaDevices.getUserMedia({
      video: { facingMode: { ideal: "environment" } },
      audio: false
    });
    const overlay = document.createElement("div");
    Object.assign(overlay.style, {
      position: "fixed", inset: "0", zIndex: "2147483647", display: "grid",
      placeItems: "center", padding: "24px", background: "rgba(2,6,23,.92)"
    });
    const panel = document.createElement("div");
    Object.assign(panel.style, { width: "min(720px,100%)", color: "white" });
    const video = document.createElement("video");
    video.autoplay = true;
    video.muted = true;
    video.playsInline = true;
    video.srcObject = stream;
    Object.assign(video.style, {
      display: "block", width: "100%", maxHeight: "70vh", objectFit: "contain",
      borderRadius: "16px", background: "black"
    });
    const controls = document.createElement("div");
    Object.assign(controls.style, { display: "flex", gap: "12px", marginTop: "16px" });
    const capture = document.createElement("button");
    capture.type = "button";
    capture.textContent = "Capture photo";
    const cancel = document.createElement("button");
    cancel.type = "button";
    cancel.textContent = "Cancel";
    [capture, cancel].forEach(control => Object.assign(control.style, {
      flex: "1", padding: "14px", border: "0", borderRadius: "12px",
      fontWeight: "700", cursor: "pointer"
    }));
    capture.style.background = "#6366f1";
    capture.style.color = "white";
    const close = () => {
      stream.getTracks().forEach(track => track.stop());
      overlay.remove();
    };
    capture.onclick = () => {
      const canvas = document.createElement("canvas");
      canvas.width = video.videoWidth || 1280;
      canvas.height = video.videoHeight || 720;
      canvas.getContext("2d")?.drawImage(video, 0, 0, canvas.width, canvas.height);
      if (image) image.src = canvas.toDataURL("image/jpeg", 0.92);
      close();
    };
    cancel.onclick = close;
    controls.append(capture, cancel);
    panel.append(video, controls);
    overlay.append(panel);
    document.body.append(overlay);
    await video.play();
    return true;
  } catch (error) {
    stream?.getTracks().forEach(track => track.stop());
    return false;
  }
};

document.addEventListener("click", async event => {
  const button = event.target.closest?.("[data-mobile-camera]");
  if (!button) return;
  const selector = button.getAttribute("data-mobile-camera") || "[data-mobile-photo]";
  const image = document.querySelector(selector);
  try {
    const camera = window.Capacitor?.Plugins?.Camera;
    if (isNativeApp && camera) {
      const photo = await camera.getPhoto({ quality: 90, resultType: "uri", source: "camera" });
      if (image) image.src = photo.webPath || photo.path;
      return;
    }
    if (await openWebCamera(image)) return;
    const input = document.createElement("input");
    input.type = "file";
    input.accept = "image/*";
    input.setAttribute("capture", "environment");
    input.hidden = true;
    input.onchange = () => {
      if (image && input.files?.[0]) image.src = URL.createObjectURL(input.files[0]);
      input.remove();
    };
    document.body.append(input);
    input.click();
  } catch (error) {
    window.dispatchEvent(new CustomEvent("wrnexus:camera-error", { detail: error }));
  }
});

const nativeApp = window.Capacitor?.Plugins?.App;
nativeApp?.addListener?.("backButton", ({ canGoBack }) => {
  if (canGoBack || history.length > 1) history.back();
  // At the root screen intentionally do nothing instead of closing the app.
});`;

function revealMobileOnlyHtml(html: string): string {
  return html.replace(/<[^>]*\bdata-mobile-only\b[^>]*>/gi, (tag) =>
    tag.replace(/\s+hidden(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+))?/i, ""),
  );
}

/** The global stylesheet URL (served by the asset server when styles exist). */
export const STYLES_HREF = "/__wrnexus/styles.css";

/**
 * Inline HMR client (WebSocket). Goals:
 *  - CSS change  -> hot-swap the stylesheet, zero reload, zero flash.
 *  - markup/page -> ask over the existing HMR WebSocket for fresh HTML and
 *    MORPH the live DOM in place, without restarting the server.
 *  - island code -> same WebSocket sync path; no location.reload().
 */
export const HMR_CLIENT_JS = `
(function () {
  if (!("WebSocket" in window)) return;
  var proto = location.protocol === "https:" ? "wss" : "ws";
  var url = proto + "://" + location.host + "/__wrnexus/hmr";
  var ws, openedBefore = false, timer, pendingSync = false;

  function connect() {
    ws = new WebSocket(url);
    ws.onopen = function () {
      if (openedBefore) requestSync();   // reconnect == server restarted
      openedBefore = true;
    };
    ws.onmessage = function (e) {
      var msg; try { msg = JSON.parse(e.data); } catch (_) { return; }
      if (msg.channel === "toolbar") {
        window.dispatchEvent(new CustomEvent("wrnexus:toolbar-message", { detail: msg }));
      } else if (msg.type === "css") {
        swapCss(msg.version);
        window.dispatchEvent(new CustomEvent("wrnexus:hmr", { detail: msg }));
      } else if (msg.type === "store-update") {
        applyStoreUpdates(msg);
      } else if (msg.type === "reload") requestSync();
      else if (msg.type === "html") applyHtml(msg.html);
      else if (msg.type === "error") console.error("[wrnexus] HMR update failed:", msg.message);
    };
    ws.onclose = function () { clearTimeout(timer); timer = setTimeout(connect, 400); };
    ws.onerror = function () { try { ws.close(); } catch (_) {} };
  }

  function send(message) {
    if (!ws || ws.readyState !== WebSocket.OPEN) return false;
    ws.send(JSON.stringify(message));
    return true;
  }


  async function applyStoreUpdates(message) {
    var updates = Array.isArray(message.stores) ? message.stores : [];
    for (var i = 0; i < updates.length; i++) {
      var update = updates[i];
      try {
        var separator = String(update.url).indexOf("?") >= 0 ? "&" : "?";
        var module = await import(String(update.url) + separator + "hmr=" + encodeURIComponent(String(message.version || Date.now())));
        var definition = module[String(update.name) + "Definition"];
        if (!definition) throw new Error("Generated store module did not export its definition");
        if (typeof window.__wrnexusApplyStoreHotUpdate === "function") {
          var result = await window.__wrnexusApplyStoreHotUpdate(String(update.name), definition);
          window.dispatchEvent(new CustomEvent("wrnexus:hmr-store-updated", { detail: { update: update, result: result } }));
        }
      } catch (error) {
        console.error("[wrnexus] store HMR update failed", update, error);
        requestSync();
        return;
      }
    }
  }

  function requestSync() {
    if (pendingSync) return;
    pendingSync = true;
    if (!send({ type: "sync", path: location.pathname + location.search })) {
      pendingSync = false;
    }
  }

  function swapCss(version) {
    var links = document.querySelectorAll('link[rel="stylesheet"]');
    for (var i = 0; i < links.length; i++) (function (link) {
      var href = link.getAttribute("href");
      if (!href || href.charAt(0) !== "/") return;       // skip CDN links
      var next = href.split("?")[0] + "?hmr=" + encodeURIComponent(String(version || "css"));
      var copy = link.cloneNode(false);
      copy.setAttribute("href", next);
      copy.addEventListener("load", function () {
        if (link.parentNode) link.parentNode.removeChild(link);
      });
      link.parentNode.insertBefore(copy, link.nextSibling);
    })(links[i]);
  }

  function applyHtml(html) {
    pendingSync = false;
    var doc = new DOMParser().parseFromString(html, "text/html");

    if (doc.title) {
      document.title = doc.title;
    }

    // HMR morphs only #app. Keep the live browser theme and accent because
    // they represent the user's current selection. Language remains SSR-owned.
    var nextLang = doc.documentElement.getAttribute("lang");

    if (nextLang == null) {
      document.documentElement.removeAttribute("lang");
    } else {
      document.documentElement.setAttribute("lang", nextLang);
    }

    var from = document.getElementById("app");
    var to = doc.getElementById("app");

    syncWrnStyles(doc);
    if (from && to) {
      morph(from, to);
    }

    swapCss("html");

    if (window.__wrnexusHydrateScopes) {
      window.__wrnexusHydrateScopes(document);
    }

    if (window.__wrnexusHydrateCsrFetches) {
      window.__wrnexusHydrateCsrFetches(document);
    }

    if (
      window.wireTheme &&
      typeof window.wireTheme.bind === "function"
    ) {
      window.wireTheme.bind(document);
    }

    window.dispatchEvent(
      new CustomEvent("wrnexus:hmr-updated", {
        detail: {
          theme: document.documentElement.getAttribute("data-theme"),
          accent: document.documentElement.getAttribute("data-accent"),
        },
      }),
    );
  }

  function currentDocumentNonce() {
    var node = document.querySelector("script[nonce],style[nonce]");
    if (!node) return "";
    return node.nonce || node.getAttribute("nonce") || "";
  }

  function syncWrnStyles(nextDocument) {
    var current = Array.prototype.slice.call(
      document.querySelectorAll("style[data-wrnexus-style-id]"),
    );
    var next = Array.prototype.slice.call(
      nextDocument.querySelectorAll("style[data-wrnexus-style-id]"),
    );
    var currentById = Object.create(null);
    var nextIds = Object.create(null);
    var nonce = currentDocumentNonce();

    current.forEach(function (style) {
      var id = style.getAttribute("data-wrnexus-style-id");
      if (id) currentById[id] = style;
    });

    next.forEach(function (nextStyle) {
      var id = nextStyle.getAttribute("data-wrnexus-style-id");
      if (!id) return;
      nextIds[id] = true;

      var currentStyle = currentById[id];
      if (!currentStyle) {
        currentStyle = document.createElement("style");
        currentStyle.setAttribute("data-wrnexus-style-id", id);
      }

      ["data-wrnexus-style-owner", "data-wrnexus-style-kind"].forEach(function (name) {
        var value = nextStyle.getAttribute(name);
        if (value == null) currentStyle.removeAttribute(name);
        else currentStyle.setAttribute(name, value);
      });

      if (nonce) currentStyle.setAttribute("nonce", nonce);
      if (currentStyle.textContent !== nextStyle.textContent) {
        currentStyle.textContent = nextStyle.textContent;
      }

      // Appending an existing node moves it. This keeps every local style after
      // global stylesheets and in the exact layout -> page -> component order
      // produced by the incoming SSR document.
      document.head.appendChild(currentStyle);
    });

    current.forEach(function (style) {
      var id = style.getAttribute("data-wrnexus-style-id");
      if (id && !nextIds[id]) style.remove();
    });
  }

  // Minimal index-based DOM morph: preserve matching nodes (keeps state/focus),
  // patch text and attributes, clone genuinely new nodes, drop removed ones.
  // Preserve a hydrated subtree only while its server hydration signature and
  // behavior are unchanged. Component edits must replace and re-hydrate the
  // old subtree or HMR will keep stale markup indefinitely.
  function morph(from, to) {
    if (from.__wrnexusHydrated) {
      var sameHydration =
        from.getAttribute("data-wrn-hydration") === to.getAttribute("data-wrn-hydration") &&
        from.getAttribute("data-wrn-behavior") === to.getAttribute("data-wrn-behavior") &&
        from.getAttribute("data-scope") === to.getAttribute("data-scope");
      if (sameHydration) return;
      if (window.__wrnexusDisposeBehaviors) window.__wrnexusDisposeBehaviors(from);
      from.replaceWith(to.cloneNode(true));
      return;
    }
    syncAttrs(from, to);
    var fc = from.childNodes, tc = to.childNodes, i;
    for (i = 0; i < tc.length; i++) {
      var t = tc[i], f = fc[i];
      if (!f) { from.appendChild(t.cloneNode(true)); continue; }
      if (f.nodeType !== t.nodeType || (f.nodeType === 1 && f.nodeName !== t.nodeName)) {
        from.replaceChild(t.cloneNode(true), f); continue;
      }
      if (f.nodeType === 3 || f.nodeType === 8) { if (f.nodeValue !== t.nodeValue) f.nodeValue = t.nodeValue; continue; }
      if (f.nodeType === 1) morph(f, t);
    }
    while (from.childNodes.length > tc.length) from.removeChild(from.lastChild);
  }
  function syncAttrs(from, to) {
    var ta = to.attributes, fa = from.attributes, i;
    for (i = 0; i < ta.length; i++) if (from.getAttribute(ta[i].name) !== ta[i].value) from.setAttribute(ta[i].name, ta[i].value);
    for (i = fa.length - 1; i >= 0; i--) if (!to.hasAttribute(fa[i].name)) from.removeAttribute(fa[i].name);
  }

  // Exposed for tests; harmless (the client is injected only in dev).
  window.__wrnexusHmr = { morph: morph, swapCss: swapCss, requestSync: requestSync, applyHtml: applyHtml };

  connect();
})();
`;

/** A base64 CSP nonce for this request's inline scripts. */
function randomNonce(): string {
  const bytes = new Uint8Array(16);
  crypto.getRandomValues(bytes);
  let bin = "";
  for (const b of bytes) bin += String.fromCharCode(b);
  return btoa(bin);
}

/** The dev HMR client as a nonce-tagged inline script (strict-CSP friendly). */
function hmrClientTag(nonce: string): string {
  return `<script nonce="${nonce}">${HMR_CLIENT_JS}</script>`;
}

/** 403 for a rejected cross-site WebSocket handshake. */
function forbiddenOrigin(): Response {
  return new Response("Forbidden WebSocket origin", { status: 403 });
}

export interface UpgradeServer {
  upgrade(req: Request, opts: { data: WsData }): boolean;
  /** Bun's per-request socket peer address (used for the non-spoofable client IP). */
  requestIP?(req: Request): { address: string } | null;
}

/** The subset of Bun's ServerWebSocket the runtime touches. */
export interface Ws {
  data: WsData;
  send(data: string | Uint8Array): unknown;
  close(code?: number, reason?: string): void;
}

export interface Handlers {
  fetch(req: Request, server: UpgradeServer): Promise<Response | undefined>;
  websocket: {
    open(ws: Ws): void;
    message(ws: Ws, message: string | Uint8Array): void;
    close(ws: Ws, code?: number, reason?: string): void;
    drain(ws: Ws): void;
  };
}

/** Build the fetch + websocket handlers from a set of dependencies. */
export function createHandlers(deps: RuntimeDeps): Handlers {
  const { mode, hmr, router, loadModule, getMiddleware, assets } = deps;
  const cache =
    deps.cache ??
    new CacheCoordinator({
      onEvent: (event) => {
        if (mode === "development")
          console.debug(
            `[wrnexus:cache] ${event.layer} ${event.operation}${event.key ? ` ${event.key}` : ""}`,
          );
      },
    });
  const initializeRequestCache = (ctx: Context): void => {
    ctx.locals.cache = cache;
    ctx.locals.requestCache ??= cache.request();
  };
  const builtInMiddleware = frameworkMiddleware(deps);
  const resolveMiddleware = async (): Promise<Middleware[]> => [
    ...builtInMiddleware,
    ...(await getMiddleware()),
  ];
  const maxBodyBytes = deps.maxBodyBytes ?? 10 * 1024 * 1024; // 10 MB default
  // Not memoized across failures: a single bad file in app/services/ (e.g. a
  // co-located helper with no default export) must not permanently break
  // every route in the app. Only a SUCCESSFUL load is cached; a failed
  // attempt logs loudly and is retried on the next RPC request.
  let servicesPromise:
    Promise<Map<string, ServiceImplementation | StreamImplementation>> | undefined;
  const loadServices = (): Promise<Map<string, ServiceImplementation | StreamImplementation>> => {
    if (!servicesPromise) {
      servicesPromise = (async () => {
        const services = new Map<string, ServiceImplementation | StreamImplementation>();
        for (const entry of router.services) {
          const imported = await loadModule(entry.file);
          const implementation = imported.default as
            (ServiceImplementation | StreamImplementation) | undefined;
          if (
            !implementation ||
            (typeof (implementation as ServiceImplementation).invoke !== "function" &&
              typeof (implementation as StreamImplementation).stream !== "function")
          ) {
            throw new Error(
              `RPC service ${entry.file} must default-export implement(...) or implementStream(...)`,
            );
          }
          if (implementation.contract.name !== entry.name) {
            throw new Error(
              `RPC service file ${entry.file} is mounted as "${entry.name}" (its filename) ` +
                `but its contract is named "${implementation.contract.name}". Rename the file to ` +
                `match the contract, or rename the contract to match the file.`,
            );
          }
          services.set(entry.name, implementation);
        }
        return services;
      })().catch((error) => {
        servicesPromise = undefined;
        const app = process.env.WRNEXUS_APP_NAME ?? "app";
        console.error(
          `[wrnexus] failed to load RPC services (${app}):`,
          error instanceof Error ? (error.stack ?? error.message) : error,
        );
        throw error;
      });
    }
    return servicesPromise;
  };

  // Server-side realtime room manager (shared by every `defineRoom` connection).
  const realtime = createRealtimeRegistry();
  // Bridge across processes/instances when a pub/sub bus is provided, so room
  // broadcasts reach clients on every app run sharing the bus (Redis in prod).
  if (deps.realtimeBus) bridgeRealtime(realtime, deps.realtimeBus);

  // Global <head> additions, identical on every page (styles are global).
  // Theme tokens load first so global.css and component styles can override them.
  // Order: theme tokens, then Wire UI, then the app stylesheet — so the app's
  // own CSS (loaded last) can override both the tokens and the UI classes.
  const headParts: string[] = [];
  if (!deps.theme && deps.hasFrameworkStyles && !deps.stylesIncludeFramework) {
    headParts.push(
      `<link rel="stylesheet" href="${versionAssetUrl("/__wrnexus/framework.css", deps.assetVersion)}" />`,
    );
  }
  if (deps.hasUi && !deps.stylesIncludeUi && !deps.stylesIncludeFramework) {
    headParts.push(
      `<link rel="stylesheet" href="${versionAssetUrl("/__wrnexus/ui.css", deps.assetVersion)}" />`,
    );
  }
  if (deps.inlineStyles) {
    headParts.push(`<style data-wrnexus-global>${escapeStyleContent(deps.inlineStyles)}</style>`);
  } else if (deps.hasStyles) {
    const stylesHref = versionAssetUrl(STYLES_HREF, deps.assetVersion);
    headParts.push(`<link rel="preload" href="${stylesHref}" as="style" />`);
    headParts.push(`<link rel="stylesheet" href="${stylesHref}" />`);
  }
  if (deps.head) headParts.push(deps.head);
  const extraHead = headParts.join("\n    ") || undefined;
  const pwaEnabled = deps.pwa !== false && deps.pwa?.enabled !== false;
  const pwaConfig: PwaConfig = deps.pwa && typeof deps.pwa === "object" ? deps.pwa : {};
  const pwaServiceWorkerEnabled =
    mode === "production" && pwaEnabled && pwaConfig.serviceWorker !== false;
  const pwaDevCleanupEnabled = mode === "development";
  const webVitalsEnabled =
    deps.observability?.enabled !== false && deps.observability?.webVitals === true;
  const webVitalsEndpoint = deps.observability?.webVitalsEndpoint ?? "/__wrnexus/metrics/vitals";
  const webVitalsHandler = createWebVitalsHandler({ registry: defaultMetrics });
  const livenessHandler = createLivenessHandler();
  const readinessHandler = createReadinessHandler(deps.health ?? new HealthRegistry());

  const configuredPermissions = deps.security?.permissionsPolicy;
  const runtimeSecurity: SecurityConfig | undefined =
    deps.mobile?.enabled === false
      ? deps.security
      : {
          ...deps.security,
          permissionsPolicy:
            configuredPermissions === false
              ? false
              : { camera: ["self"], ...(configuredPermissions ?? {}) },
        };

  async function fetchHandler(req: Request, server: UpgradeServer): Promise<Response | undefined> {
    // Behind a trusted proxy (nginx / gateway), honor X-Forwarded-Proto/Host so
    // ctx.url reflects the external HTTPS scheme — makes CSRF/session cookies Secure.
    const url = resolveRequestUrl(req, deps.security?.trustProxy);
    const nonce = randomNonce();
    const secure = (res: Response): Response =>
      withSecurityHeaders(req, res, mode, runtimeSecurity, nonce);

    if (isRpcPath(url.pathname)) {
      let services: Map<string, ServiceImplementation | StreamImplementation>;
      try {
        services = await loadServices();
      } catch (error) {
        const app = process.env.WRNEXUS_APP_NAME ?? "app";
        const detail = error instanceof Error ? (error.stack ?? error.message) : String(error);
        console.error(`[wrnexus] RPC request failed to load services (${app})\n${detail}`);
        return secure(
          Response.json(
            { ok: false, code: "RPC_UNKNOWN", message: "Service unavailable", retryable: false },
            { headers: { "cache-control": "private, no-store" } },
          ),
        );
      }
      const rpcResponse = await handleRpcRequest(req, url, services);
      if (rpcResponse) return secure(rpcResponse);
    }

    const preflight = createCorsPreflightResponse(req, deps.security);
    if (preflight) return secure(preflight);

    // Health check — unauthenticated, skips the middleware pipeline.
    if (url.pathname === "/healthz" || url.pathname === "/__wrnexus/health") {
      return secure(await livenessHandler(req));
    }
    if (url.pathname === "/readyz" || url.pathname === "/__wrnexus/ready") {
      return secure(await readinessHandler(req));
    }
    if (webVitalsEnabled && url.pathname === webVitalsEndpoint) {
      // Validate same-origin telemetry against the proxy-resolved public URL,
      // rather than the internal listener URL used by the hosting platform.
      return secure(await webVitalsHandler(new Request(url, req)));
    }
    if (webVitalsEnabled && url.pathname === "/__wrnexus/vitals.js") {
      return secure(
        new Response(
          webVitalsClient({
            endpoint: webVitalsEndpoint,
            sampleRate: deps.observability?.sampleRate,
          }),
          {
            headers: {
              "content-type": "text/javascript; charset=utf-8",
              "cache-control": url.searchParams.has("v")
                ? "public, max-age=31536000, immutable"
                : "no-cache",
            },
          },
        ),
      );
    }

    if (url.pathname === "/site.webmanifest" && pwaEnabled) {
      const pwa = pwaConfig;
      return secure(
        Response.json(
          {
            id: pwa.id ?? pwa.startUrl ?? "/",
            name: pwa.name ?? deps.seo?.title ?? "WrNexus App",
            short_name: pwa.shortName ?? pwa.name ?? deps.seo?.title ?? "WrNexus",
            description: pwa.description ?? deps.seo?.description,
            start_url: pwa.startUrl ?? "/",
            scope: pwa.scope ?? "/",
            lang: pwa.lang ?? "en",
            display: pwa.display ?? "standalone",
            orientation: pwa.orientation ?? "any",
            theme_color: pwa.themeColor ?? deps.seo?.themeColor ?? "#6c8cff",
            background_color: pwa.backgroundColor ?? "#0f172a",
            icons: pwa.icons ?? [
              {
                src: "/__wrnexus/pwa-icon.svg",
                sizes: "any",
                type: "image/svg+xml",
                purpose: "any maskable",
              },
            ],
            categories: pwa.categories,
            screenshots: pwa.screenshots?.map((screenshot) => ({
              src: screenshot.src,
              sizes: screenshot.sizes,
              type: screenshot.type,
              form_factor: screenshot.formFactor,
              label: screenshot.label,
            })),
            shortcuts: pwa.shortcuts?.map((shortcut) => ({
              name: shortcut.name,
              short_name: shortcut.shortName,
              description: shortcut.description,
              url: shortcut.url,
              icons: shortcut.icons,
            })),
          },
          { headers: { "content-type": "application/manifest+json", "cache-control": "no-cache" } },
        ),
      );
    }
    if (url.pathname === "/__wrnexus/pwa-icon.svg" && pwaEnabled) {
      return secure(
        new Response(DEFAULT_PWA_ICON, {
          headers: { "content-type": "image/svg+xml", "cache-control": "public, max-age=86400" },
        }),
      );
    }
    if (url.pathname === "/sw.js" && pwaServiceWorkerEnabled) {
      return secure(
        new Response(renderPwaServiceWorker(pwaConfig), {
          headers: {
            "content-type": "text/javascript; charset=utf-8",
            "cache-control": "no-cache",
          },
        }),
      );
    }
    if (url.pathname === "/__wrnexus/pwa.js" && pwaServiceWorkerEnabled) {
      return secure(
        new Response(renderPwaClient(deps.assetVersion ?? "2"), {
          headers: {
            "content-type": "text/javascript; charset=utf-8",
            "cache-control": url.searchParams.has("v")
              ? "public, max-age=31536000, immutable"
              : "no-cache",
          },
        }),
      );
    }
    if (url.pathname === "/__wrnexus/pwa-dev-cleanup.js" && pwaDevCleanupEnabled) {
      return secure(
        new Response(PWA_DEV_CLEANUP_CLIENT, {
          headers: {
            "content-type": "text/javascript; charset=utf-8",
            "cache-control": "no-store",
          },
        }),
      );
    }
    if (url.pathname === "/__wrnexus/mobile.js" && deps.mobile?.enabled !== false) {
      return secure(
        new Response(MOBILE_CLIENT, {
          headers: {
            "content-type": "text/javascript; charset=utf-8",
            "cache-control": url.searchParams.has("v")
              ? "public, max-age=31536000, immutable"
              : "no-cache",
          },
        }),
      );
    }

    // Reject oversized request bodies early (DoS guard).
    const contentLength = Number(req.headers.get("content-length") ?? 0);
    if (contentLength > maxBodyBytes) {
      return secure(new Response("Payload Too Large", { status: 413 }));
    }

    if (deps.devToolbar) {
      const toolbarResponse = await handleDevToolbarRoute(req, {
        mode,
        root: deps.devToolbar.root,
        collector: deps.devToolbar.collector,
        editor: deps.devToolbar.config.editor,
        allowOpenEditor: deps.devToolbar.config.openEditor !== false,
        platform: deps.devToolbar.platform,
        panels: deps.devToolbar.panels,
      });
      if (toolbarResponse) return secure(toolbarResponse);
    }

    // --- HMR socket (dev only): upgrade before anything else. ---
    if (hmr && url.pathname === "/__wrnexus/hmr") {
      if (!isWebSocketOriginAllowed(req, deps.security)) return secure(forbiddenOrigin());
      const headers = Array.from(req.headers.entries()).filter(
        ([name]) => !/^sec-websocket-|^connection$|^upgrade$/i.test(name),
      );
      if (server.upgrade(req, { data: { kind: "hmr", baseUrl: url.origin, headers } })) {
        return undefined;
      }
      return secure(new Response("Expected a WebSocket upgrade request", { status: 426 }));
    }

    // --- Realtime: try to upgrade BEFORE the normal HTTP pipeline. ---
    const rt = router.matchRealtime(url.pathname);
    if (rt) {
      // Reject cross-site WebSocket handshakes (CSWSH) before doing any work.
      if (!isWebSocketOriginAllowed(req, deps.security)) return secure(forbiddenOrigin());
      const mod = await loadModule(rt.route.file);
      let data: WsData;
      if (isRoomDefinition(mod.default)) {
        // Identify the connection: an authenticated session user id, else ?user=.
        const wsCtx = createContext(req, url);
        const sessionUser = wsCtx.session.get<{ id?: unknown }>("user");
        const user =
          sessionUser && typeof sessionUser === "object" && sessionUser.id != null
            ? String(sessionUser.id)
            : (url.searchParams.get("user") ?? undefined);
        const query = Object.fromEntries(url.searchParams);
        // Room-level gate (e.g. require auth) — reject before accepting the socket.
        const allowed = await mod.default.handlers.authorize?.({
          user,
          query,
          headers: req.headers,
        });
        if (allowed === false) {
          return secure(new Response("Forbidden", { status: 403 }));
        }
        data = { kind: "room", meta: { room: url.pathname, def: mod.default, query, user } };
      } else if (mod.websocket && typeof mod.websocket === "object") {
        data = { kind: "realtime", handler: mod.websocket as WsHandler };
      } else {
        return secure(
          new Response("Realtime route needs a default defineRoom() or a `websocket` export", {
            status: 500,
          }),
        );
      }
      const upgraded = server.upgrade(req, { data });
      if (upgraded) return undefined; // Bun takes over the socket.
      return secure(new Response("Expected a WebSocket upgrade request", { status: 426 }));
    }

    try {
      const ctx = createContext(req, url);
      initializeRequestCache(ctx);
      ctx.ip = server.requestIP?.(req)?.address ?? undefined;
      ctx.locals.cspNonce = nonce; // available to pages for their own inline scripts
      if (!["GET", "HEAD", "OPTIONS"].includes(req.method.toUpperCase())) {
        const contentType = req.headers.get("content-type") ?? "";
        if (contentType.includes("form")) {
          try {
            const form = await req.clone().formData();
            const token = form.get("_csrf");
            if (typeof token === "string") ctx.locals._csrf = token;
          } catch {
            // The endpoint will return its normal malformed-input response.
          }
        }
      }
      // Resolve the request language so both pages and API can translate.
      if (deps.i18n) {
        ctx.lang = resolveLang(
          deps.i18n,
          ctx.cookies.get(deps.i18n.cookie.name),
          req.headers.get("accept-language"),
        );
        ctx.t = makeT(deps.i18n, ctx.lang);
      }
      const mws = await resolveMiddleware();
      const res = secure(
        withContextHeaders(ctx, await runMiddleware(mws, ctx, () => dispatch(ctx))),
      );
      return compressResponse(req, res);
    } catch (err) {
      const app = process.env.WRNEXUS_APP_NAME ?? "app";
      let detail =
        err instanceof Error ? (err.stack ?? `${err.name}: ${err.message}`) : String(err);
      const message = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
      // AggregateError (e.g. Bun.build() "Bundle failed") hides its real cause in
      // `.errors` — the top-level message/stack alone is useless for diagnosing a
      // failed bundle. Print every nested error so the actual failure is visible.
      const nested = (err as { errors?: unknown[] } | undefined)?.errors;
      if (Array.isArray(nested) && nested.length) {
        detail +=
          "\n  caused by:\n" +
          nested
            .map((e, i) => `  [${i}] ${e instanceof Error ? (e.stack ?? e.message) : String(e)}`)
            .join("\n");
      }
      console.error(
        `[wrnexus] unhandled request error (${app}) ${req.method} ${url.pathname}\n${detail}`,
      );
      deps.devToolbar?.collector.add(
        issueFromError(err, {
          ruleId: "server/request-error",
          category: "server",
          title: "Request processing failed",
          pathname: url.pathname,
        }),
      );
      const response = secure(renderError(err, mode));
      // Gateway-managed production apps bind to loopback. Carry a bounded,
      // encoded diagnostic to the parent gateway so centralized log collectors
      // can explain child failures; the gateway always strips this header.
      response.headers.set("x-wrnexus-internal-error", encodeURIComponent(message.slice(0, 500)));
      return compressResponse(req, response);
    }
  }

  async function dispatch(ctx: Context): Promise<Response> {
    const { pathname } = ctx.url;

    if (pathname === "/__wrnexus/cache") {
      if (mode !== "development") return new Response("Not Found", { status: 404 });
      return Response.json(cache.inspect(), { headers: { "cache-control": "no-store" } });
    }

    // Framework-owned assets (island chunks, reactive runtime, HMR stream).
    if (pathname === "/__wrnexus/csr") {
      return handleCsrBinding(ctx);
    }
    if (pathname === "/__wrnexus/client-load") {
      return handleClientLoad(ctx);
    }

    if (pathname.startsWith("/__wrnexus/")) {
      const res = await assets.serve(pathname);
      return res ?? new Response("Not Found", { status: 404 });
    }

    if (pathname === "/api" || pathname.startsWith("/api/")) {
      return handleApi(ctx);
    }

    const publicAsset = await assets.serve(pathname);
    if (publicAsset) return publicAsset;

    return handlePage(ctx);
  }

  async function handleCsrBinding(ctx: Context): Promise<Response> {
    const routePath = ctx.url.searchParams.get("route") ?? "";
    const id = ctx.url.searchParams.get("id") ?? "";

    if (
      !routePath.startsWith("/") ||
      routePath.startsWith("/__wrnexus/") ||
      !isSafeRequestPath(routePath) ||
      !/^[A-Za-z0-9_-]+$/.test(id)
    ) {
      return new Response("Not Found", { status: 404 });
    }

    const page = router.matchPage(routePath);
    if (!page) return new Response("Not Found", { status: 404 });

    const pageModule = await loadModule(page.route.file);
    const bindings = pageModule.__wrnexusCsr;
    if (!Array.isArray(bindings)) return new Response("Not Found", { status: 404 });

    const binding = bindings.find((item): item is CsrBinding => {
      if (!item || typeof item !== "object") return false;
      const candidate = item as Partial<CsrBinding>;
      return (
        candidate.id === id &&
        typeof candidate.path === "string" &&
        (candidate.method === undefined || typeof candidate.method === "string") &&
        (typeof candidate.body === "string" || typeof candidate.expr === "string") &&
        (candidate.helpers === undefined || typeof candidate.helpers === "string")
      );
    });
    if (!binding || !isSafeApiPath(binding.path)) return new Response("Not Found", { status: 404 });

    const data = await callApiFromContext(ctx, binding.path, binding.method ?? "GET");
    const value = evalData(ctx, data, binding.body ?? binding.expr ?? "", binding.helpers ?? "");

    return new Response(String(value), {
      headers: { "content-type": "text/plain; charset=utf-8" },
    });
  }

  async function handleClientLoad(ctx: Context): Promise<Response> {
    const routePath = ctx.url.searchParams.get("route") ?? "";
    const routeSearch = ctx.url.searchParams.get("search") ?? "";
    const name = ctx.url.searchParams.get("name") ?? "";
    if (
      !routePath.startsWith("/") ||
      routePath.startsWith("/__wrnexus/") ||
      !isSafeRequestPath(routePath) ||
      (routeSearch !== "" &&
        (!routeSearch.startsWith("?") || routeSearch.includes("#") || routeSearch.length > 4096)) ||
      !/^[A-Za-z_$][\w$]{0,63}$/.test(name)
    ) {
      return new Response("Not Found", { status: 404 });
    }
    const page = router.matchPage(routePath);
    if (!page) return new Response("Not Found", { status: 404 });
    const pageModule = await loadModule(page.route.file);
    const load = pageModule.__wrnexusClientLoad;
    if (typeof load !== "function") return new Response("Not Found", { status: 404 });
    try {
      const routeUrl = new URL(routePath + routeSearch, ctx.url.origin);
      if (routeUrl.pathname !== routePath) return new Response("Not Found", { status: 404 });
      const routeRequest = new Request(routeUrl, { method: "GET", headers: ctx.req.headers });
      const loadCtx: Context = { ...ctx, req: routeRequest, url: routeUrl, params: page.params };
      const values = await load(loadCtx);
      if (!values || typeof values !== "object" || !(name in values)) {
        return new Response("Not Found", { status: 404 });
      }
      return Response.json(
        { data: (values as Record<string, unknown>)[name] },
        { headers: { "cache-control": "private, no-store" } },
      );
    } catch {
      return Response.json(
        { error: { code: "CLIENT_LOAD_FAILED", message: "Client data loading failed." } },
        { status: 500, headers: { "cache-control": "private, no-store" } },
      );
    }
  }

  async function callApiFromContext(ctx: Context, path: string, method = "GET"): Promise<unknown> {
    if (!isSafeApiPath(path)) {
      throw new Error("Unsafe framework API path");
    }

    const normalizedMethod = method.toUpperCase();
    if (!HTTP_METHODS.includes(normalizedMethod as (typeof HTTP_METHODS)[number])) {
      throw new Error(`Unsupported framework API method: ${normalizedMethod}`);
    }

    const apiUrl = new URL(path, ctx.req.url);
    const apiReq = new Request(apiUrl, {
      method: normalizedMethod,
      headers: ctx.req.headers,
    });
    const apiCtx = createContext(apiReq, apiUrl);
    apiCtx.locals = ctx.locals;
    apiCtx.cookies = ctx.cookies;
    apiCtx.session = ctx.session;
    apiCtx.localStorage = ctx.localStorage;

    const apiRes = await handleApi(apiCtx);
    if (!apiRes.ok) {
      throw new Error(`API route ${apiUrl.pathname} returned ${apiRes.status}`);
    }

    const contentType = apiRes.headers.get("content-type") ?? "";
    return contentType.includes("application/json") ? await apiRes.json() : await apiRes.text();
  }

  function isSafeApiPath(pathname: string): boolean {
    return (
      (pathname === "/api" || pathname.startsWith("/api/")) &&
      isSafeRequestPath(pathname) &&
      !pathname.includes("?") &&
      !pathname.includes("#")
    );
  }

  function evalData(ctx: Context, data: unknown, body: string, helpers = ""): unknown {
    const adapters = {
      cookies: ctx.cookies,
      session: ctx.session,
      localStorage: ctx.localStorage,
    };
    return new Function(
      "$data",
      "$adapters",
      "const cookies = $adapters.cookies;\nconst session = $adapters.session;\nconst localStorage = $adapters.localStorage;\nwith ($data ?? {}) {\n" +
        helpers +
        "\n" +
        body +
        "\n}",
    )(data, adapters);
  }

  async function handleApi(ctx: Context): Promise<Response> {
    const matched = router.matchApi(ctx.url.pathname);
    if (!matched) return Response.json({ error: "Not Found" }, { status: 404 });

    // Expose the canonical matched route to package dispatchers. A package may
    // contribute several URL paths from one module, and request URLs can be
    // rewritten by gateways or internal framework calls. The router match is
    // the authoritative route identity.
    ctx.params = matched.params;
    ctx.locals.__wrnexusRoute = matched.route.raw;
    ctx.locals.__wrnexusRouteKind = "api";

    const mod = await loadModule(matched.route.file);
    const method = ctx.req.method.toUpperCase();
    const embeddedApi = mod.__wrnexusApi as ApiRegistry | undefined;
    const handler = embeddedApi?.[`${method} ${matched.route.raw}`] ?? mod[method];

    if (typeof handler !== "function") {
      const allowed = HTTP_METHODS.filter(
        (m) =>
          typeof mod[m] === "function" ||
          typeof embeddedApi?.[`${m} ${matched.route.raw}`] === "function",
      );
      return new Response("Method Not Allowed", {
        status: 405,
        headers: { Allow: allowed.join(", ") },
      });
    }

    return (await handler(ctx)) as Response;
  }

  /**
   * Resolve `<tag data-component="name" ...props></tag>` mounts by rendering the
   * matching `.wrn` component on the server and splicing its HTML in place.
   * Runs before script collection so the injected `data-scope` is visible.
   * Recurses so components can mount other components.
   */
  async function renderComponents(
    body: string,
    translate: TFunction = (key) => key,
    language?: string,
    depth = 0,
  ): Promise<string> {
    if (depth > 15 || router.components.length === 0 || !body.includes("data-component=")) {
      return body;
    }

    let result = "";
    let i = 0;
    for (;;) {
      const m = MOUNT_OPEN_RE.exec(body.slice(i));
      if (!m) {
        result += body.slice(i);
        break;
      }
      const tagStart = i + m.index;
      result += body.slice(i, tagStart);

      const [open, tag, attrStr, name, selfClose] = m;
      const openEnd = tagStart + open.length;
      const { inner, end } =
        selfClose === "/" ? { inner: "", end: openEnd } : readElementBody(body, tag!, openEnd);
      i = end;

      const normalizedName = normalizeComponentName(name);
      const component = router.components.find(
        (candidate) => normalizeComponentName(candidate.name) === normalizedName,
      );
      if (!component) {
        console.warn(`[wrnexus] no component registered for '${name}'`);
        result += body.slice(tagStart, end);
        continue;
      }
      try {
        const mod = await loadModule(component.file);
        const render = (mod as { render?: (props: Record<string, string>) => string }).render;
        if (typeof render !== "function") {
          console.warn(`[wrnexus] component '${name}' has no render() export`);
          result += body.slice(tagStart, end);
          continue;
        }
        // Render, drop the mount's children into the component's <slot>, then
        // recurse so components-in-the-view and components-in-the-children resolve.
        // Props may carry `{t:key}` i18n markers — resolve them for the active
        // language before handing them to the component.
        const props = resolveTProps(parseComponentProps(attrStr!), translate);
        if (normalizedName === "languageswitcher" && deps.i18n) {
          props.locales ??= JSON.stringify(
            deps.i18n.langs.map((locale) => ({
              value: locale,
              label: deps.i18n?.labels[locale] ?? locale.toUpperCase(),
              shortLabel: locale.split("-")[0]!.toUpperCase(),
            })),
          );
          props.current ??=
            language && deps.i18n.langs.includes(language) ? language : deps.i18n.default;
        }
        const policy = (mod.__wrnexusCache ?? {}) as Record<string, string>;
        const strategy = policy.strategy?.toLowerCase();
        const renderComponent = () => fillSlots(String(render(props)), inner, true);
        const rendered =
          strategy && !["none", "no-store", "request"].includes(strategy)
            ? await cache.getOrLoad(
                "component",
                `${normalizedName}:${language}:${JSON.stringify(props)}:${inner}`,
                renderComponent,
                {
                  ttlMs: cacheDuration(policy.ttl, 60_000),
                  staleWhileRevalidateMs:
                    strategy === "stale-while-revalidate"
                      ? cacheDuration(policy.stale ?? policy.ttl, 60_000)
                      : 0,
                  tags: cacheList(policy.tags),
                },
              )
            : renderComponent();
        result += await renderComponents(rendered, translate, language, depth + 1);
      } catch (err) {
        console.error(`[wrnexus] component '${name}' failed to render`, err);
        deps.devToolbar?.collector.add(
          issueFromError(err, {
            ruleId: "server/component-render-error",
            category: "server",
            title: `Component '${name}' failed to render`,
            source: { file: component.file, component: name },
          }),
        );
        result += body.slice(tagStart, end);
      }
    }
    return result;
  }

  async function handlePage(ctx: Context): Promise<Response> {
    // Page rendering is also reached by HMR synchronization and internal
    // dispatches, so never rely exclusively on the public fetch initializer.
    initializeRequestCache(ctx);
    const isMobileRequest =
      ctx.req.headers.get("x-wrnexus-mobile") === "1" ||
      new RegExp(deps.mobile?.userAgent ?? "WrNexusMobile", "i").test(
        ctx.req.headers.get("user-agent") ?? "",
      );
    const matched = router.matchPage(ctx.url.pathname);
    if (!matched) {
      if (deps.devToolbar && shouldReportNotFound(ctx.url.pathname)) {
        deps.devToolbar.collector.add(
          createServerIssue({
            ruleId: "routing/not-found",
            category: "routing",
            severity: "warning",
            title: "Route not found",
            message: `No page route matched ${ctx.url.pathname}.`,
            pathname: ctx.url.pathname,
            recommendation:
              "Verify the URL, page filename, dynamic route parameters, and route casing.",
          }),
        );
      }
      const response = renderNotFound();
      if (!isMobileRequest) return response;
      const headers = new Headers(response.headers);
      headers.set("x-wrnexus-original-status", "404");
      // Android WebView may replace a main-frame HTTP error with its own error
      // surface. Keep the WrNexus 404 document renderable inside Capacitor.
      return new Response(response.body, { status: 200, headers });
    }

    // Issue the CSRF token cookie so forms on this page can echo it back.
    const pageCsrf = csrfToken(ctx);
    const storeContainer = requestStoreContainer(ctx.req, matched.route.raw);

    const mod = await loadModule(matched.route.file);
    if (ctx.req.method.toUpperCase() === "POST") {
      const actionResponse = await handlePageAction(ctx, mod);
      if (actionResponse) return actionResponse;
    }
    const component = mod.default;
    if (typeof component !== "function") {
      throw new Error(`Page ${matched.route.file} has no default export`);
    }

    ctx.params = matched.params;
    const meta = (mod.meta ?? {}) as PageMeta;
    const pageNavigation = (mod.__wrnexusNavigation ?? {}) as { preserve?: string };
    const pageCache = (mod.__wrnexusCache ?? {}) as Record<string, string>;
    const fullPageEnabled = ["page", "full-page"].includes(pageCache.scope?.toLowerCase() ?? "");
    const fullPageKey = `page:${matched.route.raw}:${ctx.url.search}:${cacheIdentity(ctx, [
      "language",
      `cookie:${THEME_COOKIE}`,
      `cookie:${ACCENT_COOKIE}`,
      ...cacheList(pageCache.vary),
    ])}`;
    if (fullPageEnabled && ["GET", "HEAD"].includes(ctx.req.method.toUpperCase())) {
      const pageHit = cache.page.lookup(fullPageKey);
      if (pageHit.state === "fresh") {
        const cached = pageHit.entry.value as { html: string; etag: string; nonce: string };
        const restoredHtml = cached.nonce
          ? cached.html.replaceAll(
              `nonce="${cached.nonce}"`,
              `nonce="${String(ctx.locals.cspNonce ?? "")}"`,
            )
          : cached.html;
        await disposeRequestStores(ctx.req);
        return new Response(ctx.req.method.toUpperCase() === "HEAD" ? null : restoredHtml, {
          headers: {
            "content-type": "text/html; charset=utf-8",
            etag: cached.etag,
            "cache-control": "public, max-age=0, must-revalidate",
            "x-wrnexus-page-cache": "HIT",
          },
        });
      }
    }
    let dataCacheState: "HIT" | "STALE" | "MISS" | "BYPASS" = "BYPASS";
    const preserve = (pageNavigation.preserve ?? "")
      .match(/(?:scroll|forms|tabs|expanded|filters|pagination|component|workflow)/g)
      ?.filter((value, index, values) => values.indexOf(value) === index)
      .join(",");
    const pageCtx = ctx as Context & {
      __wrnexusCallApi?: (path: string, method?: string) => Promise<unknown>;
      __wrnexusUseStore?: (definition: StoreDefinition<any, any, any>) => Promise<unknown>;
    };
    pageCtx.__wrnexusCallApi = (path, method = "GET") => callApiFromContext(ctx, path, method);
    pageCtx.__wrnexusUseStore = (definition) => storeContainer.use(definition);
    const load = mod.__wrnexusLoad as ((ctx: Context) => Promise<unknown>) | undefined;
    if (typeof load === "function") {
      const strategy = pageCache.strategy?.toLowerCase();
      const cacheEnabled = Boolean(strategy && !["none", "no-store", "request"].includes(strategy));
      let data: unknown;
      if (cacheEnabled && ["GET", "HEAD"].includes(ctx.req.method.toUpperCase())) {
        const key = `route:${matched.route.raw}:${ctx.url.search}:${cacheIdentity(ctx, cacheList(pageCache.vary))}`;
        const lookup = cache.data.lookup(key);
        dataCacheState =
          lookup.state === "fresh" ? "HIT" : lookup.state === "stale" ? "STALE" : "MISS";
        data = await cache.getOrLoad("data", key, () => load(pageCtx), {
          ttlMs: cacheDuration(pageCache.ttl, 60_000),
          staleWhileRevalidateMs:
            strategy === "stale-while-revalidate"
              ? cacheDuration(pageCache.stale ?? pageCache.ttl, 60_000)
              : 0,
          tags: cacheList(pageCache.tags),
        });
      } else {
        data = await (
          ctx.locals.requestCache as {
            getOrLoad<V>(key: string, loader: () => Promise<V>): Promise<V>;
          }
        ).getOrLoad(`route:${matched.route.raw}`, () => load(pageCtx));
      }
      (pageCtx as Context & { data?: unknown }).data = data;
      if (data && typeof data === "object") Object.assign(pageCtx, data);
    }
    let body = await renderComponents(String(await component(pageCtx)), ctx.t, ctx.lang);
    const partial = (mod as { __wrnexusRender?: string }).__wrnexusRender === "partial-static";
    const precomputedShell = (mod as { __wrnexusStaticShell?: unknown }).__wrnexusStaticShell;
    const pagePartial = partial ? partialPrerender(body) : undefined;
    if (partial) {
      body = typeof precomputedShell === "string" ? precomputedShell : (pagePartial?.shell ?? body);
    }
    const routeParamsMarker = `<span hidden data-wrn-route-params="${escapeHtml(
      JSON.stringify(ctx.params ?? {}),
    )}"></span>`;
    body = `${routeParamsMarker}${body}`;
    if (body.includes("data-wrn-action=")) {
      body = body.replace(
        /(<form\b[^>]*\bdata-wrn-action=(?:"[^"]+"|'[^']+')[^>]*>)/gi,
        `$1<input type="hidden" name="_csrf" value="${pageCsrf}">`,
      );
    }
    const resolvedTheme = deps.theme
      ? resolveThemeName(ctx.cookies.get(THEME_COOKIE), deps.theme)
      : "";
    const language = deps.i18n && ctx.lang ? ctx.lang : (meta.lang ?? deps.seo?.lang ?? "en");
    let documentTemplate: string | undefined;

    // Page layout: a page selects one by exporting `layout = "<name>"`
    // (app/layouts/<name>.wrn), else falls back to a `default` layout if one
    // exists. `layout = "none"` opts out. The layout wraps the body via <slot>.
    const importedLayout =
      mod.layout && typeof mod.layout === "object"
        ? (mod.layout as { name?: string; render?: (props: Record<string, unknown>) => string })
        : undefined;
    const layoutName =
      (isMobileRequest && deps.mobile?.layout
        ? deps.mobile.layout
        : typeof mod.layout === "string"
          ? mod.layout
          : importedLayout?.name) ?? "default";
    const layout = importedLayout
      ? undefined
      : layoutName === "none"
        ? undefined
        : router.layouts.find((l) => l.name === layoutName);
    if (importedLayout?.render) {
      body = await renderComponents(
        fillSlots(String(importedLayout.render({})), body),
        ctx.t,
        ctx.lang,
      );
    } else if (layout) {
      try {
        const layoutMod = await loadModule(layout.file);
        const layoutRender = (layoutMod as { render?: (p: Record<string, string>) => string })
          .render;
        if (typeof layoutRender === "function") {
          body = await renderComponents(fillSlots(String(layoutRender({})), body), ctx.t, ctx.lang);
        }
      } catch (err) {
        console.error(`[wrnexus] layout '${layoutName}' failed to render`, err);
        deps.devToolbar?.collector.add(
          issueFromError(err, {
            ruleId: "server/layout-render-error",
            category: "server",
            title: `Layout '${layoutName}' failed to render`,
            pathname: ctx.url.pathname,
            source: { file: layout.file },
          }),
        );
      }
    }

    // A conventional app/layouts/document.wrn is a request-aware, top-level
    // document shell. It wraps the selected page layout and may own html, head,
    // body, and #app. The SSR renderer still merges framework metadata/assets.
    const documentLayout =
      layoutName === "document"
        ? undefined
        : router.layouts.find((item) => item.name === "document");
    if (documentLayout) {
      try {
        const documentMod = await loadModule(documentLayout.file);
        const documentRender = (
          documentMod as { render?: (props: Record<string, unknown>) => string }
        ).render;
        if (typeof documentRender === "function") {
          const rendered = String(
            documentRender({
              cookies: ctx.cookies.getAll(),
              theme: resolvedTheme,
              language,
              url: ctx.url.toString(),
              pathname: ctx.url.pathname,
            }),
          );
          documentTemplate = await renderComponents(fillSlots(rendered, body), ctx.t, ctx.lang);
          body = documentTemplate;
        }
      } catch (err) {
        console.error("[wrnexus] document layout failed to render", err);
        deps.devToolbar?.collector.add(
          issueFromError(err, {
            ruleId: "server/document-layout-render-error",
            category: "server",
            title: "Document layout failed to render",
            pathname: ctx.url.pathname,
            source: { file: documentLayout.file },
          }),
        );
      }
    }

    if (isMobileRequest) body = revealMobileOnlyHtml(body);

    // i18n: resolve `{t:key}` / `t:attr` markers against the request language.
    if (deps.i18n) {
      body = translateHtml(body, ctx.t);
      // renderDocument prefers the complete document template when one was
      // rendered. Keep it in sync with the translated body; otherwise markers
      // owned by document/page layouts are replaced by the stale pre-translation
      // template even though page-only responses translate correctly.
      if (documentTemplate) documentTemplate = body;
    }

    // Point 3: only ship the JS this page actually uses.
    const scripts = collectScripts(body, deps.clientRuntimes, deps.navigation).map((script) =>
      versionRenderScript(script, deps.assetVersion),
    );
    if (webVitalsEnabled) {
      scripts.push(versionAssetUrl("/__wrnexus/vitals.js", deps.assetVersion));
    }
    if (pwaServiceWorkerEnabled)
      scripts.push(versionAssetUrl("/__wrnexus/pwa.js", deps.assetVersion));
    if (pwaDevCleanupEnabled) scripts.push("/__wrnexus/pwa-dev-cleanup.js");
    if (deps.mobile?.enabled !== false && usesMobileRuntime(body))
      scripts.push(versionAssetUrl("/__wrnexus/mobile.js", deps.assetVersion));

    // <html> attributes: no-flash theme (from cookie, validated) + active lang.
    const attrs: string[] = [];
    let activeThemeHead = "";

    if (deps.theme) {
      const themeName = resolveThemeName(ctx.cookies.get(THEME_COOKIE), deps.theme);

      attrs.push(`data-theme="${themeName}"`);

      const accentName = resolveAccentName(ctx.cookies.get(ACCENT_COOKIE), deps.theme);

      if (accentName) {
        attrs.push(`data-accent="${accentName}"`);
      }
      if (!deps.stylesIncludeFramework) {
        const themeHref = versionAssetUrl(
          activeThemeCssHref(themeName, accentName),
          deps.assetVersion,
        );
        activeThemeHead = `<link rel="stylesheet" data-wrnexus-theme href="${themeHref}" />`;
      }
    }
    attrs.push(`lang="${safeLanguageTag(language)}"`);
    if (deps.i18n) attrs.push(`dir="${deps.i18n.direction[language] ?? "ltr"}"`);
    const htmlAttrs = attrs.length ? ` ${attrs.join(" ")}` : undefined;

    const outerPartial = partial
      ? partialPrerender(body, pagePartial?.regions.length ?? 0)
      : undefined;
    const partialRegions = [...(pagePartial?.regions ?? []), ...(outerPartial?.regions ?? [])];
    const renderedBody = outerPartial?.shell ?? body;
    if (partial && documentTemplate) documentTemplate = renderedBody;

    let html = renderDocument({
      meta,
      seo: deps.seo,
      url: ctx.url,
      body: renderedBody,
      scripts,
      extraHead: [
        preserve ? `<meta name="wrnexus-preserve" content="${preserve}" />` : "",
        pwaEnabled ? `<link rel="manifest" href="/site.webmanifest" />` : "",
        pwaEnabled ? `<meta name="mobile-web-app-capable" content="yes" />` : "",
        pwaEnabled ? `<meta name="apple-mobile-web-app-capable" content="yes" />` : "",
        activeThemeHead,
        extraHead,
      ]
        .filter(Boolean)
        .join("\n    "),
      extraBody:
        [
          renderStoreHydration(storeContainer, (ctx.locals.cspNonce as string) ?? undefined),
          deps.i18n
            ? `<script${ctx.locals.cspNonce ? ` nonce="${String(ctx.locals.cspNonce)}"` : ""}>${renderI18nData(deps.i18n, language)}</script>`
            : "",
          hmr ? hmrClientTag((ctx.locals.cspNonce as string) ?? "") : "",
          shouldEnableDevToolbar(mode, deps) ? DEV_TOOLBAR_SCRIPT : "",
        ]
          .filter(Boolean)
          .join("\n") || undefined,
      htmlAttrs,
      documentTemplate,
      styleNonce: (ctx.locals.cspNonce as string | undefined) ?? undefined,
    });
    if (deps.renderHtml) html = await deps.renderHtml(html);
    // Conditional GET: hash the page CONTENT (`body`), not the assembled shell —
    // the shell carries a per-request CSP nonce in dev, which would otherwise make
    // the ETag change every request. Same content → same ETag → 304 on revalidate.
    const tag = etag(`${htmlAttrs ?? ""}\n${JSON.stringify(scripts)}\n${body}`);
    if (
      fullPageEnabled &&
      ["GET", "HEAD"].includes(ctx.req.method.toUpperCase()) &&
      !/name=["']_csrf["']/.test(html)
    ) {
      cache.page.set(
        fullPageKey,
        { html, etag: tag, nonce: String(ctx.locals.cspNonce ?? "") },
        {
          ttlMs: cacheDuration(pageCache.ttl, 60_000),
          staleWhileRevalidateMs:
            pageCache.strategy?.toLowerCase() === "stale-while-revalidate"
              ? cacheDuration(pageCache.stale ?? pageCache.ttl, 60_000)
              : 0,
          tags: cacheList(pageCache.tags),
        },
      );
    }
    await disposeRequestStores(ctx.req);
    const method = ctx.req.method.toUpperCase();
    if ((method === "GET" || method === "HEAD") && notModified(ctx.req, tag)) {
      return new Response(null, {
        status: 304,
        headers: { etag: tag, "cache-control": "private, no-cache" },
      });
    }
    return new Response(
      partial && method !== "HEAD"
        ? streamPartialDocument(
            { shell: html, regions: partialRegions },
            (ctx.locals.cspNonce as string | undefined) ?? undefined,
          )
        : html,
      {
        headers: {
          "content-type": "text/html; charset=utf-8",
          etag: tag,
          "cache-control": "private, no-cache",
          "x-wrnexus-data-cache": dataCacheState,
          ...(partial ? { "x-wrnexus-render": "partial-static" } : {}),
          ...(partial && typeof precomputedShell === "string"
            ? { "x-wrnexus-static-shell": "build" }
            : {}),
          ...(fullPageEnabled ? { "x-wrnexus-page-cache": "MISS" } : {}),
          ...(shouldEnableDevToolbar(mode, deps)
            ? {
                "x-wrnexus-dev-toolbar": "enabled",
                "x-wrnexus-route": matched.route.raw,
              }
            : {}),
        },
      },
    );
  }

  async function actionInput(request: Request): Promise<{ name?: string; input: unknown }> {
    const contentType = request.headers.get("content-type") ?? "";
    if (contentType.includes("application/json")) {
      return {
        name: request.headers.get("x-wrnexus-action") ?? undefined,
        input: await request.json(),
      };
    }
    const form = await request.formData();
    const input: Record<string, unknown> = {};
    for (const [key, value] of form) {
      if (key === "_wrnexus_action" || key === "_csrf") continue;
      if (!(key in input)) input[key] = value;
      else
        input[key] = Array.isArray(input[key])
          ? [...(input[key] as unknown[]), value]
          : [input[key], value];
    }
    const submittedName = form.get("_wrnexus_action");
    return {
      name:
        request.headers.get("x-wrnexus-action") ??
        (typeof submittedName === "string" ? submittedName : undefined),
      input,
    };
  }

  async function handlePageAction(ctx: Context, mod: RouteModule): Promise<Response | null> {
    const actions = mod.__wrnexusActions as Record<string, ActionEntry> | undefined;
    if (!actions) return null;
    let submitted: Awaited<ReturnType<typeof actionInput>>;
    try {
      submitted = await actionInput(ctx.req);
    } catch {
      return Response.json({ error: "Malformed action input" }, { status: 400 });
    }
    if (!submitted.name) return null;
    if (!/^[A-Za-z_$][\w$]*$/.test(submitted.name) || !actions[submitted.name]) {
      return Response.json({ error: "Unknown server action" }, { status: 404 });
    }
    const security = (mod.__wrnexusSecurity ?? {}) as Record<string, string>;
    if (/^(?:required|true)$/i.test(security.auth ?? "") && !ctx.user) {
      return Response.json({ error: "Authentication required" }, { status: 401 });
    }
    if (security.permission) {
      const permissions = ctx.locals.permissions;
      const allowed =
        typeof permissions === "function"
          ? await permissions(security.permission, ctx)
          : Array.isArray(permissions) && permissions.includes(security.permission);
      if (!allowed) return Response.json({ error: "Permission denied" }, { status: 403 });
    }
    if (security.csrf !== "false" && !verifyCsrf(ctx)) {
      return Response.json({ error: "Invalid CSRF token" }, { status: 403 });
    }
    const action = actions[submitted.name]!;
    let input = submitted.input;
    if (action.schema) {
      const parsed = action.schema.parse(input);
      if (!parsed.ok) {
        const acceptsJson = (ctx.req.headers.get("accept") ?? "").includes("application/json");
        if (acceptsJson)
          return Response.json(
            { error: "Validation failed", errors: parsed.errors },
            { status: 422 },
          );
        const errors = Object.entries(parsed.errors)
          .map(
            ([field, message]) =>
              `<li><strong>${escapeHtml(field)}</strong>: ${escapeHtml(message)}</li>`,
          )
          .join("");
        return new Response(
          `<!doctype html><title>Validation failed</title><h1>Validation failed</h1><ul>${errors}</ul><a href="${escapeHtml(ctx.url.pathname)}">Go back</a>`,
          {
            status: 422,
            headers: { "content-type": "text/html; charset=utf-8" },
          },
        );
      }
      input = parsed.value;
    }
    const data = await action.run(input, ctx);
    const invalidated = [
      ...new Set(
        Array.isArray(ctx.locals.__wrnexusInvalidatedTags)
          ? (ctx.locals.__wrnexusInvalidatedTags as string[])
          : [],
      ),
    ];
    if (invalidated.length) cache.invalidateTags(invalidated);
    if ((ctx.req.headers.get("accept") ?? "").includes("application/json")) {
      return Response.json({ ok: true, data, invalidated });
    }
    return new Response(null, {
      status: 303,
      headers: { location: ctx.url.pathname + ctx.url.search },
    });
  }

  const hub = deps.hub;

  async function handleHmrMessage(ws: Ws, message: string | Uint8Array): Promise<void> {
    let msg: { type?: unknown; path?: unknown };
    try {
      msg = JSON.parse(typeof message === "string" ? message : new TextDecoder().decode(message));
    } catch {
      return;
    }
    if (msg.type !== "sync") return;
    const path = typeof msg.path === "string" ? msg.path : "/";
    let url: URL;
    try {
      url = new URL(path, ws.data.kind === "hmr" ? ws.data.baseUrl : "http://localhost");
    } catch {
      ws.send(JSON.stringify({ type: "error", message: "Invalid HMR sync path" }));
      return;
    }
    if (
      url.origin !== (ws.data.kind === "hmr" ? ws.data.baseUrl : url.origin) ||
      !url.pathname.startsWith("/") ||
      url.pathname.startsWith("/__wrnexus/") ||
      !isSafeRequestPath(url.pathname)
    ) {
      ws.send(JSON.stringify({ type: "error", message: "Unsafe HMR sync path" }));
      return;
    }

    try {
      const headers = new Headers(ws.data.kind === "hmr" ? ws.data.headers : undefined);
      headers.set("accept", "text/html");
      headers.set("x-wrnexus-hmr", "1");
      const req = new Request(url, { headers });
      const ctx = createContext(req, url);
      initializeRequestCache(ctx);
      ctx.locals.cspNonce = randomNonce();
      if (deps.i18n) {
        ctx.lang = resolveLang(
          deps.i18n,
          ctx.cookies.get(deps.i18n.cookie.name),
          req.headers.get("accept-language"),
        );
        ctx.t = makeT(deps.i18n, ctx.lang);
      }
      const res = withContextHeaders(
        ctx,
        await runMiddleware(await resolveMiddleware(), ctx, () => dispatch(ctx)),
      );
      const html = await res.text();
      ws.send(JSON.stringify({ type: "html", html }));
    } catch (err) {
      if (mode === "development") console.error(err);
      ws.send(
        JSON.stringify({
          type: "error",
          message: err instanceof Error ? err.message : "Unknown HMR sync error",
        }),
      );
    }
  }

  return {
    fetch: fetchHandler,
    websocket: {
      open(ws) {
        if (ws.data?.kind === "hmr") hub?.add(ws);
        else if (ws.data?.kind === "room") void realtime.open(ws, ws.data.meta);
        else ws.data?.handler?.open?.(ws);
      },
      message(ws, message) {
        if (ws.data?.kind === "hmr") {
          void handleHmrMessage(ws, message);
          return;
        }
        if (ws.data?.kind === "room") void realtime.message(ws, message);
        else ws.data?.handler?.message?.(ws, message);
      },
      close(ws, code, reason) {
        if (ws.data?.kind === "hmr") hub?.remove(ws);
        else if (ws.data?.kind === "room") void realtime.close(ws);
        else ws.data?.handler?.close?.(ws, code, reason);
      },
      drain(ws) {
        if (ws.data?.kind === "hmr" || ws.data?.kind === "room") return;
        ws.data?.handler?.drain?.(ws);
      },
    },
  };
}

/**
 * Decode the handful of HTML entities the page compiler emits when escaping
 * attribute values, so component props arrive as the author wrote them.
 */
function decodeHtmlEntities(value: string): string {
  let decoded = value;
  // Static page attributes can be escaped by their author/generator and then
  // escaped again by the page compiler. Decode both layers so structured
  // component props arrive as valid JSON instead of `&quot;` text.
  for (let pass = 0; pass < 2; pass += 1) {
    const next = decoded
      .replace(/&quot;/g, '"')
      .replace(/&#39;/g, "'")
      .replace(/&lt;/g, "<")
      .replace(/&gt;/g, ">")
      .replace(/&amp;/g, "&");
    if (next === decoded) break;
    decoded = next;
  }
  return decoded;
}

/** Content types worth gzipping (text + text-like application types). */
const COMPRESSIBLE_TYPE =
  /^(?:text\/|application\/(?:json|xml|javascript|manifest\+json)|image\/svg\+xml)/i;
/** Below this size gzip's overhead isn't worth it. */
const COMPRESS_MIN_BYTES = 1024;
const COMPRESSED_RESPONSE_CACHE_MAX = 256;
const compressedResponseCache = new Map<
  string,
  { body: Uint8Array; headers: [string, string][]; status: number; statusText: string }
>();

function rememberCompressedResponse(
  key: string,
  value: { body: Uint8Array; headers: [string, string][]; status: number; statusText: string },
) {
  compressedResponseCache.delete(key);
  compressedResponseCache.set(key, value);
  if (compressedResponseCache.size > COMPRESSED_RESPONSE_CACHE_MAX) {
    const oldest = compressedResponseCache.keys().next().value;
    if (oldest) compressedResponseCache.delete(oldest);
  }
}

/**
 * Gzip a response when the client accepts it and the body is a compressible,
 * buffered (non-streaming) payload. Streaming/SSE responses opt out via
 * `Cache-Control: no-transform`, so they are never buffered here.
 */
async function compressResponse(req: Request, res: Response): Promise<Response> {
  if (req.method.toUpperCase() === "HEAD") return res;
  const accept = (req.headers.get("accept-encoding") ?? "").toLowerCase();
  const acceptsBrotli = /(?:^|,)\s*br(?:\s*;|\s*,|$)/.test(accept);
  const acceptsGzip = /(?:^|,)\s*gzip(?:\s*;|\s*,|$)/.test(accept);
  if (!acceptsBrotli && !acceptsGzip) return res;
  if (res.headers.get("content-encoding")) return res;
  if (res.status === 204 || res.status === 304) return res;
  if (!COMPRESSIBLE_TYPE.test(res.headers.get("content-type") ?? "")) return res;
  if ((res.headers.get("cache-control") ?? "").includes("no-transform")) return res;

  const immutable = (res.headers.get("cache-control") ?? "").includes("immutable");
  // Brotli is ideal for immutable assets because the result is cached. Prefer
  // substantially faster gzip for per-request HTML/JSON when the client allows
  // both, avoiding synchronous Brotli work on the request hot path.
  const preferredEncoding = acceptsBrotli && (immutable || !acceptsGzip) ? "br" : "gzip";
  const cacheKey = immutable ? `${req.url}\n${preferredEncoding}` : "";
  if (cacheKey) {
    const cached = compressedResponseCache.get(cacheKey);
    if (cached) {
      compressedResponseCache.delete(cacheKey);
      compressedResponseCache.set(cacheKey, cached);
      return new Response(cached.body.slice(), {
        status: cached.status,
        statusText: cached.statusText,
        headers: cached.headers,
      });
    }
  }

  const body = new Uint8Array(await res.arrayBuffer());
  if (body.length < COMPRESS_MIN_BYTES) {
    return new Response(body, {
      status: res.status,
      statusText: res.statusText,
      headers: res.headers,
    });
  }

  let encoded: Uint8Array;
  let encoding: "br" | "gzip";
  if (preferredEncoding === "br") {
    encoded = new Uint8Array(
      brotliCompressSync(body, {
        params: { [zlibConstants.BROTLI_PARAM_QUALITY]: 4 },
      }),
    );
    encoding = "br";
  } else {
    encoded = Bun.gzipSync(body);
    encoding = "gzip";
  }

  const headers = new Headers(res.headers);
  headers.set("content-encoding", encoding);
  headers.set("content-length", String(encoded.length));
  const vary = headers.get("Vary");
  if (!vary) headers.set("Vary", "Accept-Encoding");
  else if (!/\baccept-encoding\b/i.test(vary)) headers.set("Vary", `${vary}, Accept-Encoding`);
  const responseBody = new ArrayBuffer(encoded.byteLength);
  new Uint8Array(responseBody).set(encoded);
  if (cacheKey) {
    rememberCompressedResponse(cacheKey, {
      body: encoded.slice(),
      headers: [...headers.entries()],
      status: res.status,
      statusText: res.statusText,
    });
  }
  return new Response(responseBody, {
    status: res.status,
    statusText: res.statusText,
    headers,
  });
}

/** Opening tag of a component mount: captures tag, attrs, name, self-close. */
const MOUNT_OPEN_RE =
  /<([A-Za-z][A-Za-z0-9-]*)\b([^>]*?\bdata-component="([A-Za-z0-9_-]+)"[^>]*?)(\/?)>/;

/**
 * From `from` (just past a mount's opening `>`), return the element's inner HTML
 * and the index just past its matching close tag. Counts same-tag nesting so a
 * `<div data-component>` may contain other `<div>`s.
 */
/** Cache of per-tag "open tag" regexes so we don't recompile one per call. */
const OPEN_TAG_RE = new Map<string, RegExp>();
function openTagRe(tag: string): RegExp {
  let re = OPEN_TAG_RE.get(tag);
  if (!re) {
    re = new RegExp(`<${tag}\\b`, "g");
    OPEN_TAG_RE.set(tag, re);
  }
  return re;
}

export function readElementBody(
  html: string,
  tag: string,
  from: number,
): { inner: string; end: number } {
  const openRe = openTagRe(tag);
  const closeTag = `</${tag}>`;
  let depth = 1;
  let i = from;
  while (i < html.length) {
    const nextClose = html.indexOf(closeTag, i);
    if (nextClose === -1) return { inner: html.slice(from), end: html.length };
    openRe.lastIndex = i;
    const openMatch = openRe.exec(html);
    if (openMatch && openMatch.index < nextClose) {
      const gt = html.indexOf(">", openMatch.index);
      if (gt !== -1 && html[gt - 1] === "/") {
        i = gt + 1; // self-closing child: no depth change
      } else {
        depth++;
        i = gt === -1 ? html.length : gt + 1;
      }
    } else {
      depth--;
      if (depth === 0)
        return { inner: html.slice(from, nextClose), end: nextClose + closeTag.length };
      i = nextClose + closeTag.length;
    }
  }
  return { inner: html.slice(from), end: html.length };
}

/**
 * Split a mount's children into named slots and the default slot. Content inside
 * `<tag data-slot="name">…</tag>` goes to that named slot (the wrapper element is
 * dropped); everything else is the default slot.
 */
function extractSlots(inner: string): { named: Record<string, string>; def: string } {
  const named: Record<string, string> = {};
  if (!inner.includes("data-slot=")) return { named, def: inner };

  const SLOT_MOUNT_RE =
    /<([A-Za-z][A-Za-z0-9-]*)\b([^>]*?\bdata-slot="([A-Za-z0-9_-]+)"[^>]*?)(\/?)>/;
  let def = "";
  let i = 0;
  for (;;) {
    const m = SLOT_MOUNT_RE.exec(inner.slice(i));
    if (!m) {
      def += inner.slice(i);
      break;
    }
    const tagStart = i + m.index;
    def += inner.slice(i, tagStart);
    const [open, tag, , name, selfClose] = m;
    const openEnd = tagStart + open.length;
    const { inner: content, end } =
      selfClose === "/" ? { inner: "", end: openEnd } : readElementBody(inner, tag!, openEnd);
    named[name!] = content;
    i = end;
  }
  return { named, def };
}

/**
 * Replace `<slot>` elements in a component's output with the mount's children:
 *   <slot name="x">…</slot>  ← content from `data-slot="x"` on the mount
 *   <slot>…</slot>           ← everything else (the default slot)
 * A `<slot>fallback</slot>` keeps its fallback when nothing is provided.
 */
export function fillSlots(html: string, inner: string, markOwner = false): string {
  if (!/<slot\b/.test(html)) return html;
  const { named, def } = extractSlots(inner);
  const defTrimmed = def.trim();

  /*
   * Slot content is authored by whoever wrote the component tag, but it is
   * spliced *inside* the component's own [data-scope] root. Left unmarked the
   * client runtime hands it to the component -- so `@input` handlers and
   * `{state}` in slot content resolve against the component's scope, where
   * the page's functions and state do not exist (and same-named component
   * state silently shadows them).
   *
   * Wrapping it in <wrn-slot data-wrn-slot> gives the runtime an ownership
   * boundary to walk back out of, so the markup is hydrated by the scope that
   * actually wrote it. The wrapper is display: contents, so it adds an
   * ownership marker without adding a box to the layout.
   *
   * Only component mounts pass markOwner. Layouts also go through fillSlots,
   * but a layout wraps the whole page rather than being mounted inside a
   * parent scope, so there is no outer scope to hand its content back to.
   */
  const wrap = (content: string): string =>
    markOwner && content.trim() ? `<wrn-slot data-wrn-slot="">${content}</wrn-slot>` : content;

  return html
    .replace(/<slot\b[^>]*?\bname="([A-Za-z0-9_-]+)"[^>]*?\/>/g, (_m, name: string) =>
      wrap(named[name] ?? ""),
    )
    .replace(
      /<slot\b[^>]*?\bname="([A-Za-z0-9_-]+)"[^>]*?>([\s\S]*?)<\/slot>/g,
      (_m, name: string, fallback: string) =>
        named[name] != null && named[name]!.trim() ? wrap(named[name]!) : fallback,
    )
    .replace(/<slot\s*\/>/g, defTrimmed ? wrap(def) : "")
    .replace(/<slot\b[^>]*>([\s\S]*?)<\/slot>/g, (_m, fallback: string) =>
      defTrimmed ? wrap(def) : fallback,
    );
}

/**
 * Extract props from a `data-component` mount's attribute string. Every quoted
 * attribute other than `data-component` becomes a (string) prop; the component's
 * declared defaults coerce them to their final types.
 */
export function parseComponentProps(attrStr: string): Record<string, string> {
  const props: Record<string, string> = {};
  for (const m of attrStr.matchAll(/([A-Za-z_][\w-]*)(?:="([^"]*)")?/g)) {
    const key = m[1]!;
    if (key === "data-component") continue;
    props[key] = decodeHtmlEntities(m[2] ?? "");
  }
  return props;
}

/**
 * Resolve `{t:key}` i18n markers inside component prop values against the active
 * language, so authors can pass localized text into a component:
 * `<div data-component="badge" label="{t:status.new}">`.
 */
export function resolveTProps(
  props: Record<string, string>,
  translate: TFunction,
): Record<string, string> {
  for (const key of Object.keys(props)) {
    const value = props[key]!;
    if (value.includes("{t:")) {
      props[key] = value.replace(/\{t:\s*([^}]+?)\s*\}/g, (_m, tkey: string) =>
        translate(tkey.trim()),
      );
    }
  }
  return props;
}

/**
 * Normalize component identifiers for registry lookup.
 *
 * WRN declarations use PascalCase (`PublicHeader`) while explicit
 * `data-component` mounts commonly use kebab-case (`public-header`) or
 * snake_case (`public_header`). All three forms identify the same component.
 */
export function normalizeComponentName(name: string): string {
  return name.toLowerCase().replace(/[-_]/g, "");
}

function safeLanguageTag(value: string): string {
  return /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/.test(value) ? value : "en";
}

function cacheDuration(value: string | undefined, fallback: number): number {
  if (!value) return fallback;
  const match = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/i.exec(value.trim());
  if (!match) return fallback;
  const scale = { ms: 1, s: 1_000, m: 60_000, h: 3_600_000, d: 86_400_000 }[
    (match[2]?.toLowerCase() ?? "ms") as "ms" | "s" | "m" | "h" | "d"
  ];
  return Math.max(0, Number(match[1]) * scale);
}

function cacheList(value: string | undefined): string[] {
  if (!value) return [];
  try {
    const parsed = JSON.parse(value) as unknown;
    if (Array.isArray(parsed))
      return parsed.filter((item): item is string => typeof item === "string");
  } catch {
    // Fall through to a convenient comma-separated form.
  }
  return value
    .split(",")
    .map((item) => item.trim())
    .filter(Boolean);
}

function cacheIdentity(ctx: Context, vary: string[]): string {
  const user = ctx.user as { id?: unknown } | undefined;
  const values = new Map<string, string>();
  if (ctx.tenant?.id) values.set("tenant", String(ctx.tenant.id));
  if (user?.id !== undefined) values.set("user", String(user.id));
  for (const item of vary) {
    if (item === "tenant") values.set(item, String(ctx.tenant?.id ?? ""));
    else if (item === "user") values.set(item, String(user?.id ?? ""));
    else if (item === "language") values.set(item, ctx.lang);
    else if (item.startsWith("cookie:")) values.set(item, ctx.cookies.get(item.slice(7)) ?? "");
    else values.set(`header:${item}`, ctx.req.headers.get(item) ?? "");
  }
  return [...values.entries()]
    .sort()
    .map(([key, value]) => `${key}=${value}`)
    .join("|");
}

function versionAssetUrl(src: string, version?: string): string {
  if (!version) return src;
  return `${src}${src.includes("?") ? "&" : "?"}v=${encodeURIComponent(version)}`;
}

function versionRenderScript(script: RenderScript, version?: string): RenderScript {
  if (typeof script === "string") return versionAssetUrl(script, version);
  return { ...script, src: versionAssetUrl(script.src, version) } satisfies ScriptAsset;
}

function escapeStyleContent(css: string): string {
  return css.replace(/<\/style/gi, "<\\/style");
}
