import { z } from 'zod'; /** * The API-surface artifact (design/api-surface.md): a vertical exports an * OPERATION CATALOG — operation name → summary + the SAME Zod schemas its * handlers parse — and this builder renders it as an OpenAPI 3.1 document, * one path item per operation on the platform's `/api/op/{name}` convention. * * One schema object is both the runtime validator and the documented contract, * so the document cannot drift from the enforcement (decision 22 cashed in). * The same build runs in two places on purpose: the vertical serves it live at * `/openapi.json`, and `tools/api-diff.mts` writes it to a checked-in * `openapi.json` that CI re-emits with `--check` — so a surface change cannot * merge without appearing in the PR diff (the D-22 human checkpoint, given the * same mechanical home as the permission diff). * * Zod 4's native `z.toJSONSchema` emits JSON Schema draft 2020-12 — exactly * OpenAPI 3.1's schema dialect — so there is no conversion dependency and no * second schema language anywhere in the pipeline. */ export interface ApiOperationDoc { /** One line, imperative — what invoking this operation does. */ summary: string; /** Optional longer prose (permissions nuance, state-machine rules). */ description?: string; /** Scalar/OpenAPI tag the operation groups under (e.g. 'Leave'). */ tag?: string; /** The request-body schema — the SAME object the handler parses. Omit = no body. */ input?: z.ZodType; /** True when the handler also accepts no body at all (filter-style reads). */ inputOptional?: boolean; /** The response schema, when the vertical declares one (adopted incrementally). */ output?: z.ZodType; /** * Where this operation is actually served, when the model declares it. * * Absent, the document describes the platform's `/api/op/{name}` invoke * convention (api-surface.md §2.2) — which was the only shape available * before operations declared `http`. Present, the document describes the REST * route the server derives from that same declaration, so the document and * the router cannot describe different surfaces. */ http?: { method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; path: string; }; /** * Declared by a paged read (#811). `output` then carries the ENTRY schema, and this * builder emits the query parameters and the `{ entries, nextCursor }` envelope — * so the wrapper is written once here rather than restated by every list operation. */ /** * Declared by an operation participating in optimistic concurrency (#129). * * Emits the `ETag` response header on every such operation, and on an UNSAFE * method additionally the `If-Match` request header plus a 412 response. Per * operation rather than document-wide, deliberately: `DOCUMENTED_ERROR_CODES` * excludes `precondition_failed` because *"documenting a failure that cannot * occur is worse than documenting none"*, and that stays true of every * operation which did not declare this. */ concurrency?: { over: string; idFrom: string; }; /** * `false` when the operation declared out of request idempotency (#116). * * Present only as a refusal, matching the declaration: every other unsafe * operation honours `Idempotency-Key`, so the header is documented on all of * them and this is what removes it from the one that would refuse it. A header * documented where it is refused is worse than one documented nowhere — a * client reads it and builds a retry it does not have. */ idempotency?: false; paged?: { /** Present on a handler-composed read: the ENTRY field the cursor walks. */ sortKey?: string; order?: 'asc' | 'desc'; total?: boolean; /** * Present on a kernel-composed read (#811): the entity and the vocabulary the * caller may choose from. `sortable[0]` is the default sort; each `filterable` * column becomes a documented query parameter. */ over?: { entity: string; sortable: readonly string[]; filterable?: readonly string[]; }; }; } /** operation name (module-namespaced, e.g. 'hr/create-employee') → its doc. */ export type ApiCatalog = Record; export interface ApiDocumentInfo { title: string; /** Deterministic — the module manifest's version, never a timestamp. */ version: string; description?: string; } /** * Render a catalog as an OpenAPI 3.1 document (a plain JSON-able object). * Pure and deterministic: same catalog in, byte-identical document out — that * is what lets the checked-in artifact double as a drift check. */ export declare function buildOpenApiDocument(info: ApiDocumentInfo, catalog: ApiCatalog): Record; /** * The API catalog, derived from the declared operations. * * Every field `ApiOperationDoc` needs — summary, input, output — is already on * the operation, declared once and compile-checked there. Writing the catalog by * hand is the same duplication as writing the route table by hand, and drifts * the same way: Meridian's catalog is 226 lines and Manyfold's 184, all of it * restating what the model says. * * **Why not Hono's OpenAPI support.** It would make the document a function of * the route registration rather than of the model, and route registration is * itself derived from the model — so the document would be derived from a * derivation, one step further from the thing a human approved. It also needs * the Zod schemas passed in anyway, which is the part we already have. The * usual reason to reach for it is that it gives you validation middleware for * free; we do not need that, because `input` IS the schema the handler parses. * * `tag` and `description` are prose, so they are supplied — the same split as * permission descriptions in `manifestOperations`. */ export declare function apiCatalogFrom(operations: Readonly>, prose?: Readonly>): ApiCatalog; //# sourceMappingURL=openapi.d.ts.map