🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@json-render/core

Package Overview
Dependencies
Maintainers
1
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@json-render/core - npm Package Compare versions

Comparing version
0.3.0
to
0.4.0
+313
-8
dist/index.d.mts

@@ -88,3 +88,3 @@ import { z } from 'zod';

*/
interface UITree {
interface Spec {
/** Root element key */

@@ -135,5 +135,102 @@ root: string;

/**
* Set a value in an object by JSON Pointer path
* Set a value in an object by JSON Pointer path.
* Automatically creates arrays when the path segment is a numeric index.
*/
declare function setByPath(obj: Record<string, unknown>, path: string, value: unknown): void;
/**
* Find a form value from params and/or data.
* Useful in action handlers to locate form input values regardless of path format.
*
* Checks in order:
* 1. Direct param key (if not a path reference)
* 2. Param keys ending with the field name
* 3. Data keys ending with the field name (dot notation)
* 4. Data paths using getByPath (slash notation)
*
* @example
* // Find "name" from params or data
* const name = findFormValue("name", params, data);
*
* // Will find from: params.name, params["form.name"], data["customerForm.name"], data.customerForm.name
*/
declare function findFormValue(fieldName: string, params?: Record<string, unknown>, data?: Record<string, unknown>): unknown;
/**
* A SpecStream line - a single patch operation in the stream.
*/
type SpecStreamLine = JsonPatch;
/**
* Parse a single SpecStream line into a patch operation.
* Returns null if the line is invalid or empty.
*
* SpecStream is json-render's streaming format where each line is a JSON patch
* operation that progressively builds up the final spec.
*/
declare function parseSpecStreamLine(line: string): SpecStreamLine | null;
/**
* Apply a single SpecStream patch to an object.
* Mutates the object in place.
*/
declare function applySpecStreamPatch<T extends Record<string, unknown>>(obj: T, patch: SpecStreamLine): T;
/**
* Compile a SpecStream string into a JSON object.
* Each line should be a patch operation.
*
* @example
* const stream = `{"op":"set","path":"/name","value":"Alice"}
* {"op":"set","path":"/age","value":30}`;
* const result = compileSpecStream(stream);
* // { name: "Alice", age: 30 }
*/
declare function compileSpecStream<T extends Record<string, unknown> = Record<string, unknown>>(stream: string, initial?: T): T;
/**
* Streaming SpecStream compiler.
* Useful for processing SpecStream data as it streams in from AI.
*
* @example
* const compiler = createSpecStreamCompiler<MySpec>();
*
* // As chunks arrive:
* const { result, newPatches } = compiler.push(chunk);
* if (newPatches.length > 0) {
* updateUI(result);
* }
*
* // When done:
* const finalResult = compiler.getResult();
*/
interface SpecStreamCompiler<T> {
/** Push a chunk of text. Returns the current result and any new patches applied. */
push(chunk: string): {
result: T;
newPatches: SpecStreamLine[];
};
/** Get the current compiled result */
getResult(): T;
/** Get all patches that have been applied */
getPatches(): SpecStreamLine[];
/** Reset the compiler to initial state */
reset(initial?: Partial<T>): void;
}
/**
* Create a streaming SpecStream compiler.
*
* SpecStream is json-render's streaming format. AI outputs patch operations
* line by line, and this compiler progressively builds the final spec.
*
* @example
* const compiler = createSpecStreamCompiler<TimelineSpec>();
*
* // Process streaming response
* const reader = response.body.getReader();
* while (true) {
* const { done, value } = await reader.read();
* if (done) break;
*
* const { result, newPatches } = compiler.push(decoder.decode(value));
* if (newPatches.length > 0) {
* setSpec(result); // Update UI with partial result
* }
* }
*/
declare function createSpecStreamCompiler<T = Record<string, unknown>>(initial?: Partial<T>): SpecStreamCompiler<T>;

@@ -491,2 +588,196 @@ /**

/**
* Schema builder primitives
*/
interface SchemaBuilder {
/** String type */
string(): SchemaType<"string">;
/** Number type */
number(): SchemaType<"number">;
/** Boolean type */
boolean(): SchemaType<"boolean">;
/** Array of type */
array<T extends SchemaType>(item: T): SchemaType<"array", T>;
/** Object with shape */
object<T extends Record<string, SchemaType>>(shape: T): SchemaType<"object", T>;
/** Record/map with value type */
record<T extends SchemaType>(value: T): SchemaType<"record", T>;
/** Any type */
any(): SchemaType<"any">;
/** Placeholder for user-provided Zod schema */
zod(): SchemaType<"zod">;
/** Reference to catalog key (e.g., 'catalog.components') */
ref(path: string): SchemaType<"ref", string>;
/** Props from referenced catalog entry */
propsOf(path: string): SchemaType<"propsOf", string>;
/** Map of named entries with shared shape */
map<T extends Record<string, SchemaType>>(entryShape: T): SchemaType<"map", T>;
/** Optional modifier */
optional(): {
optional: true;
};
}
/**
* Schema type representation
*/
interface SchemaType<TKind extends string = string, TInner = unknown> {
kind: TKind;
inner?: TInner;
optional?: boolean;
}
/**
* Schema definition shape
*/
interface SchemaDefinition<TSpec extends SchemaType = SchemaType, TCatalog extends SchemaType = SchemaType> {
/** What the AI-generated spec looks like */
spec: TSpec;
/** What the catalog must provide */
catalog: TCatalog;
}
/**
* Schema instance with methods
*/
interface Schema<TDef extends SchemaDefinition = SchemaDefinition> {
/** The schema definition */
readonly definition: TDef;
/** Custom prompt template for this schema */
readonly promptTemplate?: PromptTemplate;
/** Create a catalog from this schema */
createCatalog<TCatalog extends InferCatalogInput<TDef["catalog"]>>(catalog: TCatalog): Catalog$1<TDef, TCatalog>;
}
/**
* Catalog instance with methods
*/
interface Catalog$1<TDef extends SchemaDefinition = SchemaDefinition, TCatalog = unknown> {
/** The schema this catalog is based on */
readonly schema: Schema<TDef>;
/** The catalog data */
readonly data: TCatalog;
/** Component names */
readonly componentNames: string[];
/** Action names */
readonly actionNames: string[];
/** Generate system prompt for AI */
prompt(options?: PromptOptions): string;
/** Export as JSON Schema for structured outputs */
jsonSchema(): object;
/** Validate a spec against this catalog */
validate(spec: unknown): SpecValidationResult<InferSpec<TDef, TCatalog>>;
/** Get the Zod schema for the spec */
zodSchema(): z.ZodType<InferSpec<TDef, TCatalog>>;
/** Type helper for the spec type */
readonly _specType: InferSpec<TDef, TCatalog>;
}
/**
* Prompt generation options
*/
interface PromptOptions {
/** Custom system message intro */
system?: string;
/** Additional rules to append */
customRules?: string[];
}
/**
* Context provided to prompt templates
*/
interface PromptContext<TCatalog = unknown> {
/** The catalog data */
catalog: TCatalog;
/** Component names from the catalog */
componentNames: string[];
/** Action names from the catalog (if any) */
actionNames: string[];
/** Prompt options provided by the user */
options: PromptOptions;
/** Helper to format a Zod type as a human-readable string */
formatZodType: (schema: z.ZodType) => string;
}
/**
* Prompt template function type
*/
type PromptTemplate<TCatalog = unknown> = (context: PromptContext<TCatalog>) => string;
/**
* Schema options
*/
interface SchemaOptions<TCatalog = unknown> {
/** Custom prompt template for this schema */
promptTemplate?: PromptTemplate<TCatalog>;
}
/**
* Spec validation result
*/
interface SpecValidationResult<T> {
success: boolean;
data?: T;
error?: z.ZodError;
}
/**
* Extract the components map type from a catalog
* @example type Components = InferCatalogComponents<typeof myCatalog>;
*/
type InferCatalogComponents<C extends Catalog$1> = C extends Catalog$1<SchemaDefinition, infer TCatalog> ? TCatalog extends {
components: infer Comps;
} ? Comps : never : never;
/**
* Extract the actions map type from a catalog
* @example type Actions = InferCatalogActions<typeof myCatalog>;
*/
type InferCatalogActions<C extends Catalog$1> = C extends Catalog$1<SchemaDefinition, infer TCatalog> ? TCatalog extends {
actions: infer Acts;
} ? Acts : never : never;
/**
* Infer component props from a catalog by component name
* @example type ButtonProps = InferComponentProps<typeof myCatalog, 'Button'>;
*/
type InferComponentProps<C extends Catalog$1, K extends keyof InferCatalogComponents<C>> = InferCatalogComponents<C>[K] extends {
props: z.ZodType<infer P>;
} ? P : never;
/**
* Infer action params from a catalog by action name
* @example type ViewCustomersParams = InferActionParams<typeof myCatalog, 'viewCustomers'>;
*/
type InferActionParams<C extends Catalog$1, K extends keyof InferCatalogActions<C>> = InferCatalogActions<C>[K] extends {
params: z.ZodType<infer P>;
} ? P : never;
type InferCatalogInput<T> = T extends SchemaType<"object", infer Shape> ? {
[K in keyof Shape]: InferCatalogField<Shape[K]>;
} : never;
type InferCatalogField<T> = T extends SchemaType<"map", infer EntryShape> ? Record<string, InferMapEntryRequired<EntryShape> & Partial<InferMapEntryOptional<EntryShape>>> : T extends SchemaType<"zod"> ? z.ZodType : T extends SchemaType<"string"> ? string : T extends SchemaType<"number"> ? number : T extends SchemaType<"boolean"> ? boolean : T extends SchemaType<"array", infer Item> ? InferCatalogField<Item>[] : T extends SchemaType<"object", infer Shape> ? {
[K in keyof Shape]: InferCatalogField<Shape[K]>;
} : unknown;
type InferMapEntryRequired<T> = {
[K in keyof T as K extends "props" ? K : never]: InferMapEntryField<T[K]>;
};
type InferMapEntryOptional<T> = {
[K in keyof T as K extends "props" ? never : K]: InferMapEntryField<T[K]>;
};
type InferMapEntryField<T> = T extends SchemaType<"zod"> ? z.ZodType : T extends SchemaType<"string"> ? string : T extends SchemaType<"number"> ? number : T extends SchemaType<"boolean"> ? boolean : T extends SchemaType<"array", infer Item> ? InferMapEntryField<Item>[] : T extends SchemaType<"object", infer Shape> ? {
[K in keyof Shape]: InferMapEntryField<Shape[K]>;
} : unknown;
type InferSpec<TDef extends SchemaDefinition, TCatalog> = TDef extends {
spec: SchemaType<"object", infer Shape>;
} ? InferSpecObject<Shape, TCatalog> : unknown;
type InferSpecObject<Shape, TCatalog> = {
[K in keyof Shape]: InferSpecField<Shape[K], TCatalog>;
};
type InferSpecField<T, TCatalog> = T extends SchemaType<"string"> ? string : T extends SchemaType<"number"> ? number : T extends SchemaType<"boolean"> ? boolean : T extends SchemaType<"array", infer Item> ? InferSpecField<Item, TCatalog>[] : T extends SchemaType<"object", infer Shape> ? InferSpecObject<Shape, TCatalog> : T extends SchemaType<"record", infer Value> ? Record<string, InferSpecField<Value, TCatalog>> : T extends SchemaType<"ref", infer Path> ? InferRefType<Path, TCatalog> : T extends SchemaType<"propsOf", infer Path> ? InferPropsOfType<Path, TCatalog> : T extends SchemaType<"any"> ? unknown : unknown;
type InferRefType<Path, TCatalog> = Path extends "catalog.components" ? TCatalog extends {
components: infer C;
} ? keyof C : string : Path extends "catalog.actions" ? TCatalog extends {
actions: infer A;
} ? keyof A : string : string;
type InferPropsOfType<Path, TCatalog> = Path extends "catalog.components" ? TCatalog extends {
components: infer C;
} ? C extends Record<string, {
props: z.ZodType<infer P>;
}> ? P : Record<string, unknown> : Record<string, unknown> : Record<string, unknown>;
/**
* Define a schema using the builder pattern
*/
declare function defineSchema<TDef extends SchemaDefinition>(builder: (s: SchemaBuilder) => TDef, options?: SchemaOptions): Schema<TDef>;
/**
* Shorthand: Define a catalog directly from a schema
*/
declare function defineCatalog<TDef extends SchemaDefinition, TCatalog extends InferCatalogInput<TDef["catalog"]>>(schema: Schema<TDef>, catalog: TCatalog): Catalog$1<TDef, TCatalog>;
/**
* Component definition with visibility and validation support

@@ -539,4 +830,4 @@ */

readonly elementSchema: z.ZodType<UIElement>;
/** Full UI tree schema */
readonly treeSchema: z.ZodType<UITree>;
/** Full UI spec schema */
readonly specSchema: z.ZodType<Spec>;
/** Check if component exists */

@@ -554,6 +845,6 @@ hasComponent(type: string): boolean;

};
/** Validate a UI tree */
validateTree(tree: unknown): {
/** Validate a UI spec */
validateSpec(spec: unknown): {
success: boolean;
data?: UITree;
data?: Spec;
error?: z.ZodError;

@@ -576,3 +867,17 @@ };

};
/**
* Options for generating system prompts
*/
interface SystemPromptOptions {
/** System message intro (replaces default) */
system?: string;
/** Additional rules to append to the rules section */
customRules?: string[];
}
/**
* Generate a complete system prompt for AI that can generate UI from a catalog.
* This produces a ready-to-use prompt that stays in sync with the catalog definition.
*/
declare function generateSystemPrompt<TComponents extends Record<string, ComponentDefinition>, TActions extends Record<string, ActionDefinition>, TFunctions extends Record<string, ValidationFunction>>(catalog: Catalog<TComponents, TActions, TFunctions>, options?: SystemPromptOptions): string;
export { type Action, type ActionConfirm, ActionConfirmSchema, type ActionDefinition, type ActionExecutionContext, type ActionHandler, type ActionOnError, ActionOnErrorSchema, type ActionOnSuccess, ActionOnSuccessSchema, ActionSchema, type AuthState, type Catalog, type CatalogConfig, type ComponentDefinition, type ComponentSchema, type DataModel, type DynamicBoolean, DynamicBooleanSchema, type DynamicNumber, DynamicNumberSchema, type DynamicString, DynamicStringSchema, type DynamicValue, DynamicValueSchema, type InferCatalogComponentProps, type JsonPatch, type LogicExpression, LogicExpressionSchema, type PatchOp, type ResolvedAction, type UIElement, type UITree, type ValidationCheck, type ValidationCheckResult, ValidationCheckSchema, type ValidationConfig, ValidationConfigSchema, type ValidationContext, type ValidationFunction, type ValidationFunctionDefinition, type ValidationMode, type ValidationResult, type VisibilityCondition, VisibilityConditionSchema, type VisibilityContext, action, builtInValidationFunctions, check, createCatalog, evaluateLogicExpression, evaluateVisibility, executeAction, generateCatalogPrompt, getByPath, interpolateString, resolveAction, resolveDynamicValue, runValidation, runValidationCheck, setByPath, visibility };
export { type Action, type ActionConfirm, ActionConfirmSchema, type ActionDefinition, type ActionExecutionContext, type ActionHandler, type ActionOnError, ActionOnErrorSchema, type ActionOnSuccess, ActionOnSuccessSchema, ActionSchema, type AuthState, type Catalog$1 as Catalog, type CatalogConfig, type ComponentDefinition, type ComponentSchema, type DataModel, type DynamicBoolean, DynamicBooleanSchema, type DynamicNumber, DynamicNumberSchema, type DynamicString, DynamicStringSchema, type DynamicValue, DynamicValueSchema, type InferActionParams, type InferCatalogActions, type InferCatalogComponentProps, type InferCatalogComponents, type InferCatalogInput, type InferComponentProps, type InferSpec, type JsonPatch, type Catalog as LegacyCatalog, type LogicExpression, LogicExpressionSchema, type PatchOp, type PromptContext, type PromptOptions, type PromptTemplate, type ResolvedAction, type Schema, type SchemaBuilder, type SchemaDefinition, type SchemaOptions, type SchemaType, type Spec, type SpecStreamCompiler, type SpecStreamLine, type SpecValidationResult, type SystemPromptOptions, type UIElement, type ValidationCheck, type ValidationCheckResult, ValidationCheckSchema, type ValidationConfig, ValidationConfigSchema, type ValidationContext, type ValidationFunction, type ValidationFunctionDefinition, type ValidationMode, type ValidationResult, type VisibilityCondition, VisibilityConditionSchema, type VisibilityContext, action, applySpecStreamPatch, builtInValidationFunctions, check, compileSpecStream, createCatalog, createSpecStreamCompiler, defineCatalog, defineSchema, evaluateLogicExpression, evaluateVisibility, executeAction, findFormValue, generateCatalogPrompt, generateSystemPrompt, getByPath, interpolateString, parseSpecStreamLine, resolveAction, resolveDynamicValue, runValidation, runValidationCheck, setByPath, visibility };

@@ -88,3 +88,3 @@ import { z } from 'zod';

*/
interface UITree {
interface Spec {
/** Root element key */

@@ -135,5 +135,102 @@ root: string;

/**
* Set a value in an object by JSON Pointer path
* Set a value in an object by JSON Pointer path.
* Automatically creates arrays when the path segment is a numeric index.
*/
declare function setByPath(obj: Record<string, unknown>, path: string, value: unknown): void;
/**
* Find a form value from params and/or data.
* Useful in action handlers to locate form input values regardless of path format.
*
* Checks in order:
* 1. Direct param key (if not a path reference)
* 2. Param keys ending with the field name
* 3. Data keys ending with the field name (dot notation)
* 4. Data paths using getByPath (slash notation)
*
* @example
* // Find "name" from params or data
* const name = findFormValue("name", params, data);
*
* // Will find from: params.name, params["form.name"], data["customerForm.name"], data.customerForm.name
*/
declare function findFormValue(fieldName: string, params?: Record<string, unknown>, data?: Record<string, unknown>): unknown;
/**
* A SpecStream line - a single patch operation in the stream.
*/
type SpecStreamLine = JsonPatch;
/**
* Parse a single SpecStream line into a patch operation.
* Returns null if the line is invalid or empty.
*
* SpecStream is json-render's streaming format where each line is a JSON patch
* operation that progressively builds up the final spec.
*/
declare function parseSpecStreamLine(line: string): SpecStreamLine | null;
/**
* Apply a single SpecStream patch to an object.
* Mutates the object in place.
*/
declare function applySpecStreamPatch<T extends Record<string, unknown>>(obj: T, patch: SpecStreamLine): T;
/**
* Compile a SpecStream string into a JSON object.
* Each line should be a patch operation.
*
* @example
* const stream = `{"op":"set","path":"/name","value":"Alice"}
* {"op":"set","path":"/age","value":30}`;
* const result = compileSpecStream(stream);
* // { name: "Alice", age: 30 }
*/
declare function compileSpecStream<T extends Record<string, unknown> = Record<string, unknown>>(stream: string, initial?: T): T;
/**
* Streaming SpecStream compiler.
* Useful for processing SpecStream data as it streams in from AI.
*
* @example
* const compiler = createSpecStreamCompiler<MySpec>();
*
* // As chunks arrive:
* const { result, newPatches } = compiler.push(chunk);
* if (newPatches.length > 0) {
* updateUI(result);
* }
*
* // When done:
* const finalResult = compiler.getResult();
*/
interface SpecStreamCompiler<T> {
/** Push a chunk of text. Returns the current result and any new patches applied. */
push(chunk: string): {
result: T;
newPatches: SpecStreamLine[];
};
/** Get the current compiled result */
getResult(): T;
/** Get all patches that have been applied */
getPatches(): SpecStreamLine[];
/** Reset the compiler to initial state */
reset(initial?: Partial<T>): void;
}
/**
* Create a streaming SpecStream compiler.
*
* SpecStream is json-render's streaming format. AI outputs patch operations
* line by line, and this compiler progressively builds the final spec.
*
* @example
* const compiler = createSpecStreamCompiler<TimelineSpec>();
*
* // Process streaming response
* const reader = response.body.getReader();
* while (true) {
* const { done, value } = await reader.read();
* if (done) break;
*
* const { result, newPatches } = compiler.push(decoder.decode(value));
* if (newPatches.length > 0) {
* setSpec(result); // Update UI with partial result
* }
* }
*/
declare function createSpecStreamCompiler<T = Record<string, unknown>>(initial?: Partial<T>): SpecStreamCompiler<T>;

@@ -491,2 +588,196 @@ /**

/**
* Schema builder primitives
*/
interface SchemaBuilder {
/** String type */
string(): SchemaType<"string">;
/** Number type */
number(): SchemaType<"number">;
/** Boolean type */
boolean(): SchemaType<"boolean">;
/** Array of type */
array<T extends SchemaType>(item: T): SchemaType<"array", T>;
/** Object with shape */
object<T extends Record<string, SchemaType>>(shape: T): SchemaType<"object", T>;
/** Record/map with value type */
record<T extends SchemaType>(value: T): SchemaType<"record", T>;
/** Any type */
any(): SchemaType<"any">;
/** Placeholder for user-provided Zod schema */
zod(): SchemaType<"zod">;
/** Reference to catalog key (e.g., 'catalog.components') */
ref(path: string): SchemaType<"ref", string>;
/** Props from referenced catalog entry */
propsOf(path: string): SchemaType<"propsOf", string>;
/** Map of named entries with shared shape */
map<T extends Record<string, SchemaType>>(entryShape: T): SchemaType<"map", T>;
/** Optional modifier */
optional(): {
optional: true;
};
}
/**
* Schema type representation
*/
interface SchemaType<TKind extends string = string, TInner = unknown> {
kind: TKind;
inner?: TInner;
optional?: boolean;
}
/**
* Schema definition shape
*/
interface SchemaDefinition<TSpec extends SchemaType = SchemaType, TCatalog extends SchemaType = SchemaType> {
/** What the AI-generated spec looks like */
spec: TSpec;
/** What the catalog must provide */
catalog: TCatalog;
}
/**
* Schema instance with methods
*/
interface Schema<TDef extends SchemaDefinition = SchemaDefinition> {
/** The schema definition */
readonly definition: TDef;
/** Custom prompt template for this schema */
readonly promptTemplate?: PromptTemplate;
/** Create a catalog from this schema */
createCatalog<TCatalog extends InferCatalogInput<TDef["catalog"]>>(catalog: TCatalog): Catalog$1<TDef, TCatalog>;
}
/**
* Catalog instance with methods
*/
interface Catalog$1<TDef extends SchemaDefinition = SchemaDefinition, TCatalog = unknown> {
/** The schema this catalog is based on */
readonly schema: Schema<TDef>;
/** The catalog data */
readonly data: TCatalog;
/** Component names */
readonly componentNames: string[];
/** Action names */
readonly actionNames: string[];
/** Generate system prompt for AI */
prompt(options?: PromptOptions): string;
/** Export as JSON Schema for structured outputs */
jsonSchema(): object;
/** Validate a spec against this catalog */
validate(spec: unknown): SpecValidationResult<InferSpec<TDef, TCatalog>>;
/** Get the Zod schema for the spec */
zodSchema(): z.ZodType<InferSpec<TDef, TCatalog>>;
/** Type helper for the spec type */
readonly _specType: InferSpec<TDef, TCatalog>;
}
/**
* Prompt generation options
*/
interface PromptOptions {
/** Custom system message intro */
system?: string;
/** Additional rules to append */
customRules?: string[];
}
/**
* Context provided to prompt templates
*/
interface PromptContext<TCatalog = unknown> {
/** The catalog data */
catalog: TCatalog;
/** Component names from the catalog */
componentNames: string[];
/** Action names from the catalog (if any) */
actionNames: string[];
/** Prompt options provided by the user */
options: PromptOptions;
/** Helper to format a Zod type as a human-readable string */
formatZodType: (schema: z.ZodType) => string;
}
/**
* Prompt template function type
*/
type PromptTemplate<TCatalog = unknown> = (context: PromptContext<TCatalog>) => string;
/**
* Schema options
*/
interface SchemaOptions<TCatalog = unknown> {
/** Custom prompt template for this schema */
promptTemplate?: PromptTemplate<TCatalog>;
}
/**
* Spec validation result
*/
interface SpecValidationResult<T> {
success: boolean;
data?: T;
error?: z.ZodError;
}
/**
* Extract the components map type from a catalog
* @example type Components = InferCatalogComponents<typeof myCatalog>;
*/
type InferCatalogComponents<C extends Catalog$1> = C extends Catalog$1<SchemaDefinition, infer TCatalog> ? TCatalog extends {
components: infer Comps;
} ? Comps : never : never;
/**
* Extract the actions map type from a catalog
* @example type Actions = InferCatalogActions<typeof myCatalog>;
*/
type InferCatalogActions<C extends Catalog$1> = C extends Catalog$1<SchemaDefinition, infer TCatalog> ? TCatalog extends {
actions: infer Acts;
} ? Acts : never : never;
/**
* Infer component props from a catalog by component name
* @example type ButtonProps = InferComponentProps<typeof myCatalog, 'Button'>;
*/
type InferComponentProps<C extends Catalog$1, K extends keyof InferCatalogComponents<C>> = InferCatalogComponents<C>[K] extends {
props: z.ZodType<infer P>;
} ? P : never;
/**
* Infer action params from a catalog by action name
* @example type ViewCustomersParams = InferActionParams<typeof myCatalog, 'viewCustomers'>;
*/
type InferActionParams<C extends Catalog$1, K extends keyof InferCatalogActions<C>> = InferCatalogActions<C>[K] extends {
params: z.ZodType<infer P>;
} ? P : never;
type InferCatalogInput<T> = T extends SchemaType<"object", infer Shape> ? {
[K in keyof Shape]: InferCatalogField<Shape[K]>;
} : never;
type InferCatalogField<T> = T extends SchemaType<"map", infer EntryShape> ? Record<string, InferMapEntryRequired<EntryShape> & Partial<InferMapEntryOptional<EntryShape>>> : T extends SchemaType<"zod"> ? z.ZodType : T extends SchemaType<"string"> ? string : T extends SchemaType<"number"> ? number : T extends SchemaType<"boolean"> ? boolean : T extends SchemaType<"array", infer Item> ? InferCatalogField<Item>[] : T extends SchemaType<"object", infer Shape> ? {
[K in keyof Shape]: InferCatalogField<Shape[K]>;
} : unknown;
type InferMapEntryRequired<T> = {
[K in keyof T as K extends "props" ? K : never]: InferMapEntryField<T[K]>;
};
type InferMapEntryOptional<T> = {
[K in keyof T as K extends "props" ? never : K]: InferMapEntryField<T[K]>;
};
type InferMapEntryField<T> = T extends SchemaType<"zod"> ? z.ZodType : T extends SchemaType<"string"> ? string : T extends SchemaType<"number"> ? number : T extends SchemaType<"boolean"> ? boolean : T extends SchemaType<"array", infer Item> ? InferMapEntryField<Item>[] : T extends SchemaType<"object", infer Shape> ? {
[K in keyof Shape]: InferMapEntryField<Shape[K]>;
} : unknown;
type InferSpec<TDef extends SchemaDefinition, TCatalog> = TDef extends {
spec: SchemaType<"object", infer Shape>;
} ? InferSpecObject<Shape, TCatalog> : unknown;
type InferSpecObject<Shape, TCatalog> = {
[K in keyof Shape]: InferSpecField<Shape[K], TCatalog>;
};
type InferSpecField<T, TCatalog> = T extends SchemaType<"string"> ? string : T extends SchemaType<"number"> ? number : T extends SchemaType<"boolean"> ? boolean : T extends SchemaType<"array", infer Item> ? InferSpecField<Item, TCatalog>[] : T extends SchemaType<"object", infer Shape> ? InferSpecObject<Shape, TCatalog> : T extends SchemaType<"record", infer Value> ? Record<string, InferSpecField<Value, TCatalog>> : T extends SchemaType<"ref", infer Path> ? InferRefType<Path, TCatalog> : T extends SchemaType<"propsOf", infer Path> ? InferPropsOfType<Path, TCatalog> : T extends SchemaType<"any"> ? unknown : unknown;
type InferRefType<Path, TCatalog> = Path extends "catalog.components" ? TCatalog extends {
components: infer C;
} ? keyof C : string : Path extends "catalog.actions" ? TCatalog extends {
actions: infer A;
} ? keyof A : string : string;
type InferPropsOfType<Path, TCatalog> = Path extends "catalog.components" ? TCatalog extends {
components: infer C;
} ? C extends Record<string, {
props: z.ZodType<infer P>;
}> ? P : Record<string, unknown> : Record<string, unknown> : Record<string, unknown>;
/**
* Define a schema using the builder pattern
*/
declare function defineSchema<TDef extends SchemaDefinition>(builder: (s: SchemaBuilder) => TDef, options?: SchemaOptions): Schema<TDef>;
/**
* Shorthand: Define a catalog directly from a schema
*/
declare function defineCatalog<TDef extends SchemaDefinition, TCatalog extends InferCatalogInput<TDef["catalog"]>>(schema: Schema<TDef>, catalog: TCatalog): Catalog$1<TDef, TCatalog>;
/**
* Component definition with visibility and validation support

@@ -539,4 +830,4 @@ */

readonly elementSchema: z.ZodType<UIElement>;
/** Full UI tree schema */
readonly treeSchema: z.ZodType<UITree>;
/** Full UI spec schema */
readonly specSchema: z.ZodType<Spec>;
/** Check if component exists */

@@ -554,6 +845,6 @@ hasComponent(type: string): boolean;

};
/** Validate a UI tree */
validateTree(tree: unknown): {
/** Validate a UI spec */
validateSpec(spec: unknown): {
success: boolean;
data?: UITree;
data?: Spec;
error?: z.ZodError;

@@ -576,3 +867,17 @@ };

};
/**
* Options for generating system prompts
*/
interface SystemPromptOptions {
/** System message intro (replaces default) */
system?: string;
/** Additional rules to append to the rules section */
customRules?: string[];
}
/**
* Generate a complete system prompt for AI that can generate UI from a catalog.
* This produces a ready-to-use prompt that stays in sync with the catalog definition.
*/
declare function generateSystemPrompt<TComponents extends Record<string, ComponentDefinition>, TActions extends Record<string, ActionDefinition>, TFunctions extends Record<string, ValidationFunction>>(catalog: Catalog<TComponents, TActions, TFunctions>, options?: SystemPromptOptions): string;
export { type Action, type ActionConfirm, ActionConfirmSchema, type ActionDefinition, type ActionExecutionContext, type ActionHandler, type ActionOnError, ActionOnErrorSchema, type ActionOnSuccess, ActionOnSuccessSchema, ActionSchema, type AuthState, type Catalog, type CatalogConfig, type ComponentDefinition, type ComponentSchema, type DataModel, type DynamicBoolean, DynamicBooleanSchema, type DynamicNumber, DynamicNumberSchema, type DynamicString, DynamicStringSchema, type DynamicValue, DynamicValueSchema, type InferCatalogComponentProps, type JsonPatch, type LogicExpression, LogicExpressionSchema, type PatchOp, type ResolvedAction, type UIElement, type UITree, type ValidationCheck, type ValidationCheckResult, ValidationCheckSchema, type ValidationConfig, ValidationConfigSchema, type ValidationContext, type ValidationFunction, type ValidationFunctionDefinition, type ValidationMode, type ValidationResult, type VisibilityCondition, VisibilityConditionSchema, type VisibilityContext, action, builtInValidationFunctions, check, createCatalog, evaluateLogicExpression, evaluateVisibility, executeAction, generateCatalogPrompt, getByPath, interpolateString, resolveAction, resolveDynamicValue, runValidation, runValidationCheck, setByPath, visibility };
export { type Action, type ActionConfirm, ActionConfirmSchema, type ActionDefinition, type ActionExecutionContext, type ActionHandler, type ActionOnError, ActionOnErrorSchema, type ActionOnSuccess, ActionOnSuccessSchema, ActionSchema, type AuthState, type Catalog$1 as Catalog, type CatalogConfig, type ComponentDefinition, type ComponentSchema, type DataModel, type DynamicBoolean, DynamicBooleanSchema, type DynamicNumber, DynamicNumberSchema, type DynamicString, DynamicStringSchema, type DynamicValue, DynamicValueSchema, type InferActionParams, type InferCatalogActions, type InferCatalogComponentProps, type InferCatalogComponents, type InferCatalogInput, type InferComponentProps, type InferSpec, type JsonPatch, type Catalog as LegacyCatalog, type LogicExpression, LogicExpressionSchema, type PatchOp, type PromptContext, type PromptOptions, type PromptTemplate, type ResolvedAction, type Schema, type SchemaBuilder, type SchemaDefinition, type SchemaOptions, type SchemaType, type Spec, type SpecStreamCompiler, type SpecStreamLine, type SpecValidationResult, type SystemPromptOptions, type UIElement, type ValidationCheck, type ValidationCheckResult, ValidationCheckSchema, type ValidationConfig, ValidationConfigSchema, type ValidationContext, type ValidationFunction, type ValidationFunctionDefinition, type ValidationMode, type ValidationResult, type VisibilityCondition, VisibilityConditionSchema, type VisibilityContext, action, applySpecStreamPatch, builtInValidationFunctions, check, compileSpecStream, createCatalog, createSpecStreamCompiler, defineCatalog, defineSchema, evaluateLogicExpression, evaluateVisibility, executeAction, findFormValue, generateCatalogPrompt, generateSystemPrompt, getByPath, interpolateString, parseSpecStreamLine, resolveAction, resolveDynamicValue, runValidation, runValidationCheck, setByPath, visibility };

@@ -36,11 +36,19 @@ "use strict";

action: () => action,
applySpecStreamPatch: () => applySpecStreamPatch,
builtInValidationFunctions: () => builtInValidationFunctions,
check: () => check,
compileSpecStream: () => compileSpecStream,
createCatalog: () => createCatalog,
createSpecStreamCompiler: () => createSpecStreamCompiler,
defineCatalog: () => defineCatalog,
defineSchema: () => defineSchema,
evaluateLogicExpression: () => evaluateLogicExpression,
evaluateVisibility: () => evaluateVisibility,
executeAction: () => executeAction,
findFormValue: () => findFormValue,
generateCatalogPrompt: () => generateCatalogPrompt,
generateSystemPrompt: () => generateSystemPrompt,
getByPath: () => getByPath,
interpolateString: () => interpolateString,
parseSpecStreamLine: () => parseSpecStreamLine,
resolveAction: () => resolveAction,

@@ -103,2 +111,5 @@ resolveDynamicValue: () => resolveDynamicValue,

}
function isNumericIndex(str) {
return /^\d+$/.test(str);
}
function setByPath(obj, path, value) {

@@ -110,10 +121,142 @@ const segments = path.startsWith("/") ? path.slice(1).split("/") : path.split("/");

const segment = segments[i];
if (!(segment in current) || typeof current[segment] !== "object") {
current[segment] = {};
const nextSegment = segments[i + 1];
const nextIsNumeric = nextSegment !== void 0 && isNumericIndex(nextSegment);
if (Array.isArray(current)) {
const index = parseInt(segment, 10);
if (current[index] === void 0 || typeof current[index] !== "object") {
current[index] = nextIsNumeric ? [] : {};
}
current = current[index];
} else {
if (!(segment in current) || typeof current[segment] !== "object") {
current[segment] = nextIsNumeric ? [] : {};
}
current = current[segment];
}
current = current[segment];
}
const lastSegment = segments[segments.length - 1];
current[lastSegment] = value;
if (Array.isArray(current)) {
const index = parseInt(lastSegment, 10);
current[index] = value;
} else {
current[lastSegment] = value;
}
}
function findFormValue(fieldName, params, data) {
if (params?.[fieldName] !== void 0) {
const val = params[fieldName];
if (typeof val !== "string" || !val.includes(".")) {
return val;
}
}
if (params) {
for (const key of Object.keys(params)) {
if (key.endsWith(`.${fieldName}`)) {
const val = params[key];
if (typeof val !== "string" || !val.includes(".")) {
return val;
}
}
}
}
if (data) {
for (const key of Object.keys(data)) {
if (key === fieldName || key.endsWith(`.${fieldName}`)) {
return data[key];
}
}
const prefixes = ["form", "newCustomer", "customer", ""];
for (const prefix of prefixes) {
const path = prefix ? `${prefix}/${fieldName}` : fieldName;
const val = getByPath(data, path);
if (val !== void 0) {
return val;
}
}
}
return void 0;
}
function parseSpecStreamLine(line) {
const trimmed = line.trim();
if (!trimmed || !trimmed.startsWith("{")) return null;
try {
const patch = JSON.parse(trimmed);
if (patch.op && patch.path !== void 0) {
return patch;
}
return null;
} catch {
return null;
}
}
function applySpecStreamPatch(obj, patch) {
if (patch.op === "set" || patch.op === "add" || patch.op === "replace") {
setByPath(obj, patch.path, patch.value);
} else if (patch.op === "remove") {
setByPath(obj, patch.path, void 0);
}
return obj;
}
function compileSpecStream(stream, initial = {}) {
const lines = stream.split("\n");
const result = { ...initial };
for (const line of lines) {
const patch = parseSpecStreamLine(line);
if (patch) {
applySpecStreamPatch(result, patch);
}
}
return result;
}
function createSpecStreamCompiler(initial = {}) {
let result = { ...initial };
let buffer = "";
const appliedPatches = [];
const processedLines = /* @__PURE__ */ new Set();
return {
push(chunk) {
buffer += chunk;
const newPatches = [];
const lines = buffer.split("\n");
buffer = lines.pop() || "";
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed || processedLines.has(trimmed)) continue;
processedLines.add(trimmed);
const patch = parseSpecStreamLine(trimmed);
if (patch) {
applySpecStreamPatch(result, patch);
appliedPatches.push(patch);
newPatches.push(patch);
}
}
if (newPatches.length > 0) {
result = { ...result };
}
return { result, newPatches };
},
getResult() {
if (buffer.trim()) {
const patch = parseSpecStreamLine(buffer);
if (patch && !processedLines.has(buffer.trim())) {
processedLines.add(buffer.trim());
applySpecStreamPatch(result, patch);
appliedPatches.push(patch);
result = { ...result };
}
buffer = "";
}
return result;
},
getPatches() {
return [...appliedPatches];
},
reset(newInitial = {}) {
result = { ...newInitial };
buffer = "";
appliedPatches.length = 0;
processedLines.clear();
}
};
}

@@ -612,4 +755,363 @@ // src/visibility.ts

// src/schema.ts
var import_zod5 = require("zod");
function createBuilder() {
return {
string: () => ({ kind: "string" }),
number: () => ({ kind: "number" }),
boolean: () => ({ kind: "boolean" }),
array: (item) => ({ kind: "array", inner: item }),
object: (shape) => ({ kind: "object", inner: shape }),
record: (value) => ({ kind: "record", inner: value }),
any: () => ({ kind: "any" }),
zod: () => ({ kind: "zod" }),
ref: (path) => ({ kind: "ref", inner: path }),
propsOf: (path) => ({ kind: "propsOf", inner: path }),
map: (entryShape) => ({ kind: "map", inner: entryShape }),
optional: () => ({ optional: true })
};
}
function defineSchema(builder, options) {
const s = createBuilder();
const definition = builder(s);
return {
definition,
promptTemplate: options?.promptTemplate,
createCatalog(catalog) {
return createCatalogFromSchema(this, catalog);
}
};
}
function createCatalogFromSchema(schema, catalogData) {
const components = catalogData.components;
const actions = catalogData.actions;
const componentNames = components ? Object.keys(components) : [];
const actionNames = actions ? Object.keys(actions) : [];
const zodSchema = buildZodSchemaFromDefinition(
schema.definition,
catalogData
);
return {
schema,
data: catalogData,
componentNames,
actionNames,
prompt(options = {}) {
return generatePrompt(this, options);
},
jsonSchema() {
return zodToJsonSchema(zodSchema);
},
validate(spec) {
const result = zodSchema.safeParse(spec);
if (result.success) {
return {
success: true,
data: result.data
};
}
return { success: false, error: result.error };
},
zodSchema() {
return zodSchema;
},
get _specType() {
throw new Error("_specType is only for type inference");
}
};
}
function buildZodSchemaFromDefinition(definition, catalogData) {
return buildZodType(definition.spec, catalogData);
}
function buildZodType(schemaType, catalogData) {
switch (schemaType.kind) {
case "string":
return import_zod5.z.string();
case "number":
return import_zod5.z.number();
case "boolean":
return import_zod5.z.boolean();
case "any":
return import_zod5.z.any();
case "array": {
const inner = buildZodType(schemaType.inner, catalogData);
return import_zod5.z.array(inner);
}
case "object": {
const shape = schemaType.inner;
const zodShape = {};
for (const [key, value] of Object.entries(shape)) {
let zodType = buildZodType(value, catalogData);
if (value.optional) {
zodType = zodType.optional();
}
zodShape[key] = zodType;
}
return import_zod5.z.object(zodShape);
}
case "record": {
const inner = buildZodType(schemaType.inner, catalogData);
return import_zod5.z.record(import_zod5.z.string(), inner);
}
case "ref": {
const path = schemaType.inner;
const keys = getKeysFromPath(path, catalogData);
if (keys.length === 0) {
return import_zod5.z.string();
}
if (keys.length === 1) {
return import_zod5.z.literal(keys[0]);
}
return import_zod5.z.enum(keys);
}
case "propsOf": {
const path = schemaType.inner;
const propsSchemas = getPropsFromPath(path, catalogData);
if (propsSchemas.length === 0) {
return import_zod5.z.record(import_zod5.z.string(), import_zod5.z.unknown());
}
if (propsSchemas.length === 1) {
return propsSchemas[0];
}
return import_zod5.z.record(import_zod5.z.string(), import_zod5.z.unknown());
}
default:
return import_zod5.z.unknown();
}
}
function getKeysFromPath(path, catalogData) {
const parts = path.split(".");
let current = { catalog: catalogData };
for (const part of parts) {
if (current && typeof current === "object") {
current = current[part];
} else {
return [];
}
}
if (current && typeof current === "object") {
return Object.keys(current);
}
return [];
}
function getPropsFromPath(path, catalogData) {
const parts = path.split(".");
let current = { catalog: catalogData };
for (const part of parts) {
if (current && typeof current === "object") {
current = current[part];
} else {
return [];
}
}
if (current && typeof current === "object") {
return Object.values(current).map((entry) => entry.props).filter((props) => props !== void 0);
}
return [];
}
function generatePrompt(catalog, options) {
if (catalog.schema.promptTemplate) {
const context = {
catalog: catalog.data,
componentNames: catalog.componentNames,
actionNames: catalog.actionNames,
options,
formatZodType
};
return catalog.schema.promptTemplate(context);
}
const {
system = "You are a UI generator that outputs JSON.",
customRules = []
} = options;
const lines = [];
lines.push(system);
lines.push("");
lines.push("OUTPUT FORMAT:");
lines.push(
"Output JSONL (one JSON object per line) with patches to build a UI tree."
);
lines.push(
"Each line is a JSON patch operation. Start with the root, then add each element."
);
lines.push("");
lines.push("Example output (each line is a separate JSON object):");
lines.push("");
lines.push(`{"op":"set","path":"/root","value":"card-1"}
{"op":"set","path":"/elements/card-1","value":{"key":"card-1","type":"Card","props":{"title":"Dashboard"},"children":["metric-1","chart-1"],"parentKey":""}}
{"op":"set","path":"/elements/metric-1","value":{"key":"metric-1","type":"Metric","props":{"label":"Revenue","valuePath":"analytics.revenue","format":"currency"},"children":[],"parentKey":"card-1"}}
{"op":"set","path":"/elements/chart-1","value":{"key":"chart-1","type":"Chart","props":{"type":"bar","dataPath":"analytics.salesByRegion"},"children":[],"parentKey":"card-1"}}`);
lines.push("");
const components = catalog.data.components;
if (components) {
lines.push(`AVAILABLE COMPONENTS (${catalog.componentNames.length}):`);
lines.push("");
for (const [name, def] of Object.entries(components)) {
const propsStr = def.props ? formatZodType(def.props) : "{}";
const hasChildren = def.slots && def.slots.length > 0;
const childrenStr = hasChildren ? " [accepts children]" : "";
const descStr = def.description ? ` - ${def.description}` : "";
lines.push(`- ${name}: ${propsStr}${descStr}${childrenStr}`);
}
lines.push("");
}
const actions = catalog.data.actions;
if (actions && catalog.actionNames.length > 0) {
lines.push("AVAILABLE ACTIONS:");
lines.push("");
for (const [name, def] of Object.entries(actions)) {
lines.push(`- ${name}${def.description ? `: ${def.description}` : ""}`);
}
lines.push("");
}
lines.push("RULES:");
const baseRules = [
"Output ONLY JSONL patches - one JSON object per line, no markdown, no code fences",
'First line sets root: {"op":"set","path":"/root","value":"<root-key>"}',
'Then add each element: {"op":"set","path":"/elements/<key>","value":{...}}',
"ONLY use components listed above",
"Each element value needs: key, type, props, children (array of child keys), parentKey",
"Use unique keys (e.g., 'header', 'metric-1', 'chart-revenue')",
"Root element's parentKey is empty string, children reference their parent's key"
];
const allRules = [...baseRules, ...customRules];
allRules.forEach((rule, i) => {
lines.push(`${i + 1}. ${rule}`);
});
return lines.join("\n");
}
function getZodTypeName(schema) {
if (!schema || !schema._def) return "";
const def = schema._def;
return def.typeName ?? def.type ?? "";
}
function formatZodType(schema) {
if (!schema || !schema._def) return "unknown";
const def = schema._def;
const typeName = getZodTypeName(schema);
switch (typeName) {
case "ZodString":
case "string":
return "string";
case "ZodNumber":
case "number":
return "number";
case "ZodBoolean":
case "boolean":
return "boolean";
case "ZodLiteral":
case "literal":
return JSON.stringify(def.value);
case "ZodEnum":
case "enum": {
let values;
if (Array.isArray(def.values)) {
values = def.values;
} else if (def.entries && typeof def.entries === "object") {
values = Object.values(def.entries);
} else {
return "enum";
}
return values.map((v) => `"${v}"`).join(" | ");
}
case "ZodArray":
case "array": {
const inner = def.type ?? def.element;
return inner ? `Array<${formatZodType(inner)}>` : "Array<unknown>";
}
case "ZodObject":
case "object": {
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
if (!shape) return "object";
const props = Object.entries(shape).map(([key, value]) => {
const innerTypeName = getZodTypeName(value);
const isOptional = innerTypeName === "ZodOptional" || innerTypeName === "ZodNullable" || innerTypeName === "optional" || innerTypeName === "nullable";
return `${key}${isOptional ? "?" : ""}: ${formatZodType(value)}`;
}).join(", ");
return `{ ${props} }`;
}
case "ZodOptional":
case "optional":
case "ZodNullable":
case "nullable": {
const inner = def.innerType ?? def.wrapped;
return inner ? formatZodType(inner) : "unknown";
}
case "ZodUnion":
case "union": {
const options = def.options;
return options ? options.map((opt) => formatZodType(opt)).join(" | ") : "unknown";
}
default:
return "unknown";
}
}
function zodToJsonSchema(schema) {
const def = schema._def;
const typeName = def.typeName ?? "";
switch (typeName) {
case "ZodString":
return { type: "string" };
case "ZodNumber":
return { type: "number" };
case "ZodBoolean":
return { type: "boolean" };
case "ZodLiteral":
return { const: def.value };
case "ZodEnum":
return { enum: def.values };
case "ZodArray": {
const inner = def.type;
return {
type: "array",
items: inner ? zodToJsonSchema(inner) : {}
};
}
case "ZodObject": {
const shape = def.shape?.();
if (!shape) return { type: "object" };
const properties = {};
const required = [];
for (const [key, value] of Object.entries(shape)) {
properties[key] = zodToJsonSchema(value);
const innerDef = value._def;
if (innerDef.typeName !== "ZodOptional" && innerDef.typeName !== "ZodNullable") {
required.push(key);
}
}
return {
type: "object",
properties,
required: required.length > 0 ? required : void 0,
additionalProperties: false
};
}
case "ZodRecord": {
const valueType = def.valueType;
return {
type: "object",
additionalProperties: valueType ? zodToJsonSchema(valueType) : true
};
}
case "ZodOptional":
case "ZodNullable": {
const inner = def.innerType;
return inner ? zodToJsonSchema(inner) : {};
}
case "ZodUnion": {
const options = def.options;
return options ? { anyOf: options.map(zodToJsonSchema) } : {};
}
case "ZodAny":
return {};
default:
return {};
}
}
function defineCatalog(schema, catalog) {
return schema.createCatalog(catalog);
}
// src/catalog.ts
var import_zod5 = require("zod");
var import_zod6 = require("zod");
function createCatalog(config) {

@@ -628,8 +1130,8 @@ const {

const def = components[componentName];
return import_zod5.z.object({
key: import_zod5.z.string(),
type: import_zod5.z.literal(componentName),
return import_zod6.z.object({
key: import_zod6.z.string(),
type: import_zod6.z.literal(componentName),
props: def.props,
children: import_zod5.z.array(import_zod5.z.string()).optional(),
parentKey: import_zod5.z.string().nullable().optional(),
children: import_zod6.z.array(import_zod6.z.string()).optional(),
parentKey: import_zod6.z.string().nullable().optional(),
visible: VisibilityConditionSchema.optional()

@@ -640,8 +1142,8 @@ });

if (componentSchemas.length === 0) {
elementSchema = import_zod5.z.object({
key: import_zod5.z.string(),
type: import_zod5.z.string(),
props: import_zod5.z.record(import_zod5.z.string(), import_zod5.z.unknown()),
children: import_zod5.z.array(import_zod5.z.string()).optional(),
parentKey: import_zod5.z.string().nullable().optional(),
elementSchema = import_zod6.z.object({
key: import_zod6.z.string(),
type: import_zod6.z.string(),
props: import_zod6.z.record(import_zod6.z.string(), import_zod6.z.unknown()),
children: import_zod6.z.array(import_zod6.z.string()).optional(),
parentKey: import_zod6.z.string().nullable().optional(),
visible: VisibilityConditionSchema.optional()

@@ -652,3 +1154,3 @@ });

} else {
elementSchema = import_zod5.z.discriminatedUnion("type", [
elementSchema = import_zod6.z.discriminatedUnion("type", [
componentSchemas[0],

@@ -659,5 +1161,5 @@ componentSchemas[1],

}
const treeSchema = import_zod5.z.object({
root: import_zod5.z.string(),
elements: import_zod5.z.record(import_zod5.z.string(), elementSchema)
const specSchema = import_zod6.z.object({
root: import_zod6.z.string(),
elements: import_zod6.z.record(import_zod6.z.string(), elementSchema)
});

@@ -674,3 +1176,3 @@ return {

elementSchema,
treeSchema,
specSchema,
hasComponent(type) {

@@ -692,4 +1194,4 @@ return type in components;

},
validateTree(tree) {
const result = treeSchema.safeParse(tree);
validateSpec(spec) {
const result = specSchema.safeParse(spec);
if (result.success) {

@@ -750,2 +1252,151 @@ return { success: true, data: result.data };

}
function formatZodType2(schema, isOptional = false) {
const def = schema._def;
const typeName = def.typeName ?? "";
let result;
switch (typeName) {
case "ZodString":
result = "string";
break;
case "ZodNumber":
result = "number";
break;
case "ZodBoolean":
result = "boolean";
break;
case "ZodLiteral":
result = JSON.stringify(def.value);
break;
case "ZodEnum":
result = def.values.map((v) => `"${v}"`).join("|");
break;
case "ZodNativeEnum":
result = Object.values(def.values).map((v) => `"${v}"`).join("|");
break;
case "ZodArray":
result = def.type ? `Array<${formatZodType2(def.type)}>` : "Array<unknown>";
break;
case "ZodObject": {
if (!def.shape) {
result = "object";
break;
}
const shape = def.shape();
const props = Object.entries(shape).map(([key, value]) => {
const innerDef = value._def;
const innerOptional = innerDef.typeName === "ZodOptional" || innerDef.typeName === "ZodNullable";
return `${key}${innerOptional ? "?" : ""}: ${formatZodType2(value)}`;
}).join(", ");
result = `{ ${props} }`;
break;
}
case "ZodOptional":
return def.innerType ? formatZodType2(def.innerType, true) : "unknown?";
case "ZodNullable":
return def.innerType ? formatZodType2(def.innerType, true) : "unknown?";
case "ZodDefault":
return def.innerType ? formatZodType2(def.innerType, isOptional) : "unknown";
case "ZodUnion":
result = def.options ? def.options.map((opt) => formatZodType2(opt)).join("|") : "unknown";
break;
case "ZodNull":
result = "null";
break;
case "ZodUndefined":
result = "undefined";
break;
case "ZodAny":
result = "any";
break;
case "ZodUnknown":
result = "unknown";
break;
default:
result = "unknown";
}
return isOptional ? `${result}?` : result;
}
function extractPropsFromSchema(schema) {
const def = schema._def;
const typeName = def.typeName ?? "";
if (typeName !== "ZodObject" || !def.shape) {
return [];
}
const shape = def.shape();
return Object.entries(shape).map(([name, value]) => {
const innerDef = value._def;
const optional = innerDef.typeName === "ZodOptional" || innerDef.typeName === "ZodNullable";
return {
name,
type: formatZodType2(value),
optional
};
});
}
function formatPropsCompact(props) {
if (props.length === 0) return "{}";
const entries = props.map(
(p) => `${p.name}${p.optional ? "?" : ""}: ${p.type}`
);
return `{ ${entries.join(", ")} }`;
}
function generateSystemPrompt(catalog, options = {}) {
const {
system = "You are a UI generator that outputs JSONL (JSON Lines) patches.",
customRules = []
} = options;
const lines = [];
lines.push(system);
lines.push("");
const componentCount = catalog.componentNames.length;
lines.push(`AVAILABLE COMPONENTS (${componentCount}):`);
lines.push("");
for (const name of catalog.componentNames) {
const def = catalog.components[name];
const props = extractPropsFromSchema(def.props);
const propsStr = formatPropsCompact(props);
const hasChildrenStr = def.hasChildren ? " Has children." : "";
const descStr = def.description ? ` ${def.description}` : "";
lines.push(`- ${String(name)}: ${propsStr}${descStr}${hasChildrenStr}`);
}
lines.push("");
if (catalog.actionNames.length > 0) {
lines.push("AVAILABLE ACTIONS:");
lines.push("");
for (const name of catalog.actionNames) {
const def = catalog.actions[name];
lines.push(
`- ${String(name)}${def.description ? `: ${def.description}` : ""}`
);
}
lines.push("");
}
lines.push("OUTPUT FORMAT (JSONL):");
lines.push('{"op":"set","path":"/root","value":"element-key"}');
lines.push(
'{"op":"add","path":"/elements/key","value":{"key":"...","type":"...","props":{...},"children":[...]}}'
);
lines.push("");
lines.push("RULES:");
const baseRules = [
"First line sets /root to root element key",
"Add elements with /elements/{key}",
"Children array contains string keys, not objects",
"Parent first, then children",
"Each element needs: key, type, props",
"ONLY use props listed above - never invent new props"
];
const allRules = [...baseRules, ...customRules];
allRules.forEach((rule, i) => {
lines.push(`${i + 1}. ${rule}`);
});
lines.push("");
if (catalog.functionNames.length > 0) {
lines.push("CUSTOM VALIDATION FUNCTIONS:");
lines.push(catalog.functionNames.map(String).join(", "));
lines.push("");
}
lines.push("Generate JSONL:");
return lines.join("\n");
}
// Annotate the CommonJS export names for ESM import in node:

@@ -766,11 +1417,19 @@ 0 && (module.exports = {

action,
applySpecStreamPatch,
builtInValidationFunctions,
check,
compileSpecStream,
createCatalog,
createSpecStreamCompiler,
defineCatalog,
defineSchema,
evaluateLogicExpression,
evaluateVisibility,
executeAction,
findFormValue,
generateCatalogPrompt,
generateSystemPrompt,
getByPath,
interpolateString,
parseSpecStreamLine,
resolveAction,

@@ -777,0 +1436,0 @@ resolveDynamicValue,

@@ -49,2 +49,5 @@ // src/types.ts

}
function isNumericIndex(str) {
return /^\d+$/.test(str);
}
function setByPath(obj, path, value) {

@@ -56,10 +59,142 @@ const segments = path.startsWith("/") ? path.slice(1).split("/") : path.split("/");

const segment = segments[i];
if (!(segment in current) || typeof current[segment] !== "object") {
current[segment] = {};
const nextSegment = segments[i + 1];
const nextIsNumeric = nextSegment !== void 0 && isNumericIndex(nextSegment);
if (Array.isArray(current)) {
const index = parseInt(segment, 10);
if (current[index] === void 0 || typeof current[index] !== "object") {
current[index] = nextIsNumeric ? [] : {};
}
current = current[index];
} else {
if (!(segment in current) || typeof current[segment] !== "object") {
current[segment] = nextIsNumeric ? [] : {};
}
current = current[segment];
}
current = current[segment];
}
const lastSegment = segments[segments.length - 1];
current[lastSegment] = value;
if (Array.isArray(current)) {
const index = parseInt(lastSegment, 10);
current[index] = value;
} else {
current[lastSegment] = value;
}
}
function findFormValue(fieldName, params, data) {
if (params?.[fieldName] !== void 0) {
const val = params[fieldName];
if (typeof val !== "string" || !val.includes(".")) {
return val;
}
}
if (params) {
for (const key of Object.keys(params)) {
if (key.endsWith(`.${fieldName}`)) {
const val = params[key];
if (typeof val !== "string" || !val.includes(".")) {
return val;
}
}
}
}
if (data) {
for (const key of Object.keys(data)) {
if (key === fieldName || key.endsWith(`.${fieldName}`)) {
return data[key];
}
}
const prefixes = ["form", "newCustomer", "customer", ""];
for (const prefix of prefixes) {
const path = prefix ? `${prefix}/${fieldName}` : fieldName;
const val = getByPath(data, path);
if (val !== void 0) {
return val;
}
}
}
return void 0;
}
function parseSpecStreamLine(line) {
const trimmed = line.trim();
if (!trimmed || !trimmed.startsWith("{")) return null;
try {
const patch = JSON.parse(trimmed);
if (patch.op && patch.path !== void 0) {
return patch;
}
return null;
} catch {
return null;
}
}
function applySpecStreamPatch(obj, patch) {
if (patch.op === "set" || patch.op === "add" || patch.op === "replace") {
setByPath(obj, patch.path, patch.value);
} else if (patch.op === "remove") {
setByPath(obj, patch.path, void 0);
}
return obj;
}
function compileSpecStream(stream, initial = {}) {
const lines = stream.split("\n");
const result = { ...initial };
for (const line of lines) {
const patch = parseSpecStreamLine(line);
if (patch) {
applySpecStreamPatch(result, patch);
}
}
return result;
}
function createSpecStreamCompiler(initial = {}) {
let result = { ...initial };
let buffer = "";
const appliedPatches = [];
const processedLines = /* @__PURE__ */ new Set();
return {
push(chunk) {
buffer += chunk;
const newPatches = [];
const lines = buffer.split("\n");
buffer = lines.pop() || "";
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed || processedLines.has(trimmed)) continue;
processedLines.add(trimmed);
const patch = parseSpecStreamLine(trimmed);
if (patch) {
applySpecStreamPatch(result, patch);
appliedPatches.push(patch);
newPatches.push(patch);
}
}
if (newPatches.length > 0) {
result = { ...result };
}
return { result, newPatches };
},
getResult() {
if (buffer.trim()) {
const patch = parseSpecStreamLine(buffer);
if (patch && !processedLines.has(buffer.trim())) {
processedLines.add(buffer.trim());
applySpecStreamPatch(result, patch);
appliedPatches.push(patch);
result = { ...result };
}
buffer = "";
}
return result;
},
getPatches() {
return [...appliedPatches];
},
reset(newInitial = {}) {
result = { ...newInitial };
buffer = "";
appliedPatches.length = 0;
processedLines.clear();
}
};
}

@@ -558,4 +693,363 @@ // src/visibility.ts

// src/schema.ts
import { z as z5 } from "zod";
function createBuilder() {
return {
string: () => ({ kind: "string" }),
number: () => ({ kind: "number" }),
boolean: () => ({ kind: "boolean" }),
array: (item) => ({ kind: "array", inner: item }),
object: (shape) => ({ kind: "object", inner: shape }),
record: (value) => ({ kind: "record", inner: value }),
any: () => ({ kind: "any" }),
zod: () => ({ kind: "zod" }),
ref: (path) => ({ kind: "ref", inner: path }),
propsOf: (path) => ({ kind: "propsOf", inner: path }),
map: (entryShape) => ({ kind: "map", inner: entryShape }),
optional: () => ({ optional: true })
};
}
function defineSchema(builder, options) {
const s = createBuilder();
const definition = builder(s);
return {
definition,
promptTemplate: options?.promptTemplate,
createCatalog(catalog) {
return createCatalogFromSchema(this, catalog);
}
};
}
function createCatalogFromSchema(schema, catalogData) {
const components = catalogData.components;
const actions = catalogData.actions;
const componentNames = components ? Object.keys(components) : [];
const actionNames = actions ? Object.keys(actions) : [];
const zodSchema = buildZodSchemaFromDefinition(
schema.definition,
catalogData
);
return {
schema,
data: catalogData,
componentNames,
actionNames,
prompt(options = {}) {
return generatePrompt(this, options);
},
jsonSchema() {
return zodToJsonSchema(zodSchema);
},
validate(spec) {
const result = zodSchema.safeParse(spec);
if (result.success) {
return {
success: true,
data: result.data
};
}
return { success: false, error: result.error };
},
zodSchema() {
return zodSchema;
},
get _specType() {
throw new Error("_specType is only for type inference");
}
};
}
function buildZodSchemaFromDefinition(definition, catalogData) {
return buildZodType(definition.spec, catalogData);
}
function buildZodType(schemaType, catalogData) {
switch (schemaType.kind) {
case "string":
return z5.string();
case "number":
return z5.number();
case "boolean":
return z5.boolean();
case "any":
return z5.any();
case "array": {
const inner = buildZodType(schemaType.inner, catalogData);
return z5.array(inner);
}
case "object": {
const shape = schemaType.inner;
const zodShape = {};
for (const [key, value] of Object.entries(shape)) {
let zodType = buildZodType(value, catalogData);
if (value.optional) {
zodType = zodType.optional();
}
zodShape[key] = zodType;
}
return z5.object(zodShape);
}
case "record": {
const inner = buildZodType(schemaType.inner, catalogData);
return z5.record(z5.string(), inner);
}
case "ref": {
const path = schemaType.inner;
const keys = getKeysFromPath(path, catalogData);
if (keys.length === 0) {
return z5.string();
}
if (keys.length === 1) {
return z5.literal(keys[0]);
}
return z5.enum(keys);
}
case "propsOf": {
const path = schemaType.inner;
const propsSchemas = getPropsFromPath(path, catalogData);
if (propsSchemas.length === 0) {
return z5.record(z5.string(), z5.unknown());
}
if (propsSchemas.length === 1) {
return propsSchemas[0];
}
return z5.record(z5.string(), z5.unknown());
}
default:
return z5.unknown();
}
}
function getKeysFromPath(path, catalogData) {
const parts = path.split(".");
let current = { catalog: catalogData };
for (const part of parts) {
if (current && typeof current === "object") {
current = current[part];
} else {
return [];
}
}
if (current && typeof current === "object") {
return Object.keys(current);
}
return [];
}
function getPropsFromPath(path, catalogData) {
const parts = path.split(".");
let current = { catalog: catalogData };
for (const part of parts) {
if (current && typeof current === "object") {
current = current[part];
} else {
return [];
}
}
if (current && typeof current === "object") {
return Object.values(current).map((entry) => entry.props).filter((props) => props !== void 0);
}
return [];
}
function generatePrompt(catalog, options) {
if (catalog.schema.promptTemplate) {
const context = {
catalog: catalog.data,
componentNames: catalog.componentNames,
actionNames: catalog.actionNames,
options,
formatZodType
};
return catalog.schema.promptTemplate(context);
}
const {
system = "You are a UI generator that outputs JSON.",
customRules = []
} = options;
const lines = [];
lines.push(system);
lines.push("");
lines.push("OUTPUT FORMAT:");
lines.push(
"Output JSONL (one JSON object per line) with patches to build a UI tree."
);
lines.push(
"Each line is a JSON patch operation. Start with the root, then add each element."
);
lines.push("");
lines.push("Example output (each line is a separate JSON object):");
lines.push("");
lines.push(`{"op":"set","path":"/root","value":"card-1"}
{"op":"set","path":"/elements/card-1","value":{"key":"card-1","type":"Card","props":{"title":"Dashboard"},"children":["metric-1","chart-1"],"parentKey":""}}
{"op":"set","path":"/elements/metric-1","value":{"key":"metric-1","type":"Metric","props":{"label":"Revenue","valuePath":"analytics.revenue","format":"currency"},"children":[],"parentKey":"card-1"}}
{"op":"set","path":"/elements/chart-1","value":{"key":"chart-1","type":"Chart","props":{"type":"bar","dataPath":"analytics.salesByRegion"},"children":[],"parentKey":"card-1"}}`);
lines.push("");
const components = catalog.data.components;
if (components) {
lines.push(`AVAILABLE COMPONENTS (${catalog.componentNames.length}):`);
lines.push("");
for (const [name, def] of Object.entries(components)) {
const propsStr = def.props ? formatZodType(def.props) : "{}";
const hasChildren = def.slots && def.slots.length > 0;
const childrenStr = hasChildren ? " [accepts children]" : "";
const descStr = def.description ? ` - ${def.description}` : "";
lines.push(`- ${name}: ${propsStr}${descStr}${childrenStr}`);
}
lines.push("");
}
const actions = catalog.data.actions;
if (actions && catalog.actionNames.length > 0) {
lines.push("AVAILABLE ACTIONS:");
lines.push("");
for (const [name, def] of Object.entries(actions)) {
lines.push(`- ${name}${def.description ? `: ${def.description}` : ""}`);
}
lines.push("");
}
lines.push("RULES:");
const baseRules = [
"Output ONLY JSONL patches - one JSON object per line, no markdown, no code fences",
'First line sets root: {"op":"set","path":"/root","value":"<root-key>"}',
'Then add each element: {"op":"set","path":"/elements/<key>","value":{...}}',
"ONLY use components listed above",
"Each element value needs: key, type, props, children (array of child keys), parentKey",
"Use unique keys (e.g., 'header', 'metric-1', 'chart-revenue')",
"Root element's parentKey is empty string, children reference their parent's key"
];
const allRules = [...baseRules, ...customRules];
allRules.forEach((rule, i) => {
lines.push(`${i + 1}. ${rule}`);
});
return lines.join("\n");
}
function getZodTypeName(schema) {
if (!schema || !schema._def) return "";
const def = schema._def;
return def.typeName ?? def.type ?? "";
}
function formatZodType(schema) {
if (!schema || !schema._def) return "unknown";
const def = schema._def;
const typeName = getZodTypeName(schema);
switch (typeName) {
case "ZodString":
case "string":
return "string";
case "ZodNumber":
case "number":
return "number";
case "ZodBoolean":
case "boolean":
return "boolean";
case "ZodLiteral":
case "literal":
return JSON.stringify(def.value);
case "ZodEnum":
case "enum": {
let values;
if (Array.isArray(def.values)) {
values = def.values;
} else if (def.entries && typeof def.entries === "object") {
values = Object.values(def.entries);
} else {
return "enum";
}
return values.map((v) => `"${v}"`).join(" | ");
}
case "ZodArray":
case "array": {
const inner = def.type ?? def.element;
return inner ? `Array<${formatZodType(inner)}>` : "Array<unknown>";
}
case "ZodObject":
case "object": {
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
if (!shape) return "object";
const props = Object.entries(shape).map(([key, value]) => {
const innerTypeName = getZodTypeName(value);
const isOptional = innerTypeName === "ZodOptional" || innerTypeName === "ZodNullable" || innerTypeName === "optional" || innerTypeName === "nullable";
return `${key}${isOptional ? "?" : ""}: ${formatZodType(value)}`;
}).join(", ");
return `{ ${props} }`;
}
case "ZodOptional":
case "optional":
case "ZodNullable":
case "nullable": {
const inner = def.innerType ?? def.wrapped;
return inner ? formatZodType(inner) : "unknown";
}
case "ZodUnion":
case "union": {
const options = def.options;
return options ? options.map((opt) => formatZodType(opt)).join(" | ") : "unknown";
}
default:
return "unknown";
}
}
function zodToJsonSchema(schema) {
const def = schema._def;
const typeName = def.typeName ?? "";
switch (typeName) {
case "ZodString":
return { type: "string" };
case "ZodNumber":
return { type: "number" };
case "ZodBoolean":
return { type: "boolean" };
case "ZodLiteral":
return { const: def.value };
case "ZodEnum":
return { enum: def.values };
case "ZodArray": {
const inner = def.type;
return {
type: "array",
items: inner ? zodToJsonSchema(inner) : {}
};
}
case "ZodObject": {
const shape = def.shape?.();
if (!shape) return { type: "object" };
const properties = {};
const required = [];
for (const [key, value] of Object.entries(shape)) {
properties[key] = zodToJsonSchema(value);
const innerDef = value._def;
if (innerDef.typeName !== "ZodOptional" && innerDef.typeName !== "ZodNullable") {
required.push(key);
}
}
return {
type: "object",
properties,
required: required.length > 0 ? required : void 0,
additionalProperties: false
};
}
case "ZodRecord": {
const valueType = def.valueType;
return {
type: "object",
additionalProperties: valueType ? zodToJsonSchema(valueType) : true
};
}
case "ZodOptional":
case "ZodNullable": {
const inner = def.innerType;
return inner ? zodToJsonSchema(inner) : {};
}
case "ZodUnion": {
const options = def.options;
return options ? { anyOf: options.map(zodToJsonSchema) } : {};
}
case "ZodAny":
return {};
default:
return {};
}
}
function defineCatalog(schema, catalog) {
return schema.createCatalog(catalog);
}
// src/catalog.ts
import { z as z5 } from "zod";
import { z as z6 } from "zod";
function createCatalog(config) {

@@ -574,8 +1068,8 @@ const {

const def = components[componentName];
return z5.object({
key: z5.string(),
type: z5.literal(componentName),
return z6.object({
key: z6.string(),
type: z6.literal(componentName),
props: def.props,
children: z5.array(z5.string()).optional(),
parentKey: z5.string().nullable().optional(),
children: z6.array(z6.string()).optional(),
parentKey: z6.string().nullable().optional(),
visible: VisibilityConditionSchema.optional()

@@ -586,8 +1080,8 @@ });

if (componentSchemas.length === 0) {
elementSchema = z5.object({
key: z5.string(),
type: z5.string(),
props: z5.record(z5.string(), z5.unknown()),
children: z5.array(z5.string()).optional(),
parentKey: z5.string().nullable().optional(),
elementSchema = z6.object({
key: z6.string(),
type: z6.string(),
props: z6.record(z6.string(), z6.unknown()),
children: z6.array(z6.string()).optional(),
parentKey: z6.string().nullable().optional(),
visible: VisibilityConditionSchema.optional()

@@ -598,3 +1092,3 @@ });

} else {
elementSchema = z5.discriminatedUnion("type", [
elementSchema = z6.discriminatedUnion("type", [
componentSchemas[0],

@@ -605,5 +1099,5 @@ componentSchemas[1],

}
const treeSchema = z5.object({
root: z5.string(),
elements: z5.record(z5.string(), elementSchema)
const specSchema = z6.object({
root: z6.string(),
elements: z6.record(z6.string(), elementSchema)
});

@@ -620,3 +1114,3 @@ return {

elementSchema,
treeSchema,
specSchema,
hasComponent(type) {

@@ -638,4 +1132,4 @@ return type in components;

},
validateTree(tree) {
const result = treeSchema.safeParse(tree);
validateSpec(spec) {
const result = specSchema.safeParse(spec);
if (result.success) {

@@ -696,2 +1190,151 @@ return { success: true, data: result.data };

}
function formatZodType2(schema, isOptional = false) {
const def = schema._def;
const typeName = def.typeName ?? "";
let result;
switch (typeName) {
case "ZodString":
result = "string";
break;
case "ZodNumber":
result = "number";
break;
case "ZodBoolean":
result = "boolean";
break;
case "ZodLiteral":
result = JSON.stringify(def.value);
break;
case "ZodEnum":
result = def.values.map((v) => `"${v}"`).join("|");
break;
case "ZodNativeEnum":
result = Object.values(def.values).map((v) => `"${v}"`).join("|");
break;
case "ZodArray":
result = def.type ? `Array<${formatZodType2(def.type)}>` : "Array<unknown>";
break;
case "ZodObject": {
if (!def.shape) {
result = "object";
break;
}
const shape = def.shape();
const props = Object.entries(shape).map(([key, value]) => {
const innerDef = value._def;
const innerOptional = innerDef.typeName === "ZodOptional" || innerDef.typeName === "ZodNullable";
return `${key}${innerOptional ? "?" : ""}: ${formatZodType2(value)}`;
}).join(", ");
result = `{ ${props} }`;
break;
}
case "ZodOptional":
return def.innerType ? formatZodType2(def.innerType, true) : "unknown?";
case "ZodNullable":
return def.innerType ? formatZodType2(def.innerType, true) : "unknown?";
case "ZodDefault":
return def.innerType ? formatZodType2(def.innerType, isOptional) : "unknown";
case "ZodUnion":
result = def.options ? def.options.map((opt) => formatZodType2(opt)).join("|") : "unknown";
break;
case "ZodNull":
result = "null";
break;
case "ZodUndefined":
result = "undefined";
break;
case "ZodAny":
result = "any";
break;
case "ZodUnknown":
result = "unknown";
break;
default:
result = "unknown";
}
return isOptional ? `${result}?` : result;
}
function extractPropsFromSchema(schema) {
const def = schema._def;
const typeName = def.typeName ?? "";
if (typeName !== "ZodObject" || !def.shape) {
return [];
}
const shape = def.shape();
return Object.entries(shape).map(([name, value]) => {
const innerDef = value._def;
const optional = innerDef.typeName === "ZodOptional" || innerDef.typeName === "ZodNullable";
return {
name,
type: formatZodType2(value),
optional
};
});
}
function formatPropsCompact(props) {
if (props.length === 0) return "{}";
const entries = props.map(
(p) => `${p.name}${p.optional ? "?" : ""}: ${p.type}`
);
return `{ ${entries.join(", ")} }`;
}
function generateSystemPrompt(catalog, options = {}) {
const {
system = "You are a UI generator that outputs JSONL (JSON Lines) patches.",
customRules = []
} = options;
const lines = [];
lines.push(system);
lines.push("");
const componentCount = catalog.componentNames.length;
lines.push(`AVAILABLE COMPONENTS (${componentCount}):`);
lines.push("");
for (const name of catalog.componentNames) {
const def = catalog.components[name];
const props = extractPropsFromSchema(def.props);
const propsStr = formatPropsCompact(props);
const hasChildrenStr = def.hasChildren ? " Has children." : "";
const descStr = def.description ? ` ${def.description}` : "";
lines.push(`- ${String(name)}: ${propsStr}${descStr}${hasChildrenStr}`);
}
lines.push("");
if (catalog.actionNames.length > 0) {
lines.push("AVAILABLE ACTIONS:");
lines.push("");
for (const name of catalog.actionNames) {
const def = catalog.actions[name];
lines.push(
`- ${String(name)}${def.description ? `: ${def.description}` : ""}`
);
}
lines.push("");
}
lines.push("OUTPUT FORMAT (JSONL):");
lines.push('{"op":"set","path":"/root","value":"element-key"}');
lines.push(
'{"op":"add","path":"/elements/key","value":{"key":"...","type":"...","props":{...},"children":[...]}}'
);
lines.push("");
lines.push("RULES:");
const baseRules = [
"First line sets /root to root element key",
"Add elements with /elements/{key}",
"Children array contains string keys, not objects",
"Parent first, then children",
"Each element needs: key, type, props",
"ONLY use props listed above - never invent new props"
];
const allRules = [...baseRules, ...customRules];
allRules.forEach((rule, i) => {
lines.push(`${i + 1}. ${rule}`);
});
lines.push("");
if (catalog.functionNames.length > 0) {
lines.push("CUSTOM VALIDATION FUNCTIONS:");
lines.push(catalog.functionNames.map(String).join(", "));
lines.push("");
}
lines.push("Generate JSONL:");
return lines.join("\n");
}
export {

@@ -711,11 +1354,19 @@ ActionConfirmSchema,

action,
applySpecStreamPatch,
builtInValidationFunctions,
check,
compileSpecStream,
createCatalog,
createSpecStreamCompiler,
defineCatalog,
defineSchema,
evaluateLogicExpression,
evaluateVisibility,
executeAction,
findFormValue,
generateCatalogPrompt,
generateSystemPrompt,
getByPath,
interpolateString,
parseSpecStreamLine,
resolveAction,

@@ -722,0 +1373,0 @@ resolveDynamicValue,

+1
-1
{
"name": "@json-render/core",
"version": "0.3.0",
"version": "0.4.0",
"license": "Apache-2.0",

@@ -5,0 +5,0 @@ "description": "JSON becomes real things. Define your catalog, register your components, let AI generate.",

+136
-122
# @json-render/core
**Predictable. Guardrailed. Fast.** Core library for safe, user-prompted UI generation.
Core library for json-render. Define schemas, create catalogs, generate AI prompts, and stream specs.
## Features
- **Conditional Visibility**: Show/hide components based on data paths, auth state, or complex logic expressions
- **Rich Actions**: Actions with typed parameters, confirmation dialogs, and success/error callbacks
- **Enhanced Validation**: Built-in validation functions with custom catalog functions support
- **Type-Safe Catalog**: Define component schemas using Zod for full type safety
- **Framework Agnostic**: Core logic is independent of UI frameworks
## Installation
```bash
npm install @json-render/core
# or
pnpm add @json-render/core
npm install @json-render/core zod
```
## Key Concepts
- **Schema**: Defines the structure of specs and catalogs
- **Catalog**: Maps component/action names to their definitions with Zod props
- **Spec**: JSON output from AI that conforms to the schema
- **SpecStream**: JSONL streaming format for progressive spec building
## Quick Start
### Define a Schema
```typescript
import { defineSchema } from "@json-render/core";
export const schema = defineSchema((s) => ({
spec: s.object({
root: s.object({
type: s.ref("catalog.components"),
props: s.propsOf("catalog.components"),
children: s.array(s.self()),
}),
}),
catalog: s.object({
components: s.map({
props: s.zod(),
description: s.string(),
}),
actions: s.map({
description: s.string(),
}),
}),
}), {
promptTemplate: myPromptTemplate, // Optional custom AI prompt generator
});
```
### Create a Catalog
```typescript
import { createCatalog } from '@json-render/core';
import { z } from 'zod';
import { defineCatalog } from "@json-render/core";
import { schema } from "./schema";
import { z } from "zod";
const catalog = createCatalog({
name: 'My Dashboard',
export const catalog = defineCatalog(schema, {
components: {

@@ -35,6 +59,5 @@ Card: {

title: z.string(),
description: z.string().nullable(),
subtitle: z.string().nullable(),
}),
hasChildren: true,
description: 'A card container',
description: "A card container with title",
},

@@ -44,106 +67,83 @@ Button: {

label: z.string(),
action: ActionSchema,
variant: z.enum(["primary", "secondary"]).nullable(),
}),
description: 'A clickable button',
description: "A clickable button",
},
},
actions: {
submit: { description: 'Submit the form' },
export: {
params: z.object({ format: z.enum(['csv', 'pdf']) }),
description: 'Export data',
},
submit: { description: "Submit the form" },
cancel: { description: "Cancel and close" },
},
functions: {
customValidation: (value) => typeof value === 'string' && value.length > 0,
},
});
```
### Visibility Conditions
### Generate AI Prompts
```typescript
import { visibility, evaluateVisibility } from '@json-render/core';
// Generate system prompt for AI
const systemPrompt = catalog.prompt();
// Simple path-based visibility
const element1 = {
key: 'error-banner',
type: 'Alert',
props: { message: 'Error!' },
visible: { path: '/form/hasError' },
};
// Auth-based visibility
const element2 = {
key: 'admin-panel',
type: 'Card',
props: { title: 'Admin' },
visible: { auth: 'signedIn' },
};
// Complex logic
const element3 = {
key: 'notification',
type: 'Alert',
props: { message: 'Warning' },
visible: {
and: [
{ path: '/settings/notifications' },
{ not: { path: '/user/dismissed' } },
{ gt: [{ path: '/items/count' }, 10] },
],
},
};
// Evaluate visibility
const isVisible = evaluateVisibility(element1.visible, {
dataModel: { form: { hasError: true } },
// With custom rules
const systemPrompt = catalog.prompt({
system: "You are a dashboard builder.",
customRules: [
"Always include a header",
"Use Card components for grouping",
],
});
```
### Rich Actions
### Stream AI Responses (SpecStream)
The SpecStream format uses JSONL patches to progressively build specs:
```typescript
import { resolveAction, executeAction } from '@json-render/core';
import { createSpecStreamCompiler } from "@json-render/core";
const buttonAction = {
name: 'refund',
params: {
paymentId: { path: '/selected/id' },
amount: 100,
},
confirm: {
title: 'Confirm Refund',
message: 'Refund $100 to customer?',
variant: 'danger',
},
onSuccess: { navigate: '/payments' },
onError: { set: { '/ui/error': '$error.message' } },
};
// Create a compiler for your spec type
const compiler = createSpecStreamCompiler<MySpec>();
// Resolve dynamic values
const resolved = resolveAction(buttonAction, dataModel);
// Process streaming chunks from AI
while (streaming) {
const chunk = await reader.read();
const { result, newPatches } = compiler.push(chunk);
if (newPatches.length > 0) {
// Update UI with partial result
setSpec(result);
}
}
// Get final compiled result
const finalSpec = compiler.getResult();
```
### Validation
SpecStream format (each line is a JSON patch):
```jsonl
{"op":"set","path":"/root/type","value":"Card"}
{"op":"set","path":"/root/props","value":{"title":"Hello"}}
{"op":"set","path":"/root/children/0","value":{"type":"Button","props":{"label":"Click"}}}
```
### Low-Level Utilities
```typescript
import { runValidation, check } from '@json-render/core';
import {
parseSpecStreamLine,
applySpecStreamPatch,
compileSpecStream,
} from "@json-render/core";
const config = {
checks: [
check.required('Email is required'),
check.email('Invalid email'),
check.maxLength(100, 'Too long'),
],
validateOn: 'blur',
};
// Parse a single line
const patch = parseSpecStreamLine('{"op":"set","path":"/root","value":{}}');
// { op: "set", path: "/root", value: {} }
const result = runValidation(config, {
value: 'user@example.com',
dataModel: {},
});
// Apply a patch to an object
const obj = {};
applySpecStreamPatch(obj, patch);
// obj is now { root: {} }
// result.valid = true
// result.errors = []
// Compile entire JSONL string at once
const spec = compileSpecStream<MySpec>(jsonlString);
```

@@ -153,35 +153,49 @@

### Visibility
### Schema
- `evaluateVisibility(condition, context)` - Evaluate a visibility condition
- `evaluateLogicExpression(expr, context)` - Evaluate a logic expression
- `visibility.*` - Helper functions for creating visibility conditions
| Export | Purpose |
|--------|---------|
| `defineSchema(builder, options?)` | Create a schema with spec/catalog structure |
| `SchemaBuilder` | Builder with `s.object()`, `s.array()`, `s.map()`, etc. |
### Actions
### Catalog
- `resolveAction(action, dataModel)` - Resolve dynamic values in an action
- `executeAction(context)` - Execute an action with callbacks
- `interpolateString(template, dataModel)` - Interpolate `${path}` in strings
| Export | Purpose |
|--------|---------|
| `defineCatalog(schema, data)` | Create a type-safe catalog from schema |
| `catalog.prompt(options?)` | Generate AI system prompt |
### Validation
### SpecStream
- `runValidation(config, context)` - Run validation checks
- `runValidationCheck(check, context)` - Run a single validation check
- `builtInValidationFunctions` - Built-in validators (required, email, min, max, etc.)
- `check.*` - Helper functions for creating validation checks
| Export | Purpose |
|--------|---------|
| `createSpecStreamCompiler<T>()` | Create streaming compiler |
| `parseSpecStreamLine(line)` | Parse single JSONL line |
| `applySpecStreamPatch(obj, patch)` | Apply patch to object |
| `compileSpecStream<T>(jsonl)` | Compile entire JSONL string |
### Catalog
### Types
- `createCatalog(config)` - Create a catalog with components, actions, and functions
- `generateCatalogPrompt(catalog)` - Generate an AI prompt describing the catalog
| Export | Purpose |
|--------|---------|
| `Spec` | Base spec type |
| `Catalog` | Catalog type |
| `SpecStreamLine` | Single patch operation |
| `SpecStreamCompiler` | Streaming compiler interface |
## Types
## Custom Schemas
See `src/types.ts` for full type definitions:
json-render supports completely different spec formats for different renderers:
- `UIElement` - Base element structure
- `UITree` - Flat tree structure
- `VisibilityCondition` - Visibility condition types
- `LogicExpression` - Logic expression types
- `Action` - Rich action definition
- `ValidationConfig` - Validation configuration
```typescript
// React: Element tree
{ root: { type: "Card", props: {...}, children: [...] } }
// Remotion: Timeline
{ composition: {...}, tracks: [...], clips: [...] }
// Your own: Whatever you need
{ pages: [...], navigation: {...}, theme: {...} }
```
Each renderer defines its own schema with `defineSchema()` and its own prompt template.

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display