From 056ff8a8c4729abaeadb5a6b0d62b35300953a64 Mon Sep 17 00:00:00 2001 From: Ajay Ghanwat Date: Wed, 29 Jul 2026 13:35:36 +0530 Subject: [PATCH] docs: include all 31 packages in catalog --- app/docs.test.ts | 5 + app/pages/packages.wrn | 17 +- app/pages/packages/auth.wrn | 367 +++++++++++++++++++++++++++++ app/pages/packages/captcha.wrn | 321 +++++++++++++++++++++++++ app/pages/packages/dev-toolbar.wrn | 44 ++++ app/pages/packages/plugin.wrn | 46 ++++ app/pages/packages/syntax.wrn | 55 +++++ scripts/generate-docs.ts | 71 +++++- 8 files changed, 921 insertions(+), 5 deletions(-) create mode 100644 app/pages/packages/auth.wrn create mode 100644 app/pages/packages/captcha.wrn create mode 100644 app/pages/packages/dev-toolbar.wrn create mode 100644 app/pages/packages/plugin.wrn create mode 100644 app/pages/packages/syntax.wrn diff --git a/app/docs.test.ts b/app/docs.test.ts index 356f49a0..0f2ab796 100644 --- a/app/docs.test.ts +++ b/app/docs.test.ts @@ -12,13 +12,16 @@ const frameworkVersion = JSON.parse(readFileSync(join(root, "package.json"), "ut const packagePages = join(root, "app", "pages", "packages"); const expected = [ "ai", + "auth", "authz", + "captcha", "cli", "compiler", "core", "csr", "db", "dev-server", + "dev-toolbar", "encryption", "helpers", "i18n", @@ -26,12 +29,14 @@ const expected = [ "mobile", "native", "oauth", + "plugin", "pubsub", "queue", "reactive", "router", "ssr", "styles", + "syntax", "test", "tracking", "ui", diff --git a/app/pages/packages.wrn b/app/pages/packages.wrn index 5fa7583a..1e3dd0be 100644 --- a/app/pages/packages.wrn +++ b/app/pages/packages.wrn @@ -15,12 +15,18 @@ page Packages {
Private preview · v0.5.0
Browse documentation
-
26 focused packages

Package reference

Everything in the framework, organized by responsibility and documented from the published 0.5.0 APIs.

Showing {category} packages

+
31 focused packages

Package reference

Everything in the framework, organized by responsibility and documented from the published 0.5.0 APIs.

Showing {category} packages

AI

@wrnexus/ai

Server-side Anthropic client with generation and streaming.

Open documentation →
+ + Security

@wrnexus/auth

Authentication routes, sessions, forms, guards, and account flows.

Open documentation → +
Security

@wrnexus/authz

Role, permission, policy, and authorization guards.

Open documentation →
+ + Security

@wrnexus/captcha

Managed CAPTCHA verification, middleware, and UI integration.

Open documentation → +
Tooling

@wrnexus/cli

Create, develop, build, generate, test, and maintain WRNexusJS apps.

Open documentation →
@@ -39,6 +45,9 @@ page Packages { Runtime

@wrnexus/dev-server

Development and production servers, HMR, assets, and gateways.

Open documentation →
+ + Tooling

@wrnexus/dev-toolbar

Development toolbar diagnostics, inspection, and runtime status.

Open documentation → +
Security

@wrnexus/encryption

Hashing, HMAC, authenticated encryption, and key derivation.

Open documentation →
@@ -60,6 +69,9 @@ page Packages { Security

@wrnexus/oauth

OAuth 2.0, PKCE, provider presets, and profile mapping.

Open documentation →
+ + Core

@wrnexus/plugin

Plugin contracts, lifecycle hooks, composition, and framework integration.

Open documentation → +
Realtime

@wrnexus/pubsub

In-process and Redis-backed publish/subscribe.

Open documentation →
@@ -78,6 +90,9 @@ page Packages { Frontend

@wrnexus/styles

CSS pipeline, themes, fonts, profiles, and application config.

Open documentation →
+ + Frontend

@wrnexus/syntax

Editor syntax definitions and language tooling for .wrn files.

Open documentation → +
Tooling

@wrnexus/test

WRNexusJS-aware component, route, and browser testing utilities.

Open documentation →
diff --git a/app/pages/packages/auth.wrn b/app/pages/packages/auth.wrn new file mode 100644 index 00000000..0d6fd382 --- /dev/null +++ b/app/pages/packages/auth.wrn @@ -0,0 +1,367 @@ +page wrnexusauth { + seo { + title = "@wrnexus/auth" + description = "Authentication routes, sessions, forms, guards, and account flows." + } + + view { +
+ +
+ W WRNexusJS + + +
+ +
+
Security · Package reference

@wrnexus/auth

Authentication routes, sessions, forms, guards, and account flows.

v0.5.0Private registrySecurity

Install the package

After WorkRoot approves private registry access, install the release-aligned package:

bun add @wrnexus/auth@0.5.0

Request preview access. Never put registry tokens in source control.

Framework-native authentication, identity, account-security, and session management for WRNexusJS.

+

Capabilities

+
    +
  • Password registration, login, recovery, reset, and authenticated password changes
  • +
  • Email, phone, and username identities with verification and generic resend responses
  • +
  • Magic links and passwordless email/SMS OTP login
  • +
  • MFA transactions using verified email OTP, verified SMS OTP, TOTP, or recovery codes
  • +
  • RFC 6238 TOTP with counter replay protection
  • +
  • One-use recovery codes; regeneration invalidates previous unused codes
  • +
  • Passkey/WebAuthn registration and strong passwordless sign-in through a provider contract
  • +
  • OAuth account linking and provider sign-in
  • +
  • Invitations, session rotation, idle and absolute expiry, revocation, and trusted devices
  • +
  • Deny-by-default audited support impersonation
  • +
  • Adaptive risk scoring, CAPTCHA escalation, temporary lockout, and optional login alerts
  • +
  • Memory and SQL stores
  • +
  • Optional encryption-keyring protection for TOTP and OAuth secrets
  • +
  • Automatic API routes, middleware, browser schemas, components, runtime, migrations, and DevToolbar checks
  • +
+

Passkeys are a strong sign-in method. They are not currently exposed as a selectable second step in TwoFactorChallenge; the implemented MFA methods are email OTP, SMS OTP, TOTP, and recovery codes.

+

Install

+
bun add @wrnexus/auth
+

WRNexusJS discovers the package automatically. Do not copy package components, client scripts, schemas, or standard /api/auth/* route files into the application.

+

Default configuration

+

Create the engine:

+
// app/lib/auth.ts
+import { createAuthEngine, MemoryAuthStore } from "@wrnexus/auth";
+
+export const auth = createAuthEngine({
+  store: new MemoryAuthStore(),
+  secret: process.env.AUTH_SECRET!,
+  issuer: "My application",
+  onSignedIn(ctx, returnTo) {
+    const safe = returnTo?.startsWith("/") && !returnTo.startsWith("//") ? returnTo : "/account";
+    return Response.redirect(new URL(safe, ctx.url), 303);
+  },
+  onSignedOut(ctx) {
+    return Response.redirect(new URL("/sign-in", ctx.url), 303);
+  },
+  onSuccessfulSignUp() {
+    return {
+      autoSignIn: true,
+      redirectTo: "/account",
+    };
+  },
+  delivery: {
+    async send(message) {
+      // Queue email/SMS through your provider. Never log message.code or message.token.
+    },
+  },
+});
+

Authentication behavior belongs in this engine definition: delivery, token URL mapping, successful sign-in/sign-out responses, password policy, risk thresholds, MFA, passkeys, and auditing can all be configured in one server-only location. The older config.auth.onSignedIn and config.auth.onSignedOut fields remain supported as compatibility overrides, but new applications should configure these hooks on createAuthEngine.

+

Successful signup behavior

+

Without onSuccessfulSignUp, a successful package registration redirects to /sign-in.

+

To sign in immediately after registration:

+
onSuccessfulSignUp(ctx, user) {
+  return {
+    autoSignIn: true,
+    redirectTo: "/account",
+  };
+}
+

Automatic sign-in runs the normal login policy. It does not bypass required email or phone verification, CAPTCHA, MFA, account status, or risk checks. The hook may also return a Response for a completely custom HTTP result, or return { redirectTo: "/welcome" } to redirect without creating a session.

+

Register it through application configuration:

+
// wrnexus.config.ts
+import type { AuthConfig } from "@wrnexus/auth";
+import type { AppConfig } from "@wrnexus/styles";
+import { auth } from "./app/lib/auth.ts";
+
+const config = {
+  auth: {
+    engine: auth,
+    routes: true,
+    middleware: true,
+    migrations: false,
+  },
+} satisfies AppConfig & { auth: AuthConfig };
+
+export default config;
+

That configuration automatically activates package routes, auth-session middleware, components, browser validation schemas, and the auth client runtime. setDefaultAuthEngine() remains available only for advanced manual integrations and tests.

+

SQL production configuration

+
import { createAuthEngine, SqlAuthStore } from "@wrnexus/auth";
+import { getDb } from "@wrnexus/db";
+
+export const auth = createAuthEngine({
+  store: new SqlAuthStore(getDb()),
+  secret: process.env.AUTH_SECRET!,
+});
+
export default {
+  db: {
+    // Application database configuration.
+  },
+  auth: {
+    engine: auth,
+    routes: true,
+    middleware: true,
+    migrations: true,
+  },
+};
+

The package contributes both ordered migrations:

+
001_auth.sql
+002_auth_otp_purpose.sql
+

Migrations are enabled automatically only when auth.engine and a default config.db are present. Set auth.migrations explicitly when an application needs different behavior.

+

Delivered action URLs

+

By default, the engine builds links from the supplied baseUrl and token purpose. Applications can map those links to their own page structure without replacing package APIs:

+
const auth = createAuthEngine({
+  store: new SqlAuthStore(getDb()),
+  secret: process.env.AUTH_SECRET!,
+  tokenUrl({ purpose, token, baseUrl }) {
+    if (!baseUrl) return undefined;
+
+    const paths = {
+      "verify-email": `/verify-email?token=${encodeURIComponent(token)}`,
+      "verify-phone": `/verify-phone?token=${encodeURIComponent(token)}`,
+      "password-reset": `/recover/reset?token=${encodeURIComponent(token)}`,
+      "magic-link": `/magic-link?token=${encodeURIComponent(token)}`,
+      invite: `/invitation?token=${encodeURIComponent(token)}`,
+    };
+
+    const path = paths[purpose as keyof typeof paths];
+    return path ? new URL(path, baseUrl).toString() : undefined;
+  },
+});
+

Returning undefined intentionally omits the URL while still delivering the raw token. The callback runs only in trusted server code.

+

Built-in validation

+

Every packaged auth form has a built-in @wrnexus/validation schema. The same resolved schema is used by the browser and the package API handler.

+

Default use requires no app/schemas files:

+
<SignUp />
+<SignIn />
+<ForgotPassword />
+<ResetPassword token='{token}' />
+<TwoFactorChallenge />
+

To customize one schema, extend the package default and register only that override:

+
// app/schemas/custom-password-request.ts
+import { authSchemas } from "@wrnexus/auth";
+import { v } from "@wrnexus/validation";
+
+export default authSchemas.passwordResetRequest.extend({
+  identifier: v
+    .string()
+    .trim()
+    .required("Enter your registered email address")
+    .email("Enter a valid registered email address"),
+});
+
import customPasswordRequest from "./app/schemas/custom-password-request.ts";
+
+export default {
+  auth: {
+    engine: auth,
+    schemas: {
+      passwordResetRequest: customPasswordRequest,
+    },
+  },
+};
+

<ForgotPassword /> can keep its default schema="auth-password-request". The plugin automatically publishes the overridden browser descriptor under that same built-in schema ID. All other forms continue using package defaults.

+

Route controls

+

Use a boolean to enable or disable all package routes:

+
auth: {
+  engine: auth,
+  routes: true,
+}
+

Or control feature groups:

+
routes: {
+  enabled: true,
+  registration: true,
+  login: true,
+  verification: true,
+  password: true,
+  invitations: true,
+  magicLink: true,
+  otp: true,
+  mfa: true,
+  sessions: true,
+  impersonation: false,
+  passkeys: true,
+}
+

Application routes have normal framework precedence. Disable a package group only when the application intentionally owns every endpoint in that group; no excludeRoutes list is required.

+

Package endpoints

+
POST     /api/auth/register
+POST     /api/auth/login
+POST     /api/auth/logout
+POST     /api/auth/verification/request
+GET|POST /api/auth/verify/email
+POST     /api/auth/verify/phone
+POST     /api/auth/password/request
+POST     /api/auth/password/reset
+POST     /api/auth/password/change
+POST     /api/auth/invitations/accept
+POST     /api/auth/magic-link/request
+GET|POST /api/auth/magic-link
+POST     /api/auth/otp/login/request
+POST     /api/auth/otp/login/complete
+POST     /api/auth/otp
+POST     /api/auth/otp/verify
+POST     /api/auth/totp/setup
+POST     /api/auth/totp/confirm
+POST     /api/auth/totp/disable
+POST     /api/auth/recovery-codes
+POST     /api/auth/mfa/otp
+POST     /api/auth/mfa/complete
+GET      /api/auth/sessions
+POST     /api/auth/sessions/revoke
+POST     /api/auth/impersonation/start
+POST     /api/auth/impersonation/stop
+POST     /api/auth/passkeys/register/options
+POST     /api/auth/passkeys/register/verify
+POST     /api/auth/passkeys/login/options
+POST     /api/auth/passkeys/login/verify
+

Each URL uses a route-specific module, so rewritten framework request URLs cannot make the handler fall through to a shared-dispatcher 404.

+

Unsafe package routes validate the framework CSRF token by default. Set auth.csrf: false only when an external API gateway provides an equivalent protection model.

+

Components

+
<SignIn />
+<SignUp />
+<ForgotPassword />
+<ResetPassword token='{token}' />
+<OtpSignIn method="email-otp" />
+<MagicLinkSignIn />
+<PasskeyButton mode="authenticate" />
+<TwoFactorChallenge mfaToken='{mfaToken}' challengeId='{challengeId}' />
+<AuthenticatorSetup credentialId='{credentialId}' secret='{secret}' uri='{uri}' />
+<RecoveryCodes codes='{codes}' />
+<DeviceSessions sessions='{sessions}' currentSessionId='{currentSessionId}' />
+<VerifyEmail token='{token}' identifier='{identifier}' />
+<VerifyPhone token='{token}' identifier='{identifier}' />
+<InvitationAccept token='{token}' />
+<ImpersonationBanner targetName='{targetName}' />
+<AccountStatus status='{account.status}' />
+

identifier is optional on verification components. Supply it when an unauthenticated verification page should support resending a token. The response remains generic whether the account exists or not.

+

CAPTCHA and risk

+

The HTTP handlers never trust a browser captchaVerified field. CAPTCHA completion is accepted only from server-populated ctx.locals.captcha.success or ctx.locals.captchaVerified === true.

+

Rate limiting remains an application or gateway responsibility. Apply it to registration, login, reset, magic-link, OTP, verification, passkey, invitation, and impersonation endpoints.

+

MFA

+

1. Password, OAuth, magic-link, or OTP login may return code: "mfa-required" with a short-lived mfaToken. 2. The response lists only methods actually available to that user. 3. Email/SMS MFA is offered only for verified linked identities. 4. beginMfaOtp() issues an MFA-bound OTP when needed. 5. completeMfa() consumes the one-time transaction and creates the session.

+

Passkeys

+

The browser runtime coordinates navigator.credentials.create() and navigator.credentials.get(). A configured server-side PasskeyProvider must verify the challenge, RP ID, origin, signature, user presence or verification, counter, and credential ownership.

+

Multi-process deployments must provide a shared PasskeyChallengeStore; the default memory implementation is process-local. Missing passkey providers return a controlled 503 response rather than crashing the route.

+

Protect long-lived secrets

+
import { createAuthSecretProtector } from "@wrnexus/auth";
+import { createKeyring } from "@wrnexus/encryption";
+
+const keyring = createKeyring([
+  {
+    id: "auth-2026-01",
+    secret: process.env.AUTH_ENCRYPTION_KEY!,
+    active: true,
+  },
+]);
+
+const auth = createAuthEngine({
+  store: new SqlAuthStore(getDb()),
+  secret: process.env.AUTH_SECRET!,
+  secretProtector: createAuthSecretProtector(keyring),
+});
+

TOTP seeds and OAuth access/refresh tokens are protected before persistence. Keep old keys available during rotation.

+

Custom HTTP integration

+

createAuthHttpHandlers() remains available for custom route paths or response behavior. Prefer package routes for standard flows; copied application API files are unnecessary.

+

Development

+
bun run auth:dev
+bun run validate:auth
+

Read [SECURITY.md](./SECURITY.md) before production deployment.

Complete TypeScript API

Generated from the exact installed package declarations.

export { A as AuthEngine, c as createAuthEngine, i as inferIdentityType, n as normalizeEmail, a as normalizeIdentity, b as normalizePhone, d as normalizeUsername, p as publicUser, s as safeAuthReturnTo } from './engine-jttXj6PP.js';
+import { n as AuthRiskSignals, l as AuthRiskDecision } from './types-JLkQpcAt.js';
+export { A as AuthAccountStatus, a as AuthClock, b as AuthDeliveryMessage, c as AuthDeliveryProvider, d as AuthEngineOptions, e as AuthIdentity, f as AuthIdentityType, g as AuthImpersonationDecision, h as AuthMfaMethod, i as AuthPublicUser, j as AuthRandom, k as AuthResult, m as AuthRiskLevel, o as AuthSecretProtector, p as AuthSecurityEvent, q as AuthSession, r as AuthSignedInHandler, s as AuthSignedOutHandler, M as AuthStore, t as AuthSuccessfulSignUpAction, u as AuthSuccessfulSignUpHandler, v as AuthTokenPurpose, w as AuthTokenUrlInput, x as AuthUser, y as AuthenticatedContext, L as LoginAttempt, z as LoginInput, N as MemoryPasskeyChallengeStore, O as OAuthAccount, B as OneTimeToken, C as OtpChallenge, P as PasskeyAuthenticationOptions, Q as PasskeyChallengeKind, S as PasskeyChallengeRecord, U as PasskeyChallengeStore, D as PasskeyCredential, E as PasskeyProvider, F as PasskeyRegistrationOptions, G as PasskeyVerificationResult, H as PasswordBreachProvider, I as PasswordCredential, R as RecoveryCodeRecord, J as RegisterInput, T as TotpCredential, K as TrustedDevice, V as assertPasskeyProvider } from './types-JLkQpcAt.js';
+export { MemoryAuthStore } from './stores/memory.js';
+export { SqlAuthStore } from './stores/sql.js';
+export { AUTH_SESSION_KEY, authSession, clearAuthSession, establishAuthSession, getAuthSession, getAuthUser, isAuthenticatedContext, requireAuth } from './middleware.js';
+export { A as AuthHttpOptions, a as AuthPasskeyHttpOptions, b as AuthSchemaOverrides, c as AuthSchemaSet, d as authBrowserSchemaDescriptors, e as authBrowserSchemaMap, f as authSchemas, g as authenticatorConfirmSchema, h as authenticatorDisableSchema, i as authenticatorSetupSchema, j as changePasswordSchema, k as createAuthHttpHandlers, l as emptyActionSchema, m as impersonationStartSchema, n as invitationAcceptSchema, o as loginSchema, p as magicLinkConsumeSchema, q as magicLinkRequestSchema, r as mfaOtpRequestSchema, s as mfaSchema, t as otpIssueSchema, u as otpLoginCompleteSchema, v as otpLoginRequestSchema, w as otpSchema, x as passkeyAuthenticationOptionsSchema, y as passkeyAuthenticationVerifySchema, z as passkeyRegistrationOptionsSchema, B as passkeyRegistrationVerifySchema, C as passwordResetRequestSchema, D as passwordResetSchema, E as recoveryCodesSchema, F as registerSchema, G as resolveAuthSchemas, H as sessionRevokeSchema, I as signUpSchema, J as verificationRequestSchema, K as verificationTokenSchema } from './index-CrF_lZDQ.js';
+export { AuthAuditIssue, AuthConfig, AuthPluginOptions, AuthRoutesConfig, authComponentsDir, authPlugin } from './plugin.js';
+export { DefaultAuthRouteOptions, clearDefaultAuthEngine, getDefaultAuthEngine, getDefaultAuthRouteOptions, getDefaultAuthSchemas, hasDefaultAuthEngine, setDefaultAuthEngine, setDefaultAuthRouteOptions, setDefaultAuthSchemas, tryGetDefaultAuthEngine } from './runtime.js';
+export { createAuthSecretProtector } from './protector.js';
+export { decodeBase32, encodeBase32, generateTotp, generateTotpSecret, totpUri, verifyTotp } from './totp/index.js';
+import '@wrnexus/oauth';
+import '@wrnexus/core';
+import '@wrnexus/db';
+import '@wrnexus/validation';
+import '@wrnexus/plugin';
+import '@wrnexus/encryption';
+
+interface RiskPolicy {
+    captchaThreshold: number;
+    mfaThreshold: number;
+    blockThreshold: number;
+}
+declare function evaluateAuthRisk(signals?: AuthRiskSignals, policy?: RiskPolicy): AuthRiskDecision;
+
+export { AuthRiskDecision, AuthRiskSignals, type RiskPolicy, evaluateAuthRisk };
+

Examples

Copy-ready examples from the installed package documentation.

## Install

bun add @wrnexus/auth

Create the engine

// app/lib/auth.ts
+import { createAuthEngine, MemoryAuthStore } from "@wrnexus/auth";
+
+export const auth = createAuthEngine({
+  store: new MemoryAuthStore(),
+  secret: process.env.AUTH_SECRET!,
+  issuer: "My application",
+  onSignedIn(ctx, returnTo) {
+    const safe = returnTo?.startsWith("/") && !returnTo.startsWith("//") ? returnTo : "/account";
+    return Response.redirect(new URL(safe, ctx.url), 303);
+  },
+  onSignedOut(ctx) {
+    return Response.redirect(new URL("/sign-in", ctx.url), 303);
+  },
+  onSuccessfulSignUp() {
+    return {
+      autoSignIn: true,
+      redirectTo: "/account",
+    };
+  },
+  delivery: {
+    async send(message) {
+      // Queue email/SMS through your provider. Never log message.code or message.token.
+    },
+  },
+});

Successful signup behavior

onSuccessfulSignUp(ctx, user) {
+  return {
+    autoSignIn: true,
+    redirectTo: "/account",
+  };
+}

Successful signup behavior

// wrnexus.config.ts
+import type { AuthConfig } from "@wrnexus/auth";
+import type { AppConfig } from "@wrnexus/styles";
+import { auth } from "./app/lib/auth.ts";
+
+const config = {
+  auth: {
+    engine: auth,
+    routes: true,
+    middleware: true,
+    migrations: false,
+  },
+} satisfies AppConfig & { auth: AuthConfig };
+
+export default config;

Successful signup behavior

import { createAuthEngine, SqlAuthStore } from "@wrnexus/auth";
+import { getDb } from "@wrnexus/db";
+
+export const auth = createAuthEngine({
+  store: new SqlAuthStore(getDb()),
+  secret: process.env.AUTH_SECRET!,
+});

Successful signup behavior

export default {
+  db: {
+    // Application database configuration.
+  },
+  auth: {
+    engine: auth,
+    routes: true,
+    middleware: true,
+    migrations: true,
+  },
+};
+ +
+ + +
+ } +} diff --git a/app/pages/packages/captcha.wrn b/app/pages/packages/captcha.wrn new file mode 100644 index 00000000..e3438170 --- /dev/null +++ b/app/pages/packages/captcha.wrn @@ -0,0 +1,321 @@ +page wrnexuscaptcha { + seo { + title = "@wrnexus/captcha" + description = "Managed CAPTCHA verification, middleware, and UI integration." + } + + view { +
+ +
+ W WRNexusJS + + +
+ +
+
Security · Package reference

@wrnexus/captcha

Managed CAPTCHA verification, middleware, and UI integration.

v0.5.0Private registrySecurity

Install the package

After WorkRoot approves private registry access, install the release-aligned package:

bun add @wrnexus/captcha@0.5.0

Request preview access. Never put registry tokens in source control.

A first-class CAPTCHA and anti-automation package for WRNexusJS. It supports self-hosted challenges, a managed WRNexus service, external providers, form submission guards, page gates, accessible audio, adaptive risk checks, and a Tailwind-only .wrn component.

+

Install

+
bun add @wrnexus/captcha
+

WRNexusJS automatically discovers the package plugin, component, client runtime, styles, and DevToolbar audit. Use <Captcha /> directly after installation. The browser runtime is injected once only on responses that render a CAPTCHA; no script tag, public-file copy, or manual plugin registration is required. Call captchaPlugin(options) explicitly only when an application needs to override the discovered package configuration.

+

Included challenge modes

+
    +
  • Number, alphabet, and alphanumeric image challenges
  • +
  • Addition, subtraction, multiplication, and exact-division calculations
  • +
  • Generated shape-selection image challenges
  • +
  • Audio alternatives for text, numbers, and calculations
  • +
  • Honeypot and minimum-completion-time invisible checks
  • +
  • Self-hosted “I’m not a robot” checkbox challenge with one-time server verification
  • +
  • Always, once-per-session, and adaptive page gates
  • +
  • Cloudflare Turnstile, Google reCAPTCHA, hCaptcha, managed, and custom providers
  • +
+

Create the self-hosted engine

+
import {
+  createCaptchaEngine,
+  createCaptchaHttpHandlers,
+  RedisCaptchaStore,
+} from "@wrnexus/captcha/server";
+
+const engine = createCaptchaEngine({
+  secret: process.env.CAPTCHA_SECRET!,
+  store: new RedisCaptchaStore(redis),
+  basePath: "/api/captcha",
+  challengeTtlMs: 2 * 60_000,
+  responseTokenTtlMs: 5 * 60_000,
+  maxAttempts: 3,
+  minCompletionMs: 800,
+});
+
+export const handlers = createCaptchaHttpHandlers(engine);
+

Mount the handlers from an API catch-all route:

+
import type { Context } from "@wrnexus/core";
+import { handlers } from "../../lib/captcha.ts";
+
+export async function POST(ctx: Context) {
+  return (await handlers.handle(ctx.req, ctx)) ?? new Response("Not Found", { status: 404 });
+}
+export const GET = POST;
+export const HEAD = POST;
+

Use the component

+
<Captcha
+  type="alphanumeric"
+  action="signup"
+  endpoint="/api/captcha/challenge"
+  verifyEndpoint="/api/captcha/verify"
+  difficulty="normal"
+  disturbance="50"
+  imageStyle="random"
+  allowedStyles="classic,snow,distortion,wave"
+  size="normal"
+  showAudio="true"
+  showListen="true"
+  @success='captchaToken = event.detail.responseToken'
+  @failure='formError = event.detail.extra.message'
+/>
+

The component uses Tailwind utilities and --wire-* theme variables. It has no companion component CSS file.

+

Main props

+

provider, siteKey, type, action, presentation, difficulty, disturbance, imageStyle, allowedStyles, excludedStyles, randomizeStyle, locale, size, color, class, name, endpoint, verifyEndpoint, responseField, labels/messages, autoLoad, autoVerify, showVerify, showRefresh, showAudio, showListen, showStatus, disabled, required, and the backward-compatible compact alias.

+

Component sizes

+

Use one of the three supported display modes:

+
<Captcha size="compact" action="small-form" />
+<Captcha size="normal" action="standard-form" />
+<Captcha size="big" action="security-page" />
+

small/sm are accepted as aliases for compact, while large/lg are accepted as aliases for big. The old compact="true" prop still forces compact mode.

+

Listen button visibility

+

Audio remains available by default. Hide the Listen and Use audio controls with either of these props:

+
<Captcha showListen="false" action="without-listen-button" />
+<Captcha showAudio="false" action="without-audio-alternative" />
+

showListen is the direct UI switch. showAudio remains the broader backward-compatible audio switch.

+

I’m not a robot checkbox

+
<Captcha
+  type="not-robot"
+  action="contact-submit"
+  size="compact"
+  showListen="false"
+/>
+

The checkbox is not a client-only boolean. Clicking it completes a self-hosted invisible challenge that is time-limited, attempt-limited, one-time-use, action-bound, optionally session/hostname/IP-bound, and verified on the server. It is a low-friction anti-automation layer; use adaptive escalation to a visual or external provider for high-risk traffic.

+

Visual disturbance

+

Use disturbance for visual and image-selection challenges. It accepts an integer from 25 through 75:

+
    +
  • 25: light disturbance and easiest readability
  • +
  • 50: balanced default
  • +
  • 75: maximum supported dots, line crossings, glyph movement, and image-tile noise
  • +
+

The browser sends this value to the challenge API, and the server validates the range before generating the challenge. It is also returned in challenge metadata.

+

Generated image renderer styles

+

Text, number, alphanumeric, and calculation CAPTCHA images support 18 concrete renderers plus a random mode:

+

classic, collision, snow, corrosion, spiderweb, cross-shadow, split, split2, cut, darts, distortion, stitch, striped, wave, grid-noise, scribble, pixel, and broken-lines.

+

Use a fixed style:

+
<Captcha
+  type="alphanumeric"
+  action="signup"
+  imageStyle="spiderweb"
+  disturbance="55"
+/>
+

Use a new random style whenever the challenge is refreshed:

+
<Captcha
+  type="number"
+  action="login"
+  imageStyle="random"
+  difficulty="normal"
+/>
+

Control the random pool with comma-separated component props or arrays in the TypeScript API:

+
<Captcha
+  type="alphanumeric"
+  action="checkout"
+  imageStyle="random"
+  allowedStyles="classic,snow,distortion,wave"
+  excludedStyles="collision"
+/>
+
const challenge = await engine.create({
+  action: "checkout",
+  type: "alphanumeric",
+  imageStyle: "random",
+  allowedStyles: ["classic", "snow", "distortion", "wave"],
+  excludedStyles: ["collision"],
+});
+

Set randomizeStyle: true to force random selection even when imageStyle names a concrete renderer. The resolved style, requested style, and active pool are returned in challenge metadata. The answer is never embedded in metadata or browser JavaScript.

+

Events

+

@ready, @challenge, @input, @verify, @success, @failure, @expired, @refresh, @audioStart, @audioEnd, and @error.

+

Protect a validated form API

+

Validate a cloned request first, then consume the CAPTCHA response token. This prevents a valid token from being consumed when ordinary field validation fails.

+
import { captchaGuard } from "@wrnexus/captcha/server";
+import { parseBody } from "@wrnexus/validation";
+import contactSchema from "../schemas/contact.ts";
+import { engine } from "../lib/captcha.ts";
+
+const guard = captchaGuard({
+  action: "contact-submit",
+  engine,
+  bindHostname: true,
+  bindSession: true,
+});
+
+export async function POST(ctx) {
+  const validation = await parseBody(contactSchema, ctx.req.clone());
+  if (!validation.ok) return validation.response;
+
+  return guard(ctx, async () => Response.json({ ok: true, submission: validation.value }));
+}
+

The CAPTCHA runtime binds its required-form check in the capture phase, so a data-schema validator cannot submit the form before CAPTCHA verification. After a successful form request, the component automatically creates a fresh challenge.

+

Retryable operations such as login

+

A login may consume a valid CAPTCHA and then fail because the password is incorrect. Configure a short action-bound session grant so the user can correct their credentials without solving CAPTCHA again:

+
const guard = captchaGuard({
+  action: "auth-login",
+  engine,
+  bindHostname: true,
+  bindSession: true,
+  verifiedForMs: 5 * 60_000,
+});
+

Keep the verified widget state for non-CAPTCHA form errors:

+
<Captcha
+  action="auth-login"
+  required="true"
+  resetOnError="false"
+/>
+

The grant is stored in the current session and bound to the configured action. Expired grants and CAPTCHA-specific errors still require and load a fresh challenge. Keep login rate limits and authentication lockout enabled; verifiedForMs removes repeated human verification, not credential-abuse controls.

+

Validate a schema and CAPTCHA together

+
const result = await parseWithCaptcha(signupSchema, body, ctx, {
+  action: "signup",
+  engine,
+});
+
+if (!result.ok) return Response.json({ ok: false, errors: result.errors }, { status: 400 });
+

Page gate

+
export default captchaPageGate({
+  action: "reports-access",
+  engine,
+  challengePath: "/captcha",
+  policy: {
+    mode: "session",
+    verifiedForMs: 15 * 60_000,
+    routeGroups: ["/reports"],
+  },
+});
+

Use mode: "always" for every visit, mode: "session" for a temporary grant, or mode: "adaptive" with signals(ctx).

+

The challenge page should post the return path as a normal hidden field instead of constructing JavaScript inside the HTML action attribute:

+
<form method="post" action="/api/page-grant">
+  <input type="hidden" name="returnTo" value='{returnTo}' />
+  <Captcha action="reports-access" />
+  <button type="submit">Continue</button>
+</form>
+

The /api/page-grant route reads returnTo, restricts it to the current origin, and redirects only after captchaPageGate() has verified and stored the temporary session grant.

+

External providers

+
const turnstile = turnstileProvider({
+  secretKey: process.env.TURNSTILE_SECRET!,
+  siteKey: process.env.PUBLIC_TURNSTILE_SITE_KEY!,
+  expectedHostnames: ["example.com"],
+  expectedAction: "signup",
+});
+
<Captcha
+  provider="turnstile"
+  siteKey="PUBLIC_SITE_KEY"
+  action="signup"
+/>
+

Use the matching provider in captchaGuard({ provider: turnstile }). reCAPTCHA and hCaptcha adapters follow the same pattern.

+

Managed provider

+
const managed = managedCaptchaProvider({
+  baseUrl: "https://captcha.example.com",
+  siteKey: process.env.PUBLIC_CAPTCHA_SITE_KEY!,
+  secretKey: process.env.CAPTCHA_SECRET_KEY!,
+});
+

For direct browser challenge creation, configure the component’s endpoint as the managed /v1/challenges URL and its verifyEndpoint as /v1/solve. Keep the secret key only in the server provider.

+

Stores

+
    +
  • MemoryCaptchaStore: development and one-process applications
  • +
  • SqliteCaptchaStore: adapter for SQLite-like prepare().run/get/all() clients
  • +
  • RedisCaptchaStore: shared TTL storage with Lua-backed atomic consumption when eval is available
  • +
  • CaptchaStore: implement this interface for PostgreSQL, MySQL, MongoDB, or another backend
  • +
+

Audio

+

AssetAudioRenderer concatenates bundled English PCM WAV clips without calling an external service. Supply a custom CaptchaAudioRenderer for recorded voices, Hindi or other languages, or managed text-to-speech.

+

DevToolbar

+

The automatically discovered CAPTCHA plugin registers its DevToolbar audit panel. It checks for likely client-side secrets, missing action bindings, missing provider site keys, optional CAPTCHA fields, accessible alternatives, and server-verification reminders. Explicit captchaPlugin(options) registration is needed only to override automatic configuration.

+

Testing

+

Use deterministic custom generators in unit tests. Never require users or CI to solve random CAPTCHA images. The package includes engine, provider, policy, HTTP, storage, replay, expiry, binding, and audio authorization tests.

+

Custom challenge generator

+
import { defineCaptchaGenerator, createCaptchaEngine } from "@wrnexus/captcha";
+
+const wordChallenge = defineCaptchaGenerator({
+  type: "word" as const,
+  generate(context) {
+    const answer = "NEXUS";
+    return {
+      type: "word",
+      presentation: "visual",
+      prompt: "Enter the displayed word",
+      answer,
+      answerKind: "text",
+      image: renderYourImage(answer),
+      inputMode: "text",
+    };
+  },
+});
+
+const engine = createCaptchaEngine({ secret, generators: [wordChallenge] });
+

Applications may also implement CaptchaStore, CaptchaAudioRenderer, or use defineCaptchaProvider() for a completely custom service.

Complete TypeScript API

Generated from the exact installed package declarations.

export { CaptchaAudioRenderer, CaptchaBinding, CaptchaChallenge, CaptchaChallengeGenerator, CaptchaChallengeRecord, CaptchaChallengeType, CaptchaConcreteImageStyle, CaptchaDifficulty, CaptchaEngine, CaptchaEngineOptions, CaptchaFailureCode, CaptchaGeneratorContext, CaptchaGuardOptions, CaptchaHttpHandlers, CaptchaImageItem, CaptchaImageStyle, CaptchaMiddleware, CaptchaPageGateOptions, CaptchaPolicyMode, CaptchaPolicyOptions, CaptchaPresentation, CaptchaProvider, CaptchaProviderClientConfig, CaptchaProviderName, CaptchaResponseTokenRecord, CaptchaRiskResult, CaptchaRiskSignals, CaptchaStore, CaptchaVerificationResult, CreateCaptchaOptions, GeneratedCaptchaChallenge, VerifyCaptchaInput } from './types.js';
+export { CaptchaHttpOptions, CaptchaParseResult, CaptchaSessionGrant, DefaultCaptchaEngine, ParseWithCaptchaOptions, bindingHash, bytesToBase64Url, captchaGuard, captchaPageGate, clearCaptchaGrants, constantTimeEqual, createCaptchaEngine, createCaptchaHttpHandlers, defaultRandomBytes, evaluateCaptchaRisk, hmacSha256, parseWithCaptcha, randomId, sha256, shouldRequireCaptcha, validCaptchaGrant } from './server/index.js';
+export { CaptchaAuditIssue, CaptchaPluginOptions, captchaComponentsDir, captchaPlugin } from './plugin.js';
+export { MemoryCaptchaStore, MemoryCaptchaStoreOptions, createMemoryCaptchaStore } from './stores/memory.js';
+export { SqliteCaptchaStore, SqliteCaptchaStoreOptions, SqliteDatabaseLike, SqliteStatementLike, createSqliteCaptchaStore } from './stores/sqlite.js';
+export { RedisCaptchaClient, RedisCaptchaStore, RedisCaptchaStoreOptions, createRedisCaptchaStore } from './stores/redis.js';
+export { SelfHostedCaptchaProvider, selfHostedProvider } from './providers/self-hosted.js';
+export { S as SiteverifyCaptchaProvider, a as SiteverifyPreset, b as SiteverifyProviderOptions } from './siteverify-Cg3TTAp4.js';
+export { TurnstileCaptchaProvider, turnstileProvider } from './providers/turnstile.js';
+export { RecaptchaProvider, recaptchaProvider } from './providers/recaptcha.js';
+export { HcaptchaProvider, hcaptchaProvider } from './providers/hcaptcha.js';
+export { ManagedCaptchaProvider, ManagedCaptchaProviderOptions, managedCaptchaProvider } from './providers/managed.js';
+export { defineCaptchaProvider } from './providers/custom.js';
+export { CAPTCHA_CONCRETE_IMAGE_STYLES, CAPTCHA_IMAGE_STYLES, CalculationCaptchaGenerator, ImageCaptchaGenerator, InvisibleCaptchaGenerator, ResolveCaptchaImageStyleOptions, ResolvedCaptchaImageStyle, Rgba, RgbaImage, TextCaptchaGenerator, alphaCaptchaGenerator, alphanumericCaptchaGenerator, bytesToBase64, calculationCaptchaGenerator, createImage, defaultCaptchaGenerators, defineCaptchaGenerator, drawGlyph, drawLine, drawText, encodePng, fillCircle, fillPolygon, fillRect, honeypotCaptchaGenerator, imageCaptchaGenerator, isCaptchaImageStyle, normalizeCaptchaImageStyle, normalizeCaptchaImageStyleList, notRobotCaptchaGenerator, numberCaptchaGenerator, pngDataUri, resolveCaptchaImageStyle, setPixel, timingCaptchaGenerator } from './challenges/index.js';
+export { AssetAudioRenderer, AssetAudioRendererOptions, createAssetAudioRenderer, resolveCaptchaAudioAssetsDir } from './audio/index.js';
+import '@wrnexus/core';
+import '@wrnexus/validation';
+import '@wrnexus/plugin';
+

Examples

Copy-ready examples from the installed package documentation.

## Install

bun add @wrnexus/captcha

## Create the self-hosted engine

import {
+  createCaptchaEngine,
+  createCaptchaHttpHandlers,
+  RedisCaptchaStore,
+} from "@wrnexus/captcha/server";
+
+const engine = createCaptchaEngine({
+  secret: process.env.CAPTCHA_SECRET!,
+  store: new RedisCaptchaStore(redis),
+  basePath: "/api/captcha",
+  challengeTtlMs: 2 * 60_000,
+  responseTokenTtlMs: 5 * 60_000,
+  maxAttempts: 3,
+  minCompletionMs: 800,
+});
+
+export const handlers = createCaptchaHttpHandlers(engine);

Mount the handlers from an API catch-all route

import type { Context } from "@wrnexus/core";
+import { handlers } from "../../lib/captcha.ts";
+
+export async function POST(ctx: Context) {
+  return (await handlers.handle(ctx.req, ctx)) ?? new Response("Not Found", { status: 404 });
+}
+export const GET = POST;
+export const HEAD = POST;

## Use the component

<Captcha
+  type="alphanumeric"
+  action="signup"
+  endpoint="/api/captcha/challenge"
+  verifyEndpoint="/api/captcha/verify"
+  difficulty="normal"
+  disturbance="50"
+  imageStyle="random"
+  allowedStyles="classic,snow,distortion,wave"
+  size="normal"
+  showAudio="true"
+  showListen="true"
+  @success='captchaToken = event.detail.responseToken'
+  @failure='formError = event.detail.extra.message'
+/>

Component sizes

<Captcha size="compact" action="small-form" />
+<Captcha size="normal" action="standard-form" />
+<Captcha size="big" action="security-page" />

Listen button visibility

<Captcha showListen="false" action="without-listen-button" />
+<Captcha showAudio="false" action="without-audio-alternative" />
+ +
+ + +
+ } +} diff --git a/app/pages/packages/dev-toolbar.wrn b/app/pages/packages/dev-toolbar.wrn new file mode 100644 index 00000000..3d5aae05 --- /dev/null +++ b/app/pages/packages/dev-toolbar.wrn @@ -0,0 +1,44 @@ +page wrnexusdevtoolbar { + seo { + title = "@wrnexus/dev-toolbar" + description = "Development toolbar diagnostics, inspection, and runtime status." + } + + view { +
+ +
+ W WRNexusJS + + +
+ +
+
Tooling · Package reference

@wrnexus/dev-toolbar

Development toolbar diagnostics, inspection, and runtime status.

v0.5.0Private registryTooling

Install the package

After WorkRoot approves private registry access, install the release-aligned package:

bun add @wrnexus/dev-toolbar@0.5.0

Request preview access. Never put registry tokens in source control.

Development-only page quality toolbar for WRNexusJS.

+

Features

+
    +
  • Runtime, resource and unhandled promise error capture
  • +
  • Accessibility, SEO, image, media, color, HTML, form, link, responsive and security checks
  • +
  • Performance and network observations
  • +
  • Element highlighting and issue filtering
  • +
  • Server-side issue collector
  • +
  • Development-only asset strings for direct serving by @wrnexus/dev-server
  • +
  • Safe open-in-editor helper
  • +
+

Dev-server integration

+

Serve DEV_TOOLBAR_RUNTIME at /__wrnexus/dev-toolbar.js and DEV_TOOLBAR_CSS at /__wrnexus/dev-toolbar.css, then inject this before </body> in development:

+
<script type="module" src="/__wrnexus/dev-toolbar.js" data-wrnexus-dev-toolbar></script>
+

The browser runtime exposes window.__wrnexusDevToolbar.

Complete TypeScript API

Generated from the exact installed package declarations.

export { DevToolbarCategory, DevToolbarClientApi, DevToolbarConfig, DevToolbarElementTarget, DevToolbarFix, DevToolbarIssue, DevToolbarMetrics, DevToolbarPageReport, DevToolbarPanel, DevToolbarPlatformSnapshot, DevToolbarServerMessage, DevToolbarSeverity, DevToolbarSourceLocation } from './types.js';
+export { DEV_TOOLBAR_RULES, DevToolbarRule, DevToolbarRuleContext, accessibilityRules, accessibleName, colorRules, contrastRatio, createFingerprint, createIssue, effectiveBackground, formRules, getStableSelector, htmlRules, imageRules, isVisible, linkRules, luminance, mediaRules, parseRgb, parseSource, performanceRules, responsiveRules, runDevToolbarRules, securityRules, seoRules } from './rules/index.js';
+export { DevToolbarApp, DevToolbarCollector, DevToolbarIssueListener, DevToolbarRegistry, DevToolbarRouteOptions, OpenEditorOptions, OpenEditorRequest, buildEditorCommand, createDevToolbarCollector, createDevToolbarRegistry, createServerIssue, handleDevToolbarRoute, issueFromError, openInEditor, resolveEditorFile, serializeDevToolbarJson } from './server/index.js';
+export { DEV_TOOLBAR_CSS, DEV_TOOLBAR_RUNTIME } from './client/index.js';
+

Examples

Copy-ready examples from the installed package documentation.

Serve DEVTOOLBARRUNTIME at /wrnexus/dev-toolbar.js and DEVTOOLBARCSS at /wrnexus/dev-toolbar.css, then inject this before </body> in development

<script type="module" src="/__wrnexus/dev-toolbar.js" data-wrnexus-dev-toolbar></script>

Run development-toolbar rules

import { runDevToolbarRules } from "@wrnexus/dev-toolbar";
+
+const issues = runDevToolbarRules(context);
+ +
+ + +
+ } +} diff --git a/app/pages/packages/plugin.wrn b/app/pages/packages/plugin.wrn new file mode 100644 index 00000000..2dd2ca98 --- /dev/null +++ b/app/pages/packages/plugin.wrn @@ -0,0 +1,46 @@ +page wrnexusplugin { + seo { + title = "@wrnexus/plugin" + description = "Plugin contracts, lifecycle hooks, composition, and framework integration." + } + + view { +
+ +
+ W WRNexusJS + + +
+ +
+
Core · Package reference

@wrnexus/plugin

Plugin contracts, lifecycle hooks, composition, and framework integration.

v0.5.0Private registryCore

Install the package

After WorkRoot approves private registry access, install the release-aligned package:

bun add @wrnexus/plugin@0.5.0

Request preview access. Never put registry tokens in source control.

Deterministic WRNexusJS plugin contracts for configuration, AST/code transforms, diagnostics, development servers, production builds, and DevToolbar extensions.

+

Use definePlugin() and declare enforce, before, or after when ordering matters. Duplicate names and dependency cycles are rejected.

Complete TypeScript API

Generated from the exact installed package declarations.

export { PageAst, WrnDiagnostic } from '@wrnexus/syntax';
+import { WrnexusPlugin, PluginInput, PluginContext, PluginRunner } from './types.js';
+export { ClientRuntimeDefinition, ClientRuntimeInject, ClientRuntimeLoad, ClientRuntimeType, PackageAssetDefinition, PackageMigrationDefinition, PackagePluginManifest, PackageRouteDefinition, PackageStyleDefinition, PluginCommand, PluginContributions, PluginDevToolbarPanel, PluginOrder, TransformContext, WrnexusPackageManifest } from './types.js';
+export { assertContributionId, contentTypeForPath, defaultClientRuntimePath, defaultPackageAssetPath, definePackageManifest, normalizeClientRuntime, normalizePackageAsset, validateStyleIds } from './manifest.js';
+export { DiscoverPluginOptions, discoverPlugins } from './discovery.js';
+
+declare function definePlugin(plugin: WrnexusPlugin): WrnexusPlugin;
+declare function flattenPlugins(input: PluginInput, output?: WrnexusPlugin[]): WrnexusPlugin[];
+
+/** Resolve plugin order deterministically and reject duplicates/cycles. */
+declare function resolvePlugins(input: PluginInput): WrnexusPlugin[];
+declare function createPluginRunner(input: PluginInput, context: PluginContext): PluginRunner;
+
+export { PluginContext, PluginInput, PluginRunner, WrnexusPlugin, createPluginRunner, definePlugin, flattenPlugins, resolvePlugins };
+

Examples

Copy-ready examples from the installed package documentation.

Define an ordered plugin

import { definePlugin } from "@wrnexus/plugin";
+
+export default definePlugin({
+  name: "analytics",
+  enforce: "post",
+});

Resolve plugin execution order

import { resolvePlugins } from "@wrnexus/plugin";
+
+const ordered = resolvePlugins([corePlugin, analyticsPlugin]);
+ +
+ + +
+ } +} diff --git a/app/pages/packages/syntax.wrn b/app/pages/packages/syntax.wrn new file mode 100644 index 00000000..e97b34a2 --- /dev/null +++ b/app/pages/packages/syntax.wrn @@ -0,0 +1,55 @@ +page wrnexussyntax { + seo { + title = "@wrnexus/syntax" + description = "Editor syntax definitions and language tooling for .wrn files." + } + + view { +
+ +
+ W WRNexusJS + + +
+ +
+
Frontend · Package reference

@wrnexus/syntax

Editor syntax definitions and language tooling for .wrn files.

v0.5.0Private registryFrontend

Install the package

After WorkRoot approves private registry access, install the release-aligned package:

bun add @wrnexus/syntax@0.5.0

Request preview access. Never put registry tokens in source control.

Canonical WRN lexer, parser, AST, language metadata, source positions, and stable diagnostics. Framework tooling should import this package instead of implementing a separate .wrn parser.

+

See docs/WRN-LANGUAGE-SPEC-1.0.md in the WRNexusJS repository.

Complete TypeScript API

Generated from the exact installed package declarations.

export { LexError, Lexer } from './tokenizer.js';
+export { ActionBlock, ApiBlock, Attr, ComputedDecl, DataApiBlock, DataMode, EffectBlock, EventDecl, LifecycleBlock, LoadBlock, ModeFunctionsBlock, PageAst, ParseError, PropDecl, RealtimeBlock, RealtimeHandler, SeoBlock, StateDecl, VOID_ELEMENTS, ViewNode, WatchBlock, parse, parseHtmlView } from './parser.js';
+export { RuntimeType, eraseFunctionTypes, inferredRuntimeType, runtimeTypeOf, validateTypedInitializer } from './types.js';
+import { WrnDiagnostic } from './diagnostics.js';
+export { DiagnoseOptions, WrnDiagnosticSeverity, WrnSourcePosition, assertValidAst, classifyParseError, diagnose, diagnosticFromError, formatDiagnostic, isHydrationStrategy, isRuntimeTarget, positionAt } from './diagnostics.js';
+export { WRN_DIAGNOSTIC_CODES, WRN_HYDRATION_STRATEGIES, WRN_LANGUAGE_VERSION, WRN_ROOT_KINDS, WRN_ROOT_MEMBERS, WRN_RUNTIME_TARGETS, WrnHydrationStrategy, WrnRootKind, WrnRootMember, WrnRuntimeTarget } from './spec.js';
+
+/** Current stable syntax contract. Bump only when parsers/codegen need migration. */
+declare const WRN_SYNTAX_VERSION: "0.4";
+type WrnSyntaxFeature = "typed-declarations" | "layouts" | "server-client-blocks" | "effects" | "watch" | "lifecycle" | "embedded-api" | "realtime" | "runtime-markers";
+declare const WRN_SYNTAX_FEATURES: Readonly<Record<WrnSyntaxFeature, boolean>>;
+interface SourceRange {
+    start: number;
+    end: number;
+}
+declare function createSourceRange(start: number, end: number): SourceRange;
+declare function sliceSource(source: string, range: SourceRange): string;
+declare function diagnosticSummary(diagnostics: readonly WrnDiagnostic[]): {
+    errors: number;
+    warnings: number;
+    info: number;
+    codes: Record<string, number>;
+};
+declare function supportsSyntaxFeature(feature: string): feature is WrnSyntaxFeature;
+
+export { type SourceRange, WRN_SYNTAX_FEATURES, WRN_SYNTAX_VERSION, WrnDiagnostic, type WrnSyntaxFeature, createSourceRange, diagnosticSummary, sliceSource, supportsSyntaxFeature };
+

Examples

Copy-ready examples from the installed package documentation.

Parse a WRNexusJS document

import { parse } from "@wrnexus/syntax";
+
+const ast = parse('component Greeting { view { <p>Hello</p> } }');

Summarize syntax diagnostics

import { diagnose, diagnosticSummary } from "@wrnexus/syntax";
+
+const summary = diagnosticSummary(diagnose(source));
+ +
+ + +
+ } +} diff --git a/scripts/generate-docs.ts b/scripts/generate-docs.ts index 9e71df85..92a764c0 100644 --- a/scripts/generate-docs.ts +++ b/scripts/generate-docs.ts @@ -40,13 +40,16 @@ const uiReference: UiComponentReference | undefined = existsSync(uiReferencePath const catalog = [ ["ai", "AI", "Server-side Anthropic client with generation and streaming."], + ["auth", "Security", "Authentication routes, sessions, forms, guards, and account flows."], ["authz", "Security", "Role, permission, policy, and authorization guards."], + ["captcha", "Security", "Managed CAPTCHA verification, middleware, and UI integration."], ["cli", "Tooling", "Create, develop, build, generate, test, and maintain WrNexus apps."], ["compiler", "Core", "Parser and code generators for the .wrn language."], ["core", "Core", "Contexts, middleware, security, sessions, caching, JSX, and realtime."], ["csr", "Frontend", "Reactive, navigation, and realtime browser runtimes."], ["db", "Data", "Database adapters, typed queries, models, migrations, and sessions."], ["dev-server", "Runtime", "Development and production servers, HMR, assets, and gateways."], + ["dev-toolbar", "Tooling", "Development toolbar diagnostics, inspection, and runtime status."], ["encryption", "Security", "Hashing, HMAC, authenticated encryption, and key derivation."], ["helpers", "Tooling", "Safe Context URL helpers and forward-auth login redirects."], ["i18n", "Frontend", "Translation loading, locale resolution, and Intl formatting."], @@ -54,12 +57,14 @@ const catalog = [ ["mobile", "Native", "SSR-safe compatibility access to Capacitor plugins."], ["native", "Native", "Cross-platform browser and Capacitor capability registry."], ["oauth", "Security", "OAuth 2.0, PKCE, provider presets, and profile mapping."], + ["plugin", "Core", "Plugin contracts, lifecycle hooks, composition, and framework integration."], ["pubsub", "Realtime", "In-process and Redis-backed publish/subscribe."], ["queue", "Data", "Background jobs with delay, concurrency, retry, and repetition."], ["reactive", "Frontend", "Small type-safe reactive signal primitives."], ["router", "Core", "Filesystem discovery, route matching, and typed route generation."], ["ssr", "Runtime", "Secure HTML document rendering and SEO metadata."], ["styles", "Frontend", "CSS pipeline, themes, fonts, profiles, and application config."], + ["syntax", "Frontend", "Editor syntax definitions and language tooling for .wrn files."], ["test", "Tooling", "WrNexus-aware component, route, and browser testing utilities."], ["tracking", "Runtime", "Error/event capture, middleware, filtering, and sinks."], ["ui", "Frontend", "Themeable server-rendered UI components and CSS."], @@ -199,9 +204,10 @@ function markdown(source: string): { html: string; headings: DocHeading[] } { 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 afterHeading = usageHeading + ? readme.slice(usageHeading.index + usageHeading[0].length) + : readme; + const nextSection = usageHeading ? /^##[ \t]+/m.exec(afterHeading) : undefined; const usage = afterHeading.slice(0, nextSection?.index ?? afterHeading.length); const examples: { title: string; language: string; code: string }[] = []; @@ -244,8 +250,65 @@ function examplesFrom(readme: string, name: string): string { .trim(); } } + const fallbacks: Record> = { + plugin: [ + { + title: "Define an ordered plugin", + language: "ts", + code: `import { definePlugin } from "@wrnexus/plugin"; + +export default definePlugin({ + name: "analytics", + enforce: "post", +});`, + }, + { + title: "Resolve plugin execution order", + language: "ts", + code: `import { resolvePlugins } from "@wrnexus/plugin"; + +const ordered = resolvePlugins([corePlugin, analyticsPlugin]);`, + }, + ], + syntax: [ + { + title: "Parse a WRNexusJS document", + language: "ts", + code: `import { parse } from "@wrnexus/syntax"; + +const ast = parse('component Greeting { view {

Hello

} }');`, + }, + { + title: "Summarize syntax diagnostics", + language: "ts", + code: `import { diagnose, diagnosticSummary } from "@wrnexus/syntax"; + +const summary = diagnosticSummary(diagnose(source));`, + }, + ], + "dev-toolbar": [ + { + title: "Run development-toolbar rules", + language: "ts", + code: `import { runDevToolbarRules } from "@wrnexus/dev-toolbar"; + +const issues = runDevToolbarRules(context);`, + }, + { + title: "Create a toolbar registry", + language: "ts", + code: `import { createDevToolbarRegistry } from "@wrnexus/dev-toolbar"; + +const registry = createDevToolbarRegistry();`, + }, + ], + }; + for (const fallback of fallbacks[name] ?? []) { + if (examples.length >= 2) break; + examples.push(fallback); + } if (examples.length < 2) { - throw new Error(`@wrnexus/${name} README needs at least two Usage code examples`); + throw new Error(`@wrnexus/${name} README needs at least two documented code examples`); } return examples .slice(0, 6)