/** * Public interface for the builder package. This module exports only the types * and functions that are part of the public recipe API. * * Workspace code should import these types via `@commontools/builder`. */ // Runtime constants - defined by @commontools/runner/src/builder/types.ts // These are ambient declarations since the actual values are provided by the runtime environment export declare const ID: unique symbol; export declare const ID_FIELD: unique symbol; // Should be Symbol("UI") or so, but this makes repeat() use these when // iterating over recipes. export declare const TYPE: "$TYPE"; export declare const NAME: "$NAME"; export declare const UI: "$UI"; // ============================================================================ // Cell Brand System // ============================================================================ /** * Brand symbol for identifying different cell types at compile-time. * Each cell variant has a unique combination of capability flags. */ export declare const CELL_BRAND: unique symbol; /** * Minimal cell type with just the brand, no methods. * Used for type-level operations like unwrapping nested cells without * creating circular dependencies. */ export type CellKind = | "cell" | "opaque" | "stream" | "comparable" | "readonly" | "writeonly"; // `string` acts as `any`, e.g. when wanting to match any kind of cell export type AnyBrandedCell = { [CELL_BRAND]: Kind; }; export type BrandedCell = AnyBrandedCell; // ============================================================================ // Cell Capability Interfaces // ============================================================================ // To constrain methods that only exists on objects export type IsThisObject = | IsThisArray | AnyBrandedCell | AnyBrandedCell>; export type IsThisArray = | AnyBrandedCell | AnyBrandedCell> | AnyBrandedCell> | AnyBrandedCell | AnyBrandedCell; /* * IAnyCell is an interface that is used by all calls and to which the runner * attaches the internal methods.. */ // deno-lint-ignore no-empty-interface export interface IAnyCell { } /** * Readable cells can retrieve their current value. */ export interface IReadable { get(options?: { traverseCells?: boolean }): Readonly; /** * Read the cell's current value without creating a reactive dependency. * Unlike `get()`, calling `sample()` inside a lift won't cause the lift * to re-run when this cell's value changes. */ sample(): Readonly; } /** * Writable cells can update their value. */ export interface IWritable> { set(value: T | AnyCellWrapping): C; update | AnyCellWrapping>)>( this: IsThisObject, values: V extends object ? AnyCellWrapping : never, ): C; push( this: IsThisArray, ...value: T extends (infer U)[] ? (U | AnyCellWrapping)[] : never ): void; remove( this: IsThisArray, ref: T extends (infer U)[] ? (U | AnyBrandedCell) : never, ): void; removeAll( this: IsThisArray, ref: T extends (infer U)[] ? (U | AnyBrandedCell) : never, ): void; } /** * Streamable cells can send events. */ export interface IStreamable { send(event: AnyCellWrapping): void; } // Lightweight HKT, so we can pass cell types to IKeyable<>. export interface HKT { _A: unknown; type: unknown; } export type Apply = (F & { _A: A })["type"]; /** * A key-addressable, **covariant** view over a structured value `T`. * * `IKeyableCell` exposes a single method, {@link IKeyableCell.key}, which selects a * property from the (possibly branded) value `T` and returns it wrapped in a * user-provided type constructor `Wrap` (default: `Cell<…>`). The interface is * declared `out T` (covariant) and is designed so that calling `key` preserves * both type inference and variance soundness. * * @template T * The underlying (possibly branded) value type. `T` is treated **covariantly**: * `IKeyableCell` is assignable to `IKeyableCell` when `Sub` is * assignable to `Super`. * * @template Wrap extends HKT * A lightweight higher-kinded “wrapper” that determines the return container for * selected fields. For example, `AsCell` wraps as `Cell`, while other wrappers * can project to `ReadonlyCell`, `Stream`, etc. Defaults to `AsCell`. * * @template Any * The “fallback” return type used when the provided key does not match a known * key (or is widened to `any`). This should usually be `Apply`. * * @remarks * ### Variance & soundness * The `key` signature is crafted to remain **covariant in `T`**. Internally, * it guards the instantiation `K = any` with `unknown extends K ? … : …`, so * the return type becomes `Any` (independent of `T`) in that case. For real keys * (`K extends keyof UnwrapCell`), the return type is precise and fully inferred. * * ### Branded / nested cells * If a selected property is itself a branded cell (e.g., `BrandedCell`), * the return value is a wrapped branded cell, i.e. `Wrap>`. * * ### Key inference * Passing a string/number/symbol that is a literal and a member of * `keyof UnwrapCell` yields precise field types; non-literal or unknown keys * fall back to `Any` (e.g., `Cell`). * * @example * // Basic usage with the default wrapper (Cell) * declare const userCell: IKeyableCell<{ id: string; profile: { name: string } }>; * const idCell = userCell.key("id"); // Cell * const profileCell = userCell.key("profile"); // Cell<{ name: string }> * * // Unknown key falls back to Any (default: Cell) * const whatever = userCell.key(Symbol()); // Cell * * @example * // Using a custom wrapper, e.g., ReadonlyCell * interface AsReadonlyCell extends HKT { type: ReadonlyCell } * type ReadonlyUserCell = IKeyableCell<{ id: string }, AsReadonlyCell, Apply>; * declare const ro: ReadonlyUserCell; * const idRO = ro.key("id"); // ReadonlyCell * * @example * // Covariance works: * declare const sub: IKeyableCell<{ a: string }>; * const superCell: IKeyableCell = sub; // OK (out T) */ export interface IKeyable { key( this: IsThisObject, valueKey: K, ): KeyResultType; } export type KeyResultType = [unknown] extends [K] ? Apply // variance guard for K = any : [0] extends [1 & T] ? Apply // keep any as-is : T extends AnyBrandedCell // wrapping a cell? delegate to it's .key ? (T extends { key(k: K): infer R } ? R : Apply) : Apply; // select key, fallback to any /** * Cells that support key() for property access - OpaqueCell variant. * OpaqueCell is "sticky" and always returns OpaqueCell<>. * * Note: And for now it always returns an OpaqueRef<>, until we clean this up. */ export interface IKeyableOpaque { key( this: IsThisObject, valueKey: K, ): unknown extends K ? OpaqueCell : K extends keyof UnwrapCell ? (0 extends (1 & T) ? OpaqueCell : UnwrapCell[K] extends never ? OpaqueCell : UnwrapCell[K] extends AnyBrandedCell ? OpaqueCell : OpaqueCell[K]>) : OpaqueCell; } /** * Cells that can be created with a cause. */ export interface ICreatable> { /** * Set a cause for this cell. Used to create a link when the cell doesn't have * one yet. * @param cause - The cause to associate with this cell * @returns This cell for method chaining */ for(cause: unknown): C; } /** * Cells that can be resolved back to a Cell. * Only available on full Cell, not on OpaqueCell or Stream. */ export interface IResolvable> { resolveAsCell(): C; getArgumentCell( schema?: S, ): Cell> | undefined; getArgumentCell(): Cell | undefined; } /** * Comparable cells have equals() method. * Available on comparable and readable cells. */ export interface IEquatable { equals(other: AnyCell | object): boolean; equalLinks(other: AnyCell | object): boolean; } /** * Cells that allow deriving new cells from existing cells. Currently just * .map(), but this will eventually include all Array, String and Number * methods. */ export interface IDerivable { map( this: IsThisObject, fn: ( element: T extends Array ? OpaqueRef : OpaqueRef, index: OpaqueRef, array: OpaqueRef, ) => Opaque, ): OpaqueRef; mapWithPattern( this: IsThisObject, op: RecipeFactory ? U : T, S>, params: Record, ): OpaqueRef; } export interface IOpaquable { /** deprecated */ get(): T; /** deprecated */ set(newValue: Opaque>): void; /** deprecated */ setSchema(schema: JSONSchema): void; } // ============================================================================ // Cell Constructor Interfaces // ============================================================================ /** * Generic constructor interface for cell types with static methods. */ export interface CellTypeConstructor< Wrap extends HKT, > { /** * Create a cell with a cause. * * Can be chained with .of() or .set(): * * const foo = Cell.for(cause).set(value); // sets cell to latest value * const bar = Cell.for(cause).of(value); // sets cell to initial value * * @param cause - The cause to associate with this cell * @returns A new cell */ for(cause: unknown): Apply; /** * Create a cell with an initial/default value. In a reactive context, this * value will only be set on first call. * * Can be chained with .for() to set a cause and initial value. * E.g. `const foo = Cell.for(cause).of(value)`. * * AST transformation adds `.for("foo")` in `const foo = Cell.of(value)`. * * Internally it just merges the value into the schema as a default value. * * @param value - The initial/default value to set on the cell * @param schema - Optional JSON schema for the cell * @returns A new cell */ of(value?: T, schema?: JSONSchema): Apply; /** * Create a cell with an initial/default value and a typed schema. * Type is inferred from the schema. * * @param value - The initial/default value to set on the cell * @param schema - JSON schema for the cell * @returns A new cell with type derived from schema */ of( value: Schema, schema: S, ): Apply>; /** * Compare two cells or values for equality after resolving, i.e. after * following all links in case we have cells pointing to other cells. * @param a - First cell or value to compare * @param b - Second cell or value to compare * @returns true if the values are equal */ equals(a: AnyCell | object, b: AnyCell | object): boolean; /** * Compare two cells or values for equality by comparing their underlying * links. No resolving. * * @param a - First cell or value to compare * @param b - Second cell or value to compare * @returns true if the values are equal */ equalLinks(a: AnyCell | object, b: AnyCell | object): boolean; } // ============================================================================ // Cell Type Definitions // ============================================================================ /** * Base type for all cell variants that has methods. Internal API augments this * interface with internal only API. Uses a second symbol brand to distinguish * from core cell brand without any methods. */ export interface AnyCell extends AnyBrandedCell, IAnyCell { } /** * Opaque cell reference - only supports keying and derivation, not direct I/O. * Has .key(), .map(), .mapWithPattern() * Does NOT have .get()/.set()/.send()/.equals()/.resolveAsCell() */ export interface AsOpaqueCell extends HKT { type: OpaqueCell; } export interface IOpaqueCell extends IAnyCell, ICreatable>, IKeyableOpaque, IDerivable, IOpaquable {} export interface OpaqueCell extends BrandedCell, IOpaqueCell {} export declare const OpaqueCell: CellTypeConstructor; /** * Full cell with read, write capabilities. * Has .get(), .set(), .update(), .push(), .equals(), .key(), .resolveAsCell() * * Note: This is an interface (not a type) to allow module augmentation by the runtime. */ export interface AsCell extends HKT { type: Cell; } export interface ICell extends IAnyCell, ICreatable>, IReadable, IWritable>, IStreamable, IEquatable, IKeyable, IDerivable, IResolvable> {} export interface Cell extends BrandedCell, ICell {} export declare const Cell: CellTypeConstructor; /** * Stream-only cell - can only send events, not read or write. * Has .send() only * Does NOT have .key()/.equals()/.get()/.set()/.resolveAsCell() * * Note: This is an interface (not a type) to allow module augmentation by the runtime. */ export interface AsStream extends HKT { type: Stream; } export interface Stream extends BrandedCell, IAnyCell, ICreatable>, IStreamable {} export declare const Stream: CellTypeConstructor; /** * Comparable-only cell - just for equality checks and keying. * Has .equals(), .key() * Does NOT have .resolveAsCell()/.get()/.set()/.send() */ export interface AsComparableCell extends HKT { type: ComparableCell; } export interface ComparableCell extends BrandedCell, IAnyCell, ICreatable>, IEquatable, IKeyable {} export declare const ComparableCell: CellTypeConstructor; /** * Read-only cell variant. * Has .get(), .equals(), .key() * Does NOT have .resolveAsCell()/.set()/.send() */ export interface AsReadonlyCell extends HKT { type: ReadonlyCell; } export interface ReadonlyCell extends BrandedCell, IAnyCell, ICreatable>, IReadable, IEquatable, IKeyable {} export declare const ReadonlyCell: CellTypeConstructor; /** * Write-only cell variant. * Has .set(), .update(), .push(), .key() * Does NOT have .resolveAsCell()/.get()/.equals()/.send() */ export interface AsWriteonlyCell extends HKT { type: WriteonlyCell; } export interface WriteonlyCell extends BrandedCell, IAnyCell, ICreatable>, IWritable>, IKeyable {} export declare const WriteonlyCell: CellTypeConstructor; // ============================================================================ // OpaqueRef - Proxy-based variant of OpaqueCell // ============================================================================ /** * OpaqueRef is a variant of OpaqueCell with recursive proxy behavior. * Each key access returns another OpaqueRef, allowing chained property access. * This is temporary until AST transformation handles .key() automatically. * * OpaqueRef> unwraps to Cell. */ export type OpaqueRef = [T] extends [AnyBrandedCell] ? T : & OpaqueCell & OpaqueRefInner; // Helper type for OpaqueRef's inner property/array mapping // Handles nullable types by extracting the non-null part for mapping type OpaqueRefInner = [T] extends [ArrayBuffer | ArrayBufferView | URL | Date] ? T : [T] extends [Array] ? Array> : [T] extends [AnyBrandedCell] ? T : [T] extends [object] ? { [K in keyof T]: OpaqueRef } // For nullable types (T | null | undefined), extract and map the non-null part : [NonNullable] extends [never] ? T // Handle nullable branded cells (e.g., (OpaqueCell & X) | undefined) - don't wrap : [NonNullable] extends [AnyBrandedCell] ? T : [NonNullable] extends [Array] ? Array> : [NonNullable] extends [object] ? { [K in keyof NonNullable]: OpaqueRef[K]> } : T; // ============================================================================ // CellLike and Opaque - Utility types for accepting cells // ============================================================================ /** * CellLike is a cell (AnyCell) whose nested values are Opaque. * The top level must be AnyCell, but nested values can be plain or wrapped. * * Note: This is primarily used for type constraints that require a cell. */ export type CellLike = AnyBrandedCell> & { [CELL_LIKE]?: unknown; }; type MaybeCellWrapped = | T | AnyBrandedCell | (T extends Array ? Array> : T extends object ? { [K in keyof T]: MaybeCellWrapped } : never); export declare const CELL_LIKE: unique symbol; /** * Helper type to transform Cell to Opaque in pattern/lift/handler inputs */ export type StripCell = T extends AnyBrandedCell ? StripCell : T extends ArrayBuffer | ArrayBufferView | URL | Date ? T : T extends Array ? StripCell[] : T extends object ? { [K in keyof T]: StripCell } : T; /** * Opaque accepts T or any cell wrapping T, recursively at any nesting level. * Used in APIs that accept inputs from developers - can be static values * or wrapped in cells (OpaqueRef, Cell, etc). * * Conceptually: T | AnyCell at any nesting level, but we use OpaqueRef * for backward compatibility since it has the recursive proxy behavior that * allows property access (e.g., Opaque<{foo: string}> includes {foo: Opaque}). */ export type Opaque = | T // We have to list them explicitly so Typescript can unwrap them. Doesn't seem // to work if we just say AnyBrandedCell | OpaqueRef | AnyCell | AnyBrandedCell | OpaqueCell | Cell | Stream | ComparableCell | ReadonlyCell | WriteonlyCell | (T extends Array ? Array> : T extends object ? { [K in keyof T]: Opaque } : T); /** * Helper type to extract the innermost Cell type from any number of OpaqueRef wrappers. * UnwrapOpaqueRefLayers> = Cell * UnwrapOpaqueRefLayers>> = Cell * UnwrapOpaqueRefLayers>>> = Cell * * Support for nested OpaqueRef layers is limited to 4 levels. */ type UnwrapOpaqueRefLayers4 = T extends OpaqueRef ? UnwrapOpaqueRefLayers3 : T; type UnwrapOpaqueRefLayers3 = T extends OpaqueRef ? UnwrapOpaqueRefLayers2 : T; type UnwrapOpaqueRefLayers2 = T extends OpaqueRef ? UnwrapOpaqueRefLayers1 : T; type UnwrapOpaqueRefLayers1 = T extends OpaqueRef ? U : T; /** * Matches any non-opaque Cell type (Cell, Stream, ComparableCell, etc.) that may be * wrapped in any number of OpaqueRef layers. Excludes OpaqueCell and AnyCell (since OpaqueCell extends AnyCell). */ type AnyCellWrappedInOpaqueRef = UnwrapOpaqueRefLayers4 extends BrandedCell ? UnwrapOpaqueRefLayers4 : never; /** * Recursively unwraps AnyBrandedCell types at any nesting level. * UnwrapCell>> = string * UnwrapCell }>> = { a: AnyBrandedCell } * * Special cases: * - UnwrapCell = any * - UnwrapCell = unknown (preserves unknown) */ export type UnwrapCell = // Preserve any 0 extends (1 & T) ? T // Unwrap AnyBrandedCell : T extends AnyBrandedCell ? UnwrapCell // Otherwise return as-is : T; /** * AnyCellWrapping is used for write operations (.set(), .push(), .update()). It * is a type utility that allows any part of type T to be wrapped in AnyCell<>, * and allow any part of T that is currently wrapped in AnyCell<> to be used * unwrapped. This is designed for use with cell method parameters, allowing * flexibility in how values are passed. The ID and ID_FIELD metadata symbols * allows controlling id generation and can only be passed to write operations. */ export type AnyCellWrapping = // Handle existing AnyBrandedCell<> types, allowing unwrapping T extends AnyBrandedCell ? AnyCellWrapping | AnyBrandedCell> // Handle arrays : T extends Array ? Array> | AnyBrandedCell>> // Handle objects (excluding null) : T extends object ? | { [K in keyof T]: AnyCellWrapping } & { [ID]?: AnyCellWrapping; [ID_FIELD]?: string } | AnyBrandedCell<{ [K in keyof T]: AnyCellWrapping }> // Handle primitives : T | AnyBrandedCell; // Factory types // TODO(seefeld): Subset of internal type, just enough to make it // differentiated. But this isn't part of the public API, so we need to find a // different way to handle this. export interface Recipe { argumentSchema: JSONSchema; resultSchema: JSONSchema; } export interface Module { type: "ref" | "javascript" | "recipe" | "raw" | "isolated" | "passthrough"; } export type toJSON = { toJSON(): unknown; }; export type Handler = Module & { with: (inputs: Opaque>) => Stream; }; export type NodeFactory = & ((inputs: Opaque) => OpaqueRef) & (Module | Handler | Recipe) & toJSON; export type RecipeFactory = & ((inputs: Opaque) => OpaqueRef) & Recipe & toJSON; export type ModuleFactory = & ((inputs: Opaque) => OpaqueRef) & Module & toJSON; export type HandlerFactory = & ((inputs: Opaque>) => Stream) & Handler & toJSON; // JSON types export type JSONValue = | null | boolean | number | string | JSONArray | JSONObject & IDFields; export interface JSONArray extends ArrayLike {} export interface JSONObject extends Record {} // Annotations when writing data that help determine the entity id. They are // removed before sending to storage. export interface IDFields { [ID]?: unknown; [ID_FIELD]?: unknown; } // Valid values for the "type" property of a JSONSchema export type JSONSchemaTypes = | "object" | "array" | "string" | "integer" | "number" | "boolean" | "null"; // See https://json-schema.org/draft/2020-12/json-schema-core // See https://json-schema.org/draft/2020-12/json-schema-validation // There is a lot of potential validation that is not handled, but this object // is defined to support them, so that generated schemas will still be usable. // TODO(@ubik2) When specifying a JSONSchema, you can often use a boolean // This is particularly useful for specifying the schema of a property. // That will require reworking some things, so for now, I'm not doing it export type JSONSchema = JSONSchemaObj | boolean; export type JSONSchemaObj = { readonly $ref?: string; readonly $defs?: Readonly>; /** @deprecated Use `$defs` for 2019-09/Draft 8 or later */ readonly definitions?: Readonly>; // Subschema logic readonly allOf?: readonly (JSONSchema)[]; // not validated readonly anyOf?: readonly (JSONSchema)[]; // not always validated readonly oneOf?: readonly (JSONSchema)[]; // not always validated readonly not?: JSONSchema; // Subschema conditionally - none applied readonly if?: JSONSchema; readonly then?: JSONSchema; readonly else?: JSONSchema; readonly dependentSchemas?: Readonly>; // Subschema for array readonly prefixItems?: readonly (JSONSchema)[]; // not always validated readonly items?: Readonly; readonly contains?: JSONSchema; // not validated // Subschema for object readonly properties?: Readonly>; readonly patternProperties?: Readonly>; // not validated readonly additionalProperties?: JSONSchema; readonly propertyNames?: JSONSchema; // not validated // Validation for any readonly type?: JSONSchemaTypes | readonly JSONSchemaTypes[]; readonly enum?: readonly Readonly[]; // not validated readonly const?: Readonly; // not validated // Validation for numeric - none applied readonly multipleOf?: number; readonly maximum?: number; readonly exclusiveMaximum?: number; readonly minimum?: number; readonly exclusiveMinimum?: number; // Validation for string - none applied readonly maxLength?: number; readonly minLength?: number; readonly pattern?: string; // Validation for array - none applied readonly maxItems?: number; readonly minItems?: number; readonly uniqueItems?: boolean; readonly maxContains?: number; readonly minContains?: number; // Validation for object readonly maxProperties?: number; // not validated readonly minProperties?: number; // not validated readonly required?: readonly string[]; readonly dependentRequired?: Readonly>; // not validated // Format annotations readonly format?: string; // not validated // Contents - none applied readonly contentEncoding?: string; readonly contentMediaType?: string; readonly contentSchema?: JSONSchema; // Meta-Data readonly title?: string; readonly description?: string; readonly default?: Readonly; readonly readOnly?: boolean; readonly writeOnly?: boolean; readonly examples?: readonly Readonly[]; readonly $schema?: string; readonly $comment?: string; // Common Tools extensions readonly [ID]?: unknown; readonly [ID_FIELD]?: unknown; // makes it so that your handler gets a Cell object for that property. So you can call .set()/.update()/.push()/etc on it. readonly asCell?: boolean; // marks values that are OpaqueRef - tracked reactive references readonly asOpaque?: boolean; // streams are what handler returns. if you pass that to another handler/lift and declare it as asSteam, you can call .send on it readonly asStream?: boolean; // temporarily used to assign labels like "confidential" readonly ifc?: { classification?: string[]; integrity?: string[] }; }; // LLM types matching Vercel AI SDK structure export type BuiltInLLMTextPart = { type: "text"; text: string; }; export type BuiltInLLMImagePart = { type: "image"; image: string | Uint8Array | ArrayBuffer | URL; }; export type BuiltInLLMToolCallPart = { type: "tool-call"; toolCallId: string; toolName: string; input: Record; }; export type BuiltInLLMToolResultPart = { type: "tool-result"; toolCallId: string; toolName: string; output: { type: "text"; value: string } | { type: "json"; value: any }; }; export type BuiltInLLMContentPart = | BuiltInLLMTextPart | BuiltInLLMImagePart | BuiltInLLMToolCallPart | BuiltInLLMToolResultPart; export type BuiltInLLMContent = string | BuiltInLLMContentPart[]; export type BuiltInLLMMessage = { role: "user" | "assistant" | "system" | "tool"; content: BuiltInLLMContent; }; // Image types from UI components export interface ImageData { id: string; name: string; url: string; data: string; timestamp: number; width?: number; height?: number; size: number; type: string; exif?: { // Core metadata dateTime?: string; make?: string; model?: string; orientation?: number; // Location gpsLatitude?: number; gpsLongitude?: number; gpsAltitude?: number; // Camera settings fNumber?: number; exposureTime?: string; iso?: number; focalLength?: number; // Dimensions pixelXDimension?: number; pixelYDimension?: number; // Software software?: string; // Raw EXIF tags raw?: Record; }; } export type BuiltInLLMTool = & { description?: string } & ( | { pattern: Recipe; handler?: never } | { handler: Stream | OpaqueRef; pattern?: never } ); // Built-in types export interface BuiltInLLMParams { messages?: BuiltInLLMMessage[]; model?: string; system?: string; stop?: string; maxTokens?: number; /** * Specifies the mode of operation for the LLM. * - `"json"`: Indicates that the LLM should process and return data in JSON format. * This parameter is optional and defaults to undefined, which may result in standard behavior. */ mode?: "json"; /** * Tools that can be called by the LLM during generation. * Each tool has a description, input schema, and handler function that runs client-side. */ tools?: Record; /** * Context cells to make available to the LLM. * These cells appear in the system prompt with their schemas and current values. */ context?: Record>; } export interface BuiltInLLMState { pending: boolean; result?: BuiltInLLMContent; partial?: string; error?: unknown; cancelGeneration: Stream; } export interface BuiltInLLMGenerateObjectState { pending: boolean; result?: T; partial?: string; error?: unknown; cancelGeneration: Stream; } export interface BuiltInLLMDialogState { pending: boolean; error: unknown; cancelGeneration: Stream; addMessage: Stream; flattenedTools: Record; pinnedCells: Array<{ path: string; name: string }>; } export type BuiltInGenerateObjectParams = | { model?: string; prompt: BuiltInLLMContent; messages?: never; context?: Record; schema?: JSONSchema; system?: string; cache?: boolean; maxTokens?: number; metadata?: Record; tools?: Record; } | { model?: string; prompt?: never; messages: BuiltInLLMMessage[]; context?: Record; schema?: JSONSchema; system?: string; cache?: boolean; maxTokens?: number; metadata?: Record; tools?: Record; }; export type BuiltInGenerateTextParams = | { prompt: string; messages?: never; context?: Record; system?: string; model?: string; maxTokens?: number; tools?: Record; } | { prompt?: never; messages: BuiltInLLMMessage[]; context?: Record; system?: string; model?: string; maxTokens?: number; tools?: Record; }; export interface BuiltInGenerateTextState { pending: boolean; result?: string; error?: unknown; partial?: string; requestHash?: string; } export interface BuiltInCompileAndRunParams { files: Array<{ name: string; contents: string }>; main: string; input?: T; } export interface BuiltInCompileAndRunState { pending: boolean; result?: T; error?: any; errors?: Array<{ line: number; column: number; message: string; type: string; file?: string; }>; } // Function type definitions export type PatternFunction = { ( fn: (input: OpaqueRef>) => Opaque, ): RecipeFactory, StripCell>; ( fn: (input: OpaqueRef>) => any, ): RecipeFactory, StripCell>>; ( fn: ( input: OpaqueRef>>, ) => Opaque>, argumentSchema: IS, resultSchema: OS, ): RecipeFactory, SchemaWithoutCell>; }; /** @deprecated Use pattern() instead */ export type RecipeFunction = { // Function-only overload ( fn: (input: OpaqueRef>) => Opaque, ): RecipeFactory, StripCell>; ( fn: (input: OpaqueRef>) => any, ): RecipeFactory, StripCell>>; ( argumentSchema: S, fn: (input: OpaqueRef>>) => any, ): RecipeFactory, StripCell>>; ( argumentSchema: S, fn: (input: OpaqueRef>>) => Opaque, ): RecipeFactory, StripCell>; ( argumentSchema: S, resultSchema: RS, fn: ( input: OpaqueRef>>, ) => Opaque>, ): RecipeFactory, SchemaWithoutCell>; ( argumentSchema: string | JSONSchema, fn: (input: OpaqueRef>) => any, ): RecipeFactory, StripCell>>; ( argumentSchema: string | JSONSchema, fn: (input: OpaqueRef>) => Opaque, ): RecipeFactory, StripCell>; ( argumentSchema: string | JSONSchema, resultSchema: JSONSchema, fn: (input: OpaqueRef>) => Opaque, ): RecipeFactory, StripCell>; }; export type PatternToolFunction = < T, E extends Partial = Record, >( fnOrRecipe: ((input: OpaqueRef>) => any) | RecipeFactory, extraParams?: Opaque, ) => OpaqueRef>; export type LiftFunction = { ( argumentSchema: T, resultSchema: R, implementation: (input: Schema) => Schema, ): ModuleFactory, SchemaWithoutCell>; ( implementation: (input: T) => R, ): ModuleFactory, StripCell>; ( implementation: (input: T) => any, ): ModuleFactory, StripCell>>; any>( implementation: T, ): ModuleFactory[0]>, StripCell>>; ( argumentSchema?: JSONSchema, resultSchema?: JSONSchema, implementation?: (input: T) => R, ): ModuleFactory, StripCell>; }; // Helper type to make non-Cell and non-Stream properties readonly in handler state export type HandlerState = T extends Cell ? T : T extends Stream ? T : T extends Array ? ReadonlyArray> : T extends object ? { readonly [K in keyof T]: HandlerState } : T; export type HandlerFunction = { // With schemas ( eventSchema: E, stateSchema: T, handler: (event: Schema, props: Schema) => any, ): HandlerFactory, SchemaWithoutCell>; // With inferred types ( eventSchema: JSONSchema, stateSchema: JSONSchema, handler: (event: E, props: HandlerState) => any, ): HandlerFactory; // Without schemas ( handler: (event: E, props: T) => any, options: { proxy: true }, ): HandlerFactory; ( handler: (event: E, props: HandlerState) => any, ): HandlerFactory; }; /** * ActionFunction creates a handler that doesn't use the state parameter. * * This is to handler as computed is to lift/derive: * - User writes: action((e) => count.set(e.data)) * - Transformer rewrites to: handler((e, { count }) => count.set(e.data))({ count }) * * The transformer extracts closures and makes them explicit, just like how * computed(() => expr) becomes derive({}, () => expr) with closure extraction. */ export type ActionFunction = { (fn: (event: T) => void): HandlerFactory; }; /** * DeriveFunction creates a reactive computation that transforms input values. * * Special overload ordering is critical for correct type inference: * * 1. Schema-based overload: For explicit schema definitions * 2. Boolean literal overload: Widens `OpaqueRef | OpaqueRef` to `boolean` * - Required because TypeScript infers boolean cells as a union of literal types * - Without this, the callback would get `true | false` instead of `boolean` * 3. Cell preservation overload: Keeps Cell types wrapped consistently * - Prevents unwrapping of Cell to T, maintaining consistent behavior * - Whether Cell is passed directly or nested in objects, it stays wrapped * - Example: derive(cell(), (c) => ...) gives c: Cell, not number * 4. Generic overload: Handles all other cases, unwrapping Opaque types * * @deprecated Use compute() instead */ export type DeriveFunction = { // Overload 1: Schema-based derive with explicit input/output schemas < InputSchema extends JSONSchema = JSONSchema, ResultSchema extends JSONSchema = JSONSchema, >( argumentSchema: InputSchema, resultSchema: ResultSchema, input: Opaque>, f: ( input: Schema, ) => Schema, ): OpaqueRef>; // Overload 2: Boolean literal union -> boolean // Fixes: cell() returns OpaqueRef | OpaqueRef // Without this, callback gets (input: true | false) instead of (input: boolean) ( input: OpaqueRef | OpaqueRef, f: (input: In) => Out, ): OpaqueRef; // Overload 3: Preserve Cell types - unwrap OpaqueRef layers but keep Cell // Ensures consistent behavior: Cell stays Cell whether passed directly or in objects // Handles: Cell, OpaqueRef>, OpaqueRef>>, etc. ( input: AnyCellWrappedInOpaqueRef, f: (input: In) => Out, ): OpaqueRef; // Overload 4: Generic fallback - unwraps all Opaque types ( input: Opaque, f: (input: In) => Out, ): OpaqueRef; }; export type ComputedFunction = (fn: () => T) => OpaqueRef; export type StrFunction = ( strings: TemplateStringsArray, ...values: any[] ) => OpaqueRef; export type IfElseFunction = ( condition: Opaque, ifTrue: Opaque, ifFalse: Opaque, ) => OpaqueRef; export type WhenFunction = ( condition: Opaque, value: Opaque, ) => OpaqueRef; export type UnlessFunction = ( condition: Opaque, fallback: Opaque, ) => OpaqueRef; /** @deprecated Use generateText() or generateObject() instead */ export type LLMFunction = ( params: Opaque, ) => OpaqueRef; export type LLMDialogFunction = ( params: Opaque, ) => OpaqueRef; export type GenerateObjectFunction = ( params: Opaque, ) => OpaqueRef>; export type GenerateTextFunction = ( params: Opaque, ) => OpaqueRef; export type FetchOptions = { body?: JSONValue; headers?: Record; cache?: | "default" | "no-store" | "reload" | "no-cache" | "force-cache" | "only-if-cached"; method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS" | "HEAD"; redirect?: "follow" | "error" | "manual"; }; export type FetchDataFunction = ( params: Opaque<{ url: string; mode?: "json" | "text"; options?: FetchOptions; result?: T; }>, ) => OpaqueRef<{ pending: boolean; result: T; error: any }>; export type FetchProgramFunction = ( params: Opaque<{ url: string }>, ) => OpaqueRef<{ pending: boolean; result: { files: Array<{ name: string; contents: string }>; main: string; } | undefined; error: any; }>; export type StreamDataFunction = ( params: Opaque<{ url: string; options?: FetchOptions; result?: T; }>, ) => OpaqueRef<{ pending: boolean; result: T; error: any }>; export type CompileAndRunFunction = ( params: Opaque>, ) => OpaqueRef>; export type WishTag = `/${string}` | `#${string}`; export type DID = `did:${string}:${string}`; export type WishParams = { query: WishTag | string; path?: string[]; context?: Record; schema?: JSONSchema; scope?: (DID | "~" | ".")[]; }; export type WishState = { result?: T; error?: any; [UI]?: VNode; }; export type NavigateToFunction = (cell: OpaqueRef) => OpaqueRef; export type WishFunction = { (target: Opaque): OpaqueRef>>; ( target: Opaque, schema: S, ): OpaqueRef>>>; // TODO(seefeld): Remove old interface mid December 2025 (target: Opaque): OpaqueRef; ( target: Opaque, schema: S, ): OpaqueRef>; }; export type CreateNodeFactoryFunction = ( moduleSpec: Module, ) => ModuleFactory; // Default type for specifying default values in type definitions export type Default = T; // Internal-only way to instantiate internal modules export type ByRefFunction = (ref: string) => ModuleFactory; // Internal-only helper to create VDOM nodes export type HFunction = { ( name: string | ((...args: any[]) => VNode), props: { [key: string]: any } | null, ...children: RenderNode[] ): VNode; fragment({ children }: { children: RenderNode[] }): VNode; }; // No-op alternative to `as const as JSONSchema` export type SchemaFunction = (schema: T) => T; // toSchema is a compile-time transformer that converts TypeScript types to JSONSchema // The actual implementation is done by the TypeScript transformer export type ToSchemaFunction = (options?: Partial) => JSONSchema; // Recipe environment types export interface RecipeEnvironment { readonly apiUrl: URL; } export type GetRecipeEnvironmentFunction = () => RecipeEnvironment; // Re-export all function types as values for destructuring imports // These will be implemented by the factory export declare const pattern: PatternFunction; /** @deprecated Use pattern() instead */ export declare const recipe: RecipeFunction; export declare const patternTool: PatternToolFunction; export declare const lift: LiftFunction; export declare const handler: HandlerFunction; export declare const action: ActionFunction; /** @deprecated Use compute() instead */ export declare const derive: DeriveFunction; export declare const computed: ComputedFunction; export declare const str: StrFunction; export declare const ifElse: IfElseFunction; export declare const when: WhenFunction; export declare const unless: UnlessFunction; /** @deprecated Use generateText() or generateObject() instead */ export declare const llm: LLMFunction; export declare const llmDialog: LLMDialogFunction; export declare const generateObject: GenerateObjectFunction; export declare const generateText: GenerateTextFunction; export declare const fetchData: FetchDataFunction; export declare const fetchProgram: FetchProgramFunction; export declare const streamData: StreamDataFunction; export declare const compileAndRun: CompileAndRunFunction; export declare const navigateTo: NavigateToFunction; export declare const wish: WishFunction; export declare const createNodeFactory: CreateNodeFactoryFunction; /** @deprecated Use Cell.of(defaultValue?) instead */ export declare const cell: CellTypeConstructor["of"]; export declare const byRef: ByRefFunction; export declare const getRecipeEnvironment: GetRecipeEnvironmentFunction; /** * Get the entity ID from a cell or value. * Returns { "/": "id-string" } format if the value has an entity ID, undefined otherwise. * Useful for extracting IDs from newly created charms for linking. */ export type GetEntityIdFunction = (value: any) => { "/": string } | undefined; export declare const getEntityId: GetEntityIdFunction; export declare const schema: SchemaFunction; export declare const toSchema: ToSchemaFunction; /** * Helper type to recursively remove `readonly` properties from type `T`. * * (Duplicated from @commontools/utils/types.ts, but we want to keep this * independent for now) */ export type Mutable = T extends ReadonlyArray ? Mutable[] : T extends object ? ({ -readonly [P in keyof T]: Mutable }) : T; // ===== JSON Pointer Path Resolution Utilities ===== /** * Split a JSON Pointer reference into path segments. * * Examples: * - "#" -> [] * - "#/$defs/Address" -> ["$defs", "Address"] * - "#/properties/name" -> ["properties", "name"] * * Note: Does not handle JSON Pointer escaping (~0, ~1) at type level. * Refs with ~ in keys will not work correctly in TypeScript types. */ type SplitPath = S extends "#" ? [] : S extends `#/${infer Rest}` ? SplitPathSegments : never; type SplitPathSegments = S extends `${infer First}/${infer Rest}` ? [First, ...SplitPathSegments] : [S]; /** * Navigate through a schema following a path of keys. * Returns never if the path doesn't exist. */ type NavigatePath< Schema extends JSONSchema, Path extends readonly string[], Depth extends DepthLevel = 9, > = Depth extends 0 ? unknown : Path extends readonly [ infer First extends string, ...infer Rest extends string[], ] ? Schema extends Record ? First extends keyof Schema ? NavigatePath> : never : never : Schema; /** * Resolve a $ref string to the target schema. * * Supports: * - "#" (self-reference to root) * - "#/path/to/def" (JSON Pointer within document) * * External refs (URLs) return any. */ type ResolveRef< RefString extends string, Root extends JSONSchema, Depth extends DepthLevel, > = RefString extends "#" ? Root : RefString extends `#/${string}` ? SplitPath extends infer Path extends readonly string[] ? NavigatePath : never : any; // External ref /** * Merge two schemas, with left side taking precedence. * Used to apply ref site siblings to resolved target schema. */ type MergeSchemas< Left extends JSONSchema, Right extends JSONSchema, > = Left extends boolean ? Left : Right extends boolean ? Right extends true ? Left : false : { [K in keyof Left | keyof Right]: K extends keyof Left ? Left[K] : K extends keyof Right ? Right[K] : never; }; type MergeRefSiteWithTargetGeneric< RefSite extends JSONSchema, Target extends JSONSchema, Root extends JSONSchema, Depth extends DepthLevel, WrapCells extends boolean, > = RefSite extends { $ref: string } ? MergeSchemas, Target> extends infer Merged extends JSONSchema ? SchemaInner : never : never; type SchemaAnyOf< Schemas extends readonly JSONSchema[], Root extends JSONSchema, Depth extends DepthLevel, WrapCells extends boolean, > = { [I in keyof Schemas]: Schemas[I] extends JSONSchema ? SchemaInner, WrapCells> : never; }[number]; type SchemaArrayItems< Items, Root extends JSONSchema, Depth extends DepthLevel, WrapCells extends boolean, > = Items extends JSONSchema ? Array, WrapCells>> : unknown[]; type SchemaCore< T extends JSONSchema, Root extends JSONSchema, Depth extends DepthLevel, WrapCells extends boolean, > = T extends { $ref: "#" } ? SchemaInner< Omit, Root, DecrementDepth, WrapCells > : T extends { $ref: infer RefStr extends string } ? MergeRefSiteWithTargetGeneric< T, ResolveRef>, Root, DecrementDepth, WrapCells > : T extends { enum: infer E extends readonly any[] } ? E[number] : T extends { anyOf: infer U extends readonly JSONSchema[] } ? SchemaAnyOf : T extends { type: "string" } ? string : T extends { type: "number" | "integer" } ? number : T extends { type: "boolean" } ? boolean : T extends { type: "null" } ? null : T extends { type: "array" } ? T extends { items: infer I } ? SchemaArrayItems : unknown[] : T extends { type: "object" } ? T extends { properties: infer P } ? P extends Record ? ObjectFromProperties< P, T extends { required: readonly string[] } ? T["required"] : [], Root, Depth, T extends { additionalProperties: infer AP extends JSONSchema } ? AP : false, GetDefaultKeys, WrapCells > : Record : T extends { additionalProperties: infer AP } ? AP extends false ? Record : AP extends true ? Record : AP extends JSONSchema ? Record< string | number | symbol, SchemaInner, WrapCells> > : Record : Record : any; type SchemaInner< T extends JSONSchema, Root extends JSONSchema = T, Depth extends DepthLevel = 9, WrapCells extends boolean = true, > = Depth extends 0 ? unknown : T extends { asCell: true } ? WrapCells extends true ? Cell, Root, Depth, WrapCells>> : SchemaInner, Root, Depth, WrapCells> : T extends { asStream: true } ? WrapCells extends true ? Stream, Root, Depth, WrapCells>> : SchemaInner, Root, Depth, WrapCells> : SchemaCore; export type Schema< T extends JSONSchema, Root extends JSONSchema = T, Depth extends DepthLevel = 9, > = SchemaInner; // Get keys from the default object type GetDefaultKeys = T extends { default: infer D } ? D extends Record ? keyof D & string : never : never; // Helper type for building object types from properties type ObjectFromProperties< P extends Record, R extends readonly string[] | never, Root extends JSONSchema, Depth extends DepthLevel, AP extends JSONSchema = false, DK extends string = never, WrapCells extends boolean = true, > = & { [ K in keyof P as K extends string ? K extends R[number] | DK ? K : never : never ]: SchemaInner, WrapCells>; } & { [ K in keyof P as K extends string ? K extends R[number] | DK ? never : K : never ]?: SchemaInner, WrapCells>; } & ( AP extends false ? Record : AP extends true ? { [key: string]: unknown } : AP extends JSONSchema ? { [key: string]: SchemaInner< AP, Root, DecrementDepth, WrapCells >; } : Record ); // Restrict Depth to these numeric literal types type DepthLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9; // Decrement map for recursion limit type Decrement = { 0: 0; 1: 0; 2: 1; 3: 2; 4: 3; 5: 4; 6: 5; 7: 6; 8: 7; 9: 8; }; // Helper function to safely get decremented depth type DecrementDepth = Decrement[D] & DepthLevel; export type SchemaWithoutCell< T extends JSONSchema, Root extends JSONSchema = T, Depth extends DepthLevel = 9, > = SchemaInner; /** * Dynamic properties. Can either be string type (static) or a Mustache * variable (dynamic). */ export type Props = { [key: string]: | string | number | boolean | object | Array | null | Cell | Stream; }; /** A child in a view can be one of a few things */ export type RenderNode = | InnerRenderNode | AnyBrandedCell | Array; type InnerRenderNode = | VNode | string | number | boolean | undefined; /** A "virtual view node", e.g. a virtual DOM element */ export type VNode = { type: "vnode"; name: string; props: Props; children?: RenderNode; [UI]?: VNode; };