[ { "file": "pages/api-components.md", "title": "Component API", "section-id": "api-reference", "keywords": "defineComponent, ref, computed, watch, component API, reactive", "description": "Complete reference for the Velox component API \u2014 defineComponent, ref, computed, and watch", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Component API\n\nThis page documents the core component primitives exported from `velox/client`.\n\n## `signal(initialValue)`\n\nCreates a reactive signal \u2014 a value container that triggers DOM updates when changed.\n\n```typescript\nimport { signal } from 'velox/client';\n\nconst count = signal(0);\ncount.value; // read: 0\ncount.value = 5; // write: triggers reactive updates\ncount.peek(); // read without tracking (no reactive subscription)\n```\n\n### Type Signature\n\n```typescript\nfunction signal(initialValue: T): Signal;\n\ninterface Signal {\n value: T;\n peek(): T;\n subscribe(listener: (value: T) => void): () => void;\n}\n```\n\n## `computed(fn)`\n\nCreates a derived reactive value. Re-evaluates lazily when accessed and a dependency has changed.\n\n```typescript\nimport { signal, computed } from 'velox/client';\n\nconst price = signal(100);\nconst taxRate = signal(0.2);\nconst total = computed(() => price.value * (1 + taxRate.value));\n\ntotal.value; // 120\nprice.value = 200;\ntotal.value; // 240\n```\n\nComputed signals are read-only \u2014 attempting to set `.value` throws.\n\n## `effect(fn)`\n\nRegisters a reactive side effect. Runs immediately, then again whenever its signal dependencies change.\n\n```typescript\nimport { signal, effect } from 'velox/client';\n\nconst query = signal('');\n\nconst dispose = effect(() => {\n console.log('query =', query.value);\n // runs now, then on every change to query.value\n});\n\n// Clean up manually (effects inside components clean up on unmount):\ndispose();\n```\n\nReturn a cleanup function from the effect to run before the next execution or on disposal:\n\n```typescript\neffect(() => {\n const controller = new AbortController();\n fetchData(query.value, controller.signal);\n return () => controller.abort();\n});\n```\n\n## `batch(fn)`\n\nGroups multiple signal writes into a single reactive update pass:\n\n```typescript\nimport { batch } from 'velox/client';\n\nbatch(() => {\n a.value = 1;\n b.value = 2;\n c.value = 3;\n // Only one round of re-renders happens\n});\n```\n\n## `untrack(fn)`\n\nExecute a function without tracking its signal reads as dependencies:\n\n```typescript\nimport { signal, effect, untrack } from 'velox/client';\n\nconst a = signal(1);\nconst b = signal(2);\n\neffect(() => {\n // Only subscribes to `a`, not `b`\n const result = a.value + untrack(() => b.value);\n console.log(result);\n});\n```\n\n## Lifecycle Hooks\n\nLifecycle hooks must be called synchronously during component initialisation (similar to React rules of hooks, but without the runtime check overhead).\n\n### `onMount(fn)`\n\nCalled after the component's DOM is inserted into the document:\n\n```typescript\nimport { onMount } from 'velox/client';\n\nonMount(() => {\n // safe to access DOM, start timers, etc.\n const input = document.querySelector('#my-input') as HTMLInputElement;\n input.focus();\n});\n```\n\n### `onCleanup(fn)`\n\nCalled before the component unmounts, or before an effect runs again. Use to cancel subscriptions, abort requests, and clear timers:\n\n```typescript\nimport { onMount, onCleanup } from 'velox/client';\n\nonMount(() => {\n const timer = setInterval(tick, 1000);\n onCleanup(() => clearInterval(timer));\n});\n```\n\n### `onDestroy(fn)`\n\nLike `onCleanup`, but only runs when the component is permanently destroyed (not before re-renders):\n\n```typescript\nimport { onDestroy } from 'velox/client';\n\nonDestroy(() => {\n analytics.trackLeave(route);\n});\n```\n\n## `createContext` / `useContext`\n\nCreate a context for dependency injection without prop drilling:\n\n```typescript\nimport { createContext, useContext } from 'velox/client';\n\n// Create context with a default value\nconst ThemeContext = createContext<'light' | 'dark'>('light');\n\n// Provide a value to a subtree\n\n \n\n\n// Consume in any descendant\nfunction Button() {\n const theme = useContext(ThemeContext);\n return ;\n}\n```\n\n## `ref()`\n\nCreate a DOM element reference:\n\n```typescript\nimport { ref, onMount } from 'velox/client';\n\nexport default function TextInput() {\n const inputRef = ref();\n\n onMount(() => {\n inputRef.current?.focus();\n });\n\n return ;\n}\n```\n\n## `defineComponent(options)`\n\nThe explicit component definition API \u2014 useful when you need named components for debugging or when defining components programmatically:\n\n```typescript\nimport { defineComponent, signal } from 'velox/client';\n\nconst Counter = defineComponent({\n name: 'Counter',\n props: {\n initialValue: { type: Number, default: 0 },\n step: { type: Number, default: 1 },\n },\n setup(props) {\n const count = signal(props.initialValue);\n const increment = () => { count.value += props.step; };\n const decrement = () => { count.value -= props.step; };\n\n return { count, increment, decrement };\n },\n render({ count, increment, decrement }) {\n return (\n
\n \n {count" }, { "file": "pages/api-config.md", "title": "Config API", "section-id": "api-reference", "keywords": "config API, velox.config.ts, defineConfig, types, TypeScript reference", "description": "Full TypeScript type reference for velox.config.ts and all configuration options", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Config API\n\nThis page provides the complete TypeScript type reference for `velox.config.ts`. All types are exported from the `velox` package.\n\n## `defineConfig(config)`\n\nThe primary export. Accepts a `VeloxConfig` object and returns it with full type checking:\n\n```typescript\nimport { defineConfig } from 'velox';\n\nexport default defineConfig({\n // VeloxConfig object\n});\n```\n\nYou can also pass a function for dynamic configuration:\n\n```typescript\nexport default defineConfig(async (env) => {\n const secrets = await loadSecrets();\n return {\n app: {\n name: 'My App',\n baseUrl: secrets.BASE_URL,\n },\n };\n});\n```\n\n## `VeloxConfig`\n\n```typescript\ninterface VeloxConfig {\n app?: AppConfig;\n server?: ServerConfig;\n build?: BuildConfig;\n routes?: RoutesConfig;\n assets?: AssetsConfig;\n css?: CSSConfig;\n i18n?: I18nConfig;\n middleware?: MiddlewareConfig[];\n plugins?: VeloxPlugin[];\n experimental?: ExperimentalConfig;\n errorHandler?: ErrorHandler;\n}\n```\n\n## `AppConfig`\n\n```typescript\ninterface AppConfig {\n /** Application display name. Used in page titles and error pages. */\n name: string;\n\n /** Canonical base URL. Required in production. Example: 'https://example.com' */\n baseUrl: string;\n\n /** Default locale for i18n. Default: 'en' */\n defaultLocale?: string;\n\n /** Whether to append trailing slashes to all routes. Default: false */\n trailingSlash?: boolean;\n\n /** Custom 404 page route. Default: '_error' */\n notFoundPage?: string;\n}\n```\n\n## `ServerConfig`\n\n```typescript\ninterface ServerConfig {\n /** TCP port. Default: 3700 */\n port?: number;\n\n /** Bind hostname. Default: 'localhost' */\n host?: string;\n\n /** HTTPS configuration for the development server */\n https?: {\n cert: string; // path to PEM certificate\n key: string; // path to PEM private key\n };\n\n /** CORS policy */\n cors?: {\n origin: string | string[] | ((origin: string) => boolean);\n credentials?: boolean;\n methods?: string[];\n allowedHeaders?: string[];\n exposedHeaders?: string[];\n maxAge?: number;\n };\n\n /** Compression. Default: true in production */\n compress?: boolean;\n\n /** Trust proxy headers (X-Forwarded-For, etc.). Default: false */\n trustProxy?: boolean | number;\n}\n```\n\n## `BuildConfig`\n\n```typescript\ninterface BuildConfig {\n /**\n * Deployment target.\n * - 'node': Outputs a Node.js server (default)\n * - 'edge': Outputs a Web-API-compatible edge bundle\n * - 'static': Full static export \u2014 no server required\n */\n target: 'node' | 'edge' | 'static';\n\n /** Output directory. Default: '.velox/output' */\n outDir?: string;\n\n /** Source map generation. Default: false in production */\n sourcemap?: boolean | 'external' | 'inline';\n\n /** Minify output. Default: true in production */\n minify?: boolean;\n\n /** Enable code splitting. Default: true */\n splitting?: boolean;\n\n /** Emit a bundle analysis HTML report */\n analyze?: boolean;\n\n /**\n * Routes to explicitly pre-render as static HTML.\n * Supports globs. e.g. ['/blog/*', '/about']\n */\n prerender?: string[];\n\n /** External dependencies (not bundled). */\n external?: string[];\n\n /** Environment variables to inline into the client bundle */\n define?: Record;\n}\n```\n\n## `RoutesConfig`\n\n```typescript\ninterface RoutesConfig {\n /** Routes directory relative to project root. Default: 'routes' */\n dir?: string;\n\n /** File extensions treated as routes. Default: ['.velox', '.tsx'] */\n extensions?: string[];\n\n /** Glob patterns to exclude from routing */\n exclude?: string[];\n\n /** Prefix all generated routes with this base path */\n base?: string;\n}\n```\n\n## `AssetsConfig`\n\n```typescript\ninterface AssetsConfig {\n /** Static assets directory. Default: 'public' */\n publicDir?: string;\n\n imageOptimisation?: {\n enabled: boolean;\n /** Output formats to generate. Default: ['webp'] */\n formats?: ('webp' | 'avif' | 'jpeg' | 'png')[];\n /** JPEG/WebP/AVIF quality 1\u2013100. Default: 80 */\n quality?: number;\n /** Maximum width in pixels before downscaling */\n maxWidth?: number;\n };\n\n fonts?: {\n /** Emit for fonts. Default: true */\n preload?: boolean;\n /** Unicode range subsets. Example: ['latin', 'latin-ext'] */\n subsets?: string[];\n };\n}\n```\n\n## `I18nConfig`\n\n```typescript\ninterface I18nConfig {\n /** Supported locale codes. Example: ['en', 'fr', 'de'] */\n locales: string[];\n\n /** Default locale. Default: 'en' */\n defaultLocale: string;\n\n /** Strategy for locale in URL. Default: 'prefix-except-default' */\n routing?: 'prefix' | 'prefix-except-default' | 'domain';\n\n /** Domain mapping for 'domain' routing strategy */\n domains?: Record;\n\n /** Path to translation files. Default: 'messages' */\n messagesDir?: string;\n}\n```\n\n## `MiddlewareConfig`\n\n```typescript\ninterface MiddlewareConfig {\n /** Route path glob to match. Use '*' for global middleware */\n path: string;\n\n /** Path to the middleware module (relative to project root" }, { "file": "pages/api-hooks.md", "title": "Hooks API", "section-id": "api-reference", "keywords": "hooks, useRequest, useSession, useCookies, useEnv, server hooks", "description": "Reference for Velox server-side hooks \u2014 useRequest, useSession, useCookies, and useEnv", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Hooks API\n\nVelox provides server-side hooks for accessing request context, sessions, cookies, and environment variables within route server blocks and middleware. These hooks are only available in server-side contexts.\n\n## `useRequest()`\n\nReturns the current `VeloxRequest` object:\n\n```typescript\nimport { useRequest } from 'velox/server';\n\nconst request = useRequest();\n\nconst method = request.method;\nconst pathname = new URL(request.url).pathname;\nconst userAgent = request.headers.get('User-Agent');\n```\n\nThis is equivalent to the `request` variable that is automatically available in server blocks, but `useRequest()` is useful in helper functions that are called from a server block without threading `request` through manually.\n\n### Full Request Object Reference\n\n```typescript\ninterface VeloxRequest extends Request {\n params: Record; // route dynamic params\n query: URLSearchParams; // parsed query string\n cookies: RequestCookies; // parsed cookies\n context: Map; // middleware-set values\n ip: string | null; // client IP address\n geo: GeoInfo | null; // geographic info (if available)\n}\n```\n\n## `useSession()`\n\nReads and writes the server-managed session. Sessions are stored server-side (in memory, Redis, or a database depending on your `session.store` configuration) and identified by a signed cookie.\n\n```typescript\nimport { useSession } from 'velox/server';\n\nconst session = await useSession<{ userId: string; role: string }>();\n\n// Read\nconst userId = session.data.userId;\nconst role = session.data.role;\n\n// Write \u2014 persists changes to the session store\nawait session.set('userId', '123');\nawait session.set('role', 'admin');\n\n// Update multiple at once\nawait session.update({ userId: '123', role: 'admin' });\n\n// Destroy the session (logout)\nawait session.destroy();\n\n// Regenerate session ID (after privilege change \u2014 prevents fixation)\nawait session.regenerate();\n```\n\n### Session Configuration\n\nConfigure the session store in `velox.config.ts`:\n\n```typescript\nimport { defineConfig } from 'velox';\nimport { RedisSessionStore } from '@velox/session-redis';\n\nexport default defineConfig({\n session: {\n secret: process.env.SESSION_SECRET!,\n cookieName: 'velox.session',\n maxAge: 60 * 60 * 24 * 7, // 7 days\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n store: new RedisSessionStore({\n url: process.env.REDIS_URL!,\n }),\n },\n});\n```\n\n## `useCookies()`\n\nRead and write cookies. Returns a `CookieJar` object:\n\n```typescript\nimport { useCookies } from 'velox/server';\n\nconst cookies = useCookies();\n\n// Read\nconst theme = cookies.get('theme')?.value ?? 'light';\nconst hasConsented = cookies.get('consent')?.value === 'true';\n\n// Write (adds Set-Cookie header to the response)\ncookies.set('theme', 'dark', {\n path: '/',\n maxAge: 365 * 24 * 60 * 60, // 1 year\n sameSite: 'lax',\n});\n\n// Delete\ncookies.delete('old-cookie', { path: '/' });\n```\n\n### Cookie Options\n\n| Option | Type | Description |\n|--------|------|-------------|\n| `domain` | `string` | Cookie domain |\n| `path` | `string` | Cookie path. Default: `/` |\n| `maxAge` | `number` | Expiry in seconds |\n| `expires` | `Date` | Expiry as a date |\n| `httpOnly` | `boolean` | Prevent JavaScript access |\n| `secure` | `boolean` | HTTPS only |\n| `sameSite` | `'strict' \\| 'lax' \\| 'none'` | SameSite policy |\n\n## `useEnv(key, defaultValue?)`\n\nType-safe environment variable access:\n\n```typescript\nimport { useEnv } from 'velox/server';\n\nconst dbUrl = useEnv('DATABASE_URL'); // throws if not set\nconst port = useEnv('PORT', '3700'); // returns default if not set\nconst debug = useEnv.boolean('DEBUG', false); // parse as boolean\nconst timeout = useEnv.number('TIMEOUT', 30); // parse as number\n```\n\n### `useEnv` Methods\n\n| Method | Description |\n|--------|-------------|\n| `useEnv(key)` | Return string value, throw if missing |\n| `useEnv(key, default)` | Return string value or default |\n| `useEnv.boolean(key, default?)` | Parse as boolean (`'true'` / `'1'` = `true`) |\n| `useEnv.number(key, default?)` | Parse as float |\n| `useEnv.json(key, default?)` | Parse as JSON |\n| `useEnv.url(key, default?)` | Validate and return as URL string |\n\n## `useHeaders()`\n\nAccess and modify response headers from within a server block or middleware:\n\n```typescript\nimport { useHeaders } from 'velox/server';\n\nconst headers = useHeaders();\n\n// Read request headers\nconst accept = headers.request.get('Accept');\n\n// Set response headers\nheaders.response.set('Cache-Control', 'public, max-age=3600');\nheaders.response.set('X-Custom-Header', 'value');\n```\n\n## `useLocale()`\n\nReturns the current request locale (resolved by the i18n middleware):\n\n```typescript\nimport { useLocale } from 'velox/server';\n\nconst { locale, locales, defaultLocale } = useLocale();\n\n// locale: 'fr' (current request locale)\n// locales: ['en', 'fr" }, { "file": "pages/api-router.md", "title": "Router API", "section-id": "api-reference", "keywords": "router, useRouter, navigate, Link, createRouter, programmatic navigation", "description": "Complete API reference for the Velox router \u2014 createRouter, useRouter, navigate, and Link", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Router API\n\nThe Velox router provides both declarative and programmatic navigation. This page documents the full public API surface of `@velox/router`.\n\n## `useRouter()`\n\nThe `useRouter` hook returns the current router instance. It is available inside any client-side component:\n\n```typescript\nimport { useRouter } from 'velox/client';\n\nconst router = useRouter();\n```\n\n### Router Instance Properties\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `pathname` | `string` | Current URL pathname, e.g. `/blog/my-post` |\n| `search` | `string` | Current query string including `?`, e.g. `?page=2` |\n| `hash` | `string` | Current hash fragment including `#` |\n| `params` | `Record` | Dynamic route parameters |\n| `query` | `URLSearchParams` | Parsed query parameters |\n| `state` | `unknown` | History state object (if provided to `navigate`) |\n\n### Router Instance Methods\n\n#### `navigate(href, options?)`\n\nPerforms client-side navigation to the given href:\n\n```typescript\nrouter.navigate('/dashboard');\n\n// With options:\nrouter.navigate('/profile/edit', {\n replace: true, // replace current history entry\n state: { from: '/dashboard' }, // pass arbitrary state\n scroll: false, // don't scroll to top after navigation\n});\n```\n\n#### `back()` / `forward()`\n\nNavigate through the browser history stack:\n\n```typescript\nrouter.back();\nrouter.forward();\n```\n\n#### `prefetch(href)`\n\nManually prefetch a route (downloads the JS bundle and optionally data):\n\n```typescript\nrouter.prefetch('/heavy-page');\n```\n\n#### `refresh()`\n\nRe-run the current route's server block and update the page without a full navigation:\n\n```typescript\nrouter.refresh();\n```\n\n## `` Component\n\nThe `` component renders an accessible `` element with client-side navigation and built-in prefetching:\n\n```tsx\nimport { Link } from 'velox/client';\n\nAbout\n```\n\n### Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `href` | `string` | required | Destination URL |\n| `prefetch` | `'hover' \\| 'viewport' \\| false` | `'hover'` | Prefetch strategy |\n| `replace` | `boolean` | `false` | Replace history entry instead of pushing |\n| `scroll` | `boolean` | `true` | Scroll to top after navigation |\n| `state` | `unknown` | `undefined` | History state object |\n| `activeClass` | `string` | `'active'` | CSS class applied when href matches current pathname |\n| `exactActiveClass` | `string` | `'exact-active'` | CSS class applied only on exact pathname match |\n\n```tsx\n\n Blog\n\n```\n\n## `createRouter()`\n\nFor testing or server-side use, create a router instance manually:\n\n```typescript\nimport { createRouter } from 'velox/router';\n\nconst router = createRouter({\n base: '/app', // base path prefix\n history: 'hash', // 'browser' (default) | 'hash' | 'memory'\n scrollRestoration: 'auto', // 'auto' | 'manual'\n});\n```\n\n## `useParams()`\n\nAccess current route parameters in any component:\n\n```typescript\nimport { useParams } from 'velox/client';\n\nconst { slug, category } = useParams<{ slug: string; category: string }>();\n```\n\n## `useSearchParams()`\n\nRead and update the URL search parameters reactively:\n\n```typescript\nimport { useSearchParams } from 'velox/client';\n\nconst [searchParams, setSearchParams] = useSearchParams();\n\n// Read\nconst page = searchParams.get('page') ?? '1';\n\n// Update (triggers navigation without full page reload)\nsetSearchParams({ page: String(currentPage + 1) });\n\n// Merge with existing params\nsetSearchParams(prev => {\n prev.set('sort', 'date');\n return prev;\n});\n```\n\n## `usePathname()`\n\nReactively access the current pathname:\n\n```typescript\nimport { usePathname } from 'velox/client';\n\nconst pathname = usePathname();\n// pathname is a Signal\n```\n\n## `useNavigate()`\n\nA lightweight hook that returns only the `navigate` function, without the full router object:\n\n```typescript\nimport { useNavigate } from 'velox/client';\n\nconst navigate = useNavigate();\nnavigate('/login');\n```\n\n## Navigation Events\n\nSubscribe to router lifecycle events:\n\n```typescript\nimport { useRouter } from 'velox/client';\n\nconst router = useRouter();\n\nconst unsubscribe = router.on('beforeNavigate', ({ to, from, cancel }) => {\n if (hasUnsavedChanges && !confirm('Leave without saving?')) {\n cancel();\n }\n});\n\n// Clean up\nonCleanup(unsubscribe);\n```\n\nAvailable events: `beforeNavigate`, `afterNavigate`, `navigationError`.\n\n## `redirect()` (Server)\n\nUsed inside server blocks and API route handlers:\n\n```typescript\nimport { redirect } from 'velox/server';\n\n// Temporary redirect (302)\nthrow redirect('/login');\n\n// Permanent redirect (301)\nthrow redirect('/new-url', 301);\n\n// With custom headers\nthrow redirect('/dashboard', 302, {\n 'Set-Cookie': 'session=...; Path=/',\n});\n```\n\n## `notFound()` (Server)\n\nTrigger the nearest `_error.velox" }, { "file": "pages/api-server.md", "title": "Server API", "section-id": "api-reference", "keywords": "server API, createServer, defineHandler, Response helpers, server-side", "description": "Reference for Velox server-side APIs \u2014 createServer, defineHandler, and Response helpers", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Server API\n\nThe Velox server API provides utilities for API route handlers, middleware, and server-side logic. All exports documented here come from `velox/server`.\n\n## `defineHandler(fn)`\n\nWraps an API route handler with type inference and error handling:\n\n```typescript\nimport { defineHandler } from 'velox/server';\n\nexport const GET = defineHandler(async (request) => {\n return Response.json({ status: 'ok' });\n});\n```\n\nThe handler receives a `VeloxRequest` object (an extension of the standard `Request`) and must return a `Response` or `Promise`.\n\n### `VeloxRequest`\n\nThe enhanced request object passed to handlers:\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `url` | `string` | Full request URL |\n| `method` | `string` | HTTP method (uppercase) |\n| `headers` | `Headers` | Request headers |\n| `params` | `Record` | URL route parameters |\n| `query` | `URLSearchParams` | Parsed query string |\n| `cookies` | `RequestCookies` | Parsed cookies |\n| `context` | `Map` | Values set by middleware |\n| `json()` | `Promise` | Parse body as JSON |\n| `text()` | `Promise` | Parse body as text |\n| `formData()` | `Promise` | Parse body as form data |\n| `arrayBuffer()` | `Promise` | Parse body as binary |\n\n## Response Helpers\n\nVelox exports a set of `Response` factory helpers for common responses:\n\n### `json(data, init?)`\n\nReturn a JSON response with appropriate `Content-Type` header:\n\n```typescript\nimport { json } from 'velox/server';\n\nreturn json({ users }, { status: 200 });\nreturn json({ error: 'Not found' }, { status: 404 });\n```\n\n### `text(body, init?)`\n\nReturn a plain text response:\n\n```typescript\nimport { text } from 'velox/server';\n\nreturn text('Hello, World!');\nreturn text('Not Found', { status: 404 });\n```\n\n### `html(body, init?)`\n\nReturn an HTML response:\n\n```typescript\nimport { html } from 'velox/server';\n\nreturn html('

Hello

');\n```\n\n### `redirect(url, status?, headers?)`\n\nReturn a redirect response:\n\n```typescript\nimport { redirect } from 'velox/server';\n\nreturn redirect('/login', 302);\nreturn redirect('/new-location', 301);\n```\n\n### `notFound(message?)`\n\nReturn a 404 response:\n\n```typescript\nimport { notFound } from 'velox/server';\n\nreturn notFound('User not found');\n// Returns: Response with status 404 and JSON body\n```\n\n### `unauthorized(message?)`\n\nReturn a 401 response:\n\n```typescript\nimport { unauthorized } from 'velox/server';\n\nreturn unauthorized('Please log in');\n```\n\n### `forbidden(message?)`\n\nReturn a 403 response:\n\n```typescript\nimport { forbidden } from 'velox/server';\n\nreturn forbidden('You do not have access to this resource');\n```\n\n### `badRequest(message?, details?)`\n\nReturn a 400 response with optional validation details:\n\n```typescript\nimport { badRequest } from 'velox/server';\n\nreturn badRequest('Validation failed', { field: 'email', message: 'Invalid format' });\n```\n\n### `stream(generator, init?)`\n\nStream a response body using an async generator:\n\n```typescript\nimport { stream } from 'velox/server';\n\nexport const GET = defineHandler(async () => {\n return stream(async function* () {\n for await (const chunk of getLargeDataset()) {\n yield JSON.stringify(chunk) + '\\n';\n }\n });\n});\n```\n\n## `createServer(options)`\n\nBootstrap a standalone Velox server programmatically (useful for testing):\n\n```typescript\nimport { createServer } from 'velox/server';\n\nconst server = await createServer({\n root: '/path/to/project',\n port: 3700,\n mode: 'development',\n});\n\nawait server.start();\nconsole.log(`Server running on port ${server.port}`);\n\n// Later:\nawait server.stop();\n```\n\n### `VeloxServer` Methods\n\n| Method | Description |\n|--------|-------------|\n| `start()` | Start the HTTP server |\n| `stop()` | Gracefully shut down the server |\n| `getUrl()` | Returns the server base URL as a string |\n| `inject(request)` | Inject a synthetic request for testing without a network |\n\n## Cookies\n\n### Reading Cookies\n\n```typescript\nconst session = request.cookies.get('session');\nconst userId = request.cookies.get('userId')?.value;\n```\n\n### Setting Cookies\n\nUse the `ResponseCookies` API on the response object, or the `setCookie` helper:\n\n```typescript\nimport { setCookie, deleteCookie } from 'velox/server';\n\nconst response = json({ ok: true });\nsetCookie(response, 'session', token, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n path: '/',\n maxAge: 60 * 60 * 24 * 7, // 7 days\n});\n\nreturn response;\n```\n\n### Deleting Cookies\n\n```typescript\nconst response = redirect('/login');\ndeleteCookie(response, 'session');\nreturn response;\n```\n\n## `useEnv(key, defaultValue?)`\n\nType-safe environment variable access with optional validation:\n\n```typescript\nimport { useEnv } from 'velox/server';\n\nconst dbUrl = useEnv('DATABASE_URL'); // throws if missing\nconst port = useEnv('PORT', '3700'); // returns defaultValue if missing\nconst apiKey = useEnv.required('SECRET_KEY'); // alias for useEn" }, { "file": "pages/components.md", "title": "Components", "section-id": "core-concepts", "keywords": "components, props, slots, lifecycle, tsx, velox components, islands", "description": "The Velox component model, including props, slots, lifecycle hooks, and the islands architecture", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Components\n\nVelox components are TypeScript/TSX files that describe a piece of UI. They are the fundamental building blocks of a Velox application \u2014 reusable, composable, and by default rendered entirely on the server.\n\n## Defining a Component\n\nA basic Velox component is a TypeScript function that returns JSX:\n\n```tsx\n// components/Greeting.tsx\ninterface GreetingProps {\n name: string;\n formal?: boolean;\n}\n\nexport default function Greeting({ name, formal = false }: GreetingProps) {\n const salutation = formal ? 'Good day' : 'Hello';\n return

{salutation}, {name}!

;\n}\n```\n\nUse it in a route or another component:\n\n```tsx\n---\nimport Greeting from '../components/Greeting.tsx';\n---\n\n
\n \n \n
\n```\n\n## Props\n\nProps are typed using TypeScript interfaces or types. All props are validated at compile time \u2014 no runtime prop checking overhead.\n\n```tsx\ninterface CardProps {\n title: string;\n description: string;\n href?: string;\n variant?: 'default' | 'featured' | 'compact';\n children?: VeloxNode;\n}\n\nexport default function Card({\n title,\n description,\n href,\n variant = 'default',\n children,\n}: CardProps) {\n return (\n
\n

{href ? {title} : title}

\n

{description}

\n {children &&
{children}
}\n
\n );\n}\n```\n\n### Default Props\n\nSet default values directly in the destructuring parameter, as shown above. Velox does not use a separate `defaultProps` mechanism.\n\n### Required vs Optional Props\n\nBy TypeScript convention, optional props are marked with `?`. Omitting a required prop is a compile-time error.\n\n## Slots\n\nThe `children` prop is the default slot \u2014 content placed between the opening and closing tags of a component:\n\n```tsx\n\n

This is the card body.

\n
\n```\n\n### Named Slots\n\nFor more complex component APIs, use named slot props:\n\n```tsx\ninterface ModalProps {\n title: VeloxNode;\n footer?: VeloxNode;\n children: VeloxNode;\n}\n\nexport default function Modal({ title, footer, children }: ModalProps) {\n return (\n
\n
{title}
\n
{children}
\n {footer &&
{footer}
}\n
\n );\n}\n```\n\nUsage:\n\n```tsx\nConfirm Delete}\n footer={\n <>\n \n \n \n }\n>\n

Are you sure you want to delete this item?

\n\n```\n\n## Server vs Client Components\n\nBy default, all Velox components run on the server. They have no JavaScript bundle size on the client and cannot use browser APIs or client-side reactivity.\n\nTo make a component interactive on the client, use the `client:*` hydration directive when you include it in a route:\n\n```tsx\n---\nimport Counter from '../components/Counter.tsx';\nimport LazyChart from '../components/LazyChart.tsx';\n---\n\n\n\n\n\n\n\n\n\n\n\n\n```\n\n### Writing Client Components\n\nClient components can use signals, effects, and browser APIs:\n\n```tsx\nimport { signal, effect, onMount, onCleanup } from 'velox/client';\n\nexport default function LiveClock() {\n const time = signal(new Date().toLocaleTimeString());\n\n onMount(() => {\n const interval = setInterval(() => {\n time.value = new Date().toLocaleTimeString();\n }, 1000);\n\n onCleanup(() => clearInterval(interval));\n });\n\n return

Current time: {time}

;\n}\n```\n\n## Lifecycle Hooks\n\nClient-side components can use the following lifecycle hooks:\n\n| Hook | When it runs |\n|------|-------------|\n| `onMount(fn)` | After the component is first rendered and inserted into the DOM |\n| `onUpdate(fn)` | After every re-render (reactive signal change) |\n| `onCleanup(fn)` | Before the component unmounts or before the next `onUpdate` call |\n| `onDestroy(fn)` | When the component is permanently unmounted |\n\n```tsx\nimport { signal, onMount, onUpdate, onCleanup } from 'velox/client';\n\nexport default function DataComponent() {\n const data = signal([]);\n const loading = signal(true);\n\n onMount(async () => {\n const response = await fetch('/api/data');\n data.value = await response.json();\n loading.value = false;\n });\n\n onCleanup(() => {\n // abort pending requests, clear timers, etc.\n });\n\n return (\n
\n {loading.value ?

Loading...

:
    {data.value.map(d =>
  • {d.name}
  • )}
}\n
\n );\n}\n```\n\n## Component Composition Patterns\n\n### Higher-Order Components\n\n```tsx\nfunction withAuth(Component: VeloxComponent) {\n retur" }, { "file": "pages/configuration.md", "title": "Configuration", "section-id": "getting-started", "keywords": "configuration, velox.config.ts, settings, options, defineConfig", "description": "Complete reference for velox.config.ts and all available configuration options", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Configuration\n\nVelox is configured through a single `velox.config.ts` file at the root of your project. This file is evaluated at build time (and at dev-server startup) to determine how Velox should compile and serve your application.\n\n## Creating the Config File\n\nIf you used `create-velox` to scaffold your project, a `velox.config.ts` is generated for you. To create one manually:\n\n```typescript\nimport { defineConfig } from 'velox';\n\nexport default defineConfig({\n // options go here\n});\n```\n\n`defineConfig` is a helper that provides full TypeScript type inference over the configuration object \u2014 use it rather than exporting a plain object.\n\n## Top-Level Options\n\n### `app`\n\nGeneral application metadata.\n\n```typescript\napp: {\n name: string; // used in HTML and meta tags\n baseUrl: string; // canonical base URL (e.g. https://example.com)\n defaultLocale?: string; // default locale for i18n (default: 'en')\n trailingSlash?: boolean; // append trailing slash to all routes (default: false)\n}\n```\n\nExample:\n\n```typescript\napp: {\n name: 'My Velox App',\n baseUrl: process.env.PUBLIC_BASE_URL ?? 'http://localhost:3700',\n defaultLocale: 'en',\n trailingSlash: false,\n},\n```\n\n### `server`\n\nDevelopment and production server settings.\n\n```typescript\nserver: {\n port?: number; // dev server port (default: 3700)\n host?: string; // bind address (default: 'localhost')\n https?: { // enable HTTPS for the dev server\n cert: string; // path to certificate file\n key: string; // path to key file\n };\n cors?: {\n origin: string | string[] | '*';\n credentials?: boolean;\n methods?: string[];\n };\n}\n```\n\n### `build`\n\nBuild system options.\n\n```typescript\nbuild: {\n target: 'node' | 'edge' | 'static'; // deployment target\n outDir?: string; // output directory (default: '.velox/output')\n sourcemap?: boolean | 'external'; // generate source maps\n minify?: boolean; // minify output (default: true in production)\n splitting?: boolean; // enable code splitting (default: true)\n analyze?: boolean; // emit a bundle analysis report\n prerender?: string[]; // explicit list of routes to pre-render\n}\n```\n\n### `routes`\n\nFine-grained routing options.\n\n```typescript\nroutes: {\n dir?: string; // routes directory (default: 'routes')\n extensions?: string[]; // file extensions treated as routes\n // default: ['.velox', '.tsx', '.ts']\n exclude?: string[]; // glob patterns to exclude\n}\n```\n\n### `assets`\n\nAsset handling and optimisation.\n\n```typescript\nassets: {\n publicDir?: string; // static assets directory (default: 'public')\n imageOptimisation?: {\n enabled: boolean;\n formats?: ('webp' | 'avif')[];\n quality?: number; // 1\u2013100, default 80\n };\n fonts?: {\n preload?: boolean;\n subsets?: string[];\n };\n}\n```\n\n### `css`\n\nStylesheet handling.\n\n```typescript\ncss: {\n modules?: {\n localsConvention?: 'camelCase' | 'camelCaseOnly' | 'dashes';\n generateScopedName?: string;\n };\n preprocessors?: {\n sass?: boolean; // enable Sass (requires @velox/sass)\n less?: boolean;\n stylus?: boolean;\n };\n postcss?: {\n plugins?: any[];\n };\n}\n```\n\n### `plugins`\n\nThe plugin array lets you extend Velox with first-party and community plugins.\n\n```typescript\nimport { defineConfig } from 'velox';\nimport { veloxMDX } from '@velox/mdx';\nimport { veloxSass } from '@velox/sass';\nimport { veloxPWA } from '@velox/pwa';\n\nexport default defineConfig({\n plugins: [\n veloxMDX(),\n veloxSass(),\n veloxPWA({\n name: 'My App',\n themeColor: '#3a7bd5',\n }),\n ],\n});\n```\n\n### `experimental`\n\nOpt-in to experimental features that are not yet stable.\n\n```typescript\nexperimental: {\n viewTransitions?: boolean; // CSS View Transitions API support\n partialHydration?: boolean; // fine-grained component hydration\n reactCompat?: boolean; // React component compatibility shim\n}\n```\n\n## Environment-Specific Configuration\n\nYou can provide environment-specific overrides:\n\n```typescript\nimport { defineConfig } from 'velox';\n\nexport default defineConfig({\n app: {\n name: 'My App',\n baseUrl: process.env.PUBLIC_BASE_URL!,\n },\n server: {\n port: parseInt(process.env.PORT ?? '3700'),\n },\n build: {\n target: process.env.VELOX_TARGET as 'node' | 'edge' ?? 'node',\n sourcemap: process.env.NODE_ENV !== 'production',\n },\n});\n```\n\n## Per-Route Rendering Configuration\n\nYou can override the rendering mode for individual routes from within the route file:\n\n```typescript\n// routes/dashboard.velox server block\nexport const config = {\n render: 'ssr', // 'ssr' | 'ssg' | 'isr' | 'csr'\n revalidate: 60, // ISR: revalidate every 60 seconds\n edge: true, // run on edge runtime\n};\n```\n\n## Full Example\n\nA production-ready `velox.config.ts` for " }, { "file": "pages/data-fetching.md", "title": "Data Fetching", "section-id": "core-concepts", "keywords": "data fetching, SSR, SSG, ISR, server-side rendering, static generation, fetch, async", "description": "How to fetch data in Velox using SSR, SSG, ISR, and client-side fetching patterns", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Data Fetching\n\nVelox provides multiple data-fetching patterns depending on when and how often your data changes. You can mix strategies freely across routes in the same project.\n\n## Server-Side Rendering (SSR)\n\nIn SSR mode, data is fetched fresh on every request. Use `await` directly in the server block of a `.velox` route:\n\n```tsx\n---\n// routes/blog/[slug].velox\nimport { db } from '$lib/db';\nimport { NotFound } from 'velox/server';\n\nconst { slug } = params;\nconst post = await db.posts.findBySlug(slug);\n\nif (!post) throw new NotFound();\n\nexport const meta = {\n title: post.title,\n description: post.excerpt,\n};\nexport const config = { render: 'ssr' };\n---\n\n<article>\n <h1>{post.title}</h1>\n <p class=\"byline\">By {post.author} on {post.publishedAt}</p>\n <div innerHTML={post.htmlContent} />\n</article>\n```\n\nThe `render: 'ssr'` export is optional \u2014 SSR is the default for routes that contain `await` expressions.\n\n### Request Context in SSR\n\nIn SSR mode, the `request` object is available in the server block:\n\n```typescript\nconst authHeader = request.headers.get('Authorization');\nconst userAgent = request.headers.get('User-Agent');\nconst cookieHeader = request.headers.get('Cookie');\n```\n\n## Static Site Generation (SSG)\n\nSSG routes are rendered once at build time. They are ideal for content that rarely changes.\n\n```tsx\n---\nexport const config = { render: 'ssg' };\n\nconst features = await fetch('https://api.example.com/features').then(r => r.json());\n---\n\n<section>\n <h1>Features</h1>\n <ul>\n {features.map(f => <li>{f.name}: {f.description}</li>)}\n </ul>\n</section>\n```\n\n### Dynamic SSG Routes\n\nFor SSG routes with dynamic segments, export a `paths` function to tell Velox which parameter values to pre-render:\n\n```tsx\n---\nimport { db } from '$lib/db';\n\nexport const config = { render: 'ssg' };\n\n// Called at build time to enumerate all slugs\nexport async function paths() {\n const slugs = await db.posts.findManySlugs();\n return slugs.map(slug => ({ slug }));\n}\n\nconst { slug } = params;\nconst post = await db.posts.findBySlug(slug);\nexport const meta = { title: post.title };\n---\n\n<article>\n <h1>{post.title}</h1>\n <div innerHTML={post.htmlContent} />\n</article>\n```\n\n## Incremental Static Regeneration (ISR)\n\nISR generates pages statically but revalidates them in the background after a configurable interval:\n\n```tsx\n---\nexport const config = {\n render: 'isr',\n revalidate: 3600, // regenerate at most once per hour\n};\n\nconst products = await db.products.findMany({ orderBy: 'created_at' });\n---\n\n<section>\n {products.map(p => <ProductCard product={p} />)}\n</section>\n```\n\nOn a cache miss (first request, or after `revalidate` seconds), Velox serves the stale page immediately while regenerating the fresh version in the background. The next request gets the fresh version.\n\n### Manual Revalidation\n\nTrigger revalidation programmatically (e.g., from a webhook):\n\n```typescript\n// routes/api/revalidate+server.ts\nimport { revalidatePath, revalidateTag } from 'velox/server';\n\nexport const POST = defineHandler(async (req) => {\n const { path, secret } = await req.json();\n\n if (secret !== process.env.REVALIDATE_SECRET) {\n return Response.json({ error: 'Unauthorized' }, { status: 401 });\n }\n\n await revalidatePath(path);\n return Response.json({ revalidated: true });\n});\n```\n\nTag-based revalidation:\n\n```typescript\n// When fetching, attach cache tags:\nconst data = await fetch('/api/products', {\n next: { tags: ['products'] }\n});\n\n// Later, invalidate all pages that used the 'products' tag:\nawait revalidateTag('products');\n```\n\n## Client-Side Fetching\n\nFor data that must be fresh on the client (user-specific dashboards, real-time data), use client-side fetching in an interactive component:\n\n```tsx\nimport { signal, onMount } from 'velox/client';\n\nexport default function UserDashboard() {\n const stats = signal<DashboardStats | null>(null);\n const error = signal<string | null>(null);\n const loading = signal(true);\n\n onMount(async () => {\n try {\n const res = await fetch('/api/dashboard/stats', {\n credentials: 'include',\n });\n if (!res.ok) throw new Error('Failed to fetch stats');\n stats.value = await res.json();\n } catch (e: any) {\n error.value = e.message;\n } finally {\n loading.value = false;\n }\n });\n\n return (\n <div>\n {loading.value && <Spinner />}\n {error.value && <ErrorMessage message={error.value} />}\n {stats.value && <StatsGrid stats={stats.value} />}\n </div>\n );\n}\n```\n\n### Using `@velox/query`\n\nFor production client-side data fetching, `@velox/query` handles caching, deduplication, background refetch, and stale-while-revalidate:\n\n```typescript\nimport { useQuery } from '@velox/query';\n\nexport default function ProductList() {\n const { data, status, error, refetch } = useQuery({\n key: ['products'],\n fetcher: () => fetch('/api/products').then(r => r.json()),\n staleTime: 30_000,\n refetchOnWindowFocus: true,\n });\n\n if (status === 'loading') " }, { "file": "pages/deploy-cloudflare.md", "title": "Deploy to Cloudflare", "section-id": "deployment", "keywords": "Cloudflare, Pages, Workers, deployment, edge, CDN, Wrangler", "description": "Deploying a Velox application to Cloudflare Pages and Workers", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Deploy to Cloudflare\n\nVelox runs natively on Cloudflare Workers. By combining Cloudflare Pages for static assets and Cloudflare Workers for server-side rendering, you get a globally distributed application with sub-millisecond cold starts.\n\n## Architecture\n\nVelox on Cloudflare uses:\n- **Cloudflare Pages** \u2014 serves static assets from the CDN edge\n- **Cloudflare Workers** \u2014 handles SSR requests at the edge\n- **KV** \u2014 used for ISR page caching\n- **D1** \u2014 SQLite-at-the-edge database (optional)\n- **R2** \u2014 object storage for user uploads (optional)\n\n## Setup\n\nInstall the Cloudflare adapter:\n\n```bash\nnpm install @velox/cloudflare\nnpm install -D wrangler\n```\n\nConfigure the adapter:\n\n```typescript\n// velox.config.ts\nimport { defineConfig } from 'velox';\nimport { cloudflare } from '@velox/cloudflare';\n\nexport default defineConfig({\n adapter: cloudflare({\n kvNamespace: 'VELOX_CACHE', // KV namespace for ISR cache\n routes: {\n exclude: ['/admin/*'], // serve these from Pages CDN only\n },\n }),\n build: {\n target: 'edge',\n },\n});\n```\n\n## Wrangler Configuration\n\nCreate `wrangler.toml`:\n\n```toml\nname = \"my-velox-app\"\ncompatibility_date = \"2026-01-01\"\ncompatibility_flags = [\"nodejs_compat\"]\npages_build_output_dir = \".velox/output\"\n\n[[kv_namespaces]]\nbinding = \"VELOX_CACHE\"\nid = \"your-kv-namespace-id\"\n\n[[d1_databases]]\nbinding = \"DB\"\ndatabase_name = \"my-database\"\ndatabase_id = \"your-database-id\"\n\n[vars]\nNODE_ENV = \"production\"\n```\n\n## Deployment Steps\n\n### Option 1: Cloudflare Pages Dashboard\n\n1. Go to [dash.cloudflare.com](https://dash.cloudflare.com) \u2192 **Pages** \u2192 **Create a project**\n2. Connect your Git provider and select your repository\n3. Set build settings:\n - **Framework preset:** Velox\n - **Build command:** `npm run build`\n - **Build output directory:** `.velox/output`\n4. Add environment variables\n5. Click **Save and Deploy**\n\n### Option 2: Wrangler CLI\n\n```bash\n# Log in\nnpx wrangler login\n\n# Build\nnpm run build\n\n# Deploy\nnpx wrangler pages deploy .velox/output --project-name my-velox-app\n\n# Or deploy as a Worker\nnpx wrangler deploy\n```\n\n## Using Cloudflare D1 (SQLite at the Edge)\n\nCloudflare D1 is a serverless SQLite database that runs at the edge alongside your Workers.\n\n### Create a Database\n\n```bash\nnpx wrangler d1 create my-database\nnpx wrangler d1 execute my-database --file=./migrations/001_init.sql\n```\n\n### Query D1 in Velox\n\n```typescript\n// routes/api/posts+server.ts\nimport { defineHandler } from 'velox/server';\n\nexport const GET = defineHandler(async (req) => {\n // env.DB is automatically injected by the Cloudflare adapter\n const { DB } = process.env as any;\n const { results } = await DB.prepare('SELECT * FROM posts WHERE published = 1').all();\n return Response.json(results);\n});\n```\n\nOr with Drizzle's D1 driver:\n\n```typescript\nimport { drizzle } from 'drizzle-orm/d1';\nimport * as schema from '$lib/schema';\n\nexport function getDB(env: Env) {\n return drizzle(env.DB, { schema });\n}\n```\n\n## Using Cloudflare KV\n\nFor key-value storage (feature flags, cached responses):\n\n```typescript\nconst value = await env.MY_KV.get('my-key');\nawait env.MY_KV.put('my-key', JSON.stringify(data), { expirationTtl: 3600 });\nawait env.MY_KV.delete('my-key');\n```\n\n## Using Cloudflare R2\n\nFor file uploads and object storage:\n\n```typescript\n// routes/api/upload+server.ts\nimport { defineHandler } from 'velox/server';\n\nexport const POST = defineHandler(async (req) => {\n const formData = await req.formData();\n const file = formData.get('file') as File;\n\n const key = `uploads/${Date.now()}-${file.name}`;\n await env.R2_BUCKET.put(key, file.stream(), {\n httpMetadata: { contentType: file.type },\n });\n\n return Response.json({ url: `https://assets.example.com/${key}` });\n});\n```\n\n## Custom Domains\n\n1. Add your domain in Cloudflare's DNS settings (it should already be proxied through Cloudflare)\n2. Go to Pages \u2192 Your project \u2192 **Custom domains**\n3. Click **Set up a custom domain** and enter your domain\n4. Cloudflare provisions SSL automatically\n\nFor Workers, add a route in `wrangler.toml`:\n\n```toml\n[[routes]]\npattern = \"example.com/*\"\nzone_name = \"example.com\"\n```\n\n## Environment Variables\n\nSet secrets securely:\n\n```bash\nnpx wrangler secret put DATABASE_URL\nnpx wrangler secret put SESSION_SECRET\nnpx wrangler secret put JWT_SECRET\n```\n\nNon-secret variables go in `wrangler.toml` under `[vars]` or in the Pages dashboard.\n\n## Performance Tips\n\n- Use **Cache Rules** in the Cloudflare dashboard to cache SSR pages at the CDN layer\n- Enable **Argo Smart Routing** for improved global routing\n- Use **Tiered Cache** to reduce origin requests\n- Set `Cache-Control: public, max-age=0, s-maxage=3600` on ISR pages to let Cloudflare cache them\n\n## Limits\n\n| Feature | Limit |\n|---------|-------|\n| Worker request timeout | 30s (CPU time) |\n| Worker memory | 128 MB |\n| KV value size | 25 MB |\n| D1 database size | 2 GB |\n| R2 object size | 5 GB |" }, { "file": "pages/deploy-docker.md", "title": "Docker", "section-id": "deployment", "keywords": "Docker, Dockerfile, docker-compose, container, containerisation, deployment", "description": "Containerising a Velox application with Docker and docker-compose", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Docker\n\nContainerising your Velox application with Docker makes it portable across any infrastructure \u2014 from a simple VPS to a Kubernetes cluster. This guide covers building optimised Docker images and running multi-service stacks with docker-compose.\n\n## Dockerfile\n\nA production-grade multi-stage Dockerfile:\n\n```dockerfile\n# Stage 1: Install dependencies\nFROM node:22-alpine AS deps\nWORKDIR /app\n\n# Copy package manifests only \u2014 enables Docker layer caching\nCOPY package.json package-lock.json ./\nRUN npm ci --frozen-lockfile\n\n# Stage 2: Build the application\nFROM node:22-alpine AS builder\nWORKDIR /app\n\nCOPY --from=deps /app/node_modules ./node_modules\nCOPY . .\n\n# Build args \u2014 pass at build time\nARG PUBLIC_BASE_URL\nARG NODE_ENV=production\nENV PUBLIC_BASE_URL=$PUBLIC_BASE_URL\nENV NODE_ENV=$NODE_ENV\n\nRUN npm run build\n\n# Stage 3: Production runtime\nFROM node:22-alpine AS runner\nWORKDIR /app\n\nENV NODE_ENV=production\nENV PORT=3700\n\n# Create non-root user for security\nRUN addgroup --system --gid 1001 veloxgroup && \\\n adduser --system --uid 1001 veloxuser\n\n# Copy only production artefacts\nCOPY --from=builder --chown=veloxuser:veloxgroup /app/.velox/output ./\nCOPY --from=builder --chown=veloxuser:veloxgroup /app/package.json ./\n\n# Install production dependencies only\nRUN npm ci --omit=dev --frozen-lockfile\n\nUSER veloxuser\n\nEXPOSE 3700\n\nCMD [\"node\", \"server.js\"]\n```\n\n## Building and Running\n\n```bash\n# Build the image\ndocker build \\\n --build-arg PUBLIC_BASE_URL=https://example.com \\\n -t my-velox-app:latest .\n\n# Run the container\ndocker run \\\n --env-file .env.production \\\n -p 3700:3700 \\\n --name velox-app \\\n my-velox-app:latest\n\n# Run in background\ndocker run -d \\\n --env-file .env.production \\\n -p 3700:3700 \\\n --restart unless-stopped \\\n --name velox-app \\\n my-velox-app:latest\n```\n\n## `.dockerignore`\n\nExclude files from the build context to speed up builds:\n\n```\nnode_modules\n.velox\n.git\n.gitignore\n*.md\n.env\n.env.*\n!.env.example\ntests\n*.test.*\n*.spec.*\ncoverage\n```\n\n## docker-compose\n\nA complete stack with the app, PostgreSQL, and Redis:\n\n```yaml\n# docker-compose.yml\nversion: '3.9'\n\nservices:\n app:\n build:\n context: .\n args:\n PUBLIC_BASE_URL: ${PUBLIC_BASE_URL:-http://localhost:3700}\n image: my-velox-app:latest\n ports:\n - \"3700:3700\"\n environment:\n NODE_ENV: production\n DATABASE_URL: postgresql://velox:${DB_PASSWORD}@db:5432/velox\n REDIS_URL: redis://redis:6379\n SESSION_SECRET: ${SESSION_SECRET}\n JWT_SECRET: ${JWT_SECRET}\n depends_on:\n db:\n condition: service_healthy\n redis:\n condition: service_healthy\n restart: unless-stopped\n healthcheck:\n test: [\"CMD\", \"wget\", \"-qO-\", \"http://localhost:3700/api/health\"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 40s\n\n db:\n image: postgres:16-alpine\n environment:\n POSTGRES_USER: velox\n POSTGRES_PASSWORD: ${DB_PASSWORD}\n POSTGRES_DB: velox\n volumes:\n - postgres_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U velox\"]\n interval: 10s\n timeout: 5s\n retries: 5\n restart: unless-stopped\n\n redis:\n image: redis:7-alpine\n command: redis-server --requirepass ${REDIS_PASSWORD} --appendonly yes\n volumes:\n - redis_data:/data\n healthcheck:\n test: [\"CMD\", \"redis-cli\", \"--no-auth-warning\", \"-a\", \"${REDIS_PASSWORD}\", \"ping\"]\n interval: 10s\n timeout: 5s\n retries: 5\n restart: unless-stopped\n\n nginx:\n image: nginx:alpine\n ports:\n - \"80:80\"\n - \"443:443\"\n volumes:\n - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro\n - ./nginx/ssl:/etc/nginx/ssl:ro\n - nginx_cache:/var/cache/nginx\n depends_on:\n - app\n restart: unless-stopped\n\nvolumes:\n postgres_data:\n redis_data:\n nginx_cache:\n```\n\n## Nginx Reverse Proxy\n\n```nginx\n# nginx/nginx.conf\nevents { worker_connections 1024; }\n\nhttp {\n upstream velox_app {\n server app:3700;\n keepalive 64;\n }\n\n # Redirect HTTP \u2192 HTTPS\n server {\n listen 80;\n server_name example.com www.example.com;\n return 301 https://$host$request_uri;\n }\n\n server {\n listen 443 ssl http2;\n server_name example.com www.example.com;\n\n ssl_certificate /etc/nginx/ssl/fullchain.pem;\n ssl_certificate_key /etc/nginx/ssl/privkey.pem;\n ssl_protocols TLSv1.2 TLSv1.3;\n ssl_ciphers HIGH:!aNULL:!MD5;\n\n # Static assets with long cache\n location /assets/ {\n proxy_pass http://velox_app;\n add_header Cache-Control \"public, max-age=31536000, immutable\";\n }\n\n # Application\n location / {\n proxy_pass http://velox_app;\n proxy_http_version 1.1;\n proxy_set_header Upgrade $http_upgrade;\n proxy_set_header Connection 'upgrade';\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $sch" }, { "file": "pages/deploy-self-hosted.md", "title": "Self-Hosted", "section-id": "deployment", "keywords": "self-hosted, VPS, nginx, PM2, systemd, Linux, server deployment", "description": "Running Velox on a VPS with nginx as a reverse proxy, managed by PM2 or systemd", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Self-Hosted\n\nDeploying Velox to your own server (a VPS, dedicated server, or on-premises machine) gives you full control over your infrastructure. This guide covers setting up a Linux server with nginx as a reverse proxy, and managing the Node.js process with either PM2 or systemd.\n\n## Server Preparation\n\nThis guide assumes Ubuntu 24.04 LTS. Adjust package manager commands for other distributions.\n\n```bash\n# Update the system\nsudo apt update && sudo apt upgrade -y\n\n# Install Node.js (LTS) via nvm\ncurl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash\nsource ~/.bashrc\nnvm install 22\nnvm use 22\nnvm alias default 22\n\n# Install nginx\nsudo apt install -y nginx\n\n# Install PostgreSQL (if needed)\nsudo apt install -y postgresql postgresql-contrib\n\n# Install Redis (if needed)\nsudo apt install -y redis-server\n```\n\n## Deploying the Application\n\n### Option A: Direct File Copy\n\n```bash\n# On your local machine \u2014 build the app\nnpm run build\n\n# Copy build output to the server\nrsync -avz --delete .velox/output/ user@your-server.com:/var/www/my-velox-app/\nrsync -avz package.json package-lock.json user@your-server.com:/var/www/my-velox-app/\n\n# On the server \u2014 install production dependencies\ncd /var/www/my-velox-app\nnpm ci --omit=dev\n```\n\n### Option B: Git + CI/CD\n\nCreate a deploy script on the server:\n\n```bash\n#!/bin/bash\n# /home/deploy/deploy.sh\nset -e\n\nAPP_DIR=/var/www/my-velox-app\nREPO=https://github.com/your-org/your-repo.git\n\ncd $APP_DIR\n\n# Pull latest code\ngit fetch --all\ngit reset --hard origin/main\n\n# Install dependencies\nnpm ci --frozen-lockfile --omit=dev\n\n# Build\nnpm run build\n\n# Restart application\npm2 reload my-velox-app --update-env\n\necho \"Deployment complete.\"\n```\n\nCall this from your GitHub Actions workflow:\n\n```yaml\n# .github/workflows/deploy.yml\njobs:\n deploy:\n runs-on: ubuntu-latest\n steps:\n - name: Deploy to server\n uses: appleboy/ssh-action@v1\n with:\n host: ${{ secrets.SERVER_HOST }}\n username: deploy\n key: ${{ secrets.SERVER_SSH_KEY }}\n script: /home/deploy/deploy.sh\n```\n\n## Environment Variables\n\nStore production secrets in `/etc/environment` or a `.env.production` file:\n\n```bash\n# /etc/environment (system-wide)\nNODE_ENV=production\nDATABASE_URL=postgresql://velox:secretpass@localhost:5432/velox\nSESSION_SECRET=your-long-random-secret-here\nJWT_SECRET=another-long-random-secret\n\n# Or in a project-specific .env.production\nsudo nano /var/www/my-velox-app/.env.production\n```\n\nLoad the env file in your start command (covered below).\n\n## Process Management with PM2\n\nPM2 is a battle-tested process manager for Node.js applications.\n\n```bash\nnpm install -g pm2\n```\n\nCreate a PM2 ecosystem file:\n\n```javascript\n// /var/www/my-velox-app/ecosystem.config.js\nmodule.exports = {\n apps: [{\n name: 'my-velox-app',\n script: 'server.js',\n cwd: '/var/www/my-velox-app',\n instances: 'max', // one process per CPU core\n exec_mode: 'cluster', // enable cluster mode for zero-downtime restarts\n env_file: '/var/www/my-velox-app/.env.production',\n env: {\n NODE_ENV: 'production',\n PORT: 3700,\n },\n error_file: '/var/log/pm2/my-velox-app-error.log',\n out_file: '/var/log/pm2/my-velox-app-out.log',\n log_date_format: 'YYYY-MM-DD HH:mm:ss',\n max_memory_restart: '500M', // restart if memory exceeds 500 MB\n }],\n};\n```\n\nStart and persist:\n\n```bash\npm2 start ecosystem.config.js\npm2 save # persist process list across reboots\npm2 startup # install systemd startup script\n\n# Useful commands\npm2 status\npm2 logs my-velox-app\npm2 reload my-velox-app # zero-downtime reload\npm2 restart my-velox-app # full restart\npm2 monit # real-time monitoring\n```\n\n## systemd Service (Alternative to PM2)\n\nFor simpler setups, a systemd unit file:\n\n```ini\n# /etc/systemd/system/my-velox-app.service\n[Unit]\nDescription=My Velox Application\nAfter=network.target postgresql.service redis.service\n\n[Service]\nType=simple\nUser=www-data\nGroup=www-data\nWorkingDirectory=/var/www/my-velox-app\nEnvironmentFile=/var/www/my-velox-app/.env.production\nExecStart=/usr/bin/node server.js\nRestart=always\nRestartSec=5\nStandardOutput=journal\nStandardError=journal\nSyslogIdentifier=my-velox-app\n\n# Resource limits\nLimitNOFILE=65536\nMemoryMax=512M\n\n[Install]\nWantedBy=multi-user.target\n```\n\n```bash\nsudo systemctl daemon-reload\nsudo systemctl enable my-velox-app\nsudo systemctl start my-velox-app\nsudo journalctl -u my-velox-app -f # follow logs\n```\n\n## Nginx Configuration\n\n```nginx\n# /etc/nginx/sites-available/my-velox-app\nupstream velox {\n server 127.0.0.1:3700;\n keepalive 32;\n}\n\nserver {\n listen 80;\n server_name example.com www.example.com;\n return 301 https://$host$request_uri;\n}\n\nserver {\n listen 443 ssl http2;\n server_name example.com www.example.com;\n\n ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;\n ssl_certificate_key /etc/letsencrypt/live/example.com/privke" }, { "file": "pages/deploy-vercel.md", "title": "Deploy to Vercel", "section-id": "deployment", "keywords": "Vercel, deployment, serverless, edge, CI/CD, deploy", "description": "Step-by-step guide to deploying a Velox application to Vercel", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Deploy to Vercel\n\nVercel is the recommended hosting platform for Velox applications. The Velox Vercel adapter handles all configuration automatically \u2014 no manual `vercel.json` setup is required for most projects.\n\n## Prerequisites\n\n- A Vercel account ([vercel.com](https://vercel.com))\n- The Vercel CLI: `npm install -g vercel`\n- A Velox project committed to a Git repository (GitHub, GitLab, or Bitbucket)\n\n## Automatic Deployment via Git Integration\n\n### Step 1 \u2014 Push Your Project to Git\n\n```bash\ngit init\ngit add .\ngit commit -m \"Initial commit\"\ngit remote add origin https://github.com/your-username/your-project.git\ngit push -u origin main\n```\n\n### Step 2 \u2014 Import the Project on Vercel\n\n1. Go to [vercel.com/new](https://vercel.com/new)\n2. Click **Add New Project** \u2192 **Import Git Repository**\n3. Select your repository\n4. Vercel automatically detects Velox and sets the correct build settings:\n - **Framework Preset:** Velox\n - **Build Command:** `velox build`\n - **Output Directory:** `.velox/output`\n - **Install Command:** `npm ci`\n\n### Step 3 \u2014 Configure Environment Variables\n\nIn the Vercel project dashboard:\n1. Go to **Settings** \u2192 **Environment Variables**\n2. Add each variable from your `.env.production` file\n\nDo **not** add `PUBLIC_BASE_URL` manually \u2014 Vercel sets `VERCEL_URL` automatically and the Velox Vercel adapter uses it.\n\n### Step 4 \u2014 Deploy\n\nClick **Deploy**. Vercel will:\n1. Clone your repository\n2. Install dependencies\n3. Run `velox build`\n4. Deploy to its global edge network\n\nYour site is live at `https://your-project.vercel.app`.\n\n## Vercel CLI Deployment\n\nFor manual or scripted deployments:\n\n```bash\n# Install adapter\nnpm install @velox/vercel\n\n# Deploy to preview (equivalent to a branch deploy)\nvercel\n\n# Deploy to production\nvercel --prod\n```\n\n## Configuring the Vercel Adapter\n\nInstall and configure `@velox/vercel`:\n\n```bash\nnpm install @velox/vercel\n```\n\n```typescript\n// velox.config.ts\nimport { defineConfig } from 'velox';\nimport { vercel } from '@velox/vercel';\n\nexport default defineConfig({\n adapter: vercel({\n // Route-level edge function configuration\n edgeRoutes: ['/api/stream', '/api/realtime'],\n\n // Override ISR revalidation for specific routes\n isr: {\n expiration: 60, // default revalidation (seconds)\n bypassToken: process.env.VERCEL_ISR_BYPASS_TOKEN,\n },\n\n // Enable Vercel Image Optimisation (uses Vercel's CDN)\n images: {\n sizes: [640, 1080, 1920],\n },\n }),\n});\n```\n\n## Edge Functions\n\nMark individual routes to run on Vercel Edge instead of Node.js serverless:\n\n```typescript\n// routes/api/stream+server.ts\nexport const config = { edge: true };\n\nexport const GET = defineHandler(async (req) => {\n // Runs on Vercel Edge \u2014 uses Web APIs only\n return new Response('Hello from edge!');\n});\n```\n\nEdge functions are deployed globally in ~70 Vercel regions and have ~0ms cold start. Use them for latency-sensitive, stateless handlers.\n\n## Custom Domains\n\n1. Go to your project on Vercel \u2192 **Settings** \u2192 **Domains**\n2. Click **Add Domain**\n3. Enter your domain (e.g., `example.com`)\n4. Follow the DNS configuration instructions:\n - Add a **CNAME** record: `www` \u2192 `cname.vercel-dns.com`\n - Add an **A** record: `@` \u2192 `76.76.21.21`\n5. SSL certificates are provisioned automatically via Let's Encrypt\n\nSet the canonical base URL environment variable in Vercel:\n\n```\nPUBLIC_BASE_URL = https://example.com\n```\n\n## Preview Deployments\n\nEvery pull request gets an automatic preview deployment at a unique URL (`https://your-project-git-branch-name.vercel.app`). This is configured by default and requires no additional setup.\n\nTo share preview deployments with your team, enable **Password Protection** under **Settings** \u2192 **Deployment Protection**.\n\n## Build Cache\n\nVelox's Velocitor build cache is automatically persisted across deployments by the Vercel adapter. Subsequent deployments typically build 3\u20135\u00d7 faster than the first.\n\n## `vercel.json` Reference\n\nFor advanced configuration, create a `vercel.json` at your project root:\n\n```json\n{\n \"regions\": [\"iad1\", \"fra1\"],\n \"cleanUrls\": true,\n \"trailingSlash\": false,\n \"headers\": [\n {\n \"source\": \"/assets/(.*)\",\n \"headers\": [\n { \"key\": \"Cache-Control\", \"value\": \"public, max-age=31536000, immutable\" }\n ]\n }\n ],\n \"redirects\": [\n { \"source\": \"/old-path\", \"destination\": \"/new-path\", \"permanent\": true }\n ]\n}\n```\n\n## Monitoring and Analytics\n\nEnable Vercel Analytics and Speed Insights in your project dashboard. The Velox adapter hooks into these automatically \u2014 no code changes needed.\n\nFor custom event tracking:\n\n```typescript\nimport { track } from '@vercel/analytics';\n\ntrack('button_clicked', { component: 'Hero', variant: 'primary' });\n```" }, { "file": "pages/guide-auth.md", "title": "Authentication", "section-id": "guides", "keywords": "authentication, JWT, OAuth2, session, login, auth, security", "description": "Implementing authentication in Velox using JWT, OAuth2, and session-based strategies", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Authentication\n\nThis guide covers the three most common authentication patterns for Velox applications: session-based auth, JWT-based auth, and OAuth2 with third-party providers.\n\n## Session-Based Authentication\n\nSession auth stores user state server-side. The client holds only a signed session cookie. This is the simplest and most secure approach for most web applications.\n\n### Setup\n\nInstall the session plugin:\n\n```bash\nnpm install @velox/session @velox/session-redis\n```\n\nConfigure in `velox.config.ts`:\n\n```typescript\nimport { RedisSessionStore } from '@velox/session-redis';\n\nexport default defineConfig({\n session: {\n secret: process.env.SESSION_SECRET!,\n cookieName: 'app.session',\n maxAge: 60 * 60 * 24 * 7, // 7 days\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n store: new RedisSessionStore({ url: process.env.REDIS_URL! }),\n },\n});\n```\n\n### Login Handler\n\n```typescript\n// routes/api/auth/login+server.ts\nimport { defineHandler, json, redirect } from 'velox/server';\nimport { useSession } from 'velox/server';\nimport { db } from '$lib/db';\nimport { verifyPassword } from '$lib/crypto';\n\nexport const POST = defineHandler(async (req) => {\n const { email, password } = await req.json();\n\n if (!email || !password) {\n return json({ error: 'Email and password are required' }, { status: 400 });\n }\n\n const user = await db.users.findByEmail(email);\n if (!user || !(await verifyPassword(password, user.passwordHash))) {\n return json({ error: 'Invalid credentials' }, { status: 401 });\n }\n\n const session = await useSession();\n await session.regenerate(); // prevent session fixation\n await session.update({ userId: user.id, role: user.role });\n\n return json({ ok: true, redirectTo: '/dashboard' });\n});\n```\n\n### Auth Middleware\n\n```typescript\n// middleware/auth.ts\nimport { defineMiddleware, redirect } from 'velox/server';\nimport { useSession } from 'velox/server';\n\nconst PUBLIC_PATHS = ['/login', '/register', '/api/auth'];\n\nexport default defineMiddleware(async ({ request, next }) => {\n const pathname = new URL(request.url).pathname;\n\n if (PUBLIC_PATHS.some(p => pathname.startsWith(p))) {\n return next();\n }\n\n const session = await useSession<{ userId: string }>();\n\n if (!session.data.userId) {\n return redirect(`/login?next=${encodeURIComponent(pathname)}`);\n }\n\n request.context.set('userId', session.data.userId);\n return next();\n});\n```\n\n## JWT Authentication\n\nJWT is stateless and ideal for API-first applications or mobile/SPA backends.\n\n### Generating Tokens\n\n```typescript\n// lib/jwt.ts\nimport { SignJWT, jwtVerify } from 'jose';\n\nconst secret = new TextEncoder().encode(process.env.JWT_SECRET!);\n\nexport interface JWTPayload {\n sub: string; // user ID\n role: string;\n iat: number;\n exp: number;\n}\n\nexport async function signToken(userId: string, role: string): Promise<string> {\n return new SignJWT({ sub: userId, role })\n .setProtectedHeader({ alg: 'HS256' })\n .setIssuedAt()\n .setExpirationTime('15m')\n .sign(secret);\n}\n\nexport async function signRefreshToken(userId: string): Promise<string> {\n return new SignJWT({ sub: userId, type: 'refresh' })\n .setProtectedHeader({ alg: 'HS256' })\n .setIssuedAt()\n .setExpirationTime('30d')\n .sign(secret);\n}\n\nexport async function verifyToken(token: string): Promise<JWTPayload> {\n const { payload } = await jwtVerify(token, secret);\n return payload as JWTPayload;\n}\n```\n\n### Login + Refresh Endpoints\n\n```typescript\n// routes/api/auth/token+server.ts\nimport { defineHandler, json, badRequest, unauthorized } from 'velox/server';\nimport { signToken, signRefreshToken, verifyToken } from '$lib/jwt';\nimport { db } from '$lib/db';\nimport { verifyPassword } from '$lib/crypto';\n\nexport const POST = defineHandler(async (req) => {\n const { grant_type, email, password, refresh_token } = await req.json();\n\n if (grant_type === 'password') {\n const user = await db.users.findByEmail(email);\n if (!user || !(await verifyPassword(password, user.passwordHash))) {\n return unauthorized('Invalid credentials');\n }\n return json({\n access_token: await signToken(user.id, user.role),\n refresh_token: await signRefreshToken(user.id),\n token_type: 'Bearer',\n expires_in: 900,\n });\n }\n\n if (grant_type === 'refresh_token') {\n const payload = await verifyToken(refresh_token).catch(() => null);\n if (!payload || payload.type !== 'refresh') {\n return unauthorized('Invalid refresh token');\n }\n const user = await db.users.findById(payload.sub);\n return json({\n access_token: await signToken(user.id, user.role),\n token_type: 'Bearer',\n expires_in: 900,\n });\n }\n\n return badRequest('Unsupported grant_type');\n});\n```\n\n## OAuth2 with Third-Party Providers\n\n### Using `@velox/auth`\n\n```bash\nnpm install @velox/auth\n```\n\nConfigure providers:\n\n```typescript\n// lib/auth.ts\nimport { createAuth } from '@velox/auth';\n\nexport const auth = create" }, { "file": "pages/guide-database.md", "title": "Database Integration", "section-id": "guides", "keywords": "database, Prisma, DrizzleORM, SQL, ORM, PostgreSQL, database integration", "description": "How to integrate databases in Velox using Prisma, DrizzleORM, or raw SQL", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Database Integration\n\nVelox is database-agnostic. This guide covers the three most popular approaches: Prisma (full-featured ORM), DrizzleORM (lightweight TypeScript-first ORM), and raw SQL with a typed query builder.\n\n## Prisma\n\nPrisma is the most popular ORM in the Node.js ecosystem. It provides a schema-first approach, auto-generated type-safe client, and powerful migrations.\n\n### Setup\n\n```bash\nnpm install prisma @prisma/client\nnpx prisma init\n```\n\nThis creates a `prisma/schema.prisma` file. Example schema:\n\n```prisma\n// prisma/schema.prisma\ngenerator client {\n provider = \"prisma-client-js\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"DATABASE_URL\")\n}\n\nmodel User {\n id String @id @default(cuid())\n email String @unique\n name String?\n role Role @default(USER)\n posts Post[]\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Post {\n id String @id @default(cuid())\n title String\n slug String @unique\n content String\n published Boolean @default(false)\n author User @relation(fields: [authorId], references: [id])\n authorId String\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nenum Role {\n USER\n ADMIN\n ANALYST\n}\n```\n\nGenerate and run the initial migration:\n\n```bash\nnpx prisma migrate dev --name init\nnpx prisma generate\n```\n\n### Database Client Singleton\n\nIn a server environment, always reuse a single Prisma client instance:\n\n```typescript\n// lib/db.ts\nimport { PrismaClient } from '@prisma/client';\n\nconst globalForPrisma = globalThis as unknown as { prisma: PrismaClient };\n\nexport const db = globalForPrisma.prisma ?? new PrismaClient({\n log: process.env.NODE_ENV === 'development' ? ['query', 'warn', 'error'] : ['error'],\n});\n\nif (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db;\n```\n\n### Usage in Routes\n\n```typescript\n// routes/blog/[slug].velox server block\nimport { db } from '$lib/db';\n\nconst post = await db.post.findUnique({\n where: { slug: params.slug, published: true },\n include: { author: { select: { name: true } } },\n});\n\nif (!post) throw notFound();\n```\n\n### Transactions\n\n```typescript\nconst result = await db.$transaction(async (tx) => {\n const user = await tx.user.create({ data: { email, name } });\n const profile = await tx.profile.create({ data: { userId: user.id } });\n return { user, profile };\n});\n```\n\n## DrizzleORM\n\nDrizzleORM is a TypeScript-first ORM with a SQL-like query API and zero overhead.\n\n### Setup\n\n```bash\nnpm install drizzle-orm postgres\nnpm install -D drizzle-kit\n```\n\nDefine your schema in TypeScript:\n\n```typescript\n// lib/schema.ts\nimport { pgTable, text, boolean, timestamp, pgEnum } from 'drizzle-orm/pg-core';\n\nexport const roleEnum = pgEnum('role', ['user', 'admin', 'analyst']);\n\nexport const users = pgTable('users', {\n id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),\n email: text('email').notNull().unique(),\n name: text('name'),\n role: roleEnum('role').default('user').notNull(),\n createdAt: timestamp('created_at').defaultNow().notNull(),\n updatedAt: timestamp('updated_at').defaultNow().notNull(),\n});\n\nexport const posts = pgTable('posts', {\n id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),\n title: text('title').notNull(),\n slug: text('slug').notNull().unique(),\n content: text('content').notNull(),\n published: boolean('published').default(false).notNull(),\n authorId: text('author_id').notNull().references(() => users.id),\n createdAt: timestamp('created_at').defaultNow().notNull(),\n});\n```\n\n### Database Client\n\n```typescript\n// lib/db.ts\nimport { drizzle } from 'drizzle-orm/postgres-js';\nimport postgres from 'postgres';\nimport * as schema from './schema';\n\nconst client = postgres(process.env.DATABASE_URL!);\nexport const db = drizzle(client, { schema });\n```\n\n### Querying\n\n```typescript\nimport { db } from '$lib/db';\nimport { posts, users } from '$lib/schema';\nimport { eq, and, desc } from 'drizzle-orm';\n\n// Find one\nconst post = await db.query.posts.findFirst({\n where: and(eq(posts.slug, slug), eq(posts.published, true)),\n with: { author: { columns: { name: true } } },\n});\n\n// Find many with ordering\nconst recentPosts = await db\n .select()\n .from(posts)\n .where(eq(posts.published, true))\n .orderBy(desc(posts.createdAt))\n .limit(10);\n\n// Insert\nconst [newPost] = await db\n .insert(posts)\n .values({ title, slug, content, authorId })\n .returning();\n\n// Update\nawait db\n .update(posts)\n .set({ published: true, updatedAt: new Date() })\n .where(eq(posts.id, postId));\n\n// Delete\nawait db.delete(posts).where(eq(posts.id, postId));\n```\n\n### Migrations\n\nConfigure Drizzle Kit:\n\n```typescript\n// drizzle.config.ts\nimport { defineConfig } from 'drizzle-kit';\n\nexport default defineConfig({\n schema: './lib/schema.ts',\n out: './drizzle',\n dialect: 'postgresql',\n dbCredentials: { url: process.env.DATABASE_URL! },\n});\n```\n\n```bash\nnpx drizzle-kit generate" }, { "file": "pages/guide-i18n.md", "title": "Internationalisation", "section-id": "guides", "keywords": "i18n, internationalisation, localisation, translation, locale routing, multilingual", "description": "Setting up internationalisation in Velox \u2014 translation files, locale routing, and pluralisation", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Internationalisation\n\nVelox has built-in internationalisation (i18n) support through `@velox/i18n`. It handles locale detection, URL-based locale routing, typed translation files, and pluralisation.\n\n## Setup\n\n```bash\nnpm install @velox/i18n\n```\n\nConfigure in `velox.config.ts`:\n\n```typescript\nimport { defineConfig } from 'velox';\n\nexport default defineConfig({\n i18n: {\n locales: ['en', 'fr', 'de', 'ja'],\n defaultLocale: 'en',\n routing: 'prefix-except-default',\n // 'prefix': all locales get a prefix (/en, /fr, /de, /ja)\n // 'prefix-except-default': default locale has no prefix\n // 'domain': different domains per locale\n messagesDir: 'messages',\n },\n});\n```\n\n## Translation Files\n\nCreate a `messages/` directory at your project root:\n\n```\nmessages/\n\u251c\u2500\u2500 en.json\n\u251c\u2500\u2500 fr.json\n\u251c\u2500\u2500 de.json\n\u2514\u2500\u2500 ja.json\n```\n\nTranslation files use a flat or nested key structure:\n\n```json\n// messages/en.json\n{\n \"nav.home\": \"Home\",\n \"nav.blog\": \"Blog\",\n \"nav.about\": \"About\",\n \"home.hero.title\": \"Build faster with Velox\",\n \"home.hero.subtitle\": \"The TypeScript framework for the modern web\",\n \"home.cta\": \"Get started\",\n \"post.readMore\": \"Read more\",\n \"post.publishedOn\": \"Published on {date}\",\n \"post.comments\": \"{count, plural, =0{No comments} =1{1 comment} other{# comments}}\",\n \"auth.loginButton\": \"Log in\",\n \"auth.logoutButton\": \"Log out\",\n \"errors.notFound\": \"Page not found\",\n \"errors.serverError\": \"Something went wrong\"\n}\n```\n\n```json\n// messages/fr.json\n{\n \"nav.home\": \"Accueil\",\n \"nav.blog\": \"Blog\",\n \"nav.about\": \"\u00c0 propos\",\n \"home.hero.title\": \"Construisez plus vite avec Velox\",\n \"home.hero.subtitle\": \"Le framework TypeScript pour le web moderne\",\n \"home.cta\": \"Commencer\",\n \"post.readMore\": \"Lire la suite\",\n \"post.publishedOn\": \"Publi\u00e9 le {date}\",\n \"post.comments\": \"{count, plural, =0{Aucun commentaire} =1{1 commentaire} other{# commentaires}}\",\n \"auth.loginButton\": \"Se connecter\",\n \"auth.logoutButton\": \"Se d\u00e9connecter\",\n \"errors.notFound\": \"Page introuvable\",\n \"errors.serverError\": \"Une erreur est survenue\"\n}\n```\n\n## Using Translations\n\n### In Server Blocks\n\n```tsx\n---\nimport { useTranslations } from 'velox/i18n';\n\nconst t = useTranslations();\nconst locale = useLocale().locale;\n---\n\n<main>\n <h1>{t('home.hero.title')}</h1>\n <p>{t('home.hero.subtitle')}</p>\n <a href=\"/docs\">{t('home.cta')}</a>\n</main>\n```\n\n### With Parameters\n\n```tsx\n---\nconst t = useTranslations();\nconst publishedDate = new Intl.DateTimeFormat(locale).format(post.createdAt);\n---\n\n<p>{t('post.publishedOn', { date: publishedDate })}</p>\n```\n\n### Pluralisation\n\nVelox uses the ICU message format for pluralisation:\n\n```typescript\nt('post.comments', { count: 0 }); // \"No comments\"\nt('post.comments', { count: 1 }); // \"1 comment\"\nt('post.comments', { count: 42 }); // \"42 comments\"\n```\n\n### In Client Components\n\n```typescript\nimport { useTranslations } from 'velox/i18n/client';\n\nexport default function LikeButton({ postId }: { postId: string }) {\n const t = useTranslations();\n const liked = signal(false);\n\n return (\n <button onClick={() => liked.value = !liked.value}>\n {liked.value ? t('post.liked') : t('post.like')}\n </button>\n );\n}\n```\n\n## Locale Routing\n\nWith `routing: 'prefix-except-default'` and `defaultLocale: 'en'`:\n\n| URL | Locale |\n|-----|--------|\n| `/` | `en` |\n| `/about` | `en` |\n| `/fr` | `fr` |\n| `/fr/about` | `fr` |\n| `/de/blog/my-post` | `de` |\n\nVelox automatically generates alternate hreflang links for SEO.\n\n## Locale Switcher Component\n\n```tsx\nimport { useLocale, usePathname } from 'velox/i18n/client';\n\nexport default function LocaleSwitcher() {\n const { locale, locales } = useLocale();\n const pathname = usePathname();\n\n return (\n <div class=\"locale-switcher\">\n {locales.map(loc => (\n <a\n key={loc}\n href={getLocalizedPath(pathname.value, loc)}\n class={loc === locale ? 'active' : ''}\n hreflang={loc}\n >\n {getLocaleLabel(loc)}\n </a>\n ))}\n </div>\n );\n}\n\nfunction getLocaleLabel(locale: string): string {\n const labels: Record<string, string> = {\n en: 'English',\n fr: 'Fran\u00e7ais',\n de: 'Deutsch',\n ja: '\u65e5\u672c\u8a9e',\n };\n return labels[locale] ?? locale;\n}\n```\n\n## Domain-Based Routing\n\nFor country-specific top-level domains:\n\n```typescript\nexport default defineConfig({\n i18n: {\n locales: ['en', 'fr', 'de'],\n defaultLocale: 'en',\n routing: 'domain',\n domains: {\n en: 'example.com',\n fr: 'example.fr',\n de: 'example.de',\n },\n },\n});\n```\n\n## Type-Safe Translation Keys\n\nGenerate a TypeScript type for your translation keys:\n\n```bash\nnpx velox i18n:generate-types\n```\n\nThis creates `.velox/types/i18n.d.ts` so that `t('invalid.key')` is a compile-time error.\n\n## RTL Languages\n\nFor right-to-left languages (Arabic, Hebrew, etc.), add the `dir` attribute dynamically:\n\n```tsx\n// layouts/default.velox\n---\nconst { locale } = useLocale();\nconst isRTL = ['ar', 'he', 'fa'].includes(locale);\n---" }, { "file": "pages/guide-performance.md", "title": "Performance", "section-id": "guides", "keywords": "performance, code splitting, lazy loading, caching, optimisation, Core Web Vitals", "description": "Performance optimisation strategies for Velox apps \u2014 code splitting, lazy loading, and caching", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Performance\n\n![Performance Dashboard](assets/images/performance.jpg)\n\nVelox is designed to be fast by default. The Rust-based compiler handles tree-shaking, code splitting, and asset optimisation automatically. This guide covers the additional strategies you can apply to push your application to peak performance.\n\n## Core Web Vitals Targets\n\nBefore optimising, establish baselines. Aim for:\n\n| Metric | Good | Needs Work |\n|--------|------|-----------|\n| LCP (Largest Contentful Paint) | \u2264 2.5s | > 4.0s |\n| FID / INP (Interaction to Next Paint) | \u2264 200ms | > 500ms |\n| CLS (Cumulative Layout Shift) | \u2264 0.1 | > 0.25 |\n| TTFB (Time to First Byte) | \u2264 800ms | > 1800ms |\n\nUse Velox's built-in analytics to monitor these in production:\n\n```typescript\n// velox.config.ts\nexport default defineConfig({\n analytics: {\n webVitals: true,\n endpoint: '/api/analytics/vitals',\n },\n});\n```\n\n## Code Splitting\n\nVelox automatically splits your JavaScript bundle by route. Every route gets its own chunk, and shared code is extracted into a common chunk. You do not need to configure this.\n\nFor further control, use dynamic imports:\n\n```typescript\n// Only loads when the user clicks \"Open\"\nconst HeavyEditor = lazy(() => import('./HeavyEditor'));\n\nfunction PostPage() {\n const isEditing = signal(false);\n return (\n <div>\n <h1>{post.title}</h1>\n {isEditing.value && (\n <Suspense fallback={<Skeleton />}>\n <HeavyEditor post={post} />\n </Suspense>\n )}\n <button onClick={() => isEditing.value = true}>Edit</button>\n </div>\n );\n}\n```\n\n## Lazy Hydration\n\nDelay hydration of interactive components until they are needed:\n\n```tsx\n<!-- Hydrate only when the component scrolls into view -->\n<CommentSection client:visible postId={post.id} />\n\n<!-- Hydrate only when the browser is idle -->\n<AnalyticsWidget client:idle />\n\n<!-- Hydrate only when a CSS media query matches -->\n<MobileNav client:media=\"(max-width: 768px)\" />\n\n<!-- Never hydrate \u2014 purely server-rendered, no JS -->\n<StaticSidebar />\n```\n\nThe less JavaScript you ship to the client, the better the INP score.\n\n## Image Optimisation\n\nEnable image optimisation in `velox.config.ts`:\n\n```typescript\nassets: {\n imageOptimisation: {\n enabled: true,\n formats: ['webp', 'avif'],\n quality: 85,\n maxWidth: 2000,\n },\n},\n```\n\nUse the `<Image>` component for automatic width/height and format negotiation:\n\n```tsx\nimport { Image } from 'velox';\n\n<Image\n src=\"/assets/images/hero.jpg\"\n alt=\"Hero image\"\n width={1200}\n height={400}\n priority // preload this image (use for above-the-fold images)\n sizes=\"(max-width: 768px) 100vw, 1200px\"\n/>\n```\n\nThe `priority` prop adds a `<link rel=\"preload\">` tag to the document head and marks the image as `fetchpriority=\"high\"`, which is the single biggest LCP improvement for image-heavy pages.\n\n## HTTP Caching\n\nSet appropriate `Cache-Control` headers for your routes:\n\n```typescript\n// In a route server block\nresponse.headers.set('Cache-Control', 'public, max-age=3600, stale-while-revalidate=86400');\n\n// Or via config for specific paths\nexport default defineConfig({\n headers: [\n {\n source: '/assets/*',\n headers: [\n { key: 'Cache-Control', value: 'public, max-age=31536000, immutable' },\n ],\n },\n {\n source: '/',\n headers: [\n { key: 'Cache-Control', value: 'public, max-age=0, s-maxage=3600, stale-while-revalidate=86400' },\n ],\n },\n ],\n});\n```\n\n## Server-Side Caching\n\nCache expensive computations and database queries:\n\n```typescript\nimport { useCache } from 'velox/server';\n\nconst cache = useCache();\n\nasync function getPopularPosts() {\n const cached = await cache.get<Post[]>('posts:popular');\n if (cached) return cached;\n\n const posts = await db.posts.findMany({\n where: { published: true },\n orderBy: { viewCount: 'desc' },\n take: 10,\n });\n\n await cache.set('posts:popular', posts, { ttl: 300 }); // 5 minutes\n return posts;\n}\n```\n\n## Database Query Optimisation\n\nCommon patterns that prevent N+1 queries:\n\n```typescript\n// Bad: N+1 query\nconst posts = await db.post.findMany();\nconst postsWithAuthors = await Promise.all(\n posts.map(p => db.user.findById(p.authorId)) // N extra queries!\n);\n\n// Good: single query with include\nconst postsWithAuthors = await db.post.findMany({\n include: { author: { select: { id: true, name: true } } },\n});\n```\n\nAdd indexes for commonly filtered columns:\n\n```sql\n-- In a migration\nCREATE INDEX CONCURRENTLY idx_posts_published_created\n ON posts (published, created_at DESC)\n WHERE published = true;\n```\n\n## Edge Deployment\n\nDeploy to edge locations close to your users:\n\n```typescript\n// velox.config.ts\nexport default defineConfig({\n build: {\n target: 'edge',\n },\n});\n```\n\nEdge-compatible routes must use Web APIs only:\n\n```typescript\n// \u2705 Edge-compatible\nimport { defineHandler } from 'velox/server';\nexport const GET = defineHandler(async (req) => {\n const data = await fetch('https://api.example.com/data');\n " }, { "file": "pages/guide-testing.md", "title": "Testing", "section-id": "guides", "keywords": "testing, unit tests, integration tests, E2E, Playwright, Vitest, testing strategy", "description": "Testing Velox applications with unit tests, integration tests, and end-to-end tests using Playwright", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Testing\n\nVelox integrates with Vitest for unit and integration tests, and Playwright for end-to-end tests. The `@velox/test` package provides additional test utilities tailored for Velox's server blocks and API routes.\n\n## Setup\n\n```bash\nnpm install -D vitest @velox/test @playwright/test\nnpx playwright install\n```\n\nAdd test scripts to `package.json`:\n\n```json\n{\n \"scripts\": {\n \"test\": \"vitest\",\n \"test:ui\": \"vitest --ui\",\n \"test:e2e\": \"playwright test\",\n \"test:coverage\": \"vitest --coverage\"\n }\n}\n```\n\nConfigure Vitest:\n\n```typescript\n// vitest.config.ts\nimport { defineConfig } from 'vitest/config';\nimport { veloxTestPlugin } from '@velox/test';\n\nexport default defineConfig({\n plugins: [veloxTestPlugin()],\n test: {\n environment: 'node',\n globals: true,\n setupFiles: ['./tests/setup.ts'],\n coverage: {\n provider: 'v8',\n reporter: ['text', 'lcov'],\n },\n },\n});\n```\n\n## Unit Tests\n\n### Testing Utility Functions\n\n```typescript\n// lib/formatters.ts\nexport function formatCurrency(amount: number, currency = 'USD'): string {\n return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount);\n}\n```\n\n```typescript\n// tests/unit/formatters.test.ts\nimport { describe, it, expect } from 'vitest';\nimport { formatCurrency } from '$lib/formatters';\n\ndescribe('formatCurrency', () => {\n it('formats USD amounts', () => {\n expect(formatCurrency(1000)).toBe('$1,000.00');\n expect(formatCurrency(9.99)).toBe('$9.99');\n });\n\n it('formats other currencies', () => {\n expect(formatCurrency(1000, 'EUR')).toBe('\u20ac1,000.00');\n });\n\n it('handles zero', () => {\n expect(formatCurrency(0)).toBe('$0.00');\n });\n});\n```\n\n### Testing Components\n\n```tsx\n// tests/unit/Counter.test.tsx\nimport { describe, it, expect } from 'vitest';\nimport { render, fireEvent } from '@velox/test';\nimport Counter from '$components/Counter';\n\ndescribe('Counter', () => {\n it('renders with initial value', () => {\n const { getByText } = render(<Counter initialValue={5} />);\n expect(getByText('5')).toBeTruthy();\n });\n\n it('increments on button click', async () => {\n const { getByText, getByRole } = render(<Counter initialValue={0} />);\n await fireEvent.click(getByRole('button', { name: /increment/i }));\n expect(getByText('1')).toBeTruthy();\n });\n\n it('decrements below initial value', async () => {\n const { getByText, getByRole } = render(<Counter initialValue={3} />);\n await fireEvent.click(getByRole('button', { name: /decrement/i }));\n expect(getByText('2')).toBeTruthy();\n });\n});\n```\n\n## Integration Tests\n\n### Testing API Routes\n\nThe `createTestServer` utility starts a real Velox server for integration testing:\n\n```typescript\n// tests/integration/api/users.test.ts\nimport { describe, it, expect, beforeAll, afterAll } from 'vitest';\nimport { createTestServer } from '@velox/test';\nimport { db } from '$lib/db';\n\nlet server: Awaited<ReturnType<typeof createTestServer>>;\n\nbeforeAll(async () => {\n server = await createTestServer({ seed: true });\n});\n\nafterAll(async () => {\n await server.stop();\n await db.$disconnect();\n});\n\ndescribe('GET /api/users', () => {\n it('returns all users', async () => {\n const res = await server.inject({\n method: 'GET',\n url: '/api/users',\n headers: { Authorization: `Bearer ${server.getAdminToken()}` },\n });\n\n expect(res.status).toBe(200);\n const body = await res.json();\n expect(Array.isArray(body)).toBe(true);\n expect(body.length).toBeGreaterThan(0);\n });\n\n it('returns 401 without auth', async () => {\n const res = await server.inject({ method: 'GET', url: '/api/users' });\n expect(res.status).toBe(401);\n });\n});\n\ndescribe('POST /api/users', () => {\n it('creates a new user', async () => {\n const res = await server.inject({\n method: 'POST',\n url: '/api/users',\n body: { email: 'new@example.com', name: 'New User' },\n headers: { Authorization: `Bearer ${server.getAdminToken()}` },\n });\n\n expect(res.status).toBe(201);\n const user = await res.json();\n expect(user.email).toBe('new@example.com');\n expect(user.id).toBeDefined();\n });\n\n it('rejects duplicate email', async () => {\n await server.inject({\n method: 'POST',\n url: '/api/users',\n body: { email: 'dup@example.com', name: 'First' },\n headers: { Authorization: `Bearer ${server.getAdminToken()}` },\n });\n\n const res = await server.inject({\n method: 'POST',\n url: '/api/users',\n body: { email: 'dup@example.com', name: 'Second' },\n headers: { Authorization: `Bearer ${server.getAdminToken()}` },\n });\n\n expect(res.status).toBe(409);\n });\n});\n```\n\n### Testing Database Operations\n\n```typescript\n// tests/integration/db/posts.test.ts\nimport { describe, it, expect, beforeEach } from 'vitest';\nimport { db } from '$lib/db';\nimport { createTestUser, createTestPost } from '../factories';\n\nbeforeEach(async () => {\n await db.$executeRaw`TRUNCATE posts, users RESTART IDENTITY CASC" }, { "file": "pages/index.md", "title": "Introduction", "section-id": "getting-started", "keywords": "velox, framework, typescript, javascript, full-stack, introduction", "description": "An introduction to Velox, the high-performance TypeScript web framework", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Introduction to Velox\n\n![Velox Framework Hero](assets/images/hero.jpg)\n\nVelox is a high-performance, full-stack TypeScript and JavaScript web framework built for developers who refuse to compromise between developer experience and production performance. Combining a file-based routing model inspired by the best parts of Next.js, an island-based rendering architecture inspired by Astro, and a Rust-powered build toolchain that makes cold starts and hot reloads feel instantaneous, Velox occupies a unique position in the modern web ecosystem.\n\n## Why Velox?\n\nThe JavaScript ecosystem is rich, yet fragmented. Some frameworks offer outstanding developer experience but struggle with raw performance at scale. Others are extremely fast but require significant configuration overhead before you can write your first component. Velox was created to close that gap.\n\nThe core philosophy of Velox can be summarised in three words: **fast by default**. Every architectural decision \u2014 from the Rust-based bundler (Velocitor) to the zero-runtime component model \u2014 is made to ensure that your production application runs as efficiently as possible without requiring manual intervention from the developer.\n\n## Key Features\n\n### Rust-Powered Toolchain (Velocitor)\n\nThe Velox build system, internally called Velocitor, is written in Rust. It handles TypeScript transpilation, module bundling, tree-shaking, code splitting, and asset optimisation in a single pass. On modern hardware, a full production build of a medium-sized application typically completes in under three seconds. Incremental rebuilds during development are sub-50ms for most file changes.\n\n### File-Based Routing\n\nVelox uses a file-based routing system. Any `.velox` or `.tsx` file placed under the `routes/` directory automatically becomes a route. Dynamic segments use `[param]` notation, optional segments use `[[param]]`, and catch-all routes use `[...rest]`. No manual route registration is ever required.\n\n### Islands Architecture\n\nBy default, Velox renders pages entirely on the server. Interactive components are \"islands\" \u2014 explicitly opt-in to client-side hydration using the `client:*` directive. This means your pages ship zero JavaScript by default; you add interactivity exactly where it is needed.\n\n### Hybrid Rendering Modes\n\nVelox supports four rendering strategies per route:\n- **SSR (Server-Side Rendering)** \u2014 rendered fresh on every request\n- **SSG (Static Site Generation)** \u2014 pre-rendered at build time\n- **ISR (Incremental Static Regeneration)** \u2014 statically generated but revalidated on a schedule\n- **CSR (Client-Side Rendering)** \u2014 fully client-rendered for dashboard-style pages\n\nYou can mix rendering modes across routes within a single project.\n\n### TypeScript First\n\nVelox is written in TypeScript and treats TypeScript as a first-class citizen. Configuration files, route handlers, middleware, and components all benefit from full type inference. There is no separate type-generation step required.\n\n### Edge-Ready\n\nVelox applications are deployable to Cloudflare Workers, Vercel Edge, and similar runtimes out of the box. The framework's core HTTP runtime has no Node.js-specific dependencies and operates on standard Web APIs (`Request`, `Response`, `fetch`, `crypto`), making edge deployment trivially simple.\n\n### Built-In Middleware System\n\nThe middleware system allows you to intercept and transform requests and responses at multiple points in the pipeline. Auth, rate limiting, logging, CORS, and header manipulation are all achievable through composable middleware functions.\n\n## How Velox Compares\n\n| Feature | Velox | Next.js | Astro | Remix |\n|---|---|---|---|---|\n| Build system | Rust (Velocitor) | Webpack/Turbopack | Vite | Vite |\n| Default JS shipped | Zero | Varies | Zero | Varies |\n| Rendering modes | SSR/SSG/ISR/CSR | SSR/SSG/ISR | SSG/SSR | SSR |\n| Full-stack API routes | Yes | Yes | Yes | Yes |\n| File-based routing | Yes | Yes | Yes | No |\n| TypeScript support | First-class | First-class | First-class | First-class |\n| Edge runtime | Native | Adapter | Adapter | Adapter |\n\n## Who Is Velox For?\n\nVelox is well suited for:\n\n- Teams building content-heavy marketing sites that need fast initial load times with selective interactivity\n- Full-stack application teams who want a unified framework for frontend and backend\n- Engineers at high-scale companies who need ISR and edge caching to serve global audiences\n- Developers migrating from Next.js who want faster build times without giving up the Next.js mental model\n\nVelox is perhaps not the best choice if you are building a highly stateful single-page application that is mostly client-rendered \u2014 in that case a pure SPA framework may be simpler.\n\n## Getting Started\n\nThe quickest way to get started with Velox is to install the CLI and scaffold a new project:\n\n```bash\nnpm create velox@latest my-app\ncd my-app\nnpm run dev\n```\n\nYour new project will be running at `http://localhost:3700` in under ten seconds.\n\nRead on to the " }, { "file": "pages/installation.md", "title": "Installation", "section-id": "getting-started", "keywords": "install, setup, npm, yarn, pnpm, bun, node, requirements", "description": "How to install Velox and set up your development environment", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Installation\n\nThis page covers everything you need to install Velox and get your development environment ready to build production applications.\n\n## System Requirements\n\nBefore installing Velox, ensure your system meets the following requirements:\n\n| Requirement | Minimum Version | Recommended |\n|---|---|---|\n| Node.js | 20.0.0 | 22.x LTS |\n| npm | 9.0.0 | 10.x |\n| Operating system | macOS 12, Ubuntu 22.04, Windows 10 | Latest stable |\n| RAM | 2 GB | 8 GB+ |\n| Disk space | 500 MB | 2 GB (for caches) |\n\nVelox's Rust-based build system (Velocitor) ships as a pre-compiled binary for macOS (arm64 + x86_64), Linux (x86_64 + aarch64), and Windows (x86_64). You do **not** need a Rust toolchain installed on your machine.\n\n## Installing the Velox CLI\n\nThe Velox CLI (`velox`) is the primary tool for scaffolding projects, running the development server, and triggering builds. Install it globally with your preferred package manager:\n\n```bash\n# npm\nnpm install -g velox-cli\n\n# yarn\nyarn global add velox-cli\n\n# pnpm\npnpm add -g velox-cli\n\n# bun\nbun add -g velox-cli\n```\n\nAfter installation, verify the CLI is available:\n\n```bash\nvelox --version\n# velox-cli v1.4.0\n```\n\n## Creating a New Project\n\n### Using `create-velox` (Recommended)\n\nThe easiest way to start a new Velox project is through the `create-velox` initialiser. It interactively walks you through project setup:\n\n```bash\nnpm create velox@latest\n```\n\nYou will be asked:\n1. **Project name** \u2014 used as the directory name and initial package name\n2. **Template** \u2014 choose from `minimal`, `blog`, `docs`, `dashboard`, or `e-commerce`\n3. **TypeScript or JavaScript** \u2014 TypeScript is strongly recommended\n4. **Package manager** \u2014 the scaffolder will use this for the initial install\n\nFor non-interactive use, pass flags directly:\n\n```bash\nnpm create velox@latest my-app -- --template minimal --ts --pm pnpm\n```\n\n### Manual Installation\n\nIf you prefer to configure everything from scratch:\n\n```bash\nmkdir my-app && cd my-app\nnpm init -y\nnpm install velox\nnpm install -D velox-cli typescript @types/node\n```\n\nCreate a minimal `velox.config.ts`:\n\n```typescript\nimport { defineConfig } from 'velox';\n\nexport default defineConfig({\n app: {\n name: 'my-app',\n },\n});\n```\n\n## Installing Dependencies in an Existing Project\n\nIf you are adding Velox to an existing TypeScript project:\n\n```bash\nnpm install velox\nnpm install -D velox-cli\n```\n\nThen add the following scripts to your `package.json`:\n\n```json\n{\n \"scripts\": {\n \"dev\": \"velox dev\",\n \"build\": \"velox build\",\n \"start\": \"velox start\",\n \"preview\": \"velox preview\"\n }\n}\n```\n\n## Node Version Management\n\nWe recommend using a Node version manager to ensure you always have the correct version. Popular options:\n\n```bash\n# nvm (macOS/Linux)\nnvm install 22\nnvm use 22\n\n# fnm (fast, cross-platform)\nfnm install 22\nfnm use 22\n\n# volta (pins versions per project)\nvolta install node@22\n```\n\nYou can also pin the Node version for your project by adding a `.nvmrc` or `.node-version` file:\n\n```\n22\n```\n\n## Environment Variables\n\nVelox reads environment variables from `.env` files at the project root. The following files are loaded in order (later files override earlier ones):\n\n| File | Loaded in | Committed to git? |\n|---|---|---|\n| `.env` | All environments | Usually yes (no secrets) |\n| `.env.local` | All environments | No (gitignored) |\n| `.env.development` | `velox dev` only | Usually yes |\n| `.env.production` | `velox build` / `velox start` | Usually yes |\n| `.env.test` | Test runs | Usually yes |\n\nVariables prefixed with `PUBLIC_` are exposed to the browser. All other variables remain server-side only.\n\n```bash\n# .env\nPUBLIC_APP_NAME=My Velox App\nDATABASE_URL=postgres://localhost:5432/mydb\nSECRET_API_KEY=do-not-expose-this\n```\n\n## Updating Velox\n\nTo update Velox to the latest version within your project:\n\n```bash\nnpm install velox@latest\nnpm install -D velox-cli@latest\n```\n\nTo check whether updates are available without applying them:\n\n```bash\nvelox upgrade --check\n```\n\n## Troubleshooting Installation\n\n**`velox` command not found after global install**\nEnsure your package manager's global bin directory is in your `PATH`. For npm, run `npm config get prefix` and add the `bin` subdirectory to your shell profile.\n\n**Velocitor binary fails to run on Linux**\nSome minimal Linux environments (e.g., Alpine Linux) may be missing the `glibc` version Velocitor requires. Install `glibc` compatibility libraries or use the musl build:\n\n```bash\nnpm install velox@latest --velox-binary-variant=musl\n```\n\n**Windows Defender flags the Velocitor binary**\nThis is a false positive. Add an exclusion for your project's `node_modules/.velox-bin/` directory.\n\nNext, follow the [Quick Start](quick-start.md) guide to build your first Velox application." }, { "file": "pages/layouts.md", "title": "Layouts", "section-id": "core-concepts", "keywords": "layouts, nested layouts, shared layouts, layout groups, _layout, slots", "description": "How to use nested layouts, shared layouts, and layout groups in Velox", "author": null, "date": "", "datetime": "", "language": "en", "body": "# Layouts\n\nLayouts define the structural shell around your page content \u2014 navigation headers, sidebars, footers, and anything else that persists across multiple pages. Velox provides a flexible, composable layout system built directly into the file-based router.\n\n## Default Layout\n\nThe file `layouts/default.velox` is the root layout applied to all routes unless overridden:\n\n```tsx\n// layouts/default.velox\n---\nimport Header from '../components/Header.tsx';\nimport Footer from '../components/Footer.tsx';\n---\n\n<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>{meta.title} \u2014 My App\n \n \n \n \n
\n
\n \n
\n