Example ${index + 1}
${escape(example.code)}$1")
.replace(/\*\*([^*]+)\*\*/g, "$1")
.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g, '$1');
}
interface DocHeading {
id: string;
title: string;
level: number;
}
function markdown(source: string): { html: string; headings: DocHeading[] } {
const lines = source.replace(/\r/g, "").split("\n");
const out: string[] = [];
const headings: DocHeading[] = [];
const usedIds = new Map${inline(paragraph.join(" "))}
`); paragraph = []; }; const closeList = () => { if (list) out.push(""); list = false; }; const flushTable = () => { if (!table.length) return; const separator = table[1]?.every((cell) => /^:?-{3,}:?$/.test(cell.trim())); const header = separator ? table[0]! : undefined; const rows = separator ? table.slice(2) : table; out.push('| ${inline(cell.trim())} | `).join("")}
|---|
| ${inline(cell.trim())} | `).join("")}
${escape(code.join("\n"))}`,
);
code = null;
} else {
code = [];
language = fence[1]!.trim();
}
continue;
}
if (code) {
code.push(line);
continue;
}
const heading = /^(#{1,4})\s+(.+)$/.exec(line);
if (heading) {
flushParagraph();
closeList();
const level = Math.min(4, heading[1]!.length + 1);
const title = heading[2]!.replace(/[`*_]/g, "").trim();
const base =
title
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-|-$/g, "") || "section";
const occurrence = usedIds.get(base) ?? 0;
usedIds.set(base, occurrence + 1);
const id = occurrence ? `${base}-${occurrence + 1}` : base;
headings.push({ id, title, level });
out.push(`${inline(line.replace(/^>\s?/, ""))}`); continue; } if (/^---+$/.test(line.trim())) continue; paragraph.push(line.trim()); } flushParagraph(); closeList(); flushTable(); if (code) out.push(`
${escape(code.join("\n"))}`);
return { html: out.join("\n"), headings };
}
function examplesFrom(readme: string, name: string): string {
const examples = [...readme.matchAll(/```([^\n]*)\n([\s\S]*?)```/g)]
.map((match) => ({ language: match[1]!.trim(), code: match[2]!.trim() }))
.filter((example) => example.code)
.filter(
(example, index, all) => all.findIndex((value) => value.code === example.code) === index,
)
.slice(0, 4);
if (!examples.length) {
examples.push({
language: "ts",
code: `import * as packageApi from "@wrnexus/${name}";\n\nconsole.log(packageApi);`,
});
}
return examples
.map(
(example, index) =>
`${escape(example.code)}${summary}
Open documentation → `, ) .join("\n"); const categories = ["All", ...new Set(catalog.map(([, category]) => category))]; const categoryButtons = categories .map((category) => { const count = category === "All" ? catalog.length : catalog.filter(([, value]) => value === category).length; return ``; }) .join(""); writeFileSync( join(pages, "packages.wrn"), shell( "Packages", "Explore every WrNexus package, API, function, and copy-ready usage example.", `Everything in the framework, organized by responsibility and documented from the published ${frameworkVersion} APIs.
Showing {category} packages
${summary}
bun add @wrnexus/${name}@${frameworkVersion}This declaration is generated from the exact published package and lists its exported functions, classes, interfaces, and types.
${escape(types)}Copy-ready examples taken from this package's published documentation.
An SSR-first, Bun-native framework with reactive .wrn components, typed data, realtime rooms, mobile capabilities, and production security built in.
page Counter {
state count = 0
view {
<button @click="count++">
Count {count}
</button>
}
}Useful HTML reaches the browser immediately. Interactive pages hydrate only the runtime they use.
CSP, Trusted Types, CSRF, sessions, validation, authorization, encryption, and safe rendering are integrated.
Share markup through Capacitor or compile portable pages into Expo and React Native routes.
Create a production-ready WrNexus application with Bun.
bunx @wrnexus/cli create my-app
cd my-app
bun install
bun run devpage Dashboard {
state count = 0
view {
<main>
<h1>Dashboard</h1>
<button @click="count++">{count}</button>
</main>
}
}wrnexus doctor
wrnexus test
wrnexus buildapp/pages contains routes.app/components contains reusable .wrn components.app/api contains server API handlers.app/layouts contains shared shells.app/middleware contains request middleware.wrnexus.config.ts configures security, styles, data, mobile, and deployment.This page documents the .wrn language and declarative browser features that span multiple packages.
A file declares a page or component and can contain metadata, props, state, data, view, style, server functions, APIs, and realtime handlers.
${escape(`page Dashboard {
layout = "default"
seo { title = "Dashboard" }
state count = 0
view { }
style { button { padding: 12px; } }
}`)}
State is scoped to the nearest generated data-scope. Text expressions update reactively after hydration.
${escape(`state count = 0
state user = { name: "Ada" }
view {
Count: {count}
{user.name}
}`)}
Any DOM event can use @event="statement". The compiler emits data-on-event. The expression receives event and can mutate state.
| Syntax | Purpose |
|---|---|
@click | Pointer or keyboard activation. |
@input | Read live field values. |
@change | React to committed field changes. |
@submit | Handle form submission behavior. |
@browser-click | Run only in a browser target. |
@mobile-click | Run only in a native/mobile target. |
${escape(`
`)}
| Directive | Behavior |
|---|---|
data-scope | Declares reactive state for a subtree. |
data-text | Synchronizes textContent with an expression. |
data-show | Shows or hides an element by truthiness. |
data-for | Repeats an element for a client-side list. |
data-on-<event> | Compiled form of an event binding. |
data-component | Mounts a server-rendered component. |
data-slot | Fills a named component or layout slot. |
data-wrnexus-csr | Connects generated client data fetching. |
Server blocks render only the selected branch into the response.
${escape(`{#if user.isAdmin}
Admin
{:else if user}
Welcome {user.name}
{:else}
Sign in
{/if}`)}${escape(`Visible while open is true `)}
${escape(`{#each users as user, i}
{i + 1}. {user.name}
{:empty}
No users
{/each}`)}${escape(`
`)}
${escape(`component Card {
props { title = "Card" }
view {
{title}
}
}
Card content
`)}
Use named data bindings for SSR data or client hydration. Secrets and database work stay on the server.
${escape(`data users {
ssr GET "/api/users"
}
view {
{#each users as user}{user.name}
{/each}
}`)}
Schema-backed forms validate in the browser and on the server with the same descriptor.
${escape(``)}
${escape(`{t:home.title}
`)}
${escape(`
%user%: %text%
`)}
${escape(`
Browser instructions
`)}
| Attribute | Purpose |
|---|---|
data-error | Field validation error destination. |
data-success | Successful form message. |
data-redirect | Navigation after form success. |
data-room-* | Realtime status, templates, sending, and reset behavior. |
data-uploader | Config-driven upload widget. |
data-wire-theme-* | Theme selection and toggling. |
data-wire-lang* | Language selection. |
data-native-* | Cross-platform capability and visibility behavior. |
WrNexus separates server work, generated markup, and browser behavior so applications stay understandable and efficient.
Request → Router → Middleware → Page/API → SSR document → Browser runtimeThe compiler parses .wrn files and lowers state, events, interpolation, loops, conditionals, data bindings, components, and styles into server modules and small declarative browser directives.
The server owns routing, data, secrets, sessions, validation, uploads, and rendering. The browser owns reactive scopes, navigation, forms, realtime clients, and native capability dispatch.
Each package is independently installable. Start with the CLI and core, then add database, security, realtime, native, UI, and operational packages as required.