API
@ogygia/router
The server router, with routes, pages, layouts, loads, actions and endpoints.
On this page
- action
- anonymousVisitor
- api
- compile
- DELETE
- error
- fail
- GET
- is_http_error
- is_redirect
- layout
- load
- match_path
- page
- PATCH
- POST
- PUT
- redirect
- routes
- when
- ApiError
- ApiError.status
- ApiError.body
- ActionDef
- ActionDef.pattern
- ActionFailure
- ActionFailure.[ACTION_FAILURE]
- ActionFailure.status
- ActionFailure.data
- AnonymousVisitorOptions
- AnonymousVisitorOptions.cookie
- AnonymousVisitorOptions.days
- ApiClient
- ApiClient.get
- ApiClient.delete
- ApiClient.post
- ApiClient.put
- ApiClient.patch
- ApiClientOptions
- ApiClientOptions.headers
- ApiClientOptions.fetch
- CompiledPattern
- CompiledPattern.pattern
- CompiledPattern.regex
- CompiledPattern.params
- CompiledPattern.score
- ComponentPick
- Ctx
- Ctx.params
- Ctx.search
- Ctx.input
- Ctx.url
- Ctx.request
- Ctx.route
- Ctx.fetch
- Ctx.cookies
- Ctx.locals
- Ctx.setHeaders
- Ctx.collected_headers
- Ctx.visitor
- Ctx.platform
- Ctx.getClientAddress
- Ctx.event
- Ctx.json
- Ctx.redirect
- Ctx.text
- Ctx.href
- Ctx.state
- Endpoint
- Endpoint.GET
- Endpoint.DELETE
- Endpoint.HEAD
- Endpoint.OPTIONS
- Endpoint.POST
- Endpoint.PUT
- Endpoint.PATCH
- LayoutDef
- LayoutDef.name
- LayoutDef.component
- LayoutDef.load
- LayoutDef.error
- LayoutDef.config
- LoadDef
- LoadDef.pattern
- LoadDef.run
- PageDef
- PageDef.component
- PageDef.load
- PageDef.actions
- PageDef.params_schema
- PageDef.search_schema
- PageDef.fallback
- PageDef.entries
- PageDef.config
- PageDef.layouts
- PageServer
- PageServer.params
- PageServer.search
- PageServer.load
- PageServer.actions
- PageServer.entries
- PageServer.config
- Router
- Router.fetch
- Router.handle
- Router.href
- Router.entries
- Router.$infer
- RoutesOptions
- RoutesOptions.base
- RoutesOptions.slash
- RoutesOptions.miss
- RoutesOptions.error
- RoutesOptions.load
- RoutesOptions.visitor
- RoutesOptions.flags
- RoutesOptions.config
- StandardSchemaV1
- StandardSchemaV1."~standard"
- VerbEntry
- VerbEntry.handler
- VerbEntry.bodySchema
- Action
- AnyComponent
- Handler
- HrefArgs
- HrefParams
- InferMap
- InferOutput
- Load
- Params
- RouteTable
- Simplify
This page is generated from the package’s own .d.ts files on every build, so the signatures, descriptions and examples below always match the source.
import { … } from '@ogygia/router';Exports action · anonymousVisitor · api · compile · DELETE · error · fail · GET · is_http_error · is_redirect · layout · load · match_path · page · PATCH · POST · PUT · redirect · routes · when · ApiError · ActionDef · ActionFailure · AnonymousVisitorOptions · ApiClient · ApiClientOptions · CompiledPattern · ComponentPick · Ctx · Endpoint · LayoutDef · LoadDef · PageDef · PageServer · Router · RoutesOptions · StandardSchemaV1 · VerbEntry · Action · AnyComponent · Handler · HrefArgs · HrefParams · InferMap · InferOutput · Load · Params · RouteTable · Simplify
action
function
action(fn) / action(pattern, fn) — one form action. Called once on POST (no memoization).
In a page's actions, action(fn) takes the page's params like load(fn) does.
function action<Ret, Prm = LooseParams, Srch = Record<string, string>>(run: (c: Ctx<Prm, Srch>) => Ret | Promise<Ret>): ActionDef<Awaited<Ret>, string, Prm, Srch>;
function action<Ret, P extends string, Srch = Record<string, string>>(pattern: P, run: (c: Ctx<Params<P>, Srch>) => Ret | Promise<Ret>): ActionDef<Awaited<Ret>, P, Params<P>, Srch>;anonymousVisitor
function
function anonymousVisitor(opts?: AnonymousVisitorOptions): (c: Ctx) => Visitor | undefined;api
function
function api<App>(base: string, opts?: ApiClientOptions): ApiClient<App>;compile
function
function compile(pattern: string): CompiledPattern;DELETE
function
DELETE(handler) — a verb wrapper for a DELETE slot (no body schema).
function DELETE<P = LooseParams, R = unknown>(handler: (c: Ctx<P>) => R): VerbEntry<Awaited<R>, undefined, P>;error
function
Throw an HTTP error from a load / action / handler — Kit's error(404, 'Not found'), verbatim.
On a page branch the nearest error boundary renders it; on an endpoint it becomes JSON.
function error(status: number, message?: string): never;fail
function
Return an action validation failure — Kit's fail(400, { incorrect: true }). The router re-renders
the page with form = data and the given status. RETURNED, not thrown (Kit's contract).
function fail<T extends Record<string, unknown>>(status: number, data: T): ActionFailure<T>;GET
function
GET(handler) — a verb wrapper for a GET slot (no body, so no schema). Inside a table its c.params are the key's; a bare { GET: (c) => … } is the same thing — the wrapper exists for
symmetry with the body verbs.
function GET<P = LooseParams, R = unknown>(handler: (c: Ctx<P>) => R): VerbEntry<Awaited<R>, undefined, P>;is_http_error
function
const is_http_error: (x: unknown) => x is HttpErroris_redirect
function
const is_redirect: (x: unknown) => x is Redirectlayout
function
layout(name, Component, { load?, error?, config? }) — a named table→table wrapper. Applying it PREPENDS
this layout to every entry's chain and returns a table with the SAME KEYS, so ...admin({...}) spreads the tagged entries into the parent (a wrapped table nested in another accumulates the
chain — Kit's nested layouts). load may be a load() wrapper or a bare typed fn. The name keys
the layout's { data, children } type as App['(name)']; duplicate names are a build error.
function layout<Name extends string, S extends {
load?: LoadInput;
error?: AnyComponent;
config?: RouteConfig;
}>(name: Name, component: AnyComponent, opts?: S): LayoutFn<LayoutDef<Name, S["load"]>>;load
function
load(fn) / load(pattern, fn). The returned function memoizes on c (run once per request);
the dispatcher's concurrent branch-load pass and any await other_load(c) share the same promise.
In a page's load slot, load(fn) takes the page's params through its contextual return type
(like a verb wrapper); anywhere else they are loose.
function load<Data, Prm = LooseParams, Srch = Record<string, string>>(run: (c: Ctx<Prm, Srch>) => Data | Promise<Data>): LoadDef<Awaited<Data>, string, Prm, Srch>;
function load<Data, P extends string, Srch = Record<string, string>>(pattern: P, run: (c: Ctx<Params<P>, Srch>) => Data | Promise<Data>): LoadDef<Awaited<Data>, P, Params<P>, Srch>;match_path
function
First (most-specific) match in a pre-sorted list, or null.
function match_path(sorted: CompiledPattern[], pathname: string): PathMatch | null;page
function
page(Component) / page(Component, { params?, search?, load?, actions? }). The second arg mirrors
+page.server.ts; params/search are input schemas (typed into c + $infer), load/actions are behaviors (wrappers or bare typed functions — see Load / Action). The
component slot also takes a flag.pick(), a PageSlotResolver, or a streamed-page
generator (StreamSlot).
Inside a table, c.params in load and actions is the KEY's: P comes from the contextual
return type (PageFor), like a verb wrapper's. A params schema wins over the key, and a search schema types c.search. Outside a table P stays loose. Ld/A lead with NO default:
a slot's contextual type is then its constraint (the typed c) — a default would win outside a
table and leave c untyped. An unwritten slot infers its constraint, which keeps the | undefined $infer reads as "no load" / "no actions".
function page<Ld extends PageLoad<PageParams<P, PS>, PageSearch<SS>> | undefined, A extends Record<string, PageAction<PageParams<P, PS>, PageSearch<SS>>> | undefined, P = LooseParams, PS extends StandardSchemaV1 | undefined = undefined, SS extends StandardSchemaV1 | undefined = undefined>(component: AnyComponent | ComponentPick | PageSlotResolver | StreamSlot, server?: PageServer<PS, SS, Ld, A>): PageDef<readonly [], Ld, A, PS, SS, P>;PATCH
function
PATCH(handler) / PATCH(bodySchema, handler).
function PATCH<P = LooseParams, R = unknown>(handler: (c: Ctx<P>) => R): VerbEntry<Awaited<R>, undefined, P>;
function PATCH<Sch extends StandardSchemaV1, P = LooseParams, R = unknown>(schema: Sch, handler: (c: Ctx<P, Record<string, string>, InferOutput<Sch>>) => R): VerbEntry<Awaited<R>, InferOutput<Sch>, P>;POST
function
POST(handler) / POST(bodySchema, handler) — a POST slot. The schema validates the JSON body
into c.input (400 on failure), like a command RF's typed argument.
function POST<P = LooseParams, R = unknown>(handler: (c: Ctx<P>) => R): VerbEntry<Awaited<R>, undefined, P>;
function POST<Sch extends StandardSchemaV1, P = LooseParams, R = unknown>(schema: Sch, handler: (c: Ctx<P, Record<string, string>, InferOutput<Sch>>) => R): VerbEntry<Awaited<R>, InferOutput<Sch>, P>;PUT
function
PUT(handler) / PUT(bodySchema, handler).
function PUT<P = LooseParams, R = unknown>(handler: (c: Ctx<P>) => R): VerbEntry<Awaited<R>, undefined, P>;
function PUT<Sch extends StandardSchemaV1, P = LooseParams, R = unknown>(schema: Sch, handler: (c: Ctx<P, Record<string, string>, InferOutput<Sch>>) => R): VerbEntry<Awaited<R>, InferOutput<Sch>, P>;redirect
function
Throw a redirect from a load / action / handler — Kit's redirect(303, '/login'), verbatim.
function redirect(status: number, location: string | URL): never;routes
function
function routes<const T extends KeyedTable<T>>(typed: T, opts?: RoutesOptions): Router<T>;when
function
Flag-gate a table entry — page or endpoint. OFF means the route DOES NOT EXIST for that
request: 404 (the app's error page) under an owned base, fall-through to the rest of the app
without one. The gate is any (c) => boolean — a boolean flag() slots in directly, so a
staged rollout of a whole page, a beta route, or an endpoint kill switch is one wrapper:
'/checkout-v2': when(checkoutV2, page(NewCheckout, { load })),
'/api/export': when(exportsFlag, { GET: GET(export_csv) }),
'/cms/[...rest]': when(cmsRollout, mount(cms)), // per-cohort fragment rolloutDecided AFTER decide({ source }) primes, so a vendor kill switch gates routes too; the
decision self-registers for federation carry like any flag read. Type-transparent: $infer sees the entry exactly as if unwrapped.
function when<T extends object>(gate: (c: Ctx) => boolean, entry: T): T;ApiError
class
class ApiError extends Error {/*…*/}ApiError.status
readonly status: number;ApiError.body
readonly body: unknown;ActionDef
interface
A form action def (symmetric with load): action(fn) / action(pattern, fn). Returns fail(...) for validation, throws redirect() on success, or returns data for the form prop —
Kit's action contract. pattern types c.params; $infer reads Ret into the form prop. Prm/Srch are the params and search its c was typed with (see LoadDef).
interface ActionDef<Ret = unknown, P extends string = string, Prm = LooseParams, Srch = Record<string, string>> {/*…*/}ActionDef.pattern
readonly pattern: P | undefined;ActionFailure
interface
interface ActionFailure<T = Record<string, unknown>> {/*…*/}ActionFailure.[ACTION_FAILURE]
[ACTION_FAILURE]: true;ActionFailure.status
status: number;ActionFailure.data
data: T;AnonymousVisitorOptions
interface
interface AnonymousVisitorOptions {/*…*/}AnonymousVisitorOptions.cookie
Cookie name (default og-vid — the same one experiment()'s fallback chain reads).
cookie?: string;AnonymousVisitorOptions.days
Cookie lifetime in days (default 365).
days?: number;ApiClient
interface
interface ApiClient<App> {/*…*/}ApiClient.get
get<K extends KeysWith<App, "get">>(path: K, ...args: CallArgs<K>): Promise<OutOf<App, K, "get">>;ApiClient.delete
delete<K extends KeysWith<App, "delete">>(path: K, ...args: CallArgs<K>): Promise<OutOf<App, K, "delete">>;ApiClient.post
post<K extends KeysWith<App, "post">>(path: K, ...args: CallArgs<K, InOf<App, K, "post">>): Promise<OutOf<App, K, "post">>;ApiClient.put
put<K extends KeysWith<App, "put">>(path: K, ...args: CallArgs<K, InOf<App, K, "put">>): Promise<OutOf<App, K, "put">>;ApiClient.patch
patch<K extends KeysWith<App, "patch">>(path: K, ...args: CallArgs<K, InOf<App, K, "patch">>): Promise<OutOf<App, K, "patch">>;ApiClientOptions
interface
interface ApiClientOptions {/*…*/}ApiClientOptions.headers
Extra headers on every call (auth tokens, signed hops — compose with sign_headers).
headers?: Record<string, string>;ApiClientOptions.fetch
Custom fetch (a Kit event's cookie-forwarding fetch, a test stub). Default: global.
fetch?: typeof fetch;CompiledPattern
interface
interface CompiledPattern {/*…*/}CompiledPattern.pattern
the original table key, e.g. '/docs/[slug]' — also the route.id we expose
pattern: string;CompiledPattern.regex
regex: RegExp;CompiledPattern.params
params: ParamSpec[];CompiledPattern.score
higher = more specific; used to order overlapping matches (static beats dynamic)
score: number;ComponentPick
interface
A per-request component chooser for page() — what flag.pick({ … }) returns
(page(hero.pick({ control: A, bold: B }))). Branded, not a bare function, because Svelte 5
components ARE functions. Structural: the flags package builds the same shape without importing
the router.
interface ComponentPick {/*…*/}Ctx
interface
interface Ctx<P = Record<string, string | undefined>, S = Record<string, string>, I = undefined> {/*…*/}Ctx.params
Path params — pattern-typed, or the params schema's coerced output (404 on bad input).
params: Simplify<P>;Ctx.search
Query params — raw { [k]: string }, or the search schema's typed output (400 on bad input).
search: Simplify<S>;Ctx.input
A body-verb endpoint's validated JSON body (POST/PUT/PATCH schema); else undefined.
input: I;Ctx.url
url: URL;Ctx.request
request: Request;Ctx.route
The matched route pattern, e.g. /docs/[slug] (Kit's route.id).
route: {
id: string;
};Ctx.fetch
fetch: typeof fetch;Ctx.cookies
cookies?: RequestEvent["cookies"];Ctx.locals
locals?: RequestEvent["locals"];Ctx.setHeaders
Set response headers from a load/action/handler. ALWAYS works: the router applies these to
the Response IT builds — Kit's own event.setHeaders only affects resolve()-built
responses, and a router-rendered document bypasses resolve (found when a mount load's
Server-Timing silently vanished).
setHeaders?: RequestEvent["setHeaders"];Ctx.collected_headers
collected_headers?: Map<string, string>;Ctx.visitor
WHO is this request? THE identity — derived ONCE. Precedence: signature-bound claims from
an upstream shell (fragment federation — unforgeable, they arrived inside an Ed25519
signature) → the table's visitor resolver (routes(table, { visitor })) → undefined.
Everything downstream READS this instead of re-deriving: experiments stick on it, mounts
sign it onward, loads personalize with it.
readonly visitor?: Visitor;Ctx.platform
platform?: Readonly<RequestEvent["platform"]>;Ctx.getClientAddress
getClientAddress?: RequestEvent["getClientAddress"];Ctx.event
The raw Kit event, when mounted in Kit.
event?: RequestEvent;Ctx.json
JSON response (default application/json, no-store).
json(data: unknown, init?: ResponseInit): Response;Ctx.redirect
Redirect Response (default 303). Prefer the throwable redirect() inside loads/actions.
redirect(location: string, status?: number): Response;Ctx.text
Plain-text response.
text(body: string, init?: ResponseInit): Response;Ctx.href
A rename-safe URL to a route in this router: c.href('/report/[id]', { id }).
href(pattern: string, params?: Record<string, string | number>): string;Ctx.state
A per-request scratch bag (a guard writes, a handler reads).
state: Record<string, unknown>;Endpoint
interface
An endpoint route entry — the +server.ts shape, as an object of uppercase verb slots. Each verb
is a bare handler or a verb wrapper. Distinguished from a page by having no __ogkind.
interface Endpoint {/*…*/}Endpoint.GET
GET?: VerbSlot;Endpoint.DELETE
DELETE?: VerbSlot;Endpoint.HEAD
HEAD?: VerbSlot;Endpoint.OPTIONS
OPTIONS?: VerbSlot;Endpoint.POST
POST?: VerbSlot;Endpoint.PUT
PUT?: VerbSlot;Endpoint.PATCH
PATCH?: VerbSlot;LayoutDef
interface
A layout def, generic over its NAME and own LOAD (authored form) so $infer can key App['(name)'] and merge its data into the chains that contain it.
interface LayoutDef<Name extends string = string, Load = unknown> {/*…*/}LayoutDef.name
readonly name: Name;LayoutDef.component
readonly component: AnyComponent;LayoutDef.load
readonly load: LoadDef | undefined;LayoutDef.error
readonly error?: AnyComponent;LayoutDef.config
config for every page under this layout (a deeper layout's or the page's own key wins)
readonly config?: RouteConfig;LoadDef
interface
A load is a CALLABLE, memoized per request: await session_load(c) runs .run(c) once per
request (the memo lives on c), so a layout and its child sharing a load never double-fetch —
Kit's parent(), generalized. pattern (optional) binds c.params AND is checked against the
table key at assembly. $infer reads Data. Prm/Srch are the params and search its c was
typed with: the page's (its key's params or its schemas' output) when written in a page's load slot, loose elsewhere. They type the call, so a loose load (a shared one) fits any page, and its
call signature matches a bare load's — which is what lets a bare (c) => … in the same slot keep
its contextual type.
interface LoadDef<Data = unknown, P extends string = string, Prm = LooseParams, Srch = Record<string, string>> {/*…*/}LoadDef.pattern
readonly pattern: P | undefined;LoadDef.run
readonly run: (c: Ctx) => Data | Promise<Data>;PageDef
interface
A page route def, generic over its layout chain / load / actions so $infer reads them. L is
the OUTERMOST-first layout chain (empty = no chrome); the runtime prepends to it as layout(...)() wraps this entry's table. Load/Actions keep the AUTHORED form (wrapper or bare fn) for infer. KeyParams is what page() took from its table key (loose outside a table) — see PageFor.
interface PageDef<L extends readonly LayoutDef[] = readonly LayoutDef[], Load = unknown, Actions = unknown, ParamsSchema = undefined, SearchSchema = undefined, KeyParams = LooseParams> {/*…*/}PageDef.component
The page component, a per-request ComponentPick (page(flag.pick({...}))), or a
STREAMED page (page(async function* (c, data) { … }) — see StreamSlot);
the dispatcher resolves it with the ctx.
readonly component: AnyComponent | ComponentPick | StreamSlot;PageDef.load
readonly load: LoadDef | undefined;PageDef.actions
readonly actions: Record<string, ActionDef> | undefined;PageDef.params_schema
readonly params_schema?: StandardSchemaV1;PageDef.search_schema
readonly search_schema?: StandardSchemaV1;PageDef.fallback
a deferred page's loading placeholder — the component's reserved ogygiaFallback slot
readonly fallback?: AnyComponent;PageDef.entries
prerender param sets for a dynamic pattern — see PageServer.entries
readonly entries?: PageServer["entries"];PageDef.config
the page's own config — see PageServer.config
readonly config?: RouteConfig;PageDef.layouts
readonly layouts: L;PageServer
interface
The +page.server.ts-shaped second argument to page(). Always an object (ruled). Route input is
declared here (params/search schemas — coerced, typed into c, shown in $infer); load and actions are the behaviors (each a wrapper or a bare typed fn). Actions read FormData themselves
(Kit-identical) — no action schema; typed mutation input is a command RF, not a form action. page() instantiates it with what it infers; the bare PageServer is the loose shape.
interface PageServer<PS = StandardSchemaV1, SS = StandardSchemaV1, Ld = LoadInput, A = Record<string, ActionInput>> {/*…*/}PageServer.params
Coerce + validate path params (garbage → 404); c.params and App['/p']['params'] = its output.
params?: PS;PageServer.search
Coerce + validate query (bad → 400, or lenient via .default()); c.search + App['/p']['search'].
search?: SS;PageServer.load
load?: Ld;PageServer.actions
actions?: A;PageServer.entries
PRERENDER param sets for a DYNAMIC pattern — Kit's per-route entries export. Each item
fills the pattern (/posts/[id] + { id: '1' } → /posts/1) and joins the router's entries() crawl list; a dynamic page without this is skipped (crawled or SSR'd live).
entries?: () => Array<Record<string, string>> | Promise<Array<Record<string, string>>>;PageServer.config
Kit's config page option, the programmatic twin of export const config = {…} in a +page.server.ts: plugins read their keys from it ({ postrender: false }). Merged like Kit's: routes(table, { config }), then each layout outermost → innermost, then the page; a later
level replaces a key, top level only.
config?: RouteConfig;Router
interface
The router value. $infer is a type-only phantom read as typeof app.$infer.
interface Router<T extends TableShape = RouteTable> {/*…*/}Router.fetch
(request) => Response | null — null on no-match (a catchall 404s its own way).
fetch(request: Request, event?: RequestEvent): Promise<Response | null>;Router.handle
Kit handle: dispatches, else falls through to the rest of the app.
handle(input: {
event: RequestEvent;
resolve: (e: RequestEvent) => Response | Promise<Response>;
}): Promise<Response>;Router.href
Rename-safe URL to a route in this table.
href<P extends keyof T & string>(pattern: P, ...args: HrefArgs<P>): string;Router.entries
The catchall crawl list (static page routes) — export const entries = app.entries.
entries(): Promise<Array<{
path: string;
}>>;Router.$infer
Type-only phantom map: export type App = typeof app.$infer.
readonly $infer: InferMap<T>;RoutesOptions
interface
interface RoutesOptions {/*…*/}RoutesOptions.base
Mount prefix, stripped before matching. Required for a library that owns a subtree.
base?: string;RoutesOptions.slash
Trailing-slash policy. 'ignore' (default) matches both; 'never'/'always' 308-canonicalize.
slash?: "ignore" | "never" | "always";RoutesOptions.miss
Response for an unmatched path UNDER base (default: 404). Ignored without base.
miss?: (c: Ctx) => unknown;RoutesOptions.error
Root error boundary component (Kit's top +error.svelte) for thrown error() with no nearer
layout boundary.
error?: AnyComponent;RoutesOptions.load
A table-wide load run before EVERY route (pages and endpoints) — Kit's root +layout.server.ts load without a component. The place for a guard with no shared chrome: it throws redirect/error to gate, and its returned data merges into every page's data (outermost).
load?: LoadInput;RoutesOptions.visitor
WHO is this request? Derived ONCE here, read everywhere as c.visitor — experiments stick
on it, mounts sign it into claims, loads personalize with it. Signature-bound claims from
an upstream shell (fragment federation) take precedence over this resolver.
visitor?: (c: Ctx) => Visitor | undefined;RoutesOptions.flags
Flags to PRE-DECIDE at the table, so mounts carry their buckets even when no page on this
server reads them (a shell that only ROUTES to teams still wants the visitor's world to
travel). Any flag a page DOES read auto-carries without being listed here — this is just for
the decide-but-don't-render case. flags: [csr].
flags?: ReadonlyArray<(c: Ctx) => unknown>;RoutesOptions.config
Table-wide Kit config page option for every page (a layout's or a page's own key wins):
plugins read their keys from it ({ postrender: false }). The handle consults this table for
any request Kit's file router did not claim — keep ogygia.handle() ahead of the table in the sequence() so a stored page is served before the table runs.
config?: RouteConfig;StandardSchemaV1
interface
The Standard Schema interface (standardschema.dev): the ~standard spec Zod / Valibot / ArkType all
implement, the very thing Kit's remote functions consume. Validate a value with any schema library.
Types only.
interface StandardSchemaV1<Output = unknown> {/*…*/}StandardSchemaV1."~standard"
readonly "~standard": {
readonly version: 1;
readonly vendor: string;
readonly validate: (value: unknown) => StandardResult<Output> | Promise<StandardResult<Output>>;
readonly types?: {
readonly output: Output;
};
};VerbEntry
interface
A verb entry produced by the GET/POST/… value wrappers — a handler plus (for body verbs) a
body schema (POST(schema, fn)). Symmetric with load()/action(). Out/In/P are PHANTOM
types for $infer + the typed api() client: a PLAIN return is the endpoint's JSON payload
(finalize() serializes it), a Response return erases to unknown; In is the body schema's
output; P the params the handler was typed with (its table key's, inside a table).
interface VerbEntry<Out = unknown, In = undefined, P = unknown> {/*…*/}VerbEntry.handler
readonly handler: (c: Ctx) => unknown;VerbEntry.bodySchema
readonly bodySchema?: StandardSchemaV1;Action
type
TYPE helper for a pulled-out or inline action (mirrors Load).
type Action<P extends string = string> = (c: Ctx<Params<P>>) => unknown;AnyComponent
type
Any component. Props are a component's INPUT, so never props is the one shape every
component's props can stand in for: every Component<Props> is assignable to this.
type AnyComponent = Component<never>;Handler
type
TYPE helper for an endpoint handler written OUTSIDE its table. In (a schema output) types c.input — the validated JSON body of a POST/PUT/PATCH. Verb-agnostic (the method doesn't
change the ctx). Check against it with satisfies, which keeps the handler's own return type (the
payload $infer and api() read): const getPost = ((c) => …) satisfies Handler<'/p/[id]'>.
Inside a table nothing is needed: the key types the handler.
type Handler<P extends string = string, In = undefined> = (c: Ctx<Params<P>, Record<string, string>, In>) => unknown;HrefArgs
type
href args: params are REQUIRED when the pattern has any, omittable when it has none.
type HrefArgs<P extends string> = {} extends Params<P> ? [params?: HrefParams<P>] : [params: HrefParams<P>];HrefParams
type
The params object href accepts for a pattern — every param, as a string or number.
type HrefParams<P extends string> = { [K in keyof Params<P>]: string | number; };InferMap
type
The full $infer map: page paths ⊕ endpoint paths ⊕ layout (name)s.
type InferMap<T extends TableShape> = Simplify<PageEntries<T> & EndpointEntries<T> & AllLayoutEntries<T>>;InferOutput
type
type InferOutput<S> = S extends StandardSchemaV1$1<infer O> ? O : never;Load
type
TYPE helper for a pulled-out or inline load — annotate a bare function so its c (and c.params)
are typed without the load() value wrapper: const l: Load<'/docs/[slug]'> = async (c) => ….
Pass it straight to page(Comp, { load: l }). Use the load() VALUE wrapper only when a load must
be SHARED across loads (await other(c) — memoized per request).
type Load<P extends string = string> = (c: Ctx<Params<P>>) => unknown;Params
type
type Params<P extends string> = Simplify<ParamsAcc<P>>;RouteTable
type
type RouteTable = Record<string, RouteDef>;Simplify
type
Route params derived from the pattern string via template literals — /docs/[slug] → { slug: string }, /[[lang]] → { lang?: string }, /[...rest] → { rest: string }, /[id].json → { id: string }. No codegen.
type Simplify<T> = { [K in keyof T]: T[K]; } & {};