API / @aihu/router

@aihu/router

App & routing

File-based router for the aihu meta-framework.

version
0.4.0
exports
44
values
23
types
21
01

__router_registerAfterGuard

function
function __router_registerAfterGuard( fn: Parameters<Router['registerAfterGuard']>[0], ): () => void
02

__router_registerBeforeGuard

function
function __router_registerBeforeGuard( fn: Parameters<Router['registerBeforeGuard']>[0], ): () => void
03

bindRouteSignalWriter

function
function bindRouteSignalWriter( value: RouteContextValue, write: (m: MatchResult | null) => void, ): void
04

componentTagFor

function
function componentTagFor(name: string): string

Normalize a component name to its custom-element tag (O1b).

05

composeRouterMiddleware

function
function composeRouterMiddleware(...middlewares: RouterMiddleware[]): RouterMiddleware
06

createPrefetcher

function
function createPrefetcher(mode: PrefetchMode): { attach: (a: HTMLAnchorElement, getMatch: () => MatchResult | null) => void detach: (a: HTMLAnchorElement) => void }

Build the DOM hooks needed for a prefetch-enabled `<a>`.

07

createRouter

function
function createRouter(routes: RouteDefinition[]): Router
08

createRouteSignal

function
function createRouteSignal( router: Router, initialPathname?: string, ): { read: () => MatchResult | null write: (m: MatchResult | null) => void dispose: () => void }

Wrap a `Router` in a reactive `Signal<MatchResult | null>` that updates on `popstate` events and on calls to `navigate()`.

09

createServerRouter

function
function createServerRouter( routes: RouteDefinition[], options?: ServerRouterOptions, ): ServerRouter

Construct a router with the server-side `handle(req)` request handler wired in.

10

defineRouterMiddleware

function
function defineRouterMiddleware(fn: RouterMiddleware): RouterMiddleware
11

layoutTagFor

function
function layoutTagFor(name: string): string

Runtime layout namespace convention (v0.7.5).

12function
async function navigate( href: string, opts: NavigateOptions = {}, ): Promise<'navigated' | 'cancelled' | 'no-router' | 'no-match'>

Programmatically navigate to `href` within the active router.

13

provideRouteContext

function
function provideRouteContext(value: RouteContextValue): void

Provide the route context (used by `<router>` SFC).

14

readAihuComponentTag

function
function readAihuComponentTag(f: string): string

O1b: Resolve the custom-element tag a component file registers under.

15

readRouteSidecar

function
function readRouteSidecar(f: string): RouteSidecar | null

v0.6.3: Read sibling .route.json sidecar.

16

scanComponents

function
function scanComponents(d: string): Record<string, string>

O1b: Recursively scan a components dir for `.aihu` files, mapping each file's normalized custom-element tag → absolute POSIX path.

17

scanLayouts

function
function scanLayouts(d: string): LayoutMap

v0.6.8: Scan layouts dir for .aihu files.

18

scanPages

function
function scanPages(root: string, pd: string): MiddlewareScan
19

useRoute

function
function useRoute(): MatchResult | null

Read the current matched route from the nearest `<router>` context.

20

useRouter

function
function useRouter(): Router | null

Get the active `Router` instance from the nearest `<router>` context.

21

viteRouterPlugin

function
function viteRouterPlugin(opts?: RouterPluginOptions): VitePlugin
22

RouteContext

const
const RouteContext

Context token for the active routing context.

23

viteRouterIntegration

const
const viteRouterIntegration
24

LayoutMap

interface
interface LayoutMap {
  [name: string]: string
}

Layout name → absolute file path (v0.6.8).

25

MiddlewareScan

interfaceagent
interface MiddlewareScan {
  /** Route files (non-underscore page files). */
  routes: string[]
  /**
   * Map from directory (absolute path) to its `_middleware.(ts|js)` file.
   * When the runtime composes a route, all middleware files from the route
   * file's ancestor directories (innermost first) should be applied.
   */
  middlewareByDir: Record<string, string>
}

v0.7.2: File-convention middleware discovered alongside routes.

26interface
interface NavigateOptions {
  /** Use `history.replaceState` instead of `pushState`. */
  replace?: boolean
  /** Wrap navigation in `document.startViewTransition()` when supported. */
  viewTransitions?: boolean
}
27

RouteContextValue

interface
interface RouteContextValue {
  router: Router
  /** Reactive read accessor for the current match. */
  current: () => MatchResult | null
  /** @internal — view transitions opt-in (read by `<a>`). */
  viewTransitions?: boolean
}

Value carried by `RouteContext`.

28

RouteMatchContext

interface
interface RouteMatchContext {
  url: URL
  params: Record<string, string>
  route: RouteDefinition
  signal: AbortSignal
  env?: Record<string, string>
}
29

RouterPluginOptions

interface
interface RouterPluginOptions {
  pagesDir?: string
  /** Directory to scan for layout files. Default: 'src/layouts' */
  layoutsDir?: string
  /** Directory to scan for component files. Default: 'src/components' */
  componentsDir?: string
  /**
   * Build-time route-metadata extractor — `@aihu/compiler`'s `compileRouteMeta`.
   * When provided, `genR` uses it to recover FULL `@route` metadata
   * (`head`/`middleware`/`params`/`ssr`/`layout`) for `.aihu` pages, since the
   * stdin compile path writes no `.route.json` sidecar to disk. Wired by
   * `@aihu/app` (which pairs the compiler + router plugins). When absent (e.g.
   * standalone `viteRouterIntegration` without the compiler), `genR` falls back
   * to reading `name`+`layout` from the `@route` block via regex.
   */
  compileRouteMeta?: (source: string, id: string) => RouteSidecar | null
}
30

RouteSidecar

interface
interface RouteSidecar {
  name?: string
  middleware?: string[]
  ssr?: boolean
  layout?: string
  /** Declared route param names, e.g. ["slug"]. Emitted by the Rust compiler from $prop declarations. */
  params?: string[]
  /**
   * B2: per-route `<head>` metadata (compiler `head:` block). Omitted entirely
   * when a route declares no `head:`. Threaded through to RouteDefinition.head
   * and the generated `virtual:aihu-routes` module.
   */
  head?: RouteHead
  /**
   * O1a: normalized custom-element tags this route's template references
   * (e.g. `["hn-comment", "user-card"]`). Emitted by the compiler's
   * `route.json`; threaded through to RouteDefinition.components so O1c can
   * register a route's components on navigation instead of eager imports.
   */
  components?: string[]
  /**
   * GX Phase 3 (#437-GX): the compiled `extract` policy (Phase 1 fan-out —
   * always present in a v0.1.12+ `.route.json`, the default recorded, never
   * implied by absence). Values stay `unknown` here: the compiler validated
   * them (C483), but sidecars can be hand-edited, so consumers normalize
   * fail-closed via `@aihu/server`'s `deriveReadPolicy`.
   */
  extract?: { read?: unknown; call?: unknown }
  /**
   * GX Phase 4 (#466): the compiled `data:` declaration — the governed
   * resource type binding (`type` keys the provider registry) and the
   * declared locked-state `preview:` fields. INTEGRATION SEAM (Builder A):
   * the Rust compiler parses the `@route` `data:` block and fans it into
   * `.route.json` beside `extract`; this field threads it (sidecar or
   * `compileRouteMeta`) into `RouteDefinition.data`, where
   * `createServerRouter` normalizes it fail-closed (`normalizeGovernedData`).
   */
  data?: { type?: string; preview?: string[] }
}

Fields from a .route.json compiler sidecar (v0.6.3).

31

ServerRouterOptions

interfaceagent
interface ServerRouterOptions {
  /**
   * The governed registry (`createGovernedRegistry()` from `@aihu/server`).
   * Passing it activates the governed pipeline for every route with a
   * compiled `data:` declaration, boot-validates the registry against the
   * route census (spec §2.3 — a typo is a boot refusal, not a 500 at first
   * request), and enables the E3 governed-data endpoint. Pass the SAME
   * instance to `AgentServiceOptions.entitlements` so both axes share one
   * live meaning per scope (§4.6).
   */
  readonly governed?: GovernedRegistry
  /**
   * Credential material for principal resolution on the SSR path: the
   * verifying auth plugin (Bearer JWTs), the anonymous-UA classifier, and a
   * host-verified session resolver (cookie path). Same injection posture as
   * `AgentServiceOptions.resolveAuth` / `PrincipalGateDeps`.
   */
  readonly auth?: GovernedRequestAuth
}

GX Phase 4 (#466, 70-governed-data-access): options for the server router.

32

AfterGuard

type
type AfterGuard = (to: MatchResult, from: MatchResult | null) => void

After-navigation callback.

33

BeforeGuard

type
type BeforeGuard = (
  to: MatchResult,
  from: MatchResult | null,
  next: NextFn,
) => void | Promise<void>

Before-navigation guard.

34

MatchResult

type
type MatchResult = {
  route: RouteDefinition
  params: Record<string, string>
  /** The matched pathname, e.g. '/posts/hello' */
  pathname: string
}
35

NextFn

type
type NextFn = (decision?: void | false | string) => void

Navigation guard `next` callback (RFC-A5-015).

36

PrefetchMode

type
type PrefetchMode = 'none' | 'hover' | 'visible'
37

RouteDefinition

type
type RouteDefinition = {
  pattern: string
  segments: RouteSegment[]
  module: () => Promise<RouteModule>
  // v0.6.3: fields from .route.json compiler sidecars
  name?: string
  middleware?: string[]
  ssr?: boolean
  layout?: string
  // B2: per-route <head> metadata from the .route.json `head:` block
  head?: RouteHead
  /** Normalized custom-element tags this route references; used for route-scoped registration (O1). */
  components?: readonly string[]
  /**
   * GX Phase 3 (#437-GX): the route's compiled `extract` policy from the
   * `.route.json` sidecar (`{ read, call }`, the Phase 1 fan-out). Values are
   * `unknown` — consumers normalize fail-closed (`deriveReadPolicy` in
   * `@aihu/server`). Drives the compliance-tier noindex signal in
   * `createServerRouter.handle` and the derived robots/discovery listings in
   * `@aihu-plugin/agent-readiness`. Absent on hand-built routes → the
   * resolved default (`read: 'agents'`, `call: 'anonymous'`) applies.
   */
  extract?: { readonly read?: unknown; readonly call?: unknown }
  /**
   * GX Phase 4 (#466): the route's compiled `data:` declaration from the
   * `.route.json` sidecar — `{ type, preview? }`, naming the governed
   * resource type (the provider key) and the fields renderable in the locked
   * state. Values are `unknown`: consumers normalize FAIL-CLOSED
   * (`normalizeGovernedData` in `@aihu/server` — malformed is a boot
   * refusal, never rounded to ungoverned). Presence makes the route governed:
   * `createServerRouter.handle` replaces the ungated `mod.loader` path with
   * the generated loader (70-governed-data-access §3).
   */
  data?: unknown
}
38

RouteHead

type
type RouteHead = {
  title?: string
  description?: string
  canonical?: string
  og?: {
    title?: string
    description?: string
    image?: string
    type?: string
    url?: string
  }
  twitter?: {
    card?: string
    title?: string
    description?: string
    image?: string
    site?: string
  }
  jsonld?: unknown
}

Per-route `<head>` metadata, threaded from the compiler's `.route.json` sidecar (`head:` block).

39

RouteModule

type
type RouteModule = {
  default: unknown
  /**
   * The route's data loader. Either a plain server loader (the shipped,
   * ungoverned contract — called with the matched params), or — GX Phase 4
   * (#466) — the `defineGovernedFetch` escape hatch (structurally typed here
   * so this browser-eligible file keeps zero `@aihu/server` imports): a
   * route-local provider the generated loader gates. On a `data:`-declared
   * route a PLAIN loader is a C486 build error (one data source per route).
   */
  loader?:
    | ((params: Record<string, string>) => Promise<unknown>)
    | { readonly _brand: 'DefinedGovernedFetch' }
}
40

Router

type
type Router = {
  match(pathname: string): MatchResult | null
  /**
   * Register a guard that runs before each navigation. Multiple guards run
   * in registration order. The first `next(false)` cancels navigation; the
   * first `next('/x')` redirects. Returns a dispose fn.
   */
  registerBeforeGuard(fn: BeforeGuard): () => void
  /**
   * Register a callback that runs after each navigation completes (after the
   * outlet has updated). Returns a dispose fn.
   */
  registerAfterGuard(fn: AfterGuard): () => void
  /**
   * Run the registered before-guard chain. Returns the final navigation
   * decision: 'continue', 'cancel', or { redirect: string }.
   * @internal — used by `<a>` and `<navigate>`.
   */
  runBeforeGuards(
    to: MatchResult,
    from: MatchResult | null,
  ): Promise<'continue' | 'cancel' | { redirect: string }>
  /**
   * Run the registered after-guard chain.
   * @internal — used by `<router>`.
   */
  runAfterGuards(to: MatchResult, from: MatchResult | null): void
}
41

RouterMiddleware

type
type RouterMiddleware = (
  ctx: RouteMatchContext,
  next: () => Promise<RouterResult>,
) => Promise<RouterResult>
42

RouterResult

type
type RouterResult =
  | { kind: 'continue' }
  | { kind: 'redirect'; location: string; status?: number }
  | { kind: 'cancel'; status?: number; body?: string }
43

RouteSegment

type
type RouteSegment =
  | { kind: 'static'; path: string }
  | { kind: 'param'; name: string }
  | { kind: 'catchall' }
44

ServerRouter

type
type ServerRouter = Router & {
  handle(req: Request): Promise<Response>
}