From ac164789bdbb4a43809261b7e0c532f8048576e2 Mon Sep 17 00:00:00 2001 From: Ajay Ghanwat Date: Tue, 18 Aug 2026 19:25:40 +0530 Subject: [PATCH] fix(dev): serve the HMR client as an external script A document's CSP nonce is fixed at load, so an inline script delivered by a later response can never carry a nonce that document accepts. The HMR client is now served at /__wrnexus/hmr-client.js, which script-src 'self' already covers and which needs no nonce at all. This removes one of the two inline scripts CSP was blocking in development. The i18n data script is still blocked and needs the same treatment; it is shared with the CSR navigation and HMR parsers, so moving it spans @wrnexus/i18n, csr, and dev-server. Co-Authored-By: Claude Opus 5 --- packages/dev-server/src/assets.ts | 2 ++ packages/dev-server/src/runtime.ts | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/dev-server/src/assets.ts b/packages/dev-server/src/assets.ts index f0d94f85..d364b9a3 100644 --- a/packages/dev-server/src/assets.ts +++ b/packages/dev-server/src/assets.ts @@ -34,6 +34,7 @@ import type { AssetServer } from "./runtime.ts"; import { servePublicAsset } from "./public.ts"; import { servePluginAsset, type ServedPluginAsset } from "./plugin-assets.ts"; import { serveIslandArtifact, serveWrnBrowserArtifact } from "./pipeline.ts"; +import { HMR_CLIENT_HREF, HMR_CLIENT_JS } from "./runtime.ts"; /** Style inputs the dev asset server needs to build `/__wrnexus/styles.css`. */ export interface DevStyles { @@ -99,6 +100,7 @@ export function createDevAssetServer( if (pathname.startsWith("/__wrnexus/island/")) { return serveIslandArtifact(pathname) ?? new Response("Not Found", { status: 404 }); } + if (pathname === HMR_CLIENT_HREF) return jsResponse(HMR_CLIENT_JS); if (pathname === "/__wrnexus/islands.js") return jsResponse(getIslandRuntime(true)); if (pathname === "/__wrnexus/reactive.js") return jsResponse(getReactiveRuntime(true)); if (pathname === "/__wrnexus/controllers.js") diff --git a/packages/dev-server/src/runtime.ts b/packages/dev-server/src/runtime.ts index 22bfcf18..740d5823 100644 --- a/packages/dev-server/src/runtime.ts +++ b/packages/dev-server/src/runtime.ts @@ -878,8 +878,19 @@ function randomNonce(): string { } /** The dev HMR client as a nonce-tagged inline script (strict-CSP friendly). */ -function hmrClientTag(nonce: string): string { - return ``; +/** 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. */