Server runtime + native renderer (napi-rs) for aihu SSR.
_buildStateScriptfunction _buildStateScript(root: unknown, opts: SsrOptions | undefined): stringBuild the `__aihu_state__` script for one finished render, or `''` when there is no state to ship (so a state-free page's HTML is byte-identical to today and the client's no-script hydration path is exercised).
_getNativeStateKindfunction _getNativeStateKind(): NativeState['kind']Returns the resolved native loader state kind.
_resetNativeStatefunction _resetNativeState(): voidReset the cached native state.
_setContextFnsfunction _setContextFns(set: (map: Map<symbol, unknown>) => void, clear: () => void): voidInject context activation/deactivation functions from
_setNativeStatefunction _setNativeState(state: NativeState): voidInject a native loader state (e.g.
_setStoreSerializerfunction _setStoreSerializer(fn: (() => Record<string, unknown>) | undefined): voidInject the store snapshot function from
attachSsrStringfunction attachSsrString<T extends () => unknown>( component: T, ssrString: unknown, props: unknown, ): TCarry a compiled string renderer across a props-binding wrapper.
badRequestfunction badRequest(message?: string): ResponsebuildChildRegistryfunction buildChildRegistry( components: Iterable<DiscoveredComponent>, onWarn?: (message: string) => void, ): ReadonlyMap<string, ChildModuleLike>Index discovered components by tag, after verifying the graph is acyclic.
checkEntitlementfunction checkEntitlement( registry: EntitlementsHandle, principal: EntitledPrincipal, scope: string, memo?: EntitlementMemo, url?: URL, platform?: PlatformContext, ): Promise<EntitlementVerdict>THE single live check, both axes (§4.6) — a named alias over the registry's `check` so call sites read as the spec does.
composeMiddlewarefunction composeMiddleware(middlewares: ReadonlyArray<Middleware>): MiddlewareCompose middleware in array order (index 0 = outermost).
createGovernedRegistryfunction createGovernedRegistry(): GovernedRegistryCreate the governed registry (§2.2).
createRequestRouterfunction createRequestRouter( manifest: RouteManifest, options?: RouterOptions, ): (req: Request) => Promise<Response>Create a fetch-API compatible request handler.
defineAihuConfigfunction defineAihuConfig(config: AihuConfig): AihuConfigDefine the aihu application configuration.
defineApiRoutefunction defineApiRoute(method: HttpMethod, pattern: string, handler: ApiHandler): RouteThe router enforces the method: a request to the matching pattern with a non-matching method receives `405 Method Not Allowed` with an `Allow` header listing all registered methods for that pattern.
defineGovernedFetchfunction defineGovernedFetch<T>(p: DataProvider<T>): DefinedGovernedFetch<T>The escape hatch (§4.7): hand-written data logic on a governed route.
defineLoaderfunction defineLoader<T>(fn: LoaderFn<T>): DefinedLoader<T>Loaders run before the route handler.
defineMiddlewarefunction defineMiddleware(handler: Middleware): MiddlewaredefineRoutefunction defineRoute<T = never>( pattern: string, handlerOrSubroutes: | RouteHandler | ((req: Request, ctx: LoadedRouteContext<T>) => Response | Promise<Response>) | ReadonlyArray<SubrouteTuple>, options?: RouteOptions<T>, ): Route | Route[]defineRoutesfunction defineRoutes(inputs: ReadonlyArray<RouteInput>): Route[]Register multiple routes in a single call.
defineStreamRoutefunction defineStreamRoute(handler: StreamRouteHandler): RouteDefine a route that streams text/plain chunks to the client.
deriveReadPolicyfunction deriveReadPolicy(read: unknown): ReadDerivationDerive every compliance-tier output signal from one `read` value.
extractReadValuefunction extractReadValue(extract: unknown): unknownPull the `read` member off a compiled `extract` object without trusting its shape.
governedHttpStatusfunction governedHttpStatus<T>(emission: GovernedEmission<T>): 200 | 401 | 403 | 500 | 503The HTTP status a governed emission serves with (read axis, §4.3 ladder).
injectIntoOutletfunction injectIntoOutlet(layoutHtml: string, content: string): string | nullInject rendered page content into a layout shell's `data-aihu-outlet` marker.
isCallAdvertisedfunction isCallAdvertised(extract: unknown): booleanIs this surface's agent surface advertisable at all — i.e.
isGovernedFetchfunction isGovernedFetch(x: unknown): x is DefinedGovernedFetch<unknown>Is this module export the branded governed-fetch escape hatch?
jsonfunction json(data: unknown, status?: number): ResponsematerializeGeneratedLoaderfunction materializeGeneratedLoader<T>( registry: GovernedRegistry, decl: GovernedRouteDataDecl, readValue: unknown, override?: DataProvider<T>, ): GeneratedLoader<T>Materialize the generated loader for one governed route (§3, §4.7): `data:` present and no sibling loader → registry provider; the `defineGovernedFetch` escape hatch supplies `override` — replacing step 4 ONLY.
methodNotAllowedfunction methodNotAllowed(allowed: ReadonlyArray<HttpMethod>): ResponsenormalizeGovernedDatafunction normalizeGovernedData(data: unknown): GovernedRouteDataDecl | 'malformed' | nullNormalize a `.route.json` `data` field fail-closed: `null` when absent, `'malformed'` when present but unparseable (a corrupted declaration is refused at boot, never rounded to ungoverned — the same posture as `deriveReadPolicy`'s `'malformed'`).
notFoundfunction notFound(): ResponsepassesRouteReadfunction passesRouteRead(principal: Principal, readValue: unknown): booleanRoute-level read decision for the T4 fallback path (a hard-`read` route with a plain `defineLoader` and no `data:`): may this principal receive the route's loader output at all?
renderToStreamfunction renderToStream( component: ComponentDescription, opts?: StreamOptions, ): ReadableStream<string>renderToStringasync function renderToString( component: ComponentDescription, opts?: SsrOptions, ): Promise<string>Render a component to an HTML string.
renderToStringNativeasync function renderToStringNative( component: ComponentDescription, opts?: SsrOptions, ): Promise<string>Render via the native (Rust) renderer when available, falling back to the TS implementation for edge / unsupported platforms.
resolveNativeStatefunction resolveNativeState(): NativeStateResolve (and cache) the native loader state.
resolveRequestPrincipalasync function resolveRequestPrincipal( req: Request, auth?: GovernedRequestAuth, platform?: PlatformContext, ): Promise<Principal>Step 1 for the SSR/E3 transports: settle THE principal for one request from its credential material (Authorization bearer, User-Agent, host-verified session).
routeHeadToSsrHeadfunction routeHeadToSsrHead( head: RouteHead | undefined, opts: RouteHeadLowerOptions = {}, ): HeadConfigLower a route's head metadata into a renderable `HeadConfig`, merged with an optional global head.
serverErrorfunction serverError(err?: unknown): ResponseSECURITY: In production (`__DEV__ === false`), the error message MUST NOT appear in the response body.
throwIfNativeFailedfunction throwIfNativeFailed(state: NativeState): voidConvert a `native-failed-loud` state into the formatted AihuNativeError and throw it.
validateGovernedBootfunction validateGovernedBoot( registry: GovernedRegistry | undefined, routes: readonly GovernedRouteCensusEntry[], opts?: { strict?: boolean }, ): voidValidate the registry against the compiled route census at `createServerRouter` construction (§2.3).
DEFAULT_ENTITLEMENT_TIMEOUT_MSconst DEFAULT_ENTITLEMENT_TIMEOUT_MSDefault live-resolver deadline (ms) — ratified Q1 (§7).
GOVERNED_RETRY_AFTER_SECONDSconst GOVERNED_RETRY_AFTER_SECONDS`Retry-After` seconds on `'unavailable'` responses (503).
AihuNativeErrorclass AihuNativeError extends ErrorAgentReadinessConfiginterface AgentReadinessConfig {
// ── Identity ─────────────────────────────────────────────────────────
readonly name: string
readonly version?: string
readonly summary?: string
// ── MCP endpoint ─────────────────────────────────────────────────────
/** When present: MCP card generated at `/.well-known/mcp/server-card.json`. */
readonly endpoint?: string
// ── Auth (opt-in, default: no-auth) ──────────────────────────────────
readonly auth?: {
readonly type: 'oauth2'
readonly authorizationUrl: string
readonly tokenUrl: string
readonly scopes?: ReadonlyArray<string>
readonly resourceUri?: string
}
// ── llms.txt ─────────────────────────────────────────────────────────
/**
* `intro` and each section's `body` carry free markdown prose, for llms.txt
* documents that must teach an agent something (a wire protocol, a route
* table, a reference grammar) before a link list means anything. Both are
* emitted verbatim and unescaped — authored content, never interpolated
* input. A section needs `body` or `links`; one with neither is omitted.
*/
readonly llmsIntro?: string
readonly llmsSections?: ReadonlyArray<{
readonly title: string
readonly links?: ReadonlyArray<{
readonly title: string
readonly url: string
readonly description?: string
}>
readonly body?: string
}>
readonly llmsOptional?: ReadonlyArray<{
readonly title: string
readonly url: string
readonly description?: string
}>
// ── robots.txt ───────────────────────────────────────────────────────
/**
* AI-agent crawl policy. Default: `'allow-agents'` (tiered, since
* @aihu-plugin/agent-readiness 2.1.0 / #430) — user-delegated fetchers
* (ChatGPT-User, OAI-SearchBot, DuckAssistBot, Applebot) are allowed;
* training/scraping crawlers (GPTBot, CCBot, Bytespider, …) are disallowed
* and must be opted in explicitly via `'allow-all'` or rules.
* Any other string value throws at robots.txt generation time.
*/
readonly aiAgents?:
| 'allow-agents'
| 'allow-all'
| 'deny-all'
| ReadonlyArray<{
readonly userAgent: string | ReadonlyArray<string>
readonly allow?: ReadonlyArray<string>
readonly disallow?: ReadonlyArray<string>
readonly crawlDelay?: number
}>
readonly standardBots?: ReadonlyArray<{
readonly userAgent: string | ReadonlyArray<string>
readonly allow?: ReadonlyArray<string>
readonly disallow?: ReadonlyArray<string>
readonly crawlDelay?: number
}>
/**
* GX Phase 3 (#437-GX): the COMPILED route table — structurally,
* `@aihu/router`'s `RouteDefinition[]` (e.g. `import routes from
* 'virtual:aihu-routes'`, the same array handed to `createServerRouter`).
* Each route's compiled `extract` declaration derives its robots.txt
* per-path directives and its presence in the agent-facing discovery
* documents (llms.txt). This is a conduit for compiled output, never a
* place to hand-author policy — the `.aihu` `extract:` declaration is the
* one source (spec §8). Omitted → global-policy-only robots.txt and no
* derived route listing, byte-identical to pre-GX output.
*/
readonly routes?: ReadonlyArray<{
readonly pattern: string
readonly extract?: { readonly read?: unknown; readonly call?: unknown }
}>
readonly sitemap?: string
/**
* robots.txt always ends with a `User-agent: * / Allow: /` block for
* predictable output. Set `false` to suppress it. (It is also skipped
* automatically when one of your own rules already targets `*`.)
*/
readonly wildcard?: boolean
// ── Skills ───────────────────────────────────────────────────────────
/** Manually declared MCP skills, merged with auto-derived from AgentMetadata.actions. */
readonly skills?: ReadonlyArray<{
readonly id: string
readonly name: string
readonly description: string
readonly inputSchema?: Record<string, unknown>
}>
// ── Site identity ─────────────────────────────────────────────────────
/** Canonical base URL (e.g. 'https://aihu.dev'). Used by A2A card, MCP discovery, and JSON-LD. */
readonly siteUrl?: string
// ── A2A Agent Card ────────────────────────────────────────────────────
/**
* Generate /.well-known/agent.json (Google A2A protocol).
* `true` derives name/description/url/version from top-level config fields.
* Object form overrides streaming/pushNotifications capabilities.
*/
readonly a2aCard?:
| true
| {
readonly capabilities?: {
readonly streaming?: boolean
readonly pushNotifications?: boolean
}
readonly skills?: ReadonlyArray<{
readonly id: string
readonly name: string
readonly description?: string
}>
}
// ── MCP Discovery ─────────────────────────────────────────────────────
/**
* Generate /.well-known/mcp.json.
* When true and `endpoint` is set, points to the endpoint URL.
* When true and `endpoint` is absent, falls back to /.well-known/mcp/server-card.json.
*/
readonly mcpDiscovery?: boolean
// ── Sitemap pages ─────────────────────────────────────────────────────
/**
* Pages to include in /sitemap.xml.
* When provided, the Vite plugin emits sitemap.xml at build time.
* The existing `sitemap` field (URL string) is used for robots.txt Sitemap: reference.
*/
readonly sitemapPages?: ReadonlyArray<{
readonly url: string
readonly lastmod?: string
readonly changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never'
readonly priority?: number
}>
// ── JSON-LD structured data ───────────────────────────────────────────
/**
* JSON-LD objects injected into <head> via <script type="application/ld+json">.
* If absent but `siteUrl` is set, a SoftwareApplication schema is auto-generated.
* Set `false` to disable auto-generation.
*/
readonly jsonLd?: ReadonlyArray<Record<string, unknown>> | false
}Canonical `AgentReadinessConfig` — the type of `AihuConfig.agent`.
AihuConfiginterface AihuConfig {
readonly server?: ServerConfig
readonly agent?: import('./agent-readiness-config.ts').AgentReadinessConfig
readonly routes?: RouteConfig
/**
* Plugins registered in this aihu project.
*
* Per Plugin Contract Spec §7.1-§7.2: plugins MUST be explicitly imported
* and registered here. Auto-discovery is forbidden.
*
* v0.2.1: type contract + registration plumbing only. The compiler
* dispatcher is a no-op until v0.3+ wires block parsers, macro lowerings,
* and hook execution. Admitting the field now lets plugin authors begin
* shaping `definePlugin({...})` calls against a stable type surface.
*/
readonly plugins?: ReadonlyArray<Plugin>
/**
* v0.6.5: Build target configuration.
* Controls whether aihu emits a client bundle, server bundle, or both.
* This field is read by the compiler/build tooling; it has no runtime effect.
*/
readonly build?: BuildConfig
/**
* Runtime rendering strategy. Defaults to SSR with hydration enabled —
* the agent-ready posture that gives LLMs, crawlers, and the aihu MCP
* server access to content without JavaScript execution.
*/
readonly rendering?: RenderingConfig
/**
* v1: `@aihu/ui` styled-recipe registry configuration.
* BUILD-TIME ONLY — consumed by the `aihu add` CLI and the css-engine
* scanner; it has no runtime/edge effect (matching the `build`/`plugins`
* field posture). Defaults are resolved by the CLI at read-time, so omitting
* `ui` entirely is valid.
*/
readonly ui?: UiConfig
}BuildConfiginterface BuildConfig {
/** Build target. Default: 'universal' */
target?: BuildTarget
}ChildCycleinterface ChildCycle {
/** The loop, first recurrence to recurrence: `a → b → c → a`. */
readonly cycle: ReadonlyArray<string>
/** Human-readable, for a build log. */
readonly message: string
}A component reference cycle, reported (not thrown) by `buildChildRegistry`.
ChildModuleLikeinterface ChildModuleLike extends SsrChildModule {
/** `__aihu_child_tags__` — every component tag this module's template references. */
readonly __aihu_child_tags__?: ReadonlyArray<string>
}A component module as the registry sees it: whatever `__aihu_schild` needs, plus the child-tag set that drives the walk.
CorsConfiginterface CorsConfig {
readonly origin: string | ReadonlyArray<string> | '*'
readonly methods?: ReadonlyArray<import('./types.ts').HttpMethod>
readonly headers?: ReadonlyArray<string>
readonly credentials?: boolean
readonly maxAge?: number
}DataProviderinterface DataProvider<T> {
/** The one data-source access. Runs ONLY after grant. Server-only, never bundled. */
fetch(ctx: GovernedFetchContext): Promise<T>
/** Optional: data for the declared `preview:` fields of the withheld state. */
preview?(ctx: GovernedFetchContext): Promise<Partial<T>>
}WHERE a governed type's data comes from.
DataSourceinterface DataSource<T> {
/** Current resolution state. */
readonly status: 'pending' | 'ready' | 'error'
/** The resolved value. Defined only when status === 'ready'. */
readonly value?: T
/** The rejection reason. Defined only when status === 'error'. */
readonly error?: unknown
/**
* Register a callback to be invoked exactly once when status transitions
* to 'ready' or 'error'. Returns a dispose function that cancels the
* registration if called before the transition fires.
*/
onReady(cb: () => void): () => void
}Describes an async data boundary that renderToStream can suspend on.
DefinedGovernedFetchinterface DefinedGovernedFetch<T> extends DataProvider<T> {
readonly _brand: 'DefinedGovernedFetch'
}The branded route-local provider — replaces step 4 ONLY, never the gate.
DefinedLoaderinterface DefinedLoader<T> {
readonly _brand: 'DefinedLoader'
/** @internal */
readonly fn: LoaderFn<T>
}DiscoveredComponentinterface DiscoveredComponent {
readonly tag: string
readonly module: ChildModuleLike
}One discovered component: the tag it registers under, and its module.
EntitlementContextinterface EntitlementContext {
/** Static meet already passed — never anonymous (§4.2). */
readonly principal: EntitledPrincipal
readonly scope: string
/** Never the raw credential. */
readonly request: { readonly url: URL }
/**
* Aborted when the registered `timeoutMs` (default 2000) elapses (§4.3).
* Deadline exhaustion is indistinguishable from a throw: `'unavailable'`.
*/
readonly signal: AbortSignal
/**
* The host runtime's per-request ambient state, as the adapter supplied it
* to `ServerRouter.handle(request, platform)`.
*
* A live entitlement check is a lookup against something — a subscriptions
* table in D1, a KV entry, a billing API behind a secret — and on a Worker
* every one of those arrives through the bindings. Without this member the
* resolver's only options are a module-scope handle (which does not exist
* on Workers: bindings are per-request) or an unauthenticated public fetch.
*
* `undefined` on the call axis (`@aihu/agent-service`'s `runGate`), which
* has no platform to offer, and whenever the caller passed none.
*/
readonly platform?: PlatformContext
}The live per-request entitlement check's context (§3.1, §4.3).
EntitlementRegistrationinterface EntitlementRegistration {
/** The LIVE per-request check. `true` = entitled now. Throw/timeout ⇒ withhold. */
resolve(ctx: EntitlementContext): Promise<boolean>
/** Resolver deadline in ms; default {@link DEFAULT_ENTITLEMENT_TIMEOUT_MS}. */
timeoutMs?: number
/** Positive-verdict TTL cache, per (scope, principal.sub). Negatives are NEVER cached (Q2). */
cache?: { ttlMs: number }
}Whether a principal may have the scope's authority RIGHT NOW.
ExtractDeclarationLikeinterface ExtractDeclarationLike {
readonly read?: unknown
readonly call?: unknown
}A compiled `extract` declaration as it appears on `.route.json` / agent-meta artifacts.
GovernedFetchContextinterface GovernedFetchContext {
readonly params: Record<string, string>
readonly url: URL
/**
* The SETTLED principal (§4.7): a hand-written fetch may further narrow on
* it, never widen. On the granted path this is always a verified principal;
* on the `preview` path it may be anonymous.
*/
readonly principal: Principal
/**
* The host runtime's per-request ambient state (Worker bindings, D1/KV/R2
* handles, secrets), as the adapter supplied it to
* `ServerRouter.handle(request, platform)`.
*
* A provider IS the data-source access, so this is the member that makes
* `data:` usable on a Worker at all: without it the only reachable data
* source is `fetch()` over the public internet. `undefined` when the caller
* passed no platform — a provider that needs one must say so, not assume.
*/
readonly platform?: PlatformContext
}The context a provider's `fetch`/`preview` receives.
GovernedLoadContextinterface GovernedLoadContext {
readonly params: Record<string, string>
readonly url: URL
/** Settled by `resolvePrincipal` in `handle` — step 1, once per request. */
readonly principal: Principal
/** The per-request memo (§4.4 layer 1). */
readonly entitlements: EntitlementMemo
/**
* The host runtime's per-request ambient state, threaded from
* `ServerRouter.handle(request, platform)`. Forwarded UNREAD to both the
* live entitlement resolver (step 3) and the provider (step 4) — the loader
* is a gate, not a consumer.
*/
readonly platform?: PlatformContext
}The context the generated loader runs in (principal settled by `handle`).
GovernedRegistryinterface GovernedRegistry extends EntitlementsHandle {
/** Register the data-source provider for a governed resource type. */
provider<T>(type: string, p: DataProvider<T>): GovernedRegistry
/**
* Register the live entitlement resolver for a scope — or declare the scope
* explicitly static with `'token-only'` (required in strict mode, §2.3).
* Registering a resolver makes EVERY use of the scope — both axes — live.
*/
entitlement(scope: string, r: EntitlementRegistration | 'token-only'): GovernedRegistry
/**
* Revocation composition hook (§4.4.3): purge every cached entitlement
* entry for `sub`, so "revoke this delegation" also drops cached
* entitlement warmth immediately (in-process; a shared cache store is an
* operator upgrade, not a framework default).
*/
onRevoke(sub: string): void
/** @internal Provider lookup for the generated loader / boot validation. */
providerFor(type: string): DataProvider<unknown> | undefined
/** @internal How a scope is registered — drives strict-mode boot validation. */
entitlementKind(scope: string): 'live' | 'token-only' | 'unregistered'
/** @internal Telemetry counters (G7d/G7e assertions). */
stats(): GovernedRegistryStats
}The single server-init registration surface (§4.1): a builder passed to `createServerRouter(routes, { governed })` and (same instance) to `AgentServiceOptions.entitlements`.
GovernedRegistryStatsinterface GovernedRegistryStats {
/** Live resolver invocations (post-memo, post-cache). */
readonly resolves: number
/** Cross-request TTL cache consults. */
readonly cacheReads: number
/** Cross-request TTL cache hits (positive, unexpired). */
readonly cacheHits: number
}GovernedRequestAuthinterface GovernedRequestAuth extends PrincipalGateDeps {
/**
* Resolve a HOST-VERIFIED session from the request (e.g. `getAuthState`
* over the cookie). Same injection posture as `PrincipalSource.session`:
* never pass unverified cookie contents.
*/
readonly resolveSession?: (
req: Request,
/**
* The host runtime's per-request ambient state. A cookie-session lookup is
* a store read — a KV `get`, a D1 `select`, a signing secret — and on a
* Worker all three arrive through the bindings. Optional second parameter,
* so every existing single-parameter resolver keeps working unchanged.
*/
platform?: PlatformContext,
) =>
| { readonly sub: string; readonly scopes: readonly string[] }
| null
| undefined
| Promise<{ readonly sub: string; readonly scopes: readonly string[] } | null | undefined>
}The host-injected auth material for principal resolution on the SSR path.
GovernedRouteCensusEntryinterface GovernedRouteCensusEntry {
readonly pattern: string
readonly data?: unknown
readonly extract?: { readonly read?: unknown; readonly call?: unknown } | undefined
}One row of the compiled route census the boot check reads.
GovernedRouteDataDeclinterface GovernedRouteDataDecl {
/** Names the governed resource type — the provider key. */
readonly type: string
/** Fields renderable in the locked state (declared public-tier, §4.5). */
readonly preview?: readonly string[]
}The normalized `data:` route declaration.
HeadConfiginterface HeadConfig {
readonly title?: string
readonly meta?: ReadonlyArray<MetaTag>
readonly links?: ReadonlyArray<LinkTag>
readonly lang?: string
/**
* Inline `<script>` elements (e.g. JSON-LD structured data). Backward
* compatible: omitted means no script tags are emitted, matching prior
* `buildHead` behavior.
*/
readonly scripts?: ReadonlyArray<ScriptTag>
}LinkTaginterface LinkTag {
readonly rel: string
readonly href: string
readonly [attr: string]: string | undefined
}LoadedRouteContextinterface LoadedRouteContext<T> extends RouteContext {
readonly loaderData: LoaderResult<T>
}LoaderResultinterface LoaderResult<T> {
readonly data: T
readonly error?: Error
readonly status: number
}MetaTaginterface MetaTag {
readonly name?: string
readonly property?: string
readonly content: string
readonly [attr: string]: string | undefined
}NativeAddoninterface NativeAddon {
renderTree(treeJson: string, hydratable: boolean): string
renderDocument(treeJson: string, headJson: string, hydratable: boolean): string
}PlatformDescriptorinterface PlatformDescriptor {
readonly platformId: string
readonly packageName: string
readonly nodeFile: string
}ReadDerivationinterface ReadDerivation {
readonly value: NormalizedReadValue
/**
* `'compliance'` for the anonymous values (`'all'`/`'agents'`/`'search'`/
* `'none'`), `'hard'` for the verified values — the spec §2.1 tier break.
* NOTE: the tier names what full enforcement WOULD be; this module only
* emits the compliance-tier signals either way (see module docblock).
*/
readonly tier: 'compliance' | 'hard'
/**
* Which declared crawler tiers the value admits. Meaningful for compliance
* values; all-false for hard values (no anonymous crawl access at all).
*/
readonly crawl: CrawlerTierAccess
/**
* Should robots.txt carry per-path directives for this surface?
* Compliance values: yes (`read:'none'` routes are served to humans, so
* their existence is not a secret — they DO get Disallow lines, spec §8).
* Hard values: NO — the path is simply absent from robots.txt. A Disallow
* line naming a governed path would advertise its existence (spec §8
* existence-advertising; the A-5 self-contradiction).
*/
readonly advertiseInRobots: boolean
/**
* Should the served response carry a noindex signal (`X-Robots-Tag:
* noindex`)? True for `read:'none'` and every hard/malformed value.
*/
readonly noindex: boolean
/**
* Listed in agent-facing discovery (llms.txt, MCP server-card tools)?
* Requires user-fetcher access: a surface that refuses user-directed AI
* fetchers must not be advertised to them.
*/
readonly agentDiscovery: boolean
/** Listed in search-facing surfaces (sitemap)? Requires searcher access. */
readonly searchDiscovery: boolean
}Everything the derived surfaces need to know about one `read` value.
RenderingConfiginterface RenderingConfig {
/**
* Default rendering mode for all routes.
* Default: 'ssr' — server-renders every page with hydration markers enabled.
*/
readonly mode?: RenderingMode
/**
* Emit `data-aihu-path` hydration markers on SSR output by default.
* Only applies when mode is 'ssr' or 'hybrid'.
* Default: true — enabling client-side takeover after first paint.
*/
readonly hydratable?: boolean
}Routeinterface Route {
readonly pattern: string
readonly handler: RouteHandler
readonly middleware?: ReadonlyArray<Middleware>
}RouteConfiginterface RouteConfig {
/** Default: `./routes.gen.ts`. */
readonly manifestPath?: string
}RouteContextinterface RouteContext {
readonly params: Record<string, string>
readonly url: URL
readonly env?: unknown
}RouteHeadinterface RouteHead {
readonly title?: string
readonly description?: string
readonly canonical?: string
readonly og?: {
readonly title?: string
readonly description?: string
readonly image?: string
readonly type?: string
readonly url?: string
}
readonly twitter?: {
readonly card?: string
readonly title?: string
readonly description?: string
readonly image?: string
readonly site?: string
}
/**
* JSON-LD structured data. The compiler captures the `@route` `jsonld` block
* VERBATIM as a raw JSON string, so this is typically a `string`. A
* pre-parsed object/value is also accepted and will be `JSON.stringify`'d.
*/
readonly jsonld?: unknown
}Per-route head metadata, as emitted by the compiler into `.route.json` and (post-B2) re-exported from `@aihu/router`.
RouteHeadLowerOptionsinterface RouteHeadLowerOptions {
/**
* Absolute site base URL (e.g. `https://example.com`) used to resolve
* relative `canonical` / `og.image` / `og.url` values into absolute URLs.
* Supplied by the wiring Builder (from `AihuConfig.site.url`). When absent,
* relative values are emitted as-is (documented, non-throwing).
*/
readonly siteUrl?: string
/**
* Global head defaults (typically from `app.head`). Route fields override
* global per field; `meta`/`links`/`scripts` arrays are key-merged with the
* route winning on conflicts.
*/
readonly globalHead?: HeadConfig
}RouteInputinterface RouteInput {
readonly pattern: string
readonly handler: RouteHandler
readonly options?: RouteOptions<never>
}A route config object for use with `defineRoutes`.
RouteManifestinterface RouteManifest {
readonly routes: ReadonlyArray<Route | ReadonlyArray<Route>>
readonly layouts?: Readonly<Record<string, ReadonlyArray<string>>>
}Route manifest produced by the file-based routing Vite plugin at build time.
RouteOptionsinterface RouteOptions<T = never> {
readonly loader?: DefinedLoader<T>
readonly middleware?: ReadonlyArray<Middleware>
}RouterOptionsinterface RouterOptions {
readonly middleware?: ReadonlyArray<Middleware>
readonly notFound?: RouteHandler
readonly env?: unknown
}ScriptTaginterface ScriptTag {
readonly type: string
readonly content: string
}A `<script>` element in the document head.
ServerConfiginterface ServerConfig {
readonly basePath?: string
readonly cors?: CorsConfig
/** Default: 1_048_576 (1 MB). */
readonly maxBodySize?: number
}SsrOptionsinterface SsrOptions {
/**
* When provided: output is a full HTML document.
* When absent: output is the component's inner HTML fragment only.
*/
readonly head?: HeadConfig
/**
* When true: rendered HTML includes hydration markers as data attributes.
* Default: false.
*/
readonly hydratable?: boolean
/**
* Injected signal serializer (an arbor `MountScope.serialize`-shaped
* snapshot: path key → value). When provided it REPLACES the default
* post-render signal collection and its result rides the `signals` key of
* the `__aihu_state__` envelope. When it throws, the error is swallowed
* and the `signals` channel is omitted.
*/
readonly serializer?: () => Record<string, unknown>
/**
* Optional per-render context setup hook. When provided alongside a prior call
* to _setContextFns, ssr.ts will:
* 1. Activate a fresh context Map (per-render isolation).
* 2. Call contextSetup(activateFn, deactivateFn) so the caller can do
* per-request setup — including replacing that map with a pre-populated
* one, which is the point of the hook.
* 3. Clear it in a finally block after the walk.
*
* ssr.ts never imports @aihu/context — the hard boundary is preserved.
* The activate/deactivate functions are wired via _setContextFns at startup.
*
* Pre-populating (the common case — providing a context no component in the
* tree provides, e.g. the router's `RouteContext` during SSG):
* import { setSsrContextMap, clearSsrContextMap } from '@aihu/context/ssr'
* import { _setContextFns, renderToString } from '@aihu/server'
* _setContextFns(setSsrContextMap, clearSsrContextMap)
* await renderToString(component, {
* contextSetup: (activate) => activate(new Map([[Token._id, value]])),
* })
*/
readonly contextSetup?: (
activate: (map: Map<symbol, unknown>) => void,
deactivate: () => void,
) => void
/**
* GX Phase 4 (#466) — the P5/I2s guard: mark this render as a GOVERNED
* tree. Governed trees are never streamed — encountering a `pending`
* `dataSource` boundary in a governed render is refused fail-closed
* (`GOVERNED_UNGATED` posture, 40-spec §10) rather than suspended, because
* a streamed governed subtree would be an emission path the generated
* loader never gated. Set by the router's governed `handle` path.
*/
readonly governed?: boolean
/**
* The component's light-DOM scope id (light-DOM leaf flip, LDF §10 step 3).
* When set, the render stamps `data-a="<id>"` on the component's ROOT
* element — the server-side mirror of `@aihu/runtime`'s `wrapClass`
* `connectedCallback` stamp — so the `@scope([data-a="<id>"]) to ([data-a])`
* blocks the build emitted for this component apply to the prerendered HTML
* at FIRST PAINT, before any client JS runs.
*
* This is not cosmetic. Without the stamp, a prerendered light-DOM
* component's scoped CSS (its entire `@style` block, media queries
* included) matches NOTHING until the component's chunk loads and the
* runtime stamps the host — measured on apps/docs-next under Lighthouse's
* throttled profile, the unstyled first paint pushed the LCP element below
* the fold and cost ~1.9s of LCP (3430ms → 1470ms once stamped).
*
* The value is the compiler-assigned id (`_lightScopeId` over the module
* id) — read it from the compiled module's `__aihu_light_scope__` export;
* never hand-compute it. Undefined (the default) stamps nothing —
* byte-identical output to the pre-option renderer.
*/
readonly lightScopeId?: string
/**
* Wrap the render in the component's own custom-element tag.
*
* WHY. SSR renders a component's TEMPLATE, not the component. Output is the
* template root — `<div class="dn-docs">` — while the client does
* `document.createElement('aihu-layout-docs')` and mounts the template
* INSIDE it. The two shapes never match, so the client cannot adopt the
* prerendered subtree and replaces it wholesale. Measured on apps/docs by
* tagging every prerendered node before hydration: 0 of 391 survived.
*
* Read the tag from the compiled module's `__aihu_tag__` export; never
* hand-derive it from a filename (`layoutTagFor`/`componentTagFor` are the
* client's own derivation and can drift from what `defineElement` actually
* registered).
*
* The wrapper carries `data-a` and the template root does NOT, because that
* is where the CLIENT puts it: `define-element.ts` stamps the HOST in its
* constructor. Its comment already asserts "a server-rendered element
* already carries `data-a`" — true only once the host exists in server
* output. The wrapper takes no `data-aihu-path`: the host is not a node in
* the component's arbor tree, so the template root keeps ROOT_PATH and every
* hydration path below it is unchanged.
*
* Undefined (the default) emits exactly what the pre-option renderer did.
*/
readonly wrapTag?: string
/**
* Child components, keyed by the custom-element tag they register under —
* the fix for `<site-header>` prerendering empty on every page.
*
* A component referenced inside another component's template renders as an
* empty shell, because the child's template lives in its own module and only
* materialises when the element upgrades in the browser. Supplying this map
* lets both renderers fill it in: the compiled string renderer receives it on
* its opts and the tree walker reads it here, and BOTH hand it to the same
* `__aihu_schild`, so a resolved child is serialized in exactly one place.
*
* PRE-RESOLVED, and a Map rather than a callback, for three reasons that all
* point the same way: module loading is async while the compiled fast path is
* synchronous (a callback would force the fast path off, which is the whole
* thing this design avoids); the cycle guard wants to run once over the tag
* graph rather than at every render; and resolution needs the caller's module
* loader, which `@aihu/server` has no business owning.
*
* How the caller builds it is step 5's job — SSG prerender via
* `ssrLoadModule`, the Workers handler via a generated tag→module manifest —
* and that is also where a cyclic tag graph must be REJECTED, loudly, before
* any render begins. (`__aihu_schild` carries a depth cap as a backstop, not
* as the guard.)
*
* NOT YET EMITTED, and deliberately not described here as if it were: the
* plan has the compiler export `__aihu_child_tags__` — each module's static
* set of referenced component tags — to drive that transitive walk. It is a
* prerequisite of step 5, not of this option, which works with any map the
* caller assembles. Naming an export in a doc comment before it exists is how
* `contextSetup` and `resolveComponent` each read as done while unbuilt.
*
* Omitted (the default) renders every reference as the empty element it
* renders today — byte-identical output, which is what makes this safe to
* ship ahead of the callers that populate it.
*/
readonly children?: ReadonlyMap<string, SsrChildModule>
/**
* @internal Nesting depth of this render below a top-level one, incremented
* per child rendered through the walker's escape-hatch path (§8).
*
* Only that path sets it. The string path keeps its own `__depth` on
* `SsrChildRenderOpts`; both cap at `_MAX_CHILD_DEPTH`, and both exist for
* the same reason — the registry only WARNS about a cyclic component graph
* now, so the renderers are what make a cycle finite.
*/
readonly __childDepth?: number
/**
* @internal Shared byte budget for child markup, threaded down the same path
* as `__childDepth`. Mutated in place; one object per top-level render.
*
* A depth cap bounds how deep a graph goes, not how wide. Three references
* repeated thirteen deep is `3^13` reference sites and megabytes of legal,
* acyclic output — see the `__budget` commentary in `ssr-string.ts`.
*/
readonly __childBudget?: { bytes: number; reported: boolean }
}StreamOptionsinterface StreamOptions extends SsrOptions {}Options for renderToStream.
UiConfiginterface UiConfig {
/** Source registry to pull recipes from. v1: the '@aihu/ui' package specifier. Default: '@aihu/ui'. */
readonly registry?: string
/** Directory `aihu add` copies recipe sources into. Default: './src/components/ui'. */
readonly target?: string
/** Active style pack name. Default: 'aihu-default'. */
readonly style?: string
/** Custom-element tag prefix for copied recipes. Default: 'aihu'. */
readonly prefix?: string
/** RESERVED for v2 multi-registry support — schema slot only, unimplemented. */
readonly registries?: Readonly<Record<string, string>>
}Configuration for the `@aihu/ui` styled-recipe registry, consumed by the `aihu add` / `aihu list` CLI subcommands and the css-engine scanner.
ApiHandlertype ApiHandler = (
req: Request,
ctx: import('./types.ts').RouteContext,
) => Response | Promise<Response>BuildTargettype BuildTarget = 'client' | 'server' | 'universal'Build target for the aihu application.
ComponentDescriptiontype ComponentDescription = (() => unknown) | { toHtml(): string }Accepts: 1.
CrawlerTiertype CrawlerTier = 'searcher' | 'user-fetcher' | 'training-crawler'The three declared-crawler tiers of the unified bot registry.
CrawlerTierAccesstype CrawlerTierAccess = Readonly<Record<CrawlerTier, boolean>>Per-tier crawl access derived from a `read` value.
Entitledtype Entitled<T> = T & { readonly $gx: { readonly entitled: true } }The granted shape: the declared type plus the reserved `$gx` discriminant.
EntitledPrincipaltype EntitledPrincipalEntitlementMemotype EntitlementMemoEntitlementsHandletype EntitlementsHandleEntitlementVerdicttype EntitlementVerdictGeneratedLoadertype GeneratedLoader<T> = (ctx: GovernedLoadContext) => Promise<GovernedEmission<T>>The generated loader — what the framework materializes per governed route.
GovernedEmissiontype GovernedEmission<T> =
| { readonly kind: 'granted'; readonly data: Entitled<T> }
| {
readonly kind: 'withheld'
readonly data: Withheld<T>
readonly decision: EmissionDecision
}
/** Provider failed AFTER grant (§4.3): an error state, never a locked state. */
| { readonly kind: 'error'; readonly status: 500 }What the generated loader emits (§3.1).
HttpMethodtype HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'LoaderFntype LoaderFn<T> = (ctx: RouteContext) => Promise<T>Middlewaretype Middleware = (req: Request, next: Next) => Response | Promise<Response>NativeStatetype NativeState =
| { kind: 'native-loaded'; addon: NativeAddon; descriptor: PlatformDescriptor }
| { kind: 'edge-skipped' }
| { kind: 'native-failed-loud'; descriptor: PlatformDescriptor; error: Error }
| { kind: 'unsupported-platform' }Nexttype Next = () => Promise<Response>NormalizedReadValuetype NormalizedReadValue =
| 'all'
| 'agents'
| 'search'
| 'none'
| 'verified'
| 'human'
| { readonly scope: string }
| 'malformed'The normalized `read` value.
PlatformContexttype PlatformContext = unknownThe per-request platform context — the host runtime's ambient state for one request, threaded from the adapter's `fetch` down to route loaders and the governed data path.
Principaltype PrincipalPrincipalGateDepstype PrincipalGateDepsPrincipalSourcetype PrincipalSourceRenderingModetype RenderingMode = 'ssr' | 'spa' | 'hybrid'Runtime rendering strategy for the application.
RouteHandlertype RouteHandler = (req: Request, ctx: RouteContext) => Response | Promise<Response>StreamRouteHandlertype StreamRouteHandler = (req: Request) => Promise<ReadableStream<string>>SubrouteTupletype SubrouteTuple =
| readonly [string, RouteHandler]
| readonly [string, RouteHandler, RouteOptions<never>]A subroute tuple for use with the prefix-group overload of `defineRoute`.
Withheldtype Withheld<T> = {
readonly $gx: {
readonly entitled: false
readonly reason: WithheldReason
}
readonly preview?: Partial<T>
}The withheld shape: NO field of `T` beyond the declared `preview:` subset.
WithheldReasontype WithheldReason = 'auth' | 'scope' | 'entitlement' | 'unavailable'Why a withheld response is withheld — the template's "sign in" vs "upgrade" vs "try later".