Runtime & Manifest
The runtime module provides the unified Web handler used by every adapter (Node, Bun, Vercel, Netlify) and the Vite dev server. The manifest module generates a portable description of the app's routes, actions, and islands.
# createWebHandler(routes, actions, options)
Creates a single Web handler function (Request) => Promise<Response> from
scanned routes and actions. This is the entry point for all runtimes:
import { createWebHandler } from "@elurjs/kit/runtime";
import { scanRoutes } from "@elurjs/kit";
import { scanActions } from "@elurjs/kit";
const routes = await scanRoutes("./src/app");
const actions = await scanActions("./src/app");
const handler = createWebHandler(routes, actions, {
staticRoot: "./dist",
lang: "es",
clientEntry: "/_elur/entry-client.js",
renderEndpoint: true,
cacheDir: "./.elur/cache",
defaultRevalidate: 60,
});
// Use with any Web-standard server
const response = await handler(request);# WebHandlerOptions
| Field | Type | Description |
|---|---|---|
staticRoot |
string |
Static file root (absolute path, usually dist/) |
noCache |
boolean? |
Bypass ISR cache (dev mode) |
cacheDir |
string? |
ISR cache directory (absolute) |
defaultRevalidate |
number? |
Default ISR revalidate seconds |
importer |
(path) => Promise<unknown>? |
Custom module loader for bundled adapters |
lang |
string? |
HTML lang attribute |
clientEntry |
string? |
Client entry path |
renderEndpoint |
boolean? |
Whether /__elur-js/render exists |
securityHeaders |
SecurityHeadersConfig | false? |
Security response headers |
# WebHandlerRouteTable
interface WebHandlerRouteTable {
pages: PageRoute[];
api: ApiRoute[];
error404?: PageRoute;
error500?: PageRoute;
}# RequestContext
Per-request state container with cookies, locals, params, and response accumulators:
import { RequestContext } from "@elurjs/kit/runtime";
const ctx = new RequestContext({
request,
config: resolvedConfig,
routes,
actions,
publicActions,
});
ctx.pathname; // URL pathname
ctx.method; // HTTP method (uppercased)
ctx.wantsJson; // true if Accept: application/json
ctx.cookies; // read-only cookie jar
ctx.locals; // per-request data from middleware
ctx.params; // route params
ctx.response; // { status, headers, cookies }
ctx.requestId; // unique ID for logging
ctx.signal; // AbortSignal from host disconnect# RequestContextOptions
| Field | Type | Description |
|---|---|---|
request |
Request |
The Web Request |
config |
ResolvedElurConfig |
Resolved config |
routes |
RouteTable |
Scanned routes |
actions |
ActionRegistry |
Action registry |
publicActions |
Record<string, string[]> |
Public action names |
importer |
(path) => unknown | Promise<unknown>? |
Module loader |
renderEndpoint |
boolean? |
Whether render endpoint exists (default true) |
noCache |
boolean? |
Bypass ISR cache (default false) |
cacheDir |
string? |
ISR cache directory |
defaultRevalidate |
number? |
Default ISR revalidate seconds |
params |
Record<string, string | string[] | undefined>? |
Initial route params |
locals |
Record<string, unknown>? |
Initial locals |
signal |
AbortSignal? |
Abort signal |
requestId |
string? |
Custom request ID |
platform |
unknown? |
Platform-specific context |
route |
PageRoute | ApiRoute? |
Pre-matched route |
# RouteTable
interface RouteTable {
pages: PageRoute[];
api: ApiRoute[];
error404?: PageRoute;
error500?: PageRoute;
}# Response helpers
import {
htmlResponse,
jsonResponse,
textResponse,
notFound,
methodNotAllowed,
serverError,
} from "@elurjs/kit/runtime";
htmlResponse("<h1>Hi</h1>", 200);
jsonResponse({ ok: true }, 200);
textResponse("Not found", 404);
notFound(); // 404 "Not Found"
methodNotAllowed("PUT"); // 405
serverError("oops"); // 500# guessContentType(filePath)
Infers Content-Type from file extension:
import { guessContentType } from "@elurjs/kit/runtime";
guessContentType("style.css"); // "text/css; charset=utf-8"# Static file serving
import { serveStaticFile, resolveStaticFile } from "@elurjs/kit/runtime";
// High-level: returns a Response
const response = await serveStaticFile("./dist", "/images/hero.webp");
// Low-level: returns the file path or undefined
const filePath = await resolveStaticFile("./dist", "/images/hero.webp");# incomingMessageToRequest(req)
Converts a Node.js IncomingMessage to a Web Request:
import { incomingMessageToRequest } from "@elurjs/kit/runtime";
import { createServer } from "node:http";
const server = createServer(async (req, res) => {
const request = incomingMessageToRequest(req);
const response = await handler(request);
// ...write response to res...
});# Security headers
import {
buildSecurityHeaders,
applySecurityHeaders,
DEFAULT_SECURITY_HEADERS,
} from "@elurjs/kit/runtime";
// Build headers from config
const headers = buildSecurityHeaders(config.security.headers);
// Apply to a response
applySecurityHeaders(response, headers);# App manifest
# createAppManifest(config)
Scans routes, actions, and islands in parallel and returns a portable manifest:
import { createAppManifest } from "@elurjs/kit";
const manifest = await createAppManifest(resolvedConfig);
// manifest.routes, manifest.actions, manifest.islands, manifest.base, manifest.output# AppManifest
interface AppManifest {
version: 1;
root: string;
routes: ScannedRoutes;
actions: ActionRegistry;
islands: IslandModule[];
base: string;
output: "static" | "server" | "hybrid";
}# writeAppManifest(manifest, path)
Writes the manifest as JSON (with portable paths) to disk:
import { writeAppManifest } from "@elurjs/kit";
await writeAppManifest(manifest, "./dist/.elur/manifest.json");# writeRouteTypes(manifest, path)
Generates TypeScript route types for type-safe navigation:
import { writeRouteTypes } from "@elurjs/kit";
await writeRouteTypes(manifest, "./src/types/elur-routes.d.ts");
// Generates:
// export type ElurRoutePath = "/" | "/blog" | "/blog/:slug" | ...;
// export type ElurActionName = "submitContact" | "createPost" | ...;
// export interface ElurRouteParams { [name: string]: string | string[] | undefined }# validateManifestRoutes(routes)
Throws on duplicate or invalid route patterns:
import { validateManifestRoutes } from "@elurjs/kit";
validateManifestRoutes(routes); // throws if conflicts found# assertClientImportAllowed(id, importer?)
Guards against importing server-only modules in client bundles:
import { assertClientImportAllowed } from "@elurjs/kit";
assertClientImportAllowed("some-module", importer);