import { type MetaRoot } from "@metaobjectsdev/metadata"; import { type RelationMap } from "../relation-resolver.js"; import { type OutputLayout } from "../import-path.js"; import type { PkInfo } from "../pk-resolver.js"; import { type FieldShape } from "./api-field-shape.js"; export type ApiSymbolKind = "model" | "data-access" | "rest" | "validation" | "extractor" | "render" | "relation" | "callable" | "rest-hono" | "prompt"; export interface ApiSymbol { /** The exact symbol the real generator emits (function/type/schema name, or * a "METHOD /path" for a REST endpoint). Never invented. */ name: string; kind: ApiSymbolKind; /** * The module specifier an adopter imports this symbol from — the generated * file's path WITHOUT the `.ts` extension, exactly as the EMITTING generator * writes it (so it can't drift): e.g. `Product.queries`, `Product`, * `ProductSummary.extractor`, `ProductSummary.render`. Package layout folds * entity-derived modules under the package path (`acme/shop/Product.queries`) * iff the emitting generator does (it keys off the entity's OWN package). * * REST symbols are NOT importable functions — their importPath is the entity's * routes MODULE; `registrar` carries the camelCase `Routes` handler an * adopter mounts (`await (fastify)`) to wire the endpoints. */ importPath: string; /** REST-only: the route-registrar function exported from `importPath` that an * adopter mounts to wire the endpoints (`Routes`). Undefined for * importable-symbol kinds (their `name` IS the import). */ registrar?: string; /** A human-readable one-line signature (composed; the param/return SHAPE * mirrors the generated code). For REST symbols this is "METHOD /path". */ signature: string; /** Parameter descriptions, when meaningful. */ params?: string[]; /** Return-type description (e.g. "Product | null", "string", "EmailDocument"). */ returns?: string; /** When/why the symbol throws, if it does. */ throws?: string; /** One-line "what you use this for" prose. */ usage: string; /** Optional usage example snippet. */ example?: string; /** * The field SHAPE this symbol's payload carries — name + TS type + optionality * per field — so both renderers (human field table + agent inline shape) can * show WHAT fields to pass, not just the type NAME. Accurate by construction: * derived by REUSING the real generators' field walks (api-field-shape.ts), so * the api-docs accuracy gate can assert the documented field set == the emitted * one. Attached to: * • model → the entity's inferred fields (model line / GET response); * • create payload → the InsertSchema field set (create / POST body); * • update payload → the UpdateSchema field set (update / PATCH body); * • extractor payload → the @payloadRef VO interface (extract's return). * Undefined for symbols with no documented payload shape (e.g. deleteById, list). */ fields?: FieldShape[]; } export interface ApiUnitDoc { /** The metadata node name (entity or template). */ node: string; /** The node's EFFECTIVE package (own package OR the file-default captured at * parse time), used to place the unit's doc page + compute collision-safe * links to it in package layout. Undefined for a package-less node. */ package?: string | undefined; nodeKind: "entity" | "template"; symbols: ApiSymbol[]; /** * ONE worked, runnable example per unit — a concrete call site an agent (or a * human) can copy. ACCURATE BY CONSTRUCTION: it is composed from the SAME * symbol NAMES + importPaths this builder already documents (never invented) * and the SAME field SHAPES (T2) attached to the unit's payload symbols, with * example VALUES derived from each field's TS type (string→"…", number→1, * enum→a real member, …) — not entity-hardcoded. The body lines (without * imports) are what the agent form shows; the human page wraps the full block * (imports + body) in a fenced ```ts. * * For an ENTITY: a create→find→update→delete flow over the entity's own * CRUD helpers. For a TEMPLATE: an extract (parse LLM text) and/or render * (produce the document) call. Undefined when a unit has no runnable surface * (e.g. a bare value-object model with no queries/template). */ example?: UnitExample; } /** A worked example for a unit: the imports it needs (one `import { … } from "…"` * per module, in first-appearance order) + the body statements. Split so the * human page can render a full fenced block and the agent form can show a tight * body. */ export interface UnitExample { /** `import { a, b } from "Mod"` lines, deduped by module, reusing the symbols' * own importPaths (never re-derived). */ imports: string[]; /** The worked-flow statements (no imports), e.g. * `const created = await createProduct(db, { name: "…" });`. */ body: string[]; } export interface ApiModel { units: ApiUnitDoc[]; } /** Minimal context the builder needs. Accepts a full RenderContext OR just the * loaded root (pkMap is derived when absent). Keeping it structural means the * builder runs both inside a gen run and from a thin docs entrypoint. */ export interface ApiModelContext { loadedRoot: MetaRoot; pkMap?: Map; /** The relation map (relation-resolver) the entity file's relations() block is * derived from. Derived from `loadedRoot` when absent — keeps the builder * callable from a thin docs entrypoint. */ relationMap?: RelationMap; /** The output layout the codegen run uses. The per-symbol `importPath` mirrors * the emitting generator's own path computation under this layout (flat → * `Product.queries`; package → folded under the entity's package path iff the * generator folds). Defaults to "flat" (today's byte-identical placement). */ outputLayout?: OutputLayout; /** Whether to ALSO document the OPT-IN Hono CRUD variant (routesFileHono). * Hono is not in the default generator suite — it is an alternative wired by * the adopter — so its symbols are documented ONLY when the adopter opts in * (mirrors "match the generator's filters": don't over-document a surface the * run didn't configure). The Fastify REST surface is always documented (it is * the default-suite routes generator). Defaults to false. */ includeHonoRoutes?: boolean; } export declare function buildApiModel(root: MetaRoot, ctx: ApiModelContext): ApiModel; //# sourceMappingURL=api-model.d.ts.map