/** * Declare a middleware. Export as many as you like from `middleware.ts`; each * one carries its own `match`. * * ```ts * import { defineMiddleware } from '@voltro/web/middleware' * * export const session = defineMiddleware({ * match: { under: '/app' }, * run: async (req) => { * const fresh = await refreshSession(req.cookies['sb-session']) * if (!fresh) return * return { setCookies: [{ name: 'sb-session', value: fresh.cookie }] } * }, * }) * ``` * * **At most one middleware may match a given route.** Two hooks writing one * `authorization` header have no defensible winner, and picking one silently is * how a render gets the wrong credential — so an overlap refuses the boot and * names the route, rather than resolving itself by declaration order. */ export declare const defineMiddleware: (spec: { readonly match?: MiddlewareMatch; readonly run: Middleware["run"]; }) => Middleware; /** Is this export a middleware? Used by the boot path to pick middlewares out * of a module that also exports helpers. */ export declare const isMiddleware: (value: unknown) => value is Middleware; /** A middleware, as `defineMiddleware` produces it. */ export declare interface Middleware { /** * The marker the boot path recognises. A plain string, not a class: the serve * bundle inlines the framework, so a middleware built against the app's copy * of `@voltro/web` crosses an instance boundary and `instanceof` would not * survive it — the same reason the core table registry is keyed by * `Symbol.for`. */ readonly _tag: 'VoltroMiddleware'; readonly match?: MiddlewareMatch; readonly run: (req: MiddlewareRequest) => MiddlewareResult | undefined | Promise; } /** A cookie to write on the RESPONSE. Serialised by the boot path, so an app * never hand-builds a `Set-Cookie` string (and never forgets `HttpOnly`). */ export declare interface MiddlewareCookie { readonly name: string; readonly value: string; readonly path?: string; /** `0` or less DELETES, in the browser and in this render's own view of the * jar — a hook that signs someone out renders them signed out. */ readonly maxAge?: number; readonly httpOnly?: boolean; readonly secure?: boolean; readonly sameSite?: 'strict' | 'lax' | 'none'; } /** * Where a middleware runs. Omit it entirely and the middleware runs on every * server-rendered route, which is the right default for a single one. * * Every path here is a ROUTE path (`/app`, `/notes/[id]`) and is validated * against the app's routes at boot. */ export declare interface MiddlewareMatch { /** Route subtree(s). `/app` matches `/app` and everything below it, on * segment boundaries — never `/application`. */ readonly under?: string | ReadonlyArray; /** Exact route patterns, as the router spells them: `/notes/[id]`. */ readonly routes?: ReadonlyArray; /** Subtrees or exact patterns to subtract from the two above. */ readonly except?: ReadonlyArray; /** * Also run on requests that matched NO page — assets, and paths the router * does not know. * * Off by default and deliberately opt-in: forgetting to EXCLUDE assets is the * failure mode this whole vocabulary exists to remove, so reaching them has to * be something you asked for. Note what a middleware can still do there: there * is no render and no rpc call on an asset request, so `headers` has nothing * to act on and only `setCookies` takes effect. */ readonly assets?: boolean; } /** The request, as the middleware sees it. Read-only — everything a middleware * can CHANGE is in the return value, so a reader sees the whole effect in one * place instead of hunting for mutations. */ export declare interface MiddlewareRequest { /** Matched path, no query string. */ readonly pathname: string; /** Raw query string including the leading `?`, or `''`. */ readonly search: string; /** Incoming request headers, lowercased keys. */ readonly headers: Readonly>; /** Parsed `Cookie` header, for the common case. */ readonly cookies: Readonly>; /** * The ROUTE PATTERN this request matched — `/notes/[id]`, not `/notes/42`. * * `undefined` only when the request matched no page at all, which a * middleware can reach solely by declaring `assets: true`. Everywhere else it * is a string, so a middleware that branches on the route does not need a * fallback for a case it cannot be called in. */ readonly route: string | undefined; } export declare interface MiddlewareResult { /** * Headers to attach to the rpc calls this render makes — the loader's * `ctx.query` AND every `preload` entry, which is the point: they are bound * from one cookie string before any loader runs, so a layout loader renewing * the session cannot reach the preload. * * MERGED over the request's own headers, not replacing them. Only auth-shaped * names (`authorization`, `x-tenant`, `x-voltro-*`) are forwarded to the api. */ readonly headers?: Readonly>; /** Cookies to write on the response. Applied to THIS render's cookie jar too * — see `MiddlewareCookie`. */ readonly setCookies?: ReadonlyArray; } export { }