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 connects in dynamic module loading + on-the-fly bundling; * prod connects 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 { renderI18nDataTag, 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 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; type ApiRegistry = Record; interface ActionEntry { run: (input: unknown, ctx: Context) => unknown | Promise; schema?: { parse(input: unknown): { ok: boolean; value: unknown; errors: Record }; }; } 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; } 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; /** Resolve the ordered middleware chain. */ getMiddleware(): Promise; /** Serve `/__wrnexus/*` assets. */ assets: AssetServer; /** When true, inject the global stylesheet link into every page head. */ hasStyles?: boolean; /** When true, inject the WrNexus 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` + ``. */ theme?: ResolvedTheme; /** Resolved i18n bundle: enables `ctx.t`, ``, 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; devToolbar?: { config: DevToolbarConfig; collector: DevToolbarCollector; root: string; platform?: DevToolbarPlatformSnapshot; panels?: DevToolbarPanel[] | (() => DevToolbarPanel[] | Promise); }; } const 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 = ` `; 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"); var i18nScript = doc.querySelector('script[type="application/json"][data-wrn-i18n]'); if (i18nScript) { try { var incomingI18n = JSON.parse(String(i18nScript.textContent || "{}")); var existingI18n = window.__wrnI18n || {}; incomingI18n.t = existingI18n.t; incomingI18n.set = existingI18n.set; window.__wrnI18n = incomingI18n; } catch (error) { console.error("[wrnexus] failed to synchronize i18n HMR data", error); } } 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.__wrnLang && typeof window.__wrnLang.bind === "function") { window.__wrnLang.bind(document); } if ( window.wrnTheme && typeof window.wrnTheme.bind === "function" ) { window.wrnTheme.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. // HMR fetches fresh HTML whose inline scripts carry a NEW server nonce, but a // document's CSP nonce is fixed at load and cannot be updated. Any node moved // across therefore has to be re-stamped with the live document's nonce or the // browser blocks it. function adoptNonce(node) { var nonce = currentDocumentNonce(); if (!nonce || !node || node.nodeType !== 1) return node; var stamp = function (element) { if (element.getAttribute("src")) return; element.setAttribute("nonce", nonce); try { element.nonce = nonce; } catch (error) { // Read-only in some engines; the attribute above is what CSP checks. } }; if (node.nodeName === "SCRIPT" || node.nodeName === "STYLE") stamp(node); if (node.querySelectorAll) { var nested = node.querySelectorAll("script,style"); for (var i = 0; i < nested.length; i++) stamp(nested[i]); } return node; } 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(adoptNonce(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(adoptNonce(t.cloneNode(true))); continue; } if (f.nodeType !== t.nodeType || (f.nodeType === 1 && f.nodeName !== t.nodeName)) { from.replaceChild(adoptNonce(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; // Never copy the incoming nonce: it belongs to the fetched document and // would replace the live nonce this document's CSP actually allows. for (i = 0; i < ta.length; i++) if (ta[i].name !== "nonce" && 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 (fa[i].name !== "nonce" && !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). */ /** Path the dev asset server publishes the HMR client on. */ export const HMR_CLIENT_HREF = "/__wrnexus/hmr-client.js"; /** * The HMR client is served as an external module rather than inlined. * * A document's CSP nonce is fixed at load, so an inline script arriving from a * later response — which is exactly what an HMR reload produces — can never * carry a nonce this document accepts. An external file is covered by * script-src 'self' and needs no nonce at all. */ function hmrClientTag(_nonce: string): string { return ``; } /** 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; 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 => [ ...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> | undefined; const loadServices = (): Promise> => { if (!servicesPromise) { servicesPromise = (async () => { const services = new Map(); 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 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 WrNexus 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( ``, ); } if (deps.hasUi && !deps.stylesIncludeUi && !deps.stylesIncludeFramework) { headParts.push( ``, ); } if (deps.inlineStyles) { headParts.push(``); } else if (deps.hasStyles) { const stylesHref = versionAssetUrl(STYLES_HREF, deps.assetVersion); headParts.push(``); headParts.push(``); } 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 { // 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; 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 { 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 { 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; 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 { 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)[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 { 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 { const matched = router.matchApi(ctx.url.pathname); if (!matched) { const fallback = router.matchApi("/api/404"); if (fallback && ctx.url.pathname !== "/api/404") { const originalPath = ctx.url.pathname; ctx.url.pathname = "/api/404"; try { const response = await handleApi(ctx); return new Response(response.body, { status: 404, headers: response.headers, }); } finally { ctx.url.pathname = originalPath; } } 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 `` 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 { 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 }).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 , 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; 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 { // 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.", }), ); } let response: Response; const fallback = router.matchPage("/404"); if (fallback && ctx.url.pathname !== "/404") { const originalPath = ctx.url.pathname; ctx.url.pathname = "/404"; try { const rendered = await handlePage(ctx); response = new Response(rendered.body, { status: 404, headers: rendered.headers, }); } finally { ctx.url.pathname = originalPath; } } else { 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; 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; __wrnexusUseStore?: (definition: StoreDefinition) => Promise; }; pageCtx.__wrnexusCallApi = (path, method = "GET") => callApiFromContext(ctx, path, method); pageCtx.__wrnexusUseStore = (definition) => storeContainer.use(definition); const load = mod.__wrnexusLoad as ((ctx: Context) => Promise) | 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(key: string, loader: () => Promise): Promise; } ).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 = ``; body = `${routeParamsMarker}${body}`; if (body.includes("data-wrn-action=")) { body = body.replace( /(]*\bdata-wrn-action=(?:"[^"]+"|'[^']+')[^>]*>)/gi, `$1`, ); } 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 = ""` // (app/layouts/.wrn), else falls back to a `default` layout if one // exists. `layout = "none"` opts out. The layout wraps the body via . const importedLayout = mod.layout && typeof mod.layout === "object" ? (mod.layout as { name?: string; render?: (props: Record) => 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 }) .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 } ).render; if (typeof documentRender === "function") { const rendered = String( documentRender({ 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)); // 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 = ``; } } 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 ? `` : "", pwaEnabled ? `` : "", pwaEnabled ? `` : "", pwaEnabled ? `` : "", activeThemeHead, extraHead, ] .filter(Boolean) .join("\n "), extraBody: [ renderStoreHydration(storeContainer, (ctx.locals.cspNonce as string) ?? undefined), deps.i18n ? renderI18nDataTag(deps.i18n, language) : "", 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 = {}; 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 { const actions = mod.__wrnexusActions as Record | undefined; if (!actions) return null; let submitted: Awaited>; 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; 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]) => `
  • ${escapeHtml(field)}: ${escapeHtml(message)}
  • `, ) .join(""); return new Response( `Validation failed

    Validation failed

      ${errors}
    Go back`, { 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 { 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 `"` text. for (let pass = 0; pass < 2; pass += 1) { const next = decoded .replace(/"/g, '"') .replace(/'/g, "'") .replace(/</g, "<") .replace(/>/g, ">") .replace(/&/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 { 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 * `
    ` may contain other `
    `s. */ /** Cache of per-tag "open tag" regexes so we don't recompile one per call. */ const OPEN_TAG_RE = new Map(); 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 = ``; 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 * `` goes to that named slot (the wrapper element is * dropped); everything else is the default slot. */ function extractSlots(inner: string): { named: Record; def: string } { const named: Record = {}; 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 `` elements in a component's output with the mount's children: * ← content from `data-slot="x"` on the mount * ← everything else (the default slot) * A `fallback` keeps its fallback when nothing is provided. */ export function fillSlots(html: string, inner: string, markOwner = false): string { if (!/ 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() ? `${content}` : content; return html .replace(/]*?\bname="([A-Za-z0-9_-]+)"[^>]*?\/>/g, (_m, name: string) => wrap(named[name] ?? ""), ) .replace( /]*?\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(//g, defTrimmed ? wrap(def) : "") .replace(/]*>([\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 { const props: Record = {}; 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: * `
    `. */ export function resolveTProps( props: Record, translate: TFunction, ): Record { 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(); 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"); }