import type { z } from "zod"; /** * Structural interface that `validatePlan` and `generatePrompt` accept. Renderer * adapters (e.g. for json-render, A2UI) implement this by wrapping their native * component-declaration format. The default implementation is `defineUIAdapter` * below. * * Binding is not represented in this interface. Props that accept `{ $bind }` * references declare so structurally in their Zod schema (see `bindable()`). * Adapters just hand back the schemas; detection is structural. */ export interface UIAdapter { hasComponent(id: string): boolean; getComponentIds(): string[]; getDescription(id: string): string | undefined; getProps(componentId: string): z.ZodObject | undefined; } export interface UIComponentEntry< P extends z.ZodObject = z.ZodObject, > { id: string; description?: string; props: P; } /** * Default adapter implementation, useful for tests and for renderers that don't * have a native component-declaration format. Most users should reach for a * renderer-specific adapter (e.g. `toUIAdapter` from `@usecarte/json-render`) — * this is the slow path. */ export function defineUIAdapter(entries: ReadonlyArray): UIAdapter { const map = new Map(); for (const entry of entries) { if (map.has(entry.id)) { throw new Error(`Duplicate UI component id: ${entry.id}`); } map.set(entry.id, entry); } return { hasComponent: (id) => map.has(id), getComponentIds: () => [...map.keys()], getDescription: (id) => map.get(id)?.description, getProps: (id) => map.get(id)?.props, }; }