HBForge Documentation

Complete API reference, usage guides, and code examples for all 75 modules (106,300+ lines) and 1,280+ APIs. Web7-L6 AAA conformant — 30/30 baseline tests passing in ~8 ms.

What is HBForge?

HBForge is a unified JavaScript framework built entirely from scratch by HyperBridge Digital. It ships 27 production-ready modules covering every layer of a modern application — UI, server, auth, data, animation, charting, PDF, search, email, i18n, testing, CLI, plus the full Web7 Layer 6 primitive set (AMP, AIG, PoO, memory, consent, provenance, ZK-ML, conformance) — with zero external dependencies.

Instead of assembling 60+ npm packages from different authors with incompatible APIs, versioning headaches, and cascading security vulnerabilities, HBForge gives your team one coherent toolkit. Every module is designed to work standalone or together, with shared conventions and no transitive bloat.

Philosophy

Zero dependencies — nothing in node_modules except HBForge itself
Cohesive APIs — all modules follow the same patterns (options objects, async-first, chainable builders)
Tree-shakeable — import one module with a subpath, pay only for what you use
TypeScript-first — full .d.ts types for all 1,280+ APIs, no @types/* packages needed
Node ≥ 18 — uses native fetch, streams, crypto, and Web APIs throughout
Production-ready — battle-tested by HyperBridge Digital on client projects

Architecture

HBForge is organized into four logical layers. Each layer can be used independently:

UI Layer (Browser)

forge/client · forge/chart · forge/animate · forge/form · forge/i18n

Server Layer (Node.js)

forge/server · forge/auth · forge/data · forge/notify · forge/mail

Platform Layer

forge/pwa · forge/display · forge/3d

Tooling Layer

forge/test · forge/cli · forge/pdf · forge/search · forge/schema

Module Overview

Module Purpose Lines APIs
forge/3d WebGL2 3D engine — geometry, materials, lights, cameras, particles, post-fx 28,640 368+
forge/client React-like UI framework with VDOM, hooks, signals, router, SSR 12,276 171+
forge/server HTTP server, routing, middleware, WebSocket, Cron, background tasks 11,177 85+
forge/data PostgreSQL ORM, query builder, migrations, transactions, seeding 5,488 95+
forge/animate Tweens, springs, scroll effects, FLIP, stagger, timeline, easings 7,528 45+
forge/auth Password, JWT, OAuth2, MagicLink, TOTP, WebAuthn, RBAC, MFA 3,820 65+
forge/form Form management, async validation, field arrays, file upload, steps 2,890 40+
forge/schema Zod-compatible schema validation, coercion, transforms, OpenAPI gen 2,340 42+
forge/search Full-text search, BM25 ranking, facets, autocomplete, highlights 2,100 35+
forge/pwa Service Workers, Cache API, Web Push, Background Sync, offline-first 1,880 35+
forge/test Test runner, matchers, mocks, DOM testing, snapshots, property-based 2,050 45+
forge/chart SVG/Canvas charts: line, bar, pie, scatter, radar, heatmap, streaming 1,960 25+
forge/pdf PDF generation, tables, custom fonts, encryption, watermarks, QR codes 1,830 30+
forge/mail SMTP client, DKIM signing, email templates, queue, tracking, calendar 1,750 22+
forge/notify Toast UI, multi-channel notifications, retry queue, notification center 1,620 18+
forge/i18n ICU MessageFormat, lazy namespaces, date/number/currency formatters, RTL 1,540 30+
forge/cli CLI framework, interactive prompts, spinners, progress bars, config 1,450 25+
forge/display Canvas 2D, DOM utilities, layout, color, CSS variables, typography, SVG, theme management 1,944 60+
forge/prime Web7 Web7 bridge — DID identity, AMP routing, PoO, AIG, Vigil, BYOA, Telemetry, Model Routing, Kynetra Prime. Lite · Pro · Enterprise 3,016 32+
forge/wasm Web7 Universal WASM loader for KYRx (L1) & ClearScript (L5) — memory manager, WASI-lite, worker pool, host imports (web7/prime · vigil · auth · amp), hot-reload, type bridge 1,481 20+
forge/_internal shared Shared primitives — _internal/crypto (HMAC/SHA-256/random), _internal/binary (UTF-8/LEB128), _internal/time (now/duration/sleep). De-duplicates code across 6–8 modules. Advisory API; stable signatures. 442 27
forge/ai roadmap LLM clients, streaming, tool use, embeddings, RAG pipeline

Why Zero Dependencies?

Most JavaScript frameworks pull in dozens of transitive dependencies. Each one is a potential security vector, a version conflict, and a maintenance burden. HBForge is built entirely from scratch using modern JavaScript (ES2022+) and Web APIs available in Node 18+:

  • node:crypto for all cryptography (bcrypt-style PBKDF2, HMAC, AES-GCM)
  • node:http / node:https for the server — no Express, no Fastify
  • node:net / node:tls for the SMTP client — no Nodemailer
  • Native fetch for HTTP requests — no Axios, no node-fetch
  • Inverted indexes and BM25 ranking written from scratch — no Lunr, no Flexsearch
  • Canvas 2D / WebGL2 used directly — no Three.js dependency
  • structuredClone, AbortController, ReadableStream, URLPattern

Running npm ls --depth=0 after installing HBForge shows exactly one package: @hyperbridge/forge.

Installation

Enterprise Access Only

HBForge is currently available exclusively for enterprise clients. Opening to the Developer Community on June 25, 2026. Contact kr@hyperbridge.in for early access.

Requirements

  • Node.js ≥ 18.0.0 (uses native fetch, crypto, streams)
  • npm ≥ 8, yarn ≥ 3, pnpm ≥ 7, or bun ≥ 1.0
  • TypeScript 5+ (optional — ships full .d.ts types, no @types/* needed)

Package Managers

# npm
npm install @hyperbridge/forge

# yarn
yarn add @hyperbridge/forge

# pnpm
pnpm add @hyperbridge/forge

# bun
bun add @hyperbridge/forge

Verify Installation

node -e "const forge = require('@hyperbridge/forge'); console.log(forge.VERSION);"
# → 3.2.1

Subpath Imports (Recommended)

Each module is a separate subpath entry. Import only what you need — the rest is never loaded. All subpaths are defined in package.json#exports and work with Node.js, bundlers (Vite, webpack, esbuild, Rollup), and TypeScript.

// ── UI Layer (Browser) ─────────────────────────────────────
// React-like component system, hooks, router, signals, SSR
import { createElement, Component, useState, useEffect, useRef,
         useContext, createContext, useMemo, useCallback, useSignal,
         useComputed, Router, Route, Link, render } from '@hyperbridge/forge/client';

// SVG/Canvas charts — line, bar, pie, scatter, radar, heatmap, etc.
import { ChartForge, ChartDashboard, ChartBrush } from '@hyperbridge/forge/chart';

// Tween, spring, scroll, FLIP, stagger, timeline animations
import { Tween, Spring, Timeline, onScroll, stagger, flip } from '@hyperbridge/forge/animate';

// Form management with async/cross-field validation and file upload
import { Form, Controller, DragDropUpload, RepeatingGroup,
         AccessibleForm, fromSchema } from '@hyperbridge/forge/form';

// ICU MessageFormat, formatters, lazy namespaces, RTL support
import { I18n, RTLManager, NumberFormatterEx } from '@hyperbridge/forge/i18n';

// ── Server Layer (Node.js) ──────────────────────────────────
// HTTP/HTTPS server, routing, middleware, WebSockets, cron
import { Forge, Router, WebSocketServer, CronJob, TaskQueue,
         rateLimit, cors, compress, staticFiles } from '@hyperbridge/forge/server';

// Password, JWT, OAuth2, MagicLink, TOTP, WebAuthn, RBAC, MFA
import { Password, JWT, OAuth2Provider, TOTP, BackupCodes,
         MagicLink, RBAC, SessionManager, WebAuthn, OAuth2PKCE,
         AdaptiveMFA, SAMLProvider, IPAllowList, AuditTrail,
         DeviceManager } from '@hyperbridge/forge/auth';

// PostgreSQL ORM, query builder, migrations, seeding, transactions
import { Pool, PgConnection, Model, QueryBuilder, Migrator,
         SchemaBuilder, ReadReplica, QueryCache, AuditPlugin,
         SeedFactory } from '@hyperbridge/forge/data';

// Multi-channel notifications (toast, WebSocket, email, SMS)
import { NotificationManager, ToastManager, RetryQueue,
         DedupeFilter, NotificationCenter, QuietHours } from '@hyperbridge/forge/notify';

// SMTP client with DKIM, templates, queue, tracking, calendar
import { SMTPClient, EmailTemplate, MailQueue, MailTracker,
         BounceList, CalendarInvite, ABTest } from '@hyperbridge/forge/mail';

// ── Platform Layer ──────────────────────────────────────────
// Service Workers, Cache API, Web Push, Background Sync
import { PWAForge, CacheStrategy, PrecacheManager, PushManager,
         BackgroundSync, OfflineManager, AppShell, BadgeManager,
         UpdateManager, generateVAPIDKeys } from '@hyperbridge/forge/pwa';

// DPR/Retina/4K detection, quality tiers, canvas scaling
import { getDPR, isRetina, is4K, qualityTier, qualityScore,
         snapshot, scaleCanvas, setupWebGLViewport,
         createVariant, createVariants, onDPRChange } from '@hyperbridge/forge/display';

// WebGL2 3D engine — scene graph, geometry, materials, post-fx
import { Scene, WebGLRenderer, PerspectiveCamera, Mesh,
         BoxGeometry, SphereGeometry, StandardMaterial,
         DirectionalLight, PointLight, AnimationMixer,
         ParticleSystem, Raycaster } from '@hyperbridge/forge/3d';

// ── Tooling Layer ───────────────────────────────────────────
// Test runner, matchers, mocking, DOM/HTTP testing, snapshots
import { TestRunner, expect, fn, spyOn, bench } from '@hyperbridge/forge/test';

// CLI framework, prompts, spinners, progress bars, task runner
import { CLI, Command, Prompt, Spinner, ProgressBar,
         TaskRunner, Logger, c } from '@hyperbridge/forge/cli';

// PDF generation with tables, fonts, encryption, QR codes
import { PDFForge, PDFReader, QRCode, Barcode } from '@hyperbridge/forge/pdf';

// Full-text BM25 search, autocomplete, facets, analytics
import { SearchForge, Autocomplete, FacetedSearch,
         SearchAnalytics, Highlighter, RelevanceTuner } from '@hyperbridge/forge/search';

// Zod-compatible schema validation with OpenAPI generation
import { z, Schema } from '@hyperbridge/forge/schema';

CommonJS (require)

// Full bundle (lazy-loaded per module)
const forge = require('@hyperbridge/forge');
console.log(forge.VERSION); // 3.2.1

// Specific modules
const { Password, JWT } = require('@hyperbridge/forge/auth');
const { Pool, Model }   = require('@hyperbridge/forge/data');
const { Forge, Router } = require('@hyperbridge/forge/server');
const { PDFForge }      = require('@hyperbridge/forge/pdf');
const { z }             = require('@hyperbridge/forge/schema');

TypeScript Setup

No extra packages required — all types ship with the package. Recommended tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "strict": true,
    "types": []    // no @types/* needed
  }
}
// Types are inferred automatically
import { z } from '@hyperbridge/forge/schema';

const UserSchema = z.object({
  id:    z.number(),
  email: z.string().email(),
  role:  z.enum(['admin', 'user']),
});

type User = z.infer<typeof UserSchema>;
// → { id: number; email: string; role: 'admin' | 'user' }

ESM in Browsers (CDN)

Client-side modules work in modern browsers via ESM CDN (available at GA on June 25, 2026):

<!-- Via CDN (GA June 2026) -->
<script type="module">
  import { createElement, useState, render }
    from 'https://cdn.hbforge.dev/v3/client/index.js';
  import { Tween }
    from 'https://cdn.hbforge.dev/v3/animate/index.js';
</script>

Quick Start

One working example per module. Each snippet is self-contained — copy, paste, run.

Browser UI — forge/client

import { createElement as h, useState, useEffect, render } from '@hyperbridge/forge/client';

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);

  return h('div', { style: { padding: '20px' } },
    h('h1', null, `Count: ${count}`),
    h('button', { onClick: () => setCount(c => c + 1) }, 'Increment'),
    h('button', { onClick: () => setCount(0), style: { marginLeft: 8 } }, 'Reset')
  );
}

render(h(Counter), document.getElementById('root'));

HTTP Server — forge/server

import { Forge, Router, rateLimit, cors } from '@hyperbridge/forge/server';

const app   = new Forge();
const users = new Router({ prefix: '/users' });

app.use(cors({ origin: '*' }));
app.use(rateLimit({ max: 100, windowMs: 60_000 }));

users.get('/',    async (req, res) => res.json(await db.all()));
users.get('/:id', async (req, res) => res.json(await db.find(req.params.id)));
users.post('/',   async (req, res) => {
  const body = await req.json();
  res.status(201).json(await db.insert(body));
});

app.use(users);
app.listen(3000, () => console.log('Server on :3000'));

Schema Validation — forge/schema

import { z } from '@hyperbridge/forge/schema';

const UserSchema = z.object({
  id:       z.number().int().positive(),
  email:    z.string().email(),
  role:     z.enum(['admin', 'editor', 'viewer']).default('viewer'),
  age:      z.number().min(13).max(120).optional(),
  tags:     z.array(z.string()).max(10).default([]),
  settings: z.record(z.string(), z.unknown()).optional(),
});

// Parse (throws on failure)
const user = UserSchema.parse({ id: 1, email: 'a@b.com' });

// Safe parse (returns result object)
const { success, data, error } = UserSchema.safeParse(rawInput);

// Infer TypeScript type
type User = z.infer<typeof UserSchema>;

// Generate OpenAPI schema
const openApiSchema = UserSchema.toOpenAPI();

Forms — forge/form

import { Form, fromSchema } from '@hyperbridge/forge/form';
import { z } from '@hyperbridge/forge/schema';

const schema = z.object({
  email:    z.string().email('Invalid email'),
  password: z.string().min(8, 'Min 8 characters'),
  name:     z.string().min(1),
});

// Build form from schema — field types inferred automatically
const form = fromSchema(schema, {
  onSubmit: async (data) => {
    const res = await fetch('/api/register', {
      method: 'POST',
      body: JSON.stringify(data),
    });
    return res.json();
  },
});

// Use in UI
form.mount('#register-form');

// Access state programmatically
form.on('change', ({ field, value, errors }) => {
  console.log(field, 'changed to', value, 'errors:', errors);
});

form.on('submit', ({ data }) => console.log('Submitted:', data));
form.on('error',  ({ errors }) => console.warn('Errors:', errors));

Database ORM — forge/data

import { Pool, Model, QueryBuilder } from '@hyperbridge/forge/data';

const pool = new Pool({
  host: 'localhost', database: 'myapp',
  user: 'postgres',  password: 'secret',
  max: 10,
});

// Declare a model
class User extends Model {
  static table    = 'users';
  static pool     = pool;
  static fillable = ['email', 'name', 'role'];
  static hidden   = ['password_hash'];

  static scopes = {
    active: qb => qb.where('active', true),
    admins: qb => qb.where('role', 'admin'),
  };
}

// CRUD
const user = await User.create({ email: 'a@b.com', name: 'Alex' });
const all  = await User.scope('active').orderBy('name').limit(20).get();
const one  = await User.where('email', 'a@b.com').first();
await one.update({ name: 'Alexandra' });
await one.delete();

// Raw query builder
const rows = await new QueryBuilder(pool)
  .table('orders')
  .join('users', 'users.id', 'orders.user_id')
  .where('orders.total', '>', 100)
  .orderBy('orders.created_at', 'desc')
  .select('orders.*', 'users.email')
  .get();

Authentication — forge/auth

import { Password, JWT, TOTP, RBAC } from '@hyperbridge/forge/auth';

// Hash & verify passwords (PBKDF2-SHA-256, 310,000 iterations)
const hash  = await Password.hash('mypassword');
const valid = await Password.verify('mypassword', hash); // true

// JWT — sign, verify, refresh
const secret = process.env.JWT_SECRET;
const token  = await JWT.sign({ userId: 42, role: 'admin' }, secret, { expiresIn: '15m' });
const claims = await JWT.verify(token, secret);
// { userId: 42, role: 'admin', iat: ..., exp: ... }

const refreshed = await JWT.refresh(token, secret, { expiresIn: '15m' });

// TOTP (Google Authenticator compatible)
const { secret: totpSecret, qrDataUrl } = await TOTP.generateSecret('user@app.com', 'MyApp');
const isValid = TOTP.verify('123456', totpSecret);

// RBAC
const rbac = new RBAC({
  roles: {
    admin:  { permissions: ['*'] },
    editor: { permissions: ['posts:read', 'posts:write'] },
    viewer: { permissions: ['posts:read'] },
  },
});

rbac.can('editor', 'posts:write'); // true
rbac.can('viewer', 'posts:write'); // false

Animations — forge/animate

import { Tween, Spring, Timeline, onScroll, stagger } from '@hyperbridge/forge/animate';

// Tween with easing
const tween = new Tween({
  target:   document.querySelector('.card'),
  duration: 400,
  values:   { opacity: [0, 1], translateY: [20, 0] },
  easing:   'easeOutCubic',
});
tween.play();

// Physics spring
const spring = new Spring({
  target:  document.querySelector('.modal'),
  values:  { scale: [0.9, 1], opacity: [0, 1] },
  tension: 280, friction: 20, mass: 1,
});
spring.play();

// Chained timeline
const tl = new Timeline();
tl.add(new Tween({ target: logo, values: { opacity: [0, 1] }, duration: 300 }))
  .add(new Tween({ target: nav,  values: { translateY: [-20, 0] }, duration: 200 }), 150)
  .add(new Tween({ target: hero, values: { scale: [0.95, 1] }, duration: 400 }), 250)
  .play();

// Scroll-driven animation
onScroll(({ progress, element }) => {
  element.style.opacity  = progress;
  element.style.transform = `translateY(${(1 - progress) * 30}px)`;
}, { target: document.querySelector('.section'), threshold: 0.1 });

// Stagger list items
stagger(document.querySelectorAll('.list-item'), { delay: 60, duration: 300, values: { opacity: [0, 1], translateX: [-20, 0] } });

Charts — forge/chart

import { ChartForge } from '@hyperbridge/forge/chart';

const chart = new ChartForge({
  container: '#sales-chart',
  type:      'line',
  width:     800,
  height:    400,
  theme:     'dark',
});

chart.setData({
  labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
  datasets: [
    { label: 'Revenue', data: [42, 58, 39, 71, 63, 88], color: '#6366f1' },
    { label: 'Expenses', data: [30, 35, 28, 45, 50, 60], color: '#f43f5e' },
  ],
});

chart.render();

// Live streaming updates
const ws = new WebSocket('/api/metrics');
ws.onmessage = ({ data }) => {
  chart.push(JSON.parse(data));
};

// Export
const pngBlob = await chart.export('png', { scale: 2 });
const svgStr  = chart.export('svg');

PDF Generation — forge/pdf

import { PDFForge, QRCode } from '@hyperbridge/forge/pdf';

const doc = new PDFForge({ pageSize: 'A4', margin: { top: 40, left: 40, right: 40, bottom: 40 } });

// Page 1 — invoice
doc.setFont('Helvetica-Bold', 18);
doc.text('INVOICE #1042', { x: 40, y: 40 });
doc.setFont('Helvetica', 11);
doc.text('HyperBridge Digital · Chennai, India', { x: 40, y: 65 });
doc.text(`Date: ${new Date().toLocaleDateString()}`,      { x: 420, y: 40 });

// Draw divider
doc.line({ x1: 40, y1: 90, x2: 555, y2: 90, width: 0.5, color: '#cccccc' });

// Table
doc.table({
  x: 40, y: 105,
  columns: [
    { header: 'Item',     width: 250 },
    { header: 'Qty',      width: 60,  align: 'right' },
    { header: 'Rate',     width: 90,  align: 'right' },
    { header: 'Amount',   width: 105, align: 'right' },
  ],
  rows: [
    ['HBForge Enterprise License', '1', '$2,400.00', '$2,400.00'],
    ['Setup & Onboarding',         '3', '  $150.00', '  $450.00'],
  ],
  headerStyle: { fillColor: '#f5f5f5', bold: true },
  cellPadding: 8,
});

// QR code embedded in PDF
const qr = QRCode.generate('https://hbforge.dev', { size: 80 });
doc.image(qr, { x: 450, y: 680 });

const pdfBytes = doc.build();
// Node.js: await fs.writeFile('invoice.pdf', pdfBytes)
// Browser: const url = URL.createObjectURL(new Blob([pdfBytes], { type: 'application/pdf' }))

Full-Text Search — forge/search

import { SearchForge, Autocomplete, FacetedSearch } from '@hyperbridge/forge/search';

// Build an index
const search = new SearchForge({
  fields:  ['title', 'body', 'author'],
  weights: { title: 3, author: 2, body: 1 },
});

search.addDocuments([
  { id: 1, title: 'Getting Started with HBForge', body: 'A complete guide...', author: 'HyperBridge', category: 'tutorial' },
  { id: 2, title: 'forge/auth deep dive',           body: 'JWT, OAuth2, TOTP...', author: 'HyperBridge', category: 'guide' },
  { id: 3, title: 'Building REST APIs',              body: 'forge/server routing...', author: 'HyperBridge', category: 'guide' },
]);

// BM25 search
const results = search.search('jwt auth tokens');
// [{ id: 2, score: 4.82, title: 'forge/auth deep dive', ... }, ...]

// Autocomplete (prefix matching)
const ac = new Autocomplete(search);
const suggestions = ac.suggest('forg'); // ['forge/auth deep dive', 'Getting Started with HBForge', ...]

// Faceted search
const faceted = new FacetedSearch(search, { facets: ['category'] });
const { hits, facets } = faceted.search('guide', { filters: { category: 'guide' } });
// hits = [...], facets = { category: { tutorial: 1, guide: 2 } }

Notifications — forge/notify

import { ToastManager, NotificationManager, NotificationCenter } from '@hyperbridge/forge/notify';

// DOM toasts (browser)
const toast = new ToastManager({ position: 'top-right', maxStack: 5 });

toast.success('File uploaded successfully');
toast.error('Connection failed — retrying…');
toast.warning('Your session expires in 5 minutes');
toast.info('New version available');

// Async toast (shows loading, resolves to success/error)
await toast.promise(
  fetch('/api/upload', { method: 'POST', body: formData }),
  { loading: 'Uploading…', success: 'Done!', error: 'Upload failed' }
);

// Multi-channel notification manager (Node.js)
const nm = new NotificationManager({
  channels: {
    email: { type: 'smtp', host: 'smtp.example.com', from: 'noreply@app.com' },
    sms:   { type: 'twilio', accountSid: '...', authToken: '...', from: '+1555...' },
  },
});

await nm.send({
  to:       'user@example.com',
  channels: ['email', 'sms'],
  subject:  'Password changed',
  body:     'Your password was changed. If this wasn\'t you, contact support.',
});

// Notification center with persistence
const center = new NotificationCenter({ maxHistory: 100 });
center.add({ id: '1', title: 'New comment', body: 'Alex replied to your post', read: false });
const unread = center.getUnread();

Email — forge/mail

import { SMTPClient, EmailTemplate, MailQueue } from '@hyperbridge/forge/mail';

// SMTP client with DKIM signing
const smtp = new SMTPClient({
  host:     'smtp.postmarkapp.com',
  port:     587,
  secure:   false,
  auth:     { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS },
  dkim:     { domain: 'hyperbridge.digital', keySelector: 'default', privateKey: DKIM_KEY },
  pool:     true,  // connection pooling
  maxConns: 5,
});

// HTML templates with variable substitution
const tmpl = new EmailTemplate({
  subject:  'Welcome to {{appName}}, {{firstName}}!',
  html:     '

Hi {{firstName}},

Your account is ready.

', text: 'Hi {{firstName}}, your account is ready.', }); await smtp.send({ to: 'user@example.com', from: 'welcome@hyperbridge.digital', ...tmpl.render({ appName: 'HBForge', firstName: 'Alex' }), }); // Queue for bulk sending with rate limiting const queue = new MailQueue(smtp, { rateLimit: 50, rateLimitInterval: 1000 }); for (const subscriber of subscribers) { queue.enqueue({ to: subscriber.email, ...newsletter.render(subscriber) }); } await queue.flush(); // sends all queued emails respecting rate limits

Internationalization — forge/i18n

import { I18n, NumberFormatterEx } from '@hyperbridge/forge/i18n';

const i18n = new I18n({
  locale:   'en',
  fallback: 'en',
  messages: {
    en: {
      'greeting':            'Hello, {name}!',
      'unread':              'You have {count, plural, one {# message} other {# messages}}',
      'appointment':         'Your appointment is on {date, date, long} at {time, time, short}',
      'balance':             'Balance: {amount, number, ::currency/USD}',
      'progress':            '{pct, number, ::percent}',
    },
    ta: {
      'greeting':  'வணக்கம், {name}!',
      'unread':    '{count} செய்திகள் உள்ளன',
    },
  },
});

i18n.t('greeting', { name: 'Alex' });
// en → "Hello, Alex!"

i18n.t('unread', { count: 3 });
// en → "You have 3 messages"

i18n.t('appointment', { date: new Date(), time: new Date() });
// en → "Your appointment is on April 14, 2026 at 9:00 AM"

// Locale negotiation
I18n.negotiate(['ta-IN', 'en-US'], ['en', 'ta', 'fr']); // → 'ta'

// Extended number formatting
const fmt = new NumberFormatterEx('en-US');
fmt.currency(2400, 'USD');   // "$2,400.00"
fmt.compact(81954);          // "82K"
fmt.percent(0.876);          // "87.6%"
fmt.ordinal(3);              // "3rd"

CLI Tools — forge/cli

import { CLI, Command, Prompt, Spinner, TaskRunner, c } from '@hyperbridge/forge/cli';

const cli = new CLI({ name: 'myapp', version: '1.0.0' });

cli.add(new Command({
  name:    'deploy',
  desc:    'Deploy to production',
  options: [
    { flag: '--env, -e', desc: 'Environment', default: 'staging' },
    { flag: '--dry-run', desc: 'Simulate without deploying', boolean: true },
  ],
  action: async ({ env, dryRun }) => {
    // Interactive confirmation
    const confirm = await Prompt.confirm(`Deploy to ${c.red(env)}?`);
    if (!confirm) return;

    const spinner = new Spinner('Building…').start();

    const runner = new TaskRunner([
      { name: 'Build',    run: () => build() },
      { name: 'Test',     run: () => test() },
      { name: 'Upload',   run: () => upload(), skip: dryRun },
      { name: 'Migrate',  run: () => migrate(), skip: dryRun },
    ]);

    runner.on('task:done',   ({ name }) => console.log(c.green('✔') + ' ' + name));
    runner.on('task:failed', ({ name, error }) => console.error(c.red('✖') + ' ' + name, error));

    await runner.run();
    spinner.succeed('Deployed to ' + c.cyan(env));
  },
}));

cli.run(process.argv.slice(2));

Testing — forge/test

import { TestRunner, expect, fn, spyOn, bench } from '@hyperbridge/forge/test';

const runner = new TestRunner({ timeout: 5000, bail: false });

runner.describe('User service', () => {
  runner.it('creates a user', async () => {
    const user = await createUser({ email: 'a@b.com', name: 'Alex' });
    expect(user.id).toBeGreaterThan(0);
    expect(user.email).toBe('a@b.com');
    expect(user.createdAt).toBeInstanceOf(Date);
  });

  runner.it('hashes password', async () => {
    const mockHash = fn().mockResolvedValue('hashed_pw');
    const user = await createUser({ email: 'b@c.com', password: 'secret' }, { hashFn: mockHash });
    expect(mockHash).toHaveBeenCalledWith('secret');
    expect(user.passwordHash).toBe('hashed_pw');
  });

  runner.it('throws on duplicate email', async () => {
    await createUser({ email: 'dup@b.com' });
    await expect(createUser({ email: 'dup@b.com' })).rejects.toThrow('Email already exists');
  });
});

// Benchmark
runner.describe('Performance', () => {
  bench('schema parse', () => UserSchema.parse(rawUser), { iterations: 10_000 });
});

await runner.run();

Display & Device Detection — forge/display

import {
  getDPR, isRetina, is4K, qualityTier, qualityScore, snapshot,
  scaleCanvas, setupWebGLViewport, createVariants, onDPRChange,
} from '@hyperbridge/forge/display';

// Device capabilities
console.log(getDPR());        // 2 (on a Retina display)
console.log(isRetina());      // true  (DPR >= 2)
console.log(is4K());          // false (viewport < 3840)
console.log(qualityScore());  // 72  (0–100)
console.log(qualityTier());   // 'high' ('low'|'medium'|'high'|'ultra')

// Full capability snapshot
const caps = snapshot();
// { dpr: 2, tier: 'high', score: 72, gpu: 'high', width: 1920,
//   height: 1080, hdr: false, saveData: false, webgl2: true, ... }

// Pixel-perfect canvas scaling
const canvas = document.getElementById('my-canvas');
const ctx = scaleCanvas(canvas, 800, 600);
// canvas is now 1600×1200 px (Retina), CSS size is 800×600

// WebGL DPR-aware viewport
const gl = canvas.getContext('webgl2');
setupWebGLViewport(gl, canvas);

// Adaptive asset variants
const { src, srcset } = createVariants('/hero', {
  low:    { suffix: '@1x', ext: 'webp' },
  medium: { suffix: '@1.5x', ext: 'webp' },
  high:   { suffix: '@2x', ext: 'webp' },
  ultra:  { suffix: '@3x', ext: 'avif' },
});
// On a Retina high-tier device: src='/hero@2x.webp'

// React to DPR changes (e.g. drag to external monitor)
onDPRChange(({ from, to }) => {
  console.log(`DPR changed ${from} → ${to}`);
  scaleCanvas(canvas, 800, 600);  // rescale
});

Progressive Web App — forge/pwa

// service-worker.js  (register with navigator.serviceWorker.register('/sw.js'))
import { PWAForge, CacheStrategy, PrecacheManager,
         PushManager, BackgroundSync } from '@hyperbridge/forge/pwa';

const pwa = new PWAForge({ version: '1.0.0', debug: false });

// Precache app shell at install time
const precache = new PrecacheManager(pwa);
precache.add([
  '/', '/app.js', '/app.css', '/offline.html',
  '/icons/icon-192.png', '/icons/icon-512.png',
]);

// Route-level caching strategies
pwa.route('/api/*',   CacheStrategy.NetworkFirst({ timeout: 3000, cacheName: 'api-v1' }));
pwa.route('/images/*', CacheStrategy.CacheFirst({ cacheName: 'images-v1', maxAge: 86400 }));
pwa.route('/*',       CacheStrategy.StaleWhileRevalidate({ cacheName: 'pages-v1' }));

// Web Push
const push = new PushManager(pwa, { vapidPublicKey: VAPID_KEY });
push.onMessage(payload => {
  self.registration.showNotification(payload.title, { body: payload.body, icon: '/icon.png' });
});

// Background Sync — replay failed requests when back online
const sync = new BackgroundSync('api-queue');
sync.register(failedFetchRequest);  // queues the request
// → replayed automatically when connection restores

pwa.install();

forge/client

Fiber-based concurrent UI framework with VDOM, 146+ APIs, priority lanes, hooks, signals, atoms, routing, CSS-in-JS, SSR, and more. Zero dependencies. 12,276 lines.

forge/client uses a fiber-based reconciler with 6 priority lanes for time-sliced rendering. State updates are batched by default -- multiple setState calls within the same microtask produce only a single re-render. Priority lanes range from Immediate (user input) down to Idle (off-screen prefetching), giving you React 18-level scheduling without any external dependencies.

Core Rendering

createElement / h

Create virtual DOM nodes. h(tag, props, ...children) — supports components, fragments, and nested arrays.

render

Mount a virtual tree to a DOM container. Kicks off the fiber reconciler with priority scheduling.

hydrate

Rehydrate server-rendered HTML. Attaches event listeners without re-rendering existing DOM.

Fragment

Group children without adding extra DOM nodes. Use h(Fragment, null, ...children).

createPortal

Render children into a different DOM container. SSR-safe with event bubbling through the virtual tree.

PortalHost

Named portal target component. Place PortalHost in layout, render into it from anywhere.

createErrorBoundary

Catch render errors in child trees. Accepts fallback UI and onError callback.

Suspense

Show fallback UI while async children resolve. Works with lazy() and useSuspenseData().

import { h, render, Fragment } from '@hyperbridge/forge/client';

function App() {
  return h(Fragment, null,
    h('h1', null, 'Hello HBForge'),
    h('p', null, 'Fiber-based concurrent rendering')
  );
}

render(h(App), document.getElementById('root'));

Hooks

useState

Local state. Returns [value, setter]. Setter accepts value or updater function. Auto-batched.

useReducer

Complex state via reducer(state, action). Returns [state, dispatch]. Supports lazy initializer.

useEffect

Run side effects after render. Dependency array controls re-runs. Return cleanup function.

useLayoutEffect

Like useEffect but fires synchronously after DOM mutations. Use for measurements.

useRef

Mutable ref object persisting across renders. Attach to DOM with ref prop.

useMemo

Memoize expensive computations. Re-computes only when dependencies change.

useCallback

Memoize callback functions. Prevents unnecessary child re-renders.

useContext

Read value from nearest Provider of a given context. Re-renders on change.

useId

Generate unique stable IDs. SSR-safe. Useful for form labels and aria attributes.

usePrevious

Track the previous value of a variable across renders.

useTransition

Mark state updates as non-urgent. Returns [isPending, startTransition].

useDeferredValue

Defer a value to keep UI responsive. Low-priority re-render with the new value.

useSyncExternalStore

Subscribe to external stores with concurrent-safe reads. Accepts subscribe + getSnapshot.

import { h, useState, useEffect, useRef } from '@hyperbridge/forge/client';

function Timer() {
  const [seconds, setSeconds] = useState(0);
  const intervalRef = useRef(null);

  useEffect(() => {
    intervalRef.current = setInterval(() => {
      setSeconds(s => s + 1);
    }, 1000);
    return () => clearInterval(intervalRef.current);
  }, []);

  return h('p', null, `Elapsed: ${seconds}s`);
}

Signals & Reactive State

Signals provide fine-grained reactivity outside the VDOM. Unlike hooks, which schedule a component re-render, a signal update surgically patches the DOM node it is bound to. This makes signals ideal for high-frequency updates like counters, progress bars, and real-time data feeds.

ParameterTypeDescription
initialValueTThe starting value of the signal. Can be any serialisable type.
.valueTRead or write the current value. Setting triggers subscribers.
.peek()TRead the current value without subscribing (no tracking).
.subscribe(fn)(value: T) => voidRegister a listener. Returns an unsubscribe function.

signal

Create a reactive signal. signal(initialValue) returns { value, subscribe, peek }. Fine-grained updates bypass VDOM.

computed

Derive a read-only signal from other signals. Auto-tracks dependencies. Lazy evaluation.

useSignal

Use a signal inside a component. Auto-subscribes and re-renders on change.

bindSignalToText

Bind a signal directly to a text node. Updates textContent without re-rendering component.

bindSignalToAttribute

Bind a signal to a DOM attribute. Updates attribute value directly.

bindSignalToStyle

Bind a signal to a CSS style property. Surgical style updates, no reconciliation.

useSignalEffect

Run an effect that auto-tracks signal reads. Re-runs when any tracked signal changes.

import { h, signal, computed, useSignal, bindSignalToText } from '@hyperbridge/forge/client';

const count = signal(0);
const doubled = computed(() => count.value * 2);

function Display() {
  const val = useSignal(doubled);
  return h('p', null, `Doubled: ${val}`);
}

// Direct DOM binding — bypasses VDOM entirely
const el = document.getElementById('counter');
bindSignalToText(count, el);
count.value = 5; // el.textContent instantly becomes "5"

Signals bypass the VDOM entirely. If you bind a signal to DOM via bindSignalToText(), changes will not trigger component re-renders. This is by design for performance, but it means component-level effects (useEffect) won't fire in response to signal changes. Use useSignal() inside components when you need both reactivity and lifecycle integration.

Atoms & Global State

atom

Global state atom. atom(initialValue) returns a subscribable store. Shared across components.

derived

Derive a read-only atom from other atoms. Re-computes when source atoms change.

useAtom

Bind an atom to a component. Returns [value, setValue]. Component re-renders on atom change.

createStore

Create a centralized store with actions and selectors. Supports middleware.

useStore

Subscribe to a store with an optional selector. Only re-renders when selected slice changes.

import { h, atom, derived, useAtom } from '@hyperbridge/forge/client';

const todosAtom = atom([]);
const countAtom = derived([todosAtom], ([todos]) => todos.length);

function TodoList() {
  const [todos, setTodos] = useAtom(todosAtom);
  const [count] = useAtom(countAtom);

  const add = () => setTodos(t => [...t, { text: 'New', done: false }]);

  return h('div', null,
    h('p', null, `${count} items`),
    h('button', { onClick: add }, 'Add Todo'),
    ...todos.map(t => h('p', null, t.text))
  );
}

Async Data & Mutations

useAsync

Fetch async data with built-in SWR cache. Returns { data, loading, error, refetch }. Dependency-driven.

useMutation

Trigger async writes. Returns { mutate, loading, error }. Supports optimistic updates.

invalidateCache

Invalidate cached async data by key. Forces refetch on next access.

useSuspenseData

Throw promise for Suspense integration. Caches resolved data. clearSuspenseCache() to reset.

preloadSuspenseData

Pre-fetch data before component mounts. Warm up the cache for instant renders.

useOptimistic

Optimistic UI updates. Shows predicted state immediately, rolls back on failure.

useSuspenseMutation

Combine Suspense with mutation. Suspends during async operation.

useDeferredMutation

Deferred mutation that keeps UI responsive. Low-priority state update on completion.

createResource

Declarative async resource. createResource(fetcher) returns { read, preload, invalidate }.

useResource

Hook to consume a resource. Suspends until data is available.

useActionState

Form action state. Combines submission state with result tracking.

import { h, useAsync, useMutation, invalidateCache } from '@hyperbridge/forge/client';

function UserProfile({ userId }) {
  const { data: user, loading, error } = useAsync(
    () => fetch(`/api/users/${userId}`).then(r => r.json()),
    [userId]
  );
  const { mutate: updateName, loading: saving } = useMutation(
    (name) => fetch(`/api/users/${userId}`, {
      method: 'PATCH', body: JSON.stringify({ name }),
    })
  );

  if (loading) return h('p', null, 'Loading...');
  if (error) return h('p', null, `Error: ${error.message}`);

  return h('div', null,
    h('h2', null, user.name),
    h('button', {
      onClick: () => updateName('New Name').then(() => invalidateCache()),
      disabled: saving,
    }, saving ? 'Saving...' : 'Rename')
  );
}

Router & Navigation

Router

SPA router with history-based navigation. Supports nested routes, params, query strings, and wildcards.

Link

Declarative navigation link. Prevents full page reload. Supports active class styling.

Navigate

Programmatic redirect component. Renders nothing, navigates on mount.

useRouter

Access router instance. Returns { path, params, query, push, replace, back }.

createFileRouter

File-system-based routing. Automatically creates routes from directory structure.

router.beforeEach

Navigation guard. Called before each route change. Return false to cancel navigation.

router.afterEach

Post-navigation hook. Called after route change completes. Useful for analytics.

router.guard

Per-route guard. Attach auth checks or data loading to individual routes.

RouterWithGuards

Enhanced router component with built-in guard support and navigation middleware.

useGuardedRouter

Hook for guarded router. Access guards, pending state, and navigation methods.

import { h, render, Router, Route, Link, useRouter } from '@hyperbridge/forge/client';

function Home() {
  return h('div', null,
    h('h1', null, 'Home'),
    h(Link, { to: '/users/42' }, 'View User')
  );
}

function UserPage() {
  const { params, back } = useRouter();
  return h('div', null,
    h('h1', null, `User #${params.id}`),
    h('button', { onClick: back }, 'Go Back')
  );
}

const router = new Router([
  new Route('/', Home),
  new Route('/users/:id', UserPage),
]);

// Navigation guards
router.beforeEach((to, from) => {
  if (to.path.startsWith('/admin') && !isLoggedIn()) return false;
});

render(h(router), document.getElementById('root'));

Context & Selectors

createContext

Create a context with default value. Returns { Provider, Consumer } pair.

useContext

Consume context value from nearest Provider ancestor. Re-renders on any change.

useContextSelector

Select a slice of context. Only re-renders when the selected value changes. Prevents wasted renders.

import { h, createContext, useContextSelector } from '@hyperbridge/forge/client';

const AppContext = createContext({ user: null, theme: 'dark', lang: 'en' });

// Only re-renders when user.name changes — not on theme/lang changes
function UserName() {
  const name = useContextSelector(AppContext, ctx => ctx.user?.name);
  return h('span', null, name || 'Guest');
}

Forms & Validation

useFormStatus

Track form submission state. Returns { pending, data, method, action }.

FormStatusProvider

Wrap forms to provide submission status to descendant useFormStatus() calls.

CSS-in-JS

css

Generate scoped className from CSS string. Returns hashed class name. Deduplicates styles.

styled

Create styled components. styled('div', cssString) or styled('button', props => cssString).

globalStyles

Inject global CSS. Returns cleanup function. Useful for resets and base styles.

keyframes

Define CSS @keyframes. Returns animation name. Use with css() or styled().

injectCSSReset

Inject a production CSS reset. Modes: "modern" (Josh Comeau-style) or "normalize".

import { h, css, styled, keyframes, globalStyles } from '@hyperbridge/forge/client';

globalStyles(`body { margin: 0; font-family: system-ui; }`);

const fadeIn = keyframes(`
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
`);

const cardClass = css(`
  padding: 24px;
  border-radius: 12px;
  background: #1a1a2e;
  animation: ${fadeIn} 0.3s ease-out;
`);

const Button = styled('button', `
  padding: 10px 20px;
  border: none;
  border-radius: 6px;
  background: #6366f1;
  color: white;
  cursor: pointer;
  &:hover { background: #4f46e5; }
`);

function Card() {
  return h('div', { className: cardClass },
    h(Button, { onClick: () => alert('Clicked') }, 'Click Me')
  );
}

Styles created with css() are automatically deduped. The same template literal always returns the same className, and the corresponding <style> tag is injected only once. This means you can call css() inside render functions without worrying about style bloat -- the framework fingerprints each rule and reuses existing injections.

Animation Hooks

useSpring

Physics-based spring animation hook. Returns [animatedValue, api]. api.start(), api.stop().

useAnimation

Keyframe animation hook. Define from/to states with easing and duration.

Animated.*

Animated component wrappers. Animated.div, Animated.span, etc. Accept spring/animation values.

usePresence

Track mount/unmount state for exit animations. Returns [isPresent, safeToRemove].

PresenceGroup

Animate children entering and leaving. Keeps exiting children in DOM until animation completes.

useAnimatedMount

Simple mount/unmount animation. Returns { style, mounted }. Handles enter and exit transitions.

import { h, useSpring, Animated } from '@hyperbridge/forge/client';

function BouncyCard() {
  const [style, api] = useSpring({ scale: 1, opacity: 1 });

  return h(Animated.div, {
    style,
    onMouseEnter: () => api.start({ scale: 1.05 }),
    onMouseLeave: () => api.start({ scale: 1 }),
  }, 'Hover me!');
}

Transitions

Transition

Animate a single element entering/leaving. CSS class-based transitions with configurable durations.

TransitionGroup

Manage transitions for a list of elements. Handles enter, update, and exit animations.

Batching & Concurrent

batch

Explicitly batch multiple state updates into a single re-render.

flushSync

Force synchronous flush of pending state updates. Useful for DOM measurements.

useBatchedState

Like useState but auto-batches all updates within a microtask.

enableAutoBatching

Enable automatic microtask-based batching globally. All setState calls are coalesced.

disableAutoBatching

Disable auto-batching. Each setState triggers its own render cycle.

startTransition

Mark updates as low-priority. UI stays responsive while transition processes in background.

useTransitionState

Track transition state. Returns isPending boolean for showing loading indicators.

use() Hook

use

React 19-style hook. use(promise) suspends until resolved. use(context) reads context value. Works in conditionals and loops.

import { h, use, Suspense } from '@hyperbridge/forge/client';

const dataPromise = fetch('/api/data').then(r => r.json());

function DataView() {
  const data = use(dataPromise); // suspends until resolved
  return h('pre', null, JSON.stringify(data, null, 2));
}

function App() {
  return h(Suspense, { fallback: h('p', null, 'Loading...') },
    h(DataView)
  );
}

SSR & Server Components

renderToString

Render component tree to HTML string. Synchronous. For traditional SSR.

renderToReadableStream

Streaming SSR via ReadableStream. Progressive HTML delivery with Suspense boundaries.

selectiveHydrate

Hydrate specific subtrees with priority scheduling. Prioritize visible/interactive regions.

createServerComponent

Define server-only components. Fetches data on server, sends serialized props to client.

Error Boundaries

createErrorBoundary

Basic error boundary. Catches render errors, shows fallback UI.

createEnhancedErrorBoundary

Advanced boundary with retry logic. Configurable maxRetries, onError callback.

createRecoverableErrorBoundary

Auto-recovery with exponential backoff. Retries rendering with increasing delays.

import { h, createRecoverableErrorBoundary } from '@hyperbridge/forge/client';

const SafeZone = createRecoverableErrorBoundary({
  maxRetries: 3,
  fallback: (error, retry) => h('div', null,
    h('p', null, `Error: ${error.message}`),
    h('button', { onClick: retry }, 'Retry')
  ),
  onError: (error) => console.error('Boundary caught:', error),
});

function App() {
  return h(SafeZone, null, h(RiskyComponent));
}

Events & EventBus

EventBus

Global pub/sub event system. new EventBus() creates isolated bus. on(), off(), emit().

eventBus

Default shared EventBus instance. Use for app-wide cross-component communication.

useEvent

Subscribe to EventBus events inside a component. Auto-unsubscribes on unmount.

useEmit

Returns a stable emit function bound to the EventBus. No re-render on emit.

Internationalization (i18n)

createI18n

Create i18n instance with translations and locale. Supports nested keys and interpolation.

I18nProvider

Provide i18n context to component tree. All descendants can access translations.

useI18n

Access translation function t() and locale management. t('key', { params }).

Head & Meta Management

Head

Manage document head. Set title, meta tags, link tags. Supports nesting (last wins).

useCanonical

Set canonical URL meta tag. Useful for SEO deduplication.

useStructuredData

Inject JSON-LD structured data into head. Pass a schema.org-compatible object.

useOpenGraph

Set Open Graph meta tags (og:title, og:image, etc.) for social sharing.

useTwitterCard

Set Twitter Card meta tags (twitter:card, twitter:title, twitter:image).

Theme & Accessibility

createTheme

Define a theme with CSS custom properties. Returns theme object for ThemeProvider.

ThemeProvider

Apply theme to component subtree. Injects CSS variables on the container element.

useTheme

Access current theme values and toggle function inside a component.

useFocusTrap

Trap keyboard focus within a container. Essential for modals and dialogs. Returns ref.

useAnnouncer

Screen reader announcements via ARIA live region. announce(message, priority).

SkipLink

Accessibility skip-to-content link. Visually hidden until focused.

useReducedMotion

Detect prefers-reduced-motion media query. Returns boolean. Respect user preferences.

import { h, createTheme, ThemeProvider, useTheme } from '@hyperbridge/forge/client';

const lightTheme = createTheme({
  bg: '#ffffff', text: '#1a202c', primary: '#6366f1',
});
const darkTheme = createTheme({
  bg: '#0a0a0f', text: '#f1f5f9', primary: '#818cf8',
});

function ThemeToggle() {
  const { theme, setTheme } = useTheme();
  return h('button', {
    onClick: () => setTheme(theme === lightTheme ? darkTheme : lightTheme),
  }, 'Toggle Theme');
}

function App() {
  return h(ThemeProvider, { theme: darkTheme }, h(ThemeToggle));
}

Fetch Client

createFetchClient

Create configured fetch wrapper. Supports baseURL, interceptors, retry, timeout, and middleware.

useFetch

Hook wrapper around fetch client. Returns { data, loading, error, refetch }.

import { createFetchClient } from '@hyperbridge/forge/client';

const api = createFetchClient({
  baseURL: 'https://api.example.com',
  timeout: 5000,
  retry: 3,
  interceptors: {
    request: (config) => {
      config.headers.Authorization = `Bearer ${getToken()}`;
      return config;
    },
    response: (response) => {
      if (response.status === 401) refreshToken();
      return response;
    },
  },
});

const users = await api.get('/users');
await api.post('/users', { name: 'Alice' });

Multi-Tab Sync & Web Vitals

useSyncedState

State synchronized across browser tabs via BroadcastChannel. Atomic updates with conflict resolution.

reportWebVitals

Collect Core Web Vitals: LCP, FID, CLS, TTFB, INP. Calls callback with metric objects.

useWebVitals

Hook to access web vitals inside a component. Returns live metrics object.

import { h, useSyncedState, reportWebVitals } from '@hyperbridge/forge/client';

// Sync cart state across tabs
function CartIcon() {
  const [cartCount, setCartCount] = useSyncedState('cart-count', 0);
  // Updates in one tab instantly reflect in all other tabs
  return h('span', null, `Cart (${cartCount})`);
}

// Report metrics to analytics
reportWebVitals((metric) => {
  console.log(metric.name, metric.value); // "LCP" 1234
  navigator.sendBeacon('/analytics', JSON.stringify(metric));
});

Utility Hooks

useDebounce

Debounce a value. Updates after delay of inactivity. Great for search inputs.

useThrottle

Throttle a value. Updates at most once per interval.

useLocalStorage

Persist state to localStorage. Returns [value, setValue]. Syncs across tabs.

useSessionStorage

Persist state to sessionStorage. Returns [value, setValue]. Cleared on tab close.

useMediaQuery

React to CSS media query changes. Returns boolean match state.

useIntersectionObserver

Observe element visibility via IntersectionObserver. Returns { ref, isIntersecting, entry }.

useEventListener

Attach event listeners declaratively. Auto-cleans up. Supports window, document, or element ref.

useOnClickOutside

Detect clicks outside a referenced element. Useful for dropdown/modal dismiss.

useWindowSize

Track window dimensions. Returns { width, height }. Debounced for performance.

useClipboard

Copy text to clipboard. Returns { copy, copied, error }.

useOnline

Track network status. Returns boolean. Updates on online/offline events.

useDocumentTitle

Set document.title reactively. Restores previous title on unmount.

useHover

Track hover state on an element. Returns [ref, isHovered].

useKeyPress

Detect specific key presses. useKeyPress('Enter') returns boolean.

usePermission

Query browser Permissions API. Returns state: "granted", "denied", or "prompt".

useIdle

Detect user inactivity. Returns isIdle boolean after configurable timeout.

useScrollPosition

Track scroll position. Returns { x, y }. Throttled for performance.

useLongPress

Detect long press/touch gestures. Returns event handlers { onMouseDown, onTouchStart, ... }.

import { h, useLocalStorage, useMediaQuery, useOnClickOutside, useRef }
  from '@hyperbridge/forge/client';

function Dropdown() {
  const ref = useRef(null);
  const [open, setOpen] = useLocalStorage('dropdown-open', false);
  const isMobile = useMediaQuery('(max-width: 768px)');

  useOnClickOutside(ref, () => setOpen(false));

  return h('div', { ref },
    h('button', { onClick: () => setOpen(o => !o) },
      isMobile ? 'Menu' : 'Options'
    ),
    open && h('ul', { className: 'dropdown' },
      h('li', null, 'Profile'),
      h('li', null, 'Settings'),
      h('li', null, 'Logout')
    )
  );
}

Advanced APIs

memo

Memoize a component. Skips re-render when props are shallowly equal.

forwardRef

Forward a ref to a child DOM node. Enables parent to access child's DOM element.

cloneElement

Clone a virtual element with merged props. Useful for HOC patterns.

Children

Utilities for working with children: Children.map, Children.forEach, Children.count, Children.toArray.

lazy

Code-split components. lazy(() => import('./Heavy')) loads on first render with Suspense.

Profiler

Measure render timing. Profiler({ id, onRender }) reports phase, duration, and commit time.

StrictMode

Development checks. Double-invokes effects and renders to catch side-effect bugs.

VirtualList

Windowed list rendering. Only renders visible items. Supports variable item heights.

Slot / SlotProvider

Named content slots. Define slot regions in layout, fill them from child components.

memoDeep

Deep equality memoization. Compares props by value, not reference.

useShallowMemo

Memoize with shallow comparison. Useful for objects/arrays as dependencies.

createSelector

Reselect-style memoized selector. Derived values only recompute when inputs change.

DevTools

enableDevOverlay

Show development overlay with component tree, render counts, and performance metrics.

disableDevOverlay

Hide the development overlay.

getProfilerData

Retrieve collected profiler metrics. Returns render counts, durations, and commit log.

Form Actions

useFormState

Manage form state with server-validated feedback. Returns [state, formAction] where state updates after the server action completes.

useFormAction

Create a form action bound to a server endpoint. Automatically handles serialization, pending state, and error recovery.

ProgressiveForm

Progressive enhancement form component. Works without JavaScript, upgrades to async submission with optimistic UI when JS is available.

import { h, useFormState, useFormAction, ProgressiveForm } from '@hyperbridge/forge/client';

function CreatePost() {
  const [state, formAction] = useFormState('/api/posts', { error: null, success: false });
  const { pending } = useFormAction(formAction);

  return h(ProgressiveForm, { action: formAction },
    h('input', { name: 'title', placeholder: 'Post title', required: true }),
    h('textarea', { name: 'body', placeholder: 'Write your post...' }),
    h('button', { type: 'submit', disabled: pending }, pending ? 'Saving...' : 'Publish'),
    state.error && h('p', { class: 'error' }, state.error),
    state.success && h('p', { class: 'success' }, 'Post created!')
  );
}

Hydration Mismatch Detection

hydrateWithMismatchDetection

Like hydrate() but compares server HTML against client VDOM. Logs detailed diffs of mismatched attributes, text, and tree structure in development.

suppressHydrationWarning

Prop to suppress mismatch warnings on a per-element basis. Use for intentional differences like timestamps or randomized content.

configureHydration

Global configuration for hydration behavior. Options: strict (throw on mismatch), warn (default), silent, and onMismatch callback.

import { h, hydrateWithMismatchDetection, configureHydration } from '@hyperbridge/forge/client';

configureHydration({
  mode: 'strict',                     // 'strict' | 'warn' | 'silent'
  onMismatch(path, expected, actual) {
    reportToMonitoring({ path, expected, actual });
  }
});

function App() {
  return h('div', null,
    h('h1', null, 'My App'),
    h('span', { suppressHydrationWarning: true }, new Date().toLocaleTimeString())
  );
}

hydrateWithMismatchDetection(h(App), document.getElementById('root'));

Enhanced Server Components

createServerAction

Define a server-side action callable from client components. Handles serialization, CSRF, and streaming response automatically.

ServerBoundary

Mark a subtree as server-rendered. Children are streamed as HTML and hydrated on the client. Supports fallback UI during loading.

registerClientComponent

Explicitly register a component for client-side rendering within a server component tree. Creates a hydration island with its own state.

import { h, createServerAction, ServerBoundary, registerClientComponent } from '@hyperbridge/forge/client';

// Server action — runs on the server, callable from client
const saveUser = createServerAction(async (formData) => {
  const user = await db.users.update(formData.id, {
    name: formData.name,
    email: formData.email
  });
  return { success: true, user };
});

// Client island inside a server-rendered page
const LikeButton = registerClientComponent(function LikeButton({ postId }) {
  const [liked, setLiked] = useState(false);
  return h('button', { onClick: () => setLiked(!liked) }, liked ? 'Liked' : 'Like');
});

function UserProfile({ userId }) {
  return h(ServerBoundary, { fallback: h('p', null, 'Loading profile...') },
    h('div', { class: 'profile' },
      h('h2', null, 'User Profile'),
      h(LikeButton, { postId: userId })
    )
  );
}

Auto-Memoization

enableAutoMemo

Enable automatic memoization for all function components. The framework wraps each component in a shallow-equality memo check, eliminating unnecessary re-renders.

disableAutoMemo

Disable automatic memoization globally. Useful for debugging or when manual control over memo behavior is preferred.

AutoMemoProvider

Scoped auto-memoization provider. Wraps a subtree to enable or disable auto-memo independently of the global setting.

import { h, render, enableAutoMemo, disableAutoMemo, AutoMemoProvider } from '@hyperbridge/forge/client';

// Enable globally — all components are auto-memoized
enableAutoMemo();

function ExpensiveList({ items }) {
  return h('ul', null, items.map(item =>
    h('li', { key: item.id }, item.name)
  ));
}

function App() {
  return h('div', null,
    h(ExpensiveList, { items: data }),            // auto-memoized
    h(AutoMemoProvider, { enabled: false },
      h(AlwaysRerenderWidget, null)               // opts out of auto-memo
    )
  );
}

render(h(App), document.getElementById('root'));

Enhanced useOptimistic

useOptimisticAction

Combine optimistic state with a server action. Immediately applies the optimistic update, then reconciles when the server responds or rolls back on error.

useOptimisticMutation

Optimistic mutations for list data. Supports add, update, and remove operations with automatic rollback and conflict resolution.

import { h, useOptimisticAction, useOptimisticMutation } from '@hyperbridge/forge/client';

function TodoList() {
  const [todos, addTodo] = useOptimisticMutation('/api/todos', {
    add(current, newTodo) {
      return [...current, { ...newTodo, id: 'temp-' + Date.now(), pending: true }];
    },
    rollback(current, failedTodo) {
      return current.filter(t => t.id !== failedTodo.id);
    }
  });

  const [likeState, likeAction] = useOptimisticAction(
    '/api/like',
    (prev, postId) => ({ ...prev, [postId]: (prev[postId] || 0) + 1 })
  );

  return h('div', null,
    h('button', { onClick: () => addTodo({ title: 'New task' }) }, 'Add Todo'),
    h('ul', null, todos.map(todo =>
      h('li', { key: todo.id, class: todo.pending ? 'pending' : '' }, todo.title)
    ))
  );
}

Test Utilities

renderForTest

Render a component into a test container. Returns a cleanup function and the container element. Automatically runs inside an act() scope.

screen

Query helpers for the test DOM: screen.getByText, screen.getByRole, screen.queryByTestId, screen.findByLabelText (async).

fireEvent

Dispatch DOM events in tests. fireEvent.click(el), fireEvent.change(el, { target: { value } }), fireEvent.submit(form).

waitForUpdate

Async utility that resolves after the next render cycle completes. Useful for testing state transitions and async effects.

act

Wrap test actions that trigger state updates. Ensures all effects and re-renders complete before assertions.

import { h } from '@hyperbridge/forge/client';
import { renderForTest, screen, fireEvent, waitForUpdate, act } from '@hyperbridge/forge/client/test';

function Counter() {
  const [count, setCount] = useState(0);
  return h('div', null,
    h('span', { 'data-testid': 'count' }, String(count)),
    h('button', { onClick: () => setCount(c => c + 1) }, 'Increment')
  );
}

// Test
const { cleanup } = renderForTest(h(Counter));

expect(screen.getByTestId('count').textContent).toBe('0');

await act(async () => {
  fireEvent.click(screen.getByText('Increment'));
  await waitForUpdate();
});

expect(screen.getByTestId('count').textContent).toBe('1');
cleanup();

Hot Module Replacement

createHMRClient

Initialize the HMR WebSocket client. Connects to the dev server and listens for module update events.

hot.accept

Accept hot updates for the current module or specific dependencies. Callback receives the updated module for custom re-rendering logic.

hot.dispose

Register a cleanup function that runs before the module is replaced. Use to tear down subscriptions, timers, or side effects.

preserveStateOnHMR

HOC that preserves component state across hot reloads. Wraps a component to persist its useState and useReducer values during development.

import { h, render, preserveStateOnHMR } from '@hyperbridge/forge/client';
import { createHMRClient } from '@hyperbridge/forge/client/hmr';

const hmr = createHMRClient({ port: 3001 });

const App = preserveStateOnHMR('App', function App() {
  const [count, setCount] = useState(0);
  return h('div', null,
    h('p', null, `Count: ${count}`),
    h('button', { onClick: () => setCount(c => c + 1) }, '+1')
  );
});

render(h(App), document.getElementById('root'));

// Accept hot updates and re-render
if (hmr.hot) {
  hmr.hot.accept('./App', (updatedModule) => {
    render(h(updatedModule.default), document.getElementById('root'));
  });

  hmr.hot.dispose(() => {
    console.log('Cleaning up before hot reload...');
  });
}

forge/server

Full-featured HTTP framework — radix-trie router, 20+ built-in middleware, WebSocket (RFC 6455), GraphQL engine, cron scheduler, task queue, JWT auth, circuit breaker, LRU cache, HTTP client, PubSub, plugin system, DI container, OpenTelemetry tracing, Prometheus metrics, HTTP/2, cluster mode, and more. 120+ APIs, zero dependencies, 11,177 lines.

forge/server uses a radix-trie router for O(k) path matching (k = path length), significantly faster than Express's linear middleware scan. With 500 registered routes, Express checks up to 500 regex patterns per request; forge/server walks the trie in at most ~5 node hops. Combined with zero-copy body parsing and pre-compiled middleware chains, this typically yields 2-5x higher throughput than Express on the same hardware.

OptionTypeDescription
portnumberPort to listen on. Default: 3000. Overridden by PORT env var.
hoststringBind address. Default: '0.0.0.0'. Use '127.0.0.1' for local-only.
trustProxyboolean | numberTrust X-Forwarded-* headers. true trusts one hop; a number sets the hop count.
maxBodySizestringMaximum request body size. Default: '1mb'. Accepts '10kb', '5mb', etc.
clusternumber | booleanNumber of worker processes. true = CPU count. Only used with Forge.cluster().

1. Core

Forge

Main application class. Extends EventEmitter. Owns router, middleware stack, cron, task queue, health checks, and shutdown hooks.

app.listen(port, host?, cb?)

Start an HTTP server. Auto-wires WebSocket, cron, graceful shutdown, and GraphQL if configured.

app.listenTLS(port, tlsOpts, host?, cb?)

Start an HTTPS server with the given TLS cert and key options.

app.use(...middleware)

Register global middleware. Accepts optional path prefix: app.use('/api', cors()).

app.set(key, value) / app.get(key)

Application settings store. When get is called with a non-path string, returns the setting value.

app.onShutdown(fn)

Register an async cleanup hook. Runs on SIGTERM/SIGINT before the process exits.

app.onError(handler)

Custom error handler: (err, req, res) => { ... }. Receives ForgeError instances.

app.close(cb?)

Stop the server and cron scheduler. Runs onClose plugin hooks.

Forge.cluster(workerFn, opts?)

Static method. Forks workers using Node cluster. Auto-restarts crashed workers. opts.workers defaults to CPU count.

ForgeError

Custom error class with status, message, and optional details. Serializes to JSON via toJSON().

import { Forge } from '@hyperbridge/forge/server';

const app = new Forge();
app.set('env', 'production');

app.onShutdown(async () => {
  await db.close();
  console.log('Cleanup complete');
});

app.get('/api/status', (req, res) => {
  res.json({ status: 'ok', uptime: process.uptime() });
});

app.listen(3000, () => console.log('Running'));
// Cluster mode — spawns one worker per CPU core
Forge.cluster(() => {
  const app = new Forge();
  app.get('/', (req, res) => res.json({ pid: process.pid }));
  app.listen(3000);
}, { workers: 4 });

2. Router

Router(prefix?)

Radix-trie based router. Supports static, parameterized (:id), optional (:id?), and wildcard (*) segments.

HTTP Methods

app.get, app.post, app.put, app.delete, app.patch, app.options, app.head, app.all — register route handlers directly on Forge or Router.

Route Params & Wildcards

/users/:id populates req.params.id. Wildcard /files/* captures the rest in req.params['*'].

Named Routes

Pass { name: 'getUser' } as option. Generate URLs with router.url('getUser', { id: 42 }).

app.group(prefix, fn)

Route groups with shared prefix and middleware. Supports nested groups. Methods: get, post, put, delete, patch, all, use.

app.version(ver, routerOrFn)

API versioning via URL prefix (/v2/...) or Accept header (application/vnd.api.v2+json).

Per-Route Middleware

Stack middleware before the handler: app.get('/admin', authMw, adminMw, handler).

405 Method Not Allowed

Auto-detected when a path matches but the HTTP method does not. Returns Allow header with valid methods.

const app = new Forge();

// Route params
app.get('/users/:id', (req, res) => {
  res.json({ userId: req.params.id });
});

// Wildcard catch-all
app.get('/files/*', (req, res) => {
  res.json({ path: req.params['*'] });
});

// Route groups with shared auth middleware
app.group('/api/v1', (g) => {
  g.use(authMiddleware);
  g.get('/users', listUsers);
  g.post('/users', createUser);
  g.group('/admin', (admin) => {
    admin.get('/stats', getStats);
  });
});

app.listen(3000);

3. Middleware (21+)

bodyParser.json(opts?)

Parse JSON request bodies. opts.limit (default '1mb'). Sets req.body.

bodyParser.urlencoded(opts?)

Parse URL-encoded form bodies. Sets req.body.

bodyParser.raw(opts?)

Collect raw request body as a Buffer. opts.limit default '5mb'.

bodyParser.multipart(opts?)

Parse multipart/form-data. Sets req.body (fields) and req.files (uploaded files).

cors(opts?)

CORS headers. origin (string, function, or '*'), methods, allowedHeaders, credentials, maxAge. Auto-handles OPTIONS preflight.

helmet(opts?)

Security headers: X-Content-Type-Options, X-Frame-Options, HSTS, Referrer-Policy, Permissions-Policy. Optional csp for Content-Security-Policy.

logger(opts?)

Colored request logger with method, path, status, duration, size, and request ID. opts.level: error, warn, info, debug.

rateLimit(opts?)

Sliding-window rate limiter. windowMs, max, keyGenerator, skip. Sets X-RateLimit-* and Retry-After headers.

rateLimitAdvanced(opts?)

Four strategies: fixed-window, sliding-window, token-bucket, leaky-bucket. keyBy: 'ip' | 'user' | function.

compression(opts?)

Gzip/deflate response compression. opts.threshold (default 1024 bytes) — responses below this size are not compressed.

serveStatic(rootDir, opts?)

Serve static files with ETag, 304 caching, range requests, directory index, and dotfile protection. opts.maxAge, opts.index.

session(opts?)

Cookie-based sessions with in-memory store. opts.secret, opts.maxAge, opts.name. Auto-cleanup of expired sessions.

upload(opts?)

File upload middleware. opts.maxSize, opts.types (allowed MIME types), opts.dest. Populates req.files.

proxy(target, opts?)

Reverse proxy. pathRewrite, changeOrigin, xfwd (X-Forwarded-*), timeout. Streams request/response bodies.

wsProxy(target, opts?)

WebSocket reverse proxy. Tunnels upgrade requests to a target WebSocket server.

healthProbe(checkFn?)

Responds on GET /health with status, uptime, and timestamp. Optional async check function.

readyProbe(checkFn?)

Responds on GET /ready. Returns 200 if ready, 503 if not. For Kubernetes readiness probes.

correlationId(opts?)

Extracts or generates req.id / req.correlationId from X-Request-Id header. Propagates to response.

signedCookies(secret)

HMAC-SHA256 signed cookie verification. Populates req.signedCookies. Tampered cookies are silently rejected.

etag(opts?)

Auto-generate ETag headers (SHA-256). Supports If-None-Match and If-Modified-Since for 304 responses. opts.weak for weak ETags.

timeout(ms)

Request timeout middleware. Returns 408 if handler exceeds ms. Exposes req.timedOut() check.

decompress(opts?)

Decompress incoming request bodies (gzip, deflate, brotli). Place before body parsers.

cacheMiddleware(opts?)

LRU-backed GET response cache with ETag and X-Cache HIT/MISS headers. opts.max, opts.ttl.

apiVersion(version, router)

Mount versioned routes via URL prefix or Accept header versioning.

import { Forge, bodyParser, cors, helmet, logger, rateLimit,
         compression, serveStatic, timeout } from '@hyperbridge/forge/server';

const app = new Forge();

// Middleware stack
app.use(logger({ level: 'info' }));
app.use(cors({ origin: 'https://myapp.com', credentials: true }));
app.use(helmet({ csp: "default-src 'self'" }));
app.use(compression({ threshold: 512 }));
app.use(rateLimit({ windowMs: 60000, max: 100 }));
app.use(timeout(30000));
app.use(bodyParser.json({ limit: '2mb' }));
app.use(serveStatic('./public', { maxAge: 86400 }));

app.get('/api/data', (req, res) => res.json({ ok: true }));
app.listen(3000);

rateLimit Options

OptionTypeDescription
windowMsnumberTime window in milliseconds. Default: 60000 (1 minute).
maxnumberMax requests per window per key. Default: 100.
messagestring | objectResponse body when limit is exceeded. Default: { error: 'Too many requests' }.
keyGenerator(req) => stringFunction that returns the rate-limit key. Default: req.ip.
algorithmstringRate limiting strategy: 'sliding-window' (default), 'fixed-window', 'token-bucket', 'leaky-bucket'.
skip(req) => booleanReturn true to bypass rate limiting for this request (e.g. health checks).

4. WebSocket

WebSocketServer

Full RFC 6455 WebSocket server. Extends EventEmitter. Auto-wires on upgrade event. Handles text/binary frames, ping/pong, and close handshake.

ws.broadcast(data, exclude?)

Send a message to all connected clients. Optionally exclude a specific client.

ws.to(room).emit(data)

Send a message to all clients in a named room.

ws.join(client, room) / ws.leave(client, room)

Room management. Add or remove a WebSocket client from a named room.

WebSocketClient

Per-connection wrapper. Properties: id (UUID), readyState, req. Methods: send(data), close(). Events: message, close, error.

import { Forge, WebSocketServer } from '@hyperbridge/forge/server';

const app = new Forge();

app.ws((wss) => {
  wss.on('connection', (client, req) => {
    console.log(`Client ${client.id} connected`);

    wss.join(client, 'lobby');

    client.on('message', (data) => {
      // Broadcast to room
      wss.to('lobby').emit({ type: 'chat', ...data });
    });

    client.on('close', () => {
      wss.leave(client, 'lobby');
    });
  });
});

app.listen(3000);

WebSocket connections are per-worker in cluster mode. A client connected to worker 2 will not receive messages broadcast from worker 1. Use the built-in PubSub adapter to broadcast across all workers -- it coordinates via the primary process's IPC channel, so messages fan out to every worker's connected clients automatically.

5. GraphQL Engine

GraphQLEngine

Built-in GraphQL engine. Parses SDL schemas, supports Query, Mutation, enums, scalars, input types, aliases, variables, and introspection.

app.graphql(schema, resolvers, opts?)

Register a GraphQL endpoint on the app. Default path: /graphql. Supports both GET and POST.

engine.middleware(endpoint?)

Standalone GraphQL middleware for use with app.use().

engine.subscription(name, { subscribe, resolve? })

Register a subscription resolver with async generator. Supports graphql-ws protocol over WebSocket.

Fragment Support

Use fragment UserFields on User { ... } and spread with ...UserFields in queries.

@deprecated Directive

Mark fields as deprecated in the schema: oldField: String @deprecated(reason: "Use newField"). Reported in introspection.

DataLoader

Batch and cache data fetches to prevent N+1 queries. Methods: load(key), loadMany(keys), clear(key), clearAll(), prime(key, value).

import { Forge, DataLoader } from '@hyperbridge/forge/server';

const app = new Forge();

const schema = `
  type User { id: ID!, name: String!, email: String }
  type Query {
    user(id: ID!): User
    users: [User!]!
  }
  type Mutation {
    createUser(name: String!, email: String!): User!
  }
`;

const userLoader = new DataLoader(async (ids) => {
  const users = await db.findByIds(ids);
  return ids.map(id => users.find(u => u.id === id) || null);
});

app.graphql(schema, {
  Query: {
    user: (_, { id }) => userLoader.load(id),
    users: () => db.allUsers(),
  },
  Mutation: {
    createUser: (_, { name, email }) => db.createUser({ name, email }),
  },
});

app.listen(3000); // GraphQL at POST /graphql

6. CronScheduler

CronScheduler

Built-in cron scheduler. Parses 5-field cron expressions (min, hour, day, month, weekday). Supports ranges, steps, and lists.

cron.schedule(name, expression, fn)

Schedule a named job. Concurrent execution is prevented — running jobs are skipped.

cron.cancel(name) / cron.cancelAll()

Cancel a specific job or all scheduled jobs.

app.cron(name, expression, fn)

Shortcut to schedule a cron job on the Forge app instance.

import { Forge } from '@hyperbridge/forge/server';

const app = new Forge();

// Run every day at 2:00 AM
app.cron('cleanup', '0 2 * * *', async () => {
  await db.cleanup();
  console.log('Daily maintenance complete');
});

// Run every 5 minutes
app.cron('sync', '*/5 * * * *', async () => {
  await syncExternalData();
});

app.listen(3000);

7. TaskQueue

TaskQueue(opts?)

Async task queue with configurable concurrency. opts.concurrency (default 5). Extends EventEmitter.

queue.register(name, handler)

Register a named task handler function.

queue.add(name, data, opts?)

Enqueue a task. Options: priority, delay (ms), maxRetries (default 3). Returns the task object with UUID.

queue.persist(filepath) / queue.restore()

Enable file-based persistence. Restore re-queues pending and retryable jobs on restart.

queue.history(limit?) / queue.stats()

View completed/failed history (ring buffer, max 1000). Stats: pending, running, completed, failed, total.

Events

added, processing, completed, failed, retry — listen for task lifecycle events.

import { TaskQueue } from '@hyperbridge/forge/server';

const queue = new TaskQueue({ concurrency: 10 });
queue.persist('./data/jobs.json').restore();

queue.register('sendEmail', async (data) => {
  await emailService.send(data.to, data.subject, data.body);
});

queue.on('failed', (task, err) => {
  console.error(`Task ${task.id} failed: ${err.message}`);
});

await queue.add('sendEmail', {
  to: 'user@example.com',
  subject: 'Welcome',
  body: 'Hello!'
}, { priority: 10, maxRetries: 5 });

8. JWT

JWT(secret, opts?)

JWT signing and verification. opts.algorithm (HS256), opts.expiresIn (seconds, default 3600).

jwt.sign(payload, opts?)

Create a signed token. Auto-adds iat, exp, jti. Optional issuer, audience.

jwt.verify(token)

Verify and decode a token. Checks signature (timing-safe) and expiration. Throws ForgeError on failure.

jwt.middleware(opts?)

Express-style middleware. Extracts Bearer token from Authorization header, sets req.user. opts.optional skips auth if no token.

import { Forge, JWT } from '@hyperbridge/forge/server';

const jwt = new JWT(process.env.JWT_SECRET, { expiresIn: 7200 });
const app = new Forge();

app.post('/auth/login', async (req, res) => {
  const user = await authenticate(req.body);
  const token = jwt.sign({ id: user.id, role: user.role });
  res.json({ token });
});

// Protected route
app.get('/api/profile', jwt.middleware(), (req, res) => {
  res.json({ user: req.user });
});

app.listen(3000);

9. Validator & Schema

Validator.body(schema) / .query(schema) / .params(schema)

Middleware factories. Schema is a plain object: { field: { required, type, min, max, pattern, enum, email, custom } }.

v.string()

Zod-like string schema. Chain: min, max, email, url, regex, nonempty, trim, toLowerCase, toUpperCase, includes, startsWith, endsWith, length, enum.

v.number()

Number schema. Chain: min, max, int, positive, negative, nonnegative, multipleOf, finite, enum.

v.boolean() / v.enum(values)

Boolean coercion (accepts "true"/"false" strings). Enum validates against a fixed set of values.

v.array(itemSchema?) / v.object(shape)

Array with optional item validation. Object validates nested shape recursively. Chain: min, max, nonempty, length.

schema.optional() / .default(val) / .transform(fn) / .label(name)

Common modifiers on all schema types. transform mutates the parsed value. label sets the field name in error messages.

objectSchema.bodyParser()

Middleware factory from an ObjectSchema. Validates req.body and replaces it with the parsed, transformed result.

import { Forge, v } from '@hyperbridge/forge/server';

const app = new Forge();

const createUserSchema = v.object({
  name: v.string().min(2).max(100),
  email: v.string().email(),
  age: v.number().int().min(18).optional(),
  role: v.enum(['admin', 'user']).default('user'),
});

app.post('/users', createUserSchema.bodyParser(), (req, res) => {
  // req.body is validated and transformed
  res.status(201).json(req.body);
});

app.listen(3000);

10. CircuitBreaker

CircuitBreaker(fn, opts?)

Wraps an async function. States: closed (normal), open (failing), half-open (probing). opts.threshold (5), opts.timeout (10s), opts.resetTimeout (30s).

breaker.fire(...args)

Call the protected function. Throws ForgeError(503) when open. Transitions to half-open after reset timeout.

breaker.state / breaker.getStats() / breaker.reset()

Read current state, get failure/success counts, or manually reset to closed.

Events

success, failure, open, halfOpen, close, rejected — monitor circuit state transitions.

import { CircuitBreaker } from '@hyperbridge/forge/server';

const breaker = new CircuitBreaker(
  (url) => fetch(url).then(r => r.json()),
  { threshold: 3, timeout: 5000, resetTimeout: 15000 }
);

breaker.on('open', () => console.warn('Circuit opened!'));

try {
  const data = await breaker.fire('https://api.example.com/data');
} catch (err) {
  // Fallback logic when circuit is open
}

11. LRU Cache

LRUCache(opts?)

Doubly-linked-list LRU cache. opts.max (1000), opts.ttl (ms, 0 = no expiry). O(1) get/set/delete.

cache.get(key) / cache.set(key, value)

Get promotes to head. Set evicts the least-recently-used item when capacity is exceeded.

cache.has(key) / cache.delete(key) / cache.clear()

Check existence, remove a key, or clear all entries. TTL-expired entries return false/undefined.

cache.size / cache.keys() / cache.values()

Current entry count, list all keys, or list all values.

import { LRUCache } from '@hyperbridge/forge/server';

const cache = new LRUCache({ max: 500, ttl: 60000 });

cache.set('user:42', { id: 42, name: 'Alice' });
const user = cache.get('user:42'); // { id: 42, name: 'Alice' }
console.log(cache.size); // 1

12. PubSub

PubSub

In-process publish/subscribe with topic strings and wildcard pattern matching (e.g. user.*).

pubsub.subscribe(topic, fn)

Subscribe to a topic or pattern. Returns an unsubscribe function. Handler receives (data, topic).

pubsub.publish(topic, data)

Publish data to all matching subscribers. Returns the number of handlers invoked.

pubsub.unsubscribe(topic, fn) / pubsub.clear() / pubsub.topics()

Remove a handler, clear all subscriptions, or list active topics.

import { PubSub } from '@hyperbridge/forge/server';

const pubsub = new PubSub();

// Wildcard pattern
pubsub.subscribe('order.*', (data, topic) => {
  console.log(`Event ${topic}:`, data);
});

pubsub.publish('order.created', { id: 1, total: 99.99 }); // matches
pubsub.publish('order.shipped', { id: 1 });                // matches

13. HTTP Client

httpClient.request(url, opts?)

Full-featured HTTP client. Auto-serializes JSON body, auto-parses JSON responses, follows redirects (max 5), basic auth, timeout (30s).

httpClient.get / .post / .put / .patch / .delete

Shorthand methods. post(url, body, opts?) etc. Returns { status, statusText, headers, data, raw }.

import { httpClient } from '@hyperbridge/forge/server';

const { data } = await httpClient.get('https://api.example.com/users', {
  headers: { Authorization: 'Bearer ...' },
  timeout: 5000,
});

await httpClient.post('https://api.example.com/users', {
  name: 'Alice', email: 'alice@example.com'
});

14. Plugin System

app.register(plugin, opts?)

Register a plugin. Plugin is { name, dependencies?, register(scope, opts) } or a plain function. Supports dependency resolution.

PluginScope

Encapsulated scope passed to the register function. Delegates all HTTP methods, use(), group() to the parent app. Adds scoped hooks.

scope.addHook(event, handler)

Hook events: onRequest, onResponse, onError, onClose. Hooks are merged into the global lifecycle.

scope.decorate(key, value)

Add a named property to the app instance. Throws if the key already exists.

scope.decorateRequest(key, value) / scope.decorateReply(key, value)

Extend request or response objects with custom properties. Applied automatically on each request.

app.hasPlugin(name) / app.listPlugins()

Check if a plugin is registered or list all registered plugins with their dependencies.

import { Forge } from '@hyperbridge/forge/server';

const authPlugin = {
  name: 'auth',
  async register(app, opts) {
    app.decorate('authenticate', (req) => { /* ... */ });
    app.addHook('onRequest', async (req, res) => {
      req.user = await app.authenticate(req);
    });
    app.get('/auth/me', (req, res) => res.json(req.user));
  }
};

const app = new Forge();
await app.register(authPlugin, { secret: 'my-secret' });
app.listen(3000);

Plugins are encapsulated by default. A plugin's decorators and hooks don't leak to sibling plugins -- they only affect the plugin's own scope and its children. This means two plugins can safely decorate req with the same key name without conflicting. If you need a decorator to be globally visible, register it on the root app instance instead of the plugin scope.

15. Dependency Injection

createContainer()

Create a DI container. Supports singleton, transient, and scoped lifetimes.

container.register(name, factory, opts?)

Register a service factory. opts.lifetime: 'singleton' (default), 'transient', 'scoped'. opts.deps for explicit dependency list.

container.registerValue(name, value)

Register a constant value (always singleton). Immediately cached.

container.resolve(name)

Resolve a service. Auto-injects dependencies by introspecting factory parameter names. Detects circular dependencies.

container.createScope()

Create a child scope. Scoped services are fresh within the child; singletons are shared with the parent.

container.middleware()

Middleware that creates a scoped container per request at req.container.

import { createContainer, Forge } from '@hyperbridge/forge/server';

const container = createContainer();
container.register('logger', () => new Logger(), { lifetime: 'singleton' });
container.register('userService', (logger) => new UserService(logger));
container.register('requestCtx', () => new RequestContext(), { lifetime: 'scoped' });

const app = new Forge();
app.use(container.middleware());

app.get('/users', (req, res) => {
  const svc = req.container.resolve('userService');
  res.json(svc.findAll());
});

app.listen(3000);

16. OpenTelemetry Tracing

createTracer(name, opts?)

Create a Tracer instance. opts.maxSpans (10000), opts.sampler (function returning true/false).

tracer.startSpan(name, opts?)

Create a new span. Inherits parent from AsyncLocalStorage context. opts.parentSpanId, opts.attributes.

tracer.trace(name, fn, opts?)

Start a span, run an async function within its context, auto-end and set status. Records exceptions on throw.

span.setAttribute / .setAttributes / .addEvent / .recordException / .end()

Full OpenTelemetry Span API. Duration computed from high-resolution timers. Status codes: 0=UNSET, 1=OK, 2=ERROR.

tracer.middleware()

Auto-trace HTTP requests. Extracts W3C traceparent header for distributed context propagation. Sets req._traceSpan.

tracer.export() / tracer.flush() / tracer.addExporter(fn)

Export spans as JSON, flush and clear, or add a real-time exporter callback.

import { createTracer, Forge } from '@hyperbridge/forge/server';

const tracer = createTracer('my-service');
const app = new Forge();
app.use(tracer.middleware());

app.get('/api/data', async (req, res) => {
  const result = await tracer.trace('fetchData', async (span) => {
    span.setAttribute('db.system', 'postgres');
    const data = await db.query('SELECT * FROM items');
    span.addEvent('query-complete', { rowCount: data.length });
    return data;
  });
  res.json(result);
});

app.listen(3000);

17. Prometheus Metrics

createMetrics(opts?)

Create a MetricsRegistry. opts.prefix, opts.defaultLabels, opts.collectDefaults (process CPU, memory, Node version).

metrics.counter({ name, help, labels? })

Prometheus Counter. Methods: inc(labels?, value?), get(labels?), reset().

metrics.gauge({ name, help, labels? })

Prometheus Gauge. Methods: set(labels, value), inc(labels?, value?), dec(labels?, value?), get(labels?).

metrics.histogram({ name, help, buckets? })

Prometheus Histogram with configurable buckets. observe(labels, value), startTimer(labels?) returns an end function.

metrics.middleware()

Auto-tracks http_request_duration_seconds, http_requests_total, and http_requests_in_flight with method/route/status labels.

metrics.endpoint(path?) / metrics.collect()

Expose GET /metrics in Prometheus text format. collect() returns the raw text output.

import { createMetrics, Forge } from '@hyperbridge/forge/server';

const metrics = createMetrics({ prefix: 'myapp_' });
const app = new Forge();

app.use(metrics.middleware());
app.use(metrics.endpoint('/metrics'));

const orderCounter = metrics.counter({
  name: 'orders_total',
  help: 'Total orders placed',
});

app.post('/orders', (req, res) => {
  orderCounter.inc({ status: 'created' });
  res.status(201).json({ ok: true });
});

app.listen(3000); // Prometheus scrapes GET /metrics

18. Webhook Verification

verifyWebhook(opts)

Middleware for HMAC signature verification. opts.secret, opts.algorithm ('sha256'|'sha512'), opts.header, opts.tolerance (seconds for replay protection).

signWebhook(payload, secret, opts?)

Generate a webhook signature for testing or sending. Returns { signature, timestamp, body }.

Custom Parsing

Pass opts.parse to handle custom header formats (e.g. Stripe's t=...,v1=... format). req.webhookVerified is set on success.

import { Forge, verifyWebhook } from '@hyperbridge/forge/server';

const app = new Forge();

app.post('/webhooks/stripe',
  verifyWebhook({
    secret: process.env.STRIPE_SECRET,
    header: 'stripe-signature',
    tolerance: 300,
    parse: (header) => {
      const parts = {};
      header.split(',').forEach(p => {
        const [k, val] = p.split('=');
        parts[k] = val;
      });
      return { timestamp: parts.t, signature: parts.v1 };
    },
  }),
  (req, res) => {
    console.log('Verified webhook:', req.body);
    res.json({ received: true });
  }
);

app.listen(3000);

19. Enhanced Sessions

sessionEnhanced(opts?)

Enhanced session middleware with pluggable stores, session rotation (opts.rotateInterval), flash messages, and req.session.regenerate() / destroy().

MemorySessionStore

Default in-memory store with auto-cleanup of expired sessions. Interface: get, set, destroy, touch, clear.

FileSessionStore(opts?)

File-based persistence. opts.dir, opts.cleanupInterval. Each session is a separate JSON file.

RedisSessionStore(opts?)

Redis-protocol TCP client. opts.host, opts.port, opts.password, opts.db, opts.prefix. Uses RESP protocol directly — no Redis library needed.

Flash Messages

req.flash('error', 'Bad input') to set; req.flash('error') to read and clear. Enabled by default.

import { Forge, sessionEnhanced, createRedisSessionStore }
  from '@hyperbridge/forge/server';

const app = new Forge();

app.use(sessionEnhanced({
  secret: 'session-secret',
  maxAge: 86400,
  store: createRedisSessionStore({ host: '127.0.0.1', port: 6379 }),
  rotateInterval: 3600,
}));

app.post('/login', async (req, res) => {
  req.session.userId = user.id;
  await req.session.regenerate(); // new session ID, same data
  res.json({ ok: true });
});

app.get('/dashboard', (req, res) => {
  const error = req.flash('error');
  res.json({ userId: req.session.userId, flashError: error });
});

20. Backpressure

backpressure(opts?)

Limits concurrent requests with overflow queueing. maxConcurrent (100), maxQueue (500), queueTimeout (10s). Returns 503 when overloaded.

opts.strategy

Queue strategy: 'fifo' (default), 'lifo', or 'priority'. Use opts.getPriority(req) for priority mode.

middleware.stats()

Live snapshot: { active, queued, maxConcurrent, maxQueue, totalProcessed, totalShed, totalQueued, totalTimedOut }.

import { Forge, backpressure } from '@hyperbridge/forge/server';

const bp = backpressure({
  maxConcurrent: 50,
  maxQueue: 200,
  queueTimeout: 5000,
  strategy: 'priority',
  getPriority: (req) => req.headers['x-priority'] === 'high' ? 10 : 0,
});

const app = new Forge();
app.use(bp);

app.get('/stats', (req, res) => res.json(bp.stats()));
app.listen(3000);

21. Config Management

createConfig(schema)

Define a typed config schema. Each field specifies type, default, env (env var name), required, enum, min, max, pattern, validate.

config.load(opts?)

Load from multiple sources in priority order: env vars > env-specific JSON > JSON file > .env file > defaults. Validates against schema.

config.get(keyPath) / config.set(keyPath, value) / config.has(keyPath)

Dot-notation access: config.get('db.host'). Supports nested objects.

config.env / config.isProduction / config.isDevelopment / config.isTest

Environment helpers. config.env reads NODE_ENV.

import { createConfig } from '@hyperbridge/forge/server';

const config = createConfig({
  port: { type: 'number', default: 3000, env: 'PORT' },
  db: {
    host: { type: 'string', default: 'localhost', env: 'DB_HOST' },
    port: { type: 'number', default: 5432, env: 'DB_PORT' },
    name: { type: 'string', required: true, env: 'DB_NAME' },
  },
  logLevel: { type: 'string', enum: ['debug','info','warn','error'], default: 'info' },
});

config.load({ envFile: '.env', jsonFile: 'config.json' });

console.log(config.get('db.host'));  // 'localhost' or DB_HOST value
console.log(config.isProduction);    // true if NODE_ENV=production

22. Streaming JSON

res.streamJSON(asyncIterable, opts?)

Stream an async iterable as NDJSON (Newline Delimited JSON). Handles backpressure via drain events. Content-Type: application/x-ndjson.

res.jsonLines(items, opts?)

Send an array as JSON Lines (one JSON object per line). Non-streaming, sets Content-Length.

res.sseStream(opts?)

Enhanced SSE with auto-incrementing IDs, typed events, reconnect (retry), last-event-id support, and keepAlive. Methods: event(type, data, id?), data(payload, id?), comment(text), keepAlive(), close().

res.sse()

Basic SSE helper. Returns { send(data, event?, id?), retry(ms), comment(text), close() }.

import { Forge } from '@hyperbridge/forge/server';

const app = new Forge();

// NDJSON streaming from async generator
app.get('/stream', async (req, res) => {
  async function* generate() {
    for (let i = 0; i < 100; i++) {
      yield { id: i, timestamp: Date.now() };
    }
  }
  await res.streamJSON(generate());
});

// Server-Sent Events with typed events
app.get('/events', (req, res) => {
  const sse = res.sseStream({ retry: 5000, keepAlive: 15000 });

  const interval = setInterval(() => {
    sse.event('tick', { time: new Date().toISOString() });
  }, 1000);

  req.on('close', () => {
    clearInterval(interval);
    sse.close();
  });
});

app.listen(3000);

23. Request/Response Interceptors

app.addHook(event, handler)

Fastify-style lifecycle hooks. Events: onRequest, preParsing, preValidation, preHandler, preSerialization, onResponse, onError, onClose, onRoute, onReady.

preSerialization

Transform response data before JSON serialization. Hook receives (req, res, data) and returns the modified payload.

Hook Execution Order

preParsing → preValidation → preHandler → [route handler] → preSerialization → onResponse. Hooks can short-circuit by ending the response.

import { Forge } from '@hyperbridge/forge/server';

const app = new Forge();

// Log every request before parsing
app.addHook('preParsing', async (req, res) => {
  console.log(`Incoming: ${req.method} ${req.url}`);
});

// Transform all JSON responses
app.addHook('preSerialization', async (req, res, data) => {
  return { ...data, timestamp: Date.now(), requestId: req.id };
});

app.get('/api/users', (req, res) => {
  res.json({ users: [] }); // { users: [], timestamp: ..., requestId: ... }
});

app.listen(3000);

24. HTTP/2

createHttp2Server(app, opts?)

Create an HTTP/2 server with ALPN negotiation. opts.key, opts.cert, opts.allowHTTP1 (default true). Supports maxConcurrentStreams, initialWindowSize, maxFrameSize.

res.push(path, opts?)

HTTP/2 Server Push. Push assets to the client before they request them. opts.contentType, opts.body or opts.file, opts.maxAge.

Http2Request / Http2Response

Compatibility wrappers that map HTTP/2 streams to the standard Node.js req/res interface. Full API parity with HTTP/1.1 handlers.

import { Forge, createHttp2Server } from '@hyperbridge/forge/server';
import fs from 'fs';

const app = new Forge();

app.get('/', (req, res) => {
  // Push CSS and JS before the client requests them
  if (res.push) {
    res.push('/styles.css', { file: './public/styles.css', contentType: 'text/css' });
    res.push('/app.js', { file: './public/app.js', contentType: 'application/javascript' });
  }
  res.html('<html><head><link rel="stylesheet" href="/styles.css"></head>...</html>');
});

const server = createHttp2Server(app, {
  key: fs.readFileSync('key.pem'),
  cert: fs.readFileSync('cert.pem'),
});
server.listen(3000);

25. OpenAPI

generateOpenAPI(app, info?)

Auto-generate an OpenAPI 3.0.3 spec from registered routes. Extracts path params, request body schemas from validators, and named route metadata.

app.openapi(info?)

Shortcut on Forge instance. info: { title, version, description, contact, license, servers }.

import { Forge, generateOpenAPI } from '@hyperbridge/forge/server';

const app = new Forge();
app.get('/users/:id', (req, res) => { /* ... */ });
app.post('/users', (req, res) => { /* ... */ });

const spec = app.openapi({
  title: 'My API',
  version: '2.0.0',
  description: 'User management API',
});

app.get('/docs/openapi.json', (req, res) => res.json(spec));
app.listen(3000);

26. HTTPS & ACME

app.listenTLS(port, tlsOpts, host?, cb?)

Start an HTTPS server with pre-existing cert and key files.

createHttpsServer(app, opts)

ACME/Let's Encrypt helper. opts.domain, opts.email, opts.certDir, opts.staging. Serves HTTP-01 challenges on port 80, auto-renews certificates.

import { Forge, createHttpsServer } from '@hyperbridge/forge/server';

const app = new Forge();
app.get('/', (req, res) => res.json({ secure: true }));

// Auto-obtain TLS cert via Let's Encrypt
const server = await createHttpsServer(app, {
  domain: 'api.example.com',
  email: 'admin@example.com',
  certDir: './.forge-certs',
  port: 443,
});

27. Cluster

Forge.cluster(workerFn, opts?)

Multi-process cluster mode using Node.js cluster module. Forks opts.workers (default: CPU count). Auto-restarts crashed workers.

import { Forge } from '@hyperbridge/forge/server';

Forge.cluster(() => {
  const app = new Forge();

  app.get('/', (req, res) => {
    res.json({ worker: process.pid });
  });

  app.onShutdown(async () => {
    await db.close();
  });

  app.listen(3000);
}, { workers: 8 });

Response Helpers

res.json(data) / res.html(str) / res.text(str) / res.xml(str)

Send typed responses with correct Content-Type headers.

res.send(body)

Auto-detect type: objects become JSON, numbers become status codes, buffers become binary, strings become HTML.

res.status(code)

Chainable status setter. res.status(201).json({ created: true }).

res.redirect(url, code?) / res.noContent()

HTTP redirect (default 302). noContent() sends 204 with empty body.

res.sendFile(path, opts?) / res.download(path, filename?)

Stream a file with ETag, range requests, and proper MIME type. download adds Content-Disposition attachment header.

res.cookie(name, value, opts?) / res.clearCookie(name)

Set/clear cookies. Options: maxAge, expires, path, domain, httpOnly, secure, sameSite.

res.set(key, value) / res.stream(readable, opts?)

Chainable header setter. stream pipes a ReadableStream with backpressure handling.

res.jsonp(data)

JSONP response using req.query.callback. Falls back to res.json if no callback.

Request Properties

req.params / req.query / req.body / req.files

Route parameters, query string, parsed body, and uploaded files.

req.id / req.correlationId

Auto-generated UUID or propagated from X-Request-Id header.

req.ip / req.hostname / req.protocol / req.baseUrl / req.fullUrl

Proxy-aware IP detection (X-Forwarded-For), hostname, protocol, and full URL reconstruction.

req.cookies / req.signedCookies / req.session

Parsed cookies, verified signed cookies, and session data (when middleware is active).

req.is(type) / req.accepts(type) / req.get(header)

Content-type check, Accept header check, and case-insensitive header getter.

Type-Safe RPC

createRPCRouter

Create a type-safe RPC router with automatic input validation, serialization, and error handling. Mount on any Forge app with app.use('/rpc', router).

procedure.input().query()

Define a read-only RPC procedure. Chain .input(schema) for validation and .query(resolver) for the handler. Accessed via GET.

procedure.input().mutation()

Define a write RPC procedure. Chain .input(schema) for validation and .mutation(resolver) for the handler. Accessed via POST.

mergeRouters

Combine multiple RPC routers into one. Namespaces are preserved, and procedures are merged with conflict detection.

Batch Support

Multiple RPC calls in a single HTTP request. The client batches calls automatically and the server resolves them in parallel.

import { Forge, createRPCRouter, procedure, mergeRouters } from '@hyperbridge/forge/server';

const userRouter = createRPCRouter({
  getUser: procedure
    .input({ id: 'string' })
    .query(async ({ input, ctx }) => {
      return ctx.db.users.findById(input.id);
    }),

  updateUser: procedure
    .input({ id: 'string', name: 'string', email: 'string?' })
    .mutation(async ({ input, ctx }) => {
      return ctx.db.users.update(input.id, input);
    })
});

const postRouter = createRPCRouter({
  list: procedure.query(async ({ ctx }) => ctx.db.posts.findAll())
});

const appRouter = mergeRouters({ user: userRouter, post: postRouter });

const app = new Forge();
app.use('/rpc', appRouter);   // POST /rpc/user.getUser, /rpc/post.list, etc.
app.listen(3000);

AI/LLM Streaming

res.streamTokens

Stream tokens from an LLM response using Server-Sent Events. OpenAI-compatible format with data: {"choices":[...]} lines. Handles backpressure and auto-sends [DONE].

res.streamChunks

Stream arbitrary chunks as SSE events. Supports custom event types, retry intervals, and ID-based resumption for reconnecting clients.

import { Forge } from '@hyperbridge/forge/server';

const app = new Forge();

// Stream LLM tokens — OpenAI-compatible SSE
app.post('/api/chat', async (req, res) => {
  const { messages } = req.body;
  const llmStream = await callLLM({ messages, stream: true });

  res.streamTokens(async function* () {
    for await (const chunk of llmStream) {
      yield { content: chunk.text, role: 'assistant' };
    }
  });
});

// Stream arbitrary data chunks
app.get('/api/feed', (req, res) => {
  res.streamChunks(async function* () {
    yield { event: 'status', data: { connected: true } };
    for await (const update of liveUpdates()) {
      yield { event: 'update', data: update, id: update.id };
    }
  }, { retry: 3000 });
});

app.listen(3000);

Content Negotiation

negotiate

Middleware that parses the Accept header and sets req.negotiated with the best matching content type, encoding, and language.

res.format

Respond with different representations based on the Accept header. Provide a map of content-type handlers. Falls back to 406 Not Acceptable.

res.vary

Append a Vary header field. Used by caches to determine when a response can be reused. Automatically deduplicates values.

import { Forge, negotiate } from '@hyperbridge/forge/server';

const app = new Forge();
app.use(negotiate());

app.get('/api/users/:id', async (req, res) => {
  const user = await db.users.findById(req.params.id);

  res.vary('Accept');
  res.format({
    'application/json': () => res.json(user),
    'text/html':        () => res.html(`<h1>${user.name}</h1>`),
    'text/csv':         () => res.text(`${user.id},${user.name},${user.email}`),
    default:            () => res.status(406).json({ error: 'Not Acceptable' })
  });
});

app.listen(3000);

Streaming Uploads

streamUpload

Middleware for streaming file uploads without buffering the entire file in memory. Processes multipart data as a readable stream. Supports size limits, allowed MIME types, and progress callbacks.

import { Forge, streamUpload } from '@hyperbridge/forge/server';
import { createWriteStream } from 'fs';

const app = new Forge();

app.post('/api/upload', streamUpload({
  maxSize: '500mb',
  allowedTypes: ['video/*', 'image/*'],
  onProgress(bytes, total) { console.log(`${Math.round(bytes/total*100)}%`); }
}), async (req, res) => {
  // req.file is a readable stream — pipe directly to storage
  const dest = createWriteStream(`./uploads/${req.file.filename}`);
  await req.file.stream.pipe(dest);

  res.status(201).json({
    filename: req.file.filename,
    size: req.file.size,
    type: req.file.mimetype
  });
});

app.listen(3000);

Test Client

app.inject()

Create an in-process test client without starting a real server. Simulates HTTP requests through the full middleware and routing stack.

.get() / .post() / .put() / .delete()

Chainable request builders. Set headers, body, and query params: app.inject().post('/api/users').send({ name: 'Alice' }).

.expect(status) / .expect(header, value)

Assertion helpers on the response. Chain multiple expects. Throws descriptive errors on mismatch.

.end()

Execute the request and return a promise resolving to the full response object with status, headers, body, and text.

import { Forge } from '@hyperbridge/forge/server';

const app = new Forge();
app.get('/api/health', (req, res) => res.json({ status: 'ok' }));
app.post('/api/users', (req, res) => {
  res.status(201).json({ id: 1, name: req.body.name });
});

// Test without starting a server
const res = await app.inject()
  .get('/api/health')
  .expect(200)
  .expect('content-type', /json/)
  .end();

console.assert(res.body.status === 'ok');

// Test POST with body
const created = await app.inject()
  .post('/api/users')
  .set('Authorization', 'Bearer test-token')
  .send({ name: 'Alice' })
  .expect(201)
  .end();

console.assert(created.body.name === 'Alice');

Dev Mode

app.enableDevMode

Enable development mode with enhanced error pages, stack traces, request logging, and auto-reload on file changes. Automatically disabled in production.

devLogger

Colorized development logger middleware. Logs method, path, status code, response time, and payload size. Supports filtering by path or status.

import { Forge, devLogger } from '@hyperbridge/forge/server';

const app = new Forge();

if (process.env.NODE_ENV !== 'production') {
  app.enableDevMode({
    errorOverlay: true,          // rich HTML error pages with stack traces
    watchDirs: ['./src'],        // auto-restart on file changes
    openBrowser: true            // open browser on first listen
  });

  app.use(devLogger({
    filter: (req) => !req.path.startsWith('/health'),
    colorize: true,
    showBody: true               // log request/response bodies
  }));
}

app.get('/', (req, res) => res.json({ hello: 'world' }));
app.listen(3000);
// DevLogger output: GET / 200 1.2ms 27B

OpenAPI 3.1

generateOpenAPI31

Auto-generate an OpenAPI 3.1 specification from your Forge routes, including path params, query schemas, request bodies, and response types.

swaggerUI

Serve an interactive Swagger UI explorer at a given path. Powered by the generated OpenAPI spec. Supports try-it-out and authentication.

app.swagger

Convenience method to mount both the JSON spec endpoint and the Swagger UI in one call. Configurable path prefix, title, and version.

import { Forge, generateOpenAPI31, swaggerUI } from '@hyperbridge/forge/server';

const app = new Forge();

app.get('/api/users', { summary: 'List users', tags: ['Users'] }, (req, res) => {
  res.json([{ id: 1, name: 'Alice' }]);
});

app.post('/api/users', {
  summary: 'Create user',
  tags: ['Users'],
  body: { name: 'string', email: 'string' },
  response: { 201: { id: 'number', name: 'string' } }
}, (req, res) => {
  res.status(201).json({ id: 2, name: req.body.name });
});

// Mount spec + UI
app.swagger({
  path: '/docs',
  info: { title: 'My API', version: '2.0.0' }
});

// Or manually:
// const spec = generateOpenAPI31(app, { info: { title: 'My API', version: '2.0.0' } });
// app.get('/openapi.json', (req, res) => res.json(spec));
// app.use('/docs', swaggerUI({ specUrl: '/openapi.json' }));

app.listen(3000);
// Swagger UI at http://localhost:3000/docs

Enhanced Errors

EnhancedForgeError

Extended error class with code, status, details, cause chain, and serialization. Supports toJSON() and toString() with full context.

Error Factories

Pre-built factory functions: notFound(), badRequest(), unauthorized(), forbidden(), conflict(), tooManyRequests(), internalError(). Accept message and details.

Typed Error Handlers

Register error handlers by error code or class. app.onError('VALIDATION', handler) catches only validation errors. Supports fallback and wildcard handlers.

import { Forge, EnhancedForgeError, notFound, badRequest, unauthorized } from '@hyperbridge/forge/server';

const app = new Forge();

// Typed error handlers — catch specific error codes
app.onError('VALIDATION', (err, req, res) => {
  res.status(400).json({
    code: err.code,
    message: err.message,
    fields: err.details.fields     // per-field validation errors
  });
});

app.onError('NOT_FOUND', (err, req, res) => {
  res.status(404).json({ code: err.code, message: err.message });
});

// Throw enhanced errors from routes
app.get('/api/users/:id', async (req, res) => {
  const user = await db.users.findById(req.params.id);
  if (!user) throw notFound(`User ${req.params.id} not found`);

  res.json(user);
});

app.post('/api/users', async (req, res) => {
  if (!req.body.email) {
    throw badRequest('Validation failed', {
      fields: { email: 'Email is required' }
    });
  }

  // EnhancedForgeError with cause chain
  try {
    const user = await db.users.create(req.body);
    res.status(201).json(user);
  } catch (dbErr) {
    throw new EnhancedForgeError('DB_ERROR', 'Failed to create user', 500, { cause: dbErr });
  }
});

app.listen(3000);

forge/auth

Complete authentication & authorization — Password, JWT, OAuth2, TOTP, WebAuthn, MagicLink, RBAC, Sessions, MFA, SAML, and audit trails. Zero external dependencies.

Never store JWT secrets in source code or commit them to version control. forge/auth reads process.env.JWT_SECRET by default. In production use a 256-bit (32-byte) random secret minimum — shorter secrets are vulnerable to brute-force. Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

Password

PBKDF2-SHA-256 with 310,000 iterations (OWASP 2023 minimum). No bcrypt dependency required.

MethodReturnsDescription
Password.hash(plaintext, opts?)Promise<string>Hash a password. Opts: { iterations, saltLength, digest }
Password.verify(plaintext, hash)Promise<boolean>Constant-time comparison to prevent timing attacks
Password.needsRehash(hash)booleanReturns true if hash was created with fewer iterations than current config — signal to rehash on next login
Password.generate(length?)stringGenerate a cryptographically random password. Default 16 chars.

JWT

MethodDescription
JWT.sign(payload, secret, opts?)Sign payload. Opts: { expiresIn, issuer, audience, algorithm }. Algorithms: HS256/384/512, RS256, ES256.
JWT.verify(token, secret, opts?)Verify and decode token. Throws on expiry, bad signature, or claim mismatch. Opts: { issuer, audience, clockTolerance }
JWT.decode(token)Decode without verification (for reading claims only — do NOT use for auth)
JWT.refresh(token, secret, opts?)Re-sign token with new expiry without requiring re-login. Original claims preserved.
JWT.isExpired(token)Quick expiry check without full verification
JWT.getExpiry(token)Returns expiry Date or null

OAuth2Provider

Built-in presets for Google, GitHub, Microsoft, GitLab, Discord, Facebook, LinkedIn. Pass 'custom' for any OAuth2-compliant provider.

MethodDescription
getAuthUrl(state?, scope?)Generate redirect URL. state is auto-generated (CSRF protection) if omitted.
handleCallback(code, state?)Exchange authorization code for access token. Validates state parameter.
getUserInfo(token)Fetch user profile from provider. Normalized: { id, email, name, avatar, raw }
refreshToken(refreshToken)Refresh expired access token
revokeToken(token)Revoke access or refresh token

TOTP (2FA)

MethodDescription
TOTP.generateSecret(account, issuer)Returns { secret, qrDataUrl, otpauthUrl }. Show qrDataUrl as an <img> — user scans with Authenticator app.
TOTP.verify(code, secret, opts?)Validate 6-digit TOTP code. Opts: { window: 1 } — accepts codes ±1 time step (handles clock skew).
TOTP.generate(secret)Generate current TOTP code (for testing)

BackupCodes

MethodDescription
BackupCodes.generate(count?)Generate N one-time backup codes (default 10). Returns { codes, hashes }. Store hashes, show codes once.
BackupCodes.verify(inputCode, storedHashes)Check if code is valid and unused. Returns index of matched hash or -1.
BackupCodes.consume(index, hashes)Returns new hashes array with the used code removed (immutable)

RBAC

MethodDescription
new RBAC(config)Config: { roles: { roleName: { permissions: string[], inherits?: string[] } } }
.can(role, permission)Check if role has permission. Supports wildcards: 'posts:*', '*'.
.cannot(role, permission)Inverse of .can()
.getRoles()List all defined roles
.getPermissions(role)List all permissions for a role (including inherited)
.addRole(name, permissions, inherits?)Dynamically add a role at runtime

MagicLink

MethodDescription
new MagicLink(opts)Opts: { secret, expiresIn, baseUrl }. baseUrl prefixes the verification URL.
.generate(email, metadata?)Returns { token, url, expiresAt }. Email the URL to the user.
.verify(token)Returns { email, metadata } or throws if expired/invalid
.invalidate(token)Revoke token before expiry (e.g., after use)

SessionManager

MethodDescription
new SessionManager(opts)Opts: { store: 'memory'|'redis'|Store, ttl, maxSessions }
.create(userId, data?)Create session. Returns { sessionId, expiresAt }
.get(sessionId)Returns session data or null if expired/invalid
.touch(sessionId)Extend TTL (call on each authenticated request)
.destroy(sessionId)Log out one session
.destroyAll(userId)Log out all sessions for a user (e.g., after password change)
.listSessions(userId)List active sessions with device info and last-active time

Example: Password + JWT Auth Middleware

import { Password, JWT } from '@hyperbridge/forge/auth';

// Registration
router.post('/auth/register', async (req, res) => {
  const { email, password } = req.body;
  const hash = await Password.hash(password);
  const user = await db.users.create({ email, passwordHash: hash });
  res.status(201).json({ id: user.id, email: user.email });
});

// Login
router.post('/auth/login', async (req, res) => {
  const { email, password } = req.body;
  const user = await db.users.findByEmail(email);
  if (!user) return res.status(401).json({ error: 'Invalid credentials' });

  const valid = await Password.verify(password, user.passwordHash);
  if (!valid) return res.status(401).json({ error: 'Invalid credentials' });

  // Rehash if iterations are outdated
  if (Password.needsRehash(user.passwordHash)) {
    const newHash = await Password.hash(password);
    await db.users.update(user.id, { passwordHash: newHash });
  }

  const accessToken  = JWT.sign({ sub: user.id, role: user.role }, process.env.JWT_SECRET, { expiresIn: '15m' });
  const refreshToken = JWT.sign({ sub: user.id, type: 'refresh' }, process.env.JWT_SECRET, { expiresIn: '30d' });
  res.json({ accessToken, refreshToken });
});

// Auth middleware
function requireAuth(req, res, next) {
  const token = req.headers.authorization?.replace('Bearer ', '');
  if (!token) return res.status(401).json({ error: 'Missing token' });
  try {
    req.user = JWT.verify(token, process.env.JWT_SECRET);
    next();
  } catch {
    res.status(401).json({ error: 'Invalid or expired token' });
  }
}

Example: TOTP Two-Factor Authentication

import { TOTP, BackupCodes, JWT } from '@hyperbridge/forge/auth';

// Step 1: Generate TOTP secret for user
router.post('/auth/2fa/setup', requireAuth, async (req, res) => {
  const { secret, qrDataUrl, otpauthUrl } = await TOTP.generateSecret(
    req.user.email, 'MyApp'
  );
  // Save secret temporarily (not activated until verified)
  await db.users.update(req.user.sub, { totpSecretPending: secret });
  res.json({ qrDataUrl, otpauthUrl }); // show qrDataUrl as <img>
});

// Step 2: Verify setup (user scans QR and enters first code)
router.post('/auth/2fa/verify-setup', requireAuth, async (req, res) => {
  const { code } = req.body;
  const user = await db.users.find(req.user.sub);
  const valid = TOTP.verify(code, user.totpSecretPending);
  if (!valid) return res.status(400).json({ error: 'Invalid code' });

  // Generate backup codes
  const { codes, hashes } = BackupCodes.generate(10);
  await db.users.update(user.id, {
    totpSecret:        user.totpSecretPending,
    totpSecretPending: null,
    backupCodeHashes:  hashes,
    twoFactorEnabled:  true,
  });
  res.json({ backupCodes: codes }); // show once, never again
});

// Step 3: Login flow
router.post('/auth/login/2fa', async (req, res) => {
  const { tempToken, code } = req.body;
  const { sub } = JWT.verify(tempToken, process.env.JWT_SECRET);
  const user = await db.users.find(sub);

  const validTotp   = TOTP.verify(code, user.totpSecret);
  const backupIndex = BackupCodes.verify(code, user.backupCodeHashes);

  if (!validTotp && backupIndex === -1) {
    return res.status(401).json({ error: 'Invalid 2FA code' });
  }

  // Consume backup code if used
  if (backupIndex !== -1) {
    const newHashes = BackupCodes.consume(backupIndex, user.backupCodeHashes);
    await db.users.update(user.id, { backupCodeHashes: newHashes });
  }

  const accessToken = JWT.sign({ sub: user.id, role: user.role }, process.env.JWT_SECRET, { expiresIn: '15m' });
  res.json({ accessToken });
});

Example: RBAC Middleware

import { RBAC, JWT } from '@hyperbridge/forge/auth';

const rbac = new RBAC({
  roles: {
    admin:  { permissions: ['*'] },
    editor: { permissions: ['posts:read', 'posts:write', 'media:upload'], inherits: ['viewer'] },
    viewer: { permissions: ['posts:read', 'comments:read'] },
  },
});

// Middleware factory
function can(permission) {
  return (req, res, next) => {
    if (!req.user?.role) return res.status(401).json({ error: 'Unauthenticated' });
    if (!rbac.can(req.user.role, permission)) {
      return res.status(403).json({ error: `Permission denied: ${permission}` });
    }
    next();
  };
}

// Apply to routes
router.get('/posts',         can('posts:read'),  listPosts);
router.post('/posts',        can('posts:write'), createPost);
router.delete('/posts/:id',  can('posts:*'),     deletePost);   // wildcard
router.post('/admin/users',  can('*'),           adminAction);  // admin only

Example: OAuth2 PKCE Flow

import { OAuth2PKCE } from '@hyperbridge/forge/auth';

const pkce = new OAuth2PKCE({
  clientId:    process.env.GITHUB_CLIENT_ID,
  redirectUri: 'http://localhost:3000/auth/github/callback',
  provider:    'github',
  scopes:      ['user:email', 'read:user'],
});

// 1. Start login — generates code_verifier + code_challenge
router.get('/auth/github', (req, res) => {
  const { url, state, codeVerifier } = pkce.startFlow();
  req.session.pkceState        = state;
  req.session.pkceCodeVerifier = codeVerifier;
  res.redirect(url);
});

// 2. Handle callback — exchanges code + verifier for token
router.get('/auth/github/callback', async (req, res) => {
  const { code, state } = req.query;
  if (state !== req.session.pkceState) return res.status(400).send('State mismatch');

  const token = await pkce.exchangeCode(code, req.session.pkceCodeVerifier);
  const user  = await pkce.getUserInfo(token);
  // { id, email, name, avatar }

  const jwt = JWT.sign({ sub: user.id, email: user.email }, process.env.JWT_SECRET, { expiresIn: '7d' });
  res.redirect(`/dashboard?token=${jwt}`);
});

forge/data

PostgreSQL ORM — connection pooling, fluent query builder, full ORM with relations & hooks, schema builder, migrations, read replicas, query caching, seeding, and audit logging.

Always use parameterized queries. Never concatenate user input into SQL strings. All Model and QueryBuilder methods use parameterized queries automatically. For raw SQL, use placeholders: pool.query('SELECT * FROM users WHERE id = $1', [userId]).

PostgreSQL-only. forge/data is purpose-built for PostgreSQL 13+ and uses node:net / node:tls to speak the PostgreSQL wire protocol directly. No pg, libpq, or native bindings required.

Pool Configuration

OptionTypeDefaultDescription
hoststring'localhost'PostgreSQL server host
portnumber5432PostgreSQL server port
databasestringDatabase name
userstringPostgreSQL username
passwordstringPostgreSQL password
sslboolean | objectfalsetrue for default TLS, or { ca, cert, key, rejectUnauthorized }
maxnumber10Max connections in pool
minnumber2Min idle connections to maintain
idleTimeoutnumber30000Release idle connections after ms
connectionTimeoutnumber5000Fail if connection not acquired within ms
statementTimeoutnumber0Cancel queries running longer than ms. 0 = no limit.

QueryBuilder Methods

MethodDescription
.select(...cols)Columns to return. Default *. Supports raw: .select('COUNT(*) as total')
.table(name)Set/override the table name
.where(col, op, val)Add WHERE clause. Also accepts object: .where({ status: 'active' })
.orWhere(col, op, val)Add OR WHERE clause
.whereIn(col, values[])WHERE col IN ($1, $2, …)
.whereNull(col)WHERE col IS NULL
.whereRaw(sql, bindings)Raw SQL fragment with parameterized bindings
.join(table, a, b)INNER JOIN. Also: .leftJoin(), .rightJoin(), .fullJoin()
.orderBy(col, dir?)dir: 'asc' (default) or 'desc'
.groupBy(...cols)GROUP BY columns
.having(col, op, val)HAVING clause (used with GROUP BY)
.limit(n)LIMIT n rows
.offset(n)OFFSET n rows (for pagination)
.get()Execute SELECT, return row array
.first()Execute SELECT with LIMIT 1, return first row or null
.count(col?)Execute COUNT query, return number
.insert(data)INSERT row(s), return inserted row(s)
.update(data)UPDATE matching rows, return updated count
.delete()DELETE matching rows, return deleted count
.toSQL()Return { sql, bindings } without executing
.paginate(page, perPage)Returns { data, total, page, perPage, lastPage }

Model Static Methods

MethodDescription
Model.create(data)INSERT and return instance
Model.find(id)Find by primary key
Model.findOrFail(id)Find by PK — throws ModelNotFoundError if missing
Model.where(col, val)Start a scoped QueryBuilder
Model.scope(name)Apply a named scope defined in static scopes
Model.all()SELECT all rows
Model.count(col?)COUNT query
Model.truncate()TRUNCATE TABLE (dangerous — dev/test only)

Model Instance Methods

MethodDescription
.save()INSERT if new, UPDATE if existing (dirty checking)
.update(data)UPDATE specific fields. Merges into current state.
.delete()DELETE this row
.fresh()Re-fetch from DB, return new instance with latest data
.refresh()Re-fetch and mutate this instance in place
.toJSON()Serialize to plain object (respects hidden field list)
.isDirty(field?)Check if any (or specific) field has changed since load
.load(relation)Lazy-load a relation: await post.load('comments')

Example: Full ORM Setup

import { Pool, Model } from '@hyperbridge/forge/data';

const pool = new Pool({
  host:     process.env.DB_HOST,
  database: process.env.DB_NAME,
  user:     process.env.DB_USER,
  password: process.env.DB_PASS,
  ssl:      true,
  max:      20,
});

class User extends Model {
  static table    = 'users';
  static pool     = pool;
  static fillable = ['email', 'name', 'role'];
  static hidden   = ['passwordHash'];           // excluded from toJSON()
  static casts    = { createdAt: 'date', settings: 'json' };

  // Named scopes
  static scopes = {
    active:  qb => qb.where('active', true),
    admins:  qb => qb.where('role', 'admin'),
    recent:  qb => qb.orderBy('created_at', 'desc').limit(20),
  };

  // Lifecycle hooks
  static beforeCreate = async (data) => ({
    ...data,
    createdAt: new Date(),
  });

  static afterCreate = async (user) => {
    await EmailQueue.enqueue({ to: user.email, template: 'welcome' });
  };

  // Relations
  static relations = {
    posts:    { type: 'hasMany',    model: () => Post,    foreignKey: 'user_id' },
    profile:  { type: 'hasOne',     model: () => Profile, foreignKey: 'user_id' },
    roles:    { type: 'belongsToMany', model: () => Role, pivot: 'user_roles' },
  };
}

// CRUD
const user  = await User.create({ email: 'a@b.com', name: 'Alex', role: 'editor' });
const found = await User.findOrFail(user.id);
await found.update({ name: 'Alexandra' });

// Scoped query
const admins = await User.scope('active').scope('admins').orderBy('name').get();

// Eager-load relations
const posts = await user.load('posts');

// Pagination
const page = await User.scope('active').paginate(1, 20);
// { data: User[], total: 154, page: 1, perPage: 20, lastPage: 8 }

Example: Migrations

import { Pool, Migrator, SchemaBuilder } from '@hyperbridge/forge/data';

const migrator = new Migrator(pool, {
  directory: './db/migrations',
  tableName: 'schema_migrations',
});

// Create a migration file
await migrator.create('create_posts_table');
// → ./db/migrations/20260414120000_create_posts_table.js

// Migration file content (generated template):
export const up = async (sb) => {
  sb.createTable('posts', (t) => {
    t.serial('id').primaryKey();
    t.string('title', 255).notNull();
    t.text('body').notNull();
    t.string('status', 20).defaultValue('draft');
    t.integer('user_id').references('users.id').onDelete('CASCADE');
    t.boolean('featured').defaultValue(false);
    t.jsonb('metadata').defaultValue('{}');
    t.timestamps(); // created_at, updated_at
    t.index(['status', 'user_id']);
  });
};

export const down = async (sb) => {
  sb.dropTable('posts');
};

// Run migrations
await migrator.latest();        // run pending
await migrator.rollback();      // revert last batch
await migrator.rollback(3);     // revert last 3 batches
const status = await migrator.status(); // list all with run/pending

Example: Transactions with Savepoints

import { Pool } from '@hyperbridge/forge/data';

// Simple transaction
await pool.transaction(async (trx) => {
  const user  = await trx.table('users').insert({ email: 'x@y.com' }).returning('*').first();
  await trx.table('profiles').insert({ userId: user.id, bio: 'Hello' });
  // auto-commit on return, auto-rollback on throw
});

// Nested savepoints
await pool.transaction(async (trx) => {
  await trx.table('orders').insert({ userId: 1, total: 500 });

  await trx.savepoint('sp1', async () => {
    await trx.table('inventory').update({ stock: 0 }, { where: { sku: 'ABC' } });
    // Only this savepoint rolls back on failure, outer transaction continues
  });

  await trx.table('audit_log').insert({ action: 'order_placed', userId: 1 });
});

Example: Read Replica + Query Cache

import { Pool, ReadReplica, QueryCache } from '@hyperbridge/forge/data';

// Primary (writes) + replicas (reads)
const primary  = new Pool({ host: 'primary.db.internal', /* ... */ });
const replica1 = new Pool({ host: 'replica1.db.internal', /* ... */ });
const replica2 = new Pool({ host: 'replica2.db.internal', /* ... */ });

const replica = new ReadReplica(primary, [replica1, replica2], {
  strategy: 'round-robin',  // or 'random', 'least-connections'
});

// Writes go to primary, reads auto-routed to replicas
await replica.query('INSERT INTO events VALUES ($1)', [data]);   // → primary
const rows = await replica.query('SELECT * FROM events');         // → replica

// Query result cache (Redis or in-memory)
const cache = new QueryCache(replica, {
  store:   'memory',    // or { type: 'redis', url: process.env.REDIS_URL }
  default: 60_000,      // 60s default TTL
});

const hot = await cache.query(
  'SELECT * FROM featured_products ORDER BY rank LIMIT 20',
  [],
  { ttl: 300_000 }  // 5-minute TTL for this query
);

forge/form

Headless form management — async & cross-field validation, schema binding, file upload, field arrays, multi-step forms, conditional fields, and accessibility helpers.

Schema-first. fromSchema(zodSchema) infers field types, default values, and validation rules from a forge/schema definition — eliminating duplicate validation logic between your form and API layer. The same schema validates on submit and on the server.

Headless. forge/form manages state and validation only — it has no opinion on your HTML or CSS. Wire it to any UI framework (forge/client, React, Svelte, or vanilla JS) by binding form.values, form.errors, and form.touched.

Form Constructor Options

OptionTypeDefaultDescription
initialValuesobject{}Initial field values. Supports nested objects and arrays.
schemaZodSchemanullforge/schema object for automatic validation on change and submit
validatefn(values) → errorsnullCustom synchronous validator. Return an errors object. Runs alongside schema.
validateAsyncasync fn(values) → errorsnullAsync validator (e.g., check email uniqueness). Debounced automatically.
validateOnstring'submit'When to validate: 'submit', 'change', 'blur'
onSubmitasync fn(data)nullSubmit handler. Called only when validation passes.
resetOnSubmitbooleanfalseReset to initialValues after successful submit

Form State

PropertyTypeDescription
form.valuesobjectCurrent field values (deeply reactive)
form.errorsobjectField-level error messages. { email: 'Invalid email' }
form.touchedobjectWhich fields have been blurred. { email: true }
form.dirtybooleanTrue if any field differs from initialValues
form.isSubmittingbooleanTrue during async submit handler execution
form.isValidbooleanTrue if errors object is empty
form.submitCountnumberNumber of submit attempts (use for showing all errors after first attempt)

Form Methods

MethodDescription
.setField(path, value)Set a field value by dot-path: form.setField('address.city', 'Chennai')
.setValues(data)Batch-update multiple fields
.setError(path, msg)Manually set an error (e.g., from server response)
.setErrors(errors)Batch-set errors from server validation response
.touch(path)Mark field as touched (shows validation errors for that field)
.touchAll()Mark all fields touched (use before showing all errors)
.validate()Run validation. Returns { valid, data, errors }
.submit()Validate then call onSubmit if valid
.reset(values?)Reset to initialValues (or custom values if provided)
.watch(path, fn)Subscribe to field changes: form.watch('country', val => updateStates(val))
.getFieldProps(path)Returns { value, onChange, onBlur, name } spread onto <input>

Example: Registration Form with Schema

import { Form, fromSchema } from '@hyperbridge/forge/form';
import { z } from '@hyperbridge/forge/schema';

const RegisterSchema = z.object({
  email:           z.string().email('Invalid email'),
  password:        z.string().min(8, 'At least 8 characters'),
  confirmPassword: z.string(),
  name:            z.string().min(1, 'Name is required'),
  role:            z.enum(['admin', 'editor', 'viewer']).default('viewer'),
  acceptTerms:     z.literal(true, { errorMap: () => ({ message: 'You must accept the terms' }) }),
}).refine(d => d.password === d.confirmPassword, {
  message: 'Passwords do not match',
  path:    ['confirmPassword'],
});

// fromSchema infers initial values and wires validation automatically
const form = fromSchema(RegisterSchema, {
  validateOn: 'blur',
  onSubmit:   async (data) => {
    const res = await fetch('/api/register', { method: 'POST', body: JSON.stringify(data) });
    if (!res.ok) {
      const { errors } = await res.json();
      form.setErrors(errors); // map server errors back to fields
    }
  },
});

// Wire to HTML (vanilla JS example)
document.querySelector('#register-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  form.touchAll(); // show all validation errors
  await form.submit();
});

document.querySelectorAll('input').forEach(input => {
  const props = form.getFieldProps(input.name);
  input.value = props.value;
  input.addEventListener('input', (e) => props.onChange(e.target.value));
  input.addEventListener('blur', props.onBlur);
});

// React to state changes
form.on('change', ({ path, value }) => {
  const errEl = document.querySelector(`[data-error="${path}"]`);
  if (errEl) errEl.textContent = form.errors[path] ?? '';
});

Example: Multi-Step Form

import { Form } from '@hyperbridge/forge/form';

const wizard = new Form({
  steps: [
    {
      name: 'personal',
      fields: ['firstName', 'lastName', 'email'],
      validate: (v) => {
        const e = {};
        if (!v.firstName) e.firstName = 'Required';
        if (!v.email?.includes('@')) e.email = 'Valid email required';
        return e;
      },
    },
    {
      name: 'company',
      fields: ['company', 'role', 'teamSize'],
    },
    {
      name: 'billing',
      fields: ['plan', 'cardholderName'],
      condition: (v) => v.teamSize > 1, // skip if solo
    },
  ],
  initialValues: {
    firstName: '', lastName: '', email: '',
    company: '', role: '', teamSize: 1,
    plan: 'starter', cardholderName: '',
  },
  onSubmit: async (data) => {
    await fetch('/api/onboarding', { method: 'POST', body: JSON.stringify(data) });
  },
});

// Navigation
await wizard.nextStep();       // validate current step, advance if valid
await wizard.prevStep();       // go back (no validation)
wizard.currentStep.name;       // 'personal' | 'company' | 'billing'
wizard.progress;               // 0.0 – 1.0 (for progress bar)
wizard.isLastStep;             // true on final step

Example: DragDropUpload

import { DragDropUpload } from '@hyperbridge/forge/form';

const uploader = new DragDropUpload('#drop-zone', {
  accept:      ['image/jpeg', 'image/png', 'application/pdf'],
  maxSize:     10 * 1024 * 1024,   // 10MB per file
  maxFiles:    5,
  multiple:    true,
  preview:     true,               // show image thumbnails

  onSelect:  (files) => console.log('Selected:', files.map(f => f.name)),
  onError:   (err)   => toast.error(err.message),

  upload: async (file, { onProgress }) => {
    const fd = new FormData();
    fd.append('file', file);
    return fetch('/api/upload', {
      method:    'POST',
      body:      fd,
      onUploadProgress: ({ loaded, total }) => onProgress(loaded / total),
    }).then(r => r.json());
  },
});

// Programmatic control
uploader.open();                   // open file picker
uploader.clear();                  // remove all files
const urls = await uploader.uploadAll(); // upload all selected files

Example: RepeatingGroup (Field Arrays)

import { Form, RepeatingGroup } from '@hyperbridge/forge/form';

const form = new Form({
  initialValues: {
    recipients: [{ name: '', email: '', role: 'viewer' }],
  },
});

const recipients = new RepeatingGroup(form, 'recipients', {
  template:  () => ({ name: '', email: '', role: 'viewer' }),
  min:       1,
  max:       10,
  validate: (item) => {
    const errors = {};
    if (!item.email?.includes('@')) errors.email = 'Valid email required';
    return errors;
  },
});

// API
recipients.add();               // append new row from template
recipients.remove(2);           // remove row at index 2
recipients.move(0, 2);          // reorder (drag-drop support)
recipients.swap(1, 3);          // swap two rows
recipients.items;               // current array value
recipients.errors;              // per-item error objects

forge/animate

Zero-dependency animation engine with spring physics, tweens, gestures, scroll-linked timelines, FLIP layout animations, SVG morphing, text effects, motion paths, and 50+ transition presets. 80+ APIs. 7,528 lines.

All animations respect prefers-reduced-motion by default. When a user has enabled reduced motion in their OS settings, tweens resolve instantly and springs jump to their final value. Use prefersReducedMotion() to check manually, or wrap any animation call with respectMotionPreference() for automatic handling.

Tween & Core Animation

Tween

Time-based animation with easing. new Tween({ duration, easing, values, onUpdate }). play/pause/reverse/seek.

animate

Shorthand animate(element, props, options). Returns Tween instance. CSS transforms and properties.

Animate

Class-based animation with chaining. new Animate(el).to({ opacity: 1 }, 500).start().

applyAnimation

Apply animation config object to an element. Declarative animation from plain objects.

repeatAnimation

Loop or repeat an animation. repeatAnimation(tween, { count: 3, alternate: true }).

import { Tween, animate } from '@hyperbridge/forge/animate';

// Full Tween API
const tween = new Tween({
  duration: 800,
  easing: 'easeOutCubic',
  values: { opacity: [0, 1], x: [100, 0], y: [-50, 0] },
  onUpdate: (v) => {
    el.style.opacity = v.opacity;
    el.style.transform = `translate(${v.x}px, ${v.y}px)`;
  },
});
tween.play();
tween.pause();
tween.reverse();
tween.seek(0.5); // jump to 50%

// Shorthand
animate('.box', { opacity: 1, transform: 'translateY(0)' }, { duration: 600 });

Spring Physics

Spring

Physics-based spring animation. Configure tension, friction, mass. Natural deceleration.

springPresets

Built-in spring presets: gentle, wobbly, stiff, slow, molasses. Ready-to-use configs.

springChain

Chain multiple springs in sequence. Each spring triggers the next on settle.

springTo

Animate a spring to a target value. springTo(spring, { scale: 1.2 }). Returns promise.

springEasing

Convert spring physics into a CSS easing function. Use with CSS transitions.

import { Spring, springPresets, springChain } from '@hyperbridge/forge/animate';

const spring = new Spring({
  ...springPresets.wobbly,
  values: { scale: [0.8, 1], opacity: [0, 1] },
  onUpdate: (v) => {
    el.style.transform = `scale(${v.scale})`;
    el.style.opacity = v.opacity;
  },
});
spring.play();

// Chain springs: fade in, then scale up, then slide right
springChain([
  { values: { opacity: [0, 1] }, ...springPresets.gentle },
  { values: { scale: [0.9, 1] }, ...springPresets.stiff },
  { values: { x: [0, 100] }, ...springPresets.wobbly },
], (v) => { /* onUpdate for each step */ });

Spring Configuration

ParameterTypeDescription
tensionnumberSpring stiffness. Higher values = snappier motion. Default: 170.
frictionnumberResistance / damping. Higher values = less oscillation. Default: 26.
massnumberMass of the animated object. Higher = more inertia, slower acceleration. Default: 1.
velocitynumberInitial velocity. Use to continue momentum from a gesture. Default: 0.
valuesobjectMap of animated properties with [from, to] pairs, e.g. { scale: [0.8, 1] }.
onUpdate(values) => voidCalled on each frame with the current interpolated values.
onComplete() => voidCalled when the spring settles (velocity below threshold).

Easing Functions

Easing

15+ built-in easings: linear, easeInQuad, easeOutCubic, easeInOutExpo, easeOutBack, easeOutBounce, easeOutElastic, and more.

registerEasing

Register custom easing functions. registerEasing('myEase', t => t * t * t).

interpolate

Interpolate between values. interpolate(0.5, [0, 100]) returns 50. Supports multi-stop ranges.

transform

Map value from one range to another. transform(50, [0, 100], [0, 1]) returns 0.5.

mix

Linear interpolation. mix(0.5, 0, 100) returns 50. Works with colors and numbers.

clamp / wrap

clamp(value, min, max) constrains to range. wrap(value, min, max) wraps around cyclically.

Timeline & Sequencing

Timeline

Sequence animations with labels and overlaps. new Timeline(). add(tween, offset). play/pause/seek.

timeline

Shorthand timeline builder. timeline([anim1, anim2, ...], { defaults }). Returns Timeline instance.

sequence

Run animations in strict sequence. sequence([a, b, c]) plays each after the previous finishes.

keyframes

Multi-step keyframe animation. keyframes(el, [{ opacity: 0 }, { opacity: 1 }], options).

orchestrate

Coordinate complex animation graphs. orchestrate({ enter: [...], exit: [...], stagger: 50 }).

import { Timeline, Tween, sequence } from '@hyperbridge/forge/animate';

const tl = new Timeline();
tl.add(new Tween({ duration: 300, values: { opacity: [0, 1] }, onUpdate: ... }));
tl.add(new Tween({ duration: 500, values: { x: [0, 200] }, onUpdate: ... }), 200); // overlap by 100ms
tl.play();

// Or use sequence for strict ordering
sequence([
  animate('.title', { opacity: 1 }, { duration: 400 }),
  animate('.subtitle', { opacity: 1 }, { duration: 300 }),
  animate('.cta', { opacity: 1, transform: 'scale(1)' }, { duration: 500 }),
]);

Stagger Animations

stagger

Stagger animations across elements. stagger(elements, { delay, duration, values }). Sequential entrance effects.

staggerAdvanced

Advanced stagger with custom delay functions. Supports ease-based, random, and from-center patterns.

staggerGrid

Grid-aware stagger. Animates by row, column, or diagonal. staggerGrid(els, { columns, from }).

staggerPresets

Built-in stagger patterns: linear, center, edges, random. Ready-to-use configurations.

import { stagger, staggerGrid } from '@hyperbridge/forge/animate';

// Simple stagger
stagger(document.querySelectorAll('.card'), {
  duration: 500,
  delay: 80,
  easing: 'easeOutQuad',
  values: { opacity: [0, 1], y: [30, 0] },
});

// Grid stagger — animate from center outward
staggerGrid(document.querySelectorAll('.grid-item'), {
  columns: 4,
  from: 'center',
  duration: 400,
  delay: 60,
  values: { scale: [0, 1], opacity: [0, 1] },
});

Scroll Animations

onScroll

Bind animation to scroll position. onScroll(container, (progress) => {}). Progress is 0 to 1.

scrollProgress

Get current scroll progress of an element. Returns 0-1 float.

scrollPin

Pin an element during scroll range. Element stays fixed while content scrolls past.

useScroll

Reactive scroll position. useScroll(container) returns { x, y, progress }.

scrollTimeline

ScrollTrigger-inspired API. Link animation playback to scroll position with start/end markers.

scrollProgressDriver

Drive any animation from scroll progress. Connects scroll to Tween/Spring seek().

parallax

Parallax scrolling effect. parallax(element, { speed, direction }). Layers move at different rates.

import { scrollTimeline, parallax, inView } from '@hyperbridge/forge/animate';

// Scroll-linked animation (GSAP ScrollTrigger-style)
scrollTimeline('.progress-bar', {
  trigger: '.section',
  start: 'top center',
  end: 'bottom center',
  animation: { scaleX: [0, 1] },
});

// Parallax layers
parallax('.bg-layer', { speed: 0.3 });
parallax('.fg-layer', { speed: 0.8 });

scrollTimeline() pins elements by setting position: fixed during the active scroll range. Ensure your layout accounts for this -- pinned elements are removed from the document flow, so sibling content may collapse. Use a wrapper div with an explicit height equal to the pinned duration to reserve space.

scrollTimeline Options

OptionTypeDescription
scrubboolean | numberLink animation progress to scroll position. A number adds smoothing (e.g. 0.5 = 500ms ease). Default: true.
pinbooleanPin the element in place during the scroll range. Default: false.
snapnumber | number[]Snap to progress points. 0.25 snaps to 0%, 25%, 50%, 75%, 100%. Array for custom stops.
startstringScroll start position. Format: '<element edge> <viewport edge>', e.g. 'top center'.
endstringScroll end position. Same format as start. Default: 'bottom top'.
onEnter() => voidCallback fired when the trigger element scrolls into the active range.
onLeave() => voidCallback fired when the trigger element scrolls out of the active range.

Viewport & Reveal

inView

Trigger callback when element enters viewport. IntersectionObserver-based. Returns cleanup function.

reveal

Declarative scroll reveal. reveal('.card', { preset: 'fadeUp' }). Auto-triggers on viewport entry.

onResize

Observe element resize events. onResize(element, ({ width, height }) => {}). ResizeObserver-based.

Gesture System

gesture

Unified gesture recognizer. gesture(element, { onDrag, onPinch, onRotate }). Touch and mouse support.

draggable

Make element draggable. draggable(element, { axis, bounds, onDrag }). Supports constraints.

inertia

Apply inertia/momentum after gesture release. Element decelerates naturally based on velocity.

useVelocity

Track velocity of a MotionValue. Useful for gesture-driven animations and fling detection.

import { gesture, draggable, inertia } from '@hyperbridge/forge/animate';

// Full gesture handling
gesture(card, {
  onDrag: ({ x, y, velocity }) => {
    card.style.transform = `translate(${x}px, ${y}px)`;
  },
  onDragEnd: ({ velocity }) => {
    inertia(card, { velocity, friction: 0.95 });
  },
});

// Simple draggable
draggable('.handle', { axis: 'x', bounds: '.container' });

Layout Animations (FLIP)

flip

FLIP technique. flip(element, changeFunction, options). Smoothly animates layout changes.

layoutTransition

Automatic FLIP on DOM changes. layoutTransition(container, { duration, easing }). Watches mutations.

layoutGroup

Group elements for shared layout animation. Children animate together during reordering.

import { flip, layoutTransition } from '@hyperbridge/forge/animate';

// Manual FLIP for a layout change
flip(listContainer, () => {
  // DOM mutation happens here
  listContainer.prepend(listContainer.lastChild);
}, { duration: 400, easing: 'easeOutCubic' });

// Auto FLIP — watches for any DOM changes
const cleanup = layoutTransition('.grid', {
  duration: 350,
  easing: 'easeInOutQuad',
});

SVG & Path Animations

pathDraw

SVG path drawing animation. Animates stroke-dashoffset for line-draw effect.

morphSVG

Morph between SVG shapes. morphSVG(fromPath, toPath, options). Interpolates path data.

morphPath

Enhanced path morphing with point matching. Handles paths with different point counts.

motionPath

Animate element along an SVG path. motionPath(element, path, { duration, easing }).

Text Animations

splitText

Split text into characters, words, or lines for individual animation. Returns { chars, words, lines }.

typewriter

Typewriter effect. typewriter(element, { speed, cursor }). Types text character by character.

scrambleText

Scramble/decode text effect. Characters randomly resolve to final text. Cyberpunk style.

animateNumber

Animate a number from start to end. animateNumber(el, { from: 0, to: 1000, duration: 2000 }).

import { splitText, typewriter, scrambleText, stagger } from '@hyperbridge/forge/animate';

// Split and stagger characters
const { chars } = splitText('.heading');
stagger(chars, {
  duration: 400, delay: 30,
  values: { opacity: [0, 1], y: [20, 0] },
});

// Typewriter effect
typewriter('.terminal-text', { speed: 50, cursor: true });

// Scramble reveal
scrambleText('.secret-code', { duration: 1500, chars: '!@#$%^&*' });

Presence & Transitions

AnimatePresence

Animate component enter/exit. Wraps children, plays exit animation before removal from DOM.

AnimatePresenceStandalone

Standalone version (no framework dependency). Works with vanilla JS DOM manipulation.

VariantAnimator

State-based animation variants. Define named states, transition between them.

variants

Define animation variant sets. variants({ hidden: {...}, visible: {...} }). Propagates to children.

transition

50+ built-in transition presets: fadeIn, slideUp, scaleIn, flipX, blur, bounce, rotate, and more.

presets

Animation preset library. presets.fadeIn, presets.slideUp, presets.bounceIn, etc.

crossfade

Crossfade between two elements. Fades out old, fades in new simultaneously.

sharedElement

Shared element transition between views. Animates position/size from source to destination.

Motion Values & Reactivity

MotionValue

Reactive animated value. Subscribe to changes. Drives animations without re-rendering.

useVelocity

Derive velocity from a MotionValue. Tracks rate of change over time.

Performance Utilities

batchRAF

Batch requestAnimationFrame calls. Reduces layout thrashing for multiple simultaneous animations.

willChange

Apply will-change CSS hint. willChange(element, ['transform', 'opacity']). Improves GPU compositing.

gpuPromote / gpuDemote

Force GPU layer promotion or demotion. gpuPromote(el) adds translateZ(0). gpuDemote(el) removes it.

animateAll

WAAPI batch helper. Animate multiple elements via Web Animations API in one call.

waitForAnimation

Promise that resolves when element's animations complete. Await transition end.

cancelAllAnimations

Cancel all running animations on an element. Clean stop with optional fill state.

Reduced Motion & Accessibility

prefersReducedMotion

Check if user prefers reduced motion. Returns boolean. Query prefers-reduced-motion media.

prefersReducedMotionCheck

Enhanced check with callback. Runs callback on preference change. Returns unsubscribe function.

respectMotionPreference

Wrap any animation to respect reduced motion. Returns static state if user prefers reduced motion.

import { animate, respectMotionPreference, prefersReducedMotion } from '@hyperbridge/forge/animate';

// Automatically respects user preferences
const safeAnimate = respectMotionPreference(animate);
safeAnimate('.hero', { opacity: 1, y: 0 }, { duration: 600 });
// If reduced motion: instant jump, no animation

// Manual check
if (!prefersReducedMotion()) {
  animate('.fancy-element', { rotate: 360 }, { duration: 2000 });
}

forge/chart

SVG chart library with line, bar, pie, scatter, radar, heatmap, treemap, gauge, and funnel charts.

Zero-dependency SVG rendering. forge/chart generates pure SVG markup — no Canvas, no WebGL, no D3. Charts are resolution-independent, print-ready, and fully accessible with ARIA labels. Every element is a real DOM node you can style with CSS or animate with forge/animate.

Responsive by default. Pass responsive: true and charts automatically resize with their container using a ResizeObserver. No manual chart.resize() calls needed — the layout engine recalculates axes, labels, and legends on every frame.

Quick Start

Every chart follows the same pattern: pick a type, point to a container, pass data, and call render(). The data shape is consistent across all chart types — labels for categories, series for datasets.

Chart Constructor Parameters

ParameterTypeDefaultDescription
typestringChart type: 'line', 'bar', 'area', 'pie', 'donut', 'scatter', 'bubble', 'radar', 'heatmap', 'treemap', 'gauge', 'funnel', 'candlestick', 'sparkline'
containerHTMLElementDOM element to render into
dataChartDataObject with labels (string[]) and series (array of { name, data })
options.titlestring''Chart title displayed above the chart area
options.responsivebooleantrueAuto-resize with container via ResizeObserver
options.animationbooleantrueEnable enter/update/exit animations
options.colorsstring[]Built-in paletteCustom color palette for series
options.legendboolean | objecttrueShow legend. Pass object for position: { position: 'bottom' }
options.tooltipboolean | functiontrueShow tooltip on hover. Pass function for custom formatting
options.gridbooleantrueShow background grid lines

Instance Methods

MethodReturnsDescription
render()voidRender the chart into the container
update(data)voidUpdate data with animated transitions
resize()voidForce recalculate layout (automatic when responsive)
destroy()voidRemove chart, cleanup observers and event listeners
toSVG()stringExport chart as raw SVG string
toImage(format)Promise<Blob>Export as PNG or JPEG blob
on(event, fn)voidListen to chart events: 'click', 'hover', 'legendClick'

Large datasets. For datasets over 10,000 points, enable options.downsample: true — the chart engine will use the Largest-Triangle-Three-Buckets algorithm to reduce points while preserving visual shape. Sparklines and heatmaps handle large datasets natively.

Supported Charts

Line Chart
Bar Chart (vertical & horizontal)
Area Chart
Stacked Charts
Pie Chart
Donut Chart
Scatter Plot
Bubble Chart
Radar Chart
Heatmap
Treemap
Gauge
Funnel Chart
Candlestick (OHLC)
Sparkline

Example: Line Chart

import { Chart } from '@hyperbridge/forge/chart';

const chart = new Chart({
  type: 'line',
  container: document.getElementById('chart'),
  data: {
    labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
    series: [
      { name: 'Sales', data: [120, 150, 200, 180, 220] },
      { name: 'Profit', data: [30, 45, 60, 50, 80] },
    ],
  },
  options: {
    title: 'Monthly Performance',
    responsive: true,
    animation: true,
  },
});

chart.render();

Example: Pie Chart

import { Chart } from '@hyperbridge/forge/chart';

const chart = new Chart({
  type: 'pie',
  container: document.getElementById('chart'),
  data: {
    labels: ['Desktop', 'Mobile', 'Tablet'],
    series: [
      { name: 'Traffic', data: [45, 35, 20] },
    ],
  },
  options: {
    title: 'Traffic by Device',
    colors: ['#6366f1', '#22d3ee', '#f97316'],
  },
});

chart.render();

Example: Real-Time Dashboard

import { Chart } from '@hyperbridge/forge/chart';

// Live-updating gauge
const gauge = new Chart({
  type: 'gauge',
  container: document.getElementById('cpu-gauge'),
  data: { value: 0, min: 0, max: 100, label: 'CPU %' },
  options: {
    colors: ['#22c55e', '#eab308', '#ef4444'], // green → yellow → red
    thresholds: [60, 85],
    animation: { duration: 300 },
  },
});
gauge.render();

// Update every second from WebSocket
ws.on('metrics', (data) => {
  gauge.update({ value: data.cpuPercent });
});

Example: Multi-Series Line + Overlays

import { ChartForge } from '@hyperbridge/forge/chart';

const chart = new ChartForge({
  container: '#revenue-chart',
  type:      'line',
  width:     900,
  height:    420,
  theme:     'dark',
});

chart.setData({
  labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug'],
  datasets: [
    {
      label:  'Revenue',
      data:   [42000, 58000, 39000, 71000, 63000, 88000, 74000, 95000],
      color:  '#6366f1',
      fill:   true,      // area fill
      smooth: true,      // cubic bezier curves
    },
    {
      label: 'Target',
      data:  [50000, 55000, 60000, 65000, 70000, 75000, 80000, 85000],
      color: '#22d3ee',
      dash:  [6, 3],    // dashed line
    },
  ],
});

// Statistical overlays
chart.addOverlay('sma',        { period: 3, color: '#f59e0b' });      // simple moving average
chart.addOverlay('trendline',  { color: '#ef4444', lineWidth: 1.5 }); // least-squares trendline
chart.addOverlay('bollinger',  { period: 5, stdDev: 2, color: 'rgba(99,102,241,0.2)' });

// Annotations
chart.annotate({ x: 'Apr', label: 'Campaign launch', color: '#22c55e' });
chart.annotate({ x: 'Jun', y: 88000, label: 'Record month', type: 'point' });

chart.render();

// Zoom into a date range
chart.zoom({ from: 'Mar', to: 'Jul' });

Example: Real-Time Streaming Chart

import { ChartForge } from '@hyperbridge/forge/chart';

const liveChart = new ChartForge({
  container: '#metrics-chart',
  type:      'line',
  width:     800,
  height:    300,
  streaming: {
    maxPoints:    60,      // rolling 60-point window
    updateInterval: 1000, // redraw every 1000ms
  },
});

liveChart.setData({
  datasets: [
    { label: 'CPU %',    data: [], color: '#6366f1' },
    { label: 'Memory %', data: [], color: '#22d3ee' },
  ],
});
liveChart.render();

// Push new data points from WebSocket
const ws = new WebSocket('/api/metrics');
ws.onmessage = ({ data }) => {
  const { cpu, memory, ts } = JSON.parse(data);
  liveChart.push([
    { label: ts, value: cpu },
    { label: ts, value: memory },
  ]);
};

Example: ChartDashboard — Multiple Charts

import { ChartDashboard, ChartForge } from '@hyperbridge/forge/chart';

// Manage multiple charts as a synchronized grid
const dashboard = new ChartDashboard('#analytics-dashboard', {
  columns: 2,
  gap:     16,
  theme:   'dark',
});

dashboard.add('revenue',  new ChartForge({ type: 'bar',    /* ... */ }));
dashboard.add('devices',  new ChartForge({ type: 'donut',  /* ... */ }));
dashboard.add('sessions', new ChartForge({ type: 'area',   /* ... */ }));
dashboard.add('funnel',   new ChartForge({ type: 'funnel', /* ... */ }));

dashboard.render();  // renders all charts, coordinates resize events

// Update all from one API response
const { data } = await fetch('/api/analytics').then(r => r.json());
dashboard.updateAll(data);

Example: Candlestick + Volume

import { ChartForge } from '@hyperbridge/forge/chart';

const ohlcv = new ChartForge({
  container: '#price-chart',
  type:      'candlestick',
  width:     1000,
  height:    500,
});

ohlcv.setData({
  datasets: [{
    label: 'BTC/USD',
    data: [
      // { t, o, h, l, c, v }
      { t: '2026-04-01', o: 71000, h: 73500, l: 70200, c: 72800, v: 1240000 },
      { t: '2026-04-02', o: 72800, h: 74100, l: 71500, c: 71900, v: 980000  },
      { t: '2026-04-03', o: 71900, h: 75000, l: 71800, c: 74500, v: 2100000 },
    ],
  }],
});

ohlcv.addOverlay('volume',  { position: 'bottom', height: 100 }); // volume bars below
ohlcv.addOverlay('rsi',     { period: 14, overbought: 70, oversold: 30 });
ohlcv.render();

Example: Export

// SVG string — resolution-independent, embed in HTML or PDF
const svg = chart.toSVG();
document.getElementById('preview').innerHTML = svg;

// PNG blob — for download or attaching to emails/PDFs
const blob = await chart.toImage('png', { scale: 2 }); // 2× for Retina
const url  = URL.createObjectURL(blob);
const a    = document.createElement('a');
a.href = url; a.download = 'chart.png'; a.click();

// Base64 — for embedding in email HTML
const b64 = await chart.toImage('png', { format: 'base64' });
// <img src="data:image/png;base64,${b64}">

forge/schema

Zod-compatible schema validation with TypeScript inference, coercion, transforms, and OpenAPI generation.

forge/schema is wire-compatible with Zod. Replace import { z } from 'zod' with import { z } from '@hyperbridge/forge/schema'. The full API surface — .object(), .string(), .parse(), .safeParse(), .refine(), .transform(), .infer — is identical. Existing Zod schemas migrate with a one-line import change.

Email validation checks TLD against 100+ known domains. Typos like gmial.com or yaho.com are caught and a suggestion field is returned in the error object — perfect for "Did you mean…?" UX.

Primitive Types

ConstructorTypeScript TypeDescription
z.string()stringUTF-8 string
z.number()numberIEEE-754 float
z.boolean()booleantrue / false
z.bigint()bigintArbitrary precision integer
z.date()DateJS Date object
z.symbol()symbolUnique symbol
z.undefined()undefinedExactly undefined
z.null()nullExactly null
z.any()anyBypass validation entirely
z.unknown()unknownUnknown — must narrow before use
z.never()neverRejects all values (useful in discriminated unions)
z.literal(value)typeof valueExact value match: z.literal('admin')

String Validators

MethodDescription
.min(n, msg?)Minimum character length
.max(n, msg?)Maximum character length
.length(n, msg?)Exact length
.email(msg?)RFC 5322 email with TLD check
.url(msg?)Valid URL (http/https/ftp)
.uuid(msg?)UUID v1–v5 format
.cuid(msg?)CUID / CUID2 format
.nanoid(msg?)NanoID format
.regex(pattern, msg?)Custom regex test
.startsWith(str, msg?)Must begin with prefix
.endsWith(str, msg?)Must end with suffix
.includes(str, msg?)Must contain substring
.ip(msg?)IPv4 or IPv6 address
.datetime(msg?)ISO 8601 datetime string
.trim()Transform: strip whitespace
.toLowerCase()Transform: force lowercase
.toUpperCase()Transform: force uppercase

Number Validators

MethodDescription
.min(n, msg?)≥ n
.max(n, msg?)≤ n
.gt(n) / .gte(n)> n / ≥ n (aliases for min/max with exclusive)
.lt(n) / .lte(n)< n / ≤ n
.int(msg?)Must be an integer (no fractional part)
.positive(msg?)> 0
.negative(msg?)< 0
.nonnegative(msg?)≥ 0
.nonpositive(msg?)≤ 0
.finite(msg?)Not Infinity or NaN
.safe(msg?)Within Number.MIN/MAX_SAFE_INTEGER
.multipleOf(n, msg?)Divisible by n (step validation)

Complex Types

ConstructorDescription
z.object(shape)Object with named fields. Strips unknown keys by default.
z.array(schema)Array where every element is validated by schema
z.tuple([s1, s2, ...])Fixed-length array with per-index schemas
z.record(keySchema, valSchema)Object with dynamic keys (like Record<string, T>)
z.map(keySchema, valSchema)ES6 Map
z.set(schema)ES6 Set
z.enum(['a', 'b'])One of a fixed set of string values
z.nativeEnum(MyEnum)TypeScript enum (numeric or string)
z.union([s1, s2])Value must satisfy at least one schema
z.discriminatedUnion(key, [s1, s2])Faster union via discriminant field (e.g., 'type')
z.intersection(s1, s2)Value must satisfy both schemas (like TypeScript &)
z.lazy(() => schema)Recursive/self-referential schemas
z.function()Validates a function, with .args() and .returns()

Object Methods

MethodDescription
.shapeAccess the shape object of an object schema
.pick({ key: true })Return a new schema with only selected keys
.omit({ key: true })Return a new schema without selected keys
.partial()Make all fields optional
.required()Make all fields required
.extend(shape)Add new fields to the schema
.merge(other)Merge two object schemas (like Object.assign for schemas)
.strict()Error on unknown keys instead of stripping them
.passthrough()Pass unknown keys through unvalidated
.strip()Strip unknown keys (default)
.keyof()Return an enum schema of the object's keys

Schema Modifiers

MethodTypeScript EffectDescription
.optional()T | undefinedField may be absent or undefined
.nullable()T | nullField may be null
.nullish()T | null | undefinedCombines optional + nullable
.default(value)TUse value when input is undefined
.catch(value)TUse value when validation fails (never throws)
.describe(text)TAttach description (used in OpenAPI)
.brand(symbol)T & BrandNominal typing — prevents accidental cross-assignment
.readonly()Readonly<T>Freezes object output

Transforms & Custom Validation

MethodDescription
.transform(fn)Map output value: z.string().transform(s => s.split(','))
.refine(fn, msg?)Custom validator — fn returns boolean. Sync or async.
.superRefine(fn)Full control — fn receives (val, ctx), call ctx.addIssue()
.preprocess(fn, schema)Transform input before validation: z.preprocess(Number, z.number())
.pipe(schema)Chain schemas: output of this becomes input to next

Parsing Methods

MethodReturnsOn failure
.parse(data)TThrows ZodError
.safeParse(data){ success: true, data: T } | { success: false, error: ZodError }Never throws
.parseAsync(data)Promise<T>Rejects with ZodError
.safeParseAsync(data)Promise<SafeParseResult>Never rejects

Coercion

The z.coerce namespace wraps schemas with a type-cast transform, useful for URL params and form data that arrive as strings:

import { z } from '@hyperbridge/forge/schema';

// URL param ?page=3 arrives as string
const querySchema = z.object({
  page:   z.coerce.number().int().positive().default(1),
  limit:  z.coerce.number().int().min(1).max(100).default(20),
  active: z.coerce.boolean().default(true),   // '1','true','yes' → true
  date:   z.coerce.date().optional(),          // ISO string → Date
});

querySchema.parse({ page: '3', limit: '50', active: '1' });
// → { page: 3, limit: 50, active: true }

TypeScript Inference

import { z } from '@hyperbridge/forge/schema';

const PostSchema = z.object({
  id:          z.number().int().positive(),
  title:       z.string().min(1).max(200),
  body:        z.string(),
  status:      z.enum(['draft', 'published', 'archived']).default('draft'),
  tags:        z.array(z.string()).max(10).default([]),
  publishedAt: z.date().optional(),
  author: z.object({
    id:    z.number(),
    email: z.string().email(),
    name:  z.string(),
  }),
});

// Infer the output type
type Post = z.infer<typeof PostSchema>;
// {
//   id: number;
//   title: string;
//   body: string;
//   status: 'draft' | 'published' | 'archived';
//   tags: string[];
//   publishedAt?: Date;
//   author: { id: number; email: string; name: string };
// }

// Input type (before defaults are applied)
type PostInput = z.input<typeof PostSchema>;

Async Validation

import { z } from '@hyperbridge/forge/schema';

const RegisterSchema = z.object({
  email: z.string().email().refine(
    async (email) => {
      const count = await db.count('users', { email });
      return count === 0;
    },
    { message: 'Email already registered' }
  ),
  username: z.string().min(3).max(20).regex(/^[a-z0-9_]+$/).refine(
    async (username) => {
      const taken = await db.exists('users', { username });
      return !taken;
    },
    { message: 'Username taken' }
  ),
  password:        z.string().min(8),
  confirmPassword: z.string(),
}).refine(data => data.password === data.confirmPassword, {
  message: 'Passwords do not match',
  path: ['confirmPassword'],
});

const result = await RegisterSchema.safeParseAsync(req.body);
if (!result.success) {
  return res.status(422).json({ errors: result.error.flatten() });
}

OpenAPI Generation

import { z } from '@hyperbridge/forge/schema';

const ProductSchema = z.object({
  id:       z.number().int().positive().describe('Unique product ID'),
  name:     z.string().min(1).max(100).describe('Product display name'),
  price:    z.number().positive().describe('Price in USD'),
  category: z.enum(['electronics', 'clothing', 'food']).describe('Product category'),
  inStock:  z.boolean().default(true).describe('Whether product is available'),
});

// Generate OpenAPI 3.0 schema component
const openApiSchema = ProductSchema.toOpenAPI({ title: 'Product' });
// {
//   title: 'Product',
//   type: 'object',
//   required: ['id', 'name', 'price', 'category'],
//   properties: {
//     id:       { type: 'integer', minimum: 1, description: 'Unique product ID' },
//     name:     { type: 'string', minLength: 1, maxLength: 100 },
//     price:    { type: 'number', exclusiveMinimum: 0 },
//     category: { type: 'string', enum: ['electronics', 'clothing', 'food'] },
//     inStock:  { type: 'boolean', default: true },
//   },
// }

forge/test

Jest-compatible test runner — describe/it/expect, mocking, spies, DOM testing, HTTP mocking, property-based testing, snapshots, benchmarks, and coverage. Zero config, zero dependencies.

Jest-compatible API. The same describe/it/expect/fn/spyOn API you already know — but forge/test runs without Babel, without ts-jest, and without 47 transitive dependencies. Drop in as a replacement or use it fresh.

Built-in component testing. render(), screen, and fireEvent work like React Testing Library but are built for forge/client. No @testing-library/react, no jest-dom, no user-event needed.

Runner Configuration

OptionTypeDefaultDescription
includestring[]['**/*.test.js']Glob patterns for test files. Also matches *.spec.js and *.test.ts.
excludestring[]['node_modules', 'dist']Glob patterns to skip
parallelbooleantrueRun test files in isolated worker threads
timeoutnumber5000Per-test timeout in ms. Override per-test with it('...', fn, 10000)
coveragebooleanfalseCollect V8 coverage. Report in terminal + write coverage/.
coverageThresholdobject{}Fail CI if coverage drops: { lines: 80, branches: 70 }
bailboolean | numberfalseStop after N failures. true = stop after first failure.
watchbooleanfalseRe-run affected tests on file change (uses file dependency graph)
reporterstring'default''default', 'dot', 'json', 'junit', or custom reporter function

Expect Matchers

MatcherDescription
.toBe(val)Strict equality (===). Use for primitives.
.toEqual(val)Deep structural equality. Use for objects and arrays.
.toStrictEqual(val)Deep equality + checks object types (class instances)
.toBeCloseTo(num, digits?)Floating-point comparison to N decimal places (default 2)
.toBeTruthy() / .toBeFalsy()Truthy/falsy in boolean context
.toBeNull() / .toBeUndefined()Null / undefined checks
.toBeDefined()Not undefined
.toBeNaN()Is NaN
.toBeGreaterThan(n) / .toBeLessThan(n)Numeric comparison
.toBeGreaterThanOrEqual(n) / .toBeLessThanOrEqual(n)Numeric comparison (inclusive)
.toBeInstanceOf(Class)instanceof check
.toContain(item)Array includes item or string contains substring
.toContainEqual(item)Array contains item with deep equality
.toHaveLength(n)Array/string .length === n
.toHaveProperty(path, val?)Object has nested property (dot path: 'user.email')
.toMatch(regex | string)String matches regex or contains substring
.toThrow(msg?)Sync function throws. Optional message match.
.toThrowError(Class)Throws specific error class
.toMatchSnapshot()Serialize and compare to stored snapshot
.toMatchInlineSnapshot(str)Inline snapshot (stored in test file)
.resolves.toBe(val)Chain for Promise assertions
.rejects.toThrow(msg?)Promise rejects with error
.toHaveBeenCalled()Mock was called at least once
.toHaveBeenCalledTimes(n)Mock call count equals n
.toHaveBeenCalledWith(...args)Mock called with specific arguments (deep equal)
.toHaveBeenLastCalledWith(...args)Most recent call had these arguments
.toHaveReturnedWith(val)Mock returned specific value

Snapshot files. Stored in __snapshots__/ next to the test file. Run with --update-snapshots (or -u) to regenerate after intentional changes. Commit snapshots to version control — they are your visual regression baseline.

Mock API (fn / spyOn)

MethodDescription
fn(impl?)Create a mock function. Optionally provide default implementation.
spyOn(obj, method)Replace obj.method with a spy that passes through to the original by default
mock.mockReturnValue(val)Always return val
mock.mockReturnValueOnce(val)Return val for the next call only, then fall through
mock.mockResolvedValue(val)Return Promise.resolve(val)
mock.mockRejectedValue(err)Return Promise.reject(err)
mock.mockImplementation(fn)Replace the mock's full implementation
mock.mockClear()Reset call history (not implementation)
mock.mockReset()Reset call history AND implementation
mock.mockRestore()Restore original (spyOn only)
mock.callsArray of call argument arrays: [[arg1, arg2], [arg1]]
mock.resultsArray of { type: 'return'|'throw', value }

Example: Unit Tests with Lifecycle Hooks

import { describe, it, expect, beforeEach, afterEach, beforeAll, afterAll } from '@hyperbridge/forge/test';
import { UserService } from './user-service.js';

describe('UserService', () => {
  let service;
  let db;

  beforeAll(async () => {
    db = await createTestDatabase();
  });

  afterAll(async () => {
    await db.destroy();
  });

  beforeEach(async () => {
    await db.truncate('users');
    service = new UserService(db);
  });

  it('creates a user', async () => {
    const user = await service.create({ email: 'a@b.com', name: 'Alex' });
    expect(user.id).toBeGreaterThan(0);
    expect(user.email).toBe('a@b.com');
    expect(user.createdAt).toBeInstanceOf(Date);
  });

  it('throws on duplicate email', async () => {
    await service.create({ email: 'dup@b.com' });
    await expect(service.create({ email: 'dup@b.com' })).rejects.toThrowError('Email already exists');
  });

  it('finds by id', async () => {
    const created = await service.create({ email: 'find@b.com', name: 'Find Me' });
    const found   = await service.findById(created.id);
    expect(found).toEqual(expect.objectContaining({ email: 'find@b.com', name: 'Find Me' }));
  });
});

Example: Mocking & Spies

import { describe, it, expect, fn, spyOn, beforeEach } from '@hyperbridge/forge/test';
import { NotificationService } from './notifications.js';
import * as mailer from '@hyperbridge/forge/mail';

describe('NotificationService', () => {
  let sendMailSpy;

  beforeEach(() => {
    sendMailSpy = spyOn(mailer.SMTPClient.prototype, 'sendMail')
      .mockResolvedValue({ messageId: 'test-123' });
  });

  afterEach(() => {
    sendMailSpy.mockRestore();
  });

  it('sends welcome email on registration', async () => {
    const svc = new NotificationService();
    await svc.sendWelcome({ email: 'user@example.com', name: 'Alex' });

    expect(sendMailSpy).toHaveBeenCalledTimes(1);
    expect(sendMailSpy).toHaveBeenCalledWith(
      expect.objectContaining({
        to:      'user@example.com',
        subject: expect.stringContaining('Welcome'),
      })
    );
  });

  it('retries on transient failure', async () => {
    sendMailSpy
      .mockRejectedValueOnce(new Error('SMTP connection refused'))
      .mockResolvedValueOnce({ messageId: 'retry-ok' });

    const svc = new NotificationService({ retries: 2 });
    await svc.sendWelcome({ email: 'retry@example.com', name: 'Retry' });

    expect(sendMailSpy).toHaveBeenCalledTimes(2);
  });
});

Example: DOM / Component Testing

import { describe, it, expect, render, screen, fireEvent, waitFor } from '@hyperbridge/forge/test';
import { Counter, LoginForm, UserList } from './components.js';

describe('Counter', () => {
  it('renders initial count', () => {
    render(Counter, { props: { initial: 5 } });
    expect(screen.getByText('Count: 5')).toBeDefined();
  });

  it('increments on button click', async () => {
    render(Counter);
    fireEvent.click(screen.getByRole('button', { name: 'Increment' }));
    expect(screen.getByText('Count: 1')).toBeDefined();
  });

  it('matches snapshot', () => {
    const { container } = render(Counter);
    expect(container.innerHTML).toMatchSnapshot();
  });
});

describe('LoginForm', () => {
  it('shows error on empty submit', async () => {
    render(LoginForm);
    fireEvent.submit(screen.getByRole('form'));
    await waitFor(() => {
      expect(screen.getByText('Email is required')).toBeDefined();
    });
  });

  it('calls onSubmit with form data', async () => {
    const onSubmit = fn();
    render(LoginForm, { props: { onSubmit } });

    await fireEvent.type(screen.getByLabelText('Email'),    'user@example.com');
    await fireEvent.type(screen.getByLabelText('Password'), 'pass1234');
    fireEvent.submit(screen.getByRole('form'));

    await waitFor(() => {
      expect(onSubmit).toHaveBeenCalledWith({ email: 'user@example.com', password: 'pass1234' });
    });
  });
});

Example: Property-Based Testing

import { describe, it, expect, forAll, gen } from '@hyperbridge/forge/test';
import { sortByAge } from './sort.js';

describe('sortByAge', () => {
  // Runs 100 random inputs automatically
  forAll('is idempotent', gen.array(gen.object({ name: gen.string(), age: gen.integer(0, 120) })), (users) => {
    const once  = sortByAge(users);
    const twice = sortByAge(once);
    expect(once).toEqual(twice); // sorting twice == sorting once
  });

  forAll('output is sorted', gen.array(gen.object({ age: gen.integer(0, 120) }), { minLength: 1 }), (users) => {
    const sorted = sortByAge(users);
    for (let i = 0; i < sorted.length - 1; i++) {
      expect(sorted[i].age).toBeLessThanOrEqual(sorted[i + 1].age);
    }
  });
});

// Available generators
gen.integer(min, max)        // random integer
gen.float(min, max)          // random float
gen.string(minLen?, maxLen?) // random alphanumeric string
gen.boolean()                // random boolean
gen.enum(['a', 'b', 'c'])    // random enum value
gen.array(itemGen, opts?)    // random array
gen.object(shape)            // random object from shape map
gen.nullable(gen)            // randomly null or the generator's value
gen.oneOf([gen1, gen2])      // pick one of multiple generators

Example: HTTP Mocking

import { describe, it, expect, mockFetch, clearFetchMocks } from '@hyperbridge/forge/test';
import { GithubClient } from './github.js';

describe('GithubClient', () => {
  afterEach(clearFetchMocks);

  it('fetches repos', async () => {
    mockFetch('GET https://api.github.com/users/octocat/repos', {
      status: 200,
      body:   [{ id: 1, name: 'Hello-World', stargazers_count: 1000 }],
    });

    const client = new GithubClient({ token: 'test' });
    const repos  = await client.getRepos('octocat');

    expect(repos).toHaveLength(1);
    expect(repos[0].name).toBe('Hello-World');
  });

  it('throws on 404', async () => {
    mockFetch('GET https://api.github.com/users/nobody/repos', { status: 404 });
    const client = new GithubClient({ token: 'test' });
    await expect(client.getRepos('nobody')).rejects.toThrow('Not found');
  });
});

Example: Benchmarks

import { bench, describe } from '@hyperbridge/forge/test';
import { z } from '@hyperbridge/forge/schema';

const UserSchema = z.object({ id: z.number(), email: z.string().email(), name: z.string() });
const user = { id: 1, email: 'a@b.com', name: 'Alex' };

describe('Schema performance', () => {
  bench('z.parse (valid)',   () => UserSchema.parse(user),          { iterations: 50_000 });
  bench('z.safeParse (valid)', () => UserSchema.safeParse(user),    { iterations: 50_000 });
  bench('z.safeParse (invalid)', () => UserSchema.safeParse({ id: 'bad' }), { iterations: 50_000 });
});

// Output:
// z.parse (valid)           ~1,840,000 ops/s  (0.54µs/op)
// z.safeParse (valid)       ~1,750,000 ops/s  (0.57µs/op)
// z.safeParse (invalid)     ~1,200,000 ops/s  (0.83µs/op)

forge/mail

SMTP client with DKIM signing, connection pooling, templating, queuing, open/click tracking, bounce management, calendar invites, and A/B testing. No Nodemailer, no external transport libraries.

Two transports, one API. forge/mail supports direct SMTP (RFC 5321) and the Resend HTTP API. Switch with a config change — your sendMail() calls stay identical. Use SMTP in development, Resend in production.

DKIM signing built in. Pass your RSA private key and domain selector — forge/mail signs every outgoing email with DKIM automatically. No additional library, no DNS configuration tool. Just provide the key and it works.

Never hardcode credentials. Use process.env.SMTP_PASS, never a string literal. Gmail requires an App Password (not your account password) — generate one at Google Account → Security → App Passwords.

SMTPClient Configuration

OptionTypeDefaultDescription
hoststringSMTP server hostname (e.g. smtp.postmarkapp.com)
portnumber587587 for STARTTLS, 465 for implicit TLS, 25 for no TLS
securebooleanfalsetrue for port 465 (TLS from connect). Leave false for 587 (STARTTLS).
authobject{ user, pass } for LOGIN/PLAIN. { type: 'oauth2', accessToken } for OAuth2.
dkimobjectnull{ domain, keySelector, privateKey } — RSA-SHA256 DKIM signing
poolbooleanfalseEnable persistent connection pool (faster for bulk sending)
maxConnectionsnumber5Max simultaneous pooled SMTP connections
rateLimitnumber0Max messages per second. 0 = unlimited.
timeoutnumber30000Connection + data timeout in ms
tlsobject{}Node.js TLS options: { rejectUnauthorized, ca, cert, key }

sendMail Options

FieldTypeDescription
fromstringSender: 'Display Name <addr@domain.com>'
tostring | string[]Recipient(s). Max 50 per call (SMTP limit)
cc / bccstring | string[]CC and BCC recipients
replyTostringReply-To header address
subjectstringSubject line. UTF-8, encoded automatically.
htmlstringHTML body. Plain text is auto-generated by stripping tags if text is omitted.
textstringPlain text fallback (used by screen readers and text-only clients)
attachmentsAttachment[]{ filename, content: Buffer|string, contentType, encoding? }
inlineImagesobject[]{ cid, content, contentType } — embed images in HTML via cid: src
headersobjectCustom SMTP headers: { 'X-Campaign-ID': 'spring-2026' }
prioritystring'high', 'normal', 'low' — sets X-Priority header

Additional Classes

ClassDescription
EmailTemplateMustache-compatible templates: {{var}}, {{#if}}, {{#each}}, partials, layouts
MailQueueIn-memory queue with rate limiting, retry, and flush. Feeds into any transport.
MailTrackerOpen/click pixel tracking via HTTP webhooks. Returns { opens, clicks, lastOpened }
BounceListHard bounce registry. add(email), has(email), import(list). Prevent sending to bounced addresses.
CalendarInviteGenerate iCal (.ics) attachments for meeting invites. Compatible with Outlook, Google Calendar, Apple Calendar.
ABTestSplit subject lines or HTML variants across recipients. Track open rates per variant.

Example: SMTP with DKIM

import { SMTPClient } from '@hyperbridge/forge/mail';
import fs from 'node:fs';

const smtp = new SMTPClient({
  host:   'smtp.postmarkapp.com',
  port:   587,
  auth:   { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS },
  dkim:   {
    domain:     'hyperbridge.digital',
    keySelector: 'default',
    privateKey:  fs.readFileSync('./keys/dkim-private.pem', 'utf8'),
  },
  pool:   true,
  maxConnections: 5,
  rateLimit: 20,  // 20 msgs/sec
});

// Transactional email with attachment
await smtp.sendMail({
  from:    'HyperBridge Digital <noreply@hyperbridge.digital>',
  to:      'user@example.com',
  subject: 'Your invoice is ready',
  html:    `<h1>Invoice #1042</h1><p>See attached.</p>`,
  attachments: [{
    filename:    'invoice-1042.pdf',
    content:     pdfBuffer,
    contentType: 'application/pdf',
  }],
});

Example: EmailTemplate

import { SMTPClient, EmailTemplate } from '@hyperbridge/forge/mail';

// Template with conditionals and loops
const orderTemplate = new EmailTemplate({
  subject: 'Order #{{orderId}} confirmed',
  html: `
    <h1>Hi {{firstName}},</h1>
    <p>Your order #{{orderId}} has been confirmed.</p>

    <table>
      {{#each items}}
      <tr>
        <td>{{name}}</td>
        <td>{{qty}}</td>
        <td>{{price}}</td>
      </tr>
      {{/each}}
    </table>

    <p>Total: {{total}}</p>

    {{#if isPremium}}
    <p>🎁 As a Premium member, you get free shipping!</p>
    {{/if}}
  `,
});

const email = orderTemplate.render({
  firstName: 'Priya',
  orderId:   'ORD-9981',
  items: [
    { name: 'HBForge License', qty: 1, price: '$2,400' },
    { name: 'Support',         qty: 3, price: '$450'   },
  ],
  total:     '$2,850',
  isPremium: true,
});

await smtp.sendMail({ from: 'orders@app.com', to: 'priya@example.com', ...email });

Example: MailQueue — Bulk Newsletter

import { SMTPClient, MailQueue, EmailTemplate, BounceList } from '@hyperbridge/forge/mail';

const bounces = new BounceList();
await bounces.import(await db.getBounces()); // load from DB

const queue = new MailQueue(smtp, {
  rateLimit: 50,           // 50 msgs/sec
  concurrency: 5,
  retries: 2,
  onError: (err, msg) => console.error('Failed:', msg.to, err.message),
});

const newsletter = new EmailTemplate({
  subject: 'HBForge April Update',
  html:    await fs.promises.readFile('./emails/newsletter.html', 'utf8'),
});

for (const subscriber of subscribers) {
  if (bounces.has(subscriber.email)) continue; // skip bounced

  queue.enqueue({
    to:   subscriber.email,
    from: 'newsletter@hyperbridge.digital',
    ...newsletter.render({ name: subscriber.firstName }),
  });
}

const result = await queue.flush();
console.log(`Sent: ${result.sent}, Failed: ${result.failed}`);

Example: CalendarInvite

import { SMTPClient, CalendarInvite } from '@hyperbridge/forge/mail';

const invite = new CalendarInvite({
  uid:         'meet-2026-04-20@hyperbridge.digital',
  summary:     'HBForge Onboarding Call',
  description: 'Welcome call with your HBForge onboarding team.',
  location:    'Google Meet — meet.google.com/abc-defg-hij',
  organizer:   { name: 'HyperBridge Digital', email: 'hello@hyperbridge.digital' },
  attendees:   [{ name: 'Alex', email: 'alex@client.com', rsvp: true }],
  start:       new Date('2026-04-20T10:00:00+05:30'),
  end:         new Date('2026-04-20T11:00:00+05:30'),
  timezone:    'Asia/Kolkata',
  reminders:   [{ minutes: 15 }, { minutes: 1440 }], // 15 min + 1 day before
});

await smtp.sendMail({
  from:        'hello@hyperbridge.digital',
  to:          'alex@client.com',
  subject:     'HBForge Onboarding Call — April 20',
  html:        '<p>You are invited to an onboarding call. See the attached invite.</p>',
  attachments: [invite.toAttachment()], // .ics attachment
});

forge/pdf

Pure JavaScript PDF 1.7 generation — text, tables, images, custom fonts, encryption, QR codes, and streaming output.

No headless browser. forge/pdf writes valid PDF 1.7 binary output directly — no Puppeteer, no Chromium, no wkhtmltopdf, no system fonts required. The output renders identically in Adobe Reader, macOS Preview, Chrome, Firefox, and every major PDF viewer.

Streaming output. For large documents (1,000+ pages), use pdf.build('stream') to pipe directly to an HTTP response or file write stream. Memory usage stays constant regardless of page count — critical for invoice batch jobs and report generators.

Constructor

OptionTypeDefaultDescription
pageSizestring | object'A4''A4', 'Letter', 'Legal', 'A3', 'A5', or { width, height } in points
orientationstring'portrait''portrait' or 'landscape'
marginnumber | object40Margin in points. Object: { top, right, bottom, left }. 72 points = 1 inch.
compressbooleantrueFlate-compress content streams for smaller output
metadataobject{}{ title, author, subject, keywords, creator } — embedded in PDF metadata
encryptobjectnull{ userPassword, ownerPassword, permissions: ['print', 'copy'] }

Coordinate origin. PDF natively places (0,0) at the bottom-left. forge/pdf flips this — (0,0) is the top-left, matching web conventions. An A4 page is 595 × 842 points. The usable area with 40pt margins is 515 × 762 points.

Drawing API

MethodSignatureDescription
text()(content, options)Add text at { x, y, fontSize, color, font, align, lineHeight, maxWidth }
line()(options)Draw line: { x1, y1, x2, y2, width, color, dash }
rect()(options)Rectangle: { x, y, width, height, fill, stroke, strokeWidth, radius }
circle()(options)Circle: { cx, cy, r, fill, stroke }
image()(src, options)Embed PNG/JPEG: { x, y, width, height, opacity }. Accepts Buffer, file path, or data URI.
table()(options)Auto-layout table: { x, y, columns, rows, headerStyle, cellPadding, stripedRows, borderColor }
addPage()(options?)Add new page. Can override pageSize and orientation per page.
setHeader()(fn | string)Global header rendered on every page. Function receives ({ page, total, doc }).
setFooter()(fn | string)Global footer. Use '{page} of {total}' for automatic page numbering.
watermark()(text, options)Diagonal text across every page: { opacity, angle, color, fontSize }
setFont()(name, size)Set current font. Built-in: Helvetica, Times, Courier. Load custom: loadFont(name, ttfBuffer).
setColor()(hex)Set current fill/stroke color (persists until changed)
moveTo()(y)Move cursor to absolute Y position (for sequential text flow)
build()(format?)Finalize document. Returns Uint8Array (default), 'base64' string, or 'stream' (Node.js ReadableStream).

QR Codes & Barcodes

ClassMethodDescription
QRCodeQRCode.generate(data, options)Generate QR code as PNG Buffer. Options: { size, errorLevel, margin, dark, light }
QRCodeQRCode.toDataURL(data)Generate as data URI (for browser use)
BarcodeBarcode.generate(data, options)Generate barcode PNG. Options: { format: 'CODE128'|'EAN13'|'QR', width, height }

Example: Professional Invoice

import { PDFForge, QRCode } from '@hyperbridge/forge/pdf';

const doc = new PDFForge({
  pageSize: 'A4',
  margin:   { top: 40, right: 50, bottom: 60, left: 50 },
  metadata: { title: 'Invoice #1042', author: 'HyperBridge Digital' },
});

// Header bar
doc.rect({ x: 0, y: 0, width: 595, height: 80, fill: '#6366f1' });
doc.setFont('Helvetica-Bold', 22);
doc.setColor('#ffffff');
doc.text('HyperBridge Digital', { x: 50, y: 28 });
doc.setFont('Helvetica', 11);
doc.text('hyperbridge.digital · Chennai, India', { x: 50, y: 52 });

// Invoice number (right-aligned)
doc.setFont('Helvetica-Bold', 18);
doc.text('INVOICE #1042', { x: 340, y: 35, align: 'right', maxWidth: 205 });

doc.setColor('#111111');

// Bill to / date block
doc.setFont('Helvetica-Bold', 10);
doc.text('BILL TO', { x: 50, y: 105 });
doc.setFont('Helvetica', 10);
doc.text('Acme Technologies\nMumbai, India\nbilling@acme.example', { x: 50, y: 118, lineHeight: 1.5 });

doc.setFont('Helvetica-Bold', 10);
doc.text('DATE', { x: 380, y: 105 });
doc.setFont('Helvetica', 10);
doc.text('April 14, 2026', { x: 380, y: 118 });
doc.text('DUE: April 28, 2026', { x: 380, y: 132 });

// Horizontal rule
doc.line({ x1: 50, y1: 175, x2: 545, y2: 175, width: 0.5, color: '#cccccc' });

// Items table
doc.table({
  x: 50, y: 185,
  columns: [
    { header: 'Description', width: 260 },
    { header: 'Qty',         width: 55, align: 'right' },
    { header: 'Unit Price',  width: 90, align: 'right' },
    { header: 'Amount',      width: 90, align: 'right' },
  ],
  rows: [
    ['HBForge Enterprise License (Annual)', '1', '$2,400.00', '$2,400.00'],
    ['Onboarding & Setup (hrs)',             '4', '  $150.00', '  $600.00'],
    ['Priority Support (Monthly)',           '3', '  $200.00', '  $600.00'],
  ],
  headerStyle: { fillColor: '#f5f7ff', bold: true, fontSize: 10 },
  cellPadding: 9,
  borderColor: '#e5e7eb',
  stripedRows: true,
});

// Totals
doc.setFont('Helvetica-Bold', 11);
doc.text('Total: $3,600.00', { x: 545, y: 320, align: 'right' });

// QR code for payment URL
const qr = QRCode.generate('https://pay.hyperbridge.digital/inv/1042', { size: 80 });
doc.image(qr, { x: 450, y: 680, width: 80, height: 80 });
doc.setFont('Helvetica', 8);
doc.setColor('#888888');
doc.text('Scan to pay', { x: 490, y: 765, align: 'center', maxWidth: 80 });

// Footer
doc.setFooter(({ page, total }) =>
  `Page ${page} of ${total}  ·  Thank you for your business!`
);

const pdfBytes = doc.build();
await fs.writeFile('invoice-1042.pdf', pdfBytes);

Example: Custom TTF Fonts

import { PDFForge } from '@hyperbridge/forge/pdf';
import fs from 'node:fs';

const doc = new PDFForge({ pageSize: 'A4' });

// Load custom font from TTF buffer
const interBold    = fs.readFileSync('./fonts/Inter-Bold.ttf');
const interRegular = fs.readFileSync('./fonts/Inter-Regular.ttf');

doc.loadFont('Inter-Bold',    interBold);
doc.loadFont('Inter-Regular', interRegular);

doc.setFont('Inter-Bold', 24);
doc.text('Custom Fonts', { x: 50, y: 60 });

doc.setFont('Inter-Regular', 12);
doc.text(
  'This paragraph uses the Inter typeface, loaded from a .ttf file. ' +
  'forge/pdf subsets the font automatically — only the glyphs actually ' +
  'used in the document are embedded, keeping file size minimal.',
  { x: 50, y: 100, maxWidth: 495, lineHeight: 1.6 }
);

const pdf = doc.build();
await fs.promises.writeFile('custom-font.pdf', pdf);

Example: Password-Protected PDF

import { PDFForge } from '@hyperbridge/forge/pdf';

const doc = new PDFForge({
  pageSize: 'A4',
  encrypt: {
    userPassword:  'view-password',   // required to open
    ownerPassword: 'admin-password',  // required to change permissions
    permissions: ['print'],           // allow printing, disallow copy/edit
  },
});

doc.setFont('Helvetica-Bold', 18);
doc.text('Confidential Report', { x: 50, y: 50 });
doc.setFont('Helvetica', 12);
doc.text('This document is encrypted. Copying is disabled.', { x: 50, y: 80 });

const pdf = doc.build();
await fs.promises.writeFile('confidential.pdf', pdf);

Example: Streaming to HTTP Response

import { PDFForge } from '@hyperbridge/forge/pdf';

// forge/server route — streams PDF directly to client
router.get('/invoices/:id/pdf', async (req, res) => {
  const invoice = await Invoice.findById(req.params.id);
  const doc = new PDFForge({ pageSize: 'A4' });

  buildInvoiceContent(doc, invoice);

  const stream = doc.build('stream');

  res.setHeader('Content-Type', 'application/pdf');
  res.setHeader('Content-Disposition', `attachment; filename="invoice-${invoice.id}.pdf"`);
  stream.pipe(res);
});

Example: PDFReader — Extract Text

import { PDFReader } from '@hyperbridge/forge/pdf';

const reader = new PDFReader(pdfBuffer);

// Extract all text
const text = await reader.extractText();

// Get page count
const pageCount = reader.pageCount;

// Extract text per page
for (let p = 1; p <= pageCount; p++) {
  const pageText = await reader.extractText({ page: p });
  console.log(`Page ${p}:`, pageText);
}

// Read metadata
const meta = reader.metadata;
// { title, author, creator, createdAt, modifiedAt, pageCount }

forge/notify

Unified notification system — DOM toasts, multi-channel delivery (email, SMS, push, WebSocket), retry queue, deduplication, and notification center.

Multi-channel, single API. forge/notify unifies DOM toasts, email, SMS, Web Push, and WebSocket delivery behind one API. Define channels once during setup, then call nm.send() — it routes to all configured transports without per-channel boilerplate.

Smart deduplication. The built-in DedupeFilter uses a djb2 hash of the notification content. Identical messages within the dedup window (default 5 s) are collapsed to one — no more stacking 10 "Connection lost" toasts when a WebSocket flickers.

ToastManager

ToastManager handles DOM toast notifications in the browser. It injects its own CSS and manages the toast container automatically.

OptionTypeDefaultDescription
positionstring'top-right''top-right', 'top-left', 'bottom-right', 'bottom-left', 'top-center', 'bottom-center'
maxStacknumber5Max simultaneous visible toasts. Oldest dismissed when exceeded.
defaultDurationnumber4000Auto-dismiss delay in ms. 0 = persistent (must be closed manually).
dedupWindownumber5000Ignore duplicate content within this ms window. 0 = disabled.
pauseOnHoverbooleantruePause auto-dismiss timer while mouse is over the toast

ToastManager Methods

MethodReturnsDescription
.success(message, opts?)string (id)Green success toast
.error(message, opts?)stringRed error toast. Default duration 6000ms (longer for errors).
.warning(message, opts?)stringAmber warning toast
.info(message, opts?)stringBlue info toast
.loading(message, opts?)stringSpinner toast — persists until resolved
.promise(promise, messages)Promise<T>Shows loading → success/error. messages: { loading, success, error }
.dismiss(id)voidDismiss specific toast by ID
.dismissAll()voidDismiss all visible toasts immediately
.update(id, opts)voidUpdate content/type of an existing toast (e.g., loading → success)

Accessibility. Toasts use role="status" for success/info and role="alert" for warnings/errors. The ARIA live region announces new toasts to screen readers without stealing focus. Modal notifications trap focus and restore it on dismiss.

NotificationManager

NotificationManager is the server-side multi-channel dispatcher. Configure channels once, then .send() routes to all of them.

Channel keyConfig fieldsDescription
email{ type: 'smtp', host, port, auth, from }Routes to SMTPClient internally
sms{ type: 'twilio', accountSid, authToken, from }Twilio SMS API
push{ type: 'webpush', vapidPublicKey, vapidPrivateKey }RFC 8291 Web Push
websocket{ server: wsServer }Broadcast to connected WebSocket clients
slack{ webhookUrl }Slack incoming webhook

NotificationCenter

A persistent in-memory notification inbox — track read/unread state, pin, archive, group, and paginate notifications.

MethodReturnsDescription
.add(notification)string (id)Add notification to center. { id, title, body, type, read, pinned, groupId }
.getAll(opts?)Notification[]All notifications. opts: { page, limit, filter: 'unread'|'pinned' }
.getUnread()Notification[]Only unread notifications
.markRead(id)voidMark single notification as read
.markAllRead()voidMark all as read
.pin(id)voidPin notification (always shown at top)
.archive(id)voidArchive (hidden from default view)
.unreadCount()numberBadge count
.on(event, fn)voidEvents: 'add', 'read', 'archive'

RetryQueue

Automatically retries failed notification deliveries with exponential backoff.

OptionDefaultDescription
maxAttempts3Max delivery attempts before giving up
initialDelay1000First retry delay in ms
backoffFactor2Multiplier per attempt: 1s, 2s, 4s, 8s…
maxDelay30000Cap on retry delay in ms
jittertrueAdd random ±20% jitter to prevent thundering herd

Example: Toast Notifications

import { ToastManager } from '@hyperbridge/forge/notify';

const toast = new ToastManager({ position: 'top-right', maxStack: 5 });

// Basic types
toast.success('File uploaded successfully');
toast.error('Connection failed — please retry');
toast.warning('Your session expires in 5 minutes');
toast.info('New version available — reload to update');

// Custom duration + action button
toast.success('3 files deleted', {
  duration: 6000,
  action: { label: 'Undo', onClick: () => restoreFiles() },
});

// Loading → result (the most common pattern)
await toast.promise(
  fetch('/api/data', { method: 'POST', body: payload }),
  {
    loading: 'Saving changes…',
    success: (data) => `Saved ${data.count} records`,
    error:   (err)  => `Save failed: ${err.message}`,
  }
);

// Update an existing toast
const id = toast.loading('Processing…');
await runHeavyTask();
toast.update(id, { type: 'success', message: 'Done!' });

Example: Multi-Channel NotificationManager

import { NotificationManager, RetryQueue } from '@hyperbridge/forge/notify';

const nm = new NotificationManager({
  channels: {
    email: {
      type: 'smtp',
      host: 'smtp.postmarkapp.com',
      port: 587,
      auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS },
      from: 'noreply@myapp.com',
    },
    sms: {
      type: 'twilio',
      accountSid: process.env.TWILIO_SID,
      authToken:  process.env.TWILIO_TOKEN,
      from:       '+15551234567',
    },
  },
  retry: new RetryQueue({ maxAttempts: 3, backoffFactor: 2 }),
});

// Route to all configured channels
await nm.send({
  to:       { email: 'user@example.com', phone: '+919876543210' },
  channels: ['email', 'sms'],
  subject:  'Security alert',
  body:     'A new login was detected from Chennai, India.',
  type:     'warning',
  priority: 'urgent',
});

// Channel events — track delivery status
nm.on('delivered', ({ channel, notification }) =>
  console.log(`Delivered via ${channel}:`, notification.id));
nm.on('failed', ({ channel, error, attempt }) =>
  console.error(`Channel ${channel} failed (attempt ${attempt}):`, error));

Example: NotificationCenter

import { NotificationCenter, QuietHours } from '@hyperbridge/forge/notify';

const center = new NotificationCenter({ maxHistory: 200 });

// Add notifications (typically from server events or WebSocket)
center.add({ id: '1', title: 'PR merged',     body: 'main ← feature/auth',       type: 'success' });
center.add({ id: '2', title: 'Build failed',   body: 'deploy-prod failed at step 3', type: 'error' });
center.add({ id: '3', title: 'New comment',    body: 'Alex replied to your post',  type: 'info' });

// Badge count (for notification bell icon)
center.unreadCount(); // 3

// Mark interactions
center.markRead('1');
center.pin('2');       // always at top
center.archive('3');   // hide from default view

// Paginated listing
const page1 = center.getAll({ page: 1, limit: 20 });
const unread = center.getUnread();

// Quiet hours — suppress delivery between 22:00 and 08:00
const quiet = new QuietHours({ start: '22:00', end: '08:00', timezone: 'Asia/Kolkata' });
quiet.isSilent(new Date()); // true if currently in quiet window

// Subscribe to changes (update badge in UI)
center.on('add',     () => updateBadge(center.unreadCount()));
center.on('read',    () => updateBadge(center.unreadCount()));
center.on('archive', () => updateBadge(center.unreadCount()));

forge/cli

CLI framework with commands, options, prompts, colors, spinners, and config management.

Commander + Inquirer + Chalk in one module. forge/cli replaces the classic CLI trio — commander for parsing, inquirer for prompts, chalk for colors — with a single zero-dep module. Define commands, add interactive prompts, show spinners, and format output, all from one import.

Auto-generated help. Every command automatically gets --help output with usage, options, and examples. The help text is generated from your command definitions — no manual formatting. Run myapp --help and get a polished, colored help screen.

CLI Constructor

ParameterTypeDefaultDescription
namestringCLI tool name (used in help text and error messages)
versionstringVersion string (enables --version flag automatically)
descriptionstring''Tool description shown in help
strictbooleantrueError on unknown options (disable for passthrough CLIs)

Command Definition

MethodParametersDescription
command(name, handler)string, async functionRegister a command. Handler receives (args, options)
option(name, config)string, { alias, type, default, description }Add option to current command
argument(name, config)string, { required, description }Add positional argument
example(usage, description)string, stringAdd example to help text
parse(argv)string[]Parse arguments and execute matched command

Interactive Prompts

MethodReturnsDescription
Prompt.input(message)Promise<string>Free text input
Prompt.confirm(message)Promise<boolean>Yes/no confirmation
Prompt.password(message)Promise<string>Hidden input (masked with •••)
Prompt.select(message, choices)Promise<string>Arrow-key selection from list
Prompt.multiSelect(message, choices)Promise<string[]>Multi-select with space to toggle

CI environments. Interactive prompts detect non-TTY environments (CI/CD pipelines, cron jobs) and automatically use default values instead of blocking. Override with Prompt.input('Name:', { nonInteractive: 'DefaultName' }) to control fallback behavior explicitly.

Core APIs

  • CLI - Main CLI class
  • command(name, fn) - Define command
  • option(name, config) - Add option/flag
  • argument(name, config) - Add argument
  • Prompt - Interactive prompts
  • Spinner - Loading spinner
  • ProgressBar - Progress visualization
  • colors - Terminal colors
  • table - Format data tables
  • log - Logging utilities

Example: CLI Application

import { CLI, Prompt, Spinner, colors } from '@hyperbridge/forge/cli';

const cli = new CLI({
  name: 'myapp',
  version: '1.0.0',
  description: 'My awesome CLI tool',
});

// Define command
cli.command('create', async (args, options) => {
  const name = await Prompt.input('Project name:');

  const spinner = new Spinner(`Creating ${name}...`);
  spinner.start();

  // Do work...
  await new Promise(r => setTimeout(r, 2000));

  spinner.succeed(`Project created: ${colors.green(name)}`);
});

// Define another command
cli.command('build', async (args, options) => {
  const watch = options.watch || false;
  console.log(`Building${watch ? ' (watch)' : ''}...`);
  // Build logic...
});

cli.command('serve', async (args, options) => {
  const port = options.port || 3000;
  console.log(`Server running on ${colors.cyan(`http://localhost:${port}`)}`);
});

// Parse and execute
cli.parse(process.argv);

Example: Interactive Prompts

import { Prompt, colors } from '@hyperbridge/forge/cli';

// Text input
const name = await Prompt.input('Your name:');

// Confirmation
const proceed = await Prompt.confirm('Continue?');

// Select from options
const choice = await Prompt.select('Choose one:', [
  { label: 'Option A', value: 'a' },
  { label: 'Option B', value: 'b' },
  { label: 'Option C', value: 'c' },
]);

// Multi-select
const tags = await Prompt.multiSelect('Select tags:', [
  'important',
  'urgent',
  'feature',
  'bugfix',
]);

// Display results
console.log(`${colors.green('✓')} Name: ${name}`);
console.log(`${colors.blue('ℹ')} Choice: ${choice}`);
console.log(`${colors.yellow('⚠')} Tags: ${tags.join(', ')}`);

forge/i18n

Internationalization with translations, formatting, and locale management.

ICU MessageFormat compatible. forge/i18n handles plurals, gender, ordinals, and select statements following the ICU MessageFormat standard used by Java's ResourceBundle and Android's string resources. Complex plural rules for languages like Arabic (zero, one, two, few, many, other) work out of the box.

Intl API powered. Date, number, currency, list, and relative time formatting delegates to the native Intl APIs — so you get full CLDR locale data for 400+ locales without bundling any data files. The formatted output matches what users expect in their region.

I18n Constructor

ParameterTypeDefaultDescription
defaultLocalestring'en'Default locale (BCP 47 tag, e.g., 'en-US', 'fr-FR', 'ja')
fallbackLocalestring'en'Fallback when translation key is missing in current locale
missingKeyHandlerfunctionReturns keyCustom handler: (key, locale) => string
interpolationobject{ prefix: '{', suffix: '}' }Variable delimiters in translation strings

Translation API

MethodReturnsDescription
t(key, context?)stringTranslate key with optional interpolation context
addMessages(locale, messages)voidRegister translations for a locale (deep-merged with existing)
setLocale(locale)voidSwitch active locale (triggers re-renders in forge/client)
getLocale()stringGet current active locale
hasKey(key, locale?)booleanCheck if translation exists
pluralize(count, keys)stringSelect plural form based on count and locale rules
isRTL(locale?)booleanCheck if locale is right-to-left (Arabic, Hebrew, Urdu, etc.)

Formatting API

MethodReturnsDescription
formatDate(date, locale?)stringLocale-aware date string
formatTime(date, locale?)stringLocale-aware time string
formatNumber(num, locale?)stringLocale-aware number with grouping separators
formatCurrency(amount, currency, locale?)stringCurrency formatting (symbol, position, decimals)
formatRelative(date)stringRelative time (e.g., "2 hours ago", "in 3 days")
formatList(items, locale?)stringLocale-aware list ("A, B, and C" vs "A, B y C")

SSR hydration. When using forge/i18n with server-side rendering, pass the locale in the initial state to avoid hydration mismatches. The server and client must agree on the locale before the first render — use Accept-Language header detection on the server and navigator.language on the client.

Don't concatenate translated strings. Instead of t('hello') + ' ' + t('world'), use a single key with interpolation: t('greeting', { name: 'World' }). Word order varies by language — Japanese puts the subject last, Arabic reads right-to-left. Let the translator control the full sentence structure.

Core APIs

  • I18n - Main i18n class
  • t(key, context) - Translate key
  • setLocale(locale) - Set current locale
  • getLocale() - Get current locale
  • formatDate(date, locale) - Format date
  • formatTime(date, locale) - Format time
  • formatNumber(num, locale) - Format number
  • formatCurrency(amount, currency) - Format currency
  • formatList(items) - Format list
  • formatRelative(date) - Relative time (e.g., "2 hours ago")
  • pluralize(count, keys) - Plural handling
  • isRTL(locale) - Check RTL language

Example: Setup & Usage

import { I18n } from '@hyperbridge/forge/i18n';

const i18n = new I18n({
  defaultLocale: 'en',
  fallbackLocale: 'en',
});

// Register translations
i18n.addMessages('en', {
  'greeting': 'Hello, {name}!',
  'items.one': 'You have 1 item',
  'items.other': 'You have {count} items',
  'welcome': 'Welcome to {app}',
});

i18n.addMessages('es', {
  'greeting': '¡Hola, {name}!',
  'items.one': 'Tienes 1 artículo',
  'items.other': 'Tienes {count} artículos',
  'welcome': 'Bienvenido a {app}',
});

// Translate
console.log(i18n.t('greeting', { name: 'John' }));
// en: "Hello, John!"
// es: "¡Hola, John!"

// Pluralize
console.log(i18n.t('items', { count: 1 }));
// "You have 1 item" (en) / "Tienes 1 artículo" (es)

console.log(i18n.t('items', { count: 5 }));
// "You have 5 items" (en) / "Tienes 5 artículos" (es)

Example: Formatting

import { I18n } from '@hyperbridge/forge/i18n';

const i18n = new I18n({ defaultLocale: 'en-US' });

// Date formatting
const date = new Date('2026-03-15');
console.log(i18n.formatDate(date)); // "3/15/2026"
console.log(i18n.formatDate(date, 'es-ES')); // "15/3/2026"

// Number formatting
console.log(i18n.formatNumber(1234.56)); // "1,234.56" (en-US)
console.log(i18n.formatNumber(1234.56, 'de-DE')); // "1.234,56"

// Currency formatting
console.log(i18n.formatCurrency(99.99, 'USD')); // "$99.99"
console.log(i18n.formatCurrency(99.99, 'EUR', 'de-DE')); // "99,99 €"

// Relative time
console.log(i18n.formatRelative(new Date() - 3600000)); // "1 hour ago"
console.log(i18n.formatRelative(new Date() + 86400000)); // "in 1 day"

// List formatting
const items = ['Apple', 'Banana', 'Orange'];
console.log(i18n.formatList(items)); // "Apple, Banana, and Orange"
console.log(i18n.formatList(items, 'es-ES')); // "Apple, Banana y Orange"

forge/pwa

Progressive Web App toolkit — Service Workers, offline caching, Web Push (RFC 8291/8292), Background Sync, App Shell, and install management. Replaces workbox, web-push, next-pwa, and vite-plugin-pwa with zero dependencies.

Two runtime contexts. Some forge/pwa classes (PWAForge, PushManager, PrecacheManager) run in Node.js — they generate Service Worker code and send push payloads. Others (BackgroundSync, AppShell, UpdateManager, BadgeManager) run inside the Service Worker. The module detects its context automatically.

No build step required. Unlike Workbox, forge/pwa does not need webpack/Vite plugin integration. Generate the SW code once with PWAForge.generateServiceWorkerCode(), write it to /sw.js, and register it. Works with any bundler or no bundler at all.

PWAForge — Manifest & Service Worker

OptionTypeDescription
namestringFull app name (shown on install prompt and splash screen)
shortNamestringShort name for home screen icon (≤12 characters recommended)
descriptionstringApp description (shown in app stores)
themeColorstringBrowser chrome color (hex)
backgroundColorstringSplash screen background color
displaystring'standalone' (no browser chrome), 'fullscreen', 'minimal-ui', 'browser'
startUrlstringURL opened when app is launched from home screen. Default '/'.
scopestringNavigation scope. Default '/'.
iconsobject[]Array of { src, sizes, type, purpose }. Minimum: 192×192 and 512×512 PNG.
shortcutsobject[]Quick-action shortcuts in the OS long-press menu
screenshotsobject[]App store screenshots for install prompt

CacheStrategy

Five built-in strategies matching Workbox's patterns, each with configurable options:

StrategyBehaviourBest for
CacheFirstServe from cache, fall back to network. Update cache on miss.Static assets (JS, CSS, fonts, images)
NetworkFirstTry network, fall back to cache on timeout or failure.API responses, dynamic HTML pages
StaleWhileRevalidateServe from cache immediately, update cache in background.Avatar images, non-critical API data
NetworkOnlyAlways go to network. Cache never used.Analytics, payment endpoints
CacheOnlyAlways serve from cache. Error if not cached.Precached offline fallback resources

PushManager

MethodReturnsDescription
generateVAPIDKeys(){ publicKey, privateKey }P-256 ECDH key pair for VAPID authentication. Generate once, store in env vars.
sendNotification(sub, payload, opts)Promise<void>Send encrypted Web Push (RFC 8291/8292). sub = browser subscription object.
sendBatch(subs[], payload, opts)Promise<BatchResult>Send to multiple subscribers with concurrency control and failure tracking

Key API Reference

ClassKey MethodsDescription
PWAForgegenerateManifest(), generateServiceWorkerCode(opts)Create manifest JSON and complete SW script
PrecacheManager.add(urls[]), .install(), .activate()Precache app shell at SW install time
CacheStrategyCacheFirst(opts), NetworkFirst(opts), StaleWhileRevalidate(opts)Route-level caching strategy factories
PushManagergenerateVAPIDKeys(), sendNotification(), sendBatch()Web Push via RFC 8291/8292
BackgroundSyncregisterSync(name), onSync(fn)Queue failed requests, replay when online
OfflineManagerenableOfflineMode(), onOnline(fn), onOffline(fn)Online/offline detection and request queuing
AppShellgenerateShellCode(opts)SPA app shell SW pattern — serve index.html for all navigation requests
BadgeManagersetBadge(n), clearBadge()App icon badge (unread count) via Badging API
UpdateManagercheckForUpdates(), onUpdateFound(fn), applyUpdate()Detect new SW version, prompt user, apply update
generateVAPIDKeys()standalone exportConvenience wrapper — same as new PushManager().generateVAPIDKeys()

Example: Full PWA Setup

// build-sw.js — run during build to generate /public/sw.js
import { PWAForge, CacheStrategy, AppShell } from '@hyperbridge/forge/pwa';
import fs from 'node:fs';

const pwa = new PWAForge({
  name:            'HBForge Docs',
  shortName:       'HBForge',
  themeColor:      '#6366f1',
  backgroundColor: '#0a0a0f',
  display:         'standalone',
  startUrl:        '/',
  icons: [
    { src: '/icons/icon-192.png', sizes: '192x192', type: 'image/png' },
    { src: '/icons/icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
  ],
});

// 1. Serve manifest.json
fs.writeFileSync('./public/manifest.json', JSON.stringify(pwa.generateManifest(), null, 2));

// 2. Generate service worker
const sw = pwa.generateServiceWorkerCode({
  version:         '1.2.0',
  offlineFallback: '/offline.html',

  // Precache app shell
  precacheUrls: ['/', '/app.js', '/app.css', '/offline.html', '/icons/icon-192.png'],

  // Route-level strategies
  routes: [
    { match: '/api/*',    strategy: CacheStrategy.NetworkFirst({ timeout: 3000, cacheName: 'api-v1' }) },
    { match: '/images/*', strategy: CacheStrategy.CacheFirst({ cacheName: 'images-v1', maxEntries: 200, maxAge: 30 * 86400 }) },
    { match: '/*',        strategy: CacheStrategy.StaleWhileRevalidate({ cacheName: 'pages-v1' }) },
  ],

  // SPA — serve index.html for all navigation
  appShell: AppShell.generateShellCode({ shell: '/index.html' }),
});

fs.writeFileSync('./public/sw.js', sw);
console.log('Service worker generated.');

Example: Web Push

// 1. GENERATE VAPID KEYS ONCE (store in environment variables)
import { generateVAPIDKeys } from '@hyperbridge/forge/pwa';
const keys = generateVAPIDKeys();
// { publicKey: 'BN...', privateKey: 'k2...' }
// → VAPID_PUBLIC_KEY=BN...   VAPID_PRIVATE_KEY=k2...

// 2. CLIENT — subscribe the browser
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.subscribe({
  userVisibleOnly:      true,
  applicationServerKey: process.env.VAPID_PUBLIC_KEY,
});
await fetch('/api/push/subscribe', {
  method: 'POST',
  body:   JSON.stringify(subscription),
});

// 3. SERVER — send notifications
import { PushManager } from '@hyperbridge/forge/pwa';

const push = new PushManager({
  vapidPublicKey:  process.env.VAPID_PUBLIC_KEY,
  vapidPrivateKey: process.env.VAPID_PRIVATE_KEY,
  subject:         'mailto:hello@hyperbridge.digital',
});

// Send to one subscriber
await push.sendNotification(subscription, {
  title: 'New message',
  body:  'You have 3 unread messages',
  icon:  '/icons/icon-192.png',
  badge: '/icons/badge-72.png',
  url:   '/messages',
  actions: [
    { action: 'view',    title: 'View' },
    { action: 'dismiss', title: 'Dismiss' },
  ],
});

// Bulk send (e.g. broadcast to all users)
const results = await push.sendBatch(allSubscriptions, payload, { concurrency: 50 });
console.log(`Sent: ${results.sent}, Failed: ${results.failed}`);

Example: Background Sync

// In your app code — queue a request when offline
import { BackgroundSync } from '@hyperbridge/forge/pwa';

const sync = new BackgroundSync({ queueName: 'form-submissions' });

async function submitForm(data) {
  try {
    await fetch('/api/submit', { method: 'POST', body: JSON.stringify(data) });
  } catch (err) {
    // Network failed — queue for later
    await sync.enqueue({ url: '/api/submit', method: 'POST', body: JSON.stringify(data) });
    console.log('Queued for background sync when connection restores');
  }
}

// In service-worker.js — replay queued requests on sync event
self.addEventListener('sync', (event) => {
  if (event.tag === 'form-submissions') {
    event.waitUntil(sync.replay());
  }
});

Example: UpdateManager

// In your app — prompt user when a new SW version is available
import { UpdateManager } from '@hyperbridge/forge/pwa';

const updater = new UpdateManager('/sw.js');

updater.onUpdateFound(() => {
  // Show a toast or banner
  const banner = document.createElement('div');
  banner.textContent = 'App updated — reload to apply';
  const btn = document.createElement('button');
  btn.textContent = 'Reload';
  btn.onclick = () => updater.applyUpdate(); // skipWaiting + reload
  banner.appendChild(btn);
  document.body.prepend(banner);
});

// Check on focus (catches updates while tab was in background)
window.addEventListener('focus', () => updater.checkForUpdates());

forge/3d

NEW

WebGL2-based 3D engine — scene graph, PBR materials, cameras, lights, post-processing, particle systems, animation, and controls. A zero-dependency Three.js alternative with 368 APIs in 5,400 lines.

Browser-only: forge/3d requires a WebGL2-capable browser. It runs entirely on the GPU via the WebGL2 API — no server-side rendering, no native dependencies.

Installation

import {
  Scene, WebGLRenderer, PerspectiveCamera, OrthographicCamera,
  Mesh, BoxGeometry, SphereGeometry, PlaneGeometry, StandardMaterial,
  AmbientLight, DirectionalLight, PointLight,
  OrbitControls, AnimationMixer, EffectComposer
} from '@hyperbridge/forge/3d';

Quick Start — Rotating Cube

import {
  Scene, WebGLRenderer, PerspectiveCamera,
  Mesh, BoxGeometry, StandardMaterial,
  AmbientLight, DirectionalLight, Clock
} from '@hyperbridge/forge/3d';

// Create scene
const scene = new Scene();
const camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 2, 5);
camera.lookAt(0, 0, 0);

// Create renderer
const renderer = new WebGLRenderer({ canvas: document.getElementById('canvas'), antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x0a0a0f);

// Add lights
scene.add(new AmbientLight(0x404040, 0.5));
const dirLight = new DirectionalLight(0xffffff, 1.0);
dirLight.position.set(5, 10, 7);
scene.add(dirLight);

// Create cube
const cube = new Mesh(
  new BoxGeometry(1, 1, 1),
  new StandardMaterial({ color: 0x6366f1, metalness: 0.3, roughness: 0.4 })
);
scene.add(cube);

// Animation loop
const clock = new Clock();
function animate() {
  requestAnimationFrame(animate);
  const dt = clock.getDelta();
  cube.rotation.x += 0.5 * dt;
  cube.rotation.y += 0.8 * dt;
  renderer.render(scene, camera);
}
animate();

Math Primitives

Full linear algebra library for 3D math — vectors, matrices, quaternions, rays, and bounding volumes.

Vec2 / Vec3 / Vec4

2D, 3D, and 4D vector types with add, sub, scale, dot, cross, normalize, lerp, distance, and more.

Mat3 / Mat4

3x3 and 4x4 matrix types — multiply, invert, transpose, determinant, lookAt, perspective, ortho projections.

Quat

Quaternion rotation — fromAxisAngle, fromEuler, slerp, conjugate, multiply, toMat4. Avoids gimbal lock.

Euler

Euler angle representation with XYZ/YXZ/ZYX order. Converts to/from Quat and Mat4.

Color

RGB/HSL color with hex parsing, lerp, multiply, gamma/linear conversion. Feeds into materials and lights.

Ray / Box3 / Sphere

Ray casting, AABB bounding boxes, bounding spheres — intersect tests, containment, expansion, clamp.

import { Vec3, Mat4, Quat, Color } from '@hyperbridge/forge/3d';

const v = new Vec3(1, 2, 3);
const n = v.normalize();              // unit vector
const d = v.dot(new Vec3(0, 1, 0));   // dot product

const m = Mat4.perspective(Math.PI / 4, 16 / 9, 0.1, 100);
const q = Quat.fromAxisAngle(new Vec3(0, 1, 0), Math.PI / 2);

const c = new Color(0x6366f1);
const lighter = c.lerp(new Color(0xffffff), 0.3);

Geometry

Built-in geometry primitives with customizable segments, plus a base class for custom geometry.

BufferGeometry

Base class — custom vertex positions, normals, UVs, indices. setAttribute / setIndex API.

BoxGeometry

Box with width, height, depth and configurable segments per axis.

SphereGeometry

UV sphere with radius, widthSegments, heightSegments, phi/theta range.

PlaneGeometry

Flat plane with width, height, and segment count. Ideal for floors, walls, water.

IcosahedronGeometry

Geodesic sphere from icosahedron subdivision. Smoother than UV sphere at low poly.

CylinderGeometry

Cylinder / cone with radiusTop, radiusBottom, height, radialSegments, openEnded.

InstancedMesh

GPU-instanced rendering — draw thousands of copies with per-instance transforms and colors.

import { BoxGeometry, SphereGeometry, CylinderGeometry, InstancedMesh, Mesh, StandardMaterial } from '@hyperbridge/forge/3d';

const box = new BoxGeometry(2, 2, 2);
const sphere = new SphereGeometry(1, 32, 32);
const cylinder = new CylinderGeometry(0.5, 0.5, 3, 16);

// Instanced rendering — 10,000 cubes in one draw call
const instanced = new InstancedMesh(box, new StandardMaterial({ color: 0x22d3ee }), 10000);
for (let i = 0; i < 10000; i++) {
  instanced.setMatrixAt(i, Mat4.translation(Math.random() * 100, Math.random() * 100, Math.random() * 100));
}
scene.add(instanced);

Materials

PBR and non-PBR material types for every rendering need — from wireframe debugging to physically-based metallic/roughness workflows.

BasicMaterial

Unlit flat color — no lighting calculations. Fastest option for UI overlays and debug visuals.

StandardMaterial

PBR metallic-roughness workflow with color, metalness, roughness, emissive, normalMap, aoMap.

PhongMaterial

Classic Blinn-Phong shading with ambient, diffuse, specular, shininess. Good for stylized rendering.

WireframeMaterial

Wireframe overlay with configurable line width and color. Great for debugging and technical visuals.

ShaderMaterial

Custom GLSL vertex/fragment shaders with uniform bindings. Full control over the rendering pipeline.

import { StandardMaterial, ShaderMaterial } from '@hyperbridge/forge/3d';

const pbr = new StandardMaterial({
  color: 0x6366f1,
  metalness: 0.8,
  roughness: 0.2,
  emissive: 0x111111,
});

const custom = new ShaderMaterial({
  vertexShader: `
    attribute vec3 position;
    uniform mat4 projectionMatrix, modelViewMatrix;
    void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }
  `,
  fragmentShader: `
    precision mediump float;
    uniform float time;
    void main() { gl_FragColor = vec4(sin(time), 0.4, 0.9, 1.0); }
  `,
  uniforms: { time: { value: 0 } }
});

Scene Graph

Hierarchical scene graph with parent-child transforms, groups, and visibility control.

Scene

Root container. Holds all objects, lights, and environment settings (background, fog).

Object3D

Base class for all scene objects — position, rotation, scale, visible, parent, children, add/remove.

Group

Empty container for grouping objects. Transforms propagate to children.

Mesh

Geometry + Material pair. The primary visible object in the scene.

LineSegments

Renders line segments from vertex pairs. Used for grids, wireframes, debug outlines.

import { Scene, Group, Mesh, BoxGeometry, StandardMaterial } from '@hyperbridge/forge/3d';

const scene = new Scene();
scene.background = new Color(0x0a0a0f);

const group = new Group();
group.position.set(0, 1, 0);

const mesh = new Mesh(new BoxGeometry(1, 1, 1), new StandardMaterial({ color: 0x22d3ee }));
group.add(mesh);
scene.add(group);

group.rotation.y = Math.PI / 4;  // rotates all children

Cameras

PerspectiveCamera

Standard perspective projection — fov, aspect, near, far. Mimics human eye depth perception.

OrthographicCamera

Orthographic projection — left, right, top, bottom, near, far. Ideal for 2D overlays, isometric views.

import { PerspectiveCamera, OrthographicCamera } from '@hyperbridge/forge/3d';

const cam = new PerspectiveCamera(60, 16 / 9, 0.1, 1000);
cam.position.set(0, 5, 10);
cam.lookAt(0, 0, 0);

const ortho = new OrthographicCamera(-10, 10, 10, -10, 0.1, 100);

Lights

AmbientLight

Uniform light in all directions. No shadows. Sets base illumination level for the scene.

DirectionalLight

Parallel rays from a direction (like the sun). Supports shadow mapping.

PointLight

Omni-directional point source with distance and decay. Light bulbs, candles, explosions.

SpotLight

Cone-shaped light with angle, penumbra, distance, decay. Flashlights, stage lights.

HemisphereLight

Sky + ground gradient light. Simulates outdoor ambient with sky color above and ground color below.

import { AmbientLight, DirectionalLight, PointLight, SpotLight, HemisphereLight } from '@hyperbridge/forge/3d';

scene.add(new AmbientLight(0x404040, 0.4));

const sun = new DirectionalLight(0xffffff, 1.0);
sun.position.set(10, 20, 10);
scene.add(sun);

const bulb = new PointLight(0xff9900, 1.0, 50, 2);
bulb.position.set(0, 3, 0);
scene.add(bulb);

const spot = new SpotLight(0xffffff, 1.0, 100, Math.PI / 6, 0.5, 2);
spot.position.set(0, 10, 0);
scene.add(spot);

scene.add(new HemisphereLight(0x87ceeb, 0x362907, 0.6));

Renderer

WebGLRenderer

WebGL2-based renderer — antialias, alpha, stencil, depth, preserveDrawingBuffer, pixelRatio, shadow maps, tone mapping, gamma correction.

import { WebGLRenderer } from '@hyperbridge/forge/3d';

const renderer = new WebGLRenderer({
  canvas: document.getElementById('canvas'),
  antialias: true,
  alpha: false,
  stencil: true,
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(0x0a0a0f, 1);
renderer.shadowMap.enabled = true;
renderer.toneMapping = 'aces';
renderer.render(scene, camera);

Animation

Keyframe animation system with mixers, clips, actions, and blending — animate any property over time.

AnimationMixer

Drives animations on a target object. Manages multiple clips, blending, and cross-fading.

AnimationClip

Container for keyframe tracks. Defines a named animation with duration.

AnimationAction

Playback control — play, pause, stop, loop, clampWhenFinished, timeScale, weight, crossFadeTo.

KeyframeTrack

Typed keyframe data — NumberKeyframeTrack, VectorKeyframeTrack, QuaternionKeyframeTrack.

Clock

High-resolution timer — getDelta(), getElapsedTime(). Drives animation loops.

import { AnimationMixer, AnimationClip, KeyframeTrack, Clock } from '@hyperbridge/forge/3d';

const times = [0, 1, 2];
const values = [0, 0, 0, 0, 3, 0, 0, 0, 0];  // Vec3 keyframes
const track = new KeyframeTrack('.position', times, values);
const clip = new AnimationClip('bounce', 2, [track]);

const mixer = new AnimationMixer(cube);
const action = mixer.clipAction(clip);
action.setLoop('repeat');
action.play();

const clock = new Clock();
function animate() {
  requestAnimationFrame(animate);
  mixer.update(clock.getDelta());
  renderer.render(scene, camera);
}
animate();

Post-Processing

Multi-pass post-processing pipeline — bloom, SSAO, glitch, film grain, and more via composable passes.

EffectComposer

Manages a chain of render passes. Reads from/writes to framebuffers for multi-pass effects.

RenderPass

Renders the scene/camera to a framebuffer. Usually the first pass in the chain.

UnrealBloomPass

HDR bloom with threshold, strength, and radius. Emissive materials glow realistically.

SSAOPass

Screen-space ambient occlusion — darkens creases and contact edges for added depth.

GlitchPass

Digital glitch effect with configurable intensity and frequency. Great for cyberpunk aesthetics.

FilmPass

Film grain, scanlines, and vignette for a cinematic look.

import { EffectComposer, RenderPass, UnrealBloomPass, SSAOPass } from '@hyperbridge/forge/3d';

const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
composer.addPass(new UnrealBloomPass({ threshold: 0.8, strength: 1.5, radius: 0.4 }));
composer.addPass(new SSAOPass(scene, camera, { radius: 0.5, minDistance: 0.001, maxDistance: 0.1 }));

// Replace renderer.render() with:
function animate() {
  requestAnimationFrame(animate);
  composer.render();
}
animate();

Controls

Camera and object interaction — orbit, fly, pointer lock, drag, and first-person controls.

OrbitControls

Orbit around a target with mouse drag, scroll zoom, right-click pan. Auto-damping, min/max distance.

FlyControls

Free-flight camera with WASD movement and mouse look. Configurable speed and roll.

PointerLockControls

First-person mouse look with pointer lock API. Ideal for FPS-style navigation.

DragControls

Drag objects in 3D space with mouse. Fires dragstart, drag, dragend events.

FirstPersonControls

WASD + mouse look without pointer lock. Smooth movement with acceleration and deceleration.

import { OrbitControls, FlyControls } from '@hyperbridge/forge/3d';

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.minDistance = 2;
controls.maxDistance = 50;
controls.target.set(0, 1, 0);

function animate() {
  requestAnimationFrame(animate);
  controls.update();
  renderer.render(scene, camera);
}

Particle Systems

CPU and GPU particle emitters with trail rendering and configurable force fields.

ParticleEmitter

CPU-based particle emitter — rate, lifetime, velocity, color over life, size over life, gravity.

GPUParticleSystem

Transform-feedback GPU particles — millions of particles with minimal CPU overhead.

TrailRenderer

Ribbon trail effect following moving objects. Width, color, and fade over lifetime.

Force Types

Gravity, wind, turbulence, vortex, attractor, repulsor — composable force fields for particles.

import { ParticleEmitter, GPUParticleSystem, TrailRenderer } from '@hyperbridge/forge/3d';

const emitter = new ParticleEmitter({
  maxParticles: 5000,
  rate: 200,
  lifetime: [1, 3],
  velocity: { x: [-1, 1], y: [2, 5], z: [-1, 1] },
  color: [0x6366f1, 0x22d3ee],
  size: [0.1, 0.5],
  gravity: -9.8,
});
scene.add(emitter);

const trail = new TrailRenderer({ width: 0.2, length: 20, color: 0x6366f1, fade: true });
trail.attach(movingObject);
scene.add(trail);

Raycaster & Helpers

Raycaster

Cast rays into the scene — mouse picking, collision detection, intersection tests with meshes.

AxesHelper

RGB axes indicator (X=red, Y=green, Z=blue) for orientation debugging.

GridHelper

Ground-plane grid with configurable size, divisions, and colors.

ArrowHelper

Directional arrow for visualizing vectors, normals, and forces.

import { Raycaster, AxesHelper, GridHelper } from '@hyperbridge/forge/3d';

scene.add(new AxesHelper(5));
scene.add(new GridHelper(20, 20));

const raycaster = new Raycaster();
canvas.addEventListener('click', (event) => {
  const x = (event.clientX / window.innerWidth) * 2 - 1;
  const y = -(event.clientY / window.innerHeight) * 2 + 1;
  raycaster.setFromCamera({ x, y }, camera);
  const hits = raycaster.intersectObjects(scene.children);
  if (hits.length > 0) console.log('Hit:', hits[0].object, 'at', hits[0].point);
});

Curves & Loaders

CatmullRomCurve3 / LineCurve3

3D spline and line curves — getPoint(t), getPoints(n), getTangent(t), getLength(). Camera paths, extrusions.

TextureLoader / OBJLoader / GLTFLoader

Asset loaders — load textures (PNG/JPG), OBJ meshes, and glTF 2.0 scenes with materials and animations.

import { CatmullRomCurve3, Vec3, TextureLoader } from '@hyperbridge/forge/3d';

const curve = new CatmullRomCurve3([
  new Vec3(-5, 0, 0), new Vec3(-2, 3, 0),
  new Vec3(2, -1, 0), new Vec3(5, 2, 0)
]);
const points = curve.getPoints(50);  // 50 sample points along the spline

const texture = await new TextureLoader().load('/textures/brick.jpg');
const wall = new Mesh(
  new PlaneGeometry(10, 5),
  new StandardMaterial({ map: texture, roughness: 0.9 })
);

Key APIs

CategoryAPIDescription
MathVec2, Vec3, Vec4, Mat3, Mat4, Quat, Euler, Color, Ray, Box3, SphereLinear algebra, bounding volumes, color
GeometryBufferGeometry, BoxGeometry, SphereGeometry, PlaneGeometry, IcosahedronGeometry, CylinderGeometryBuilt-in geometry primitives
InstancingInstancedMeshGPU-instanced rendering for thousands of objects
MaterialsBasicMaterial, StandardMaterial, PhongMaterial, WireframeMaterial, ShaderMaterialPBR, classic shading, custom GLSL
SceneScene, Object3D, Group, Mesh, LineSegmentsHierarchical scene graph
CamerasPerspectiveCamera, OrthographicCameraPerspective and orthographic projection
LightsAmbientLight, DirectionalLight, PointLight, SpotLight, HemisphereLightScene illumination and shadow casting
RendererWebGLRendererWebGL2-based renderer with shadow maps, tone mapping
AnimationAnimationMixer, AnimationClip, AnimationAction, Clock, KeyframeTrackKeyframe animation and blending
Post-FXEffectComposer, RenderPass, UnrealBloomPass, SSAOPass, GlitchPass, FilmPassMulti-pass post-processing pipeline
ControlsOrbitControls, FlyControls, PointerLockControls, DragControls, FirstPersonControlsCamera and object interaction
ParticlesParticleEmitter, GPUParticleSystem, TrailRendererCPU/GPU particles and trails
RaycasterRaycasterMouse picking and collision detection
HelpersAxesHelper, GridHelper, ArrowHelperVisual debugging aids
CurvesCatmullRomCurve3, LineCurve33D spline and line curves
LoadersTextureLoader, OBJLoader, GLTFLoaderAsset loading (images, meshes, scenes)

368 APIs, 5,400 lines, zero dependencies. A complete Three.js alternative built from scratch — pure WebGL2 with no external packages.

forge/display

Retina, 4K, and device capability detection with an adaptive quality tier system. Pixel-perfect canvas scaling, WebGL DPR setup, per-tier variant resolution, and deep integration with forge/3d, forge/animate, and forge/client. 60+ APIs. 955 lines. Zero dependencies.

forge/display uses a composite scoring system (0–100) that weighs GPU tier, device RAM, CPU cores, DPR, HDR support, and network quality to produce one of four quality tiers: low, medium, high, or ultra. All variant APIs resolve against this tier, so a single config object works correctly on a 2015 budget phone, a MacBook Pro Retina, and a 4K workstation with a discrete GPU — with no manual branching in application code.

Installation & Import

// Named import (tree-shakeable)
import Display from '@hyperbridge/forge/display';

// Or via the full bundle
import forge from '@hyperbridge/forge';
const Display = forge.Display;

Quality Tier & Scoring

APIReturnsDescription
qualityTier()'low'|'medium'|'high'|'ultra'Composite tier from GPU, RAM, CPU, DPR, network, and accessibility prefs
qualityScore()numberRaw score 0–100 for fine-grained comparisons
snapshot()DisplaySnapshotFull immutable capability snapshot, cached until DPR changes
clearSnapshot()voidInvalidate cache — call after system settings change

Score breakdown

SignalPoints
GPU high / mid / low+50 / +30 / +10
RAM+CPU high / mid / low+25 / +15 / +5
DPR ≥ 3 / ≥ 2+20 / +10
WebGL 2+5
HDR display+3
Save-data mode−30
Slow connection−10
Reduced motion−5
const tier  = Display.qualityTier();   // 'ultra'
const score = Display.qualityScore();  // 87
console.log(Display.profileString());
// "ultra|4k|p3|hdr|retina|2x|gpu:high|ram:16|cores:12|score:87"

Device Pixel Ratio

APIReturnsDescription
getDPR(maxDPR?)numberDPR clamped to maxDPR (default 4). Use for canvas/WebGL sizing.
getDPRRaw()numberUnclamped DPR for analytics
isRetina()booleanDPR ≥ 2 (standard Retina / HiDPI)
isHiDPI()booleanDPR > 1 (any non-standard display)
is4K()booleanPhysical or logical×DPR ≥ 3840px wide
is8K()booleanPhysical ≥ 7680px wide
resolutionTier()'sd'|'hd'|'fhd'|'qhd'|'4k'|'8k'Named tier from physical pixel dimensions
physicalResolution(){ width, height, dpr }Actual hardware pixel count of the screen
if (Display.is4K()) {
  loadTexture('/assets/hero@4x.webp');
} else if (Display.isRetina()) {
  loadTexture('/assets/hero@2x.webp');
} else {
  loadTexture('/assets/hero.webp');
}

Color Gamut & HDR

APIReturnsDescription
colorGamut()'srgb'|'p3'|'rec2020'Widest supported color gamut
supportsHDR()booleandynamic-range: high media query
supportsWideColor()booleanTrue for P3 or wider
colorDepth()numberscreen.colorDepth (typically 24 or 30)
if (Display.colorGamut() === 'p3') {
  canvas.style.colorSpace = 'display-p3';
  ctx = canvas.getContext('2d', { colorSpace: 'display-p3' });
}

GPU Detection

APIReturnsDescription
getGPUInfo(){ vendor, renderer, tier, api }WebGL renderer string + estimated tier. Cached for the session.
supportsWebGL2()booleanWebGL 2.0 context availability check
const gpu = Display.getGPUInfo();
console.log(gpu.renderer); // "Apple M3 Pro"
console.log(gpu.tier);     // "high"
console.log(gpu.api);      // "webgl2"

Hardware: Memory & CPU

APIReturnsDescription
deviceMemoryGB()numberNavigator.deviceMemory (approximate GB). Falls back to 4.
cpuCores()numbernavigator.hardwareConcurrency. Falls back to 4.
hardwareTier()'low'|'mid'|'high'Combined RAM + CPU tier

Network & Connection

APIReturnsDescription
connectionInfo(){ type, downlink, rtt, saveData, tier }Network Information API data with fast/slow tier
saveDataMode()booleanTrue when user has enabled Save Data / Lite mode

Accessibility Preferences

APIReturnsDescription
prefersReducedMotion()booleanprefers-reduced-motion: reduce
prefersReducedTransparency()booleanprefers-reduced-transparency: reduce
prefersHighContrast()booleanprefers-contrast: more
prefersDarkMode()booleanprefers-color-scheme: dark

Variant System

The variant system is the primary way to branch logic per tier. Pass a config map, get back the right value for the current device.

APIDescription
createVariant(map)Resolve a single per-tier value. Accepts { low, medium?, high?, ultra? } or a 4-element array.
createVariants(schema)Resolve a full schema object — each key maps to a variant map. Returns a flat config object.
// Single value
const particles = Display.createVariant({ low: 50, medium: 500, high: 2000, ultra: 8000 });

// Full config object — the recommended pattern
const cfg = Display.createVariants({
  shadows:   { low: false,  medium: 'simple', high: 'pcf',  ultra: 'pcss' },
  particles: { low: 50,     medium: 500,       high: 2000,  ultra: 8000   },
  antialias: { low: false,  medium: false,     high: true,  ultra: true   },
  texSize:   { low: 512,    medium: 1024,      high: 2048,  ultra: 4096   },
});
// On a Retina MacBook Pro (ultra): { shadows:'pcss', particles:8000, antialias:true, texSize:4096 }
// On a budget Android phone (low):  { shadows:false,  particles:50,   antialias:false, texSize:512  }

Canvas Scaling (Pixel-Perfect)

APIDescription
scaleCanvas(canvas, ctx?, opts?)Sets canvas physical buffer = CSS size × DPR. Calls ctx.scale(dpr,dpr) so draw calls use logical px.
observeCanvas(canvas, ctx, onResize?)Attaches a ResizeObserver that re-scales on container resize. Returns the observer for cleanup.
createHiDPICanvas(w, h, opts?)Creates a new canvas (or OffscreenCanvas) at physical DPR resolution.
const canvas = document.getElementById('chart');
const ctx    = canvas.getContext('2d');

// One call — pixel-perfect on Retina, 4K, anything
Display.scaleCanvas(canvas, ctx);
ctx.fillText('Crisp on every display', 10, 20);

// Auto re-scale when container resizes
const observer = Display.observeCanvas(canvas, ctx, ({ dpr, width, height }) => {
  redraw(); // your draw function
});
// cleanup: observer.disconnect()

WebGL Viewport Setup

APIDescription
setupWebGLViewport(canvas, gl, opts?)Sizes canvas buffer for current tier DPR cap (ultra=full, high=2×, medium=1.5×, low=1×). Calls gl.viewport().
get3DDPR()Optimal DPR for 3D rendering — caps at 2 for high tier (rendering 3D at 3× DPR costs 9× fill rate).

Rendering WebGL at full 3× DPR on a Retina display costs 9× the fill rate compared to 1×. forge/display automatically caps WebGL DPR at 2 for the high tier and only allows full native DPR for ultra tier devices — giving you sharp visuals without tanking performance on most hardware.

const canvas = document.getElementById('scene');
const gl     = canvas.getContext('webgl2');

// Auto-caps DPR by tier — sharp but performant
Display.setupWebGLViewport(canvas, gl);

// Or pass into forge/3d renderer
import { createRenderer } from '@hyperbridge/forge/3d';
const renderer = createRenderer(canvas, { dpr: Display.get3DDPR() });

Adaptive Images

APIDescription
adaptiveImageSrc(base, variants?)Returns the right src variant for current DPR and connection. Degrades to base on save-data or slow network.
adaptiveSrcset(base, variants?)Generates a complete srcset string. Auto-derives @2x/@3x paths if variants not supplied.
// Explicit variant map
const src = Display.adaptiveImageSrc('/img/hero.webp', {
  x2: '/img/hero@2x.webp',
  x3: '/img/hero@3x.webp',
  x4: '/img/hero@4x.webp',
});
img.src = src;

// Auto-generated srcset (derives @2x/@3x filenames)
img.srcset = Display.adaptiveSrcset('/img/banner.webp');
// → "/img/banner.webp 1x, /img/banner@2x.webp 2x, /img/banner@3x.webp 3x"

CSS Variable Injection

Call Display.injectCSS() once at app startup. It adds CSS custom properties and data-* attributes to <html> that you can use in stylesheets:

What's injectedExample valueUse
--dpr2Scale factor for CSS transforms, border-image-width
--display-tierhighFeature flags in CSS
--display-color-gamutp3Color space switching
--canvas-scale2Same as --dpr, aliased for clarity
--anim-scale1.0Scale animation durations in CSS
--particle-scale0.8Scale particle counts in shaders
data-tierhighCSS selectors: [data-tier="high"] .glow { … }
data-retinatrue[data-retina="true"] img { image-rendering: … }
data-hdrtrueEnable HDR color palettes
data-gamutp3Wide-color palette activation
// app entry point
Display.injectCSS();

/* your stylesheet */
.hero-gradient {
  background: linear-gradient(135deg, oklch(70% 0.25 264), oklch(60% 0.3 310));
  animation-duration: calc(0.4s * var(--anim-scale));
}

[data-tier="low"] .hero-gradient { animation: none; }
[data-hdr="true"] .hero-gradient { /* wide-gamut override */ }
[data-retina="true"] .logo       { image-rendering: -webkit-optimize-contrast; }

forge/3d Integration

APIReturnsDescription
get3DConfig()Config3DFull tier-resolved 3D config object ready to spread into a forge/3d renderer
get3DDPR()numberWebGL-safe DPR (capped by tier)
import { createRenderer } from '@hyperbridge/forge/3d';
import Display from '@hyperbridge/forge/display';

const cfg      = Display.get3DConfig();
const renderer = createRenderer(canvas, cfg);

/* cfg on ultra tier:
{
  antialias: true,  shadows: 'pcss',  shadowMapSize: 4096,
  textureMaxSize: 4096,  maxParticles: 20000,
  postFX: ['bloom','ssao','dof','ssr'],
  dpr: 2,  instanceLimit: 10000,  anisotropy: 16,  mipmap: true
}
cfg on low tier:
{
  antialias: false, shadows: false, shadowMapSize: 512,
  textureMaxSize: 512, maxParticles: 100,
  postFX: [], dpr: 1, instanceLimit: 100, anisotropy: 1, mipmap: false
} */

forge/animate Integration

APIDescription
getAnimateConfig()Returns { duration, stagger, springs, parallax, blur, willChange } adjusted for tier and reduced-motion preference. All durations are 0 when reduced-motion is active.
import { animate } from '@hyperbridge/forge/animate';
import Display     from '@hyperbridge/forge/display';

const aCfg = Display.getAnimateConfig();

animate(el, { opacity: [0, 1], y: [20, 0] }, {
  duration:  aCfg.duration,
  stagger:   aCfg.stagger,
  easing:    aCfg.springs ? 'spring' : 'ease-out',
});

// On a low-end device: duration 0.15s, no parallax, no blur
// With reduced-motion: duration 0, instant resolve

forge/client Integration

APIDescription
getClientConfig()Returns { lazyThreshold, imageFormat, preloadVideos, srcsetMaxDPR, placeholders } for forge/client image components and loaders.

Pixel-Perfect Utilities

APIDescription
toPhysical(css)CSS px → physical device px (Math.round(css × DPR))
toLogical(px)Physical px → CSS px
snapToPhysical(css)Round CSS value to nearest clean physical-pixel boundary — prevents sub-pixel blur
hairlineWidth()Thinnest visible line: 1 / DPR. Use for borders, strokes, dividers.
// Hairline border — 0.5px on Retina, 1px on standard
el.style.borderWidth = Display.hairlineWidth() + 'px';

// Snap a shadow offset to physical pixels (no blurry sub-pixel shadows)
const offset = Display.snapToPhysical(8);
ctx.shadowOffsetX = offset;
ctx.shadowOffsetY = offset;

DPR Change Events

Fires when the user drags a window to a different monitor (e.g. from a Retina display to a standard external monitor).

APIDescription
onDPRChange(fn)Subscribe to DPR changes. Returns an unsubscribe function.
onTierChange(fn)Subscribe to quality tier changes (fires when DPR change crosses a tier boundary).
const unsub = Display.onDPRChange(({ dpr, prev }) => {
  console.log(`DPR changed: ${prev} → ${dpr}`);
  Display.scaleCanvas(canvas, ctx);
  redraw();
});

Display.onTierChange(({ tier, prev }) => {
  console.log(`Tier: ${prev} → ${tier}`);
  Display.injectCSS({ force: true }); // re-inject CSS vars
  renderer.setPixelRatio(Display.get3DDPR());
});

Debug & Analytics

APIDescription
profileString()Compact analytics string, e.g. "ultra|4k|p3|hdr|retina|2x|gpu:high|ram:16|cores:12|score:87"
logCapabilities()Formatted console group with the full capability report (dev tool aid)
Display.logCapabilities();
// ─────────────────────────────────────────────────────
//   Quality tier    : ultra (87/100)
//   DPR             : 2 → clamped 2
//   Display         : 4K 3840×2160px
//   Color gamut     : p3 + HDR
//   GPU tier        : high (Apple M3 Pro)
//   WebGL 2         : true
//   RAM / CPU       : 16 GB / 12 cores
//   Connection      : 4g fast
//   Reduced motion  : false
// ─────────────────────────────────────────────────────

// Send to analytics
analytics.track('display_profile', { profile: Display.profileString() });

60+ APIs, 955 lines, zero dependencies. Works in all modern browsers and Node.js (SSR-safe — returns sensible defaults on the server). Integrates natively with forge/3d, forge/animate, and forge/client.

forge/prime

Web7 compliance bridge — DID-based identity, intent-first APIs, AMP envelope routing, Proof-of-Outcome, Vigil audit trail, and Kynetra Prime agent delegation. 1,200 lines. 32+ APIs. Three tiers: Lite (free) · Pro (hosted) · Enterprise (PoO + ZK-ML).

Web7 in one import. forge/prime is the L6→L4 bridge in the Web7 stack. It connects your HBForge app to Kynetra Prime (agent kernel), the Skill Registry, and the AIG audit graph — without changing your existing HBForge code. Add it progressively: start with Lite (zero infrastructure), upgrade to Pro when you need hosted routing, Enterprise when regulators need proof.

BYOA included at every tier. Register any custom agent class with prime.registerAgent() and it immediately inherits token-scoped auth, telemetry, memory access, and PRIME self-training. No boilerplate, no separate infra.

Installation

import { prime, vigil, KynetraPrime } from '@hyperbridge/forge/prime';

Tier Overview

FeatureLiteProEnterprise
DID token auth
Intent-first API + AMP envelope
BYOA (registerAgent)
/.well-known/web7.json manifest
Vigil basic logging
Hosted Kynetra Prime routing
Skill Registry + 10 built-in agents
Vigil full audit trail + AIG graph
Proof-of-Outcome (PoO) verification
ZK-ML verifiable inference
L0 settlement + outcome escrow
Reputation ledger + slashing
w7 conformance certificate

KynetraPrime Constructor

OptionTypeDefaultDescription
tokenstringScoped API token (required). No password ever stored or transmitted.
tier'lite'|'pro'|'enterprise''lite'Determines which infrastructure is available
agentsstring[][]Built-in agents to activate: ECHO, HUNTER, PULSE, FLOW, CORTEX, FORGE, SHIELD, SPARK, LENS, HIVE
telemetrybooleantrueEnable Vigil telemetry events
endpointstring'https://prime.kynetra.ai'Kynetra Prime API endpoint (Pro/Enterprise only)
policystring'deny-all'Default policy ref. Deny-by-default; pass 'org:acme:default' to allow

Core API Reference

MethodReturnsTierDescription
prime.delegate(opts)Promise<DelegateResult>AllDelegate an intent to an agent. Emits AMP envelope, routes via Prime kernel (Pro/Enterprise) or runs locally (Lite)
prime.register(agentDef)Promise<void>AllRegister a built-in or BYOA agent with Prime. Publishes to Skill Registry on Pro/Enterprise
prime.registerAgent(name, cls, opts)voidAllBYOA — register any custom agent class. Inherits token auth, telemetry, memory, model routing automatically
prime.run(agentName, payload)Promise<any>AllRun a named agent (built-in or BYOA) with a payload. Same API regardless of tier
prime.identity.verify(opts)Promise<boolean>AllVerify a DID signature against a nonce. Used in login flows
prime.identity.authenticate(opts)Promise<{did, token}>AllPasskey/biometric DID authentication. Returns DID + scoped JWT. No password
prime.identity.current()Promise<string>AllGet the authenticated user's DID
prime.manifest()objectAllGenerate the /.well-known/web7.json manifest object for your app
prime.conform()Promise<ConformResult>AllRun local Web7 Lite conformance checks. Returns pass/fail per requirement
vigil.record(opts)Promise<void>AllLog an action to the Vigil audit trail. Creates an AIG node
vigil.trace(intentId)Promise<AIGSubgraph>Pro+Retrieve the full AIG subgraph for an intent: intent → delegation → inference → outcome
vigil.export(intentId)Promise<SignedBlob>EnterpriseExport a self-verifying signed audit blob suitable for regulator submission
prime.poo.verify(opts)Promise<PoOResult>EnterpriseVerify the full PoO predicate: σ_principal ∧ σ_prime ∧ zkml ∧ σ_agent ∧ policy ∧ stake
prime.settle(intentId, rail)Promise<Settlement>EnterpriseTrigger L0 settlement. Rails: amp-escrow, amp-outcome, amp-split

delegate() Options

OptionTypeRequiredDescription
fromstringYesPrincipal DID (the user or service delegating)
tostringYesTarget skill ref or agent DID e.g. 'skill:ledger-close'
intentobjectYes{ action, params, constraints }
budgetstringYesHard spend cap e.g. '10 USD'. Prime rejects uncapped delegations
policystringNoPolicy ref for this delegation. Overrides instance default
settleobjectNo{ rail: 'amp-escrow'|'amp-outcome'|'amp-split' } (Enterprise)
requireobjectNo{ zkml: true } — demand verifiable inference proof (Enterprise)

Example: Lite — DID Login + Intent API

import { prime } from '@hyperbridge/forge/prime';
import { JWT }   from '@hyperbridge/forge/auth';

const p = new KynetraPrime({ token: process.env.KP_TOKEN, tier: 'lite' });

// DID login — no password, no session cookie
app.post('/auth/login', async (req, res) => {
  const { did, signature, nonce } = req.body;
  const ok = await p.identity.verify({ did, signature, nonce });
  if (!ok) return res.status(401).json({ error: 'invalid DID' });

  const token = JWT.sign(
    { sub: did, scope: ['read', 'delegate'] },
    { secret: process.env.JWT_SECRET, expiresIn: '8h' }
  );
  res.json({ token, did });
});

// Intent-first route — declare what the user wants
app.post('/generate-invoice', jwtMiddleware, async (req, res) => {
  const result = await p.delegate({
    from:   req.user.did,
    to:     'skill:generate-invoice',
    intent: { action: 'generate-invoice', params: req.body },
    budget: '5 USD',
    policy: 'org:acme:billing',
  });
  res.json({ ok: true, aig: result.aigNodeId });
});

// Well-known manifest
app.get('/.well-known/web7.json', (req, res) => res.json(p.manifest()));

Example: BYOA — Register Custom Agent

import { KynetraPrime } from '@hyperbridge/forge/prime';
import { Pool } from '@hyperbridge/forge/data';

const prime = new KynetraPrime({ token: process.env.KP_TOKEN });

// 1. Define your agent class
class InvoiceAgent {
  async run({ input, memory, model }) {
    const client = await memory.recall(input.clientId);
    const draft  = await model.generate('invoice-v1', client);
    return { invoice: draft, status: 'draft' };
  }
}

// 2. Register — gets full Prime infrastructure for free
prime.registerAgent('INVOICE', InvoiceAgent, {
  token:     { scope: ['invoices:write'] },
  telemetry: true,
  trainable: true,  // PRIME learns from every run
});

// 3. Run — identical API to built-in agents
const result = await prime.run('INVOICE', { clientId: 'c_123' });

Example: Pro — Hosted Prime + Vigil Audit

import { KynetraPrime, vigil } from '@hyperbridge/forge/prime';

const prime = new KynetraPrime({
  token:    process.env.KP_TOKEN,
  tier:     'pro',
  endpoint: 'https://prime.kynetra.ai',
  agents:   ['FORGE', 'SHIELD'],
});

// Delegate to a hosted agent with full audit trail
const result = await prime.delegate({
  from:   req.user.did,
  to:     'skill:ledger-close',
  intent: { action: 'ledger-close', params: { month: '2026-04' } },
  budget: '10 USD',
  policy: 'org:acme:finance',
});

// result.aigNodeId — committed AIG node (queryable)
console.log(result.aigNodeId);  // 'aig:abc123'

// Retrieve the full audit trace
const trace = await vigil.trace(result.intentId);
// Returns: intent → delegation → inference → outcome graph
// With all signatures, timestamps, model used, cost

// Export for internal audit
const blob = JSON.stringify(trace);

Example: Enterprise — Proof-of-Outcome + L0 Settlement

import { KynetraPrime, vigil } from '@hyperbridge/forge/prime';

const prime = new KynetraPrime({
  token: process.env.KP_TOKEN,
  tier:  'enterprise',
});

// Delegate with ZK-ML requirement + escrow settlement
const result = await prime.delegate({
  from:    req.user.did,
  to:      'skill:tax-filing',
  intent:  { action: 'file-tax', params: req.body, constraints: { jurisdiction: 'IN' } },
  budget:  '50 USD',
  policy:  'org:acme:compliance',
  settle:  { rail: 'amp-outcome' },  // pay only on verified success
  require: { zkml: true },           // demand ZK-ML proof of inference
});

// Verify PoO before trusting the result
const poo = await prime.poo.verify({
  intentId: result.intentId,
  agentDid: result.agentDid,
  outcome:  result.outcome,
});

if (!poo.verified) throw new Error('PoO failed — do not settle');

// Trigger L0 settlement (funds released from escrow to agent)
const settlement = await prime.settle(result.intentId, 'amp-outcome');
console.log(settlement.status);   // 'settled'
console.log(settlement.receipt);  // signed receipt (self-verifying)

// Export audit blob for regulator
const auditBlob = await vigil.export(result.intentId);
// Self-verifying: contains all sigs + ZK-ML proof + L0 anchor hash

Web7 Conformance Checklist

// Run local conformance check (Lite tier)
const report = await prime.conform();
// {
//   did_token_auth:    true,   // JWT sub is a DID
//   intent_first_api:  true,   // at least one prime.delegate() call
//   amp_envelope:      true,   // AMP message format valid
//   well_known:        true,   // /.well-known/web7.json accessible
//   vigil_logging:     true,   // at least one vigil.record() call
//   budget_caps:       true,   // all delegations have budget set
//   byoa_registered:   true,   // custom agents registered
//   poo_verified:      false,  // Enterprise only
//   zkml_proofs:       false,  // Enterprise only
//   l0_settlement:     false,  // Enterprise only
// }

// Full conformance suite (Enterprise — run in CI)
// w7 test conformance --endpoint https://my-app.com

32+ APIs, 1,200 lines, zero extra dependencies. forge/prime is the single bridge between your HBForge app and the entire Web7 stack. Start Lite for free — upgrade when your compliance requirements need proof.

forge/wasm Web7 ✦

Universal WebAssembly loader. forge/wasm is how HBForge apps instantiate KYRx (L1 settlement) and ClearScript (L5 contract) modules, plus any other WASM payload — with zero external tooling (no wabt, no wasm-bindgen, no wasmtime).

Installation

import {
  loadModule, loadStreaming,
  MemoryManager, WorkerPool,
  WASIBridge, HostImports,
  parseWasm, validateWasm,
  hotReload, typeBridge
} from '@hyperbridge/forge/wasm';

What it does

  • Module loadingloadModule(bytes) / loadStreaming(url) with caching and streaming compilation.
  • Memory manager — typed-array views on linear memory, bounds-checked read/write helpers.
  • WASI-lite — enough of the WASI filesystem/stdio API to run Rust or AssemblyScript compiled output.
  • Worker pool — run heavy WASM workloads off the main thread with transferable memory buffers.
  • Host imports — preconfigured namespaces for web7/prime, vigil (audit trail), auth, amp (agent mesh).
  • Hot-reload — development-only module swap without losing app state.
  • Type bridge — automatic conversion between JS and WASM primitives; stringRef, arrayBuffer, bigint, struct layouts.
  • Parser / validator — LEB128 varint decode of WASM sections (via _internal/binary); catches malformed payloads before instantiation.

Example

// Load a Rust-compiled KYRx module and call its exports
const bytes = await fetch('/kyrx.wasm').then(r => r.arrayBuffer());
const mod   = await loadModule(bytes, {
  imports: HostImports.web7Prime({ did: 'did:hb:42' })
});

const balance = mod.exports.balance_of('0xabc');
console.log('KYRx balance:', balance);

// Long-running compute? Move it to a worker
const pool = new WorkerPool({ size: 4, module: bytes });
const results = await pool.runAll(inputs, 'process');

1,481 lines, 20+ APIs. Built on WebAssembly.Module + WebAssembly.Instance. LEB128 decoding shares forge/_internal/binary. Runs in Node (18+) and modern browsers (Chromium, Firefox, Safari 14+).

forge/memory Web7 ✦ new in v4.1.0

Scoped agent memory store. Four nested scopes — tasksessionprincipalglobal — with fall-through reads, TTL sweep, trigram search, and snapshot/restore. Implements the Web7 L6 agent memory primitive.

Installation

import { MemoryStore, SCOPES } from '@hyperbridge/forge/memory';

What it does

  • Four scopesglobal (app-wide), principal (per DID / user), session (per sign-in), task (per agent run).
  • Fall-through readsget(principal, key) looks in task first, then session, then principal, then global. Write to a scope, read it from a narrower one.
  • TTL sweep — set a per-key TTL; sweep() garbage-collects expired entries.
  • Trigram searchsearch(q) ranks stored keys/values by Jaccard similarity, no vector store needed.
  • Promote / snapshot — lift a value up a scope (promote('task'→'session')), or serialize the whole store for persistence.

Example

const mem = new MemoryStore();

// Global knowledge
mem.set(null, 'org-name', 'HyperBridge Digital', { scope: 'global' });

// Per-principal preference
mem.set('did:w7:alice', 'lang', 'en-IN', { scope: 'principal' });

// Per-task scratch with 5-min TTL
mem.set('did:w7:alice', 'draft', 'reply-text',
  { scope: 'task', taskId: 'run-42', ttlMs: 300_000 });

// Read falls through: task → session → principal → global
mem.get('did:w7:alice', 'org-name', { taskId: 'run-42' });
// → 'HyperBridge Digital'  (resolved from global)

// Cleanup
mem.sweep();
const blob = mem.snapshot();   // serializable

267 lines, 12 methods, zero deps. Keys are hashed with FNV-1a for O(1) lookups. Search is vector-free — no embedding model, no similarity service. Works in Node & every browser.

forge/provenance Web7 ✦ new in v4.1.0

C2PA 2.0 style content provenance. Signed assertion boxes (c2pa.* + w7.* labels), parent manifests, selective disclosure. Implements the Web7 L6 provenance primitive and pairs with forge/zkml for model-output manifests.

Installation

import { Manifest, Assertion, LABELS, buildInferenceManifest }
  from '@hyperbridge/forge/provenance';

What it does

  • Manifests — an ordered list of signed assertion boxes with a final seal over the whole manifest.
  • Assertions — labelled records (c2pa.hash.data, c2pa.actions, w7.inference, w7.attestation, …) individually signed with HMAC-SHA-256.
  • Parent linkage — a new manifest can reference a prior manifest's hash, chaining provenance across edits.
  • Selective disclosuremanifest.select([labels]) returns a compact manifest exposing only chosen assertions while keeping the seal valid.
  • Inference helperbuildInferenceManifest({ model, inputHash, outputHash, attestations }) constructs a ready-to-sign Web7 inference manifest in one call.

Example

const m = new Manifest({ issuer: 'did:w7:hbforge' });

m.addAssertion(new Assertion({
  label: LABELS.C2PA_HASH_DATA,
  data:  { algo: 'sha256', value: '0x7f12…' },
}));

m.addAssertion(new Assertion({
  label: LABELS.W7_INFERENCE,
  data:  { model: 'gpt-local-7b', inputHash: '0xaa…', outputHash: '0xbb…' },
}));

m.linkParent('0xparent-manifest-hash');
const sealed = m.seal(secretKey);

// Selective disclosure: share hash + inference, hide other boxes
const trimmed = sealed.select([LABELS.C2PA_HASH_DATA, LABELS.W7_INFERENCE]);
trimmed.verify(publicKey);           // still valid

const wire = sealed.toJSON();
const back = Manifest.fromJSON(wire);

230 lines, 10+ APIs. Assertion signatures use the shared _internal/crypto HMAC-SHA-256 primitive. Label namespace is split c2pa.* (C2PA 2.0 compatible) + w7.* (Web7 extensions); you can add custom labels without touching the core.

forge/zkml Web7 ✦ new in v4.1.0

ZK-ML multi-attestor MVP — M-of-N threshold attestations over (model, inputHash, outputHash) tuples. Swappable proof backend via registerProofBackend() so you can drop in a real SNARK/STARK later without changing callers. Implements the Web7 L6 zk-ml primitive.

Installation

import {
  ZKMLClaim, Attestor, AttestationBundle,
  verifyBundle, collectAttestations,
  registerProofBackend, getProofBackend,
} from '@hyperbridge/forge/zkml';

Why multi-attestor, not a proof

A cryptographic zero-knowledge proof of ML inference is research-grade today — slow, costly, and model-shape-specific. A multi-attestor bundle is the deployable approximation: N independent operators (you + partners + third-party auditors) each attest to having observed the same (inputHash → outputHash) transition; a client accepts if M-of-N signatures verify. When real SNARK backends become practical, register one via registerProofBackend() and callers switch over transparently.

Example

const claim = new ZKMLClaim({
  model:      'gpt-local-7b',
  inputHash:  '0xaa…',
  outputHash: '0xbb…',
});

// Three independent attestors (each with its own HMAC key)
const attestors = [
  new Attestor({ id: 'att-1', secret: s1 }),
  new Attestor({ id: 'att-2', secret: s2 }),
  new Attestor({ id: 'att-3', secret: s3 }),
];

// Require 2-of-3 threshold
const bundle = await collectAttestations(claim, attestors, { threshold: 2 });

// Anyone with the attestor pubkeys (here: HMAC secrets) can verify
const res = verifyBundle(bundle, { 'att-1': s1, 'att-2': s2, 'att-3': s3 });
// → { ok: true, threshold: 2, signed: 3 }

// Later: swap in a SNARK backend
registerProofBackend('groth16', { prove, verify });
const be = getProofBackend('groth16');

201 lines, 9 APIs. Bundle verifier rejects duplicate attestor IDs, unknown signers, and invalid signatures. Designed so the bundle wire format survives the transition from HMAC MVP to SNARK proofs — only the backend changes.

forge/conformance Web7 ✦ new in v4.1.0

Web7-L6 conformance suite v1.0.0. 30 tests across 12 categories. Runs in ~8 ms. Grade AAA / AA / A / B / C / FAIL. Zero deps.

Installation

import { runConformance, formatReport, SUITE_VERSION, CATEGORIES }
  from '@hyperbridge/forge/conformance';

const report = await runConformance();
console.log(formatReport(report));

Coverage

Category Coverage What's checked
identity4 / 4DID registry, resolver, VC issuer, VC roundtrip
amp6 / 6envelope, discover, negotiate, revoke, skill registry, SLASH_RULES
aig2 / 2node types, Merkle root of audit chain
poo2 / 2proof-of-outcome write + verify
settlement3 / 3escrow rail, subscription rail, outcome rail
consent2 / 2grant/revoke chain, isPermitted
provenance2 / 2manifest seal/verify, selective disclosure
zkml2 / 2M-of-N bundle collect + verify, swappable backend
memory3 / 3scoped set/get, fall-through, TTL sweep
reputation2 / 2score update, slashing (SLASH_RULES)
revocation1 / 1amp.revoke writes REVOCATION node
negotiation1 / 1amp.negotiate with counter/accept/reject

Grading

  • AAA — ≥98% (≥29/30)
  • AA — ≥95% (≥28/30)
  • A — ≥90% (≥27/30)
  • B — ≥80%
  • C — ≥70%
  • FAIL — below 70%

Current baseline for @hyperbridge/forge@4.2.2: AAA · 30 / 30 · ~8 ms · 0 deps.

Example report

Web7-L6 Conformance v1.0.0 — @hyperbridge/forge@4.2.2
────────────────────────────────────────────────────────
identity       ██████████  4/4
amp            ██████████  6/6
aig            ██████████  2/2
poo            ██████████  2/2
settlement     ██████████  3/3
consent        ██████████  2/2
provenance     ██████████  2/2
zkml           ██████████  2/2
memory         ██████████  3/3
reputation     ██████████  2/2
revocation     ██████████  1/1
negotiation    ██████████  1/1
────────────────────────────────────────────────────────
TOTAL          30 / 30  (100.0%)   Grade: AAA   8 ms

319 lines, drop-in CI check. Add await runConformance() to your CI pipeline; fail the build if grade drops below your target. The suite version tag (SUITE_VERSION = '1.0.0') lets you pin a spec version alongside your framework version.

forge/policy ✦ new in v4.2.2

OPA-style declarative policy engine — evaluate rule sets, gate HTTP handlers, enforce jurisdiction / budget / data-class / reputation constraints with zero deps. Web7 L6 ambient-authority primitive.

Installation

import {
  PolicyEngine, PolicySet, PolicyRule,
  PolicyDeniedError, policyGate, buildStandardSet,
  EFFECT, COMBINER, getPolicy,
} from '@hyperbridge/forge/policy';

Concepts

Concept Description
PolicyRuleA single condition → effect (ALLOW / DENY) with optional priority
PolicySetOrdered collection of rules with a combiner strategy
PolicyEngineRegistry of sets; evaluate(ctx, setId) returns ALLOW/DENY + reasons
policyGate()HTTP middleware — 403 on DENY, passes ctx.policy to downstream handlers
buildStandardSet()Quick-builder: jurisdiction + budget + dataClass + reputation + riskTier

Combiners

CombinerBehaviour
deny-overridesAny DENY wins (default — fail-secure)
permit-overridesAny ALLOW wins (whitelist pattern)
first-applicableFirst matching rule wins, ordered by priority desc

Example — HTTP gate

import { getPolicy, buildStandardSet, policyGate } from '@hyperbridge/forge/policy';
import { createServer } from '@hyperbridge/forge/server';

const engine = getPolicy();

// Register a policy set for the /pay endpoint
buildStandardSet('pay-policy', {
  allowedJurisdictions: ['US', 'EU', 'IN'],
  maxBudget:            500,
  allowedDataClasses:   ['financial'],
  minReputation:        0.6,
  maxRiskTier:          2,
}, engine);

const server = createServer();

server.use(policyGate(engine, ctx => ({
  setId:        'pay-policy',
  jurisdiction: ctx.headers['x-jurisdiction'] ?? 'US',
  budget:       Number(ctx.headers['x-budget'] ?? 0),
  dataClass:    ctx.headers['x-data-class'] ?? 'public',
  reputation:   Number(ctx.headers['x-reputation'] ?? 1),
  riskTier:     Number(ctx.headers['x-risk-tier'] ?? 0),
})));

server.post('/pay', async ctx => {
  // ctx.policy → { effect: 'allow', reasons: [...] }
  ctx.body = { ok: true };
});

server.listen(3000);

Custom rules

const set = new PolicySet('custom', { combiner: 'deny-overrides', defaultEffect: EFFECT.ALLOW });

set.addRule(new PolicyRule({
  id:     'no-weekend-writes',
  type:   'custom',
  effect: EFFECT.DENY,
  params: { fn: ctx => new Date().getDay() % 6 === 0 && ctx.method === 'write' },
  reason: 'Write operations disabled on weekends',
}));

engine.addSet(set);
const result = engine.evaluate({ method: 'write' }, 'custom');
// → { effect: 'deny', reasons: ['Write operations disabled on weekends'] }

~280 lines, 12 APIs. engine.assert(ctx, setId) throws PolicyDeniedError (HTTP 403) on deny — use inside service handlers for non-HTTP contexts. Time-window rules let you enforce rate-limiting or business-hours access without extra middleware.

forge/trace ✦ new in v4.2.2

OpenTelemetry-compatible distributed tracing — W3C Trace Context, Span/Tracer/TraceProvider, OTLP/HTTP export, Jaeger/Grafana/Vigil integration. Zero external deps.

Installation

import {
  TraceProvider, TraceContext, Span, Tracer,
  ConsoleExporter, JsonLineExporter, OtlpHttpExporter, VigilExporter,
  traceMiddleware, tracePrime,
  getProvider, setProvider, getTracer,
  SpanStatus, SpanKind,
} from '@hyperbridge/forge/trace';

Exporters

ExporterWhen to use
ConsoleExporterDev / debug — pretty-prints to stdout
JsonLineExporter(stream)NDJSON to a writable stream; pipe to a file or log aggregator
OtlpHttpExporter(endpoint, headers)Production — sends to Grafana Tempo, Jaeger, Honeycomb, etc.
VigilExporter(vigil)Mirrors spans into forge/vigil audit log

Quick start

import { getProvider, getTracer, OtlpHttpExporter } from '@hyperbridge/forge/trace';
import { createServer } from '@hyperbridge/forge/server';
import { traceMiddleware } from '@hyperbridge/forge/trace';

// Wire up an OTLP exporter (Grafana Cloud / Jaeger)
getProvider().addExporter(new OtlpHttpExporter(
  'http://localhost:4318/v1/traces',
  { Authorization: 'Bearer ' }
));

const tracer = getTracer('my-app', '1.0.0');
const server = createServer();

// Reads W3C traceparent from requests, propagates it downstream
server.use(traceMiddleware(tracer));

server.get('/users/:id', async ctx => {
  await tracer.trace('db.getUser', async span => {
    span.setAttribute('db.user_id', ctx.params.id);
    ctx.body = await db.getUser(ctx.params.id);
  });
});

Manual spans

const span = tracer.startSpan('payment.process', {
  kind: SpanKind.CLIENT,
  attributes: { 'payment.amount': 99.99, 'payment.currency': 'USD' },
});

try {
  const result = await stripe.charge({ amount: 9999 });
  span.addEvent('charge.created', { id: result.id });
  span.ok();
  return result;
} catch (err) {
  span.error(err.message);
  throw err;
} finally {
  span.end();
}

W3C Trace Context

// Parse incoming traceparent header
const ctx = TraceContext.fromTraceparent(req.headers['traceparent']);
// → TraceContext { traceId: '4bf92f3577b34da6...', spanId: 'a2fb4a1d...', sampled: true }

// Serialise for outgoing calls
const childCtx = ctx.child();
outboundHeaders['traceparent'] = childCtx.toTraceparent();
// → '00-4bf92f3577b34da6...-newSpanId-01'

~340 lines, 18 APIs. Spans batch-export every 100 spans (configurable). provider.shutdown() flushes the buffer before process exit — call it in your SIGTERM handler. The singleton getProvider() / setProvider() pattern lets test suites swap exporters without touching application code.

forge/replay ✦ new in v4.2.2

Time-travel debugger for HTTP + database layers. Record every request and query into a deterministic NDJSON tape; replay it identically without touching live services. Zero deps.

Installation

import {
  RecordingSession, ReplaySession, Tape, TraceEvent, EVENT,
  httpRecorder, dbRecorder,
  httpReplayer, dbReplayer,
  replayAll, diffTapes,
} from '@hyperbridge/forge/replay';

Architecture

Class / FunctionRole
RecordingSessionCaptures events into a Tape; call .stop() to get the finished tape
TapeOrdered NDJSON log — .save(path), Tape.load(path), .stats()
httpRecorder(session)Middleware — records http:req + http:res events
dbRecorder(pool, session)Proxy around a forge/data Pool — records all SQL queries
httpReplayer(tape)Returns recorded responses in order — no network calls
dbReplayer(tape)Mock pool — returns recorded rows for every query — no DB needed
replayAll(tape, handler)High-level: re-executes a handler with both mocks wired up
diffTapes(a, b)Regression testing — surfaces status/body/db-count differences

Record a session

import { RecordingSession, httpRecorder, dbRecorder } from '@hyperbridge/forge/replay';
import { createServer } from '@hyperbridge/forge/server';
import { Pool } from '@hyperbridge/forge/data';

const pool = new Pool({ connectionString: process.env.DB_URL });
const session = new RecordingSession({ label: 'checkout-flow' });

const server = createServer();
server.use(httpRecorder(session));  // record HTTP layer

// Wrap pool — all queries are recorded transparently
const recordedPool = dbRecorder(pool, session);

server.post('/checkout', async ctx => {
  const items = await recordedPool.query('SELECT * FROM cart WHERE user_id = $1', [ctx.user.id]);
  ctx.body = { items: items.rows };
});

await server.listen(3000);
// After your test scenario runs:
const tape = session.stop();
await tape.save('./fixtures/checkout.ndjson');

Replay without live services

import { Tape, replayAll } from '@hyperbridge/forge/replay';

const tape = await Tape.load('./fixtures/checkout.ndjson');

const { results, stats } = await replayAll(tape, async (mockPool, mockHttp, requests) => {
  // mockPool.query() returns recorded rows — no database
  const items = await mockPool.query('SELECT * FROM cart WHERE user_id = $1', ['user-123']);
  return items.rows;
});

console.log(stats);
// → { total: 4, counts: { 'http:req': 1, 'http:res': 1, 'db:query': 1, 'db:result': 1 }, duration: 42 }

Regression diff

import { diffTapes, Tape } from '@hyperbridge/forge/replay';

const baseline  = await Tape.load('./fixtures/checkout-v1.ndjson');
const candidate = await Tape.load('./fixtures/checkout-v2.ndjson');

const { hasDiff, diffs } = diffTapes(baseline, candidate);
if (hasDiff) {
  console.error('Regression detected:', diffs);
  process.exit(1);
}

~300 lines, 14 APIs. Tape format is NDJSON — one JSON object per line — so you can grep, jq, and git diff fixtures like normal text files. session.annotate('checkout started') embeds human-readable markers between events.

forge/simulator ✦ new in v4.2.2

In-process agent-mesh scenario simulator — simulate slashing, escrow unwind, SLA breach, reputation cascade, and double-spend scenarios without touching live infrastructure. Six built-in scenarios; fully composable.

Installation

import {
  Simulator, SimAgent, SimEscrow, SimResult,
  happyPath, agentSlashed, slaBreached,
  reputationCascade, escrowUnwind, doubleSpend,
  runSuite,
} from '@hyperbridge/forge/simulator';

Built-in scenarios

ScenarioWhat it tests
happyPathFull task cycle — escrow locks, agent delivers, escrow releases payout
agentSlashedAgent misbehaves (badZkml, hallucination, sla-breach, etc.) — stake slashed, requester refunded
slaBreachedSLA timer expires before delivery — partial payout (configurable), rest refunded
reputationCascadeMultiple slashes compound — reputation collapses below floor, agent disqualified
escrowUnwindEscrow fully refunded to requester without any payout (dispute resolution path)
doubleSpendSecond release on same escrow is rejected — idempotency enforced

Run the full suite

import { runSuite } from '@hyperbridge/forge/simulator';

const results = await runSuite({
  agentStake:   1000,
  escrowAmount: 500,
  slaDurationMs: 60_000,
  slashPenalty: 0.2,     // 20% of stake per infraction
});

for (const r of results) {
  console.log(r.summary());
}
// happyPath          ✓  3 assertions  0 failures
// agentSlashed       ✓  4 assertions  0 failures
// slaBreached        ✓  3 assertions  0 failures
// reputationCascade  ✓  5 assertions  0 failures
// escrowUnwind       ✓  2 assertions  0 failures
// doubleSpend        ✓  2 assertions  0 failures

Custom scenario

import { Simulator } from '@hyperbridge/forge/simulator';

const sim = new Simulator({ slashPenalty: 0.15 });
const agent = sim.addAgent('provider-1', { stake: 800, reputation: 90 });
const escrow = sim.createEscrow('req-1', agent.did, 300, { slaDurationMs: 5_000 });

const result = await sim.run('partial-delivery', async () => {
  // Agent delivers late — partial payout at 50%
  const { payout, refund } = escrow.release(0.5);
  sim.assert('partial payout correct', payout === 150);
  sim.assert('remaining refunded',     refund === 150);

  agent.earn(payout);
  sim.assert('balance updated', agent.balance === payout);
});

console.log(result.summary());
// partial-delivery  ✓  3 assertions  0 failures

SimAgent API

agent.slash('badZkml');           // reduces reputation, emits slash event
agent.earn(amount);               // increases balance
agent.reputation;                 // 0–100 score
agent.balance;                    // current token balance
agent.did;                        // 'did:w7:provider-1'
agent.behaviour;                  // 'honest' | 'lazy' | 'malicious'

~280 lines, 16 APIs. Designed for CI — runSuite() throws on any assertion failure, so add it to your test runner and it will catch regressions in slashing / escrow math whenever you touch the settlement module. Zero network calls, runs in milliseconds.

forge/_internal Shared Primitives

Three tiny modules that host the primitives several other modules used to reinvent — cryptography, byte-level encoding, and timing. The underscore prefix is advisory: signatures are stable, internals may improve between minor versions.

You rarely need to import _internal/* directly. It's primarily here so the bundler can de-duplicate across modules. That said, these are pure functions with zero dependencies and safe to use.

_internal/crypto

import {
  hmacSha256, hmacSha256Async,
  sha256, sha256Async,
  timingSafeEqual,
  randomBytes, randomId,
  toHex, fromHex, toBase64, fromBase64
} from '@hyperbridge/forge/_internal/crypto';

// Node: sync, backed by node:crypto
const sig = hmacSha256('secret', 'payload');         // hex
const mac = hmacSha256('secret', 'payload', { encoding: 'base64' });

// Browser: use async variant (WebCrypto is async-only)
const sig = await hmacSha256Async('secret', 'payload');

// Timing-safe compare — constant-time even in the fallback
timingSafeEqual(expectedSig, receivedSig);

// Cryptographically-random IDs
const id = randomId(16);  // 32-char hex string

_internal/binary

import {
  encodeUtf8, decodeUtf8,
  readULEB128, writeULEB128, readSLEB128,
  concatBytes,
  readCStr, writeCStr
} from '@hyperbridge/forge/_internal/binary';

// UTF-8 with TextEncoder/TextDecoder when present, manual fallback otherwise
const bytes = encodeUtf8('héllo 🌍');
const str   = decodeUtf8(bytes);

// LEB128 varints — used by the WASM parser
const { value, nextOffset } = readULEB128(bytes, 0);
const encoded = writeULEB128(624485);

// Concatenate chunks without the BufferList overhead
const all = concatBytes([header, body, footer]);

_internal/time

import {
  now, nowNs,
  parseDuration, formatDuration,
  sleep, withTimeout
} from '@hyperbridge/forge/_internal/time';

// Monotonic clock — performance.now() with Date.now fallback
const t0 = now();
await doWork();
const elapsed = now() - t0;
console.log(formatDuration(elapsed));  // "1.23 s"

// Parse human-readable durations
parseDuration('1h30m');    // → 5_400_000
parseDuration('500ms');    // → 500
parseDuration('7d');       // → 604_800_000

// Deadline-bounded promise race
const result = await withTimeout(fetch(url), 3000, 'fetch');
// → rejects with "withTimeout: fetch exceeded 3000ms" on overrun

Who uses what

Primitive Used by modules
_internal/cryptoauth, prime, mail, wasm, pwa
_internal/binarywasm, pdf, schema, client, search
_internal/timeprime, wasm, server, ai, notify, cli

27 APIs across 442 lines. Before v3.4.4, each of the modules above shipped its own copy of HMAC, SHA-256, UTF-8, or a duration parser. Bundlers couldn't de-duplicate. Now every module resolves to the same path — a tree-shaken build importing auth + mail no longer ships two copies of HMAC-SHA-256.

Bundle Size

HBForge ships dual CJS + ESM, with 1,400+ named exports, deep subpath imports, browser/Node conditional exports, and a sideEffects: false declaration — so modern bundlers can cut the tree aggressively.

Five phases of splitting

Versions 3.4.0 → 3.4.4 were a phased rollout, each shipped independently so apps can adopt incrementally. Full release notes →

Version Phase What it delivered Size impact
v3.4.0 1 — tree-shake sideEffects: false declaration ~12–18% smaller
v3.4.1 2 — dual CJS/ESM 22 × .mjs wrappers, 1,400+ named exports, conditional import/require/types/browser enables phase 3+
v3.4.2 3 — deep paths 371 leaf files for client + server + animate. forge/client/signal resolves standalone. enables phase 4+
v3.4.3 4 — browser/Node 5 server-only modules (server, mail, pdf, cli, test) stubbed in browser bundles with a 1.4 KB Proxy ~1 MB saved (browser)
v3.4.4 5 — _internal Shared crypto, binary, time primitives at one resolvable path. De-duplicates across 6–8 modules each. per-build savings

What a typical app imports

// ❌ Pre-3.4: pulls everything from forge root
import forge from '@hyperbridge/forge';
forge.signal(0);

// ✅ Subpath: pulls one module
import { signal } from '@hyperbridge/forge/client';

// ✅✅ Deep path: pulls one export
import { signal } from '@hyperbridge/forge/client/signal';

// ✅ Browser-side Next.js page: mail/pdf/cli/test/server auto-stub
import { sendMail } from '@hyperbridge/forge/mail';   // → 1.4 KB stub in browser bundle
// (same code in a Node API route pulls the full 223 KB implementation)

Bundler compatibility

  • Rollup / Vite / esbuild / Webpack 5+ / Parcel 2+ — honour sideEffects, conditional exports, and wildcard subpaths. Full benefit.
  • Next.js 13+ / SvelteKit / Remix / Astro — set platform: "browser" on client bundles. Phase 4 browser stubs apply automatically.
  • Webpack 4 — reads sideEffects, ignores conditional exports. Subpath imports still work via main.
  • Node 18+ — native conditional exports, deep paths, and import / require both honoured.

Zero app changes required. Existing require('@hyperbridge/forge') and import forge from '@hyperbridge/forge' still work. Every phase is additive.