docs: rank the destructive generator as item 0
Quality / quality (ubuntu-latest) (push) Failing after 19m36s
Quality / quality (windows-latest) (push) Canceled after 0s

It was written up in 4.7 but never made the work order, which is exactly how it
stayed dangerous in the first place.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-09 11:18:45 +05:30
co-authored by Claude Opus 5
parent 790b81330a
commit 247f360ae7
79 changed files with 2759 additions and 146 deletions
@@ -0,0 +1,17 @@
// POST /api/ai { "prompt": "..." } → Claude's reply.
// Set ANTHROPIC_API_KEY in your environment (e.g. a .env file) to enable this.
import { createAI } from "@wrnexus/ai";
import type { Context } from "@wrnexus/core";
const ai = createAI(); // reads ANTHROPIC_API_KEY; defaults to claude-opus-4-8
export const POST = async (ctx: Context) => {
if (!process.env.ANTHROPIC_API_KEY) {
return Response.json({ error: "Set ANTHROPIC_API_KEY to use AI." }, { status: 501 });
}
const { prompt } = await ctx.req.json().catch(() => ({}));
if (!prompt) return Response.json({ error: "Provide a 'prompt'." }, { status: 400 });
// Stream the reply back as plain text. Use `ai.generate(prompt)` for a one-shot string.
return ai.streamResponse(prompt);
};
@@ -0,0 +1,3 @@
export const GET = async () => {
return Response.json({ message: "Hello API" });
};
@@ -0,0 +1,26 @@
import type { Context } from "../../../../../../packages/core/src/index.ts";
import {
httpTransport,
retryingTransport,
ServiceError,
serviceClient,
} from "../../../../../../packages/rpc/src/index.ts";
import { catalogService } from "../../../../packages/shared/src/index.ts";
/** GET /api/product?sku=starter — reads product data from the admin app. */
export async function GET(ctx: Context): Promise<Response> {
const sku = new URL(ctx.req.url).searchParams.get("sku")?.trim() || "starter";
const catalog = serviceClient(catalogService, {
app: "admin",
as: ctx,
transport: retryingTransport(httpTransport()),
});
try {
return Response.json({ product: await catalog.getProduct({ sku }) });
} catch (error) {
if (error instanceof ServiceError && error.code === "RPC_DENIED") {
return Response.json({ error: "Forbidden" }, { status: 403 });
}
return Response.json({ error: "Catalog temporarily unavailable" }, { status: 502 });
}
}
@@ -0,0 +1,20 @@
// A reusable component. Route: none — mounted inside a page with
// <div data-component="counter" ...props></div>.
//
// Components render on the SERVER (with their props applied) and are hydrated in
// the browser by the generic reactive runtime — they ship no JS of their own.
component Counter {
// Props arrive as mount attributes, each coerced to the type of its default
// (so start="5" arrives as the number 5).
props {
start = 0
label = "Count"
}
// State can reference props. `count` seeds the reactive scope.
state count = start
view {
<button @click="count++" class="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm transition hover:bg-indigo-500 active:scale-[0.98]">{label}: {count}</button>
}
}
@@ -0,0 +1,2 @@
-- Create application tables here.
-- Run with: bunx wrnexus db migrate
@@ -0,0 +1,2 @@
// Add deterministic development seed data here.
export async function seed(): Promise<void> {}
@@ -0,0 +1,22 @@
// Global document layout. The framework renders this once around the selected
// page layout and merges SEO metadata, styles, and scripts into <head>/<body>.
// Request cookies, resolved theme, language, URL, and pathname are available
// as SSR props, so document attributes do not need a client-side correction.
layout Document {
props {
cookies = {}
theme = "light"
language = "en"
url = ""
pathname = "/"
}
view {
<html>
<head></head>
<body>
<div id="app"><slot /></div>
</body>
</html>
}
}
@@ -0,0 +1,6 @@
{
"common": {
"appName": "web",
"welcome": "Welcome to web"
}
}
@@ -0,0 +1,8 @@
import type { Middleware } from "@wrnexus/core";
const logger: Middleware = async (ctx, next) => {
console.log(ctx.req.method, ctx.url.pathname);
return next();
};
export default logger;
@@ -0,0 +1,20 @@
page About {
seo {
title = "About"
description = "Learn how web is built with WrNexus."
}
view {
<main class="min-h-screen bg-white px-6 py-20 text-slate-900 dark:bg-[#0b0f1e] dark:text-slate-100">
<article class="mx-auto max-w-2xl">
<a href="/" class="text-sm text-indigo-600 hover:underline dark:text-indigo-400">← Home</a>
<p class="mt-12 font-mono text-xs uppercase tracking-[0.2em] text-indigo-500">WrNexus application</p>
<h1 class="mt-4 text-4xl font-bold tracking-tight">About web</h1>
<p class="mt-6 text-lg leading-8 text-slate-600 dark:text-slate-400">
This page is server-rendered from <code>app/pages/about.wrn</code>. Add state,
events, components, APIs, and data without switching to another UI framework.
</p>
</article>
</main>
}
}
@@ -0,0 +1,63 @@
// Home page (route: /). SSR-first: the view is server-rendered, then components
// (.wrn files under app/components) hydrate in the browser. Styled with Tailwind.
page Home {
seo {
title = "Home"
description = "web — built with WrNexus, an SSR-first Bun framework."
}
view {
<main class="relative min-h-screen overflow-hidden bg-white text-slate-900 dark:bg-[#0b0f1e] dark:text-slate-100">
<div aria-hidden="true" class="pointer-events-none absolute inset-x-0 -top-40 mx-auto h-96 max-w-2xl rounded-full bg-indigo-500/20 blur-3xl"></div>
<div class="relative mx-auto flex min-h-screen max-w-3xl flex-col px-6">
<header class="flex items-center justify-between py-6">
<span class="flex items-center gap-2.5 font-semibold tracking-tight">
<span class="grid h-7 w-7 place-items-center rounded-md bg-gradient-to-br from-indigo-500 to-violet-600 text-sm font-bold text-white">W</span>
web
</span>
<button data-wire-theme-toggle class="rounded-md border border-slate-200 px-3 py-1.5 text-sm text-slate-600 transition hover:border-slate-300 hover:text-slate-900 dark:border-white/10 dark:text-slate-400 dark:hover:border-white/20 dark:hover:text-white">
Toggle theme
</button>
</header>
<section class="flex flex-1 flex-col items-center justify-center py-16 text-center">
<p class="font-mono text-xs uppercase tracking-[0.2em] text-indigo-500 dark:text-indigo-400">SSR-first · Bun-native</p>
<h1 class="mt-5 text-4xl font-bold leading-[1.1] tracking-tight sm:text-6xl">
Server-rendered.<br />
Instantly <span class="bg-gradient-to-r from-indigo-500 to-violet-500 bg-clip-text text-transparent">interactive</span>.
</h1>
<p class="mt-5 max-w-md text-base leading-relaxed text-slate-600 dark:text-slate-400">
web runs on WrNexus — write <code class="rounded bg-slate-100 px-1.5 py-0.5 font-mono text-[0.85em] text-slate-800 dark:bg-white/10 dark:text-slate-200">.wrn</code> components, ship no client boilerplate, and let the server do the work.
</p>
<div class="mt-8 flex flex-wrap items-center justify-center gap-3">
<a href="/about" class="rounded-lg bg-slate-900 px-5 py-2.5 text-sm font-medium text-white shadow-sm transition hover:bg-slate-700 dark:bg-white dark:text-slate-900 dark:hover:bg-slate-200">Get started</a>
<a href="/api/hello" class="rounded-lg border border-slate-200 px-5 py-2.5 text-sm font-medium text-slate-700 transition hover:border-slate-300 dark:border-white/10 dark:text-slate-300 dark:hover:border-white/20">View API</a>
</div>
<div class="mt-14 w-full max-w-md rounded-2xl border border-slate-200 bg-white p-6 text-left shadow-sm dark:border-white/10 dark:bg-white/5">
<div class="flex items-center gap-2 font-mono text-xs text-slate-400">
<span class="h-2 w-2 rounded-full bg-emerald-400"></span>
live · hydrated on the server
</div>
<div class="mt-4 flex items-center justify-between gap-4">
<div data-component="counter" start="0" label="Clicks"></div>
<span class="max-w-[10rem] text-right text-xs leading-snug text-slate-500">This button works. You wrote zero client JavaScript.</span>
</div>
</div>
<p class="mt-10 font-mono text-xs text-slate-400 dark:text-slate-600">
edit <span class="text-slate-600 dark:text-slate-400">app/pages/index.wrn</span> to make it yours
</p>
</section>
<footer class="border-t border-slate-100 py-6 text-center text-xs text-slate-400 dark:border-white/5 dark:text-slate-600">
Built with <a href="https://www.npmjs.com/package/@wrnexus/cli" class="text-slate-600 underline-offset-2 hover:underline dark:text-slate-400">WrNexus</a>
</footer>
</div>
</main>
}
}
@@ -0,0 +1,20 @@
// ws://<host>/realtime/chat — a simple broadcast room.
//
// The client side is the framework's realtime runtime; a page opts in with
// `data-room="chat"`. Here we only handle room events.
//
// client.send(msg) → just this connection
// client.broadcast(msg) → everyone else in the room
// client.room.broadcast(msg) → everyone, including the sender
import { defineRoom } from "@wrnexus/core";
export default defineRoom({
onConnect(client) {
client.send({ type: "system", text: "connected" });
},
onMessage(client, msg) {
// Echo each message to the whole room so every tab stays in sync.
client.room.broadcast({ type: "message", data: msg });
},
});
@@ -0,0 +1,6 @@
import { v } from "@wrnexus/validation";
export const contactSchema = v.object({
email: v.string().email(),
message: v.string().min(10).max(2_000),
});
@@ -0,0 +1,19 @@
/*
* Global stylesheet. Tailwind v4 is compiled by the styles.process hook in
* wrnexus.config.ts and served at /__wrnexus/styles.css on every page.
*
* @source tells Tailwind which files to scan for class names.
*/
@import "tailwindcss";
@plugin "@iconify/tailwind4";
@source "../**/*.wrn";
@source "../**/*.tsx";
/* Make Tailwind's `dark:` variant follow the framework's data-theme attribute
* (set on <html> by the theme system), not the OS setting. Any element with
* data-wire-theme-toggle flips it. */
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
body {
font-family: var(--wire-font-sans, "Plus Jakarta Sans", ui-sans-serif, system-ui, sans-serif);
}