@json-render/core 0.4.3 → 0.5.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/dist/index.js CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ ActionBindingSchema: () => ActionBindingSchema,
23
24
  ActionConfirmSchema: () => ActionConfirmSchema,
24
25
  ActionOnErrorSchema: () => ActionOnErrorSchema,
25
26
  ActionOnSuccessSchema: () => ActionOnSuccessSchema,
@@ -33,7 +34,11 @@ __export(index_exports, {
33
34
  ValidationConfigSchema: () => ValidationConfigSchema,
34
35
  VisibilityConditionSchema: () => VisibilityConditionSchema,
35
36
  action: () => action,
37
+ actionBinding: () => actionBinding,
38
+ addByPath: () => addByPath,
36
39
  applySpecStreamPatch: () => applySpecStreamPatch,
40
+ autoFixSpec: () => autoFixSpec,
41
+ buildUserPrompt: () => buildUserPrompt,
37
42
  builtInValidationFunctions: () => builtInValidationFunctions,
38
43
  check: () => check,
39
44
  compileSpecStream: () => compileSpecStream,
@@ -45,16 +50,21 @@ __export(index_exports, {
45
50
  evaluateVisibility: () => evaluateVisibility,
46
51
  executeAction: () => executeAction,
47
52
  findFormValue: () => findFormValue,
53
+ formatSpecIssues: () => formatSpecIssues,
48
54
  generateCatalogPrompt: () => generateCatalogPrompt,
49
55
  generateSystemPrompt: () => generateSystemPrompt,
50
56
  getByPath: () => getByPath,
51
57
  interpolateString: () => interpolateString,
52
58
  parseSpecStreamLine: () => parseSpecStreamLine,
59
+ removeByPath: () => removeByPath,
53
60
  resolveAction: () => resolveAction,
54
61
  resolveDynamicValue: () => resolveDynamicValue,
62
+ resolveElementProps: () => resolveElementProps,
63
+ resolvePropValue: () => resolvePropValue,
55
64
  runValidation: () => runValidation,
56
65
  runValidationCheck: () => runValidationCheck,
57
66
  setByPath: () => setByPath,
67
+ validateSpec: () => validateSpec,
58
68
  visibility: () => visibility
59
69
  });
60
70
  module.exports = __toCommonJS(index_exports);
@@ -80,26 +90,36 @@ var DynamicBooleanSchema = import_zod.z.union([
80
90
  import_zod.z.boolean(),
81
91
  import_zod.z.object({ path: import_zod.z.string() })
82
92
  ]);
83
- function resolveDynamicValue(value, dataModel) {
93
+ function resolveDynamicValue(value, stateModel) {
84
94
  if (value === null || value === void 0) {
85
95
  return void 0;
86
96
  }
87
97
  if (typeof value === "object" && "path" in value) {
88
- return getByPath(dataModel, value.path);
98
+ return getByPath(stateModel, value.path);
89
99
  }
90
100
  return value;
91
101
  }
102
+ function unescapeJsonPointer(token) {
103
+ return token.replace(/~1/g, "/").replace(/~0/g, "~");
104
+ }
105
+ function parseJsonPointer(path) {
106
+ const raw = path.startsWith("/") ? path.slice(1).split("/") : path.split("/");
107
+ return raw.map(unescapeJsonPointer);
108
+ }
92
109
  function getByPath(obj, path) {
93
110
  if (!path || path === "/") {
94
111
  return obj;
95
112
  }
96
- const segments = path.startsWith("/") ? path.slice(1).split("/") : path.split("/");
113
+ const segments = parseJsonPointer(path);
97
114
  let current = obj;
98
115
  for (const segment of segments) {
99
116
  if (current === null || current === void 0) {
100
117
  return void 0;
101
118
  }
102
- if (typeof current === "object") {
119
+ if (Array.isArray(current)) {
120
+ const index = parseInt(segment, 10);
121
+ current = current[index];
122
+ } else if (typeof current === "object") {
103
123
  current = current[segment];
104
124
  } else {
105
125
  return void 0;
@@ -111,13 +131,13 @@ function isNumericIndex(str) {
111
131
  return /^\d+$/.test(str);
112
132
  }
113
133
  function setByPath(obj, path, value) {
114
- const segments = path.startsWith("/") ? path.slice(1).split("/") : path.split("/");
134
+ const segments = parseJsonPointer(path);
115
135
  if (segments.length === 0) return;
116
136
  let current = obj;
117
137
  for (let i = 0; i < segments.length - 1; i++) {
118
138
  const segment = segments[i];
119
139
  const nextSegment = segments[i + 1];
120
- const nextIsNumeric = nextSegment !== void 0 && isNumericIndex(nextSegment);
140
+ const nextIsNumeric = nextSegment !== void 0 && (isNumericIndex(nextSegment) || nextSegment === "-");
121
141
  if (Array.isArray(current)) {
122
142
  const index = parseInt(segment, 10);
123
143
  if (current[index] === void 0 || typeof current[index] !== "object") {
@@ -133,12 +153,95 @@ function setByPath(obj, path, value) {
133
153
  }
134
154
  const lastSegment = segments[segments.length - 1];
135
155
  if (Array.isArray(current)) {
136
- const index = parseInt(lastSegment, 10);
137
- current[index] = value;
156
+ if (lastSegment === "-") {
157
+ current.push(value);
158
+ } else {
159
+ const index = parseInt(lastSegment, 10);
160
+ current[index] = value;
161
+ }
138
162
  } else {
139
163
  current[lastSegment] = value;
140
164
  }
141
165
  }
166
+ function addByPath(obj, path, value) {
167
+ const segments = parseJsonPointer(path);
168
+ if (segments.length === 0) return;
169
+ let current = obj;
170
+ for (let i = 0; i < segments.length - 1; i++) {
171
+ const segment = segments[i];
172
+ const nextSegment = segments[i + 1];
173
+ const nextIsNumeric = nextSegment !== void 0 && (isNumericIndex(nextSegment) || nextSegment === "-");
174
+ if (Array.isArray(current)) {
175
+ const index = parseInt(segment, 10);
176
+ if (current[index] === void 0 || typeof current[index] !== "object") {
177
+ current[index] = nextIsNumeric ? [] : {};
178
+ }
179
+ current = current[index];
180
+ } else {
181
+ if (!(segment in current) || typeof current[segment] !== "object") {
182
+ current[segment] = nextIsNumeric ? [] : {};
183
+ }
184
+ current = current[segment];
185
+ }
186
+ }
187
+ const lastSegment = segments[segments.length - 1];
188
+ if (Array.isArray(current)) {
189
+ if (lastSegment === "-") {
190
+ current.push(value);
191
+ } else {
192
+ const index = parseInt(lastSegment, 10);
193
+ current.splice(index, 0, value);
194
+ }
195
+ } else {
196
+ current[lastSegment] = value;
197
+ }
198
+ }
199
+ function removeByPath(obj, path) {
200
+ const segments = parseJsonPointer(path);
201
+ if (segments.length === 0) return;
202
+ let current = obj;
203
+ for (let i = 0; i < segments.length - 1; i++) {
204
+ const segment = segments[i];
205
+ if (Array.isArray(current)) {
206
+ const index = parseInt(segment, 10);
207
+ if (current[index] === void 0 || typeof current[index] !== "object") {
208
+ return;
209
+ }
210
+ current = current[index];
211
+ } else {
212
+ if (!(segment in current) || typeof current[segment] !== "object") {
213
+ return;
214
+ }
215
+ current = current[segment];
216
+ }
217
+ }
218
+ const lastSegment = segments[segments.length - 1];
219
+ if (Array.isArray(current)) {
220
+ const index = parseInt(lastSegment, 10);
221
+ if (index >= 0 && index < current.length) {
222
+ current.splice(index, 1);
223
+ }
224
+ } else {
225
+ delete current[lastSegment];
226
+ }
227
+ }
228
+ function deepEqual(a, b) {
229
+ if (a === b) return true;
230
+ if (a === null || b === null) return false;
231
+ if (typeof a !== typeof b) return false;
232
+ if (typeof a !== "object") return false;
233
+ if (Array.isArray(a)) {
234
+ if (!Array.isArray(b)) return false;
235
+ if (a.length !== b.length) return false;
236
+ return a.every((item, i) => deepEqual(item, b[i]));
237
+ }
238
+ const aObj = a;
239
+ const bObj = b;
240
+ const aKeys = Object.keys(aObj);
241
+ const bKeys = Object.keys(bObj);
242
+ if (aKeys.length !== bKeys.length) return false;
243
+ return aKeys.every((key) => deepEqual(aObj[key], bObj[key]));
244
+ }
142
245
  function findFormValue(fieldName, params, data) {
143
246
  if (params?.[fieldName] !== void 0) {
144
247
  const val = params[fieldName];
@@ -187,10 +290,38 @@ function parseSpecStreamLine(line) {
187
290
  }
188
291
  }
189
292
  function applySpecStreamPatch(obj, patch) {
190
- if (patch.op === "set" || patch.op === "add" || patch.op === "replace") {
191
- setByPath(obj, patch.path, patch.value);
192
- } else if (patch.op === "remove") {
193
- setByPath(obj, patch.path, void 0);
293
+ switch (patch.op) {
294
+ case "add":
295
+ addByPath(obj, patch.path, patch.value);
296
+ break;
297
+ case "replace":
298
+ setByPath(obj, patch.path, patch.value);
299
+ break;
300
+ case "remove":
301
+ removeByPath(obj, patch.path);
302
+ break;
303
+ case "move": {
304
+ if (!patch.from) break;
305
+ const moveValue = getByPath(obj, patch.from);
306
+ removeByPath(obj, patch.from);
307
+ addByPath(obj, patch.path, moveValue);
308
+ break;
309
+ }
310
+ case "copy": {
311
+ if (!patch.from) break;
312
+ const copyValue = getByPath(obj, patch.from);
313
+ addByPath(obj, patch.path, copyValue);
314
+ break;
315
+ }
316
+ case "test": {
317
+ const actual = getByPath(obj, patch.path);
318
+ if (!deepEqual(actual, patch.value)) {
319
+ throw new Error(
320
+ `Test operation failed: value at "${patch.path}" does not match`
321
+ );
322
+ }
323
+ break;
324
+ }
194
325
  }
195
326
  return obj;
196
327
  }
@@ -292,7 +423,7 @@ var VisibilityConditionSchema = import_zod2.z.union([
292
423
  LogicExpressionSchema
293
424
  ]);
294
425
  function evaluateLogicExpression(expr, ctx) {
295
- const { dataModel } = ctx;
426
+ const { stateModel } = ctx;
296
427
  if ("and" in expr) {
297
428
  return expr.and.every((subExpr) => evaluateLogicExpression(subExpr, ctx));
298
429
  }
@@ -303,30 +434,30 @@ function evaluateLogicExpression(expr, ctx) {
303
434
  return !evaluateLogicExpression(expr.not, ctx);
304
435
  }
305
436
  if ("path" in expr) {
306
- const value = resolveDynamicValue({ path: expr.path }, dataModel);
437
+ const value = resolveDynamicValue({ path: expr.path }, stateModel);
307
438
  return Boolean(value);
308
439
  }
309
440
  if ("eq" in expr) {
310
441
  const [left, right] = expr.eq;
311
- const leftValue = resolveDynamicValue(left, dataModel);
312
- const rightValue = resolveDynamicValue(right, dataModel);
442
+ const leftValue = resolveDynamicValue(left, stateModel);
443
+ const rightValue = resolveDynamicValue(right, stateModel);
313
444
  return leftValue === rightValue;
314
445
  }
315
446
  if ("neq" in expr) {
316
447
  const [left, right] = expr.neq;
317
- const leftValue = resolveDynamicValue(left, dataModel);
318
- const rightValue = resolveDynamicValue(right, dataModel);
448
+ const leftValue = resolveDynamicValue(left, stateModel);
449
+ const rightValue = resolveDynamicValue(right, stateModel);
319
450
  return leftValue !== rightValue;
320
451
  }
321
452
  if ("gt" in expr) {
322
453
  const [left, right] = expr.gt;
323
454
  const leftValue = resolveDynamicValue(
324
455
  left,
325
- dataModel
456
+ stateModel
326
457
  );
327
458
  const rightValue = resolveDynamicValue(
328
459
  right,
329
- dataModel
460
+ stateModel
330
461
  );
331
462
  if (typeof leftValue === "number" && typeof rightValue === "number") {
332
463
  return leftValue > rightValue;
@@ -337,11 +468,11 @@ function evaluateLogicExpression(expr, ctx) {
337
468
  const [left, right] = expr.gte;
338
469
  const leftValue = resolveDynamicValue(
339
470
  left,
340
- dataModel
471
+ stateModel
341
472
  );
342
473
  const rightValue = resolveDynamicValue(
343
474
  right,
344
- dataModel
475
+ stateModel
345
476
  );
346
477
  if (typeof leftValue === "number" && typeof rightValue === "number") {
347
478
  return leftValue >= rightValue;
@@ -352,11 +483,11 @@ function evaluateLogicExpression(expr, ctx) {
352
483
  const [left, right] = expr.lt;
353
484
  const leftValue = resolveDynamicValue(
354
485
  left,
355
- dataModel
486
+ stateModel
356
487
  );
357
488
  const rightValue = resolveDynamicValue(
358
489
  right,
359
- dataModel
490
+ stateModel
360
491
  );
361
492
  if (typeof leftValue === "number" && typeof rightValue === "number") {
362
493
  return leftValue < rightValue;
@@ -367,11 +498,11 @@ function evaluateLogicExpression(expr, ctx) {
367
498
  const [left, right] = expr.lte;
368
499
  const leftValue = resolveDynamicValue(
369
500
  left,
370
- dataModel
501
+ stateModel
371
502
  );
372
503
  const rightValue = resolveDynamicValue(
373
504
  right,
374
- dataModel
505
+ stateModel
375
506
  );
376
507
  if (typeof leftValue === "number" && typeof rightValue === "number") {
377
508
  return leftValue <= rightValue;
@@ -388,7 +519,7 @@ function evaluateVisibility(condition, ctx) {
388
519
  return condition;
389
520
  }
390
521
  if ("path" in condition && !("and" in condition) && !("or" in condition)) {
391
- const value = resolveDynamicValue({ path: condition.path }, ctx.dataModel);
522
+ const value = resolveDynamicValue({ path: condition.path }, ctx.stateModel);
392
523
  return Boolean(value);
393
524
  }
394
525
  if ("auth" in condition) {
@@ -442,6 +573,44 @@ var visibility = {
442
573
  lte: (left, right) => ({ lte: [left, right] })
443
574
  };
444
575
 
576
+ // src/props.ts
577
+ function isPathExpression(value) {
578
+ return typeof value === "object" && value !== null && "$path" in value && typeof value.$path === "string";
579
+ }
580
+ function isCondExpression(value) {
581
+ return typeof value === "object" && value !== null && "$cond" in value && "$then" in value && "$else" in value;
582
+ }
583
+ function resolvePropValue(value, ctx) {
584
+ if (value === null || value === void 0) {
585
+ return value;
586
+ }
587
+ if (isPathExpression(value)) {
588
+ return getByPath(ctx.stateModel, value.$path);
589
+ }
590
+ if (isCondExpression(value)) {
591
+ const result = evaluateVisibility(value.$cond, ctx);
592
+ return resolvePropValue(result ? value.$then : value.$else, ctx);
593
+ }
594
+ if (Array.isArray(value)) {
595
+ return value.map((item) => resolvePropValue(item, ctx));
596
+ }
597
+ if (typeof value === "object") {
598
+ const resolved = {};
599
+ for (const [key, val] of Object.entries(value)) {
600
+ resolved[key] = resolvePropValue(val, ctx);
601
+ }
602
+ return resolved;
603
+ }
604
+ return value;
605
+ }
606
+ function resolveElementProps(props, ctx) {
607
+ const resolved = {};
608
+ for (const [key, value] of Object.entries(props)) {
609
+ resolved[key] = resolvePropValue(value, ctx);
610
+ }
611
+ return resolved;
612
+ }
613
+
445
614
  // src/actions.ts
446
615
  var import_zod3 = require("zod");
447
616
  var ActionConfirmSchema = import_zod3.z.object({
@@ -460,44 +629,45 @@ var ActionOnErrorSchema = import_zod3.z.union([
460
629
  import_zod3.z.object({ set: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.unknown()) }),
461
630
  import_zod3.z.object({ action: import_zod3.z.string() })
462
631
  ]);
463
- var ActionSchema = import_zod3.z.object({
464
- name: import_zod3.z.string(),
632
+ var ActionBindingSchema = import_zod3.z.object({
633
+ action: import_zod3.z.string(),
465
634
  params: import_zod3.z.record(import_zod3.z.string(), DynamicValueSchema).optional(),
466
635
  confirm: ActionConfirmSchema.optional(),
467
636
  onSuccess: ActionOnSuccessSchema.optional(),
468
637
  onError: ActionOnErrorSchema.optional()
469
638
  });
470
- function resolveAction(action2, dataModel) {
639
+ var ActionSchema = ActionBindingSchema;
640
+ function resolveAction(binding, stateModel) {
471
641
  const resolvedParams = {};
472
- if (action2.params) {
473
- for (const [key, value] of Object.entries(action2.params)) {
474
- resolvedParams[key] = resolveDynamicValue(value, dataModel);
642
+ if (binding.params) {
643
+ for (const [key, value] of Object.entries(binding.params)) {
644
+ resolvedParams[key] = resolveDynamicValue(value, stateModel);
475
645
  }
476
646
  }
477
- let confirm = action2.confirm;
647
+ let confirm = binding.confirm;
478
648
  if (confirm) {
479
649
  confirm = {
480
650
  ...confirm,
481
- message: interpolateString(confirm.message, dataModel),
482
- title: interpolateString(confirm.title, dataModel)
651
+ message: interpolateString(confirm.message, stateModel),
652
+ title: interpolateString(confirm.title, stateModel)
483
653
  };
484
654
  }
485
655
  return {
486
- name: action2.name,
656
+ action: binding.action,
487
657
  params: resolvedParams,
488
658
  confirm,
489
- onSuccess: action2.onSuccess,
490
- onError: action2.onError
659
+ onSuccess: binding.onSuccess,
660
+ onError: binding.onError
491
661
  };
492
662
  }
493
- function interpolateString(template, dataModel) {
663
+ function interpolateString(template, stateModel) {
494
664
  return template.replace(/\$\{([^}]+)\}/g, (_, path) => {
495
- const value = resolveDynamicValue({ path }, dataModel);
665
+ const value = resolveDynamicValue({ path }, stateModel);
496
666
  return String(value ?? "");
497
667
  });
498
668
  }
499
669
  async function executeAction(ctx) {
500
- const { action: action2, handler, setData, navigate, executeAction: executeAction2 } = ctx;
670
+ const { action: action2, handler, setState, navigate, executeAction: executeAction2 } = ctx;
501
671
  try {
502
672
  await handler(action2.params);
503
673
  if (action2.onSuccess) {
@@ -505,7 +675,7 @@ async function executeAction(ctx) {
505
675
  navigate(action2.onSuccess.navigate);
506
676
  } else if ("set" in action2.onSuccess) {
507
677
  for (const [path, value] of Object.entries(action2.onSuccess.set)) {
508
- setData(path, value);
678
+ setState(path, value);
509
679
  }
510
680
  } else if ("action" in action2.onSuccess && executeAction2) {
511
681
  await executeAction2(action2.onSuccess.action);
@@ -516,7 +686,7 @@ async function executeAction(ctx) {
516
686
  if ("set" in action2.onError) {
517
687
  for (const [path, value] of Object.entries(action2.onError.set)) {
518
688
  const resolvedValue = typeof value === "string" && value === "$error.message" ? error.message : value;
519
- setData(path, resolvedValue);
689
+ setState(path, resolvedValue);
520
690
  }
521
691
  } else if ("action" in action2.onError && executeAction2) {
522
692
  await executeAction2(action2.onError.action);
@@ -526,25 +696,26 @@ async function executeAction(ctx) {
526
696
  }
527
697
  }
528
698
  }
529
- var action = {
530
- /** Create a simple action */
531
- simple: (name, params) => ({
532
- name,
699
+ var actionBinding = {
700
+ /** Create a simple action binding */
701
+ simple: (actionName, params) => ({
702
+ action: actionName,
533
703
  params
534
704
  }),
535
- /** Create an action with confirmation */
536
- withConfirm: (name, confirm, params) => ({
537
- name,
705
+ /** Create an action binding with confirmation */
706
+ withConfirm: (actionName, confirm, params) => ({
707
+ action: actionName,
538
708
  params,
539
709
  confirm
540
710
  }),
541
- /** Create an action with success handler */
542
- withSuccess: (name, onSuccess, params) => ({
543
- name,
711
+ /** Create an action binding with success handler */
712
+ withSuccess: (actionName, onSuccess, params) => ({
713
+ action: actionName,
544
714
  params,
545
715
  onSuccess
546
716
  })
547
717
  };
718
+ var action = actionBinding;
548
719
 
549
720
  // src/validation.ts
550
721
  var import_zod4 = require("zod");
@@ -653,11 +824,11 @@ var builtInValidationFunctions = {
653
824
  }
654
825
  };
655
826
  function runValidationCheck(check2, ctx) {
656
- const { value, dataModel, customFunctions } = ctx;
827
+ const { value, stateModel, customFunctions } = ctx;
657
828
  const resolvedArgs = {};
658
829
  if (check2.args) {
659
830
  for (const [key, argValue] of Object.entries(check2.args)) {
660
- resolvedArgs[key] = resolveDynamicValue(argValue, dataModel);
831
+ resolvedArgs[key] = resolveDynamicValue(argValue, stateModel);
661
832
  }
662
833
  }
663
834
  const fn = builtInValidationFunctions[check2.fn] ?? customFunctions?.[check2.fn];
@@ -682,7 +853,7 @@ function runValidation(config, ctx) {
682
853
  const errors = [];
683
854
  if (config.enabled) {
684
855
  const enabled = evaluateLogicExpression(config.enabled, {
685
- dataModel: ctx.dataModel,
856
+ stateModel: ctx.stateModel,
686
857
  authState: ctx.authState
687
858
  });
688
859
  if (!enabled) {
@@ -749,6 +920,155 @@ var check = {
749
920
  })
750
921
  };
751
922
 
923
+ // src/spec-validator.ts
924
+ function validateSpec(spec, options = {}) {
925
+ const { checkOrphans = false } = options;
926
+ const issues = [];
927
+ if (!spec.root) {
928
+ issues.push({
929
+ severity: "error",
930
+ message: "Spec has no root element defined.",
931
+ code: "missing_root"
932
+ });
933
+ return { valid: false, issues };
934
+ }
935
+ if (!spec.elements[spec.root]) {
936
+ issues.push({
937
+ severity: "error",
938
+ message: `Root element "${spec.root}" not found in elements map.`,
939
+ code: "root_not_found"
940
+ });
941
+ }
942
+ if (Object.keys(spec.elements).length === 0) {
943
+ issues.push({
944
+ severity: "error",
945
+ message: "Spec has no elements.",
946
+ code: "empty_spec"
947
+ });
948
+ return { valid: false, issues };
949
+ }
950
+ for (const [key, element] of Object.entries(spec.elements)) {
951
+ if (element.children) {
952
+ for (const childKey of element.children) {
953
+ if (!spec.elements[childKey]) {
954
+ issues.push({
955
+ severity: "error",
956
+ message: `Element "${key}" references child "${childKey}" which does not exist in the elements map.`,
957
+ elementKey: key,
958
+ code: "missing_child"
959
+ });
960
+ }
961
+ }
962
+ }
963
+ const props = element.props;
964
+ if (props && "visible" in props && props.visible !== void 0) {
965
+ issues.push({
966
+ severity: "error",
967
+ message: `Element "${key}" has "visible" inside "props". It should be a top-level field on the element (sibling of type/props/children).`,
968
+ elementKey: key,
969
+ code: "visible_in_props"
970
+ });
971
+ }
972
+ if (props && "on" in props && props.on !== void 0) {
973
+ issues.push({
974
+ severity: "error",
975
+ message: `Element "${key}" has "on" inside "props". It should be a top-level field on the element (sibling of type/props/children).`,
976
+ elementKey: key,
977
+ code: "on_in_props"
978
+ });
979
+ }
980
+ if (props && "repeat" in props && props.repeat !== void 0) {
981
+ issues.push({
982
+ severity: "error",
983
+ message: `Element "${key}" has "repeat" inside "props". It should be a top-level field on the element (sibling of type/props/children).`,
984
+ elementKey: key,
985
+ code: "repeat_in_props"
986
+ });
987
+ }
988
+ }
989
+ if (checkOrphans) {
990
+ const reachable = /* @__PURE__ */ new Set();
991
+ const walk = (key) => {
992
+ if (reachable.has(key)) return;
993
+ reachable.add(key);
994
+ const el = spec.elements[key];
995
+ if (el?.children) {
996
+ for (const childKey of el.children) {
997
+ if (spec.elements[childKey]) {
998
+ walk(childKey);
999
+ }
1000
+ }
1001
+ }
1002
+ };
1003
+ if (spec.elements[spec.root]) {
1004
+ walk(spec.root);
1005
+ }
1006
+ for (const key of Object.keys(spec.elements)) {
1007
+ if (!reachable.has(key)) {
1008
+ issues.push({
1009
+ severity: "warning",
1010
+ message: `Element "${key}" is not reachable from root "${spec.root}".`,
1011
+ elementKey: key,
1012
+ code: "orphaned_element"
1013
+ });
1014
+ }
1015
+ }
1016
+ }
1017
+ const hasErrors = issues.some((i) => i.severity === "error");
1018
+ return { valid: !hasErrors, issues };
1019
+ }
1020
+ function autoFixSpec(spec) {
1021
+ const fixes = [];
1022
+ const fixedElements = {};
1023
+ for (const [key, element] of Object.entries(spec.elements)) {
1024
+ const props = element.props;
1025
+ let fixed = element;
1026
+ if (props && "visible" in props && props.visible !== void 0) {
1027
+ const { visible, ...restProps } = fixed.props;
1028
+ fixed = {
1029
+ ...fixed,
1030
+ props: restProps,
1031
+ visible
1032
+ };
1033
+ fixes.push(`Moved "visible" from props to element level on "${key}".`);
1034
+ }
1035
+ let currentProps = fixed.props;
1036
+ if (currentProps && "on" in currentProps && currentProps.on !== void 0) {
1037
+ const { on, ...restProps } = currentProps;
1038
+ fixed = {
1039
+ ...fixed,
1040
+ props: restProps,
1041
+ on
1042
+ };
1043
+ fixes.push(`Moved "on" from props to element level on "${key}".`);
1044
+ }
1045
+ currentProps = fixed.props;
1046
+ if (currentProps && "repeat" in currentProps && currentProps.repeat !== void 0) {
1047
+ const { repeat, ...restProps } = currentProps;
1048
+ fixed = {
1049
+ ...fixed,
1050
+ props: restProps,
1051
+ repeat
1052
+ };
1053
+ fixes.push(`Moved "repeat" from props to element level on "${key}".`);
1054
+ }
1055
+ fixedElements[key] = fixed;
1056
+ }
1057
+ return {
1058
+ spec: { root: spec.root, elements: fixedElements },
1059
+ fixes
1060
+ };
1061
+ }
1062
+ function formatSpecIssues(issues) {
1063
+ const errors = issues.filter((i) => i.severity === "error");
1064
+ if (errors.length === 0) return "";
1065
+ const lines = ["The generated UI spec has the following errors:"];
1066
+ for (const issue of errors) {
1067
+ lines.push(`- ${issue.message}`);
1068
+ }
1069
+ return lines.join("\n");
1070
+ }
1071
+
752
1072
  // src/schema.ts
753
1073
  var import_zod5 = require("zod");
754
1074
  function createBuilder() {
@@ -773,6 +1093,7 @@ function defineSchema(builder, options) {
773
1093
  return {
774
1094
  definition,
775
1095
  promptTemplate: options?.promptTemplate,
1096
+ defaultRules: options?.defaultRules,
776
1097
  createCatalog(catalog) {
777
1098
  return createCatalogFromSchema(this, catalog);
778
1099
  }
@@ -928,15 +1249,95 @@ function generatePrompt(catalog, options) {
928
1249
  "Output JSONL (one JSON object per line) with patches to build a UI tree."
929
1250
  );
930
1251
  lines.push(
931
- "Each line is a JSON patch operation. Start with the root, then add each element."
1252
+ "Each line is a JSON patch operation. Start with /root, then stream /elements and /state patches interleaved so the UI fills in progressively as it streams."
932
1253
  );
933
1254
  lines.push("");
934
1255
  lines.push("Example output (each line is a separate JSON object):");
935
1256
  lines.push("");
936
- lines.push(`{"op":"set","path":"/root","value":"card-1"}
937
- {"op":"set","path":"/elements/card-1","value":{"key":"card-1","type":"Card","props":{"title":"Dashboard"},"children":["metric-1","chart-1"],"parentKey":""}}
938
- {"op":"set","path":"/elements/metric-1","value":{"key":"metric-1","type":"Metric","props":{"label":"Revenue","valuePath":"analytics.revenue","format":"currency"},"children":[],"parentKey":"card-1"}}
939
- {"op":"set","path":"/elements/chart-1","value":{"key":"chart-1","type":"Chart","props":{"type":"bar","dataPath":"analytics.salesByRegion"},"children":[],"parentKey":"card-1"}}`);
1257
+ lines.push(`{"op":"add","path":"/root","value":"blog"}
1258
+ {"op":"add","path":"/elements/blog","value":{"type":"Stack","props":{"direction":"vertical","gap":"md"},"children":["heading","posts-grid"]}}
1259
+ {"op":"add","path":"/elements/heading","value":{"type":"Heading","props":{"text":"Blog","level":"h1"},"children":[]}}
1260
+ {"op":"add","path":"/elements/posts-grid","value":{"type":"Grid","props":{"columns":2,"gap":"md"},"repeat":{"path":"/posts","key":"id"},"children":["post-card"]}}
1261
+ {"op":"add","path":"/elements/post-card","value":{"type":"Card","props":{"title":{"$path":"$item/title"}},"children":["post-meta"]}}
1262
+ {"op":"add","path":"/elements/post-meta","value":{"type":"Text","props":{"text":{"$path":"$item/author"},"variant":"muted"},"children":[]}}
1263
+ {"op":"add","path":"/state/posts","value":[]}
1264
+ {"op":"add","path":"/state/posts/0","value":{"id":"1","title":"Getting Started","author":"Jane","date":"Jan 15"}}
1265
+ {"op":"add","path":"/state/posts/1","value":{"id":"2","title":"Advanced Tips","author":"Bob","date":"Feb 3"}}
1266
+
1267
+ Note: state patches appear right after the elements that use them, so the UI fills in as it streams.`);
1268
+ lines.push("");
1269
+ lines.push("INITIAL STATE:");
1270
+ lines.push(
1271
+ "Specs include a /state field to seed the state model. Components with statePath read from and write to this state, and $path expressions read from it."
1272
+ );
1273
+ lines.push(
1274
+ "CRITICAL: You MUST include state patches whenever your UI displays data via $path expressions, uses repeat to iterate over arrays, or uses statePath bindings. Without state, $path references resolve to nothing and repeat lists render zero items."
1275
+ );
1276
+ lines.push(
1277
+ "Output state patches right after the elements that reference them, so the UI fills in progressively as it streams."
1278
+ );
1279
+ lines.push(
1280
+ "Stream state progressively - output one patch per array item instead of one giant blob:"
1281
+ );
1282
+ lines.push(
1283
+ ' For arrays: {"op":"add","path":"/state/posts/0","value":{"id":"1","title":"First Post",...}} then /state/posts/1, /state/posts/2, etc.'
1284
+ );
1285
+ lines.push(
1286
+ ' For scalars: {"op":"add","path":"/state/newTodoText","value":""}'
1287
+ );
1288
+ lines.push(
1289
+ ' Initialize the array first if needed: {"op":"add","path":"/state/posts","value":[]}'
1290
+ );
1291
+ lines.push(
1292
+ 'When content comes from the state model, use { "$path": "/some/path" } dynamic props to display it instead of hardcoding the same value in both state and props. The state model is the single source of truth.'
1293
+ );
1294
+ lines.push(
1295
+ "Include realistic sample data in state. For blogs: 3-4 posts with titles, excerpts, authors, dates. For product lists: 3-5 items with names, prices, descriptions. Never leave arrays empty."
1296
+ );
1297
+ lines.push("");
1298
+ lines.push("DYNAMIC LISTS (repeat field):");
1299
+ lines.push(
1300
+ 'Any element can have a top-level "repeat" field to render its children once per item in a state array: { "repeat": { "path": "/arrayPath", "key": "id" } }.'
1301
+ );
1302
+ lines.push(
1303
+ 'The element itself renders once (as the container), and its children are expanded once per array item. "path" is the state array path. "key" is an optional field name on each item for stable React keys.'
1304
+ );
1305
+ lines.push(
1306
+ 'Example: { "type": "Column", "props": { "gap": 8 }, "repeat": { "path": "/todos", "key": "id" }, "children": ["todo-item"] }'
1307
+ );
1308
+ lines.push(
1309
+ 'Inside children of a repeated element, use "$item/field" for per-item paths: statePath:"$item/completed", { "$path": "$item/title" }. Use "$index" for the current array index.'
1310
+ );
1311
+ lines.push(
1312
+ "ALWAYS use the repeat field for lists backed by state arrays. NEVER hardcode individual elements for each array item."
1313
+ );
1314
+ lines.push(
1315
+ 'IMPORTANT: "repeat" is a top-level field on the element (sibling of type/props/children), NOT inside props.'
1316
+ );
1317
+ lines.push("");
1318
+ lines.push("ARRAY STATE ACTIONS:");
1319
+ lines.push(
1320
+ 'Use action "pushState" to append items to arrays. Params: { path: "/arrayPath", value: { ...item }, clearPath: "/inputPath" }.'
1321
+ );
1322
+ lines.push(
1323
+ 'Values inside pushState can contain { "path": "/statePath" } references to read current state (e.g. the text from an input field).'
1324
+ );
1325
+ lines.push(
1326
+ 'Use "$id" inside a pushState value to auto-generate a unique ID.'
1327
+ );
1328
+ lines.push(
1329
+ 'Example: on: { "press": { "action": "pushState", "params": { "path": "/todos", "value": { "id": "$id", "title": { "path": "/newTodoText" }, "completed": false }, "clearPath": "/newTodoText" } } }'
1330
+ );
1331
+ lines.push(
1332
+ `Use action "removeState" to remove items from arrays by index. Params: { path: "/arrayPath", index: N }. Inside a repeated element's children, use "$index" for the current item index.`
1333
+ );
1334
+ lines.push(
1335
+ "For lists where users can add/remove items (todos, carts, etc.), use pushState and removeState instead of hardcoding with setState."
1336
+ );
1337
+ lines.push("");
1338
+ lines.push(
1339
+ 'IMPORTANT: State paths use RFC 6901 JSON Pointer syntax (e.g. "/todos/0/title"). Do NOT use JavaScript-style dot notation (e.g. "/todos.length" is WRONG). To generate unique IDs for new items, use "$id" instead of trying to read array length.'
1340
+ );
940
1341
  lines.push("");
941
1342
  const components = catalog.data.components;
942
1343
  if (components) {
@@ -946,8 +1347,9 @@ function generatePrompt(catalog, options) {
946
1347
  const propsStr = def.props ? formatZodType(def.props) : "{}";
947
1348
  const hasChildren = def.slots && def.slots.length > 0;
948
1349
  const childrenStr = hasChildren ? " [accepts children]" : "";
1350
+ const eventsStr = def.events && def.events.length > 0 ? ` [events: ${def.events.join(", ")}]` : "";
949
1351
  const descStr = def.description ? ` - ${def.description}` : "";
950
- lines.push(`- ${name}: ${propsStr}${descStr}${childrenStr}`);
1352
+ lines.push(`- ${name}: ${propsStr}${descStr}${childrenStr}${eventsStr}`);
951
1353
  }
952
1354
  lines.push("");
953
1355
  }
@@ -960,17 +1362,90 @@ function generatePrompt(catalog, options) {
960
1362
  }
961
1363
  lines.push("");
962
1364
  }
1365
+ lines.push("EVENTS (the `on` field):");
1366
+ lines.push(
1367
+ "Elements can have an optional `on` field to bind events to actions. The `on` field is a top-level field on the element (sibling of type/props/children), NOT inside props."
1368
+ );
1369
+ lines.push(
1370
+ 'Each key in `on` is an event name (from the component\'s supported events), and the value is an action binding: `{ "action": "<actionName>", "params": { ... } }`.'
1371
+ );
1372
+ lines.push("");
1373
+ lines.push("Example:");
1374
+ lines.push(
1375
+ ' {"type":"Button","props":{"label":"Save"},"on":{"press":{"action":"setState","params":{"path":"/saved","value":true}}},"children":[]}'
1376
+ );
1377
+ lines.push("");
1378
+ lines.push(
1379
+ 'Action params can use dynamic path references to read from state: { "path": "/statePath" }.'
1380
+ );
1381
+ lines.push(
1382
+ "IMPORTANT: Do NOT put action/actionParams inside props. Always use the `on` field for event bindings."
1383
+ );
1384
+ lines.push("");
1385
+ lines.push("VISIBILITY CONDITIONS:");
1386
+ lines.push(
1387
+ "Elements can have an optional `visible` field to conditionally show/hide based on data state. IMPORTANT: `visible` is a top-level field on the element object (sibling of type/props/children), NOT inside props."
1388
+ );
1389
+ lines.push(
1390
+ 'Correct: {"type":"Column","props":{"gap":8},"visible":{"eq":[{"path":"/tab"},"home"]},"children":[...]}'
1391
+ );
1392
+ lines.push(
1393
+ '- `{ "eq": [{ "path": "/statePath" }, "value"] }` - visible when state at path equals value'
1394
+ );
1395
+ lines.push(
1396
+ '- `{ "neq": [{ "path": "/statePath" }, "value"] }` - visible when state at path does not equal value'
1397
+ );
1398
+ lines.push('- `{ "path": "/statePath" }` - visible when path is truthy');
1399
+ lines.push(
1400
+ '- `{ "and": [...] }`, `{ "or": [...] }`, `{ "not": {...} }` - combine conditions'
1401
+ );
1402
+ lines.push("- `true` / `false` - always visible/hidden");
1403
+ lines.push("");
1404
+ lines.push(
1405
+ "Use the Pressable component with on.press bound to setState to update state and drive visibility."
1406
+ );
1407
+ lines.push(
1408
+ 'Example: A Pressable with on: { "press": { "action": "setState", "params": { "path": "/activeTab", "value": "home" } } } sets state, then a container with visible: { "eq": [{ "path": "/activeTab" }, "home"] } shows only when that tab is active.'
1409
+ );
1410
+ lines.push("");
1411
+ lines.push("DYNAMIC PROPS:");
1412
+ lines.push(
1413
+ "Any prop value can be a dynamic expression that resolves based on state. Two forms are supported:"
1414
+ );
1415
+ lines.push("");
1416
+ lines.push(
1417
+ '1. State binding: `{ "$path": "/statePath" }` - resolves to the value at that state path.'
1418
+ );
1419
+ lines.push(
1420
+ ' Example: `"color": { "$path": "/theme/primary" }` reads the color from state.'
1421
+ );
1422
+ lines.push("");
1423
+ lines.push(
1424
+ '2. Conditional: `{ "$cond": <condition>, "$then": <value>, "$else": <value> }` - evaluates the condition (same syntax as visibility conditions) and picks the matching value.'
1425
+ );
1426
+ lines.push(
1427
+ ' Example: `"color": { "$cond": { "eq": [{ "path": "/activeTab" }, "home"] }, "$then": "#007AFF", "$else": "#8E8E93" }`'
1428
+ );
1429
+ lines.push(
1430
+ ' Example: `"name": { "$cond": { "eq": [{ "path": "/activeTab" }, "home"] }, "$then": "home", "$else": "home-outline" }`'
1431
+ );
1432
+ lines.push("");
1433
+ lines.push(
1434
+ "Use dynamic props instead of duplicating elements with opposing visible conditions when only prop values differ."
1435
+ );
1436
+ lines.push("");
963
1437
  lines.push("RULES:");
964
1438
  const baseRules = [
965
1439
  "Output ONLY JSONL patches - one JSON object per line, no markdown, no code fences",
966
- 'First line sets root: {"op":"set","path":"/root","value":"<root-key>"}',
967
- 'Then add each element: {"op":"set","path":"/elements/<key>","value":{...}}',
1440
+ 'First set root: {"op":"add","path":"/root","value":"<root-key>"}',
1441
+ 'Then add each element: {"op":"add","path":"/elements/<key>","value":{...}}',
1442
+ "Output /state patches right after the elements that use them, one per array item for progressive loading. REQUIRED whenever using $path, repeat, or statePath.",
968
1443
  "ONLY use components listed above",
969
- "Each element value needs: key, type, props, children (array of child keys), parentKey",
970
- "Use unique keys (e.g., 'header', 'metric-1', 'chart-revenue')",
971
- "Root element's parentKey is empty string, children reference their parent's key"
1444
+ "Each element value needs: type, props, children (array of child keys)",
1445
+ "Use unique keys for the element map entries (e.g., 'header', 'metric-1', 'chart-revenue')"
972
1446
  ];
973
- const allRules = [...baseRules, ...customRules];
1447
+ const schemaRules = catalog.schema.defaultRules ?? [];
1448
+ const allRules = [...baseRules, ...schemaRules, ...customRules];
974
1449
  allRules.forEach((rule, i) => {
975
1450
  lines.push(`${i + 1}. ${rule}`);
976
1451
  });
@@ -1108,6 +1583,56 @@ function defineCatalog(schema, catalog) {
1108
1583
  return schema.createCatalog(catalog);
1109
1584
  }
1110
1585
 
1586
+ // src/prompt.ts
1587
+ function isNonEmptySpec(spec) {
1588
+ if (!spec || typeof spec !== "object") return false;
1589
+ const s = spec;
1590
+ return typeof s.root === "string" && typeof s.elements === "object" && s.elements !== null && Object.keys(s.elements).length > 0;
1591
+ }
1592
+ var PATCH_INSTRUCTIONS = `IMPORTANT: The current UI is already loaded. Output ONLY the patches needed to make the requested change:
1593
+ - To add a new element: {"op":"add","path":"/elements/new-key","value":{...}}
1594
+ - To modify an existing element: {"op":"replace","path":"/elements/existing-key","value":{...}}
1595
+ - To remove an element: {"op":"remove","path":"/elements/old-key"}
1596
+ - To update the root: {"op":"replace","path":"/root","value":"new-root-key"}
1597
+ - To add children: update the parent element with new children array
1598
+
1599
+ DO NOT output patches for elements that don't need to change. Only output what's necessary for the requested modification.`;
1600
+ function buildUserPrompt(options) {
1601
+ const { prompt, currentSpec, state, maxPromptLength } = options;
1602
+ let userText = String(prompt || "");
1603
+ if (maxPromptLength !== void 0 && maxPromptLength > 0) {
1604
+ userText = userText.slice(0, maxPromptLength);
1605
+ }
1606
+ if (isNonEmptySpec(currentSpec)) {
1607
+ const parts2 = [];
1608
+ parts2.push(
1609
+ `CURRENT UI STATE (already loaded, DO NOT recreate existing elements):`
1610
+ );
1611
+ parts2.push(JSON.stringify(currentSpec, null, 2));
1612
+ parts2.push("");
1613
+ parts2.push(`USER REQUEST: ${userText}`);
1614
+ if (state && Object.keys(state).length > 0) {
1615
+ parts2.push("");
1616
+ parts2.push(`AVAILABLE STATE:
1617
+ ${JSON.stringify(state, null, 2)}`);
1618
+ }
1619
+ parts2.push("");
1620
+ parts2.push(PATCH_INSTRUCTIONS);
1621
+ return parts2.join("\n");
1622
+ }
1623
+ const parts = [userText];
1624
+ if (state && Object.keys(state).length > 0) {
1625
+ parts.push(`
1626
+ AVAILABLE STATE:
1627
+ ${JSON.stringify(state, null, 2)}`);
1628
+ }
1629
+ parts.push(
1630
+ `
1631
+ Remember: Output /root first, then interleave /elements and /state patches so the UI fills in progressively as it streams. Output each state patch right after the elements that use it, one per array item.`
1632
+ );
1633
+ return parts.join("\n");
1634
+ }
1635
+
1111
1636
  // src/catalog.ts
1112
1637
  var import_zod6 = require("zod");
1113
1638
  function createCatalog(config) {
@@ -1124,22 +1649,18 @@ function createCatalog(config) {
1124
1649
  const componentSchemas = componentNames.map((componentName) => {
1125
1650
  const def = components[componentName];
1126
1651
  return import_zod6.z.object({
1127
- key: import_zod6.z.string(),
1128
1652
  type: import_zod6.z.literal(componentName),
1129
1653
  props: def.props,
1130
1654
  children: import_zod6.z.array(import_zod6.z.string()).optional(),
1131
- parentKey: import_zod6.z.string().nullable().optional(),
1132
1655
  visible: VisibilityConditionSchema.optional()
1133
1656
  });
1134
1657
  });
1135
1658
  let elementSchema;
1136
1659
  if (componentSchemas.length === 0) {
1137
1660
  elementSchema = import_zod6.z.object({
1138
- key: import_zod6.z.string(),
1139
1661
  type: import_zod6.z.string(),
1140
1662
  props: import_zod6.z.record(import_zod6.z.string(), import_zod6.z.unknown()),
1141
1663
  children: import_zod6.z.array(import_zod6.z.string()).optional(),
1142
- parentKey: import_zod6.z.string().nullable().optional(),
1143
1664
  visible: VisibilityConditionSchema.optional()
1144
1665
  });
1145
1666
  } else if (componentSchemas.length === 1) {
@@ -1221,7 +1742,7 @@ function generateCatalogPrompt(catalog) {
1221
1742
  lines.push("");
1222
1743
  lines.push("Components can have a `visible` property:");
1223
1744
  lines.push("- `true` / `false` - Always visible/hidden");
1224
- lines.push('- `{ "path": "/data/path" }` - Visible when path is truthy');
1745
+ lines.push('- `{ "path": "/state/path" }` - Visible when path is truthy');
1225
1746
  lines.push('- `{ "auth": "signedIn" }` - Visible when user is signed in');
1226
1747
  lines.push('- `{ "and": [...] }` - All conditions must be true');
1227
1748
  lines.push('- `{ "or": [...] }` - Any condition must be true');
@@ -1360,21 +1881,21 @@ function generateSystemPrompt(catalog, options = {}) {
1360
1881
  }
1361
1882
  lines.push("");
1362
1883
  }
1363
- lines.push("OUTPUT FORMAT (JSONL):");
1364
- lines.push('{"op":"set","path":"/root","value":"element-key"}');
1884
+ lines.push("OUTPUT FORMAT (JSONL, RFC 6902 JSON Patch):");
1885
+ lines.push('{"op":"add","path":"/root","value":"element-key"}');
1365
1886
  lines.push(
1366
- '{"op":"add","path":"/elements/key","value":{"key":"...","type":"...","props":{...},"children":[...]}}'
1887
+ '{"op":"add","path":"/elements/key","value":{"type":"...","props":{...},"children":[...]}}'
1367
1888
  );
1368
1889
  lines.push('{"op":"remove","path":"/elements/key"}');
1369
1890
  lines.push("");
1370
1891
  lines.push("RULES:");
1371
1892
  const baseRules = [
1372
- "First line sets /root to root element key",
1373
- "Add elements with /elements/{key}",
1893
+ 'First line sets /root to root element key: {"op":"add","path":"/root","value":"<key>"}',
1894
+ 'Add elements with /elements/{key}: {"op":"add","path":"/elements/<key>","value":{...}}',
1374
1895
  "Remove elements with op:remove - also update the parent's children array to exclude the removed key",
1375
1896
  "Children array contains string keys, not objects",
1376
1897
  "Parent first, then children",
1377
- "Each element needs: key, type, props",
1898
+ "Each element needs: type, props",
1378
1899
  "ONLY use props listed above - never invent new props"
1379
1900
  ];
1380
1901
  const allRules = [...baseRules, ...customRules];
@@ -1392,6 +1913,7 @@ function generateSystemPrompt(catalog, options = {}) {
1392
1913
  }
1393
1914
  // Annotate the CommonJS export names for ESM import in node:
1394
1915
  0 && (module.exports = {
1916
+ ActionBindingSchema,
1395
1917
  ActionConfirmSchema,
1396
1918
  ActionOnErrorSchema,
1397
1919
  ActionOnSuccessSchema,
@@ -1405,7 +1927,11 @@ function generateSystemPrompt(catalog, options = {}) {
1405
1927
  ValidationConfigSchema,
1406
1928
  VisibilityConditionSchema,
1407
1929
  action,
1930
+ actionBinding,
1931
+ addByPath,
1408
1932
  applySpecStreamPatch,
1933
+ autoFixSpec,
1934
+ buildUserPrompt,
1409
1935
  builtInValidationFunctions,
1410
1936
  check,
1411
1937
  compileSpecStream,
@@ -1417,16 +1943,21 @@ function generateSystemPrompt(catalog, options = {}) {
1417
1943
  evaluateVisibility,
1418
1944
  executeAction,
1419
1945
  findFormValue,
1946
+ formatSpecIssues,
1420
1947
  generateCatalogPrompt,
1421
1948
  generateSystemPrompt,
1422
1949
  getByPath,
1423
1950
  interpolateString,
1424
1951
  parseSpecStreamLine,
1952
+ removeByPath,
1425
1953
  resolveAction,
1426
1954
  resolveDynamicValue,
1955
+ resolveElementProps,
1956
+ resolvePropValue,
1427
1957
  runValidation,
1428
1958
  runValidationCheck,
1429
1959
  setByPath,
1960
+ validateSpec,
1430
1961
  visibility
1431
1962
  });
1432
1963
  //# sourceMappingURL=index.js.map