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
+122 -4
View File
@@ -32,7 +32,15 @@ import {
import { connectFromConfig } from "@wrnexus/db/connect";
import { configureStorage, type StorageConfig } from "@wrnexus/uploader";
import { realtimeBusFromConfig } from "./realtime-bus.ts";
import { invalidateModule, loadModule, setCompileCacheDir } from "./pipeline.ts";
import {
invalidateModule,
loadModule,
loadWrnServerModule,
setCompileCacheDir,
setCompileImportOptions,
wrnBrowserArtifactUrl,
} from "./pipeline.ts";
import { createRpcHandler } from "@wrnexus/ssr/rpc";
import { createHandlers, type WsData } from "./runtime.ts";
import { createDevAssetServer } from "./assets.ts";
import { pluginAssetsFromContributions } from "./plugin-assets.ts";
@@ -173,6 +181,12 @@ export async function startServer(opts: ServeOptions): Promise<RunningServer> {
const appDir = resolve(opts.appDir);
const appRoot = dirname(appDir);
const mode: Mode = opts.mode ?? "development";
const importConfig = (opts.appConfig?.imports ?? {}) as {
mode?: "legacy" | "compatible" | "explicit";
autoImport?: boolean;
aliases?: Record<string, string>;
};
setCompileImportOptions(appRoot, importConfig);
const configuredPlugins = opts.plugins ?? (opts.appConfig?.plugins as PluginInput | undefined);
const discoveredPlugins = await discoverPlugins(appRoot, configuredPlugins, {
@@ -384,8 +398,33 @@ export async function startServer(opts: ServeOptions): Promise<RunningServer> {
config: devToolbarConfig,
collector: devToolbarCollector,
root: appRoot,
panels: pluginToolbarPanels,
panels: [
{
id: "runtime",
title: "Runtime",
icon: "cpu",
description:
"Client, server, and shared functions, hydration modules, and runtime-boundary diagnostics.",
order: 10,
},
{
id: "stores",
title: "Stores",
icon: "database",
description:
"Global/page stores, safe client state, computed values, actions, persistence, and hydration.",
order: 20,
},
...pluginToolbarPanels,
],
platform: {
wrnexus060: {
clientFunctions: true,
serverFunctions: true,
sharedFunctions: true,
typedOutputs: true,
requestScopedStores: true,
},
plugins: pluginRunner.plugins.map((plugin) => ({
name: plugin.name,
version: plugin.version,
@@ -413,13 +452,59 @@ export async function startServer(opts: ServeOptions): Promise<RunningServer> {
: undefined,
};
const handlers = createHandlers(runtimeDeps);
const rpcHandler = createRpcHandler({
async resolve(rawComponent) {
const componentName = rawComponent.split(":", 1)[0] ?? rawComponent;
const preferred = router.components.find(
(entry) => entry.name.toLowerCase() === componentName.toLowerCase(),
);
const candidates = [
...(preferred ? [preferred.file] : []),
...router.pages.filter((route) => route.file.endsWith(".wrn")).map((route) => route.file),
...router.layouts.map((entry) => entry.file),
...router.stores.map((entry) => entry.file),
...router.components.filter((entry) => entry !== preferred).map((entry) => entry.file),
];
for (const file of new Set(candidates)) {
const module = await loadWrnServerModule(file);
const functions = module.__wrnexusServerFunctions;
const manifest = module.__wrnexusRpcManifest;
if (!functions || typeof functions !== "object" || !Array.isArray(manifest)) continue;
const ownsComponent = manifest.some(
(entry: any) =>
String(entry?.component ?? "").toLowerCase() === componentName.toLowerCase(),
);
if (!ownsComponent) continue;
return {
functions: functions as Record<string, (...args: any[]) => any>,
manifest: manifest as any,
};
}
return null;
},
validateCsrf(request) {
const url = new URL(request.url);
const origin = request.headers.get("origin");
if (origin && origin !== url.origin) return false;
const cookieToken = /(?:^|;\s*)wrnexus_csrf=([^;]+)/.exec(
request.headers.get("cookie") ?? "",
)?.[1];
return (
!cookieToken ||
decodeURIComponent(cookieToken) === (request.headers.get("x-wrnexus-csrf") ?? "")
);
},
});
const server = Bun.serve<WsData>({
port,
hostname,
development: mode === "development",
maxRequestBodySize: 10 * 1024 * 1024,
fetch: handlers.fetch,
fetch(request, server) {
if (new URL(request.url).pathname === "/__wrnexus/rpc") return rpcHandler(request);
return handlers.fetch(request, server);
},
websocket: handlers.websocket,
});
@@ -473,7 +558,40 @@ export async function startServer(opts: ServeOptions): Promise<RunningServer> {
}
console.log(`[wrnexus] hot update — ${files.join(", ")}`);
hub.reload();
const storeUpdates: Array<{ name: string; url: string; kind: string }> = [];
for (const changed of files) {
const absolute = isAbsolute(changed) ? changed : resolve(appDir, changed);
if (!absolute.endsWith(".wrn")) continue;
try {
const source = readFileSync(absolute, "utf8");
const declaration = /\b(global|page)\s+store\s+([A-Za-z_$][\w$]*)\s*\{/.exec(source);
if (!declaration) continue;
storeUpdates.push({
name: declaration[2]!,
kind: declaration[1]!,
url: wrnBrowserArtifactUrl(absolute),
});
} catch (error) {
console.warn(`[wrnexus] failed to prepare store HMR for ${absolute}`, error);
}
}
if (storeUpdates.length) {
hub.broadcastJson({ type: "store-update", version: Date.now(), stores: storeUpdates });
}
const onlyStores =
storeUpdates.length > 0 &&
files.every((changed) => {
const absolute = isAbsolute(changed) ? changed : resolve(appDir, changed);
if (!absolute.endsWith(".wrn")) return false;
try {
return /\b(?:global|page)\s+store\s+[A-Za-z_$][\w$]*\s*\{/.test(
readFileSync(absolute, "utf8"),
);
} catch {
return false;
}
});
if (!onlyStores) hub.reload();
hub.broadcastJson({
channel: "toolbar",
type: "toolbar:scan",