|
|
5 달 전 | |
|---|---|---|
| .. | ||
| src | 5 달 전 | |
| CHANGELOG.md | 5 달 전 | |
| README.md | 5 달 전 | |
| package.json | 5 달 전 | |
| tsconfig.json | 5 달 전 | |
| tsup.config.ts | 5 달 전 | |
Core library for json-render. Define schemas, create catalogs, generate AI prompts, and stream specs.
npm install @json-render/core zod
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
});
import { defineCatalog } from "@json-render/core";
import { schema } from "./schema";
import { z } from "zod";
export const catalog = defineCatalog(schema, {
components: {
Card: {
props: z.object({
title: z.string(),
subtitle: z.string().nullable(),
}),
description: "A card container with title",
},
Button: {
props: z.object({
label: z.string(),
variant: z.enum(["primary", "secondary"]).nullable(),
}),
description: "A clickable button",
},
},
actions: {
submit: { description: "Submit the form" },
cancel: { description: "Cancel and close" },
},
});
// Generate system prompt for AI
const systemPrompt = catalog.prompt();
// With custom rules
const systemPrompt = catalog.prompt({
system: "You are a dashboard builder.",
customRules: [
"Always include a header",
"Use Card components for grouping",
],
});
The SpecStream format uses JSONL patches to progressively build specs:
import { createSpecStreamCompiler } from "@json-render/core";
// Create a compiler for your spec type
const compiler = createSpecStreamCompiler<MySpec>();
// 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();
SpecStream format uses RFC 6902 JSON Patch operations (each line is a patch):
{"op":"add","path":"/root/type","value":"Card"}
{"op":"add","path":"/root/props","value":{"title":"Hello"}}
{"op":"add","path":"/root/children/0","value":{"type":"Button","props":{"label":"Click"}}}
All six RFC 6902 operations are supported: add, remove, replace, move, copy, test.
import {
parseSpecStreamLine,
applySpecStreamPatch,
compileSpecStream,
} from "@json-render/core";
// Parse a single line
const patch = parseSpecStreamLine('{"op":"add","path":"/root","value":{}}');
// { op: "add", path: "/root", value: {} }
// Apply a patch to an object
const obj = {};
applySpecStreamPatch(obj, patch);
// obj is now { root: {} }
// Compile entire JSONL string at once
const spec = compileSpecStream<MySpec>(jsonlString);
| Export | Purpose |
|---|---|
defineSchema(builder, options?) |
Create a schema with spec/catalog structure |
SchemaBuilder |
Builder with s.object(), s.array(), s.map(), etc. |
| Export | Purpose |
|---|---|
defineCatalog(schema, data) |
Create a type-safe catalog from schema |
catalog.prompt(options?) |
Generate AI system prompt |
| 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 |
| Export | Purpose |
|---|---|
resolvePropValue(value, ctx) |
Resolve a single prop expression |
resolveElementProps(props, ctx) |
Resolve all prop expressions in an element |
PropExpression<T> |
Type for prop values that may contain expressions |
| Export | Purpose |
|---|---|
buildUserPrompt(options) |
Build a user prompt with optional spec refinement and state context |
UserPromptOptions |
Options type for buildUserPrompt |
| Export | Purpose |
|---|---|
validateSpec(spec, catalog?) |
Validate spec structure and return issues |
autoFixSpec(spec) |
Auto-fix common spec issues (returns corrected copy) |
formatSpecIssues(issues) |
Format validation issues as readable strings |
| Export | Purpose |
|---|---|
Spec |
Base spec type |
Catalog |
Catalog type |
VisibilityCondition |
Visibility condition type (used by $cond) |
VisibilityContext |
Context for evaluating visibility and prop expressions |
SpecStreamLine |
Single patch operation |
SpecStreamCompiler |
Streaming compiler interface |
Any prop value can be a dynamic expression that resolves based on data state at render time. Expressions are resolved by the renderer before props reach components.
$path)Read a value directly from the state model:
{
"color": { "$path": "/theme/primary" },
"label": { "$path": "/user/name" }
}
$cond / $then / $else)Evaluate a condition (same syntax as visibility conditions) and pick a value:
{
"color": {
"$cond": { "eq": [{ "path": "/activeTab" }, "home"] },
"$then": "#007AFF",
"$else": "#8E8E93"
},
"name": {
"$cond": { "eq": [{ "path": "/activeTab" }, "home"] },
"$then": "home",
"$else": "home-outline"
}
}
$then and $else can themselves be expressions (recursive):
{
"label": {
"$cond": { "path": "/user/isAdmin" },
"$then": { "$path": "/admin/greeting" },
"$else": "Welcome"
}
}
import { resolvePropValue, resolveElementProps } from "@json-render/core";
// Resolve a single value
const color = resolvePropValue(
{ $cond: { eq: [{ path: "/active" }, "yes"] }, $then: "blue", $else: "gray" },
{ stateModel: myState }
);
// Resolve all props on an element
const resolved = resolveElementProps(element.props, { stateModel: myState });
Build structured user prompts for AI generation, with support for refinement and state context:
import { buildUserPrompt } from "@json-render/core";
// Fresh generation
const prompt = buildUserPrompt({ prompt: "create a todo app" });
// Refinement with existing spec (triggers patch-only mode)
const refinementPrompt = buildUserPrompt({
prompt: "add a dark mode toggle",
currentSpec: existingSpec,
});
// With runtime state context
const contextPrompt = buildUserPrompt({
prompt: "show my data",
state: { todos: [{ text: "Buy milk" }] },
});
Validate spec structure and auto-fix common issues:
import { validateSpec, autoFixSpec, formatSpecIssues } from "@json-render/core";
// Validate a spec
const { valid, issues } = validateSpec(spec, catalog);
// Format issues for display
console.log(formatSpecIssues(issues));
// Auto-fix common issues (returns a corrected copy)
const fixed = autoFixSpec(spec);
json-render supports completely different spec formats for different renderers:
// 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.