--- name: core description: Core package for defining schemas, catalogs, and AI prompt generation for json-render. Use when working with @json-render/core, defining schemas, creating catalogs, or building JSON specs for UI/video generation. --- # @json-render/core Core package for schema definition, catalog creation, and spec streaming. ## Key Concepts - **Schema**: Defines the structure of specs and catalogs (use `defineSchema`) - **Catalog**: Maps component/action names to their definitions (use `defineCatalog`) - **Spec**: JSON output from AI that conforms to the schema - **SpecStream**: JSONL streaming format for progressive spec building ## Defining a Schema ```typescript import { defineSchema } from "@json-render/core"; export const schema = defineSchema((s) => ({ spec: s.object({ // Define spec structure }), catalog: s.object({ components: s.map({ props: s.zod(), description: s.string(), }), }), }), { promptTemplate: myPromptTemplate, // Optional custom AI prompt }); ``` ## Creating a Catalog ```typescript import { defineCatalog } from "@json-render/core"; import { schema } from "./schema"; import { z } from "zod"; export const catalog = defineCatalog(schema, { components: { Button: { props: z.object({ label: z.string(), variant: z.enum(["primary", "secondary"]).nullable(), }), description: "Clickable button component", }, }, }); ``` ## Generating AI Prompts ```typescript const systemPrompt = catalog.prompt(); // Uses schema's promptTemplate const systemPrompt = catalog.prompt({ customRules: ["Rule 1", "Rule 2"] }); ``` ## SpecStream Utilities For streaming AI responses (JSONL patches): ```typescript import { createSpecStreamCompiler } from "@json-render/core"; const compiler = createSpecStreamCompiler(); // Process streaming chunks const { result, newPatches } = compiler.push(chunk); // Get final result const finalSpec = compiler.getResult(); ``` ## Dynamic Prop Expressions Any prop value can be a dynamic expression resolved at render time: - **`{ "$state": "/state/key" }`** - reads a value from the state model (one-way read) - **`{ "$bindState": "/path" }`** - two-way binding: reads from state and enables write-back. Use on the natural value prop (value, checked, pressed, etc.) of form components. - **`{ "$bindItem": "field" }`** - two-way binding to a repeat item field. Use inside repeat scopes. - **`{ "$cond": , "$then": , "$else": }`** - evaluates a visibility condition and picks a branch - **`{ "$template": "Hello, ${/user/name}!" }`** - interpolates `${/path}` references with state values - **`{ "$computed": "fnName", "args": { "key": } }`** - calls a registered function with resolved args `$cond` uses the same syntax as visibility conditions (`$state`, `eq`, `neq`, `not`, arrays for AND). `$then` and `$else` can themselves be expressions (recursive). Components do not use a `statePath` prop for two-way binding. Instead, use `{ "$bindState": "/path" }` on the natural value prop (e.g. `value`, `checked`, `pressed`). ```json { "color": { "$cond": { "$state": "/activeTab", "eq": "home" }, "$then": "#007AFF", "$else": "#8E8E93" }, "label": { "$template": "Welcome, ${/user/name}!" }, "fullName": { "$computed": "fullName", "args": { "first": { "$state": "/form/firstName" }, "last": { "$state": "/form/lastName" } } } } ``` ```typescript import { resolvePropValue, resolveElementProps } from "@json-render/core"; const resolved = resolveElementProps(element.props, { stateModel: myState }); ``` ## State Watchers Elements can declare a `watch` field (top-level, sibling of type/props/children) to trigger actions when state values change: ```json { "type": "Select", "props": { "value": { "$bindState": "/form/country" }, "options": ["US", "Canada"] }, "watch": { "/form/country": { "action": "loadCities", "params": { "country": { "$state": "/form/country" } } } }, "children": [] } ``` Watchers only fire on value changes, not on initial render. ## Validation Built-in validation functions: `required`, `email`, `url`, `numeric`, `minLength`, `maxLength`, `min`, `max`, `pattern`, `matches`, `equalTo`, `lessThan`, `greaterThan`, `requiredIf`. Cross-field validation uses `$state` expressions in args: ```typescript import { check } from "@json-render/core"; check.required("Field is required"); check.matches("/form/password", "Passwords must match"); check.lessThan("/form/endDate", "Must be before end date"); check.greaterThan("/form/startDate", "Must be after start date"); check.requiredIf("/form/enableNotifications", "Required when enabled"); ``` ## User Prompt Builder Build structured user prompts with optional spec refinement and state context: ```typescript import { buildUserPrompt } from "@json-render/core"; // Fresh generation buildUserPrompt({ prompt: "create a todo app" }); // Refinement with edit modes (default: patch-only) buildUserPrompt({ prompt: "add a toggle", currentSpec: spec, editModes: ["patch", "merge"] }); // With runtime state buildUserPrompt({ prompt: "show data", state: { todos: [] } }); ``` Available edit modes: `"patch"` (RFC 6902 JSON Patch), `"merge"` (RFC 7396 Merge Patch), `"diff"` (unified diff). ## Spec Validation Validate spec structure and auto-fix common issues: ```typescript import { validateSpec, autoFixSpec } from "@json-render/core"; const { valid, issues } = validateSpec(spec); const fixed = autoFixSpec(spec); ``` ## Visibility Conditions Control element visibility with state-based conditions. `VisibilityContext` is `{ stateModel: StateModel }`. ```typescript import { visibility } from "@json-render/core"; // Syntax { "$state": "/path" } // truthiness { "$state": "/path", "not": true } // falsy { "$state": "/path", "eq": value } // equality [ cond1, cond2 ] // implicit AND // Helpers visibility.when("/path") // { $state: "/path" } visibility.unless("/path") // { $state: "/path", not: true } visibility.eq("/path", val) // { $state: "/path", eq: val } visibility.and(cond1, cond2) // { $and: [cond1, cond2] } visibility.or(cond1, cond2) // { $or: [cond1, cond2] } visibility.always // true visibility.never // false ``` ## Built-in Actions in Schema Schemas can declare `builtInActions` -- actions that are always available at runtime and auto-injected into prompts: ```typescript const schema = defineSchema(builder, { builtInActions: [ { name: "setState", description: "Update a value in the state model" }, ], }); ``` These appear in prompts as `[built-in]` and don't require handlers in `defineRegistry`. ## StateStore The `StateStore` interface allows external state management libraries (Redux, Zustand, XState, etc.) to be plugged into json-render renderers. The `createStateStore` factory creates a simple in-memory implementation: ```typescript import { createStateStore, type StateStore } from "@json-render/core"; const store = createStateStore({ count: 0 }); store.get("/count"); // 0 store.set("/count", 1); // updates and notifies subscribers store.update({ "/a": 1, "/b": 2 }); // batch update store.subscribe(() => { console.log(store.getSnapshot()); // { count: 1 } }); ``` The `StateStore` interface: `get(path)`, `set(path, value)`, `update(updates)`, `getSnapshot()`, `subscribe(listener)`. ## Key Exports | Export | Purpose | |--------|---------| | `defineSchema` | Create a new schema | | `defineCatalog` | Create a catalog from schema | | `createStateStore` | Create a framework-agnostic in-memory `StateStore` | | `resolvePropValue` | Resolve a single prop expression against data | | `resolveElementProps` | Resolve all prop expressions in an element | | `buildUserPrompt` | Build user prompts with refinement and state context | | `buildEditUserPrompt` | Build user prompt for editing existing specs | | `buildEditInstructions` | Generate prompt section for available edit modes | | `isNonEmptySpec` | Check if spec has root and at least one element | | `deepMergeSpec` | RFC 7396 deep merge (null deletes, arrays replace, objects recurse) | | `diffToPatches` | Generate RFC 6902 JSON Patch operations from object diff | | `EditMode` | Type: `"patch" \| "merge" \| "diff"` | | `validateSpec` | Validate spec structure | | `autoFixSpec` | Auto-fix common spec issues | | `createSpecStreamCompiler` | Stream JSONL patches into spec | | `createJsonRenderTransform` | TransformStream separating text from JSONL in mixed streams | | `parseSpecStreamLine` | Parse single JSONL line | | `applySpecStreamPatch` | Apply patch to object | | `StateStore` | Interface for plugging in external state management | | `ComputedFunction` | Function signature for `$computed` expressions | | `check` | TypeScript helpers for creating validation checks | | `BuiltInAction` | Type for built-in action definitions (`name` + `description`) | | `ActionBinding` | Action binding type (includes `preventDefault` field) |