refactor: migrate legacy wire namespace to wrn
Quality / quality (ubuntu-latest) (push) Failing after 9m49s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
2026-08-12 18:51:15 +05:30
parent ec23dd4c93
commit 2c960fc1dc
488 changed files with 27306 additions and 19063 deletions
+3 -3
View File
@@ -79,7 +79,7 @@ Every command accepts an optional `[app-dir]` (defaults to `.`). Commands that r
| `wrnexus generate docker` | Scaffold `Dockerfile`, `.dockerignore`, and `docker-compose.yml`. |
| `wrnexus generate mobile` | Scaffold a Capacitor shell for iOS and Android. |
| `wrnexus mobile add <package...>` | Install Capacitor plugins and sync native projects. |
| `wrnexus eject <name...>` | Copy Wire UI component `.wrn` sources into `app/components/`. |
| `wrnexus eject <name...>` | Copy WrNexus UI component `.wrn` sources into `app/components/`. |
| `wrnexus db <cmd>` | Database migrations and tooling (see [db](#wrnexus-db)). |
| `wrnexus test [app-dir] [--watch]` | Run the app's tests via `bun test` (defaults to the `test` profile). |
| `wrnexus profiles [app-dir]` | List config profiles and their `.env` files, marking the active one. |
@@ -179,7 +179,7 @@ WrNexus response pages.
### `wrnexus eject`
Copies a Wire UI component's `.wrn` source out of `@wrnexus/ui` into `app/components/`, so the app owns and can edit it (the app copy shadows the library one by name). Run with no names to list available components. It skips components that already exist in the app.
Copies a WrNexus UI component's `.wrn` source out of `@wrnexus/ui` into `app/components/`, so the app owns and can edit it (the app copy shadows the library one by name). Run with no names to list available components. It skips components that already exist in the app.
```bash
wrnexus eject button card modal
@@ -322,5 +322,5 @@ export default config;
## Requirements / Notes
- **Bun-only.** The CLI runs on Bun, spawns the Bun binary for the dev child and `bun test`, and the production build uses `Bun.build`. Node is not supported.
- Orchestrates the rest of the framework: `@wrnexus/dev-server` (dev/prod server + gateway), `@wrnexus/router` (route + typed-routes codegen), `@wrnexus/compiler` (`.wrn``.ts`), `@wrnexus/db` (migrations, typed queries), `@wrnexus/styles` (config, profiles, `.env`, themes, styles), `@wrnexus/ui` (ejectable Wire UI components), `@wrnexus/validation`, `@wrnexus/csr`, and `@wrnexus/i18n`.
- Orchestrates the rest of the framework: `@wrnexus/dev-server` (dev/prod server + gateway), `@wrnexus/router` (route + typed-routes codegen), `@wrnexus/compiler` (`.wrn``.ts`), `@wrnexus/db` (migrations, typed queries), `@wrnexus/styles` (config, profiles, `.env`, themes, styles), `@wrnexus/ui` (ejectable WrNexus UI components), `@wrnexus/validation`, `@wrnexus/csr`, and `@wrnexus/i18n`.
- Reads `wrnexus.config.ts` for `db` / `databases`, `theme`, `styles`, `seo`, `security`, `i18n`, and `profiles`, and `wrnexus.workspace.ts` for the gateway.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/cli",
"version": "0.8.16",
"version": "0.8.17",
"type": "module",
"main": "src/index.ts",
"exports": {
+5 -5
View File
@@ -63,7 +63,7 @@ page Home {
}
style {
h1 { color: var(--wire-color-text); }
h1 { color: var(--wrn-color-text); }
}
}
\`\`\`
@@ -95,7 +95,7 @@ Components render on the server with their props, then hydrate — no per-compon
- **Server loop (DB/list/table):** \`{#each <list> as <item>[, <i>]} …rows… {:empty} …fallback… {/each}\` — iterates SSR data on the server and renders markup per item. \`{item.field}\` interpolates (HTML-escaped, XSS-safe). \`<list>\` is a JS expression, usually an \`ssr\` data binding (see "Data-driven tables" below). This is how you render a database table in \`.wrn\`.
- **Server conditional:** \`{#if <expr>} … {:else if <expr>} … {:else} … {/if}\` — renders the first truthy branch on the server. \`<expr>\` can reference \`ssr\` data, or the \`item\`/\`index\` of an enclosing \`{#each}\`. Works at page level and inside loops (e.g. \`{#if r.active}<span>●</span>{:else}<span>○</span>{/if}\` per row). For client-side show/hide based on reactive \`state\`, use \`data-show="expr"\` instead.
- i18n: \`{t:home.title}\` in text, \`t:placeholder="form.name"\` on attributes — resolved per request from \`app/locales/\`.
- Theme: any element with \`data-wire-theme-toggle\` toggles light/dark; \`data-wire-theme-set="dark"\` sets it.
- Theme: any element with \`data-wrn-theme-toggle\` toggles light/dark; \`data-wrn-theme-set="dark"\` sets it.
- Void/self-closing tags are fine: \`<br />\`, \`<img src="..." />\`.
- Only \`{\` and \`}\` are special (interpolation). Don't use a bare \`}\` in view text.
@@ -246,7 +246,7 @@ export default v.object({
});
\`\`\`
In an API route: \`import s from "../schemas/login"; import { parseBody } from "@wrnexus/validation"; const r = await parseBody(s, ctx.req);\`\`r.ok ? r.value : r.response\`.
In a form: \`<form data-schema="login" action="/api/login" method="post">\` + \`<span data-error="email"></span>\` (client + server validation wired automatically).
In a form: \`<form data-schema="login" action="/api/login" method="post">\` + \`<span data-error="email"></span>\` (client + server validation connected automatically).
## AI / LLM (\`@wrnexus/ai\`)
@@ -271,7 +271,7 @@ wrnexus update --latest # deps + syntax/config migrations + verification
wrnexus generate page <Name> # scaffold a page (aliases: g p)
wrnexus generate component <name> | api <path> | schema <name>
wrnexus db migrate | rollback | status | new [--from-models] | generate | seed
wrnexus eject <component> # copy a Wire UI component's .wrn into app/components to customize
wrnexus eject <component> # copy a WrNexus UI component's .wrn into app/components to customize
\`\`\`
## When asked to "create a page/component/feature"
@@ -279,7 +279,7 @@ wrnexus eject <component> # copy a Wire UI component's .wrn into app/compone
1. Create the \`.wrn\` file under \`app/pages/\` (or \`app/components/\`) with a \`page\`/\`component\` block — or run \`wrnexus generate page <Name>\`.
2. Put markup in \`view { }\`, interactive bits in \`state\` + \`{expr}\` + \`@event\`, reusable UI as components mounted via \`data-component\`.
3. For data, add an \`app/api/*.ts\` route and \`getDb()\`; for forms, add an \`app/schemas/*.ts\` and \`data-schema\`.
4. Style with Tailwind utility classes in the view, or theme tokens (\`var(--wire-*)\`), or \`style { }\`.
4. Style with Tailwind utility classes in the view, or theme tokens (\`var(--wrn-*)\`), or \`style { }\`.
5. Never emit React/JSX, a manual router, or client-side island JS — the framework handles hydration.\n`;
/** Agent-oriented instructions (shipped as CLAUDE.md): a preamble + the full guide. */
+1 -1
View File
@@ -515,7 +515,7 @@ export async function runBuild(appRoot: string): Promise<void> {
assetHash.update(themeJs);
console.log(`✓ Theme: ${theme.names.length} themes (default: ${theme.default})`);
// 1a2) Wire UI stylesheet (all component classes, themed via tokens).
// 1a2) WrNexus UI stylesheet (all component classes, themed via tokens).
const uiStyles = config.styles?.includeUi === false ? "" : uiCss();
writeFileSync(join(distDir, "ui.css"), uiStyles, "utf8");
const frameworkStyles = `${themeCss}\n${uiStyles}`;
+3 -3
View File
@@ -447,11 +447,11 @@ export const contactSchema = v.object({
/* 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. */
* data-wrn-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);
font-family: var(--wrn-font-sans, "Plus Jakarta Sans", ui-sans-serif, system-ui, sans-serif);
}
`,
"app/layouts/document.wrn": `// Global document layout. The framework renders this once around the selected
@@ -497,7 +497,7 @@ page Home {
<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>
APP_NAME
</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">
<button data-wrn-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>
+1 -1
View File
@@ -52,7 +52,7 @@ async function listTables(db: import("@wrnexus/db").Db): Promise<string[]> {
? "SELECT table_name AS name FROM information_schema.tables WHERE table_schema = DATABASE() ORDER BY table_name"
: "SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name";
const rows = await db.all<{ name: string }>(sql);
return rows.map((r) => r.name).filter((n) => n !== "_wire_migrations");
return rows.map((r) => r.name).filter((n) => n !== "_wrn_migrations");
}
function isModel(value: unknown): value is Model {
+4 -4
View File
@@ -49,7 +49,7 @@ const COMPOSE = `services:
environment:
NODE_ENV: production
PORT: "3000"
DATABASE_URL: postgres://wire:wire@db:5432/app
DATABASE_URL: postgres://wrn:wrn@db:5432/app
depends_on:
db:
condition: service_healthy
@@ -58,11 +58,11 @@ const COMPOSE = `services:
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: wire
POSTGRES_PASSWORD: wire
POSTGRES_USER: wrn
POSTGRES_PASSWORD: wrn
POSTGRES_DB: app
healthcheck:
test: ["CMD-SHELL", "pg_isready -U wire -d app"]
test: ["CMD-SHELL", "pg_isready -U wrn -d app"]
interval: 3s
timeout: 3s
retries: 20
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* `wrnexus eject <name...>` — copy a Wire UI component's `.wrn` source into the
* `wrnexus eject <name...>` — copy a WrNexus UI component's `.wrn` source into the
* app's `app/components/`, so you fully own and can edit it. The auto-discovered
* library version is shadowed by the app copy (same name → app wins).
*/
+1 -1
View File
@@ -75,7 +75,7 @@ export function scaffold(type: GenerateType, name: string): GeneratedFile {
}
view {
<div class="wire-${baseName(clean)}">{label}</div>
<div class="wrn-${baseName(clean)}">{label}</div>
}
}
`,
+2 -2
View File
@@ -5,7 +5,7 @@
* wrnexus dev [app-dir] [--port=3000] start the dev server (live reload)
* wrnexus build [app-dir] build a production server + assets
* wrnexus create <app-name> scaffold a new app
* wrnexus eject <name...> copy a Wire UI component into your app
* wrnexus eject <name...> copy a WrNexus UI component into your app
* wrnexus db <migrate|rollback|status|new> database migrations
* wrnexus authz <list|generate|init> authorization catalog tooling
*/
@@ -64,7 +64,7 @@ Usage:
wrnexus mobile compile Compile .wrn pages into native Expo routes
wrnexus native list List cross-platform native capabilities
wrnexus native add <capability...> Install capability packages for the configured mobile mode
wrnexus eject <name...> Copy a Wire UI component into app/components
wrnexus eject <name...> Copy a WrNexus UI component into app/components
wrnexus update [dir] [--latest] Upgrade deps, migrate project files, and verify the app
wrnexus db <cmd> Migrations: migrate | rollback | status | seed | generate | new
wrnexus authz <cmd> Authorization: list | generate | init [--dialect=sqlite|postgres|mysql]
+2 -2
View File
@@ -11,7 +11,7 @@ import {
} from "node:fs";
import { join, resolve } from "node:path";
import { loadAppConfig } from "@wrnexus/styles";
import { compileNativeWireFile } from "@wrnexus/compiler";
import { compileNativeWrnFile } from "@wrnexus/compiler";
function validPackageName(value: string): boolean {
return /^(?:@[a-z0-9._~-]+\/)?[a-z0-9._~-]+(?:@[a-zA-Z0-9._~^<>=|*+-]+)?$/.test(value);
@@ -144,7 +144,7 @@ export async function runMobileCommand(
const output = join(mobileDir, "app", relative);
mkdirSync(resolve(output, ".."), { recursive: true });
try {
writeFileSync(output, compileNativeWireFile(readFileSync(source, "utf8")), "utf8");
writeFileSync(output, compileNativeWrnFile(readFileSync(source, "utf8")), "utf8");
} catch (error) {
throw new Error(
`Native compilation failed for app/pages/${entry}: ${(error as Error).message}`,
+9 -9
View File
@@ -2064,7 +2064,7 @@ const MIGRATIONS: Migration[] = [
version: "0.8.6",
id: "0.8.6-navigation-and-layout-groups",
description:
"Rebuilds the navigation and layout component groups, moves them off Tailwind utilities onto wire-* classes, defines theme tokens that components referenced but nothing declared, and makes component outputs actually reach parent bindings.",
"Rebuilds the navigation and layout component groups, moves them off Tailwind utilities onto wrn-* classes, defines theme tokens that components referenced but nothing declared, and makes component outputs actually reach parent bindings.",
apply() {
// Source changes no codemod can make safely, so they are listed rather
// than attempted.
@@ -2100,11 +2100,11 @@ const MIGRATIONS: Migration[] = [
// move to the @change and @select bindings.
//
// Sidebar renamed its classes to the BEM form used everywhere else:
// wire-sidebar-shell, -items, -group, -toggle, -backdrop, -panel and
// -layout became wire-sidebar__*. Nesting via children still works.
// wrn-sidebar-shell, -items, -group, -toggle, -backdrop, -panel and
// -layout became wrn-sidebar__*. Nesting via children still works.
//
// The layout, page and section components moved from Tailwind utility
// classes to wire-* classes with their own styles. Application CSS
// classes to wrn-* classes with their own styles. Application CSS
// selecting on the utility classes they used to render -- max-w-7xl,
// gap-5, sm:grid-cols-2 and the rest -- no longer matches. Variants are
// data attributes now, so target [data-variant] and friends instead.
@@ -2118,12 +2118,12 @@ const MIGRATIONS: Migration[] = [
// radius.
//
// ui.css lost several application-pattern class families that nothing
// referenced: wire-catalog-*, wire-page-shell, wire-product-card,
// wire-legal-toc, wire-sdk-tabs, wire-cookie-* and wire-analytics-preview.
// referenced: wrn-catalog-*, wrn-page-shell, wrn-product-card,
// wrn-legal-toc, wrn-sdk-tabs, wrn-cookie-* and wrn-analytics-preview.
// Anything hand-written against those needs its own styles.
//
// Themes gain tokens that were referenced but never defined, including
// --wire-color-focus, --wire-color-surface-soft, --wire-color-on-danger
// --wrn-color-focus, --wrn-color-surface-soft, --wrn-color-on-danger
// and the input-* family. A custom theme that declared these itself
// keeps winning; one that did not will see focus rings and soft surfaces
// start painting where they previously rendered as nothing.
@@ -2133,7 +2133,7 @@ const MIGRATIONS: Migration[] = [
version: "0.8.7",
id: "0.8.7-ui-component-depth",
description:
"Removes superseded UI scaffolds and moves the remaining utility-styled components to self-contained wire-* BEM styles.",
"Removes superseded UI scaffolds and moves the remaining utility-styled components to self-contained wrn-* BEM styles.",
apply() {
// AdvancedDatePicker -> DatePicker; AdvancedRangeSlider -> RangeSlider;
// FileUpload -> FileInput + FileUploadProgress (or @wrnexus/uploader);
@@ -2141,7 +2141,7 @@ const MIGRATIONS: Migration[] = [
//
// List, InputNumber, Marquee, TextLink, Map, SearchBox and Timeline no
// longer render Tailwind utility class names. Application CSS selecting
// those internal utilities must move to the component's wire-* BEM
// those internal utilities must move to the component's wrn-* BEM
// classes or its data-size/data-variant attributes. List now emits
// select for non-link items as well as navigation items.
},
+1 -1
View File
@@ -151,7 +151,7 @@ test("scaffoldApp uses compatible framework packages and pins the current CLI",
const globalCss = readFileSync(join(root, "app", "styles", "global.css"), "utf8");
expect(globalCss).toContain('@plugin "@iconify/tailwind4";');
expect(globalCss).toContain('"Plus Jakarta Sans"');
expect(globalCss).toContain("--wire-font-sans");
expect(globalCss).toContain("--wrn-font-sans");
const appConfig = readFileSync(join(root, "wrnexus.config.ts"), "utf8");
expect(appConfig).toContain('family: "Plus Jakarta Sans"');
expect(appConfig).toContain("weights: [400, 500, 600, 700]");
+2 -2
View File
@@ -5,7 +5,7 @@ import { join } from "node:path";
import { generateDocker } from "../src/docker.ts";
test("generateDocker scaffolds Dockerfile, .dockerignore, compose", () => {
const dir = mkdtempSync(join(tmpdir(), "wire-docker-"));
const dir = mkdtempSync(join(tmpdir(), "wrn-docker-"));
generateDocker(dir);
expect(existsSync(join(dir, "Dockerfile"))).toBe(true);
expect(existsSync(join(dir, ".dockerignore"))).toBe(true);
@@ -21,7 +21,7 @@ test("generateDocker scaffolds Dockerfile, .dockerignore, compose", () => {
});
test("generateDocker does not overwrite existing files", () => {
const dir = mkdtempSync(join(tmpdir(), "wire-docker-"));
const dir = mkdtempSync(join(tmpdir(), "wrn-docker-"));
generateDocker(dir);
const before = readFileSync(join(dir, "Dockerfile"), "utf8");
// second run must leave the file untouched
+1 -1
View File
@@ -12,7 +12,7 @@ test("scaffold component: props + class", () => {
const f = scaffold("component", "user-card");
expect(f.path).toBe("components/user-card.wrn");
expect(f.content).toContain("component UserCard {");
expect(f.content).toContain('class="wire-user-card"');
expect(f.content).toContain('class="wrn-user-card"');
});
test("scaffold api: nested path keeps directories, exports GET", () => {
+5 -5
View File
@@ -7,7 +7,7 @@ import { runMobileCommand } from "../src/mobile-command.ts";
import { nativeCatalog, runNativeCommand } from "../src/native-command.ts";
test("generateMobile scaffolds a configurable Capacitor project", async () => {
const dir = mkdtempSync(join(tmpdir(), "wire-mobile-"));
const dir = mkdtempSync(join(tmpdir(), "wrn-mobile-"));
writeFileSync(join(dir, "package.json"), JSON.stringify({ name: "@acme/store-front" }));
await generateMobile(dir, {
@@ -30,7 +30,7 @@ test("generateMobile scaffolds a configurable Capacitor project", async () => {
});
test("generateMobile derives defaults and does not overwrite files", async () => {
const dir = mkdtempSync(join(tmpdir(), "wire-mobile-"));
const dir = mkdtempSync(join(tmpdir(), "wrn-mobile-"));
writeFileSync(join(dir, "package.json"), JSON.stringify({ name: "demo-app" }));
await generateMobile(dir);
const path = join(dir, "mobile", "capacitor.config.ts");
@@ -53,7 +53,7 @@ test("mobileOptions parses generator flags", () => {
});
test("generateMobile scaffolds a WebView-free native project from config", async () => {
const dir = mkdtempSync(join(tmpdir(), "wire-native-"));
const dir = mkdtempSync(join(tmpdir(), "wrn-native-"));
writeFileSync(join(dir, "package.json"), JSON.stringify({ name: "native-demo" }));
writeFileSync(
join(dir, "wrnexus.config.mjs"),
@@ -71,7 +71,7 @@ test("generateMobile scaffolds a WebView-free native project from config", async
});
test("mobile compile turns wrn pages into Expo routes", async () => {
const dir = mkdtempSync(join(tmpdir(), "wire-native-compile-"));
const dir = mkdtempSync(join(tmpdir(), "wrn-native-compile-"));
writeFileSync(join(dir, "package.json"), JSON.stringify({ name: "native-compile" }));
writeFileSync(join(dir, "wrnexus.config.mjs"), `export default { mobile: { mode: "native" } }`);
await generateMobile(dir);
@@ -90,7 +90,7 @@ test("mobile compile turns wrn pages into Expo routes", async () => {
test("native catalog uses built-in React Native sharing without installing expo-sharing", async () => {
expect(nativeCatalog.share?.expo).toBeUndefined();
const dir = mkdtempSync(join(tmpdir(), "wire-native-share-"));
const dir = mkdtempSync(join(tmpdir(), "wrn-native-share-"));
writeFileSync(join(dir, "package.json"), JSON.stringify({ name: "native-share" }));
writeFileSync(join(dir, "wrnexus.config.mjs"), `export default { mobile: { mode: "native" } }`);
await generateMobile(dir);