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
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.
| Parameter | Type | Description |
|---|---|---|
| initialValue | T | The starting value of the signal. Can be any serialisable type. |
| .value | T | Read or write the current value. Setting triggers subscribers. |
| .peek() | T | Read the current value without subscribing (no tracking). |
| .subscribe(fn) | (value: T) => void | Register 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.
| Option | Type | Description |
|---|---|---|
| port | number | Port to listen on. Default: 3000. Overridden by PORT env var. |
| host | string | Bind address. Default: '0.0.0.0'. Use '127.0.0.1' for local-only. |
| trustProxy | boolean | number | Trust X-Forwarded-* headers. true trusts one hop; a number sets the hop count. |
| maxBodySize | string | Maximum request body size. Default: '1mb'. Accepts '10kb', '5mb', etc. |
| cluster | number | boolean | Number 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
| Option | Type | Description |
|---|---|---|
| windowMs | number | Time window in milliseconds. Default: 60000 (1 minute). |
| max | number | Max requests per window per key. Default: 100. |
| message | string | object | Response body when limit is exceeded. Default: { error: 'Too many requests' }. |
| keyGenerator | (req) => string | Function that returns the rate-limit key. Default: req.ip. |
| algorithm | string | Rate limiting strategy: 'sliding-window' (default), 'fixed-window', 'token-bucket', 'leaky-bucket'. |
| skip | (req) => boolean | Return 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
| Option | Type | Description |
|---|---|---|
| secret | string | Buffer | Signing secret. Falls back to process.env.JWT_SECRET. Required for HMAC algorithms. |
| algorithm | string | Signing algorithm: 'HS256' (default), 'HS384', 'HS512', 'RS256', 'ES256'. |
| expiresIn | string | number | Token lifetime. Accepts '15m', '7d', '1h', or seconds as a number. Default: '1h'. |
| issuer | string | The iss claim. Used for token verification. Optional but recommended for multi-service setups. |
| audience | string | 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
| Parameter | Type | Description |
|---|---|---|
| tension | number | Spring stiffness. Higher values = snappier motion. Default: 170. |
| friction | number | Resistance / damping. Higher values = less oscillation. Default: 26. |
| mass | number | Mass of the animated object. Higher = more inertia, slower acceleration. Default: 1. |
| velocity | number | Initial velocity. Use to continue momentum from a gesture. Default: 0. |
| values | object | Map of animated properties with [from, to] pairs, e.g. { scale: [0.8, 1] }. |
| onUpdate | (values) => void | Called on each frame with the current interpolated values. |
| onComplete | () => void | Called 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
| Option | Type | Description |
|---|---|---|
| scrub | boolean | number | Link animation progress to scroll position. A number adds smoothing (e.g. 0.5 = 500ms ease). Default: true. |
| pin | boolean | Pin the element in place during the scroll range. Default: false. |
| snap | number | number[] | Snap to progress points. 0.25 snaps to 0%, 25%, 50%, 75%, 100%. Array for custom stops. |
| start | string | Scroll start position. Format: '<element edge> <viewport edge>', e.g. 'top center'. |
| end | string | Scroll end position. Same format as start. Default: 'bottom top'. |
| onEnter | () => void | Callback fired when the trigger element scrolls into the active range. |
| onLeave | () => void | Callback 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
| Parameter | Type | Default | Description |
|---|---|---|---|
type | string | — | Chart type: 'line', 'bar', 'area', 'pie', 'donut', 'scatter', 'bubble', 'radar', 'heatmap', 'treemap', 'gauge', 'funnel', 'candlestick', 'sparkline' |
container | HTMLElement | — | DOM element to render into |
data | ChartData | — | Object with labels (string[]) and series (array of { name, data }) |
options.title | string | '' | Chart title displayed above the chart area |
options.responsive | boolean | true | Auto-resize with container via ResizeObserver |
options.animation | boolean | true | Enable enter/update/exit animations |
options.colors | string[] | Built-in palette | Custom color palette for series |
options.legend | boolean | object | true | Show legend. Pass object for position: { position: 'bottom' } |
options.tooltip | boolean | function | true | Show tooltip on hover. Pass function for custom formatting |
options.grid | boolean | true | Show background grid lines |
Instance Methods
| Method | Returns | Description |
|---|---|---|
render() | void | Render the chart into the container |
update(data) | void | Update data with animated transitions |
resize() | void | Force recalculate layout (automatic when responsive) |
destroy() | void | Remove chart, cleanup observers and event listeners |
toSVG() | string | Export chart as raw SVG string |
toImage(format) | Promise<Blob> | Export as PNG or JPEG blob |
on(event, fn) | void | Listen 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
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
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
| Option | Type | Default | Description |
|---|---|---|---|
include | string[] | ['**/*.test.js'] | Glob patterns for test files |
exclude | string[] | ['node_modules'] | Glob patterns to exclude |
parallel | boolean | true | Run test files in parallel workers |
timeout | number | 5000 | Default timeout per test (ms) |
coverage | boolean | false | Enable code coverage collection |
bail | boolean | number | false | Stop after N failures. true = stop after first |
watch | boolean | false | Re-run tests on file changes |
Expect Matchers
| Matcher | Description |
|---|---|
.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 / .rejects | Promise 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
| Parameter | Type | Default | Description |
|---|---|---|---|
host | string | — | SMTP server hostname |
port | number | 587 | SMTP port (587 for STARTTLS, 465 for SSL) |
secure | boolean | false | Use TLS from the start (port 465). For STARTTLS on 587, leave false |
auth.user | string | — | SMTP username |
auth.pass | string | — | SMTP password or app-specific password |
pool | boolean | false | Enable connection pooling for high-volume sending |
maxConnections | number | 5 | Max simultaneous SMTP connections when pooling |
retries | number | 3 | Retry count on transient failures (4xx errors) |
sendMail Parameters
| Parameter | Type | Description |
|---|---|---|
from | string | Sender address (e.g., 'Name <email@domain.com>') |
to | string | string[] | Recipient(s) |
cc | string | string[] | CC recipient(s) |
bcc | string | string[] | BCC recipient(s) |
subject | string | Email subject line |
html | string | HTML body |
text | string | Plain text fallback (auto-generated from HTML if omitted) |
attachments | Attachment[] | Array of { filename, content, contentType } |
replyTo | string | Reply-to address |
headers | object | Custom 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.
`,
});
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
| Parameter | Type | Default | Description |
|---|---|---|---|
pageSize | string | 'A4' | Page size: 'A4', 'Letter', 'Legal', 'A3', or { width, height } in points |
orientation | string | 'portrait' | 'portrait' or 'landscape' |
margins | object | { top: 50, right: 50, bottom: 50, left: 50 } | Page margins in points (1 point = 1/72 inch) |
compress | boolean | true | Enable Flate compression for smaller file size |
metadata | object | {} | { title, author, subject, keywords, creator } |
Drawing Methods
| Method | Parameters | Description |
|---|---|---|
text() | content, x, y, { fontSize, color, font, align, maxWidth } | Add text with optional wrapping |
heading() | text, x, y, size | Bold heading at given size |
table() | rows[][], { x, y, width, headerBg, stripedRows, cellPadding } | Auto-layout table from 2D array |
image() | src, x, y, width, height | Embed 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 | function | Page header (function receives page number) |
footer() | text | function | Page 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/search
Full-text search engine with BM25 ranking, facets, aggregations, and persistence.
Elasticsearch power, zero infrastructure. forge/search runs entirely in-process — no Java, no Elasticsearch cluster, no Docker containers. For apps with up to 500K documents, you get sub-millisecond search with BM25 relevance ranking, faceted filtering, and boolean queries. When you outgrow it, the query syntax is compatible with Elasticsearch, so migration is straightforward.
Instant autocomplete. The suggest(prefix) API uses a trie-based prefix index that returns results in under 1ms, even with 100K+ documents indexed. Perfect for search-as-you-type UIs without debounce hacks.
SearchForge Constructor
| Parameter | Type | Default | Description |
|---|---|---|---|
fields | string[] | — | Document fields to index (e.g., ['title', 'body', 'tags']) |
fieldWeights | object | All 1.0 | Boost important fields: { title: 2, body: 1 } |
tokenizer | string | function | 'standard' | 'standard', 'whitespace', 'ngram', or custom function |
stemming | boolean | true | Enable Porter stemmer for English (running → run) |
stopWords | boolean | string[] | true | Remove common words. Pass array for custom stop words |
minWordLength | number | 2 | Minimum token length to index |
persistence | string | null | File path for automatic index persistence (JSON) |
Search Options
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 10 | Maximum results to return |
offset | number | 0 | Skip N results for pagination |
fields | string[] | All indexed | Search only specific fields |
facets | string[] | [] | Return facet counts for given fields |
filter | object | {} | Pre-filter: { category: 'tutorial' } |
highlight | boolean | false | Return highlighted snippets with <mark> tags |
fuzzy | boolean | number | false | Enable fuzzy matching. Number sets max edit distance |
Memory usage. Each indexed document consumes roughly 2-5x its text size in memory (for the inverted index, position data, and term frequencies). For 100K documents averaging 1KB each, expect ~300-500MB of memory. Use persistence to offload to disk and lazy-load on startup.
Core APIs
- SearchForge.create(options) - Create index
- add(id, doc) - Index document
- search(query, options) - Search indexed docs
- suggest(prefix) - Auto-complete suggestions
- remove(id) - Remove document from index
- export() / import() - Persistence
- BM25 ranking - Relevance scoring
- Phrase matching - Exact phrase search
- Boolean operators - AND, OR, NOT
- Field weighting - Boost important fields
- Facets - Filter by category
- Aggregations - Group and count results
Example: Basic Search Index
import { SearchForge } from '@hyperbridge/forge/search';
const search = new SearchForge({
fields: ['title', 'body', 'tags'],
fieldWeights: { title: 2, body: 1, tags: 1.5 },
});
// Index documents
search.add('doc-1', {
title: 'Getting Started with HBForge',
body: 'HBForge is a complete JavaScript framework...',
tags: ['tutorial', 'beginner'],
});
search.add('doc-2', {
title: 'Advanced Features',
body: 'Learn about animations and forms...',
tags: ['advanced', 'guide'],
});
// Search
const results = search.search('HBForge framework', {
limit: 10,
offset: 0,
});
console.log(results);
// [
// { id: 'doc-1', score: 2.5, doc: {...} },
// { id: 'doc-2', score: 0.8, doc: {...} }
// ]
Example: Advanced Queries
import { SearchForge } from '@hyperbridge/forge/search';
const search = new SearchForge({ fields: ['title', 'body', 'category'] });
// Load documents...
// Phrase search
const phrases = search.search('"HBForge framework"');
// Boolean operators
const complex = search.search('(HBForge OR framework) AND -deprecated');
// Field-specific search
const titleSearch = search.search('title:animations');
// Faceted search
const results = search.search('animation', {
facets: ['tags', 'category'],
});
// Auto-complete
const suggestions = search.suggest('HB');
// ['HBForge', 'HTTP', 'HTML']
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
| Parameter | Type | Default | Description |
|---|---|---|---|
channels | string[] | ['toast'] | Active channels: 'toast', 'alert', 'modal', 'email', 'sms', 'websocket', 'push' |
position | string | 'top-right' | Toast position: 'top-right', 'top-left', 'bottom-right', 'bottom-left', 'top-center', 'bottom-center' |
maxVisible | number | 5 | Maximum simultaneous visible toasts (older ones queue) |
dedupWindow | number | 5000 | Deduplication window in ms. Set 0 to disable |
defaultDuration | number | 4000 | Auto-dismiss duration in ms. Set 0 for persistent |
Notification Object
| Property | Type | Description |
|---|---|---|
title | string | Notification title (shown bold) |
message | string | Notification body text |
type | string | 'success', 'error', 'warning', 'info' |
priority | string | 'urgent', 'high', 'normal', 'low' |
channels | string[] | Override which channels to use for this notification |
duration | number | Override auto-dismiss (ms). 0 = persistent |
action | object | { label: string, onClick: function } — action button |
user | object | { id, email, phone } — for email/SMS/push routing |
scheduledAt | Date | Schedule 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
| Parameter | Type | Default | Description |
|---|---|---|---|
name | string | — | CLI tool name (used in help text and error messages) |
version | string | — | Version string (enables --version flag automatically) |
description | string | '' | Tool description shown in help |
strict | boolean | true | Error on unknown options (disable for passthrough CLIs) |
Command Definition
| Method | Parameters | Description |
|---|---|---|
command(name, handler) | string, async function | Register 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, string | Add example to help text |
parse(argv) | string[] | Parse arguments and execute matched command |
Interactive Prompts
| Method | Returns | Description |
|---|---|---|
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
| Parameter | Type | Default | Description |
|---|---|---|---|
defaultLocale | string | 'en' | Default locale (BCP 47 tag, e.g., 'en-US', 'fr-FR', 'ja') |
fallbackLocale | string | 'en' | Fallback when translation key is missing in current locale |
missingKeyHandler | function | Returns key | Custom handler: (key, locale) => string |
interpolation | object | { prefix: '{', suffix: '}' } | Variable delimiters in translation strings |
Translation API
| Method | Returns | Description |
|---|---|---|
t(key, context?) | string | Translate key with optional interpolation context |
addMessages(locale, messages) | void | Register translations for a locale (deep-merged with existing) |
setLocale(locale) | void | Switch active locale (triggers re-renders in forge/client) |
getLocale() | string | Get current active locale |
hasKey(key, locale?) | boolean | Check if translation exists |
pluralize(count, keys) | string | Select plural form based on count and locale rules |
isRTL(locale?) | boolean | Check if locale is right-to-left (Arabic, Hebrew, Urdu, etc.) |
Formatting API
| Method | Returns | Description |
|---|---|---|
formatDate(date, locale?) | string | Locale-aware date string |
formatTime(date, locale?) | string | Locale-aware time string |
formatNumber(num, locale?) | string | Locale-aware number with grouping separators |
formatCurrency(amount, currency, locale?) | string | Currency formatting (symbol, position, decimals) |
formatRelative(date) | string | Relative time (e.g., "2 hours ago", "in 3 days") |
formatList(items, locale?) | string | Locale-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"