docs: expand full-width guides and package navigation
This commit is contained in:
+231
-3
@@ -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 WorkRoot’s 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 WorkRoot’s 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(
|
||||
|
||||
Reference in New Issue
Block a user