page wrnexuscore {
  seo {
    title = "@wrnexus/core"
    description = "Contexts, middleware, security, sessions, caching, JSX, and realtime."
  }

  view {
    <div class="docs-shell">
      <SkipLink label="Skip to content" href="#main" class="docs-skip-link" />
      <header class="topbar">
        <a class="brand" href="/"><span>W</span> WRNexusJS</a>
        <nav aria-label="Primary"><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="/components">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a></nav>
        <div class="topbar-actions"><a class="preview-pill" href="/access">Private preview · v0.3.2</a><button data-wire-theme-toggle class="theme-button" aria-label="Toggle color theme" title="Toggle color theme">◐</button></div>
      </header>
      <div class="mobile-doc-nav"><details><summary>Browse documentation</summary><nav><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="/components">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a><a href="/tutorial">Tutorial</a><a href="/guides/project-structure">Guides</a><a href="/examples">Examples</a><a href="/search">Search</a></nav></details></div>
      <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/core</span></nav><section class="doc-intro"><span class="eyebrow">Core · Package reference</span><h1>@wrnexus/core</h1><p>Contexts, middleware, security, sessions, caching, JSX, and realtime.</p><div class="doc-meta"><span>v0.3.2</span><span>Private registry</span><span>Core</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/core@0.3.2</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"><blockquote>The framework core: the request <code>Context</code>, middleware contract, and the security, session, caching, streaming, realtime, and JSX primitives every other WRNexusJS package builds on.</blockquote>
<p>Part of the <strong>WRNexusJS</strong> framework — an SSR-first, Bun-native full-stack web framework.</p>
<h3 id="overview">Overview</h3>
<p><code>@wrnexus/core</code> is the shared foundation of WRNexusJS. It defines the <code>Context</code> object that flows through every middleware, page, and API route, plus the <code>Middleware</code>/<code>Next</code> contract they implement. On top of that it ships the building blocks a real app needs: cookie-backed sessions, password auth, CSRF protection, rate limiting, request logging, HTTP + in-memory caching, file uploads, streaming/SSE responses, WebSocket &quot;rooms&quot;, security headers/CORS, and a server-side JSX runtime that renders to HTML strings. Everything here is <strong>server-side</strong> and Bun-native (it uses <code>Bun.password</code>, <code>Bun.write</code>, the web-standard <code>Request</code>/<code>Response</code>, and <code>crypto</code>). You depend on it directly and transitively through the rest of the framework.</p>
<pre data-language="bash"><code>bun add @wrnexus/core</code></pre>
<blockquote>Private package — the machine must be authenticated to the <code>wrnexus</code> npm org</blockquote>
<blockquote>(a read token in <code>~/.npmrc</code>). Requires <strong>Bun</strong> (Node is not supported).</blockquote>
<h3 id="api">API</h3>
<h4 id="context-middleware-wrnexus-core">Context &amp; middleware — <code>@wrnexus/core</code></h4>
<p>The <code>Context</code> (<code>ctx</code>) is the single value passed to middleware and handlers.</p>
<div class="table-wrap"><table>
<thead><tr><th>Export</th><th>Kind</th><th>Description</th></tr></thead>
<tbody><tr><td><code>Context</code></td><td>type</td><td>Per-request object: <code>req</code>, <code>url</code>, <code>lang</code>, <code>t</code>, <code>params</code>, <code>locals</code>, <code>user?</code>, <code>ip?</code>, <code>cookies</code>, <code>session</code>, <code>localStorage</code>.</td></tr><tr><td><code>Next</code></td><td>type</td><td>`() =&gt; Promise&lt;Response&gt; \</td><td>Response` — invokes the next middleware/handler.</td></tr><tr><td><code>Middleware</code></td><td>type</td><td>`(ctx, next) =&gt; Promise&lt;Response&gt; \</td><td>Response<code>. Return </code>next()<code> to continue, or a </code>Response` to short-circuit.</td></tr><tr><td><code>createContext(req, url)</code></td><td>fn</td><td>Build a fresh <code>Context</code> for an incoming request (wires up cookies, session, localStorage snapshot).</td></tr><tr><td><code>withContextHeaders(ctx, res)</code></td><td>fn</td><td>Apply accumulated headers (e.g. <code>Set-Cookie</code>) from the context onto a response.</td></tr><tr><td><code>PageComponent</code></td><td>type</td><td>`(ctx) =&gt; string \</td><td>Promise&lt;string&gt;` — a page module's default export.</td></tr><tr><td><code>PageMeta</code> / <code>SeoConfig</code></td><td>type</td><td><code>&lt;head&gt;</code> metadata: <code>title</code>, <code>description</code>, <code>canonical</code>, <code>robots</code>, <code>image</code>, <code>twitterCard</code>, <code>themeColor</code>, …</td></tr><tr><td><code>TFunction</code></td><td>type</td><td><code>(key, params?) =&gt; string</code> — translate a key for <code>ctx.lang</code>, interpolating <code>&#123;param&#125;</code> placeholders.</td></tr></tbody></table></div>
<p>Key <code>Context</code> fields:</p>
<ul>
<li><code>ctx.locals</code> — per-request scratch space for passing values between middleware.</li>
<li><code>ctx.user</code> — the authenticated user (populated by <code>sessionAuth</code>/<code>logIn</code>), or <code>null</code>.</li>
<li><code>ctx.ip</code> — the direct socket peer IP (not spoofable via headers).</li>
<li><code>ctx.cookies</code> / <code>ctx.session</code> / <code>ctx.localStorage</code> — see <strong>Storage</strong> below.</li>
</ul>
<h4 id="authentication-wrnexus-core">Authentication — <code>@wrnexus/core</code></h4>
<p>Passwords are hashed with argon2id via <code>Bun.password</code>; sessions ride the cookie-backed <code>SessionStore</code>.</p>
<div class="table-wrap"><table>
<thead><tr><th>Export</th><th>Signature</th><th>Notes</th></tr></thead>
<tbody><tr><td><code>hashPassword(password)</code></td><td><code>(string) =&gt; Promise&lt;string&gt;</code></td><td>argon2id hash to store.</td></tr><tr><td><code>verifyPassword(password, hash)</code></td><td><code>(string, string) =&gt; Promise&lt;boolean&gt;</code></td><td>Constant-safe; returns <code>false</code> on bad/empty hash.</td></tr><tr><td><code>logIn(ctx, user)</code></td><td><code>(Context, U) =&gt; void</code></td><td>Regenerates the session id (fixation defense), stores the user, sets <code>ctx.user</code>.</td></tr><tr><td><code>logOut(ctx)</code></td><td><code>(Context) =&gt; void</code></td><td>Clears the session and <code>ctx.user</code>.</td></tr><tr><td><code>getUser(ctx)</code></td><td>`(Context) =&gt; U \</td><td>null`</td><td>Current user from <code>ctx.user</code>, falling back to the session.</td></tr><tr><td><code>sessionAuth()</code></td><td><code>() =&gt; Middleware</code></td><td>Hydrates <code>ctx.user</code> from the session each request. Register early.</td></tr><tr><td><code>requireAuth(options?)</code></td><td><code>(RequireAuthOptions?) =&gt; Middleware</code></td><td>Guard: API/fetch requests get <code>401 JSON</code>, page navigations get <code>302</code> to <code>loginPath</code> (default <code>/login</code>) with <code>?next=</code>.</td></tr><tr><td><code>SESSION_USER_KEY</code></td><td><code>&quot;user&quot;</code></td><td>Session key holding the user.</td></tr></tbody></table></div>
<p><code>RequireAuthOptions</code>: <code>&#123; loginPath?: string &#125;</code>.</p>
<h4 id="csrf-wrnexus-core">CSRF — <code>@wrnexus/core</code></h4>
<p>Double-submit cookie pattern: a readable <code>wire-csrf</code> cookie is echoed in an <code>x-csrf-token</code> header on unsafe requests.</p>
<div class="table-wrap"><table>
<thead><tr><th>Export</th><th>Signature</th><th>Notes</th></tr></thead>
<tbody><tr><td><code>csrfToken(ctx)</code></td><td><code>(Context) =&gt; string</code></td><td>Ensures the CSRF cookie exists and returns its token.</td></tr><tr><td><code>verifyCsrf(ctx)</code></td><td><code>(Context) =&gt; boolean</code></td><td>Safe methods (GET/HEAD/OPTIONS) pass; otherwise header/<code>ctx.locals._csrf</code> must match the cookie (constant-time).</td></tr><tr><td><code>csrfProtection()</code></td><td><code>() =&gt; Middleware</code></td><td>403s unsafe requests with a missing/mismatched token.</td></tr><tr><td><code>CSRF_COOKIE</code> / <code>CSRF_HEADER</code></td><td><code>&quot;wire-csrf&quot;</code> / <code>&quot;x-csrf-token&quot;</code></td><td>Cookie &amp; header names.</td></tr></tbody></table></div>
<h4 id="rate-limiting-wrnexus-core">Rate limiting — <code>@wrnexus/core</code></h4>
<p>Fixed-window limiter that returns <code>429</code> with <code>Retry-After</code> and emits <code>RateLimit-Limit</code>/<code>-Remaining</code>/<code>-Reset</code> headers.</p>
<div class="table-wrap"><table>
<thead><tr><th>Export</th><th>Signature</th><th>Notes</th></tr></thead>
<tbody><tr><td><code>rateLimit(options?)</code></td><td><code>(RateLimitOptions?) =&gt; Middleware</code></td><td>Main middleware.</td></tr><tr><td><code>peerKey(ctx)</code></td><td><code>(Context) =&gt; string</code></td><td>Non-spoofable key from <code>ctx.ip</code> (default).</td></tr><tr><td><code>proxyKey(ctx)</code></td><td><code>(Context) =&gt; string</code></td><td>Trusts <code>x-forwarded-for</code>/<code>x-real-ip</code>. Use only behind a trusted proxy.</td></tr><tr><td><code>defaultKey</code></td><td>—</td><td><strong>Deprecated</strong> alias of <code>proxyKey</code>.</td></tr></tbody></table></div>
<p><code>RateLimitOptions</code>: <code>windowMs</code> (default <code>60_000</code>), <code>max</code> (default <code>60</code>), <code>key</code>, <code>trustProxy</code> (default <code>false</code> → keys on <code>peerKey</code>; <code>true</code> → <code>proxyKey</code>), <code>message</code>, <code>headers</code> (default <code>true</code>), <code>store</code>.</p>
<p><code>RateLimitStore</code> is pluggable — implement <code>hit(key, windowMs, now) =&gt; Bucket | Promise&lt;Bucket&gt;</code> (a <code>Bucket</code> is <code>&#123; count, resetAt &#125;</code>) to back limits with Redis/SQL across instances. The default store is process-local memory.</p>
<h4 id="request-logging-wrnexus-core">Request logging — <code>@wrnexus/core</code></h4>
<div class="table-wrap"><table>
<thead><tr><th>Export</th><th>Signature</th><th>Notes</th></tr></thead>
<tbody><tr><td><code>requestLogger(options?)</code></td><td><code>(RequestLoggerOptions?) =&gt; Middleware</code></td><td>One record per request with a request id (stored on <code>ctx.locals[requestIdKey]</code>).</td></tr></tbody></table></div>
<p><code>RequestLoggerOptions</code>: <code>format</code> (<code>&quot;pretty&quot;</code> default \| <code>&quot;json&quot;</code>), <code>sink(line, record)</code> (default <code>console.log</code>), <code>requestIdKey</code> (default <code>&quot;requestId&quot;</code>), <code>now</code>. <code>RequestRecord</code> = <code>&#123; time, id, method, path, status, durationMs &#125;</code>.</p>
<h4 id="caching-wrnexus-core">Caching — <code>@wrnexus/core</code></h4>
<div class="table-wrap"><table>
<thead><tr><th>Export</th><th>Kind</th><th>Notes</th></tr></thead>
<tbody><tr><td><code>TTLCache&lt;V&gt;</code></td><td>class</td><td>In-memory TTL cache: <code>get</code>, <code>set</code>, <code>getOrLoad(key, loader, ttlMs?)</code>, <code>delete</code>, <code>clear</code>, <code>size</code>. Constructor takes a default <code>ttlMs</code> (60s).</td></tr><tr><td><code>cacheControl(options)</code></td><td>fn</td><td>Build a <code>Cache-Control</code> value from <code>CacheControlOptions</code>.</td></tr><tr><td><code>withCacheControl(res, options)</code></td><td>fn</td><td>Apply <code>Cache-Control</code> to a response.</td></tr><tr><td><code>etag(body, weak?)</code></td><td>fn</td><td>Stable quoted FNV-1a ETag (weak by default).</td></tr><tr><td><code>notModified(req, tag)</code></td><td>fn</td><td><code>true</code> when <code>If-None-Match</code> matches — send a <code>304</code>.</td></tr></tbody></table></div>
<p><code>CacheControlOptions</code>: <code>maxAge</code>, <code>sMaxAge</code>, <code>private</code>, <code>noStore</code>, <code>noCache</code>, <code>staleWhileRevalidate</code>, <code>immutable</code>.</p>
<h4 id="file-uploads-wrnexus-core">File uploads — <code>@wrnexus/core</code></h4>
<p>Bun parses <code>multipart/form-data</code> via <code>Request.formData()</code>; these helpers validate and persist the resulting <code>File</code>s.</p>
<div class="table-wrap"><table>
<thead><tr><th>Export</th><th>Signature</th><th>Notes</th></tr></thead>
<tbody><tr><td><code>collectUploads(form)</code></td><td><code>(FormData) =&gt; &#123; field, file &#125;[]</code></td><td>Every non-empty <code>File</code> in a parsed form.</td></tr><tr><td><code>saveUpload(file, options)</code></td><td><code>(File, SaveUploadOptions) =&gt; Promise&lt;SavedUpload&gt;</code></td><td>Validates size/type, sanitizes the name, writes via <code>Bun.write</code>. Throws <code>UploadError</code>.</td></tr><tr><td><code>sanitizeFilename(name)</code></td><td><code>(string) =&gt; string</code></td><td>Strips separators, traversal, control/illegal chars; caps at 255.</td></tr><tr><td><code>UploadError</code></td><td>class</td><td>Thrown on rejected uploads.</td></tr></tbody></table></div>
<p><code>SaveUploadOptions</code>: <code>dir</code> (required), <code>maxBytes</code>, <code>allowedTypes</code> (MIME types like <code>&quot;image/png&quot;</code> and/or extensions like <code>&quot;.png&quot;</code>), <code>filename(file)</code>. <code>SavedUpload</code> = <code>&#123; path, filename, size, type &#125;</code>.</p>
<h4 id="streaming-sse-wrnexus-core">Streaming &amp; SSE — <code>@wrnexus/core</code></h4>
<div class="table-wrap"><table>
<thead><tr><th>Export</th><th>Signature</th><th>Notes</th></tr></thead>
<tbody><tr><td><code>streamResponse(source, init?)</code></td><td>`(Iterable\</td><td>AsyncIterable&lt;string\</td><td>Uint8Array&gt;, StreamResponseInit?) =&gt; Response`</td><td>Streaming <code>Response</code> from a chunk source (basis for streaming SSR).</td></tr><tr><td><code>sse(source)</code></td><td>`(Iterable\</td><td>AsyncIterable&lt;ServerSentEvent&gt;) =&gt; Response`</td><td><code>text/event-stream</code> response.</td></tr></tbody></table></div>
<p><code>StreamResponseInit</code>: <code>status</code>, <code>headers</code>, <code>contentType</code> (default <code>&quot;text/html; charset=utf-8&quot;</code>). <code>ServerSentEvent</code>: <code>&#123; data, event?, id?, retry? &#125;</code>.</p>
<h4 id="realtime-rooms-wrnexus-core">Realtime rooms — <code>@wrnexus/core</code></h4>
<p>WebSocket rooms. A file in <code>app/realtime/</code> exports <code>default defineRoom(&#123; ... &#125;)</code> and is served at <code>ws://host/realtime/&lt;name&gt;</code>.</p>
<div class="table-wrap"><table>
<thead><tr><th>Export</th><th>Signature</th><th>Notes</th></tr></thead>
<tbody><tr><td><code>defineRoom(handlers)</code></td><td><code>(RoomHandlers) =&gt; RoomDefinition</code></td><td>Define a room. Export the result as <code>default</code>.</td></tr><tr><td><code>isRoomDefinition(value)</code></td><td><code>(unknown) =&gt; boolean</code></td><td>Type guard for a room definition.</td></tr><tr><td><code>createRealtimeRegistry()</code></td><td><code>() =&gt; RealtimeRegistry</code></td><td>Server-side connection manager mapping sockets ↔ rooms.</td></tr><tr><td><code>bridgeRealtime(registry, bus, topic?)</code></td><td><code>(RealtimeRegistry, RealtimeBus, string?) =&gt; () =&gt; void</code></td><td>Bridge broadcasts/<code>toUser</code> sends across processes via a pub/sub bus.</td></tr></tbody></table></div>
<p><code>RoomHandlers</code>: <code>authorize(info) =&gt; boolean</code> (gate before accept — return <code>false</code> to reject with 403), <code>onConnect(client)</code>, <code>onMessage(client, message)</code> (JSON auto-parsed), <code>onLeave(client)</code>. A handler receives a <code>RoomClient</code> with <code>id</code>, <code>user</code>, <code>query</code>, <code>data</code>, <code>room</code>, and <code>send</code> / <code>broadcast</code> / <code>to(id)</code> / <code>toUser(user)</code> / <code>close</code>. The <code>Room</code> API adds <code>state</code>, <code>clients()</code>, <code>count()</code>, and <code>broadcast</code>. <code>RealtimeBus</code> is structurally satisfied by <code>@wrnexus/pubsub</code>. Legacy <code>RealtimeHandler</code>/<code>RealtimeSocket</code> raw handlers are still exported. Connection-targeted sends (<code>send</code>, <code>to(id)</code>) stay local; room broadcasts and <code>toUser</code> cross the bridge.</p>
<h4 id="error-pages-wrnexus-core">Error pages — <code>@wrnexus/core</code></h4>
<div class="table-wrap"><table>
<thead><tr><th>Export</th><th>Signature</th><th>Notes</th></tr></thead>
<tbody><tr><td><code>renderError(err, mode)</code></td><td><code>(unknown, Mode) =&gt; Response</code></td><td>Dev page (with stack) or generic prod page by <code>mode</code>.</td></tr><tr><td><code>renderDevError(err, status?)</code></td><td><code>(unknown, number?) =&gt; Response</code></td><td>Readable HTML error page including the stack trace.</td></tr><tr><td><code>renderProdError(status?)</code></td><td><code>(number?) =&gt; Response</code></td><td>Generic page that never leaks file paths.</td></tr><tr><td><code>renderNotFound()</code></td><td><code>() =&gt; Response</code></td><td>Simple 404 page.</td></tr></tbody></table></div>
<p><code>Mode</code> = <code>&quot;development&quot; | &quot;production&quot;</code>.</p>
<h4 id="security-headers-cors-wrnexus-core">Security headers &amp; CORS — <code>@wrnexus/core</code></h4>
<div class="table-wrap"><table>
<thead><tr><th>Export</th><th>Signature</th><th>Notes</th></tr></thead>
<tbody><tr><td><code>withSecurityHeaders(req, res, mode, security?, nonce?)</code></td><td>→ <code>Response</code></td><td>Applies CORS + CSP, HSTS, <code>X-Frame-Options</code>, <code>X-Content-Type-Options</code>, <code>Referrer-Policy</code>, <code>Permissions-Policy</code>, COOP, Trusted Types, and <code>extraHeaders</code>.</td></tr><tr><td><code>createCorsPreflightResponse(req, security?)</code></td><td>→ `Response \</td><td>null`</td><td>Builds a <code>204</code>/<code>403</code> preflight response for CORS <code>OPTIONS</code> requests.</td></tr><tr><td><code>isWebSocketOriginAllowed(req, security?)</code></td><td>→ <code>boolean</code></td><td>Guards WS upgrades against cross-site hijacking (allows same-origin, configured CORS origins, and non-browser clients).</td></tr></tbody></table></div>
<p>Config types: <code>SecurityConfig</code> (top-level), <code>CorsConfig</code>/<code>CorsOrigin</code>, <code>ContentSecurityPolicyConfig</code>/<code>CspDirectiveValue</code>, <code>HstsConfig</code>, <code>TrustedTypesConfig</code>, <code>PermissionsPolicyConfig</code>. WRNexusJS applies sensible defaults (self-only CSP, <code>frame-ancestors 'none'</code>, restrictive Permissions-Policy, HSTS in production, Trusted Types in production); each is individually overridable or disable-able via <code>false</code>.</p>
<h4 id="storage-cookies-sessions-localstorage-wrnexus-core">Storage: cookies, sessions, localStorage — <code>@wrnexus/core</code></h4>
<p>These back the <code>ctx.cookies</code>, <code>ctx.session</code>, and <code>ctx.localStorage</code> fields.</p>
<div class="table-wrap"><table>
<thead><tr><th>Export</th><th>Kind</th><th>Notes</th></tr></thead>
<tbody><tr><td><code>setSessionBackend(backend)</code></td><td>fn</td><td>Swap the <strong>sync</strong> session persistence backend (<code>SessionBackend</code>) — e.g. <code>bun:sqlite</code>. Default is process-local memory. Call once at startup.</td></tr><tr><td><code>loadSession(backend, options?)</code></td><td>fn → <code>Middleware</code></td><td>Back <code>ctx.session</code> with an <strong>async</strong> store (<code>AsyncSessionBackend</code>: <code>load</code>/<code>save</code>/<code>destroy</code>) — loads before the request, saves after. <code>options.ttlMs</code> default 24h.</td></tr><tr><td><code>CookieStore</code></td><td>type</td><td><code>get</code>/<code>getAll</code>/<code>has</code>/<code>set(name, value, opts?)</code>/<code>delete</code>/<code>headers</code>.</td></tr><tr><td><code>SessionStore</code></td><td>type</td><td><code>id</code>/<code>get</code>/<code>getAll</code>/<code>set</code>/<code>delete</code>/<code>regenerate</code>/<code>clear</code>.</td></tr><tr><td><code>LocalStorageSnapshot</code></td><td>type</td><td>Read-only view of the browser's localStorage sent via header for CSR bindings.</td></tr><tr><td><code>CookieOptions</code></td><td>type</td><td><code>path</code>, <code>domain</code>, <code>maxAge</code>, <code>expires</code>, <code>httpOnly</code>, <code>secure</code>, <code>sameSite</code>.</td></tr><tr><td><code>SessionEntry</code> / <code>SessionBackend</code> / <code>AsyncSessionBackend</code></td><td>types</td><td>Session persistence contracts.</td></tr></tbody></table></div>
<h4 id="low-level-security-helpers-wrnexus-core">Low-level security helpers — <code>@wrnexus/core</code></h4>
<div class="table-wrap"><table>
<thead><tr><th>Export</th><th>Signature</th><th>Notes</th></tr></thead>
<tbody><tr><td><code>escapeHtml(value)</code></td><td><code>(string) =&gt; string</code></td><td>Escape for HTML text/attributes.</td></tr><tr><td><code>isSafeIslandName(name)</code></td><td><code>(string) =&gt; boolean</code></td><td>Allow only a conservative <code>[A-Za-z0-9_-]+</code> charset.</td></tr><tr><td><code>isSafeRequestPath(pathname)</code></td><td><code>(string) =&gt; boolean</code></td><td>Reject NULs, <code>..</code> traversal, and backslashes.</td></tr></tbody></table></div>
<h4 id="jsx-runtime-wrnexus-core-wrnexus-core-jsx-runtime-wrnexus-core-jsx-dev-runtime">JSX runtime — <code>@wrnexus/core</code>, <code>@wrnexus/core/jsx-runtime</code>, <code>@wrnexus/core/jsx-dev-runtime</code></h4>
<p>A server-side JSX runtime that renders to HTML <strong>strings</strong> (no virtual DOM). Point <code>tsconfig</code>'s <code>jsxImportSource</code> at <code>@wrnexus/core</code>.</p>
<div class="table-wrap"><table>
<thead><tr><th>Export</th><th>Kind</th><th>Notes</th></tr></thead>
<tbody><tr><td><code>jsx</code> / <code>jsxs</code></td><td>fn</td><td>The runtime factory (TypeScript calls these automatically). Returns an <code>Html</code> instance.</td></tr><tr><td><code>Fragment</code></td><td>symbol</td><td>JSX fragment marker.</td></tr><tr><td><code>Html</code></td><td>class</td><td>Wraps a raw, already-safe HTML string (<code>toString()</code> returns it).</td></tr><tr><td><code>mustache(expr)</code></td><td>fn</td><td>Emit a <code>&#123;&#123;expr&#125;&#125;</code> placeholder (tagged-template or string form) for the client binder.</td></tr><tr><td><code>JSXComponent</code> / <code>JSXProps</code> / <code>Renderable</code></td><td>types</td><td>Component signature and renderable value types.</td></tr></tbody></table></div>
<p>Values interpolated as children are HTML-escaped unless they are an <code>Html</code> instance; use <code>dangerouslySetInnerHTML=&#123;&#123; __html &#125;&#125;</code> for trusted markup. Void elements render without a closing tag; <code>className</code>→<code>class</code>, <code>htmlFor</code>→<code>for</code>, and <code>style</code> objects are serialized to CSS text.</p>
<p>The subpath exports map to the runtime TypeScript's JSX transform expects:</p>
<pre data-language="jsonc"><code>// tsconfig.json
&#123;
  &quot;compilerOptions&quot;: &#123;
    &quot;jsx&quot;: &quot;react-jsx&quot;,
    &quot;jsxImportSource&quot;: &quot;@wrnexus/core&quot;,
  &#125;,
&#125;</code></pre>
<h3 id="usage">Usage</h3>
<h4 id="a-minimal-middleware-chain">A minimal middleware chain</h4>
<pre data-language="ts"><code>import &#123;
  createContext,
  withContextHeaders,
  sessionAuth,
  requireAuth,
  requestLogger,
  rateLimit,
  csrfProtection,
  type Middleware,
&#125; from &quot;@wrnexus/core&quot;;

const chain: Middleware[] = [
  requestLogger(&#123; format: &quot;json&quot; &#125;),
  rateLimit(&#123; max: 100, windowMs: 60_000 &#125;),
  csrfProtection(),
  sessionAuth(),
  requireAuth(&#123; loginPath: &quot;/login&quot; &#125;),
];</code></pre>
<h4 id="password-auth">Password auth</h4>
<pre data-language="ts"><code>import &#123; hashPassword, verifyPassword, logIn, getUser &#125; from &quot;@wrnexus/core&quot;;

// Registration
const passwordHash = await hashPassword(form.password);

// Login
if (await verifyPassword(form.password, user.passwordHash)) &#123;
  logIn(ctx, &#123; id: user.id, email: user.email &#125;);
&#125;

const current = getUser&lt;&#123; id: string &#125;&gt;(ctx); // or null</code></pre>
<h4 id="http-caching-with-etags">HTTP caching with ETags</h4>
<pre data-language="ts"><code>import &#123; etag, notModified, withCacheControl &#125; from &quot;@wrnexus/core&quot;;

const body = JSON.stringify(data);
const tag = etag(body);
if (notModified(ctx.req, tag)) &#123;
  return new Response(null, &#123; status: 304, headers: &#123; ETag: tag &#125; &#125;);
&#125;
const res = new Response(body, &#123; headers: &#123; ETag: tag, &quot;content-type&quot;: &quot;application/json&quot; &#125; &#125;);
return withCacheControl(res, &#123; maxAge: 60, staleWhileRevalidate: 300 &#125;);</code></pre>
<h4 id="streaming-sse">Streaming SSE</h4>
<pre data-language="ts"><code>import &#123; sse &#125; from &quot;@wrnexus/core&quot;;

async function* ticks() &#123;
  for (let n = 0; ; n++) &#123;
    yield &#123; event: &quot;tick&quot;, data: String(n) &#125;;
    await Bun.sleep(1000);
  &#125;
&#125;
export default (ctx) =&gt; sse(ticks());</code></pre>
<h4 id="a-realtime-room">A realtime room</h4>
<pre data-language="ts"><code>// app/realtime/chat.ts
import &#123; defineRoom &#125; from &quot;@wrnexus/core&quot;;

export default defineRoom(&#123;
  authorize: (info) =&gt; !!info.user, // require auth
  onConnect(client) &#123;
    client.user = client.query.user;
    client.room.broadcast(&#123; type: &quot;join&quot;, id: client.id &#125;);
  &#125;,
  onMessage(client, msg) &#123;
    client.broadcast(&#123; type: &quot;say&quot;, from: client.id, text: msg.text &#125;);
  &#125;,
&#125;);</code></pre>
<p>Scale it across processes:</p>
<pre data-language="ts"><code>import &#123; createRealtimeRegistry, bridgeRealtime &#125; from &quot;@wrnexus/core&quot;;
import &#123; createPubSub &#125; from &quot;@wrnexus/pubsub&quot;;
import &#123; redisDriver &#125; from &quot;@wrnexus/pubsub/redis&quot;;

const registry = createRealtimeRegistry();
bridgeRealtime(registry, createPubSub(redisDriver(process.env.REDIS_URL)));</code></pre>
<h4 id="jsx-rendering">JSX rendering</h4>
<pre data-language="tsx"><code>import &#123; Html &#125; from &quot;@wrnexus/core&quot;;

function Card(&#123; title, body &#125;: &#123; title: string; body: string &#125;) &#123;
  return (
    &lt;article class=&quot;card&quot;&gt;
      &lt;h2&gt;&#123;title&#125;&lt;/h2&gt;
      &lt;p&gt;&#123;body&#125;&lt;/p&gt;
    &lt;/article&gt;
  );
&#125;

const html: Html = &lt;Card title=&quot;Hi&quot; body=&quot;&lt;b&gt;escaped&lt;/b&gt; automatically&quot; /&gt;;
return new Response(html.toString(), &#123; headers: &#123; &quot;content-type&quot;: &quot;text/html&quot; &#125; &#125;);</code></pre>
<h3 id="requirements-notes">Requirements / Notes</h3>
<ul>
<li><strong>Bun-only.</strong> Uses <code>Bun.password</code> (argon2id), <code>Bun.write</code>, web-standard</li>
<p><code>Request</code>/<code>Response</code>/<code>FormData</code>/<code>ReadableStream</code>, and the global <code>crypto</code>. Node is not supported.</p>
<li>Session and rate-limit backends default to <strong>process-local memory</strong>. For</li>
<p>multi-instance deployments, swap in a shared backend: <code>setSessionBackend</code> (sync, e.g. <code>bun:sqlite</code>) or <code>loadSession</code> (async, e.g. Redis) for sessions, a custom <code>RateLimitStore</code> for limits, and <code>bridgeRealtime</code> for realtime.</p>
<li>Works with the rest of the framework: realtime bridging is structurally</li>
<p>compatible with [<code>@wrnexus/pubsub</code>](../pubsub); the security, auth, and JSX primitives here are consumed by the WRNexusJS server/router packages.</p>
<li>Subpath exports: <code>@wrnexus/core/jsx-runtime</code> and <code>@wrnexus/core/jsx-dev-runtime</code></li>
<p>for TypeScript's automatic JSX transform.</p>
</ul></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>export &#123; Fragment, Html, Component as JSXComponent, Props as JSXProps, Renderable, jsx, jsxs, mustache &#125; from './jsx-runtime.js';

interface Tenant &#123;
    id: string;
    slug?: string;
    name?: string;
    metadata?: Record&lt;string, unknown&gt;;
&#125;
type TenantResolver = (ctx: Context) =&gt; Tenant | null | Promise&lt;Tenant | null&gt;;
interface TenantMiddlewareOptions &#123;
    required?: boolean;
    status?: number;
&#125;
declare function tenantMiddleware(resolveTenant: TenantResolver, options?: TenantMiddlewareOptions): Middleware;
declare function tenantFromSubdomain(lookup: (slug: string, ctx: Context) =&gt; Tenant | null | Promise&lt;Tenant | null&gt;, rootDomains?: string[]): TenantResolver;
declare function requireTenant(ctx: Context): Tenant;
/** Wrap a repository so every operation receives the current tenant id. */
declare function tenantScope&lt;T extends object&gt;(tenant: Tenant, repository: T): T &amp; &#123;
    tenantId: string;
&#125;;

interface SpanRecord &#123;
    name: string;
    startTime: number;
    endTime?: number;
    durationMs?: number;
    status?: &quot;ok&quot; | &quot;error&quot;;
    attributes: Record&lt;string, string | number | boolean&gt;;
    error?: unknown;
&#125;
interface Tracer &#123;
    startSpan(name: string, attributes?: SpanRecord[&quot;attributes&quot;]): Span;
    records(): readonly SpanRecord[];
&#125;
interface Span &#123;
    setAttribute(name: string, value: string | number | boolean): void;
    end(status?: &quot;ok&quot; | &quot;error&quot;, error?: unknown): SpanRecord;
&#125;
declare function createTracer(clock?: () =&gt; number): Tracer;
declare function withSpan&lt;T&gt;(tracer: Tracer, name: string, run: (span: Span) =&gt; T | Promise&lt;T&gt;, attributes?: SpanRecord[&quot;attributes&quot;]): Promise&lt;T&gt;;
interface TracingMiddlewareOptions &#123;
    /** Include W3C Server-Timing response headers. Defaults to true. */
    serverTiming?: boolean;
    /** Fraction of requests to trace, from 0 to 1. Defaults to 1. */
    sampleRate?: number;
    /** Called after a traced response completes. */
    onComplete?: (ctx: Context, records: readonly SpanRecord[]) =&gt; void | Promise&lt;void&gt;;
&#125;
declare function tracingMiddleware(tracerFactory?: (ctx: Context) =&gt; Tracer, options?: TracingMiddlewareOptions): Middleware;

interface CookieOptions &#123;
    path?: string;
    domain?: string;
    maxAge?: number;
    expires?: Date | string;
    httpOnly?: boolean;
    secure?: boolean;
    sameSite?: &quot;Strict&quot; | &quot;Lax&quot; | &quot;None&quot; | &quot;strict&quot; | &quot;lax&quot; | &quot;none&quot;;
&#125;
interface CookieStore &#123;
    get(name: string): string | undefined;
    getAll(): Record&lt;string, string&gt;;
    has(name: string): boolean;
    set(name: string, value: string, options?: CookieOptions): void;
    delete(name: string, options?: CookieOptions): void;
    headers(): string[];
&#125;
interface SessionStore &#123;
    id(): string;
    get&lt;T = unknown&gt;(key: string): T | undefined;
    getAll(): Record&lt;string, unknown&gt;;
    set(key: string, value: unknown): void;
    delete(key: string): void;
    /** Issue a fresh session id, keeping the data — defends against fixation. */
    regenerate(): void;
    clear(): void;
&#125;
interface LocalStorageSnapshot &#123;
    get(key: string): string | undefined;
    getAll(): Record&lt;string, string&gt;;
    has(key: string): boolean;
&#125;
/** A stored session: its data plus an absolute expiry timestamp (ms). */
interface SessionEntry &#123;
    data: Record&lt;string, unknown&gt;;
    expiresAt: number;
&#125;
/**
 * Pluggable session persistence. The default is process-local memory; swap in a
 * shared backend (Redis, SQL, etc.) via `setSessionBackend` so sessions survive
 * restarts and work across multiple instances. Methods are synchronous, so a
 * backend must be sync (e.g. `bun:sqlite`); async stores need a load/save
 * wrapper around the request (future work).
 */
interface SessionBackend &#123;
    get(id: string): SessionEntry | undefined;
    set(id: string, entry: SessionEntry): void;
    delete(id: string): void;
    /** Optional: drop expired entries. Called periodically by the store. */
    gc?(now: number): void;
&#125;
/** Replace the session persistence backend (call once at startup). */
declare function setSessionBackend(backend: SessionBackend): void;
/**
 * An ASYNC session store (Redis, a remote DB). Use it via the `loadSession`
 * middleware, which loads the session before the request and saves it after —
 * keeping the `ctx.session` API synchronous while persistence is shared across
 * instances.
 */
interface AsyncSessionBackend &#123;
    load(id: string): Promise&lt;SessionEntry | undefined&gt;;
    save(id: string, entry: SessionEntry): Promise&lt;void&gt;;
    destroy(id: string): Promise&lt;void&gt;;
&#125;
/**
 * Back `ctx.session` with an async store. Register early (before anything reads
 * `ctx.session`). Loads once at the start of the request and saves once at the
 * end; regenerate/clear destroy the old id.
 */
declare function loadSession(backend: AsyncSessionBackend, options?: &#123;
    ttlMs?: number;
&#125;): Middleware;

/**
 * Core request context and middleware contracts.
 *
 * The `Context` object is the single value that flows through middleware,
 * pages and API routes. It is intentionally small and framework-agnostic so
 * it can later be reused by the `.wrn` compiler output.
 */

/** Translate a key for the active language, interpolating `&#123;param&#125;` placeholders. */
type TFunction = (key: string, params?: Record&lt;string, string | number&gt;) =&gt; string;
type Context = &#123;
    /** The raw incoming web-standard Request. */
    req: Request;
    /** Parsed URL of the request (pathname, query, etc.). */
    url: URL;
    /** Active language for this request (resolved by the runtime); &quot;&quot; if i18n is unused. */
    lang: string;
    /** Translate a key for the active language (identity until the runtime sets it). */
    t: TFunction;
    /** Dynamic route params, e.g. `/users/[id]` -&gt; `&#123; id: &quot;42&quot; &#125;`. */
    params: Record&lt;string, string&gt;;
    /**
     * Per-request scratch space. Middleware can attach values here
     * (e.g. the authenticated user) and downstream handlers can read them.
     */
    locals: Record&lt;string, unknown&gt;;
    /**
     * The authenticated user for this request, or null when anonymous. Populated
     * by the `sessionAuth` middleware (or `logIn`); read via `getUser(ctx)`.
     */
    user?: unknown;
    /** Active tenant/workspace resolved by tenant middleware. */
    tenant?: Tenant;
    /** Request tracer installed by observability middleware. */
    tracer?: Tracer;
    /**
     * The direct socket peer IP, set by the server from `server.requestIP`. This
     * is NOT spoofable by request headers — prefer it over `x-forwarded-for` for
     * rate limiting unless you run behind a trusted proxy.
     */
    ip?: string;
    /** Read/write HTTP cookies for the current response. */
    cookies: CookieStore;
    /** In-memory cookie-backed session store. */
    session: SessionStore;
    /** Read-only localStorage snapshot sent by the browser for CSR data bindings. */
    localStorage: LocalStorageSnapshot;
&#125;;
/** Calls the next middleware in the chain (or the final route handler). */
type Next = () =&gt; Promise&lt;Response&gt; | Response;
/**
 * Middleware runs before pages and API routes. It can:
 *  - inspect/modify `ctx`
 *  - short-circuit by returning a `Response` without calling `next()`
 *  - continue by returning `await next()`
 */
type Middleware = (ctx: Context, next: Next) =&gt; Promise&lt;Response&gt; | Response;
/** SEO metadata rendered into the document `&lt;head&gt;`. */
type SeoConfig = &#123;
    /** BCP 47 document language used on `&lt;html lang&gt;` (default: `en`). */
    lang?: string;
    title?: string;
    titleTemplate?: string;
    description?: string;
    canonical?: string;
    canonicalBase?: string;
    robots?: string;
    keywords?: string | string[];
    image?: string;
    siteName?: string;
    type?: string;
    locale?: string;
    twitterCard?: string;
    twitterSite?: string;
    themeColor?: string;
&#125;;
/** Page metadata rendered into the document `&lt;head&gt;`. */
type PageMeta = SeoConfig;
/** A page module's default export. Returns an HTML string for the body. */
type PageComponent = (ctx: Context) =&gt; string | Promise&lt;string&gt;;
/** Create a fresh context for an incoming request. */
declare function createContext(req: Request, url: URL): Context;
/** Apply headers accumulated on the context, such as Set-Cookie. */
declare function withContextHeaders(ctx: Context, res: Response): Response;

/**
 * Small, dependency-free security helpers shared across packages.
 */
/**
 * Escape a string for safe interpolation into HTML text or attributes.
 * Used for page metadata (title/description) so untrusted values can't
 * break out of an attribute or inject markup.
 */
declare function escapeHtml(value: string): string;
declare function isSafeIslandName(name: string): boolean;
/**
 * Reject obvious path-traversal in a request path before it is ever used to
 * resolve a file. The router never builds file paths from request input
 * (routes are resolved against a pre-scanned table), but this is a cheap
 * defense-in-depth guard.
 */
declare function isSafeRequestPath(pathname: string): boolean;

/**
 * CSRF protection via the double-submit cookie pattern.
 *
 * The framework sets a readable `wire-csrf` cookie on page loads; the client
 * echoes it in an `x-csrf-token` header on unsafe requests (the Wire UI form
 * runtime does this automatically). The server checks header === cookie. A
 * cross-site attacker can't read the cookie to forge the header, so the request
 * is rejected — while same-origin requests pass.
 */

declare const CSRF_COOKIE = &quot;wire-csrf&quot;;
declare const CSRF_HEADER = &quot;x-csrf-token&quot;;
/** Ensure the CSRF cookie exists (readable by JS) and return its token. */
declare function csrfToken(ctx: Context): string;
/**
 * Verify an unsafe request's CSRF token against the cookie. Safe methods
 * (GET/HEAD/OPTIONS) always pass. The token may arrive in the `x-csrf-token`
 * header or a `_csrf` field already parsed onto `ctx.locals`.
 */
declare function verifyCsrf(ctx: Context): boolean;
/** Middleware that 403s unsafe requests with a missing/mismatched CSRF token. */
declare function csrfProtection(): Middleware;

/**
 * Authentication primitives.
 *
 * Passwords are hashed with argon2id via `Bun.password`. Sessions ride on the
 * existing cookie-backed `SessionStore`: logging a user in stores a serializable
 * user object under the &quot;user&quot; key, and `sessionAuth` hydrates `ctx.user` from
 * it on every request. `requireAuth` is a guard middleware for protected routes.
 */

/** Session key under which the authenticated user is stored. */
declare const SESSION_USER_KEY = &quot;user&quot;;
/** Hash a plaintext password (argon2id). Store the returned string. */
declare function hashPassword(password: string): Promise&lt;string&gt;;
/** Verify a plaintext password against a stored hash. Safe against bad hashes. */
declare function verifyPassword(password: string, hash: string): Promise&lt;boolean&gt;;
/** Persist the authenticated user in the session and on the context. */
declare function logIn&lt;U = unknown&gt;(ctx: Context, user: U): void;
/** Clear the session and forget the current user. */
declare function logOut(ctx: Context): void;
/**
 * The currently-authenticated user, or null. Reads `ctx.user` first (set by
 * `sessionAuth`/`logIn`), falling back to the session store.
 */
declare function getUser&lt;U = unknown&gt;(ctx: Context): U | null;
/**
 * Hydrate `ctx.user` from the session for every request. Register this early in
 * the middleware chain so downstream pages and API routes can read `ctx.user`.
 */
declare function sessionAuth(): Middleware;
interface RequireAuthOptions &#123;
    /** Where to redirect unauthenticated page requests. Default &quot;/login&quot;. */
    loginPath?: string;
&#125;
/**
 * Guard that requires an authenticated user. Unauthenticated requests that look
 * like an API/fetch call get a 401 JSON response; page navigations get a 302
 * redirect to the login page with the original target preserved as `?next=`.
 */
declare function requireAuth(options?: RequireAuthOptions): Middleware;

/**
 * Fixed-window rate limiting middleware. Keeps an in-memory counter per key
 * (client IP by default, read from `x-forwarded-for` / `x-real-ip`) and rejects
 * requests over the limit with a 429 and a `Retry-After` header. Sets the
 * `RateLimit-Limit` / `RateLimit-Remaining` / `RateLimit-Reset` headers.
 *
 * The store is process-local; behind multiple instances use a shared store
 * (out of scope here). Suitable as-is for single-process apps and dev.
 */

interface RateLimitOptions &#123;
    /** Window length in milliseconds. Default 60_000 (1 minute). */
    windowMs?: number;
    /** Max requests allowed per key per window. Default 60. */
    max?: number;
    /** Derive the bucket key from the request. Default: client IP. */
    key?: (ctx: Context) =&gt; string;
    /**
     * Trust `x-forwarded-for` / `x-real-ip` for the client IP. Default false —
     * those headers are attacker-spoofable, so by default we key on the direct
     * socket peer (`ctx.ip`). Enable ONLY when behind a proxy that overwrites
     * these headers (nginx, a load balancer, Cloudflare).
     */
    trustProxy?: boolean;
    /** Body returned on 429. Default &quot;Too Many Requests&quot;. */
    message?: string;
    /** Emit RateLimit-* headers. Default true. */
    headers?: boolean;
    /** Persistence for the counters. Default: process-local memory. */
    store?: RateLimitStore;
    /** Maximum in-memory keys before oldest buckets are evicted. Ignored for custom stores. */
    maxKeys?: number;
&#125;
interface Bucket &#123;
    count: number;
    resetAt: number;
&#125;
/**
 * Pluggable rate-limit counter store. The default is process-local memory; swap
 * in a shared store (Redis/SQL) so limits hold across instances. `hit` records
 * one request for `key` in the current window and returns the running bucket.
 * It may be async (e.g. a Redis INCR + PEXPIRE) — the middleware awaits it.
 */
interface RateLimitStore &#123;
    hit(key: string, windowMs: number, now: number): Bucket | Promise&lt;Bucket&gt;;
&#125;
declare function rateLimit(options?: RateLimitOptions): Middleware;
/** Non-spoofable key: the direct socket peer IP (set by the server). */
declare function peerKey(ctx: Context): string;
/** Proxy-aware key: trusts `x-forwarded-for` / `x-real-ip`, else the peer IP. */
declare function proxyKey(ctx: Context): string;
/** @deprecated Use `peerKey` (default) or `proxyKey`. Kept for compatibility. */
declare const defaultKey: typeof proxyKey;

/**
 * Structured request logging middleware. Emits one record per request with a
 * request id, method, path, status, and duration — as pretty text (dev) or JSON
 * (production/log aggregation). The request id is stored on `ctx.locals` so
 * downstream handlers can correlate their own logs.
 */

interface RequestRecord &#123;
    time: string;
    id: string;
    method: string;
    path: string;
    status: number;
    durationMs: number;
&#125;
interface RequestLoggerOptions &#123;
    /** &quot;pretty&quot; (default) for humans, &quot;json&quot; for machines. */
    format?: &quot;pretty&quot; | &quot;json&quot;;
    /** Where each finished record goes. Default console.log. */
    sink?: (line: string, record: RequestRecord) =&gt; void;
    /** ctx.locals key for the request id. Default &quot;requestId&quot;. */
    requestIdKey?: string;
    /** Clock injection for tests. Default Date.now. */
    now?: () =&gt; number;
&#125;
declare function requestLogger(options?: RequestLoggerOptions): Middleware;

/**
 * Caching primitives:
 *  - `TTLCache` — a small in-memory time-to-live cache with `getOrLoad`, for
 *    memoising expensive data (query results, computed pages).
 *  - HTTP helpers — `cacheControl` to build a directive, `withCacheControl` to
 *    apply it, and `etag` / `notModified` for conditional requests (304s).
 */
declare class TTLCache&lt;V = unknown&gt; &#123;
    private readonly ttlMs;
    private store;
    private loading;
    private revisions;
    private generation;
    constructor(ttlMs?: number);
    get(key: string): V | undefined;
    set(key: string, value: V, ttlMs?: number): void;
    /** Return the cached value or compute, cache, and return it. */
    getOrLoad(key: string, loader: () =&gt; Promise&lt;V&gt; | V, ttlMs?: number): Promise&lt;V&gt;;
    delete(key: string): void;
    clear(): void;
    get size(): number;
&#125;
interface CacheControlOptions &#123;
    /** max-age in seconds. */
    maxAge?: number;
    /** s-maxage (shared/CDN cache) in seconds. */
    sMaxAge?: number;
    /** Mark private (per-user) rather than public. */
    private?: boolean;
    /** no-store: never cache. Overrides other directives. */
    noStore?: boolean;
    /** no-cache: revalidate before use. */
    noCache?: boolean;
    /** stale-while-revalidate window in seconds. */
    staleWhileRevalidate?: number;
    immutable?: boolean;
&#125;
/** Build a Cache-Control header value from options. */
declare function cacheControl(options: CacheControlOptions): string;
/** Apply a Cache-Control header to a response (returns the same response). */
declare function withCacheControl(res: Response, options: CacheControlOptions): Response;
/** A stable, quoted ETag for a string/bytes body (FNV-1a, weak by default). */
declare function etag(body: string | ArrayBuffer | Uint8Array, weak?: boolean): string;
/** True when the request's If-None-Match matches the given ETag (send a 304). */
declare function notModified(req: Request, tag: string): boolean;

/**
 * File upload helpers. Bun parses `multipart/form-data` natively via
 * `Request.formData()`, yielding web `File` objects; these helpers validate and
 * persist them safely (size/type limits, filename sanitisation to prevent path
 * traversal).
 */
declare class UploadError extends Error &#123;
    constructor(message: string);
&#125;
interface SaveUploadOptions &#123;
    /** Destination directory. */
    dir: string;
    /** Reject files larger than this many bytes. */
    maxBytes?: number;
    /** Allowed MIME types (e.g. &quot;image/png&quot;) and/or extensions (e.g. &quot;.png&quot;). */
    allowedTypes?: string[];
    /** Choose the stored filename. Default: the sanitised original name. */
    filename?: (file: File) =&gt; string;
&#125;
interface SavedUpload &#123;
    path: string;
    filename: string;
    size: number;
    type: string;
&#125;
/** All `File` values in a parsed form, with their field names. */
declare function collectUploads(form: FormData): &#123;
    field: string;
    file: File;
&#125;[];
/** Validate and write one uploaded file to disk. Throws `UploadError` on reject. */
declare function saveUpload(file: File, options: SaveUploadOptions): Promise&lt;SavedUpload&gt;;
/** Strip directory separators, traversal, and control chars from a filename. */
declare function sanitizeFilename(name: string): string;

/**
 * Streaming response primitives.
 *
 * `streamResponse` turns a (sync or async) iterable of strings/bytes into a
 * streaming `Response` — the basis for streaming SSR (send the shell, then flush
 * page chunks as they render) and any progressively-generated output. `sse`
 * builds a Server-Sent Events stream from an async iterable of events.
 *
 * API routes and pages can already return a `Response` with a `ReadableStream`
 * body and the framework streams it unbuffered; these helpers just make the
 * common cases ergonomic.
 */
interface StreamResponseInit &#123;
    status?: number;
    headers?: HeadersInit;
    /** Content-Type; default &quot;text/html; charset=utf-8&quot;. */
    contentType?: string;
&#125;
type Chunk = string | Uint8Array;
type ChunkSource = Iterable&lt;Chunk&gt; | AsyncIterable&lt;Chunk&gt;;
/** Build a streaming Response from an (async) iterable of chunks. */
declare function streamResponse(source: ChunkSource, init?: StreamResponseInit): Response;
interface ServerSentEvent &#123;
    data: string;
    event?: string;
    id?: string;
    /** Client reconnection hint in milliseconds. */
    retry?: number;
&#125;
/** Build a Server-Sent Events (text/event-stream) Response from events. */
declare function sse(source: Iterable&lt;ServerSentEvent&gt; | AsyncIterable&lt;ServerSentEvent&gt;): Response;

/**
 * Realtime rooms.
 *
 * A file in `app/realtime/` exports `default defineRoom(&#123; onConnect, onMessage,
 * onLeave &#125;)` and is served at `ws://host/realtime/&lt;name&gt;`. The framework's
 * client runtime (`/__wrnexus/realtime.js`) handles the browser side, so pages
 * ship NO hand-written WebSocket code.
 *
 * Handlers get a `RoomClient` with everything you need:
 *   client.send(msg)                 → this connection
 *   client.broadcast(msg)            → everyone else in the room
 *   client.room.broadcast(msg)       → everyone (incl. sender)
 *   client.to(id | ids).send(msg)    → specific connection(s)
 *   client.toUser(u | users).send()  → a user / selected users (all their tabs)
 *   client.user = &quot;u1&quot;               → identify a connection for targeting
 *   client.data / client.room.state  → per-connection / shared room state
 *
 * The dynamic route `app/realtime/[room].ts` gives one handler many independent
 * rooms — `/realtime/lobby` and `/realtime/game-7` are separate room instances.
 */
interface RawSocket &#123;
    send(data: string): unknown;
    close(code?: number, reason?: string): void;
&#125;
interface RealtimeSocket&lt;Data = unknown&gt; &#123;
    readonly data: Data;
    send(data: string | Uint8Array): number;
    subscribe(topic: string): void;
    unsubscribe(topic: string): void;
    publish(topic: string, data: string | Uint8Array): number;
    isSubscribed(topic: string): boolean;
    close(code?: number, reason?: string): void;
&#125;
interface RealtimeHandler&lt;Data = unknown&gt; &#123;
    open?(ws: RealtimeSocket&lt;Data&gt;): void | Promise&lt;void&gt;;
    message?(ws: RealtimeSocket&lt;Data&gt;, message: string | Uint8Array): void | Promise&lt;void&gt;;
    close?(ws: RealtimeSocket&lt;Data&gt;, code?: number, reason?: string): void | Promise&lt;void&gt;;
    drain?(ws: RealtimeSocket&lt;Data&gt;): void | Promise&lt;void&gt;;
&#125;
interface Target &#123;
    /** Send a message (objects are JSON-serialized). */
    send(message: unknown): void;
&#125;
interface Room&lt;TData = Record&lt;string, unknown&gt;&gt; &#123;
    readonly name: string;
    /** Shared, in-memory room state (lives while ≥1 client is connected). */
    readonly state: Record&lt;string, unknown&gt;;
    /** All connected clients. */
    clients(): RoomClient&lt;TData&gt;[];
    /** Number of connected clients. */
    count(): number;
    /** Send to everyone in the room, including the sender. */
    broadcast(message: unknown): void;
    /** Target specific connection id(s). */
    to(id: string | string[]): Target;
    /** Target a user / users by identity (reaches all their connections). */
    toUser(user: string | string[]): Target;
&#125;
interface RoomClient&lt;TData = Record&lt;string, unknown&gt;&gt; &#123;
    /** Unique per connection (a tab). */
    readonly id: string;
    /** App identity for targeting; assign it in `onConnect`. */
    user: string | undefined;
    /** Query params from the connection URL. */
    readonly query: Record&lt;string, string&gt;;
    /** Per-connection scratch state. */
    readonly data: TData;
    readonly room: Room&lt;TData&gt;;
    /** Send to THIS connection. */
    send(message: unknown): void;
    /** Send to everyone else in the room. */
    broadcast(message: unknown): void;
    /** Target specific connection id(s). */
    to(id: string | string[]): Target;
    /** Target a user / users by identity. */
    toUser(user: string | string[]): Target;
    /** Close this connection. */
    close(code?: number, reason?: string): void;
&#125;
/** Info available when authorizing a connection, before it is accepted. */
interface RoomAuthInfo &#123;
    /** Authenticated session user id, or `?user=` — undefined when anonymous. */
    user?: string;
    /** Connection URL query params. */
    query: Record&lt;string, string&gt;;
    /** The upgrade request's headers (cookies, etc.). */
    headers: Headers;
&#125;
interface RoomHandlers&lt;TData = Record&lt;string, unknown&gt;&gt; &#123;
    /**
     * Gate the connection BEFORE it is accepted. Return false to reject the
     * upgrade with 403 (e.g. `authorize: (info) =&gt; !!info.user` to require auth).
     */
    authorize?(info: RoomAuthInfo): boolean | Promise&lt;boolean&gt;;
    /** A client connected (a new tab joined the room). */
    onConnect?(client: RoomClient&lt;TData&gt;): void | Promise&lt;void&gt;;
    /** A message arrived (JSON is parsed; non-JSON arrives as a string). */
    onMessage?(client: RoomClient&lt;TData&gt;, message: any): void | Promise&lt;void&gt;;
    /** A client disconnected. */
    onLeave?(client: RoomClient&lt;TData&gt;): void | Promise&lt;void&gt;;
&#125;
interface RoomDefinition&lt;TData = Record&lt;string, unknown&gt;&gt; &#123;
    readonly __wrnexusRoom: true;
    readonly handlers: RoomHandlers&lt;TData&gt;;
&#125;
/** Define a realtime room. Export the result as the `default` of a realtime file. */
declare function defineRoom&lt;TData = Record&lt;string, unknown&gt;&gt;(handlers: RoomHandlers&lt;TData&gt;): RoomDefinition&lt;TData&gt;;
declare function isRoomDefinition(value: unknown): value is RoomDefinition;
interface RealtimeConnectMeta &#123;
    room: string;
    def: RoomDefinition;
    query?: Record&lt;string, string&gt;;
    user?: string;
&#125;
/** One cross-instance message: a room broadcast, or a targeted user send. */
interface RealtimeEnvelope &#123;
    room: string;
    /** If set, deliver only to these user identities; otherwise the whole room. */
    users?: string[];
    message: unknown;
&#125;
/**
 * A pub/sub bridge for horizontal scaling. Wire the registry to a shared bus
 * (Redis pub/sub, NATS, …): local broadcasts/`toUser` sends are published to
 * peers, and messages received from peers are delivered via `registry.deliver`.
 * Connection-targeted sends (`send`, `to(id)`) stay local (ids are per-process).
 */
interface RealtimeBridge &#123;
    publish(envelope: RealtimeEnvelope): void;
&#125;
interface RealtimeRegistry &#123;
    open(socket: RawSocket, meta: RealtimeConnectMeta): void | Promise&lt;void&gt;;
    message(socket: RawSocket, raw: string | Uint8Array): void | Promise&lt;void&gt;;
    close(socket: RawSocket): void | Promise&lt;void&gt;;
    /** Attach a cross-instance bridge (call once at startup). */
    setBridge(bridge: RealtimeBridge): void;
    /** Deliver an envelope received from a peer to LOCAL connections only. */
    deliver(envelope: RealtimeEnvelope): void;
    /** Number of live connections (across all rooms) — for tests/metrics. */
    size(): number;
&#125;
/** Create the registry that maps sockets ↔ rooms and drives room handlers. */
declare function createRealtimeRegistry(): RealtimeRegistry;
/**
 * A minimal pub/sub bus (structurally satisfied by `@wrnexus/pubsub`). Used to
 * bridge realtime broadcasts across processes without a hard dependency.
 */
interface RealtimeBus &#123;
    publish(topic: string, message: unknown): void | Promise&lt;void&gt;;
    subscribe(topic: string, handler: (message: unknown, topic: string) =&gt; void): () =&gt; void;
&#125;
/**
 * Bridge a realtime registry across processes/instances via a pub/sub bus (use
 * the Redis driver so it crosses machines). After this, `client.room.broadcast`
 * and `client.toUser(...)` reach connected clients on **every** app process/
 * instance subscribed to the same bus — the foundation for realtime that works
 * with multiple running apps behind the gateway. Connection-targeted sends
 * (`send`, `to(id)`) stay local. Returns an unsubscribe function.
 *
 *   import &#123; createRealtimeRegistry, bridgeRealtime &#125; from &quot;@wrnexus/core&quot;;
 *   import &#123; createPubSub &#125; from &quot;@wrnexus/pubsub&quot;;
 *   import &#123; redisDriver &#125; from &quot;@wrnexus/pubsub/redis&quot;;
 *   bridgeRealtime(registry, createPubSub(redisDriver(process.env.REDIS_URL)));
 */
declare function bridgeRealtime(registry: RealtimeRegistry, bus: RealtimeBus, topic?: string): () =&gt; void;

/**
 * Error + status pages. Every page here is a self-contained HTML document —
 * inline CSS only, no external stylesheet, no JavaScript (so it renders under the
 * strict CSP, even when the app's assets are what failed). Theme-aware via
 * `prefers-color-scheme`, styled in the WRNexusJS design language (ink-navy,
 * azure, a faint blueprint grid + glow). Development shows the stack trace;
 * production never leaks internal paths.
 */
type Mode = &quot;development&quot; | &quot;production&quot;;
/** A beautiful, self-contained HTML page for any 4xx/5xx status. */
declare function renderStatusPage(status: number): Response;
/** Readable, styled development error page — includes the stack trace. */
declare function renderDevError(err: unknown, status?: number): Response;
/** Generic production error page — no stack, no file paths. */
declare function renderProdError(status?: number): Response;
/** Pick the right error page for the current mode. */
declare function renderError(err: unknown, mode: Mode): Response;
/** Beautiful 404 page. */
declare function renderNotFound(): Response;

type CorsOrigin = &quot;*&quot; | string | string[];
interface CorsConfig &#123;
    /** Enable CORS headers and preflight handling. Defaults to false. */
    enabled?: boolean;
    /** Allowed origins. Use &quot;*&quot; for public APIs. Defaults to &quot;*&quot;. */
    origin?: CorsOrigin;
    /** Allowed methods for preflight responses. */
    methods?: string[];
    /** Allowed request headers. Defaults to the browser's requested headers. */
    allowedHeaders?: string[];
    /** Response headers exposed to browser JavaScript. */
    exposedHeaders?: string[];
    /** Whether to send Access-Control-Allow-Credentials. */
    credentials?: boolean;
    /** Access-Control-Max-Age, in seconds. */
    maxAge?: number;
&#125;
type CspDirectiveValue = string | string[] | false | null | undefined;
interface ContentSecurityPolicyConfig &#123;
    /** Defaults to true. */
    enabled?: boolean;
    /** Use Content-Security-Policy-Report-Only instead of enforcing. */
    reportOnly?: boolean;
    /** Merge or remove directives. Set a directive to false/null to remove it. */
    directives?: Record&lt;string, CspDirectiveValue&gt;;
    /** Set false to start from an empty policy instead of WRNexusJS defaults. */
    useDefaults?: boolean;
&#125;
interface HstsConfig &#123;
    /** Defaults to true in production, false in development. */
    enabled?: boolean;
    /** Defaults to 31536000 seconds (1 year). */
    maxAge?: number;
    /** Defaults to true. */
    includeSubDomains?: boolean;
    /** Defaults to true. */
    preload?: boolean;
&#125;
interface TrustedTypesConfig &#123;
    /** Defaults to true in production, false in development. */
    enabled?: boolean;
    /**
     * Defaults to [&quot;*&quot;] in production so browser extensions and dev tooling can
     * create their own policies without noisy console errors. Set this to a
     * concrete list, e.g. [&quot;wrnexus&quot;, &quot;default&quot;], for stricter deployments.
     */
    policyNames?: string[];
    /** Defaults to true. */
    requireForScript?: boolean;
    /** Adds &quot;allow-duplicates&quot; to the trusted-types directive. */
    allowDuplicates?: boolean;
&#125;
type PermissionsPolicyConfig = Record&lt;string, string | string[] | false | null | undefined&gt;;
interface SecurityConfig &#123;
    /** Set false to skip all framework security headers except explicitly enabled CORS. */
    headers?: boolean;
    /**
     * Trust `X-Forwarded-Proto` / `X-Forwarded-Host` when building `ctx.url` — set
     * this when the app runs behind a TLS-terminating reverse proxy (nginx, the
     * WRNexusJS gateway, a load balancer). Without it, a proxied app sees the internal
     * `http://` request and marks cookies (e.g. CSRF/session) non-`Secure`. Default
     * false; enable ONLY when a trusted proxy actually sets these headers.
     */
    trustProxy?: boolean;
    cors?: boolean | CorsConfig;
    contentSecurityPolicy?: false | ContentSecurityPolicyConfig;
    hsts?: false | HstsConfig;
    trustedTypes?: false | TrustedTypesConfig;
    /** Defaults to &quot;same-origin&quot;. */
    crossOriginOpenerPolicy?: false | &quot;same-origin&quot; | &quot;same-origin-allow-popups&quot; | &quot;unsafe-none&quot;;
    /** Defaults to &quot;DENY&quot;. */
    frameOptions?: false | &quot;DENY&quot; | &quot;SAMEORIGIN&quot;;
    /** Defaults to &quot;strict-origin-when-cross-origin&quot;. */
    referrerPolicy?: false | string;
    /** Defaults to a restrictive browser capability policy. */
    permissionsPolicy?: false | PermissionsPolicyConfig;
    /** Extra static headers applied last. */
    extraHeaders?: Record&lt;string, string&gt;;
&#125;
/**
 * Guard a WebSocket upgrade against Cross-Site WebSocket Hijacking: browsers
 * always send an `Origin` header on a WS handshake, and — unlike fetch — WS is
 * NOT subject to CORS, so cookies would otherwise flow cross-site. We allow
 * same-origin (Origin host === Host header), configured CORS origins, and
 * non-browser clients (no Origin, which also carry no ambient cookies).
 */
declare function isWebSocketOriginAllowed(req: Request, security?: SecurityConfig): boolean;
declare function createCorsPreflightResponse(req: Request, security?: SecurityConfig): Response | null;
/**
 * Build the request URL, honoring `X-Forwarded-Proto` / `X-Forwarded-Host` when
 * `trustProxy` is set (app behind a TLS-terminating reverse proxy). This makes
 * `ctx.url.protocol` reflect the EXTERNAL scheme, so protocol-dependent logic —
 * `Secure` cookies, canonical URLs — is correct behind nginx / the gateway.
 * Security checks that compare the raw `Host`/`Origin` headers don't use this URL,
 * so they are unaffected. An invalid forwarded value is ignored by the URL setter.
 */
declare function resolveRequestUrl(req: Request, trustProxy?: boolean): URL;
declare function withSecurityHeaders(req: Request, res: Response, mode: Mode, security?: SecurityConfig, nonce?: string): Response;

interface SchemaLike&lt;T&gt; &#123;
    parse(input: unknown): T;
&#125;
interface EndpointErrorBody &#123;
    code: string;
    message: string;
    details?: unknown;
&#125;
declare class EndpointError extends Error &#123;
    readonly status: number;
    readonly code: string;
    readonly details?: unknown | undefined;
    constructor(status: number, code: string, message: string, details?: unknown | undefined);
&#125;
interface EndpointDefinition&lt;I, O&gt; &#123;
    input?: SchemaLike&lt;I&gt;;
    output?: SchemaLike&lt;O&gt;;
    auth?: &quot;optional&quot; | &quot;required&quot;;
    description?: string;
    tags?: string[];
    handler(input: I, ctx: Context): O | Promise&lt;O&gt;;
&#125;
interface DefinedEndpoint&lt;I, O&gt; &#123;
    readonly definition: EndpointDefinition&lt;I, O&gt;;
    (ctx: Context, input?: unknown): Promise&lt;Response&gt;;
&#125;
/** Define a validated, typed endpoint that can also drive SDK/OpenAPI generation. */
declare function defineEndpoint&lt;I = unknown, O = unknown&gt;(definition: EndpointDefinition&lt;I, O&gt;): DefinedEndpoint&lt;I, O&gt;;
interface RpcClientOptions &#123;
    baseUrl?: string;
    fetch?: typeof globalThis.fetch;
    headers?: HeadersInit | (() =&gt; HeadersInit | Promise&lt;HeadersInit&gt;);
&#125;
/** Create a tiny typed RPC caller for endpoints exposed by a WRNexusJS app. */
declare function createRpcClient(options?: RpcClientOptions): &lt;I, O&gt;(path: string, input: I) =&gt; Promise&lt;O&gt;;

interface CachePolicy &#123;
    ttlMs?: number;
    staleWhileRevalidateMs?: number;
    tags?: string[] | ((ctx: Context) =&gt; string[]);
&#125;
interface LoaderDefinition&lt;T&gt; &#123;
    cache?: CachePolicy;
    load(ctx: Context): T | Promise&lt;T&gt;;
&#125;
interface ActionDefinition&lt;I, O&gt; &#123;
    csrf?: boolean;
    run(input: I, ctx: Context): O | Promise&lt;O&gt;;
    invalidate?: string[] | ((output: O, ctx: Context) =&gt; string[]);
&#125;
interface DefinedLoader&lt;T&gt; &#123;
    readonly definition: LoaderDefinition&lt;T&gt;;
    (ctx: Context): Promise&lt;T&gt;;
&#125;
interface DefinedAction&lt;I, O&gt; &#123;
    readonly definition: ActionDefinition&lt;I, O&gt;;
    (input: I, ctx: Context): Promise&lt;O&gt;;
&#125;
declare function defineLoader&lt;T&gt;(definition: LoaderDefinition&lt;T&gt;): DefinedLoader&lt;T&gt;;
declare function defineAction&lt;I, O&gt;(definition: ActionDefinition&lt;I, O&gt;): DefinedAction&lt;I, O&gt;;
/** Request-local fetch deduplication keyed by a stable string. */
declare function dedupe&lt;T&gt;(ctx: Context, key: string, load: () =&gt; T | Promise&lt;T&gt;): Promise&lt;T&gt;;

type FeatureValue = boolean | string | number;
type FeatureRule = FeatureValue | ((ctx: Context) =&gt; FeatureValue | Promise&lt;FeatureValue&gt;);
interface FeatureFlags &#123;
    get(name: string, ctx: Context): Promise&lt;FeatureValue | undefined&gt;;
    enabled(name: string, ctx: Context): Promise&lt;boolean&gt;;
&#125;
declare function defineFeatureFlags(rules: Record&lt;string, FeatureRule&gt;): FeatureFlags;

interface PerformanceBudgets &#123;
    routeJsBytes?: number;
    routeCssBytes?: number;
    htmlBytes?: number;
    imageBytes?: number;
    hydrationMs?: number;
    serverRenderMs?: number;
&#125;
interface PerformanceMeasurement &#123;
    routeJsBytes?: number;
    routeCssBytes?: number;
    htmlBytes?: number;
    imageBytes?: number;
    hydrationMs?: number;
    serverRenderMs?: number;
&#125;
interface BudgetViolation &#123;
    metric: keyof PerformanceBudgets;
    budget: number;
    actual: number;
    overBy: number;
&#125;
declare function checkPerformanceBudgets(budgets: PerformanceBudgets, measurement: PerformanceMeasurement): BudgetViolation[];

export &#123; type ActionDefinition, type AsyncSessionBackend, type Bucket, type BudgetViolation, CSRF_COOKIE, CSRF_HEADER, type CacheControlOptions, type CachePolicy, type ContentSecurityPolicyConfig, type Context, type CookieOptions, type CookieStore, type CorsConfig, type CorsOrigin, type CspDirectiveValue, type DefinedAction, type DefinedEndpoint, type DefinedLoader, type EndpointDefinition, EndpointError, type EndpointErrorBody, type FeatureFlags, type FeatureRule, type FeatureValue, type HstsConfig, type LoaderDefinition, type LocalStorageSnapshot, type Middleware, type Mode, type Next, type PageComponent, type PageMeta, type PerformanceBudgets, type PerformanceMeasurement, type PermissionsPolicyConfig, type RateLimitOptions, type RateLimitStore, type RawSocket, type RealtimeBridge, type RealtimeBus, type RealtimeConnectMeta, type RealtimeEnvelope, type RealtimeHandler, type RealtimeRegistry, type RealtimeSocket, type RequestLoggerOptions, type RequestRecord, type RequireAuthOptions, type Room, type RoomAuthInfo, type RoomClient, type RoomDefinition, type RoomHandlers, type RpcClientOptions, SESSION_USER_KEY, type SaveUploadOptions, type SavedUpload, type SchemaLike, type SecurityConfig, type SeoConfig, type ServerSentEvent, type SessionBackend, type SessionEntry, type SessionStore, type Span, type SpanRecord, type StreamResponseInit, type TFunction, TTLCache, type Target, type Tenant, type TenantMiddlewareOptions, type TenantResolver, type Tracer, type TrustedTypesConfig, UploadError, bridgeRealtime, cacheControl, checkPerformanceBudgets, collectUploads, createContext, createCorsPreflightResponse, createRealtimeRegistry, createRpcClient, createTracer, csrfProtection, csrfToken, dedupe, defaultKey, defineAction, defineEndpoint, defineFeatureFlags, defineLoader, defineRoom, escapeHtml, etag, getUser, hashPassword, isRoomDefinition, isSafeIslandName, isSafeRequestPath, isWebSocketOriginAllowed, loadSession, logIn, logOut, notModified, peerKey, proxyKey, rateLimit, renderDevError, renderError, renderNotFound, renderProdError, renderStatusPage, requestLogger, requireAuth, requireTenant, resolveRequestUrl, sanitizeFilename, saveUpload, sessionAuth, setSessionBackend, sse, streamResponse, tenantFromSubdomain, tenantMiddleware, tenantScope, tracingMiddleware, verifyCsrf, verifyPassword, withCacheControl, withContextHeaders, withSecurityHeaders, withSpan &#125;;
</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"><article class="example-card"><h3>A minimal middleware chain</h3><pre data-language="ts"><code>import &#123;
  createContext,
  withContextHeaders,
  sessionAuth,
  requireAuth,
  requestLogger,
  rateLimit,
  csrfProtection,
  type Middleware,
&#125; from &quot;@wrnexus/core&quot;;

const chain: Middleware[] = [
  requestLogger(&#123; format: &quot;json&quot; &#125;),
  rateLimit(&#123; max: 100, windowMs: 60_000 &#125;),
  csrfProtection(),
  sessionAuth(),
  requireAuth(&#123; loginPath: &quot;/login&quot; &#125;),
];</code></pre></article><article class="example-card"><h3>Password auth</h3><pre data-language="ts"><code>import &#123; hashPassword, verifyPassword, logIn, getUser &#125; from &quot;@wrnexus/core&quot;;

// Registration
const passwordHash = await hashPassword(form.password);

// Login
if (await verifyPassword(form.password, user.passwordHash)) &#123;
  logIn(ctx, &#123; id: user.id, email: user.email &#125;);
&#125;

const current = getUser&lt;&#123; id: string &#125;&gt;(ctx); // or null</code></pre></article><article class="example-card"><h3>HTTP caching with ETags</h3><pre data-language="ts"><code>import &#123; etag, notModified, withCacheControl &#125; from &quot;@wrnexus/core&quot;;

const body = JSON.stringify(data);
const tag = etag(body);
if (notModified(ctx.req, tag)) &#123;
  return new Response(null, &#123; status: 304, headers: &#123; ETag: tag &#125; &#125;);
&#125;
const res = new Response(body, &#123; headers: &#123; ETag: tag, &quot;content-type&quot;: &quot;application/json&quot; &#125; &#125;);
return withCacheControl(res, &#123; maxAge: 60, staleWhileRevalidate: 300 &#125;);</code></pre></article><article class="example-card"><h3>Streaming SSE</h3><pre data-language="ts"><code>import &#123; sse &#125; from &quot;@wrnexus/core&quot;;

async function* ticks() &#123;
  for (let n = 0; ; n++) &#123;
    yield &#123; event: &quot;tick&quot;, data: String(n) &#125;;
    await Bun.sleep(1000);
  &#125;
&#125;
export default (ctx) =&gt; sse(ticks());</code></pre></article><article class="example-card"><h3>A realtime room</h3><pre data-language="ts"><code>// app/realtime/chat.ts
import &#123; defineRoom &#125; from &quot;@wrnexus/core&quot;;

export default defineRoom(&#123;
  authorize: (info) =&gt; !!info.user, // require auth
  onConnect(client) &#123;
    client.user = client.query.user;
    client.room.broadcast(&#123; type: &quot;join&quot;, id: client.id &#125;);
  &#125;,
  onMessage(client, msg) &#123;
    client.broadcast(&#123; type: &quot;say&quot;, from: client.id, text: msg.text &#125;);
  &#125;,
&#125;);</code></pre></article><article class="example-card"><h3>A realtime room</h3><pre data-language="ts"><code>import &#123; createRealtimeRegistry, bridgeRealtime &#125; from &quot;@wrnexus/core&quot;;
import &#123; createPubSub &#125; from &quot;@wrnexus/pubsub&quot;;
import &#123; redisDriver &#125; from &quot;@wrnexus/pubsub/redis&quot;;

const registry = createRealtimeRegistry();
bridgeRealtime(registry, createPubSub(redisDriver(process.env.REDIS_URL)));</code></pre></article></div></section></article>
      <aside class="on-this-page"><h2>On this page</h2><nav><a class="toc-level-2" href="#guide">Guide</a><a class="toc-level-3" href="#overview">Overview</a><a class="toc-level-3" href="#api">API</a><a class="toc-level-4" href="#context-middleware-wrnexus-core">Context &amp; middleware — @wrnexus/core</a><a class="toc-level-4" href="#authentication-wrnexus-core">Authentication — @wrnexus/core</a><a class="toc-level-4" href="#csrf-wrnexus-core">CSRF — @wrnexus/core</a><a class="toc-level-4" href="#rate-limiting-wrnexus-core">Rate limiting — @wrnexus/core</a><a class="toc-level-4" href="#request-logging-wrnexus-core">Request logging — @wrnexus/core</a><a class="toc-level-4" href="#caching-wrnexus-core">Caching — @wrnexus/core</a><a class="toc-level-4" href="#file-uploads-wrnexus-core">File uploads — @wrnexus/core</a><a class="toc-level-4" href="#streaming-sse-wrnexus-core">Streaming &amp; SSE — @wrnexus/core</a><a class="toc-level-4" href="#realtime-rooms-wrnexus-core">Realtime rooms — @wrnexus/core</a><a class="toc-level-4" href="#error-pages-wrnexus-core">Error pages — @wrnexus/core</a><a class="toc-level-4" href="#security-headers-cors-wrnexus-core">Security headers &amp; CORS — @wrnexus/core</a><a class="toc-level-4" href="#storage-cookies-sessions-localstorage-wrnexus-core">Storage: cookies, sessions, localStorage — @wrnexus/core</a><a class="toc-level-4" href="#low-level-security-helpers-wrnexus-core">Low-level security helpers — @wrnexus/core</a><a class="toc-level-4" href="#jsx-runtime-wrnexus-core-wrnexus-core-jsx-runtime-wrnexus-core-jsx-dev-runtime">JSX runtime — @wrnexus/core, @wrnexus/core/jsx-runtime, @wrnexus/core/jsx-dev-runtime</a><a class="toc-level-3" href="#usage">Usage</a><a class="toc-level-4" href="#a-minimal-middleware-chain">A minimal middleware chain</a><a class="toc-level-4" href="#password-auth">Password auth</a><a class="toc-level-4" href="#http-caching-with-etags">HTTP caching with ETags</a><a class="toc-level-4" href="#streaming-sse">Streaming SSE</a><a class="toc-level-4" href="#a-realtime-room">A realtime room</a><a class="toc-level-4" href="#jsx-rendering">JSX rendering</a><a class="toc-level-3" href="#requirements-notes">Requirements / Notes</a><a class="toc-level-2" href="#api">Complete API</a><a class="toc-level-2" href="#examples">Examples</a></nav></aside>
    </main>
      <footer><div class="footer-brand"><span class="footer-mark" aria-hidden="true">W</span><p><strong>WRNexusJS 0.3.2</strong><span>Complete API documentation generated from installed package declarations.</span></p></div><nav aria-label="Footer"><a href="/packages">All packages</a><a href="/getting-started">Get started</a><a href="/security">Security</a><a href="/support">Support</a><a href="/llms.txt">AI guide</a></nav><p class="footer-meta">Private Developer Preview · Bun-native</p></footer>
      <BackToTop />
    </div>
  }
}
