blocks-schema 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Constructive
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # blocks-schema
2
+
3
+ Portable JSON UI document specification for Constructive Blocks: the storage
4
+ format, runtime validators, JSON Schema export, and pure document-manipulation
5
+ API. No React — this package is safe in servers, workers, and agents.
6
+
7
+ ## Overview
8
+
9
+ | Layer | Purpose | File |
10
+ |-------|---------|------|
11
+ | **Envelope** | `UIDocument` container (`formatVersion`, `id`, `page`) | `envelope.ts` |
12
+ | **Nodes** | Recursive `UINode` tree, known node type sets | `node.ts` |
13
+ | **Validation** | Zod parsers for documents and nodes | `zod.ts` |
14
+ | **JSON Schema** | Exported JSON Schema of the format (for agents/tooling) | `json-schema.ts` |
15
+ | **Compose** | Pure fragment/slot/override composition | `compose.ts` |
16
+ | **Fields** | Field collection helpers and constraint validation | `node.ts`, `validation.ts` |
17
+
18
+ Rendering lives in adapters — `blocks-renderer` is the React
19
+ adapter over this spec.
20
+
21
+ ## Storage Schema
22
+
23
+ ### Document
24
+
25
+ ```json
26
+ {
27
+ "formatVersion": "1.0",
28
+ "type": "UISchema",
29
+ "id": "orders-form",
30
+ "meta": { "title": "Orders" },
31
+ "page": { "type": "Page", "key": "page", "props": {}, "children": [] }
32
+ }
33
+ ```
34
+
35
+ ### Node
36
+
37
+ ```typescript
38
+ interface UINode {
39
+ type: string; // Known block type or any registry-resolved type
40
+ key: string; // Unique within the document; identity for overrides
41
+ props: UINodeProps; // Static props (label, name, defaultValue, constraints, ...)
42
+ children: UINode[];
43
+ bindings?: Record<string, string>; // prop → "{{ scope.path }}" template
44
+ actions?: Record<string, UIAction>; // event → flow/handler action
45
+ }
46
+ ```
47
+
48
+ Unknown node types are valid documents — resolution happens at render time,
49
+ and adapters must render a visible fallback rather than throw.
50
+
51
+ ## Normative Rules
52
+
53
+ - `key` must be unique within a document; it is the node's identity for
54
+ overrides, slots, and tooling.
55
+ - Documents are plain declarative JSON: no expressions beyond `{{ path }}`
56
+ bindings, no embedded code.
57
+ - `Fragment` nodes reference reusable subtrees via `props.ref`.
58
+ - `Slot` nodes declare named insertion points via `props.name`; their children
59
+ are the default content when no filler is supplied.
60
+ - Composition is pure and never mutates its input.
61
+
62
+ ## API
63
+
64
+ ```typescript
65
+ import {
66
+ parseDocument, safeParseDocument, isUIDocument, // validation
67
+ toDocumentJsonSchema, toNodeJsonSchema, // JSON Schema export
68
+ composeDocument, // fragments/slots/overrides
69
+ collectFieldNames, collectDefaultValues,
70
+ collectFieldConstraints, validateField, // form helpers
71
+ walkNodes, findNodeByKey,
72
+ } from 'blocks-schema';
73
+
74
+ const composed = composeDocument(document, {
75
+ fragments: { address: addressSubtree },
76
+ slots: { header: customHeaderNode },
77
+ overrides: { title: { props: { label: 'Headline' } }, legacy: { remove: true } },
78
+ });
79
+ ```
package/dist/index.cjs ADDED
@@ -0,0 +1,312 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+
5
+ // src/compose.ts
6
+ function applyOverride(node, override) {
7
+ return {
8
+ ...node,
9
+ ...override.type ? { type: override.type } : {},
10
+ props: { ...node.props, ...override.props },
11
+ ...override.bindings ? { bindings: { ...node.bindings, ...override.bindings } } : {},
12
+ ...override.actions ? { actions: { ...node.actions, ...override.actions } } : {}
13
+ };
14
+ }
15
+ function expandChild(node, options) {
16
+ if (node.type === "Fragment") {
17
+ const ref = node.props?.ref;
18
+ const fragment = typeof ref === "string" ? options.fragments?.[ref] : void 0;
19
+ return fragment ? [composeNode(fragment, options)] : [composeNode({ ...node, children: [] }, options)];
20
+ }
21
+ if (node.type === "Slot") {
22
+ const name = node.props?.name;
23
+ const filler = typeof name === "string" ? options.slots?.[name] : void 0;
24
+ if (filler === void 0) {
25
+ return (node.children ?? []).flatMap((child) => expandChild(child, options));
26
+ }
27
+ const nodes = Array.isArray(filler) ? filler : [filler];
28
+ return nodes.map((filled) => composeNode(filled, options));
29
+ }
30
+ return [composeNode(node, options)];
31
+ }
32
+ function composeNode(node, options) {
33
+ const override = options.overrides?.[node.key];
34
+ const base = override ? applyOverride(node, override) : node;
35
+ const children = (base.children ?? []).filter((child) => !options.overrides?.[child.key]?.remove).flatMap((child) => expandChild(child, options));
36
+ return { ...base, children };
37
+ }
38
+ function composeDocument(document, options = {}) {
39
+ return { ...document, page: composeNode(document.page, options) };
40
+ }
41
+ function composeNodeTree(node, options = {}) {
42
+ return composeNode(node, options);
43
+ }
44
+
45
+ // src/envelope.ts
46
+ var UI_DOCUMENT_FORMAT_VERSION = "1.0";
47
+ var UI_DOCUMENT_TYPE = "UISchema";
48
+ function isUIDocument(value) {
49
+ if (!value || typeof value !== "object") return false;
50
+ const candidate = value;
51
+ return candidate.type === UI_DOCUMENT_TYPE && candidate.formatVersion === UI_DOCUMENT_FORMAT_VERSION && !!candidate.page;
52
+ }
53
+ var isUISchema = isUIDocument;
54
+ function createDocument(page, options = {}) {
55
+ return {
56
+ formatVersion: UI_DOCUMENT_FORMAT_VERSION,
57
+ type: UI_DOCUMENT_TYPE,
58
+ id: options.id ?? "document",
59
+ ...options.meta ? { meta: options.meta } : {},
60
+ page
61
+ };
62
+ }
63
+ var uiNodeConstraintsSchema = zod.z.object({
64
+ minLength: zod.z.number().int().nonnegative().optional(),
65
+ maxLength: zod.z.number().int().nonnegative().optional(),
66
+ minValue: zod.z.number().optional(),
67
+ maxValue: zod.z.number().optional(),
68
+ pattern: zod.z.string().optional(),
69
+ precision: zod.z.number().int().nonnegative().optional(),
70
+ scale: zod.z.number().int().nonnegative().optional()
71
+ });
72
+ var uiNodePropsSchema = zod.z.looseObject({
73
+ fieldId: zod.z.string().optional(),
74
+ name: zod.z.string().optional(),
75
+ label: zod.z.string().optional(),
76
+ description: zod.z.string().optional(),
77
+ placeholder: zod.z.string().optional(),
78
+ required: zod.z.boolean().optional(),
79
+ hidden: zod.z.boolean().optional(),
80
+ disabled: zod.z.boolean().optional(),
81
+ defaultValue: zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean(), zod.z.null()]).optional(),
82
+ constraints: uiNodeConstraintsSchema.optional(),
83
+ className: zod.z.string().optional()
84
+ });
85
+ var uiBindingSchema = zod.z.record(zod.z.string(), zod.z.string());
86
+ var uiActionSchema = zod.z.object({
87
+ type: zod.z.enum(["flow", "handler"]),
88
+ flowId: zod.z.string().optional(),
89
+ handler: zod.z.string().optional(),
90
+ inputMapping: zod.z.record(zod.z.string(), zod.z.string()).optional(),
91
+ params: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
92
+ });
93
+ var uiActionsSchema = zod.z.record(zod.z.string(), uiActionSchema);
94
+ var uiNodeSchema = zod.z.lazy(
95
+ () => zod.z.object({
96
+ type: zod.z.string().min(1),
97
+ key: zod.z.string().min(1),
98
+ props: uiNodePropsSchema.default({}),
99
+ children: zod.z.array(uiNodeSchema).default([]),
100
+ bindings: uiBindingSchema.optional(),
101
+ actions: uiActionsSchema.optional()
102
+ })
103
+ );
104
+ var uiRegistrySourceSchema = zod.z.object({
105
+ name: zod.z.string().min(1),
106
+ url: zod.z.string().min(1)
107
+ });
108
+ var uiDataSourceSchema = zod.z.looseObject({
109
+ name: zod.z.string().min(1),
110
+ table: zod.z.string().optional(),
111
+ query: zod.z.string().optional(),
112
+ variables: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
113
+ });
114
+ var uiDocumentMetadataSchema = zod.z.looseObject({
115
+ title: zod.z.string().optional(),
116
+ description: zod.z.string().optional()
117
+ });
118
+ var uiDocumentSchema = zod.z.object({
119
+ formatVersion: zod.z.literal(UI_DOCUMENT_FORMAT_VERSION),
120
+ type: zod.z.literal(UI_DOCUMENT_TYPE),
121
+ id: zod.z.string().min(1),
122
+ meta: uiDocumentMetadataSchema.optional(),
123
+ registries: zod.z.array(uiRegistrySourceSchema).optional(),
124
+ dataSources: zod.z.array(uiDataSourceSchema).optional(),
125
+ page: uiNodeSchema
126
+ });
127
+ function parseDocument(value) {
128
+ return uiDocumentSchema.parse(value);
129
+ }
130
+ function safeParseDocument(value) {
131
+ return uiDocumentSchema.safeParse(value);
132
+ }
133
+ function parseNode(value) {
134
+ return uiNodeSchema.parse(value);
135
+ }
136
+
137
+ // src/json-schema.ts
138
+ function toDocumentJsonSchema() {
139
+ return zod.z.toJSONSchema(uiDocumentSchema, { io: "input" });
140
+ }
141
+ function toNodeJsonSchema() {
142
+ return zod.z.toJSONSchema(uiNodeSchema, { io: "input" });
143
+ }
144
+
145
+ // src/node.ts
146
+ var WIDGET_NODE_TYPES = [
147
+ "Input",
148
+ "Textarea",
149
+ "Select",
150
+ "RadioGroup",
151
+ "Checkbox",
152
+ "Switch",
153
+ "NumberInput",
154
+ "DatePicker",
155
+ "DateTimePicker",
156
+ "TimePicker",
157
+ "PhoneInput",
158
+ "CodeEditor",
159
+ "MarkdownEditor",
160
+ "JsonEditor",
161
+ "FileUpload"
162
+ ];
163
+ var CONTAINER_NODE_TYPES = ["Page", "Form", "Grid", "GridColumn", "Section", "Tabs", "Tab"];
164
+ var BLOCK_NODE_TYPES = [
165
+ "DataTable",
166
+ "DetailPanel",
167
+ "RelationList",
168
+ "StatCard",
169
+ "Chart",
170
+ "ActionBar",
171
+ "Markdown",
172
+ "AgentChat",
173
+ "Button",
174
+ "Slot",
175
+ "Fragment",
176
+ "Custom"
177
+ ];
178
+ var widgetTypes = new Set(WIDGET_NODE_TYPES);
179
+ var containerTypes = new Set(CONTAINER_NODE_TYPES);
180
+ var blockTypes = new Set(BLOCK_NODE_TYPES);
181
+ function isWidgetNodeType(type) {
182
+ return widgetTypes.has(type);
183
+ }
184
+ function isContainerNodeType(type) {
185
+ return containerTypes.has(type);
186
+ }
187
+ function isKnownNodeType(type) {
188
+ return widgetTypes.has(type) || containerTypes.has(type) || blockTypes.has(type);
189
+ }
190
+ function isWidgetNode(node) {
191
+ return isWidgetNodeType(node.type);
192
+ }
193
+ function isContainerNode(node) {
194
+ return isContainerNodeType(node.type);
195
+ }
196
+ function* walkNodes(node) {
197
+ yield node;
198
+ for (const child of node.children ?? []) {
199
+ yield* walkNodes(child);
200
+ }
201
+ }
202
+ function collectFieldNames(node) {
203
+ const names = [];
204
+ for (const current of walkNodes(node)) {
205
+ if (isWidgetNode(current) && typeof current.props?.name === "string") {
206
+ names.push(current.props.name);
207
+ }
208
+ }
209
+ return names;
210
+ }
211
+ function collectDefaultValues(node) {
212
+ const values = {};
213
+ for (const current of walkNodes(node)) {
214
+ if (!isWidgetNode(current) || typeof current.props?.name !== "string") continue;
215
+ if (current.props.defaultValue !== void 0) {
216
+ values[current.props.name] = current.props.defaultValue;
217
+ }
218
+ }
219
+ return values;
220
+ }
221
+ function collectFieldConstraints(node) {
222
+ const result = {};
223
+ for (const current of walkNodes(node)) {
224
+ if (!isWidgetNode(current) || typeof current.props?.name !== "string") continue;
225
+ result[current.props.name] = {
226
+ constraints: current.props.constraints,
227
+ required: current.props.required
228
+ };
229
+ }
230
+ return result;
231
+ }
232
+ function findNodeByKey(node, key) {
233
+ for (const current of walkNodes(node)) {
234
+ if (current.key === key) return current;
235
+ }
236
+ return void 0;
237
+ }
238
+
239
+ // src/validation.ts
240
+ function validateField(value, constraints, required) {
241
+ const stringValue = value == null ? "" : String(value);
242
+ const isEmpty = stringValue.trim() === "";
243
+ if (required && isEmpty) {
244
+ return "This field is required";
245
+ }
246
+ if (isEmpty) return null;
247
+ if (constraints?.minLength != null && stringValue.length < constraints.minLength) {
248
+ return `Minimum ${constraints.minLength} characters required`;
249
+ }
250
+ if (constraints?.maxLength != null && stringValue.length > constraints.maxLength) {
251
+ return `Maximum ${constraints.maxLength} characters allowed`;
252
+ }
253
+ if (constraints?.minValue != null && typeof value === "number" && value < constraints.minValue) {
254
+ return `Minimum value is ${constraints.minValue}`;
255
+ }
256
+ if (constraints?.maxValue != null && typeof value === "number" && value > constraints.maxValue) {
257
+ return `Maximum value is ${constraints.maxValue}`;
258
+ }
259
+ if (constraints?.pattern) {
260
+ const regex = compilePattern(constraints.pattern);
261
+ if (regex && !regex.test(stringValue)) {
262
+ return "Invalid format";
263
+ }
264
+ }
265
+ return null;
266
+ }
267
+ function compilePattern(pattern) {
268
+ try {
269
+ return new RegExp(pattern);
270
+ } catch {
271
+ return null;
272
+ }
273
+ }
274
+
275
+ exports.BLOCK_NODE_TYPES = BLOCK_NODE_TYPES;
276
+ exports.CONTAINER_NODE_TYPES = CONTAINER_NODE_TYPES;
277
+ exports.UI_DOCUMENT_FORMAT_VERSION = UI_DOCUMENT_FORMAT_VERSION;
278
+ exports.UI_DOCUMENT_TYPE = UI_DOCUMENT_TYPE;
279
+ exports.WIDGET_NODE_TYPES = WIDGET_NODE_TYPES;
280
+ exports.collectDefaultValues = collectDefaultValues;
281
+ exports.collectFieldConstraints = collectFieldConstraints;
282
+ exports.collectFieldNames = collectFieldNames;
283
+ exports.composeDocument = composeDocument;
284
+ exports.composeNodeTree = composeNodeTree;
285
+ exports.createDocument = createDocument;
286
+ exports.findNodeByKey = findNodeByKey;
287
+ exports.isContainerNode = isContainerNode;
288
+ exports.isContainerNodeType = isContainerNodeType;
289
+ exports.isKnownNodeType = isKnownNodeType;
290
+ exports.isUIDocument = isUIDocument;
291
+ exports.isUISchema = isUISchema;
292
+ exports.isWidgetNode = isWidgetNode;
293
+ exports.isWidgetNodeType = isWidgetNodeType;
294
+ exports.parseDocument = parseDocument;
295
+ exports.parseNode = parseNode;
296
+ exports.safeParseDocument = safeParseDocument;
297
+ exports.toDocumentJsonSchema = toDocumentJsonSchema;
298
+ exports.toNodeJsonSchema = toNodeJsonSchema;
299
+ exports.uiActionSchema = uiActionSchema;
300
+ exports.uiActionsSchema = uiActionsSchema;
301
+ exports.uiBindingSchema = uiBindingSchema;
302
+ exports.uiDataSourceSchema = uiDataSourceSchema;
303
+ exports.uiDocumentMetadataSchema = uiDocumentMetadataSchema;
304
+ exports.uiDocumentSchema = uiDocumentSchema;
305
+ exports.uiNodeConstraintsSchema = uiNodeConstraintsSchema;
306
+ exports.uiNodePropsSchema = uiNodePropsSchema;
307
+ exports.uiNodeSchema = uiNodeSchema;
308
+ exports.uiRegistrySourceSchema = uiRegistrySourceSchema;
309
+ exports.validateField = validateField;
310
+ exports.walkNodes = walkNodes;
311
+ //# sourceMappingURL=index.cjs.map
312
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/compose.ts","../src/envelope.ts","../src/zod.ts","../src/json-schema.ts","../src/node.ts","../src/validation.ts"],"names":["z"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { UIDocument } from './envelope';\nimport type { UIActions, UIBinding, UINode, UINodeProps, UINodeType } from './node';\n\n/**\n * A patch applied to the node with a given `key`. Composition is per node, not\n * per document, so a generated default can be customized in a few places\n * without giving up generation.\n */\nexport interface NodeOverride {\n\ttype?: UINodeType;\n\tprops?: UINodeProps;\n\tbindings?: UIBinding;\n\tactions?: UIActions;\n\t/** Drop the node (and its subtree) from the composed document. */\n\tremove?: boolean;\n}\n\nexport type NodeOverrides = Record<string, NodeOverride>;\n\n/** Reusable subtrees addressed by `Fragment` nodes via `props.ref`. */\nexport type FragmentMap = Record<string, UINode>;\n\n/** Subtrees that fill `Slot` nodes, addressed by `props.name`. */\nexport type SlotMap = Record<string, UINode | UINode[]>;\n\nexport interface ComposeOptions {\n\tfragments?: FragmentMap;\n\tslots?: SlotMap;\n\toverrides?: NodeOverrides;\n}\n\nfunction applyOverride(node: UINode, override: NodeOverride): UINode {\n\treturn {\n\t\t...node,\n\t\t...(override.type ? { type: override.type } : {}),\n\t\tprops: { ...node.props, ...override.props },\n\t\t...(override.bindings ? { bindings: { ...node.bindings, ...override.bindings } } : {}),\n\t\t...(override.actions ? { actions: { ...node.actions, ...override.actions } } : {}),\n\t};\n}\n\nfunction expandChild(node: UINode, options: ComposeOptions): UINode[] {\n\tif (node.type === 'Fragment') {\n\t\tconst ref = node.props?.ref;\n\t\tconst fragment = typeof ref === 'string' ? options.fragments?.[ref] : undefined;\n\t\t// An unresolved reference stays in the tree so the renderer surfaces it\n\t\t// rather than silently dropping content.\n\t\treturn fragment ? [composeNode(fragment, options)] : [composeNode({ ...node, children: [] }, options)];\n\t}\n\n\tif (node.type === 'Slot') {\n\t\tconst name = node.props?.name;\n\t\tconst filler = typeof name === 'string' ? options.slots?.[name] : undefined;\n\t\tif (filler === undefined) {\n\t\t\t// No filler: fall back to the slot's own children (its default content).\n\t\t\treturn (node.children ?? []).flatMap((child) => expandChild(child, options));\n\t\t}\n\t\tconst nodes = Array.isArray(filler) ? filler : [filler];\n\t\treturn nodes.map((filled) => composeNode(filled, options));\n\t}\n\n\treturn [composeNode(node, options)];\n}\n\nfunction composeNode(node: UINode, options: ComposeOptions): UINode {\n\tconst override = options.overrides?.[node.key];\n\tconst base = override ? applyOverride(node, override) : node;\n\n\tconst children = (base.children ?? [])\n\t\t.filter((child) => !options.overrides?.[child.key]?.remove)\n\t\t.flatMap((child) => expandChild(child, options));\n\n\treturn { ...base, children };\n}\n\n/**\n * Compose a document: expand `Fragment` references, fill `Slot` nodes, then\n * apply per-node overrides. Pure — the input document is never mutated.\n */\nexport function composeDocument(document: UIDocument, options: ComposeOptions = {}): UIDocument {\n\treturn { ...document, page: composeNode(document.page, options) };\n}\n\nexport function composeNodeTree(node: UINode, options: ComposeOptions = {}): UINode {\n\treturn composeNode(node, options);\n}\n","import type { UINode } from './node';\n\nexport const UI_DOCUMENT_FORMAT_VERSION = '1.0';\nexport const UI_DOCUMENT_TYPE = 'UISchema';\n\nexport interface UIDocumentMetadata {\n\ttitle?: string;\n\tdescription?: string;\n\t[key: string]: unknown;\n}\n\n/**\n * A resolution source for node types: a shadcn-style registry URL template,\n * e.g. `https://constructive-io.github.io/blocks/r/{name}.json`.\n */\nexport interface UIRegistrySource {\n\tname: string;\n\turl: string;\n}\n\n/** A named, read-only query a document's blocks can bind against. */\nexport interface UIDataSource {\n\tname: string;\n\ttable?: string;\n\tquery?: string;\n\tvariables?: Record<string, unknown>;\n\tselect?: string;\n\twhere?: Record<string, unknown>;\n\torderBy?: unknown;\n\tfirst?: number;\n}\n\nexport interface UIDocument {\n\tformatVersion: typeof UI_DOCUMENT_FORMAT_VERSION;\n\ttype: typeof UI_DOCUMENT_TYPE;\n\tid: string;\n\tmeta?: UIDocumentMetadata;\n\tregistries?: UIRegistrySource[];\n\tdataSources?: UIDataSource[];\n\tpage: UINode;\n}\n\n/**\n * The name the deployed dashboard form builder uses for the same envelope.\n * Kept as an alias so existing `UISchema` consumers migrate by import swap.\n */\nexport type UISchema = UIDocument;\n\nexport function isUIDocument(value: unknown): value is UIDocument {\n\tif (!value || typeof value !== 'object') return false;\n\tconst candidate = value as Record<string, unknown>;\n\treturn (\n\t\tcandidate.type === UI_DOCUMENT_TYPE &&\n\t\tcandidate.formatVersion === UI_DOCUMENT_FORMAT_VERSION &&\n\t\t!!candidate.page\n\t);\n}\n\n/** @deprecated Use {@link isUIDocument}. */\nexport const isUISchema = isUIDocument;\n\nexport function createDocument(page: UINode, options: { id?: string; meta?: UIDocumentMetadata } = {}): UIDocument {\n\treturn {\n\t\tformatVersion: UI_DOCUMENT_FORMAT_VERSION,\n\t\ttype: UI_DOCUMENT_TYPE,\n\t\tid: options.id ?? 'document',\n\t\t...(options.meta ? { meta: options.meta } : {}),\n\t\tpage,\n\t};\n}\n","import { z } from 'zod';\n\nimport { UI_DOCUMENT_FORMAT_VERSION, UI_DOCUMENT_TYPE } from './envelope';\nimport type { UIDocument } from './envelope';\nimport type { UINode } from './node';\n\nexport const uiNodeConstraintsSchema = z.object({\n\tminLength: z.number().int().nonnegative().optional(),\n\tmaxLength: z.number().int().nonnegative().optional(),\n\tminValue: z.number().optional(),\n\tmaxValue: z.number().optional(),\n\tpattern: z.string().optional(),\n\tprecision: z.number().int().nonnegative().optional(),\n\tscale: z.number().int().nonnegative().optional(),\n});\n\nexport const uiNodePropsSchema = z.looseObject({\n\tfieldId: z.string().optional(),\n\tname: z.string().optional(),\n\tlabel: z.string().optional(),\n\tdescription: z.string().optional(),\n\tplaceholder: z.string().optional(),\n\trequired: z.boolean().optional(),\n\thidden: z.boolean().optional(),\n\tdisabled: z.boolean().optional(),\n\tdefaultValue: z.union([z.string(), z.number(), z.boolean(), z.null()]).optional(),\n\tconstraints: uiNodeConstraintsSchema.optional(),\n\tclassName: z.string().optional(),\n});\n\nexport const uiBindingSchema = z.record(z.string(), z.string());\n\nexport const uiActionSchema = z.object({\n\ttype: z.enum(['flow', 'handler']),\n\tflowId: z.string().optional(),\n\thandler: z.string().optional(),\n\tinputMapping: z.record(z.string(), z.string()).optional(),\n\tparams: z.record(z.string(), z.unknown()).optional(),\n});\n\nexport const uiActionsSchema = z.record(z.string(), uiActionSchema);\n\nexport const uiNodeSchema: z.ZodType<UINode> = z.lazy(() =>\n\tz.object({\n\t\ttype: z.string().min(1),\n\t\tkey: z.string().min(1),\n\t\tprops: uiNodePropsSchema.default({}),\n\t\tchildren: z.array(uiNodeSchema).default([]),\n\t\tbindings: uiBindingSchema.optional(),\n\t\tactions: uiActionsSchema.optional(),\n\t}),\n);\n\nexport const uiRegistrySourceSchema = z.object({\n\tname: z.string().min(1),\n\turl: z.string().min(1),\n});\n\nexport const uiDataSourceSchema = z.looseObject({\n\tname: z.string().min(1),\n\ttable: z.string().optional(),\n\tquery: z.string().optional(),\n\tvariables: z.record(z.string(), z.unknown()).optional(),\n});\n\nexport const uiDocumentMetadataSchema = z.looseObject({\n\ttitle: z.string().optional(),\n\tdescription: z.string().optional(),\n});\n\nexport const uiDocumentSchema: z.ZodType<UIDocument> = z.object({\n\tformatVersion: z.literal(UI_DOCUMENT_FORMAT_VERSION),\n\ttype: z.literal(UI_DOCUMENT_TYPE),\n\tid: z.string().min(1),\n\tmeta: uiDocumentMetadataSchema.optional(),\n\tregistries: z.array(uiRegistrySourceSchema).optional(),\n\tdataSources: z.array(uiDataSourceSchema).optional(),\n\tpage: uiNodeSchema,\n});\n\n/** Throws a `ZodError` describing every problem in the document. */\nexport function parseDocument(value: unknown): UIDocument {\n\treturn uiDocumentSchema.parse(value);\n}\n\nexport function safeParseDocument(value: unknown) {\n\treturn uiDocumentSchema.safeParse(value);\n}\n\nexport function parseNode(value: unknown): UINode {\n\treturn uiNodeSchema.parse(value);\n}\n","import { z } from 'zod';\n\nimport { uiDocumentSchema, uiNodeSchema } from './zod';\n\n/**\n * JSON Schema for the document envelope, for agents emitting documents as tool\n * output and for registry/editor tooling that validates without importing zod.\n */\nexport function toDocumentJsonSchema(): Record<string, unknown> {\n\treturn z.toJSONSchema(uiDocumentSchema, { io: 'input' }) as Record<string, unknown>;\n}\n\nexport function toNodeJsonSchema(): Record<string, unknown> {\n\treturn z.toJSONSchema(uiNodeSchema, { io: 'input' }) as Record<string, unknown>;\n}\n","/**\n * Node-level types for the portable JSON UI document format.\n *\n * A document is a tree of typed nodes. A node's `type` is resolved to a\n * component by the renderer's widget registry, so this package never imports\n * React and stays usable on a server, in an agent, or in a validator.\n */\n\n/** Field widget node types (a form's leaves). */\nexport const WIDGET_NODE_TYPES = [\n\t'Input',\n\t'Textarea',\n\t'Select',\n\t'RadioGroup',\n\t'Checkbox',\n\t'Switch',\n\t'NumberInput',\n\t'DatePicker',\n\t'DateTimePicker',\n\t'TimePicker',\n\t'PhoneInput',\n\t'CodeEditor',\n\t'MarkdownEditor',\n\t'JsonEditor',\n\t'FileUpload',\n] as const;\n\n/** Layout node types that own children. */\nexport const CONTAINER_NODE_TYPES = ['Page', 'Form', 'Grid', 'GridColumn', 'Section', 'Tabs', 'Tab'] as const;\n\n/** Document-level blocks (screens, not fields). */\nexport const BLOCK_NODE_TYPES = [\n\t'DataTable',\n\t'DetailPanel',\n\t'RelationList',\n\t'StatCard',\n\t'Chart',\n\t'ActionBar',\n\t'Markdown',\n\t'AgentChat',\n\t'Button',\n\t'Slot',\n\t'Fragment',\n\t'Custom',\n] as const;\n\nexport type WidgetNodeType = (typeof WIDGET_NODE_TYPES)[number];\nexport type ContainerNodeType = (typeof CONTAINER_NODE_TYPES)[number];\nexport type BlockNodeType = (typeof BLOCK_NODE_TYPES)[number];\n\n/**\n * Known node types. Unknown strings stay valid: a registry may satisfy node\n * types this package has never heard of, and the renderer falls back to an\n * `UnknownBlock` rather than throwing.\n */\nexport type KnownNodeType = WidgetNodeType | ContainerNodeType | BlockNodeType;\nexport type UINodeType = KnownNodeType | (string & {});\n\nexport type InputType = 'text' | 'email' | 'url' | 'password' | 'tel' | 'search';\n\nexport interface UINodeConstraints {\n\tminLength?: number;\n\tmaxLength?: number;\n\tminValue?: number;\n\tmaxValue?: number;\n\tpattern?: string;\n\tprecision?: number;\n\tscale?: number;\n}\n\nexport interface UINodePropsBase {\n\tfieldId?: string;\n\tname?: string;\n\tlabel?: string;\n\tdescription?: string;\n\tplaceholder?: string;\n\trequired?: boolean;\n\thidden?: boolean;\n\tdisabled?: boolean;\n\tdefaultValue?: string | number | boolean | null;\n\tconstraints?: UINodeConstraints;\n\tclassName?: string;\n}\n\nexport type UINodeProps = UINodePropsBase & Record<string, unknown>;\n\n/** Prop name → template expression, e.g. `{ label: '{{ row.title }}' }`. */\nexport interface UIBinding {\n\t[propName: string]: string;\n}\n\nexport interface UIAction {\n\ttype: 'flow' | 'handler';\n\tflowId?: string;\n\thandler?: string;\n\tinputMapping?: Record<string, string>;\n\tparams?: Record<string, unknown>;\n}\n\n/** Event name → action, e.g. `{ submit: { type: 'flow', flowId } }`. */\nexport interface UIActions {\n\t[eventName: string]: UIAction;\n}\n\nexport interface UINode {\n\ttype: UINodeType;\n\tkey: string;\n\tprops: UINodeProps;\n\tchildren: UINode[];\n\tbindings?: UIBinding;\n\tactions?: UIActions;\n}\n\nconst widgetTypes = new Set<string>(WIDGET_NODE_TYPES);\nconst containerTypes = new Set<string>(CONTAINER_NODE_TYPES);\nconst blockTypes = new Set<string>(BLOCK_NODE_TYPES);\n\nexport function isWidgetNodeType(type: string): type is WidgetNodeType {\n\treturn widgetTypes.has(type);\n}\n\nexport function isContainerNodeType(type: string): type is ContainerNodeType {\n\treturn containerTypes.has(type);\n}\n\nexport function isKnownNodeType(type: string): type is KnownNodeType {\n\treturn widgetTypes.has(type) || containerTypes.has(type) || blockTypes.has(type);\n}\n\nexport function isWidgetNode(node: UINode): boolean {\n\treturn isWidgetNodeType(node.type);\n}\n\nexport function isContainerNode(node: UINode): boolean {\n\treturn isContainerNodeType(node.type);\n}\n\n/** Depth-first walk over a node and its descendants. */\nexport function* walkNodes(node: UINode): Generator<UINode> {\n\tyield node;\n\tfor (const child of node.children ?? []) {\n\t\tyield* walkNodes(child);\n\t}\n}\n\n/** Named fields in document order; widget nodes without a `name` are skipped. */\nexport function collectFieldNames(node: UINode): string[] {\n\tconst names: string[] = [];\n\tfor (const current of walkNodes(node)) {\n\t\tif (isWidgetNode(current) && typeof current.props?.name === 'string') {\n\t\t\tnames.push(current.props.name);\n\t\t}\n\t}\n\treturn names;\n}\n\n/** Default values declared by widget nodes, keyed by field name. */\nexport function collectDefaultValues(node: UINode): Record<string, unknown> {\n\tconst values: Record<string, unknown> = {};\n\tfor (const current of walkNodes(node)) {\n\t\tif (!isWidgetNode(current) || typeof current.props?.name !== 'string') continue;\n\t\tif (current.props.defaultValue !== undefined) {\n\t\t\tvalues[current.props.name] = current.props.defaultValue;\n\t\t}\n\t}\n\treturn values;\n}\n\nexport interface FieldConstraintEntry {\n\tconstraints?: UINodeConstraints;\n\trequired?: boolean;\n}\n\n/** Validation metadata declared by widget nodes, keyed by field name. */\nexport function collectFieldConstraints(node: UINode): Record<string, FieldConstraintEntry> {\n\tconst result: Record<string, FieldConstraintEntry> = {};\n\tfor (const current of walkNodes(node)) {\n\t\tif (!isWidgetNode(current) || typeof current.props?.name !== 'string') continue;\n\t\tresult[current.props.name] = {\n\t\t\tconstraints: current.props.constraints,\n\t\t\trequired: current.props.required,\n\t\t};\n\t}\n\treturn result;\n}\n\nexport function findNodeByKey(node: UINode, key: string): UINode | undefined {\n\tfor (const current of walkNodes(node)) {\n\t\tif (current.key === key) return current;\n\t}\n\treturn undefined;\n}\n","import type { UINodeConstraints } from './node';\n\n/**\n * Validate a single field value against the constraints declared on its node.\n * Returns a human-readable message, or `null` when the value is acceptable.\n */\nexport function validateField(value: unknown, constraints?: UINodeConstraints, required?: boolean): string | null {\n\tconst stringValue = value == null ? '' : String(value);\n\tconst isEmpty = stringValue.trim() === '';\n\n\tif (required && isEmpty) {\n\t\treturn 'This field is required';\n\t}\n\n\tif (isEmpty) return null;\n\n\tif (constraints?.minLength != null && stringValue.length < constraints.minLength) {\n\t\treturn `Minimum ${constraints.minLength} characters required`;\n\t}\n\n\tif (constraints?.maxLength != null && stringValue.length > constraints.maxLength) {\n\t\treturn `Maximum ${constraints.maxLength} characters allowed`;\n\t}\n\n\tif (constraints?.minValue != null && typeof value === 'number' && value < constraints.minValue) {\n\t\treturn `Minimum value is ${constraints.minValue}`;\n\t}\n\n\tif (constraints?.maxValue != null && typeof value === 'number' && value > constraints.maxValue) {\n\t\treturn `Maximum value is ${constraints.maxValue}`;\n\t}\n\n\tif (constraints?.pattern) {\n\t\tconst regex = compilePattern(constraints.pattern);\n\t\tif (regex && !regex.test(stringValue)) {\n\t\t\treturn 'Invalid format';\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Patterns arrive from documents authored elsewhere (a JSON Schema, a database\n * check constraint), so an uncompilable one must not take the form down — it is\n * reported as unconstrained rather than as a failed field.\n */\nfunction compilePattern(pattern: string): RegExp | null {\n\ttry {\n\t\treturn new RegExp(pattern);\n\t} catch {\n\t\treturn null;\n\t}\n}\n"]}