core

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

@json-render/core

@json-render/core

Core package for schema definition, catalog creation, and spec streaming.
用于schema定义、目录创建和规范流处理的核心包。

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
  • Schema:定义规范和目录的结构(使用
    defineSchema
  • Catalog:将组件/操作名称映射到对应的定义(使用
    defineCatalog
  • Spec:AI输出的符合schema要求的JSON内容
  • SpecStream:用于渐进式构建规范的JSONL流格式

Defining a Schema

定义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
});
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",
    },
  },
});
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

生成AI提示词

typescript
const systemPrompt = catalog.prompt(); // Uses schema's promptTemplate
const systemPrompt = catalog.prompt({ customRules: ["Rule 1", "Rule 2"] });
typescript
const systemPrompt = catalog.prompt(); // Uses schema's promptTemplate
const systemPrompt = catalog.prompt({ customRules: ["Rule 1", "Rule 2"] });

SpecStream Utilities

SpecStream工具集

For streaming AI responses (JSONL patches):
typescript
import { createSpecStreamCompiler } from "@json-render/core";

const compiler = createSpecStreamCompiler<MySpec>();

// Process streaming chunks
const { result, newPatches } = compiler.push(chunk);

// Get final result
const finalSpec = compiler.getResult();
用于处理流式AI响应(JSONL补丁):
typescript
import { createSpecStreamCompiler } from "@json-render/core";

const compiler = createSpecStreamCompiler<MySpec>();

// 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": <condition>, "$then": <value>, "$else": <value> }
    - evaluates a visibility condition and picks a branch
  • { "$template": "Hello, ${/user/name}!" }
    - interpolates
    ${/path}
    references with state values
  • { "$computed": "fnName", "args": { "key": <expression> } }
    - 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": "/state/key" }
    - 从状态模型读取值(单向读取)
  • { "$bindState": "/path" }
    - 双向绑定:从状态读取值且支持回写。用在表单组件的原生值属性(value、checked、pressed等)上。
  • { "$bindItem": "field" }
    - 绑定到循环项字段的双向绑定,在循环作用域内使用。
  • { "$cond": <condition>, "$then": <value>, "$else": <value> }
    - 计算可见性条件并选择对应的分支值
  • { "$template": "Hello, ${/user/name}!" }
    - 使用状态值插值替换
    ${/path}
    引用
  • { "$computed": "fnName", "args": { "key": <expression> } }
    - 调用已注册的函数并传入解析后的参数
$cond
使用和可见性条件相同的语法(
$state
eq
neq
not
,数组表示逻辑与)。
$then
$else
本身也可以是表达式(递归支持)。
组件不需要使用
statePath
属性实现双向绑定,而是在原生值属性(例如
value
checked
pressed
)上使用
{ "$bindState": "/path" }
即可。
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.
元素可以声明
watch
字段(顶级字段,和type/props/children同级),在状态值变化时触发动作:
json
{
  "type": "Select",
  "props": { "value": { "$bindState": "/form/country" }, "options": ["US", "Canada"] },
  "watch": {
    "/form/country": { "action": "loadCities", "params": { "country": { "$state": "/form/country" } } }
  },
  "children": []
}
监听器仅在值变化时触发,初始渲染时不会触发。

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");
内置校验函数:
required
email
url
numeric
minLength
maxLength
min
max
pattern
matches
equalTo
lessThan
greaterThan
requiredIf
跨字段校验在参数中使用
$state
表达式:
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 (patch-only mode)
buildUserPrompt({ prompt: "add a toggle", currentSpec: spec });

// With runtime state
buildUserPrompt({ prompt: "show data", state: { todos: [] } });
构建结构化的用户提示词,支持可选的规范优化和状态上下文:
typescript
import { buildUserPrompt } from "@json-render/core";

// Fresh generation
buildUserPrompt({ prompt: "create a todo app" });

// Refinement (patch-only mode)
buildUserPrompt({ prompt: "add a toggle", currentSpec: spec });

// With runtime state
buildUserPrompt({ prompt: "show data", state: { todos: [] } });

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);
校验规范结构并自动修复常见问题:
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
通过基于状态的条件控制元素可见性。
VisibilityContext
{ 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

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
.
Schema可以声明
builtInActions
——这些动作在运行时始终可用,且会自动注入到提示词中:
typescript
const schema = defineSchema(builder, {
  builtInActions: [
    { name: "setState", description: "Update a value in the state model" },
  ],
});
这些动作会在提示词中标记为
[built-in]
,不需要在
defineRegistry
中添加处理逻辑。

StateStore

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)
.
StateStore
接口支持接入外部状态管理库(Redux、Zustand、XState等)到json-render渲染器中。
createStateStore
工厂函数可以创建简单的内存实现:
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 }
});
StateStore
接口包含:
get(path)
set(path, value)
update(updates)
getSnapshot()
subscribe(listener)

Key Exports

核心导出项

ExportPurpose
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
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)
导出项用途
defineSchema
创建新的schema
defineCatalog
基于schema创建目录
createStateStore
创建框架无关的内存型
StateStore
resolvePropValue
基于数据解析单个属性表达式
resolveElementProps
解析元素中所有的属性表达式
buildUserPrompt
构建带优化逻辑和状态上下文的用户提示词
validateSpec
校验规范结构
autoFixSpec
自动修复常见的规范问题
createSpecStreamCompiler
将JSONL补丁流转换为规范
createJsonRenderTransform
转换流,从混合流中分离文本和JSONL内容
parseSpecStreamLine
解析单行JSONL内容
applySpecStreamPatch
为对象应用补丁
StateStore
接入外部状态管理的接口
ComputedFunction
$computed
表达式的函数签名
check
用于创建校验规则的TypeScript助手
BuiltInAction
内置动作定义的类型(
name
+
description
ActionBinding
动作绑定类型(包含
preventDefault
字段)