/**
* Route DSL (SPEC.md §41).
*
* One declaration is the runtime and the documentation at once: it validates the
* request, types the handler, serializes the response, and describes itself to the
* Schema Registry, from which OpenAPI, the API Explorer and the SDK are generated
* (SPEC.md §3.7, §42, §121).
*/
import type { Actor, AssemoraContext } from '@assemora/core';
import { type InferShape, type JsonSchema, type Schema, type Shape } from '@assemora/schema';
export type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete';
/** An application error a route can answer with (SPEC.md §46). */
export type ErrorDescriptor = {
readonly code: string;
readonly status: number;
readonly description?: string;
};
export type RouteRequest
= {
readonly params: InferShape
;
readonly query: InferShape;
readonly body: InferShape;
readonly headers: Readonly>;
readonly actor: Actor | undefined;
readonly context: AssemoraContext;
/**
* The adapter's own request object. An advanced escape hatch and deliberately
* `unknown`: Fastify must not become part of a handler's type (SPEC.md §40, §41).
*/
readonly request: unknown;
};
export type RouteDefinition = {
readonly params?: P;
readonly query?: Q;
readonly body?: B;
/** The shape of a successful answer. Used to serialize it and to document it. */
readonly response?: Shape | Schema;
readonly auth?: boolean;
/**
* What the audit log should call this door (SPEC.md §67).
*
* `rest` unless a route says otherwise. The MCP endpoint says `mcp`, because "an
* agent did this through MCP" and "somebody did this over REST" are the two
* things that column exists to tell apart.
*/
readonly source?: AssemoraContext['source'];
readonly status?: number;
readonly description?: string;
readonly tags?: readonly string[];
readonly errors?: readonly ErrorDescriptor[];
/**
* How many bytes this route accepts, when the server's own ceiling is the wrong one
* (SPEC.md §85).
*
* Per route rather than one number for the process, because the route that takes an
* upload is the only one that needs the wide ceiling: a limit generous enough for a
* photograph is a memory amplifier on every other address, and every address is what
* a single server-wide number widens.
*/
readonly bodyLimit?: number;
handler(request: RouteRequest): Promise | R;
};
export type Route = {
readonly node: 'route';
readonly method: HttpMethod;
readonly path: string;
readonly params: Schema | undefined;
readonly query: Schema | undefined;
readonly body: Schema | undefined;
readonly response: Schema | undefined;
readonly auth: boolean;
readonly source: AssemoraContext['source'] | undefined;
/**
* The API version this route was published under, if any (SPEC.md §47).
*
* Set by `server.version()`, never by a route declaration: a route says what it does
* and a server says where it lives. The path already carries the version — this is
* so a reader can group by it without parsing one.
*/
readonly version: string | undefined;
readonly status: number;
readonly description: string | undefined;
readonly tags: readonly string[];
readonly errors: readonly ErrorDescriptor[];
/** What this route accepts, and the server's ceiling when it is `undefined`. */
readonly bodyLimit: number | undefined;
handler(request: RouteRequest): Promise | unknown;
};
/** How a route describes itself in the Schema Registry (SPEC.md §42). */
export type RouteDescriptor = {
readonly name: string;
readonly method: HttpMethod;
readonly path: string;
readonly description?: string;
readonly tags: readonly string[];
readonly auth: boolean;
readonly status: number;
readonly params?: JsonSchema;
readonly query?: JsonSchema;
readonly body?: JsonSchema;
readonly response?: JsonSchema;
readonly errors: readonly ErrorDescriptor[];
readonly module?: string;
/** Which API version published it, for a reader that groups without parsing paths. */
readonly version?: string;
/**
* The largest body it accepts, in bytes, when it is not the server's own.
*
* Described rather than only enforced: OpenAPI and the SDK are generated from this,
* and a document that promises an upload the server answers `413` to is a document
* that lies about the one thing the caller has to size (SPEC.md §42, §44).
*/
readonly bodyLimit?: number;
};
declare module '@assemora/core' {
interface RegistrySections {
routes: RouteDescriptor;
}
}
/**
* ```ts
* route.post('/auth/login', {
* body: { email: email(), password: string().min(8) },
* response: { token: string() },
* handler: async ({ body }) => ({ token: await login(body) }),
* })
* ```
*/
export declare const route: {
get: (path: string, definition: RouteDefinition
) => Route;
post:
(path: string, definition: RouteDefinition
) => Route;
put:
(path: string, definition: RouteDefinition
) => Route;
patch:
(path: string, definition: RouteDefinition
) => Route;
delete:
(path: string, definition: RouteDefinition
) => Route;
};
/** `POST /auth/login` → `post /auth/login`, the name the registry keys on. */
export declare const routeName: (method: HttpMethod, path: string) => string;
export declare const describeRoute: (definition: Route, module?: string) => RouteDescriptor;
//# sourceMappingURL=route.d.ts.map