docs: expand full-width guides and package navigation

This commit is contained in:
2026-08-10 14:18:31 +05:30
parent 19b3c9a7cd
commit c767435cc0
94 changed files with 1442 additions and 236 deletions
+14 -3
View File
@@ -352,6 +352,17 @@ const registry = createDevToolbarRegistry();`,
.join("");
}
const packageNavigation = catalog
.map(([name]) => `<a href="/packages/${name}">@wrnexus/${name}</a>`)
.join("");
const sectionNavigation = `<aside class="docs-section-menu" aria-label="Documentation sections"><nav>
<section><strong>Get started</strong><a href="/getting-started">Quick start</a><a href="/tutorial">Tutorial</a><a href="/guides/full-stack-auth-demo">Auth dashboard demo</a><a href="/guides/configuration-and-profiles">Configuration</a></section>
<section><strong>Build</strong><a href="/guides/pages-and-components">Pages and components</a><a href="/guides/routing">Routing</a><a href="/guides/server-data">Server data</a><a href="/guides/forms-and-validation">Forms and validation</a><a href="/guides/database">Database</a></section>
<section><strong>Auth and security</strong><a href="/guides/authentication">Authentication</a><a href="/guides/authorization">Authorization</a><a href="/guides/security">Application security</a><a href="/security">Security policy</a></section>
<section><strong>Reference</strong><a href="/packages">Packages overview</a><a href="/packages/cli">CLI</a><a href="/language">.wrn language</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/examples">Examples</a></section>
<section class="package-menu"><strong>Packages</strong>${packageNavigation}</section>
</nav></aside>`;
function shell(title: string, description: string, content: string, state = ""): string {
const document = `page ${title.replace(/[^A-Za-z0-9]/g, "")} {
seo {
@@ -405,7 +416,7 @@ writeFileSync(
shell(
"Packages",
"Explore every WrNexus package, API, function, and copy-ready usage example.",
`<main class="page"><section class="hero compact"><span class="eyebrow">${catalog.length} focused packages</span><h1>Package reference</h1><p>Everything in the framework, organized by responsibility and documented from the published ${frameworkVersion} APIs.</p><input class="search" type="search" placeholder="Search packages, features, or categories…" @input="query = event.target.value" /></section><section class="category-filter" aria-label="Filter packages by category"><div class="category-row">${categoryButtons}</div><p>Showing <strong>{category}</strong> packages</p></section><section class="package-grid">${cards}</section></main>`,
`<main id="main" class="portal-main docs-layout docs-layout--navigation">${sectionNavigation}<div class="docs-reading-column"><nav class="breadcrumbs" aria-label="Breadcrumb"><a href="/">Home</a><span>/</span><span aria-current="page">Packages</span></nav><article class="documentation prose standalone package-index-document"><section class="doc-intro"><span class="eyebrow">${catalog.length} focused packages</span><h1>Package reference</h1><p>Everything in the framework, organized by responsibility and documented from the published ${frameworkVersion} APIs.</p><input class="search" type="search" placeholder="Search packages, features, or categories…" @input="query = event.target.value" /></section><section id="filter" class="category-filter" aria-label="Filter packages by category"><h2>Find a package</h2><div class="category-row">${categoryButtons}</div><p>Showing <strong>{category}</strong> packages</p></section><section id="catalog"><h2>All packages</h2><div class="package-grid">${cards}</div></section></article></div><aside class="on-this-page"><h2>On this page</h2><nav><a href="#filter">Find a package</a><a href="#catalog">All packages</a></nav></aside></main>`,
' state query = ""\n state category = "All"\n',
),
);
@@ -440,8 +451,8 @@ for (const [name, category, summary] of catalog) {
`<a class="toc-level-${heading.level}" href="#${heading.id}">${escape(heading.title)}</a>`,
)
.join("");
const content = `<main class="portal-main docs-layout">
<article id="main" class="documentation prose standalone package-document"><nav class="breadcrumbs" aria-label="Breadcrumb"><a href="/">Home</a><span>/</span><a href="/packages">Packages</a><span>/</span><span aria-current="page">@wrnexus/${name}</span></nav><section class="doc-intro"><span class="eyebrow">${category} · Package reference</span><h1>@wrnexus/${name}</h1><p>${summary}</p><div class="doc-meta"><span>v${frameworkVersion}</span><span>Private registry</span><span>${category}</span></div><section id="access" class="access-callout"><h2>Install the package</h2><p>After WorkRoot approves private registry access, install the release-aligned package:</p><pre><code>bun add @wrnexus/${name}@${frameworkVersion}</code><button type="button" class="copy-button" aria-label="Copy installation command">Copy</button></pre><p><a href="/access">Request preview access</a>. Never put registry tokens in source control.</p></section></section><section id="guide">${guide.html}</section><section id="api" class="api"><h2>Complete TypeScript API</h2><p>Generated from the exact installed package declarations.</p><pre data-language="typescript"><code>${escape(types)}</code></pre></section><section id="examples" class="examples"><h2>Examples</h2><p>Copy-ready examples from the installed package documentation.</p><div class="example-grid">${examplesFrom(readme, name)}</div></section>${componentReference}</article>
const content = `<main id="main" class="portal-main docs-layout docs-layout--navigation">${sectionNavigation}<div class="docs-reading-column">
<article class="documentation prose standalone package-document"><nav class="breadcrumbs" aria-label="Breadcrumb"><a href="/">Home</a><span>/</span><a href="/packages">Packages</a><span>/</span><span aria-current="page">@wrnexus/${name}</span></nav><section class="doc-intro"><span class="eyebrow">${category} · Package reference</span><h1>@wrnexus/${name}</h1><p>${summary}</p><div class="doc-meta"><span>v${frameworkVersion}</span><span>Private registry</span><span>${category}</span></div><section id="access" class="access-callout"><h2>Install the package</h2><p>After WorkRoot approves private registry access, install the release-aligned package:</p><pre><code>bun add @wrnexus/${name}@${frameworkVersion}</code><button type="button" class="copy-button" aria-label="Copy installation command">Copy</button></pre><p><a href="/access">Request preview access</a>. Never put registry tokens in source control.</p></section></section><section id="guide">${guide.html}</section><section id="api" class="api"><h2>Complete TypeScript API</h2><p>Generated from the exact installed package declarations.</p><pre data-language="typescript"><code>${escape(types)}</code></pre></section><section id="examples" class="examples"><h2>Examples</h2><p>Copy-ready examples from the installed package documentation.</p><div class="example-grid">${examplesFrom(readme, name)}</div></section>${componentReference}</article></div>
<aside class="on-this-page"><h2>On this page</h2><nav>${toc}</nav></aside>
</main>`;
writeFileSync(join(packagePages, `${name}.wrn`), shell(`@wrnexus/${name}`, summary, content));
+231 -3
View File
@@ -210,12 +210,16 @@ const documentationNavigation = [
["/search", "Search"],
] as const;
const packageNavigation = packageNames
.map((name) => `<a href="/packages/${name}">@wrnexus/${name}</a>`)
.join("");
const sectionNavigation = `<aside class="docs-section-menu" aria-label="Documentation sections">
<nav>
<section><strong>Get started</strong><a href="/getting-started">Quick start</a><a href="/tutorial">Tutorial</a><a href="/guides/full-stack-auth-demo">Auth dashboard demo</a><a href="/guides/project-structure">Project structure</a><a href="/guides/configuration-and-profiles">Configuration</a></section>
<section><strong>Build</strong><a href="/guides/pages-and-components">Pages and components</a><a href="/guides/routing">Routing</a><a href="/guides/server-data">Server data</a><a href="/guides/forms-and-validation">Forms and validation</a><a href="/guides/database">Database</a></section>
<section><strong>Auth and security</strong><a href="/guides/authentication">Authentication</a><a href="/guides/authorization">Authorization</a><a href="/guides/security">Application security</a><a href="/security">Security policy</a></section>
<section><strong>Reference</strong><a href="/packages">Packages</a><a href="/packages/cli">CLI</a><a href="/language">.wrn language</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/examples">Examples</a></section>
<section><strong>Reference</strong><a href="/packages">Packages overview</a><a href="/packages/cli">CLI</a><a href="/language">.wrn language</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/examples">Examples</a></section>
<section class="package-menu"><strong>Packages</strong>${packageNavigation}</section>
</nav>
</aside>`;
@@ -668,12 +672,222 @@ bunx wrnexus preview . --port=3000`)}<p>Tests should prove anonymous dashboard a
<h2>9. Demo checklist</h2><ul><li>Public home, pricing, privacy, and terms pages render without authentication.</li><li>Login creates and rotates a secure session.</li><li>Dashboard navigation changes by permission, while APIs enforce every permission independently.</li><li>Member lists and mutations are tenant-scoped on the server.</li><li>Development and production profiles resolve explicitly.</li><li>Typecheck, API tests, browser tests, security audit, contract check, build, and preview all pass.</li></ul>`,
],
};
const guideExamples: Record<string, [string, string]> = {
"project-structure": [
"Generate files through the CLI so routes and application types stay synchronized.",
`bunx wrnexus generate page account/settings
bunx wrnexus generate component account-card
bunx wrnexus generate api account/profile
bunx wrnexus generate routes
bunx wrnexus generate types .`,
],
routing: [
"This creates a dynamic, server-rendered account route and verifies that the router discovered it.",
`// app/pages/accounts/[id].wrn
page Account { view { <main><h1>Account {params.id}</h1></main> } }
bunx wrnexus inspect routes .`,
],
"pages-and-components": [
"Declare the reusable contract in a component, then pass data from the owning page.",
`component StatusCard {
prop title = "Status"
prop value = "Unknown"
view { <article><h2>{title}</h2><p>{value}</p><slot /></article> }
}
page Dashboard { view { <StatusCard title="API" value="Healthy" /> } }`,
],
"server-data": [
"Fetch on the server and render useful HTML before browser JavaScript loads.",
`page Accounts {
ssr { api result GET /api/accounts { return result.accounts } }
view { <ul>{#each result as account}<li>{account.name}</li>{:empty}<li>No accounts</li>{/each}</ul> }
}`,
],
"api-routes": [
"Validate a mutation and return an explicit HTTP result.",
`import { v, parseBody } from "@wrnexus/validation";
const input = v.object({ name: v.string().trim().min(2).max(80) });
export const POST = async (ctx) => {
const parsed = await parseBody(input, ctx.req);
return parsed.ok ? Response.json(parsed.value, { status: 201 }) : parsed.response;
};`,
],
middleware: [
"Add a request identifier and reject unsupported methods before application handlers run.",
`export default async function requestContext(ctx, next) {
ctx.state.requestId = crypto.randomUUID();
if (!["GET", "HEAD", "POST"].includes(ctx.req.method))
return new Response("Method not allowed", { status: 405 });
return next();
}`,
],
"forms-and-validation": [
"Use the same named schema in the browser form and authoritative API handler.",
`page Signup { view {
<form data-schema="signup" action="/api/signup" method="post">
<input name="email" type="email" /><span data-error="email"></span>
<button type="submit">Create account</button>
</form>
} }`,
],
authentication: [
"Require an authenticated session in middleware and redirect browser requests to login.",
`export default async function requireSession(ctx, next) {
const user = await readAuthenticatedUser(ctx);
if (!user) return Response.redirect(new URL("/login", ctx.url), 303);
ctx.state.user = user;
return next();
}`,
],
authorization: [
"Check the exact permission at the mutation boundary; hiding UI is only a convenience.",
`export const DELETE = async (ctx) => {
await requirePermission(ctx, "member:delete");
await deleteMember(ctx.params.id, ctx.state.user.tenantId);
return new Response(null, { status: 204 });
};`,
],
security: [
"Enable the main browser and request protections in application configuration, then audit the resolved production profile.",
`// wrnexus.config.ts
export default {
security: {
contentSecurityPolicy: true,
csrf: true,
trustedTypes: true,
frameOptions: "deny",
referrerPolicy: "strict-origin-when-cross-origin",
requestLimit: { maxBytes: 1_048_576 },
cors: { origins: ["https://app.example.com"] },
},
};
bunx wrnexus config . --explain --profile=production
bunx wrnexus security audit .`,
],
database: [
"Create a migration from models, apply it, and regenerate typed database functions.",
`bunx wrnexus db status
bunx wrnexus db new create_accounts --from-models
bunx wrnexus db migrate
bunx wrnexus db generate`,
],
uploads: [
"Keep size and type policy server-owned and serve private objects through an authorized route.",
`export default {
uploads: { stores: { avatars: {
driver: "local", directory: "./data/avatars",
maxBytes: 2_000_000, accept: ["image/png", "image/jpeg"],
} } },
};`,
],
realtime: [
"Authorize room membership and validate every incoming message before broadcasting.",
`export default defineRoom("team", {
async connect(client, ctx) { await requireTeamMember(ctx, ctx.params.teamId); },
async message(client, raw) {
const message = chatMessage.parse(JSON.parse(raw));
client.room.broadcast(JSON.stringify(message));
},
});`,
],
pubsub: [
"Use Redis when events must cross processes; use a namespaced channel contract.",
`const bus = createRedisPubSub({ url: env.REDIS_URL });
await bus.subscribe("team:42:events", (event) => handleTeamEvent(event));
await bus.publish("team:42:events", JSON.stringify({ type: "member.invited" }));`,
],
queues: [
"Make jobs idempotent and bound retry behavior before processing external effects.",
`const emails = queue("emails", { concurrency: 4, retries: 3 });
emails.process(async (job) => sendInviteOnce(job.data.invitationId));
await emails.add({ invitationId }, { delay: 1_000 });`,
],
testing: [
"Cover successful output and the denial path, then run the built server smoke check.",
`import { describe, expect, test } from "bun:test";
describe("members API", () => {
test("denies anonymous requests", async () => {
const response = await request("/api/members");
expect(response.status).toBe(401);
});
});`,
],
"workspaces-and-gateway": [
"Register applications explicitly and verify host routing through the gateway port.",
`bunx wrnexus workspace company-platform
cd company-platform
bunx wrnexus workspace add admin --domain=admin.localhost
bunx wrnexus gateway --port=3000`,
],
deployment: [
"Build once, apply migrations before traffic, and run the immutable Bun server artifact.",
`bun install --frozen-lockfile
bunx wrnexus typecheck .
bunx wrnexus db migrate
bunx wrnexus build .
bun dist/server.js`,
],
"configuration-and-profiles": [
"Keep shared defaults at the root and make production differences explicit.",
`export default {
server: { port: 3000 },
profiles: {
development: { envFiles: [".env", ".env.development"] },
production: { envFiles: [".env", ".env.production"] },
},
};
bunx wrnexus config . --explain --profile=production`,
],
"i18n-and-themes": [
"Configure one default locale and theme, then reference translation keys in server-rendered markup.",
`export default {
i18n: { defaultLocale: "en", locales: ["en", "fr"] },
theme: { default: "system", palette: "violet" },
};
page Home { view { <h1>{t:home.title}</h1><button data-wire-theme-toggle>Theme</button> } }`,
],
mobile: [
"Generate the mobile surface, compile it, and inspect supported native capabilities before relying on one.",
`bunx wrnexus generate mobile
bunx wrnexus mobile compile
bunx wrnexus native list`,
],
observability: [
"Redact credentials at the sink boundary and attach a stable request identifier.",
`const tracking = createTracking({
redact: ["authorization", "cookie", "password", "token"],
sampleRate: 0.1,
});
export default tracking.middleware();`,
],
upgrading: [
"Preview migrations first, review the report, then update the aligned package set.",
`git status --short
bunx wrnexus update . --latest --dry-run
bunx wrnexus update . --latest
bunx wrnexus typecheck .
bunx wrnexus build .`,
],
troubleshooting: [
"Collect deterministic diagnostics without exposing application secrets.",
`bun --version
bunx wrnexus doctor .
bunx wrnexus config . --explain
bunx wrnexus inspect routes .
bunx wrnexus report . --file=app/pages/index.wrn`,
],
};
for (const [slug, [title, desc, text]] of Object.entries(guides))
page(
`/guides/${slug}`,
title,
desc,
`<article class="documentation prose standalone"><span class="status status-beta">Preview guide · ${version}</span><h1>${title}</h1>${text.startsWith("<") ? text : `<p>${text}</p>`}<h2>Configuration</h2><p>Keep configuration in <code>wrnexus.config.ts</code>, select an explicit profile, and store secrets only in validated environment variables. Use <code>wrnexus config . --explain</code> to review the resolved non-secret configuration.</p><h2>Implementation workflow</h2>${code(`bunx wrnexus doctor .
`<article class="documentation prose standalone"><span class="status status-beta">Preview guide · ${version}</span><h1>${title}</h1>${text.startsWith("<") ? text : `<p>${text}</p>`}${guideExamples[slug] ? `<h2>Practical example</h2><p>${guideExamples[slug][0]}</p>${code(guideExamples[slug][1])}<h3>What to verify</h3><p>Run this against the selected profile, inspect the generated or returned result, and add a test for both the successful path and its most important failure path.</p>` : ""}<h2>Configuration</h2><p>Keep configuration in <code>wrnexus.config.ts</code>, select an explicit profile, and store secrets only in validated environment variables. Use <code>wrnexus config . --explain</code> to review the resolved non-secret configuration.</p><h2>Implementation workflow</h2>${code(`bunx wrnexus doctor .
bunx wrnexus typecheck .
bunx wrnexus inspect routes .
bunx wrnexus build .`)}<p>Start from the exact installed package page, implement the smallest server-owned contract, and add browser behavior only where interaction requires it. Run the production build because development-only success does not prove deployability.</p><h2>Verification checklist</h2><ul><li>Inputs are validated at the authoritative server boundary.</li><li>Authentication and resource authorization are tested independently.</li><li>Generated routes and application types are current.</li><li>Error, empty, loading, denied, and success states are documented.</li><li>The production artifact starts and serves the expected route.</li></ul><h2>Release scope</h2><p>This guide describes installed ${version} capabilities. Follow linked package declarations for exact signatures; undocumented behavior is not guaranteed.</p><p><a href="/packages">Browse package APIs</a> · <a href="/packages/cli">CLI reference</a> · <a href="/guides/troubleshooting">Troubleshooting</a> · <a href="/support">Support</a></p></article>`,
@@ -740,7 +954,21 @@ page(
"/security",
"Security",
"WRNexusJS security defaults, limits, supported release status, and reporting process.",
`<article class="documentation prose standalone"><h1>Security</h1><p>WRNexusJS provides primitives for CSP, CSRF, Trusted Types, sessions, validation, authorization, encryption, upload restrictions, request limits, and WebSocket-origin checks. Applications remain responsible for correct configuration, business authorization, secrets, dependencies, data protection, and operations.</p><h2>Supported releases</h2><p>Only the current private-preview release ${version} is documented here. A formal old-release support window is not yet published.</p><h2>Report a vulnerability</h2><p>Use WorkRoots approved private contact path at <a href="https://workroot.in/">workroot.in</a>. Do not publish exploit details or secrets. Include affected version, impact, reproduction, and a safe contact method. Response targets, encryption key, bounty, audit, and certification are not currently claimed.</p><h2>Deployment controls</h2><p>Use restrictive CSP and permissions policies, HSTS only on HTTPS production origins, MIME sniffing protection, restrictive referrers, explicit CORS, secure cookies, CSRF validation, request limits, and origin checks. See <a href="/guides/security">application security</a>.</p></article>`,
`<article class="documentation prose standalone"><h1>Security</h1><p>WRNexusJS provides primitives for CSP, CSRF, Trusted Types, sessions, validation, authorization, encryption, upload restrictions, request limits, and WebSocket-origin checks. Applications remain responsible for correct configuration, business authorization, secrets, dependencies, data protection, and operations.</p><h2>Start with a production policy</h2><p>The following baseline blocks framing, restricts referrers and cross-origin access, enables CSRF and Trusted Types, and caps request bodies. Replace the example origin with the exact browser origin that calls your application.</p>${code(`// wrnexus.config.ts
export default {
security: {
contentSecurityPolicy: true,
csrf: true,
trustedTypes: true,
frameOptions: "deny",
referrerPolicy: "strict-origin-when-cross-origin",
cors: { origins: ["https://app.example.com"], credentials: true },
requestLimit: { maxBytes: 1_048_576 },
},
};`)}<h3>What you can change</h3><ul><li>Add only required API origins to CORS; never use a wildcard with credentialed requests.</li><li>Lower request limits for JSON APIs and define separate upload limits for accepted file types.</li><li>Extend CSP only for origins your application actually loads; avoid unsafe inline script exceptions.</li><li>Enable HSTS only after HTTPS works on every production hostname and subdomain you include.</li><li>Set session expiry, rotation, secure, HTTP-only, and SameSite behavior for your authentication flow.</li></ul><h2>Verify the resolved controls</h2>${code(`bunx wrnexus config . --explain --profile=production
bunx wrnexus security audit .
bunx wrnexus typecheck .
bunx wrnexus build .`)}<p>Review the resolved production configuration, then test a valid request, an invalid CSRF token, an oversized body, an unapproved origin, an anonymous protected request, and a permission-denied request. Security configuration is complete only when denial behavior is tested.</p><h2>Supported releases</h2><p>Only the current private-preview release ${version} is documented here. A formal old-release support window is not yet published.</p><h2>Report a vulnerability</h2><p>Use WorkRoots approved private contact path at <a href="https://workroot.in/">workroot.in</a>. Do not publish exploit details or secrets. Include affected version, impact, reproduction, and a safe contact method. Response targets, encryption key, bounty, audit, and certification are not currently claimed.</p><h2>Deployment controls</h2><p>Terminate TLS at a trusted edge, forward only expected proxy headers, store secrets outside source control, apply database migrations before traffic, and monitor rejected requests without logging credentials. Continue with the complete <a href="/guides/security">application security guide</a>.</p></article>`,
"Trust",
);
page(