${inline(example.title)}
${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 usageHeading = /^## Usage[ \t]*\r?$/m.exec(readme);
if (!usageHeading) throw new Error(`@wrnexus/${name} README must contain a Usage section`);
const afterHeading = readme.slice(usageHeading.index + usageHeading[0].length);
const nextSection = /^##[ \t]+/m.exec(afterHeading);
const usage = afterHeading.slice(0, nextSection?.index ?? afterHeading.length);
const examples: { title: string; language: string; code: string }[] = [];
const lines = usage.replace(/\r/g, "").split("\n");
let title = "Typical usage";
let prose = "";
for (let index = 0; index < lines.length; index++) {
const line = lines[index]!;
const heading = /^#{3,4}\s+(.+)$/.exec(line);
if (heading) {
title = heading[1]!.replace(/[`*_]/g, "").trim();
prose = "";
continue;
}
const fence = /^```(.*)$/.exec(line);
if (fence) {
const code: string[] = [];
index++;
while (index < lines.length && !/^```/.test(lines[index]!)) {
code.push(lines[index]!);
index++;
}
const value = code.join("\n").trim();
const language = fence[1]!.trim();
if (value && language !== "text" && !examples.some((example) => example.code === value)) {
examples.push({
title: title === "Typical usage" && prose ? prose : title,
language,
code: value,
});
}
prose = "";
continue;
}
if (line.trim() && !/^[-*>|]/.test(line.trim())) {
prose = line
.replace(/[`*_]/g, "")
.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1")
.replace(/:\s*$/, "")
.trim();
}
}
if (examples.length < 2) {
throw new Error(`@wrnexus/${name} README needs at least two Usage code examples`);
}
return examples
.slice(0, 6)
.map(
(example) =>
`${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
The component reference now has a dedicated catalog with searchable cards and one complete page per component.
${summary}
After WorkRoot approves private registry access, install the release-aligned package:
bun add @wrnexus/${name}@${frameworkVersion}Request preview access. Never put registry tokens in source control.
Generated from the exact installed package declarations.
${escape(types)}Copy-ready examples from the installed package documentation.
${escape(item.purpose)}
View component →`, ) .join("\n"); const componentFilters = ["All", ...componentCategories] .map( (category) => ``, ) .join(""); writeFileSync( join(pages, "components.wrn"), shell( "Components", `Browse all ${uiReference.count} themeable WRNexusJS UI components, props, slots, events, and examples.`, `Production-ready, server-rendered components documented directly from the installed .wrn sources.
Showing {category} components
${escape(prop.name)}${escape(prop.type)}${prop.default === null ? "—" : escape(prop.default)}${escape(item.purpose)}
Components are auto-discovered. Use the component directly in any .wrn view:
${escape(example)}Legacy mount name: data-component="${escape(item.mount)}".
| Prop | Type | Requirement | Default |
|---|
${escape(slot)}This component does not declare a slot.
"}${escape(event)}This component does not declare a custom event.
"}Installed source: ${escape(item.source)}
${escape(source)}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 and ordinary attribute expressions update reactively after hydration while retaining useful initial SSR values.
${escape(`state count = 0
state user = { name: "Ada" }
state showPassword = false
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.