release: WRNexusJS 0.6.0

This commit is contained in:
2026-08-01 01:09:58 +05:30
parent 3e565e8d03
commit 687d345882
502 changed files with 33038 additions and 11358 deletions
+48 -4
View File
@@ -37,6 +37,12 @@ import {
} from "@wrnexus/core";
import type { Router } from "@wrnexus/router";
import { renderDocument, 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 { runtimeScriptsForMarkup } from "./plugin-assets.ts";
import {
@@ -549,6 +555,8 @@ export const HMR_CLIENT_JS = `
} 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);
@@ -563,6 +571,28 @@ export const HMR_CLIENT_JS = `
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;
@@ -1311,6 +1341,7 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
// Issue the CSRF token cookie so forms on this page can echo it back.
csrfToken(ctx);
const storeContainer = requestStoreContainer(ctx.req, matched.route.raw);
const mod = await loadModule(matched.route.file);
const component = mod.default;
@@ -1322,8 +1353,10 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
const meta = (mod.meta ?? {}) as PageMeta;
const pageCtx = ctx as Context & {
__wrnexusCallApi?: (path: string, method?: string) => Promise<unknown>;
__wrnexusUseStore?: (definition: StoreDefinition<any, any, any>) => Promise<unknown>;
};
pageCtx.__wrnexusCallApi = (path, method = "GET") => callApiFromContext(ctx, path, method);
pageCtx.__wrnexusUseStore = (definition) => storeContainer.use(definition);
let body = await renderComponents(String(await component(pageCtx)), ctx.t);
const resolvedTheme = deps.theme
? resolveThemeName(ctx.cookies.get(THEME_COOKIE), deps.theme)
@@ -1334,15 +1367,24 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
// Page layout: a page selects one by exporting `layout = "<name>"`
// (app/layouts/<name>.wrn), else falls back to a `default` layout if one
// exists. `layout = "none"` opts out. The layout wraps the body via <slot>.
const importedLayout =
mod.layout && typeof mod.layout === "object"
? (mod.layout as { name?: string; render?: (props: Record<string, unknown>) => string })
: undefined;
const layoutName =
(isMobileRequest && deps.mobile?.layout
? deps.mobile.layout
: typeof mod.layout === "string"
? mod.layout
: undefined) ?? "default";
const layout =
layoutName === "none" ? undefined : router.layouts.find((l) => l.name === layoutName);
if (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);
} else if (layout) {
try {
const layoutMod = await loadModule(layout.file);
const layoutRender = (layoutMod as { render?: (p: Record<string, string>) => string })
@@ -1451,6 +1493,7 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
.join("\n "),
extraBody:
[
renderStoreHydration(storeContainer, (ctx.locals.cspNonce as string) ?? undefined),
hmr ? hmrClientTag((ctx.locals.cspNonce as string) ?? "") : "",
shouldEnableDevToolbar(mode, deps) ? DEV_TOOLBAR_SCRIPT : "",
]
@@ -1464,6 +1507,7 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
// 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}`);
await disposeRequestStores(ctx.req);
const method = ctx.req.method.toUpperCase();
if ((method === "GET" || method === "HEAD") && notModified(ctx.req, tag)) {
return new Response(null, {