HBForge Documentation

Complete API reference, usage guides, and code examples for all 15 modules and 900+ APIs.

What is HBForge?

HBForge is a unified JavaScript framework with 15 modules, 39,930 lines of code, and 900+ APIs—all with zero external dependencies. It replaces 60+ npm packages with a cohesive, production-ready toolkit.

Key Features

Complete UI framework (React-like)
Server framework with routing
Authentication & RBAC
PostgreSQL ORM with migrations
Charts, forms, animations
Email, PDF, CLI tools
Full-text search engine
Internationalization (i18n)
Testing framework with mocking
Zero dependencies guaranteed
MIT licensed
Production-ready

Module Overview

Module Purpose APIs
forge/client React-like UI framework with VDOM, hooks, signals, routing 171+
forge/server HTTP server, routing, middleware, WebSocket, Cron, tasks 85+
forge/auth Password, JWT, OAuth2, MagicLink, TOTP, RBAC, sessions 65+
forge/data PostgreSQL ORM, queries, migrations, seeding, transactions 95+
forge/form Form management, validation, field arrays, schema binding 40+
forge/animate Tweens, springs, scroll effects, FLIP, stagger, easings 45+
forge/chart SVG charts: line, bar, pie, scatter, radar, heatmap, etc. 25+
forge/schema Zod-compatible schema validation with TypeScript inference 42+
forge/test Testing runner, matchers, mocking, DOM testing, snapshots 45+
forge/mail SMTP client, email templates, Resend integration, queue 22+
forge/pdf PDF generation, text, tables, images, fonts, signatures 30+
forge/search Full-text search with BM25 ranking, facets, aggregations 35+
forge/notify Notifications: toast, alert, modal, WebSocket, email, SMS 18+
forge/cli CLI framework, prompts, colors, spinners, config loading 25+
forge/i18n Internationalization, translations, formatting, locales 30+

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.

From npm (Enterprise)

npm install @hyperbridge/forge

Subpath Imports

Import only what you need using subpath imports:

// Client-side UI
import { createElement, useState, useEffect } from '@hyperbridge/forge/client';

// Server-side
import { Forge, Router } from '@hyperbridge/forge/server';

// Data & ORM
import { PgConnection, Model } from '@hyperbridge/forge/data';

// Auth
import { PasswordAuth, JWT, OAuth2Provider } from '@hyperbridge/forge/auth';

// Forms with validation
import { Form, fromSchema } from '@hyperbridge/forge/form';
import { Schema } from '@hyperbridge/forge/schema';

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

// And more...
import { Chart } from '@hyperbridge/forge/chart';
import { SearchForge } from '@hyperbridge/forge/search';
import { Notify } from '@hyperbridge/forge/notify';
import { I18n } from '@hyperbridge/forge/i18n';
import { PDFForge } from '@hyperbridge/forge/pdf';
import { SMTPClient } from '@hyperbridge/forge/mail';
import { CLI } from '@hyperbridge/forge/cli';
import { TestRunner, expect } from '@hyperbridge/forge/test';

Quick Start

Browser UI with forge/client

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

function App() {
  const [count, setCount] = useState(0);
  return h('div', { style: { padding: '20px' } },
    h('h1', null, `Count: ${count}`),
    h('button',
      { onClick: () => setCount(count + 1) },
      'Increment'
    )
  );
}

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

Server with forge/server

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

const app = new Forge();
const router = new Router();

router.get('/', (req, res) => {
  res.json({ message: 'Hello World' });
});

router.get('/users/:id', async (req, res) => {
  const { id } = req.params;
  res.json({ id, name: 'John Doe' });
});

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

Forms with Validation

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

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

const form = new Form({ schema });

async function handleSubmit(data) {
  const result = await form.validate(data);
  if (result.valid) {
    console.log('Validated:', result.data);
  } else {
    console.log('Errors:', result.errors);
  }
}

Database Queries

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

class User extends Model {
  static table = 'users';
}

const conn = new PgConnection({
  host: 'localhost',
  user: 'postgres',
  password: 'secret',
  database: 'myapp',
});

// Insert
const user = await conn.insert('users', {
  email: 'john@example.com',
  name: 'John Doe',
});

// Query
const users = await conn.query('SELECT * FROM users');

// Update
await conn.update('users', { name: 'Jane' }, { where: { id: 1 } });

// Delete
await conn.delete('users', { where: { id: 1 } });

Authentication

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

const auth = new PasswordAuth();

// Register
const result = await auth.register('john@example.com', 'password123');

// Login
const session = await auth.login('john@example.com', 'password123');

// Verify token
const decoded = await JWT.verify(session.token, 'secret-key');

// OAuth2
import { OAuth2Provider } from '@hyperbridge/forge/auth';

const google = new OAuth2Provider('google', {
  clientId: 'YOUR_CLIENT_ID',
  clientSecret: 'YOUR_SECRET',
  redirectUri: 'http://localhost:3000/auth/google/callback',
});

const authUrl = google.getAuthUrl();

Animations

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

// Tween animation
const tween = new Tween({
  target: element,
  duration: 500,
  values: { opacity: [0, 1], y: [20, 0] },
  easing: 'easeOutQuad',
});
tween.play();

// Spring animation
const spring = new Spring({
  target: element,
  tension: 300,
  friction: 10,
  mass: 1,
  values: { scale: [0.8, 1] },
});

// Scroll-triggered animation
onScroll((progress) => {
  element.style.opacity = progress;
});

// Stagger elements
const elements = document.querySelectorAll('.item');
stagger(elements, {
  duration: 300,
  delay: 50,
});

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. ~8,750 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, ~7,500 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 system with password, JWT, OAuth2, MagicLink, TOTP, RBAC, and session management.

Never store JWT secrets in code or commit them to version control. Use environment variables. forge/auth reads from process.env.JWT_SECRET by default when no secret option is provided. In production, use a 256-bit (32-byte) random secret minimum -- shorter secrets are vulnerable to brute-force attacks.

JWT Constructor Options

OptionTypeDescription
secretstring | BufferSigning secret. Falls back to process.env.JWT_SECRET. Required for HMAC algorithms.
algorithmstringSigning algorithm: 'HS256' (default), 'HS384', 'HS512', 'RS256', 'ES256'.
expiresInstring | numberToken lifetime. Accepts '15m', '7d', '1h', or seconds as a number. Default: '1h'.
issuerstringThe iss claim. Used for token verification. Optional but recommended for multi-service setups.
audiencestring | string[]The aud claim. Restricts which services can accept the token.

Core APIs

PasswordAuth

Password-based registration and login with bcrypt hashing.

JWT

JSON Web Token signing, verification, and refresh.

OAuth2Provider

OAuth2 with Google, GitHub, Microsoft, GitLab, Facebook.

MagicLink

Passwordless authentication via email links.

TOTP

Time-based one-time passwords for 2FA.

RBAC

Role-based access control with permissions.

SessionManager

Session creation, validation, and cleanup.

ApiKeyAuth

API key generation and validation.

Example: Password Authentication

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

const auth = new PasswordAuth({ saltRounds: 10 });

// Register
async function register(email, password) {
  const user = await auth.register(email, password);
  return { success: true, userId: user.id };
}

// Login
async function login(email, password) {
  const user = await auth.login(email, password);
  const token = JWT.sign({ userId: user.id }, 'secret-key', { expiresIn: '7d' });
  return { token, user };
}

// Verify token in middleware
app.use((req, res, next) => {
  const token = req.headers.authorization?.split(' ')[1];
  if (!token) return res.status(401).json({ error: 'Unauthorized' });

  try {
    const decoded = JWT.verify(token, 'secret-key');
    req.user = decoded;
    next();
  } catch (err) {
    res.status(401).json({ error: 'Invalid token' });
  }
});

Example: OAuth2

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

const google = new OAuth2Provider('google', {
  clientId: process.env.GOOGLE_CLIENT_ID,
  clientSecret: process.env.GOOGLE_CLIENT_SECRET,
  redirectUri: 'http://localhost:3000/auth/google/callback',
});

// Get auth URL for login button
router.get('/auth/google', (req, res) => {
  const authUrl = google.getAuthUrl();
  res.redirect(authUrl);
});

// Handle callback
router.get('/auth/google/callback', async (req, res) => {
  const { code } = req.query;
  const token = await google.handleCallback(code);
  const user = await google.getUserInfo(token);

  // Create or update user in database
  const dbUser = await createOrUpdateUser(user);
  const jwtToken = JWT.sign({ userId: dbUser.id }, 'secret-key');

  res.redirect(`/dashboard?token=${jwtToken}`);
});

Example: RBAC (Role-Based Access Control)

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

const rbac = new RBAC();

// Define roles and permissions
rbac.defineRole('admin', ['users:read', 'users:write', 'users:delete']);
rbac.defineRole('editor', ['posts:read', 'posts:write']);
rbac.defineRole('viewer', ['posts:read']);

// Grant role to user
rbac.grantRole('user-123', 'admin');

// Check permission in middleware
function requirePermission(permission) {
  return (req, res, next) => {
    if (rbac.hasPermission(req.user.id, permission)) {
      next();
    } else {
      res.status(403).json({ error: 'Forbidden' });
    }
  };
}

// Protect route
router.delete('/api/users/:id', requirePermission('users:delete'), (req, res) => {
  // Delete user
});

forge/data

PostgreSQL ORM with connection pooling, query builder, migrations, transactions, and relationships.

Always use parameterized queries to prevent SQL injection. Never concatenate user input into query strings. All forge/data APIs (db.query(), QueryBuilder, Model) use parameterized queries internally -- but if you drop to raw SQL with db.query(), always use $1, $2 placeholders and pass values as the second argument: db.query('SELECT * FROM users WHERE id = $1', [userId]).

Core APIs

PgConnection

Low-level PostgreSQL connection with raw queries.

Pool

Connection pool management with acquire/release.

Model

ORM model base class with CRUD and query methods.

QueryBuilder

Fluent query builder: select, where, join, groupBy, orderBy.

Migrator

Database migrations: create, rollback, status, refresh.

Seeder

Seed database with test data.

Relationships

hasMany, hasOne, belongsTo, belongsToMany, hasManyThrough.

Transactions

ACID transactions with savepoints and rollback.

Example: Connection & Basic Queries

import { PgConnection } from '@hyperbridge/forge/data';

const db = new PgConnection({
  host: 'localhost',
  port: 5432,
  user: 'postgres',
  password: 'secret',
  database: 'myapp',
});

// Raw query
const users = await db.query('SELECT * FROM users WHERE age > $1', [18]);

// Insert
const user = await db.insert('users', {
  email: 'john@example.com',
  name: 'John Doe',
  age: 30,
});

// Update
await db.update('users', { age: 31 }, { where: { id: 1 } });

// Delete
await db.delete('users', { where: { id: 1 } });

// Count
const count = await db.count('users', { where: { age: { '>': 18 } } });

Example: Models

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

class User extends Model {
  static table = 'users';
  static schema = {
    id: 'serial primary key',
    email: 'varchar(255) unique',
    name: 'varchar(255)',
    age: 'integer',
  };
}

// Create
const user = await User.create({
  email: 'jane@example.com',
  name: 'Jane Doe',
  age: 28,
});

// Read
const user = await User.findById(1);
const allUsers = await User.findMany();
const adults = await User.where({ age: { '>=': 18 } }).get();

// Update
user.name = 'Jane Smith';
await user.save();

// Delete
await user.delete();

Example: Query Builder

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

const query = new QueryBuilder('users')
  .select('id', 'name', 'email')
  .where('age', '>', 18)
  .andWhere('status', '=', 'active')
  .orderBy('name', 'asc')
  .limit(10);

const users = await query.get();
const count = await query.count();
const sql = query.toSQL(); // Get generated SQL

Example: Migrations

import { Migrator } from '@hyperbridge/forge/data';

const migrator = new Migrator(db, './migrations');

// Create migration
await migrator.create('create_users_table', async (m) => {
  m.createTable('users', (table) => {
    table.increments('id');
    table.string('email').unique();
    table.string('name');
    table.integer('age');
    table.timestamps(); // created_at, updated_at
  });
});

// Run migrations
await migrator.latest();

// Rollback
await migrator.rollback();

// Check status
const status = await migrator.status();

Example: Transactions

import { PgConnection } from '@hyperbridge/forge/data';

const db = new PgConnection({ /* ... */ });

await db.transaction(async (trx) => {
  // Create user
  const user = await trx.insert('users', { email: 'john@example.com' });

  // Create related record
  await trx.insert('profiles', { userId: user.id, bio: 'Hello' });

  // Both succeed together, or both rollback
});

// If error in transaction, everything rolls back automatically

forge/form

Form management with validation, field arrays, async validation, and schema binding.

forge/form integrates with forge/schema. Pass a schema to fromSchema() for automatic field generation and validation -- the form infers initial values, field types, and validation rules directly from your schema definition. This eliminates the need to duplicate validation logic between your form and your API layer.

Core APIs

Form

Main form class with state management.

useForm Hook

React hook for form management in components.

Validation

Sync and async field validation with error tracking.

FieldArray

Dynamic arrays of fields (add/remove fields).

fromSchema

Generate form from forge/schema validation schema.

Watch

Subscribe to field value changes.

Example: Basic Form

import { h, useState } from '@hyperbridge/forge/client';
import { Form } from '@hyperbridge/forge/form';

export function LoginForm() {
  const form = new Form({
    initialValues: { email: '', password: '' },
    validate: (values) => {
      const errors = {};
      if (!values.email) errors.email = 'Email required';
      if (!values.password) errors.password = 'Password required';
      return errors;
    },
  });

  async function handleSubmit(values) {
    const response = await fetch('/api/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(values),
    });
    const data = await response.json();
    localStorage.setItem('token', data.token);
  }

  return h('form', { onSubmit: (e) => {
    e.preventDefault();
    form.submit(handleSubmit);
  }},
    h('input', {
      name: 'email',
      type: 'email',
      value: form.values.email,
      onChange: (e) => form.setField('email', e.target.value),
    }),
    form.errors.email && h('p', { style: { color: 'red' } }, form.errors.email),

    h('input', {
      name: 'password',
      type: 'password',
      value: form.values.password,
      onChange: (e) => form.setField('password', e.target.value),
    }),
    form.errors.password && h('p', { style: { color: 'red' } }, form.errors.password),

    h('button', { type: 'submit', disabled: form.isSubmitting }, 'Login')
  );
}

Example: Form with Schema

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

const schema = Schema.object({
  email: Schema.string().email('Invalid email').min(1, 'Required'),
  password: Schema.string().min(8, 'Min 8 characters'),
  confirmPassword: Schema.string(),
}).refine(
  (data) => data.password === data.confirmPassword,
  { message: 'Passwords do not match', path: ['confirmPassword'] }
);

const form = fromSchema(schema);

form.values = {
  email: '',
  password: '',
  confirmPassword: '',
};

async function handleSubmit() {
  const result = await form.validate();
  if (result.valid) {
    // Submit form
    console.log('Validated:', result.data);
  }
}

Example: Field Arrays (Dynamic Fields)

import { Form } from '@hyperbridge/forge/form';

const form = new Form({
  initialValues: {
    contacts: [
      { name: '', email: '' },
    ],
  },
});

function ContactsForm() {
  return h('div', null,
    form.values.contacts.map((contact, index) =>
      h('div', { key: index },
        h('input', {
          value: contact.name,
          onChange: (e) => form.setField(`contacts.${index}.name`, e.target.value),
          placeholder: 'Name',
        }),
        h('input', {
          value: contact.email,
          onChange: (e) => form.setField(`contacts.${index}.email`, e.target.value),
          placeholder: 'Email',
        }),
        h('button', {
          onClick: () => form.removeField(`contacts.${index}`),
        }, 'Remove'),
      )
    ),
    h('button', {
      onClick: () => form.addField('contacts', { name: '', email: '' }),
    }, 'Add Contact'),
  );
}

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. ~3,500 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: Export Chart

// Export as SVG string
const svgString = chart.toSVG();
document.getElementById('preview').innerHTML = svgString;

// Export as PNG blob for download
const blob = await chart.toImage('png');
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'chart.png';
a.click();

forge/schema

Zod-compatible schema validation with TypeScript inference and composable validators.

forge/schema is wire-compatible with Zod. You can migrate from Zod by replacing import paths -- import { z } from 'zod' becomes import { Schema as z } from '@hyperbridge/forge/schema'. The API surface (.object(), .string(), .parse(), .safeParse(), .refine(), .transform()) is identical, so existing schemas work without code changes.

Email validation includes TLD checking against 100+ known top-level domains. Invalid TLDs like .insafd are rejected automatically -- no need to write custom regex. The validator also checks for common typos like gmial.com and yaho.com and returns a suggestion field in the error object.

Core Types

String with email, url, uuid, regex
Number with min, max, int, positive
Boolean
Date and timestamp
Array with validation
Object with nested shapes
Enum and literal
Union and intersection
Optional and nullable
Custom validation
Async validation
Transform and refine

Example: Basic Schema

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

const userSchema = Schema.object({
  id: Schema.number().positive(),
  email: Schema.string().email(),
  age: Schema.number().min(0).max(120),
  isActive: Schema.boolean().default(true),
  role: Schema.enum(['admin', 'user', 'guest']),
  tags: Schema.array(Schema.string()),
  metadata: Schema.object({
    created: Schema.date(),
    updated: Schema.date(),
  }),
});

// Parse data
const data = {
  id: 1,
  email: 'john@example.com',
  age: 30,
  role: 'user',
  tags: ['premium', 'verified'],
  metadata: { created: new Date(), updated: new Date() },
};

const result = userSchema.safeParse(data);
if (result.success) {
  console.log('Valid:', result.data);
} else {
  console.log('Errors:', result.errors);
}

Example: Async Validation

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

const userSchema = Schema.object({
  email: Schema.string()
    .email()
    .async()
    .refine(
      async (email) => {
        const exists = await checkEmailExists(email);
        return !exists;
      },
      { message: 'Email already registered' }
    ),
  username: Schema.string()
    .min(3)
    .async()
    .refine(
      async (username) => {
        const available = await checkUsernameAvailable(username);
        return available;
      },
      { message: 'Username taken' }
    ),
});

const result = await userSchema.parseAsync({
  email: 'new@example.com',
  username: 'johndoe',
});

Example: Transform & Refine

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

const passwordSchema = Schema.object({
  password: Schema.string().min(8),
  confirmPassword: Schema.string(),
})
  .transform((data) => ({
    ...data,
    password: hashPassword(data.password),
  }))
  .refine(
    (data) => data.password === data.confirmPassword,
    {
      message: 'Passwords do not match',
      path: ['confirmPassword'],
    }
  );

const result = passwordSchema.parse({
  password: 'MyPassword123',
  confirmPassword: 'MyPassword123',
});

forge/test

Testing framework with expect matchers, mocking, DOM testing, and code coverage.

Jest-compatible API, zero config. forge/test uses the same describe/it/expect API as Jest. If you've written Jest tests before, your muscle memory transfers directly — no new syntax to learn. The difference: forge/test runs without Babel, without ts-jest, without 47 transitive dependencies.

Built-in component testing. The render(), screen, and fireEvent utilities work like React Testing Library but are purpose-built for forge/client components. No need for @testing-library/react + @testing-library/jest-dom + @testing-library/user-event — it's all included.

Test Runner Configuration

OptionTypeDefaultDescription
includestring[]['**/*.test.js']Glob patterns for test files
excludestring[]['node_modules']Glob patterns to exclude
parallelbooleantrueRun test files in parallel workers
timeoutnumber5000Default timeout per test (ms)
coveragebooleanfalseEnable code coverage collection
bailboolean | numberfalseStop after N failures. true = stop after first
watchbooleanfalseRe-run tests on file changes

Expect Matchers

MatcherDescription
.toBe(value)Strict equality (===)
.toEqual(value)Deep equality
.toBeCloseTo(num, digits)Floating point comparison
.toBeTruthy() / .toBeFalsy()Truthiness checks
.toBeNull() / .toBeDefined()Null/undefined checks
.toContain(item)Array/string contains
.toHaveLength(n)Array/string length
.toThrow(msg?)Function throws error
.toMatch(regex)String matches pattern
.toHaveBeenCalled()Mock was invoked
.toHaveBeenCalledWith(...args)Mock called with specific args
.toHaveBeenCalledTimes(n)Mock invocation count
.toMatchSnapshot()Snapshot comparison
.resolves / .rejectsPromise assertion chains

Snapshot gotcha. Snapshots are stored in __snapshots__/ beside your test file. Run with --update-snapshots to regenerate after intentional UI changes. Commit snapshot files to version control — they're your visual regression safety net.

Core APIs

describe / it

Test suites and cases.

expect

Assertion with 25+ matchers.

fn / spyOn

Mock functions and spy on methods.

render / screen

DOM testing utilities for components.

fireEvent

Trigger DOM events in tests.

waitFor

Wait for async changes.

coverage

Code coverage reporting.

snapshot

Snapshot testing for UI changes.

Example: Unit Test

import { describe, it, expect } from '@hyperbridge/forge/test';

describe('Math utilities', () => {
  it('should add numbers', () => {
    expect(2 + 2).toBe(4);
  });

  it('should handle floats', () => {
    expect(0.1 + 0.2).toBeCloseTo(0.3);
  });

  it('should throw on invalid input', () => {
    expect(() => divide(10, 0)).toThrow('Division by zero');
  });
});

Example: Mocking

import { describe, it, expect, fn, spyOn } from '@hyperbridge/forge/test';

describe('API calls', () => {
  it('should call fetch with correct URL', async () => {
    const mockFetch = fn();
    global.fetch = mockFetch;

    mockFetch.mockResolvedValueOnce({
      json: () => Promise.resolve({ id: 1, name: 'John' }),
    });

    const data = await fetchUser(1);

    expect(mockFetch).toHaveBeenCalledWith('/api/users/1');
    expect(data.name).toBe('John');
  });

  it('should retry on failure', async () => {
    const api = { call: fn() };
    api.call.mockRejectedValueOnce(new Error('Network error'));
    api.call.mockResolvedValueOnce({ success: true });

    const result = await retryCall(() => api.call());

    expect(api.call).toHaveBeenCalledTimes(2);
    expect(result.success).toBe(true);
  });
});

Example: DOM Testing

import { describe, it, expect, render, screen, fireEvent } from '@hyperbridge/forge/test';
import { Counter } from './Counter';

describe('Counter Component', () => {
  it('should render count', () => {
    render(Counter);
    expect(screen.getByText('Count: 0')).toBeDefined();
  });

  it('should increment on button click', () => {
    render(Counter);
    const button = screen.getByRole('button', { name: 'Increment' });

    fireEvent.click(button);
    expect(screen.getByText('Count: 1')).toBeDefined();
  });

  it('should update after async operation', async () => {
    render(DataComponent);

    expect(screen.getByText('Loading...')).toBeDefined();

    await waitFor(() => {
      expect(screen.getByText('Data loaded')).toBeDefined();
    });
  });
});

forge/mail

Email sending with SMTP, email templates, Resend API integration, and queue support.

Two transports, one API. forge/mail supports both direct SMTP connections and the Resend HTTP API. Switch between them by changing the transport — your sending code stays identical. This means you can use SMTP in development and Resend in production without any code changes.

Template engine built in. The EmailTemplate class handles variable interpolation ({{name}}), conditionals ({{#if premium}}), loops ({{#each items}}), and partials. No need for Handlebars, EJS, or Pug — it's all zero-dep.

SMTPClient Configuration

ParameterTypeDefaultDescription
hoststringSMTP server hostname
portnumber587SMTP port (587 for STARTTLS, 465 for SSL)
securebooleanfalseUse TLS from the start (port 465). For STARTTLS on 587, leave false
auth.userstringSMTP username
auth.passstringSMTP password or app-specific password
poolbooleanfalseEnable connection pooling for high-volume sending
maxConnectionsnumber5Max simultaneous SMTP connections when pooling
retriesnumber3Retry count on transient failures (4xx errors)

sendMail Parameters

ParameterTypeDescription
fromstringSender address (e.g., 'Name <email@domain.com>')
tostring | string[]Recipient(s)
ccstring | string[]CC recipient(s)
bccstring | string[]BCC recipient(s)
subjectstringEmail subject line
htmlstringHTML body
textstringPlain text fallback (auto-generated from HTML if omitted)
attachmentsAttachment[]Array of { filename, content, contentType }
replyTostringReply-to address
headersobjectCustom email headers

Gmail app passwords. If using Gmail SMTP, you need an App Password (not your regular password). Go to Google Account → Security → 2-Step Verification → App Passwords. Regular passwords will fail with a 535 authentication error.

Never hardcode credentials. Always use environment variables for SMTP credentials. The auth.pass field should reference process.env.SMTP_PASSWORD, never a string literal. forge/server's config module can load these from .env automatically.

Core APIs

SMTPClient

Direct SMTP protocol support.

ResendTransport

Resend REST API adapter with webhook and broadcast support.

EmailTemplate

Template rendering and variables.

MailQueue

Queue emails with retry logic.

Example: SMTP Email

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

const smtp = new SMTPClient({
  host: 'smtp.gmail.com',
  port: 587,
  secure: false,
  auth: {
    user: 'your-email@gmail.com',
    pass: 'your-app-password',
  },
});

await smtp.sendMail({
  from: 'noreply@example.com',
  to: 'user@example.com',
  subject: 'Welcome!',
  html: '

Welcome to our app

Thanks for signing up!

', });

Example: Resend API

import { ResendTransport } from '@hyperbridge/forge/mail';

const resend = new ResendTransport({
  apiKey: process.env.RESEND_API_KEY,
});

// Send email
const result = await resend.sendMail({
  from: 'onboarding@resend.dev',
  to: 'user@example.com',
  subject: 'Welcome!',
  html: '

Welcome

', }); // Send batch await resend.sendBatch([ { to: 'user1@example.com', subject: 'Hello User 1', html: '...' }, { to: 'user2@example.com', subject: 'Hello User 2', html: '...' }, ]); // Create audience const audience = await resend.createAudience({ name: 'My List' }); // Add contacts await resend.addContact(audience.id, 'user@example.com'); // Send broadcast await resend.createBroadcast({ audienceId: audience.id, subject: 'Monthly Newsletter', html: '...', });

Example: Email Templates

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

const welcomeTemplate = new EmailTemplate({
  name: 'welcome',
  html: `
    

Welcome, {{name}}!

Your account {{email}} has been created.

Verify Email

`, }); const html = welcomeTemplate.render({ name: 'John', email: 'john@example.com', verifyLink: 'https://example.com/verify?token=...', }); await smtp.sendMail({ from: 'noreply@example.com', to: 'john@example.com', subject: 'Welcome to Our App', html, });

forge/pdf

PDF generation with text, tables, images, fonts, watermarks, and signatures.

Pure JavaScript PDF writer. forge/pdf generates valid PDF 1.7 binary output without Puppeteer, Chrome, or wkhtmltopdf. No headless browser, no system dependencies, no 200MB Chromium download. The generated PDFs render identically across Adobe Reader, Preview, Chrome, and every major PDF viewer.

Streaming output. For large documents (1,000+ pages), use pdf.export('stream') to pipe directly to an HTTP response or file. Memory usage stays constant regardless of page count — critical for invoice batch jobs and report generators.

PDFForge Constructor

ParameterTypeDefaultDescription
pageSizestring'A4'Page size: 'A4', 'Letter', 'Legal', 'A3', or { width, height } in points
orientationstring'portrait''portrait' or 'landscape'
marginsobject{ top: 50, right: 50, bottom: 50, left: 50 }Page margins in points (1 point = 1/72 inch)
compressbooleantrueEnable Flate compression for smaller file size
metadataobject{}{ title, author, subject, keywords, creator }

Drawing Methods

MethodParametersDescription
text()content, x, y, { fontSize, color, font, align, maxWidth }Add text with optional wrapping
heading()text, x, y, sizeBold heading at given size
table()rows[][], { x, y, width, headerBg, stripedRows, cellPadding }Auto-layout table from 2D array
image()src, x, y, width, heightEmbed PNG or JPEG image (Buffer, path, or data URI)
rect()x, y, w, h, { fill, stroke, radius }Rectangle with optional rounded corners
line()x1, y1, x2, y2, { color, width, dash }Draw line with optional dash pattern
page()Add new page (inherits page size and orientation)
header()text | functionPage header (function receives page number)
footer()text | functionPage footer (function receives page number and total)
watermark()text, { opacity, angle, color }Diagonal watermark across every page
signature()imageData, x, y, { width, height }Embed signature image
export()'buffer' | 'stream' | 'base64'Export finished document

Coordinate system. PDF coordinates start from the bottom-left corner, but forge/pdf flips this — (0,0) is the top-left, matching how you think about web layouts. The x and y values are in points (72 points per inch). A4 is 595 × 842 points.

Core APIs

  • PDFForge.create() - Create document
  • text(content, x, y, options) - Add text
  • heading(text, x, y, size) - Add heading
  • table(data, options) - Add table
  • image(src, x, y, width, height) - Add image
  • rect(x, y, width, height, options) - Draw rectangle
  • line(x1, y1, x2, y2, options) - Draw line
  • page() - Add new page
  • footer(text) / header(text) - Add page header/footer
  • watermark(text) - Add watermark
  • signature(imageData, x, y) - Add signature
  • export(format) - Export to PDF/Buffer/Stream

Example: Basic Invoice

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

const pdf = new PDFForge();

// Title
pdf.heading('Invoice', 50, 50, 24);

// Company info
pdf.text('Acme Corp', 50, 90);
pdf.text('123 Business St, City', 50, 110);

// Invoice items
pdf.table([
  ['Item', 'Qty', 'Price', 'Total'],
  ['Widget A', '5', '$10', '$50'],
  ['Widget B', '3', '$20', '$60'],
  ['', '', 'Total:', '$110'],
], {
  x: 50,
  y: 150,
  width: 500,
});

// Footer
pdf.footer('Thank you for your business!');

// Export
const buffer = pdf.export('buffer');
await fs.promises.writeFile('invoice.pdf', buffer);

Example: Report with Images

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

const pdf = new PDFForge();

// Title
pdf.heading('Monthly Report', 50, 50);

// Text
pdf.text('Performance Summary:', 50, 100);
pdf.text('Sales increased by 25% this month.', 50, 120);

// Add chart image
pdf.image('./chart.png', 50, 150, 500, 300);

// New page
pdf.page();
pdf.heading('Detailed Analytics', 50, 50);

// More content...

const buffer = pdf.export('buffer');

forge/notify

Notification system with toast, alert, modal, WebSocket delivery, email, and SMS channels.

Multi-channel, single API. forge/notify unifies toasts, modals, email, SMS, push notifications, and WebSocket delivery behind one notify.send() call. Define channels once, then every notification automatically routes to the right transport — no per-channel boilerplate.

Smart deduplication. If the same notification fires multiple times within the dedup window (default 5s), only the first is shown. This prevents the common UX bug of stacking 10 identical "Connection lost" toasts when a WebSocket flickers.

Notify Constructor

ParameterTypeDefaultDescription
channelsstring[]['toast']Active channels: 'toast', 'alert', 'modal', 'email', 'sms', 'websocket', 'push'
positionstring'top-right'Toast position: 'top-right', 'top-left', 'bottom-right', 'bottom-left', 'top-center', 'bottom-center'
maxVisiblenumber5Maximum simultaneous visible toasts (older ones queue)
dedupWindownumber5000Deduplication window in ms. Set 0 to disable
defaultDurationnumber4000Auto-dismiss duration in ms. Set 0 for persistent

Notification Object

PropertyTypeDescription
titlestringNotification title (shown bold)
messagestringNotification body text
typestring'success', 'error', 'warning', 'info'
prioritystring'urgent', 'high', 'normal', 'low'
channelsstring[]Override which channels to use for this notification
durationnumberOverride auto-dismiss (ms). 0 = persistent
actionobject{ label: string, onClick: function } — action button
userobject{ id, email, phone } — for email/SMS/push routing
scheduledAtDateSchedule for future delivery

Accessibility. Toast notifications automatically use role="alert" and aria-live="polite" (or "assertive" for errors). Screen readers announce them without focus change. For modal notifications, focus is trapped inside the modal and restored on dismiss.

Core APIs

  • NotificationManager - Main manager
  • create(options) - Create notification
  • send(notification) - Send notification
  • dismiss(id) - Dismiss notification
  • Toast, Alert, Modal - Channel types
  • Email, SMS - Delivery channels
  • WebSocket - Real-time delivery
  • Priority levels - Urgent, High, Normal, Low
  • Read receipts - Track if read
  • Scheduling - Send at specific time

Example: Toast Notifications

import { Notify } from '@hyperbridge/forge/notify';

const notify = new Notify();

// Success toast
notify.toast({
  message: 'Account created successfully!',
  type: 'success',
  duration: 3000,
});

// Error toast
notify.toast({
  message: 'Something went wrong. Please try again.',
  type: 'error',
  duration: 5000,
});

// Info with action
notify.toast({
  message: 'Update available',
  type: 'info',
  action: {
    label: 'Refresh',
    onClick: () => window.location.reload(),
  },
});

Example: Channel Selection

import { Notify } from '@hyperbridge/forge/notify';

const notify = new Notify({
  channels: ['toast', 'email', 'websocket'],
});

// Send via all channels
await notify.send({
  title: 'Important Update',
  message: 'New features are available',
  channels: ['toast', 'email', 'websocket'],
  priority: 'high',
  user: {
    id: 'user-123',
    email: 'user@example.com',
  },
});

// Toast only
await notify.toast({ message: 'Quick notification' });

// Email only
await notify.email({
  to: 'user@example.com',
  subject: 'Weekly Report',
  html: '

Your Report

...', });

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"