/** * Schema-derived TypeScript declarations for one screen — the static half of * the checks floor. * * The floor already knows every type a screen file can name: the Kit's props * are zod (`core` `kit/specs.ts`), a host component's props are the JSON Schema * derived once at composition (`NormalizedCatalogEntry.propsJsonSchema`), and a * query's result is the tool's declared `outputSchema`. This module turns all * of that into ambient declaration text so `tsc` — the real compiler, not a * bespoke walker — decides whether a screen names components that exist, sets * props that exist with types that fit, reaches fields the data really carries, * and aggregates over field names the rows really have. * * Everything here is DERIVED. No hand-written component list, no hand-written * prop list: the component vocabulary comes from `KIT_SCREEN_COMPONENT_NAMES` + the * catalog. There is no CALL vocabulary to declare any more — a `{...}` gap is a * JavaScript expression, so `invoices.data.reduce((t, r) => t + r.amount, 0)` * type-checks against the query's own declared result type with nothing * ambient in the way. The old `sum`/`count`/`group_by`/`pick` declarations * existed only to give tsc a shape for a closed dialect that no longer exists; * shipping them now would type-check calls the renderer cannot evaluate. * * Pure and deterministic: same input, byte-identical output. No compiler, no * I/O — {@link screenTscFindings} in screen-tsc.ts is the half that runs one. */ import { type JsonSchema } from "@vendoai/core"; import { type NormalizedCatalog } from "../../contract/index.js"; import type { ZodTypeAny } from "zod"; import { type HostToolInfo } from "./deps.js"; /** * Where a construct neither printer can model is announced. * * Both printers degrade an unknown construct to `any` — never to an error, * because a prop we cannot type precisely must not become a false finding. The * cost is invisible: the gate quietly stops checking that prop. A caller that * passes a sink learns which ones went dark; one that passes nothing keeps the * old silence. */ export type TypeNote = (reason: string) => void; /** One query a screen declares: ``. * Structurally the floor's own `tree.queries` entry. */ export interface ScreenQueryDeclaration { readonly name: string; readonly tool: string; } export interface ScreenTypingsInput { /** The host catalog. A schema-less entry is LEGAL (01-core §14) and gets a * permissive type — never an error. */ readonly catalog: NormalizedCatalog; /** The screen's declared queries, in source order. */ readonly queries: readonly ScreenQueryDeclaration[]; /** * tool name → the tool's DECLARED output JSON Schema * (`ToolDescriptor.outputSchema`). The only source: a declaration is the * host's contract, and nothing samples the host anymore. */ readonly toolOutputSchemas?: Readonly>; } /** The virtual path the declarations occupy in the check's program. */ export declare const SCREEN_TYPINGS_FILE = "/vendo-screen-typings.d.ts"; /** * What a slot may hold — an element tree, or a function that returns one. * * One law for every slot: the element, or the function that makes it. The VM * calls whichever it was given (`genui/component/vm-program.ts` `emitSlot`) and * emits the result where the slot sits, so `header={() => }` is as real as * `header={}`. * * The ARITY is the only thing that differs, and it is what this alias states: a * slot painted ONCE has no row to be a function of, so its function takes * nothing. A `(row) => …` written here would be called with no row and read a * field off `undefined` — the compiler is the only thing that can refuse it, and * {@link ROW_SLOT_TYPE} is the other arity. * * The wire printer keeps the permissive alias: a stored document's slot holds a * SERIALIZED element (an `$element`-sigilled object) that no type here * describes, and JSON cannot carry a closure, so neither form reaches it. */ export declare const SLOT_TYPE = "VendoSlot"; /** * The same law at the other arity: a slot the Kit paints once PER ROW, whose * function is handed the row. * * A per-row slot is painted once for every row, so a function of the row is the * only way to say something different in each — and it is what React trains * anyone to write. The VM calls it once per row and hands the component that * row's own element (`vm-program.ts` `emitSlot`), so the closure is real: the * handler inside `rowActions={(row) =>