API
ogygia/plugin
The API for writing a plugin's server realm, covering its hooks and what it can learn about a request.
On this page
- deferredRegion
- defineServer
- late_chunk
- late_slot
- og_html_region
- open_late_scope
- region
- register_route_config
- take_late_regions
- LATE_BOOT_SCRIPT
- STORED_DOCUMENT_META
- HandleContext
- HandleContext.event
- HandleContext.options
- HandleContext.app
- LateRegion
- LateRegion.id
- LateRegion.promise
- PluginCapabilities
- PluginCapabilities.mint
- PluginCapabilities.open
- RequestContext
- RequestContext.event
- RequestContext.path
- RequestContext.state
- RequestContext.options
- RequestContext.app
- RequestContext.facts
- RequestContext.routeConfig
- RequestContext.renderHole
- RequestContext.perVisitor
- RequestFacts
- RequestFacts.storable
- RequestFacts.perVisitor
- RequestFacts.streamed
- RequestFacts.private
- RequestFacts.answerForm
- RequestFacts.sources
- RequestFacts.assignments
- ServerApp
- ServerApp.name
- ServerApp.options
- ServerApp.notify
- ServerApp.ask
- ServerApp.facts
- ServerApp.current
- ServerApp.sign
- ServerApp.verify
- ServerApp.capability
- ServerApp.devtools
- ServerApp.warn
- ServerHooks
- ServerHooks.invalidate
- ServerHooks.invalidated
- ServerHooks.lastBuild
- CapabilityOpened
- DevtoolsEventInput
- ResolveOptions
- RouteConfig
- RouteConfigMatcher
- ServerPlugin
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/plugin';Exports deferredRegion · defineServer · late_chunk · late_slot · og_html_region · open_late_scope · region · register_route_config · take_late_regions · LATE_BOOT_SCRIPT · STORED_DOCUMENT_META · HandleContext · LateRegion · PluginCapabilities · RequestContext · RequestFacts · ServerApp · ServerHooks · CapabilityOpened · DevtoolsEventInput · ResolveOptions · RouteConfig · RouteConfigMatcher · ServerPlugin
deferredRegion
function
A deferred region value whose HTML the browser fetches from url (a capability the plugin minted, '' for an inert one). HTML only: whatever islands the answer carries wake themselves.
function deferredRegion(id: string, url: string): DeferredRegion;defineServer
function
Typed identity: a plugin's server half.
function defineServer<Options = undefined, State = undefined>(plugin: ServerPlugin<Options, State>): ServerPlugin<Options, State>;late_chunk
function
A late chunk: inert template + nothing else — the boot script (or the runtime, post-SPA-swap) performs the swap. Multiple chunks for one slot apply in order (progressive states).
function late_chunk(id: string, html: string): string;late_slot
function
Wrap html as the swappable slot id (display:contents — layout-invisible).
function late_slot(id: string, html: string): string;og_html_region
function
A pure-HTML region — what import.meta.og.code() / .md() inline. The html is baked at build
(Shiki / markdown), so this is an INLINE region whose component ({@link RawHtml }) simply renders
the html in the page's own SSR pass. Static by construction: it ships as HTML and no client JS of
its own. Rendered like any region: <Region of={…} />.
function og_html_region(html: string): InlineRegion;open_late_scope
function
ONLY A RENDERER THAT DELIVERS LATE CHUNKS ARMS THEM. The server router's streamed document opens
this request's late scope before it renders (and drains it after); anywhere else — a Kit page render
— a promise of registers nothing, because nothing would ever fill its slot (a Kit page's header
sat in an empty slot forever).
function open_late_scope(): void;region
function
Make a held region with type-checked props.
- A plain component import → an inline region (renders in this pass; can't cross the wire).
- A component imported
with { region: 'raw' }→ a dual region: inline where it's made, a signed ticket where it travels. Minting the marked form is server-only (the signer lives on the SSR leg) — call it in a load / remote / render context.
A dual region is awaitable. await region(Card, props) renders the component to HTML on
the server and bakes it into the ticket, so the client swaps it in with no extra request. In an
async generator (query.live) or an async remote, yield region(…) / return region(…) is
awaited by the language, so the HTML travels automatically — LiveView over the channel you already
have. A held region you don't await renders inline where it lands (first paint, same SSR pass).
const region: typeof make_region & {
snippet: typeof region_snippet;
}register_route_config
function
A routes() table joins (keyed: dev re-evaluates hooks.server.ts and calls routes() again; a
matcher with the same key REPLACES its stale twin instead of shadowing it).
function register_route_config(key: string, matcher: RouteConfigMatcher): void;take_late_regions
function
The router drains THIS request's registered late regions after the document render (through
this seam, never by importing hooks — its virtual: imports don't resolve outside Vite). null when nothing registered / nothing armed. Draining clears the request's list.
function take_late_regions(): LateRegion[] | null;LATE_BOOT_SCRIPT
const
The classic inline boot: observes streamed-in <template data-og-late> nodes DURING parse and
swaps each into its slot immediately (stylesheet links hoist to head first so the swap never
paints unstyled). Kept dependency-free and tiny — it must run before any module script can.
const LATE_BOOT_SCRIPT: stringSTORED_DOCUMENT_META
const
Stamped into a document served FROM a store (not the render that just happened): the browser
runtime then treats render: 'live' lakes as stale on first mount and revalidates them.
const STORED_DOCUMENT_META = "<meta name=\"ogygia-stored\" content=\"hit\">"HandleContext
interface
A request as an AROUND hook sees it: before anything is decided about it.
interface HandleContext<Options = undefined> {/*…*/}HandleContext.event
Kit's own event, untouched.
readonly event: RequestEvent;HandleContext.options
readonly options: Options;HandleContext.app
readonly app: ServerApp<Options>;LateRegion
interface
interface LateRegion {/*…*/}LateRegion.id
id: string;LateRegion.promise
promise: Promise<unknown>;PluginCapabilities
interface
A capability URL for an endpoint the plugin serves (a hole the browser fetches later), held to the
rules of the app's own region holes: the same lifetime, expiry in unix seconds, and the same answer
once one has expired, so the runtime renews it the way it renews a region hole. The URL carries the
plugin's own fields first, then exp and sig.
interface PluginCapabilities {/*…*/}PluginCapabilities.mint
Sign a capability minted now: its exp (long on a page a plugin may store or a shared cache
keeps, else the app's ttl) and the sig over message(exp).
mint(message: (exp: string) => string): {
exp: string;
sig: string;
};PluginCapabilities.open
Check a request for a capability mint made, by its URL's exp and sig (with renew=1, the
runtime's one retry of an expired one). Refused: the response to answer with, the 403 a region
request gets. Accepted: headers to send with the answer (a renewal's carry the fresh URL).
open(url: URL, message: (exp: string) => string): CapabilityOpened;RequestContext
interface
One request, as one plugin sees it.
interface RequestContext<Options = undefined, State = undefined> {/*…*/}RequestContext.event
Kit's own event, untouched.
readonly event: RequestEvent;RequestContext.path
The decoded pathname.
readonly path: string;RequestContext.state
This plugin's per-request state, from its state().
readonly state: State;RequestContext.options
readonly options: Options;RequestContext.app
readonly app: ServerApp<Options>;RequestContext.facts
The request's shared facts.
readonly facts: RequestFacts;RequestContext.routeConfig
This request's value for a key this plugin claimed inside Kit's config page option
(b.routeConfig(key): export const config = { <key>: … } on a Kit page or layout, merged the
way Kit merges it, or a programmatic router's config), or undefined when nothing declares it.
routeConfig(key: string): unknown;RequestContext.renderHole
Render one signed hole in place, with this request's event (its cookies ride in): the hole's HTML, or null when it cannot render now (the stored fallback stays, the browser fetches).
renderHole(endpoint: string): Promise<string | null>;RequestContext.perVisitor
Name the first reason this render is per-visitor (later reasons do not replace it).
perVisitor(reason: string): void;RequestFacts
interface
FACTS — per-request shared state. A fact is filled by whoever learns it and read by whoever needs
it; neither side imports the other. Keyed by the Request (every request has its own record, made
on first touch, gone with the request), so a plugin's own code can reach them outside a hook too:
a flag() read during the render holds its request.
Plugins add their own facts by declaration merging:
declare module 'ogygia/plugin' { interface RequestFacts { myThing?: string } }
One global slot, not module scope: a plugin package and core can end up as two module instances of this file (src beside dist, a dual install), and a fact written through one must be read through the other.
interface RequestFacts {/*…*/}RequestFacts.storable
This render MAY be stored and served to others later (a plugin decided so before the render): the holes it mints get prerender-grade capabilities, and data-source receipts are kept.
storable: boolean;RequestFacts.perVisitor
The first reason this render is per-visitor (a flag read, a streamed promise, a personal cookie or header a plugin observed), named for the dev note — or null.
perVisitor: string | null;RequestFacts.streamed
The page streams load promises after the document.
streamed: boolean;RequestFacts.private
The response belongs to one browser (devtools switched on for it): never store or share it.
private: boolean;RequestFacts.answerForm
An app transform renders this router-fetched page as an ANSWER, a form a store keyed by path does not hold.
answerForm: boolean;RequestFacts.sources
Data-source receipts (s:<id>:<fp>) the render filed — collected only once a plugin asks for
them by setting a list here (a storable render's reverse index, a fragment's provenance).
sources: string[] | null;RequestFacts.assignments
Every flag decided for this request (name → variant): federation carries them as claims.
assignments: Record<string, string>;ServerApp
interface
The server process, as one plugin sees it (handed to setup, and on every ctx.app).
interface ServerApp<Options = undefined> {/*…*/}ServerApp.name
The plugin's name (its package name).
readonly name: string;ServerApp.options
The options the app gave the plugin in vite.config (JSON).
readonly options: Options;ServerApp.notify
Call a shared hook on every plugin that has it.
readonly notify: Notify;ServerApp.ask
Ask a shared hook: the first answer that is not undefined, in plugin order.
readonly ask: Ask;ServerApp.facts
A request's facts (made on first touch) — for the plugin's own code outside a hook.
facts(request: Request): RequestFacts;ServerApp.current
The facts of the request the running code belongs to (a load, a render), or null outside one.
current(): RequestFacts | null;ServerApp.sign
Sign message with the app's region key (HMAC): a capability only this app can mint.
sign(message: string): string;ServerApp.verify
Check a signature sign made (constant-time).
verify(message: string, signature: string): boolean;ServerApp.capability
Signed capabilities for an endpoint the plugin serves, held to the rules of region holes.
readonly capability: PluginCapabilities;ServerApp.devtools
Record a server-realm event into the current request's devtools stream (a no-op unless devtools is on for that browser).
devtools(event: DevtoolsEventInput): void;ServerApp.warn
Warn once per message, prefixed with the plugin's name.
warn(message: string): void;ServerHooks
interface
The shared hooks. Each is optional on a plugin; the signature is the contract.
interface ServerHooks {/*…*/}ServerHooks.invalidate
Drop every stored page carrying any of tags (a peer said they are stale). Never re-announced.
invalidate(tags: string[]): void | Promise<void>;ServerHooks.invalidated
Stored pages were dropped: tags names what (p:<path>, s:<source>:<fp>), or 'all'.
invalidated(tags: string[] | "all"): void | Promise<void>;ServerHooks.lastBuild
Record version as the build the shared store last saw; answer the one before (or undefined).
lastBuild(version: string): string | undefined | Promise<string | undefined>;CapabilityOpened
type
What capability.open decided.
type CapabilityOpened = {
ok: true;
headers: Record<string, string>;
} | {
ok: false;
response: Response;
};DevtoolsEventInput
type
What a call site passes to emit — the envelope is added by the bus.
type DevtoolsEventInput = CompileEventInput | ServerEventInput | WireEventInput | RuntimeEventInput | HubEventInput | NavEventInput;ResolveOptions
type
Kit's resolve() options (a transformPageChunk, filterSerializedResponseHeaders, …).
type ResolveOptions = NonNullable<Parameters<Parameters<Handle>[0]["resolve"]>[1]>;RouteConfig
type
A route's merged config, as far as the claimed keys go.
type RouteConfig = Readonly<Record<string, unknown>>;RouteConfigMatcher
type
Per matcher: undefined = not mine (ask the next one, then the file world) · an object = mine,
with this merged config (a claimed key it lacks answers undefined, never the file world).
type RouteConfigMatcher = (pathname: string) => RouteConfig | undefined;ServerPlugin
type
A plugin's server half: lifecycle hooks plus any shared hooks it implements.
type ServerPlugin<Options = undefined, State = undefined> = Partial<ServerHooks> & {
setup?(app: ServerApp<Options>): void;
state?(): State;
handle?(ctx: HandleContext<Options>, next: (options?: ResolveOptions) => Promise<Response>): MaybePromise<Response>;
request?(ctx: RequestContext<Options, State>): MaybePromise<void | Response>;
page?(ctx: RequestContext<Options, State>): MaybePromise<void | Response>;
response?(ctx: RequestContext<Options, State>, response: Response): MaybePromise<void | Response>;
done?(ctx: RequestContext<Options, State>): void;
};