feat: add safe project upgrades and production performance fixes

This commit is contained in:
2026-07-12 16:54:22 +05:30
parent a524c08126
commit 935b3103dd
68 changed files with 555 additions and 107 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/dev-server",
"version": "0.2.13",
"version": "0.2.14",
"type": "module",
"main": "src/index.ts",
"exports": {
+5
View File
@@ -75,6 +75,8 @@ export interface ProdOptions {
theme?: ResolvedTheme;
/** Absolute path to the pre-built Wire UI stylesheet (`ui.css`). */
uiCssPath?: string;
/** Combined production theme + Wire UI stylesheet. */
frameworkCssPath?: string;
/** Pre-built `window.__wireSchemas = {...}` script for client validation. */
schemasJs?: string;
/** Resolved i18n bundle (default lang + locale messages). */
@@ -216,6 +218,8 @@ function createProdAssetServer(opts: ProdOptions): AssetServer {
if (pathname === "/__wrnexus/theme.css") return serveFile(opts.themePath, CSS_HEADERS);
if (pathname === "/__wrnexus/theme.js") return serveFile(opts.themeJsPath, JS_HEADERS);
if (pathname === "/__wrnexus/ui.css") return serveFile(opts.uiCssPath, CSS_HEADERS);
if (pathname === "/__wrnexus/framework.css")
return serveFile(opts.frameworkCssPath, CSS_HEADERS);
if (pathname === "/__wrnexus/styles.css") return serveFile(opts.stylesPath, CSS_HEADERS);
return servePublicAsset(opts.publicDir, pathname, MODE);
},
@@ -279,6 +283,7 @@ export function createProductionHandlers(
assets,
hasStyles: !!opts.stylesPath,
hasUi: !!opts.uiCssPath,
hasFrameworkStyles: !!opts.frameworkCssPath,
theme: opts.theme,
i18n: opts.i18n,
inlineStyles: opts.inlineStyles,
+39 -9
View File
@@ -101,6 +101,8 @@ export interface RuntimeDeps {
hasStyles?: boolean;
/** When true, inject the Wire UI stylesheet link (`/__wrnexus/ui.css`). */
hasUi?: boolean;
/** Production build combined theme + UI stylesheet. */
hasFrameworkStyles?: 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. */
@@ -129,8 +131,14 @@ export interface RuntimeDeps {
realtimeBus?: RealtimeBus;
}
const PWA_CLIENT = `if ("serviceWorker" in navigator) {
addEventListener("load", () => navigator.serviceWorker.register("/sw.js").catch(() => {}));
export const PWA_CLIENT = `if ("serviceWorker" in navigator) {
var swUrl = "/sw.js";
if (window.trustedTypes) {
try {
swUrl = window.trustedTypes.createPolicy("wrnexus-pwa", { createScriptURL: function(value) { return value; } }).createScriptURL(swUrl);
} catch (_) {}
}
addEventListener("load", () => navigator.serviceWorker.register(swUrl).catch(() => {}));
}`;
function renderPwaServiceWorker(pwa: PwaConfig): string {
@@ -561,11 +569,15 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
// 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) {
if (deps.hasFrameworkStyles) {
headParts.push(
`<link rel="stylesheet" href="${versionAssetUrl("/__wrnexus/framework.css", deps.assetVersion)}" />`,
);
} else if (deps.theme) {
const themeHref = versionAssetUrl(THEME_CSS_HREF, deps.assetVersion);
headParts.push(`<link rel="stylesheet" href="${themeHref}" />`);
}
if (deps.hasUi) {
if (deps.hasUi && !deps.hasFrameworkStyles) {
headParts.push(
`<link rel="stylesheet" href="${versionAssetUrl("/__wrnexus/ui.css", deps.assetVersion)}" />`,
);
@@ -677,7 +689,9 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
new Response(PWA_CLIENT, {
headers: {
"content-type": "text/javascript; charset=utf-8",
"cache-control": "no-cache",
"cache-control": url.searchParams.has("v")
? "public, max-age=31536000, immutable"
: "no-cache",
},
}),
);
@@ -687,7 +701,9 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
new Response(MOBILE_CLIENT, {
headers: {
"content-type": "text/javascript; charset=utf-8",
"cache-control": "no-cache",
"cache-control": url.searchParams.has("v")
? "public, max-age=31536000, immutable"
: "no-cache",
},
}),
);
@@ -1046,14 +1062,17 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
// Point 3: only ship the JS this page actually uses.
const scripts = collectScripts(body).map((src) => versionAssetUrl(src, deps.assetVersion));
if (pwaServiceWorkerEnabled) scripts.push("/__wrnexus/pwa.js");
if (deps.mobile?.enabled !== false) scripts.push("/__wrnexus/mobile.js");
if (pwaServiceWorkerEnabled)
scripts.push(versionAssetUrl("/__wrnexus/pwa.js", deps.assetVersion));
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[] = [];
if (deps.theme)
attrs.push(`data-theme="${resolveThemeName(ctx.cookies.get(THEME_COOKIE), deps.theme)}"`);
if (deps.i18n && ctx.lang) attrs.push(`lang="${ctx.lang}"`);
const language = deps.i18n && ctx.lang ? ctx.lang : (meta.lang ?? deps.seo?.lang ?? "en");
attrs.push(`lang="${safeLanguageTag(language)}"`);
const htmlAttrs = attrs.length ? ` ${attrs.join(" ")}` : undefined;
const html = renderDocument({
@@ -1412,6 +1431,17 @@ export function collectScripts(body: string): string[] {
return scripts;
}
/** Whether rendered markup needs the Capacitor/native browser bridge. */
export function usesMobileRuntime(body: string): boolean {
return /\b(?:data-mobile-[\w-]+|data-native-(?:browser|mobile|only|requires|unsupported|options)|data-on-wrnexus-(?:browser|mobile)-[\w-]+)\b/.test(
body,
);
}
function safeLanguageTag(value: string): string {
return /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/.test(value) ? value : "en";
}
function versionAssetUrl(src: string, version?: string): string {
if (!version) return src;
return `${src}${src.includes("?") ? "&" : "?"}v=${encodeURIComponent(version)}`;
@@ -0,0 +1,17 @@
import { expect, test } from "bun:test";
import { PWA_CLIENT, usesMobileRuntime } from "../src/runtime.ts";
test("PWA registration is valid JavaScript and Trusted Types compatible", () => {
expect(() => new Bun.Transpiler({ loader: "js" }).transformSync(PWA_CLIENT)).not.toThrow();
expect(PWA_CLIENT).toContain('createPolicy("wrnexus-pwa"');
expect(PWA_CLIENT).toContain("createScriptURL(swUrl)");
});
test("mobile runtime is shipped only for pages using mobile or native directives", () => {
expect(usesMobileRuntime('<main class="page">Docs</main>')).toBe(false);
expect(usesMobileRuntime('<button data-native-mobile="share">Share</button>')).toBe(true);
expect(usesMobileRuntime("<div data-mobile-only>App</div>")).toBe(true);
expect(usesMobileRuntime('<button data-on-wrnexus-browser-click="open">Open</button>')).toBe(
true,
);
});