Files
WRNexusJS/packages/core/README.md
T
Clintchiz 586a6db8ff
Quality / quality (ubuntu-latest) (push) Failing after 21s
Quality / quality (windows-latest) (push) Canceled after 0s
release: WRNexusJS 0.8.0
2026-08-02 23:18:51 +05:30

26 KiB

@wrnexus/core

The framework core: the request Context, middleware contract, and the security, session, caching, streaming, realtime, and JSX primitives every other WrNexus package builds on.

Part of the WrNexus framework — an SSR-first, Bun-native full-stack web framework.

Overview

@wrnexus/core is the shared foundation of WrNexus. It defines the Context object that flows through every middleware, page, and API route, plus the Middleware/Next 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 "rooms", security headers/CORS, and a server-side JSX runtime that renders to HTML strings. Everything here is server-side and Bun-native (it uses Bun.password, Bun.write, the web-standard Request/Response, and crypto). You depend on it directly and transitively through the rest of the framework.

Installation

bun add @wrnexus/core

Private package — the machine must be authenticated to the wrnexus npm org (a read token in ~/.npmrc). Requires Bun (Node is not supported).

API

Context & middleware — @wrnexus/core

The Context (ctx) is the single value passed to middleware and handlers.

Export Kind Description
Context type Per-request object: req, url, lang, t, params, locals, user?, ip?, cookies, session, localStorage.
Next type () => Promise<Response> | Response — invokes the next middleware/handler.
Middleware type (ctx, next) => Promise<Response> | Response. Return next() to continue, or a Response to short-circuit.
createContext(req, url) fn Build a fresh Context for an incoming request (wires up cookies, session, localStorage snapshot).
withContextHeaders(ctx, res) fn Apply accumulated headers (e.g. Set-Cookie) from the context onto a response.
PageComponent type (ctx) => string | Promise<string> — a page module's default export.
PageMeta / SeoConfig type <head> metadata: title, description, canonical, robots, image, twitterCard, themeColor, …
TFunction type (key, params?) => string — translate a key for ctx.lang, interpolating {param} placeholders.

Key Context fields:

  • ctx.locals — per-request scratch space for passing values between middleware.
  • ctx.user — the authenticated user (populated by sessionAuth/logIn), or null.
  • ctx.ip — the direct socket peer IP (not spoofable via headers).
  • ctx.cookies / ctx.session / ctx.localStorage — see Storage below.

Authentication — @wrnexus/core

Passwords are hashed with argon2id via Bun.password; sessions ride the cookie-backed SessionStore.

Export Signature Notes
hashPassword(password) (string) => Promise<string> argon2id hash to store.
verifyPassword(password, hash) (string, string) => Promise<boolean> Constant-safe; returns false on bad/empty hash.
logIn(ctx, user) (Context, U) => void Regenerates the session id (fixation defense), stores the user, sets ctx.user.
logOut(ctx) (Context) => void Clears the session and ctx.user.
getUser(ctx) (Context) => U | null Current user from ctx.user, falling back to the session.
sessionAuth() () => Middleware Hydrates ctx.user from the session each request. Register early.
requireAuth(options?) (RequireAuthOptions?) => Middleware Guard: API/fetch requests get 401 JSON, page navigations get 302 to loginPath (default /login) with ?next=.
SESSION_USER_KEY "user" Session key holding the user.

RequireAuthOptions: { loginPath?: string }.

CSRF — @wrnexus/core

Double-submit cookie pattern: a readable wire-csrf cookie is echoed in an x-csrf-token header on unsafe requests.

Export Signature Notes
csrfToken(ctx) (Context) => string Ensures the CSRF cookie exists and returns its token.
verifyCsrf(ctx) (Context) => boolean Safe methods (GET/HEAD/OPTIONS) pass; otherwise header/ctx.locals._csrf must match the cookie (constant-time).
csrfProtection() () => Middleware 403s unsafe requests with a missing/mismatched token.
CSRF_COOKIE / CSRF_HEADER "wire-csrf" / "x-csrf-token" Cookie & header names.

Rate limiting — @wrnexus/core

Fixed-window limiter that returns 429 with Retry-After and emits RateLimit-Limit/-Remaining/-Reset headers.

Export Signature Notes
rateLimit(options?) (RateLimitOptions?) => Middleware Main middleware.
peerKey(ctx) (Context) => string Non-spoofable key from ctx.ip (default).
proxyKey(ctx) (Context) => string Trusts x-forwarded-for/x-real-ip. Use only behind a trusted proxy.
defaultKey Deprecated alias of proxyKey.

RateLimitOptions: windowMs (default 60_000), max (default 60), key, trustProxy (default false → keys on peerKey; trueproxyKey), message, headers (default true), store.

RateLimitStore is pluggable — implement hit(key, windowMs, now) => Bucket | Promise<Bucket> (a Bucket is { count, resetAt }) to back limits with Redis/SQL across instances. The default store is process-local memory.

Request logging — @wrnexus/core

Export Signature Notes
requestLogger(options?) (RequestLoggerOptions?) => Middleware One record per request with a request id (stored on ctx.locals[requestIdKey]).

RequestLoggerOptions: format ("pretty" default | "json"), sink(line, record) (default console.log), requestIdKey (default "requestId"), now. RequestRecord = { time, id, method, path, status, durationMs }.

Resilience — @wrnexus/core

resilientCall standardizes cancellation-aware timeouts, controlled retries, fixed or exponential backoff, fallback responses, circuit breaking, and bounded concurrency. Reuse a declarative circuit/bulkhead options object, or an explicit CircuitBreaker/Bulkhead instance, wherever calls must share health and capacity state.

import { resilientCall } from "@wrnexus/core";

const paymentCircuit = { failures: 5, resetAfter: "30s" } as const;

const status = await resilientCall({
  timeout: "5s",
  retries: 3,
  retryDelay: "100ms",
  backoff: "exponential",
  circuitBreaker: paymentCircuit,
  bulkhead: { concurrency: 20, queue: 100 },
  run: (signal) => paymentProvider.checkStatus({ signal }),
  fallback: () => ({ state: "unavailable" }),
});

CircuitBreaker.snapshot() reports closed, open, or half-open, failure and success counts, and the remaining retry delay for health endpoints and development tooling. Fail-fast conditions use stable WRN-RESILIENCE-* codes. Core's existing HealthRegistry, withIdempotency, and pluggable stores/locks cover health reporting, idempotent requests, and distributed coordination.

Caching — @wrnexus/core

Export Kind Notes
TTLCache<V> class In-memory TTL cache: get, set, getOrLoad(key, loader, ttlMs?), delete, clear, size. Constructor takes a default ttlMs (60s).
cacheControl(options) fn Build a Cache-Control value from CacheControlOptions.
withCacheControl(res, options) fn Apply Cache-Control to a response.
etag(body, weak?) fn Stable quoted FNV-1a ETag (weak by default).
notModified(req, tag) fn true when If-None-Match matches — send a 304.

CacheControlOptions: maxAge, sMaxAge, private, noStore, noCache, staleWhileRevalidate, immutable.

File uploads — @wrnexus/core

Bun parses multipart/form-data via Request.formData(); these helpers validate and persist the resulting Files.

Export Signature Notes
collectUploads(form) (FormData) => { field, file }[] Every non-empty File in a parsed form.
saveUpload(file, options) (File, SaveUploadOptions) => Promise<SavedUpload> Validates size/type, sanitizes the name, writes via Bun.write. Throws UploadError.
sanitizeFilename(name) (string) => string Strips separators, traversal, control/illegal chars; caps at 255.
UploadError class Thrown on rejected uploads.

SaveUploadOptions: dir (required), maxBytes, allowedTypes (MIME types like "image/png" and/or extensions like ".png"), filename(file). SavedUpload = { path, filename, size, type }.

Streaming & SSE — @wrnexus/core

Export Signature Notes
streamResponse(source, init?) (Iterable|AsyncIterable<string|Uint8Array>, StreamResponseInit?) => Response Streaming Response from a chunk source (basis for streaming SSR).
sse(source) (Iterable|AsyncIterable<ServerSentEvent>) => Response text/event-stream response.

StreamResponseInit: status, headers, contentType (default "text/html; charset=utf-8"). ServerSentEvent: { data, event?, id?, retry? }.

Realtime rooms — @wrnexus/core

WebSocket rooms. A file in app/realtime/ exports default defineRoom({ ... }) and is served at ws://host/realtime/<name>.

Export Signature Notes
defineRoom(handlers) (RoomHandlers) => RoomDefinition Define a room. Export the result as default.
isRoomDefinition(value) (unknown) => boolean Type guard for a room definition.
createRealtimeRegistry() () => RealtimeRegistry Server-side connection manager mapping sockets ↔ rooms.
bridgeRealtime(registry, bus, topic?) (RealtimeRegistry, RealtimeBus, string?) => () => void Bridge broadcasts/toUser sends across processes via a pub/sub bus.

RoomHandlers: authorize(info) => boolean (gate before accept — return false to reject with 403), onConnect(client), onMessage(client, message) (JSON auto-parsed), onLeave(client). A handler receives a RoomClient with id, user, query, data, room, and send / broadcast / to(id) / toUser(user) / close. The Room API adds state, clients(), count(), and broadcast. RealtimeBus is structurally satisfied by @wrnexus/pubsub. Legacy RealtimeHandler/RealtimeSocket raw handlers are still exported. Connection-targeted sends (send, to(id)) stay local; room broadcasts and toUser cross the bridge.

Error pages — @wrnexus/core

Export Signature Notes
renderError(err, mode) (unknown, Mode) => Response Dev page (with stack) or generic prod page by mode.
renderDevError(err, status?) (unknown, number?) => Response Readable HTML error page including the stack trace.
renderProdError(status?) (number?) => Response Generic page that never leaks file paths.
renderNotFound() () => Response Simple 404 page.

Mode = "development" | "production".

Security headers & CORS — @wrnexus/core

Export Signature Notes
withSecurityHeaders(req, res, mode, security?, nonce?) Response Applies CORS + CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, COOP, Trusted Types, and extraHeaders.
createCorsPreflightResponse(req, security?) Response | null Builds a 204/403 preflight response for CORS OPTIONS requests.
isWebSocketOriginAllowed(req, security?) boolean Guards WS upgrades against cross-site hijacking (allows same-origin, configured CORS origins, and non-browser clients).

Config types: SecurityConfig (top-level), CorsConfig/CorsOrigin, ContentSecurityPolicyConfig/CspDirectiveValue, HstsConfig, TrustedTypesConfig, PermissionsPolicyConfig. WrNexus applies sensible defaults (self-only CSP, frame-ancestors 'none', restrictive Permissions-Policy, HSTS in production, Trusted Types in production); each is individually overridable or disable-able via false.

Storage: cookies, sessions, localStorage — @wrnexus/core

These back the ctx.cookies, ctx.session, and ctx.localStorage fields.

Export Kind Notes
setSessionBackend(backend) fn Swap the sync session persistence backend (SessionBackend) — e.g. bun:sqlite. Default is process-local memory. Call once at startup.
loadSession(backend, options?) fn → Middleware Back ctx.session with an async store (AsyncSessionBackend: load/save/destroy) — loads before the request, saves after. options.ttlMs default 24h.
CookieStore type get/getAll/has/set(name, value, opts?)/delete/headers.
SessionStore type id/get/getAll/set/delete/regenerate/clear.
LocalStorageSnapshot type Read-only view of the browser's localStorage sent via header for CSR bindings.
CookieOptions type path, domain, maxAge, expires, httpOnly, secure, sameSite.
SessionEntry / SessionBackend / AsyncSessionBackend types Session persistence contracts.

Low-level security helpers — @wrnexus/core

Export Signature Notes
escapeHtml(value) (string) => string Escape for HTML text/attributes.
isSafeIslandName(name) (string) => boolean Allow only a conservative [A-Za-z0-9_-]+ charset.
isSafeRequestPath(pathname) (string) => boolean Reject NULs, .. traversal, and backslashes.

JSX runtime — @wrnexus/core, @wrnexus/core/jsx-runtime, @wrnexus/core/jsx-dev-runtime

A server-side JSX runtime that renders to HTML strings (no virtual DOM). Point tsconfig's jsxImportSource at @wrnexus/core.

Export Kind Notes
jsx / jsxs fn The runtime factory (TypeScript calls these automatically). Returns an Html instance.
Fragment symbol JSX fragment marker.
Html class Wraps a raw, already-safe HTML string (toString() returns it).
mustache(expr) fn Emit a {{expr}} placeholder (tagged-template or string form) for the client binder.
JSXComponent / JSXProps / Renderable types Component signature and renderable value types.

Values interpolated as children are HTML-escaped unless they are an Html instance; use dangerouslySetInnerHTML={{ __html }} for trusted markup. Void elements render without a closing tag; classNameclass, htmlForfor, and style objects are serialized to CSS text.

The subpath exports map to the runtime TypeScript's JSX transform expects:

// tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "@wrnexus/core",
  },
}

Usage

A minimal middleware chain

import {
  createContext,
  withContextHeaders,
  sessionAuth,
  requireAuth,
  requestLogger,
  rateLimit,
  csrfProtection,
  type Middleware,
} from "@wrnexus/core";

const chain: Middleware[] = [
  requestLogger({ format: "json" }),
  rateLimit({ max: 100, windowMs: 60_000 }),
  csrfProtection(),
  sessionAuth(),
  requireAuth({ loginPath: "/login" }),
];

Password auth

import { hashPassword, verifyPassword, logIn, getUser } from "@wrnexus/core";

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

// Login
if (await verifyPassword(form.password, user.passwordHash)) {
  logIn(ctx, { id: user.id, email: user.email });
}

const current = getUser<{ id: string }>(ctx); // or null

HTTP caching with ETags

import { etag, notModified, withCacheControl } from "@wrnexus/core";

const body = JSON.stringify(data);
const tag = etag(body);
if (notModified(ctx.req, tag)) {
  return new Response(null, { status: 304, headers: { ETag: tag } });
}
const res = new Response(body, { headers: { ETag: tag, "content-type": "application/json" } });
return withCacheControl(res, { maxAge: 60, staleWhileRevalidate: 300 });

Streaming SSE

import { sse } from "@wrnexus/core";

async function* ticks() {
  for (let n = 0; ; n++) {
    yield { event: "tick", data: String(n) };
    await Bun.sleep(1000);
  }
}
export default (ctx) => sse(ticks());

A realtime room

// app/realtime/chat.ts
import { defineRoom } from "@wrnexus/core";

export default defineRoom({
  authorize: (info) => !!info.user, // require auth
  onConnect(client) {
    client.user = client.query.user;
    client.room.broadcast({ type: "join", id: client.id });
  },
  onMessage(client, msg) {
    client.broadcast({ type: "say", from: client.id, text: msg.text });
  },
});

Scale it across processes:

import { createRealtimeRegistry, bridgeRealtime } from "@wrnexus/core";
import { createPubSub } from "@wrnexus/pubsub";
import { redisDriver } from "@wrnexus/pubsub/redis";

const registry = createRealtimeRegistry();
bridgeRealtime(registry, createPubSub(redisDriver(process.env.REDIS_URL)));

JSX rendering

import { Html } from "@wrnexus/core";

function Card({ title, body }: { title: string; body: string }) {
  return (
    <article class="card">
      <h2>{title}</h2>
      <p>{body}</p>
    </article>
  );
}

const html: Html = <Card title="Hi" body="<b>escaped</b> automatically" />;
return new Response(html.toString(), { headers: { "content-type": "text/html" } });

Requirements / Notes

  • Bun-only. Uses Bun.password (argon2id), Bun.write, web-standard Request/Response/FormData/ReadableStream, and the global crypto. Node is not supported.
  • Session and rate-limit backends default to process-local memory. For multi-instance deployments, swap in a shared backend: setSessionBackend (sync, e.g. bun:sqlite) or loadSession (async, e.g. Redis) for sessions, a custom RateLimitStore for limits, and bridgeRealtime for realtime.
  • Works with the rest of the framework: realtime bridging is structurally compatible with @wrnexus/pubsub; the security, auth, and JSX primitives here are consumed by the WrNexus server/router packages.
  • Subpath exports: @wrnexus/core/jsx-runtime and @wrnexus/core/jsx-dev-runtime for TypeScript's automatic JSX transform.