@frt-platform/report-core 1.4.0 → 1.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
@@ -60,7 +60,6 @@ var REPORT_TEMPLATE_FIELD_TYPES = [
60
60
  "singleSelect",
61
61
  "multiSelect",
62
62
  "repeatGroup"
63
- // NEW FIELD TYPE
64
63
  ];
65
64
  var Condition = import_zod.z.lazy(
66
65
  () => import_zod.z.union([
@@ -87,15 +86,17 @@ var BaseFieldSchema = import_zod.z.object({
87
86
  /^[a-z0-9_-]+$/,
88
87
  "Use lowercase letters, numbers, underscores, or dashes for field identifiers."
89
88
  ),
90
- type: import_zod.z.enum([
91
- "shortText",
92
- "longText",
93
- "number",
94
- "date",
95
- "checkbox",
96
- "singleSelect",
97
- "multiSelect"
98
- ]),
89
+ type: import_zod.z.enum(
90
+ [
91
+ "shortText",
92
+ "longText",
93
+ "number",
94
+ "date",
95
+ "checkbox",
96
+ "singleSelect",
97
+ "multiSelect"
98
+ ]
99
+ ),
99
100
  label: import_zod.z.string().min(1, "Field labels cannot be empty.").max(200, "Field labels must be 200 characters or fewer."),
100
101
  required: import_zod.z.boolean().optional(),
101
102
  description: import_zod.z.string().max(400, "Descriptions must be 400 characters or fewer.").optional(),
@@ -125,7 +126,9 @@ var BaseFieldSchema = import_zod.z.object({
125
126
  minSelections: import_zod.z.number().int().min(0).optional(),
126
127
  maxSelections: import_zod.z.number().int().min(0).optional(),
127
128
  // Privacy
128
- dataClassification: import_zod.z.enum(["none", "personal", "special"]).optional()
129
+ dataClassification: import_zod.z.enum(["none", "personal", "special"]).optional(),
130
+ // Computed expression (backend/platform evaluated)
131
+ computed: import_zod.z.string().max(200, "Computed expressions must be 200 characters or fewer.").optional()
129
132
  });
130
133
  var RepeatGroupFieldSchema = import_zod.z.lazy(
131
134
  () => import_zod.z.object({
@@ -138,24 +141,19 @@ var RepeatGroupFieldSchema = import_zod.z.lazy(
138
141
  // Conditional logic for the whole group
139
142
  visibleIf: Condition.optional(),
140
143
  requiredIf: Condition.optional(),
141
- // NEW: Dynamic and static min/max
144
+ // Static + dynamic min/max
142
145
  min: import_zod.z.number().int().min(0).optional(),
143
146
  max: import_zod.z.number().int().min(1).optional(),
144
147
  minIf: Condition.optional(),
145
148
  maxIf: Condition.optional(),
146
149
  // Contents: nested fields (recursive)
147
- fields: import_zod.z.array(
148
- import_zod.z.lazy(() => ReportTemplateFieldSchema)
149
- ).min(1, "Repeat groups must contain at least one field."),
150
+ fields: import_zod.z.array(import_zod.z.lazy(() => ReportTemplateFieldSchema)).min(1, "Repeat groups must contain at least one field."),
150
151
  // Pre-populated rows allowed
151
152
  defaultValue: import_zod.z.array(import_zod.z.record(import_zod.z.any())).optional()
152
153
  })
153
154
  );
154
155
  var ReportTemplateFieldSchema = import_zod.z.lazy(
155
- () => import_zod.z.union([
156
- BaseFieldSchema,
157
- RepeatGroupFieldSchema
158
- ])
156
+ () => import_zod.z.union([BaseFieldSchema, RepeatGroupFieldSchema])
159
157
  );
160
158
  var ReportTemplateSectionSchema = import_zod.z.object({
161
159
  id: import_zod.z.string().min(1, "Section identifiers cannot be empty.").max(60, "Section identifiers must be 60 characters or fewer.").regex(
@@ -173,62 +171,82 @@ var ReportTemplateSchemaValidator = import_zod.z.object({
173
171
  sections: import_zod.z.array(ReportTemplateSectionSchema).max(25, "Templates can include at most 25 sections.").default([])
174
172
  });
175
173
 
174
+ // src/template/ids.ts
175
+ function createUniqueId(prefix, existing) {
176
+ const used = new Set(existing);
177
+ let attempt = used.size + 1;
178
+ let candidate = `${prefix}-${attempt}`;
179
+ while (used.has(candidate)) {
180
+ attempt += 1;
181
+ candidate = `${prefix}-${attempt}`;
182
+ }
183
+ return candidate;
184
+ }
185
+
176
186
  // src/template/migrate.ts
177
- var LEGACY_FIELD_TYPE_MAP = {
178
- shorttext: "shortText",
179
- text: "shortText",
180
- "text-box": "shortText",
181
- longtext: "longText",
182
- textarea: "longText",
183
- paragraph: "longText",
184
- number: "number",
185
- numeric: "number",
186
- date: "date",
187
- checkbox: "checkbox",
188
- boolean: "checkbox",
189
- singleselect: "singleSelect",
190
- select: "singleSelect",
191
- dropdown: "singleSelect",
192
- radio: "singleSelect",
193
- "multi-select": "multiSelect",
194
- multiselect: "multiSelect",
195
- checkboxes: "multiSelect"
196
- };
187
+ function mapLegacyFieldType(type) {
188
+ if (!type) return "shortText";
189
+ const t = type.toLowerCase();
190
+ if (t === "text" || t === "short_text" || t === "input") return "shortText";
191
+ if (t === "textarea" || t === "long_text") return "longText";
192
+ if (t === "number" || t === "numeric") return "number";
193
+ if (t === "date" || t === "datetime" || t === "datetime-local") return "date";
194
+ if (t === "checkbox") return "checkbox";
195
+ if (t === "checkboxes" || t === "multi_select" || t === "multiselect") {
196
+ return "multiSelect";
197
+ }
198
+ if (t === "select" || t === "single_select" || t === "dropdown") {
199
+ return "singleSelect";
200
+ }
201
+ return type;
202
+ }
197
203
  function migrateLegacySchema(raw) {
198
- if (!raw || typeof raw !== "object") return raw;
199
- const obj = structuredClone(raw);
200
- if (obj.fields) {
201
- obj.sections = [
204
+ if (!raw || typeof raw !== "object") {
205
+ return {
206
+ version: REPORT_TEMPLATE_VERSION,
207
+ sections: []
208
+ };
209
+ }
210
+ const legacy = raw;
211
+ const version = typeof legacy.version === "number" ? legacy.version : REPORT_TEMPLATE_VERSION;
212
+ function mapFields(fields) {
213
+ if (!Array.isArray(fields)) return [];
214
+ let counter = 0;
215
+ return fields.map((field) => {
216
+ counter += 1;
217
+ const fallbackId = `field_${counter}`;
218
+ return {
219
+ ...field,
220
+ id: field.id ?? fallbackId,
221
+ type: mapLegacyFieldType(field.type)
222
+ };
223
+ });
224
+ }
225
+ let sections;
226
+ if (Array.isArray(legacy.sections)) {
227
+ sections = legacy.sections.map((sec, index) => ({
228
+ id: sec.id ?? `section_${index + 1}`,
229
+ title: sec.title ?? legacy.title,
230
+ description: sec.description ?? legacy.description,
231
+ fields: mapFields(sec.fields)
232
+ }));
233
+ } else if (Array.isArray(legacy.fields)) {
234
+ sections = [
202
235
  {
203
236
  id: "section_1",
204
- // ⬅ underscore to match normalizer/tests
205
- title: obj.title,
206
- description: obj.description,
207
- fields: obj.fields.map((f, i) => ({
208
- ...f,
209
- id: f.id || `field_${i + 1}`,
210
- // ⬅ underscore here too
211
- type: LEGACY_FIELD_TYPE_MAP[f.type?.toLowerCase()] ?? f.type
212
- }))
237
+ title: legacy.title,
238
+ description: legacy.description,
239
+ fields: mapFields(legacy.fields)
213
240
  }
214
241
  ];
215
- delete obj.fields;
216
- return obj;
217
- }
218
- if (Array.isArray(obj.sections)) {
219
- obj.sections = obj.sections.map((sec, i) => ({
220
- ...sec,
221
- id: sec.id || `section_${i + 1}`,
222
- // ⬅ underscore
223
- fields: (sec.fields || []).map((f, idx) => ({
224
- ...f,
225
- id: f.id || `field_${idx + 1}`,
226
- // ⬅ underscore
227
- type: LEGACY_FIELD_TYPE_MAP[f.type?.toLowerCase()] ?? f.type
228
- }))
229
- }));
242
+ } else {
243
+ sections = legacy.sections ?? [];
230
244
  }
231
- return obj;
245
+ return {
246
+ ...legacy,
247
+ version,
248
+ sections
249
+ };
232
250
  }
233
251
 
234
252
  // src/template/normalize.ts
@@ -340,27 +358,8 @@ function serializeReportTemplateSchema(schema, options = {}) {
340
358
  }
341
359
 
342
360
  // src/template/diff.ts
343
- function indexById(items) {
344
- const map = {};
345
- items.forEach((item, i) => map[item.id] = i);
346
- return map;
347
- }
348
- function diffObjectProperties(before, after, ignoreKeys = []) {
349
- const changes = [];
350
- const keys = /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)]);
351
- for (const key of keys) {
352
- if (ignoreKeys.includes(key)) continue;
353
- const b = before[key];
354
- const a = after[key];
355
- const changed = Array.isArray(b) && Array.isArray(a) ? JSON.stringify(b) !== JSON.stringify(a) : b !== a;
356
- if (changed) {
357
- changes.push({ key, before: b, after: a });
358
- }
359
- }
360
- return changes;
361
- }
362
- function diffRepeatGroupFields(beforeFields, afterFields, sectionId, groupId) {
363
- const empty = {
361
+ function createEmptyDiff() {
362
+ return {
364
363
  addedSections: [],
365
364
  removedSections: [],
366
365
  reorderedSections: [],
@@ -371,137 +370,191 @@ function diffRepeatGroupFields(beforeFields, afterFields, sectionId, groupId) {
371
370
  modifiedFields: [],
372
371
  nestedFieldDiffs: []
373
372
  };
374
- const beforeIndex = indexById(beforeFields);
375
- const afterIndex = indexById(afterFields);
376
- for (const f of afterFields) {
377
- if (!(f.id in beforeIndex)) {
378
- empty.addedFields.push({ sectionId, fieldId: f.id, index: afterIndex[f.id] });
373
+ }
374
+ function shallowDiff(before, after, ignoreKeys) {
375
+ const changes = [];
376
+ const keys = /* @__PURE__ */ new Set([
377
+ ...Object.keys(before ?? {}),
378
+ ...Object.keys(after ?? {})
379
+ ]);
380
+ for (const key of keys) {
381
+ if (ignoreKeys.has(key)) continue;
382
+ const beforeVal = before[key];
383
+ const afterVal = after[key];
384
+ if (beforeVal !== afterVal) {
385
+ changes.push({ key, before: beforeVal, after: afterVal });
379
386
  }
380
387
  }
381
- for (const f of beforeFields) {
382
- if (!(f.id in afterIndex)) {
383
- empty.removedFields.push({ sectionId, fieldId: f.id, index: beforeIndex[f.id] });
388
+ return changes;
389
+ }
390
+ function diffTemplates(before, after) {
391
+ const diff = createEmptyDiff();
392
+ const beforeSections = before.sections ?? [];
393
+ const afterSections = after.sections ?? [];
394
+ const beforeSectionIndex = /* @__PURE__ */ new Map();
395
+ const afterSectionIndex = /* @__PURE__ */ new Map();
396
+ beforeSections.forEach((s, idx) => beforeSectionIndex.set(s.id, idx));
397
+ afterSections.forEach((s, idx) => afterSectionIndex.set(s.id, idx));
398
+ for (const [id, idx] of afterSectionIndex) {
399
+ if (!beforeSectionIndex.has(id)) {
400
+ diff.addedSections.push({ sectionId: id, index: idx });
384
401
  }
385
402
  }
386
- for (const f of afterFields) {
387
- if (f.id in beforeIndex) {
388
- const from = beforeIndex[f.id];
389
- const to = afterIndex[f.id];
390
- if (from !== to) {
391
- empty.reorderedFields.push({
392
- sectionId,
393
- fieldId: f.id,
394
- from,
395
- to
396
- });
397
- }
403
+ for (const [id, idx] of beforeSectionIndex) {
404
+ if (!afterSectionIndex.has(id)) {
405
+ diff.removedSections.push({ sectionId: id, index: idx });
398
406
  }
399
407
  }
400
- for (const f of afterFields) {
401
- const idx = beforeIndex[f.id];
402
- if (idx == null) continue;
403
- const before = beforeFields[idx];
404
- const changes = diffObjectProperties(before, f, ["id", "fields"]);
405
- if (changes.length > 0) {
406
- empty.modifiedFields.push({
407
- sectionId,
408
- fieldId: f.id,
409
- before,
410
- after: f,
411
- changes
408
+ for (const [id, beforeIdx] of beforeSectionIndex) {
409
+ const afterIdx = afterSectionIndex.get(id);
410
+ if (afterIdx != null && afterIdx !== beforeIdx) {
411
+ diff.reorderedSections.push({
412
+ sectionId: id,
413
+ from: beforeIdx,
414
+ to: afterIdx
412
415
  });
413
416
  }
414
- if (f.type === "repeatGroup" && Array.isArray(f.fields)) {
415
- const innerBefore = before.fields ?? [];
416
- const innerAfter = f.fields;
417
- const nested = diffRepeatGroupFields(
418
- innerBefore,
419
- innerAfter,
420
- sectionId,
421
- f.id
417
+ }
418
+ function getSectionById(sections, id) {
419
+ return sections.find((s) => s.id === id);
420
+ }
421
+ for (const [id] of beforeSectionIndex) {
422
+ if (!afterSectionIndex.has(id)) continue;
423
+ const beforeSec = getSectionById(beforeSections, id);
424
+ const afterSec = getSectionById(afterSections, id);
425
+ const changes = shallowDiff(
426
+ beforeSec,
427
+ afterSec,
428
+ /* @__PURE__ */ new Set(["id", "fields"])
429
+ );
430
+ if (changes.length > 0) {
431
+ diff.modifiedSections.push({ sectionId: id, changes });
432
+ }
433
+ }
434
+ function diffFieldsForSection(sectionId, beforeFields, afterFields) {
435
+ const beforeIndex = /* @__PURE__ */ new Map();
436
+ const afterIndex = /* @__PURE__ */ new Map();
437
+ beforeFields.forEach((f, idx) => beforeIndex.set(f.id, idx));
438
+ afterFields.forEach((f, idx) => afterIndex.set(f.id, idx));
439
+ for (const [fieldId, idx] of afterIndex) {
440
+ if (!beforeIndex.has(fieldId)) {
441
+ diff.addedFields.push({ sectionId, fieldId, index: idx });
442
+ }
443
+ }
444
+ for (const [fieldId, idx] of beforeIndex) {
445
+ if (!afterIndex.has(fieldId)) {
446
+ diff.removedFields.push({ sectionId, fieldId, index: idx });
447
+ }
448
+ }
449
+ for (const [fieldId, beforeIdx] of beforeIndex) {
450
+ const afterIdx = afterIndex.get(fieldId);
451
+ if (afterIdx != null && afterIdx !== beforeIdx) {
452
+ diff.reorderedFields.push({
453
+ sectionId,
454
+ fieldId,
455
+ from: beforeIdx,
456
+ to: afterIdx
457
+ });
458
+ }
459
+ }
460
+ const ignoreFieldKeys = /* @__PURE__ */ new Set(["id", "fields"]);
461
+ const getField = (fields, id) => fields.find((f) => f.id === id);
462
+ for (const [fieldId] of beforeIndex) {
463
+ if (!afterIndex.has(fieldId)) continue;
464
+ const beforeField = getField(beforeFields, fieldId);
465
+ const afterField = getField(afterFields, fieldId);
466
+ const changes = shallowDiff(
467
+ beforeField,
468
+ afterField,
469
+ ignoreFieldKeys
422
470
  );
423
- if (nested.addedFields.length > 0 || nested.removedFields.length > 0 || nested.reorderedFields.length > 0 || nested.modifiedFields.length > 0) {
424
- empty.nestedFieldDiffs.push({
471
+ if (changes.length > 0) {
472
+ diff.modifiedFields.push({
425
473
  sectionId,
426
- groupId: f.id,
427
- diffs: nested
474
+ fieldId,
475
+ changes
428
476
  });
429
477
  }
478
+ if (beforeField.type === "repeatGroup" && afterField.type === "repeatGroup") {
479
+ const nestedBeforeFields = beforeField.fields ?? [];
480
+ const nestedAfterFields = afterField.fields ?? [];
481
+ const nestedDiff = createEmptyDiff();
482
+ diffFieldsInto(
483
+ sectionId,
484
+ fieldId,
485
+ nestedBeforeFields,
486
+ nestedAfterFields,
487
+ nestedDiff
488
+ );
489
+ if (hasAnyFieldDiff(nestedDiff)) {
490
+ diff.nestedFieldDiffs.push({
491
+ sectionId,
492
+ groupId: fieldId,
493
+ diffs: nestedDiff
494
+ });
495
+ }
496
+ }
430
497
  }
431
498
  }
432
- return empty;
433
- }
434
- function diffTemplates(before, after) {
435
- const diff = {
436
- addedSections: [],
437
- removedSections: [],
438
- reorderedSections: [],
439
- modifiedSections: [],
440
- addedFields: [],
441
- removedFields: [],
442
- reorderedFields: [],
443
- modifiedFields: [],
444
- nestedFieldDiffs: []
445
- };
446
- const beforeSections = before.sections;
447
- const afterSections = after.sections;
448
- const beforeSecIndex = indexById(beforeSections);
449
- const afterSecIndex = indexById(afterSections);
450
- for (const sec of afterSections) {
451
- if (!(sec.id in beforeSecIndex)) {
452
- diff.addedSections.push({
453
- sectionId: sec.id,
454
- index: afterSecIndex[sec.id]
455
- });
499
+ function diffFieldsInto(sectionId, groupId, beforeFields, afterFields, target) {
500
+ const beforeIndex = /* @__PURE__ */ new Map();
501
+ const afterIndex = /* @__PURE__ */ new Map();
502
+ beforeFields.forEach((f, idx) => beforeIndex.set(f.id, idx));
503
+ afterFields.forEach((f, idx) => afterIndex.set(f.id, idx));
504
+ for (const [fieldId, idx] of afterIndex) {
505
+ if (!beforeIndex.has(fieldId)) {
506
+ target.addedFields.push({ sectionId, fieldId, index: idx });
507
+ }
456
508
  }
457
- }
458
- for (const sec of beforeSections) {
459
- if (!(sec.id in afterSecIndex)) {
460
- diff.removedSections.push({
461
- sectionId: sec.id,
462
- index: beforeSecIndex[sec.id]
463
- });
509
+ for (const [fieldId, idx] of beforeIndex) {
510
+ if (!afterIndex.has(fieldId)) {
511
+ target.removedFields.push({ sectionId, fieldId, index: idx });
512
+ }
464
513
  }
465
- }
466
- for (const sec of afterSections) {
467
- if (sec.id in beforeSecIndex) {
468
- const from = beforeSecIndex[sec.id];
469
- const to = afterSecIndex[sec.id];
470
- if (from !== to) {
471
- diff.reorderedSections.push({ sectionId: sec.id, from, to });
514
+ for (const [fieldId, beforeIdx] of beforeIndex) {
515
+ const afterIdx = afterIndex.get(fieldId);
516
+ if (afterIdx != null && afterIdx !== beforeIdx) {
517
+ target.reorderedFields.push({
518
+ sectionId,
519
+ fieldId,
520
+ from: beforeIdx,
521
+ to: afterIdx
522
+ });
472
523
  }
473
524
  }
474
- }
475
- for (const sec of afterSections) {
476
- const idx = beforeSecIndex[sec.id];
477
- if (idx == null) continue;
478
- const beforeSec = beforeSections[idx];
479
- const changes = diffObjectProperties(beforeSec, sec, ["id", "fields"]);
480
- if (changes.length > 0) {
481
- diff.modifiedSections.push({
482
- sectionId: sec.id,
483
- changes
484
- });
525
+ const ignoreFieldKeys = /* @__PURE__ */ new Set(["id", "fields"]);
526
+ const getField = (fields, id) => fields.find((f) => f.id === id);
527
+ for (const [fieldId] of beforeIndex) {
528
+ if (!afterIndex.has(fieldId)) continue;
529
+ const beforeField = getField(beforeFields, fieldId);
530
+ const afterField = getField(afterFields, fieldId);
531
+ const changes = shallowDiff(
532
+ beforeField,
533
+ afterField,
534
+ ignoreFieldKeys
535
+ );
536
+ if (changes.length > 0) {
537
+ target.modifiedFields.push({
538
+ sectionId,
539
+ fieldId,
540
+ changes
541
+ });
542
+ }
485
543
  }
486
544
  }
487
- for (const afterSec of afterSections) {
488
- const secId = afterSec.id;
489
- const beforeIdx = beforeSecIndex[secId];
490
- if (beforeIdx == null) continue;
545
+ function hasAnyFieldDiff(d) {
546
+ return d.addedFields.length > 0 || d.removedFields.length > 0 || d.reorderedFields.length > 0 || d.modifiedFields.length > 0 || d.addedSections.length > 0 || d.removedSections.length > 0 || d.reorderedSections.length > 0 || d.modifiedSections.length > 0 || d.nestedFieldDiffs.length > 0;
547
+ }
548
+ for (const [id, beforeIdx] of beforeSectionIndex) {
549
+ const afterIdx = afterSectionIndex.get(id);
550
+ if (afterIdx == null) continue;
491
551
  const beforeSec = beforeSections[beforeIdx];
492
- const beforeFields = beforeSec.fields ?? [];
493
- const afterFields = afterSec.fields ?? [];
494
- const nestedDiff = diffRepeatGroupFields(
495
- beforeFields,
496
- afterFields,
497
- secId,
498
- ""
552
+ const afterSec = afterSections[afterIdx];
553
+ diffFieldsForSection(
554
+ id,
555
+ beforeSec.fields ?? [],
556
+ afterSec.fields ?? []
499
557
  );
500
- diff.addedFields.push(...nestedDiff.addedFields);
501
- diff.removedFields.push(...nestedDiff.removedFields);
502
- diff.reorderedFields.push(...nestedDiff.reorderedFields);
503
- diff.modifiedFields.push(...nestedDiff.modifiedFields);
504
- diff.nestedFieldDiffs.push(...nestedDiff.nestedFieldDiffs);
505
558
  }
506
559
  return diff;
507
560
  }
@@ -515,7 +568,9 @@ function mapFieldTypeToJSONSchemaCore(field) {
515
568
  "x-frt-placeholder": field.placeholder,
516
569
  "x-frt-dataClassification": field.dataClassification,
517
570
  "x-frt-visibleIf": field.visibleIf,
518
- "x-frt-requiredIf": field.requiredIf
571
+ "x-frt-requiredIf": field.requiredIf,
572
+ // ⭐ surface computed expression as vendor extension
573
+ "x-frt-computed": field.computed
519
574
  };
520
575
  switch (field.type) {
521
576
  case "shortText":
@@ -659,58 +714,86 @@ function exportJSONSchema(template) {
659
714
  return schema;
660
715
  }
661
716
 
717
+ // src/validation/conditions.ts
718
+ function evaluateCondition(condition, response) {
719
+ if (!condition) return true;
720
+ if ("equals" in condition) {
721
+ const entries = Object.entries(condition.equals ?? {});
722
+ if (entries.length === 0) return true;
723
+ return entries.every(([key, expected]) => {
724
+ const value = response[key];
725
+ return value === expected;
726
+ });
727
+ }
728
+ if ("any" in condition) {
729
+ const children = condition.any ?? [];
730
+ if (children.length === 0) return false;
731
+ return children.some(
732
+ (child) => evaluateCondition(child, response)
733
+ );
734
+ }
735
+ if ("all" in condition) {
736
+ const children = condition.all ?? [];
737
+ if (children.length === 0) return true;
738
+ return children.every(
739
+ (child) => evaluateCondition(child, response)
740
+ );
741
+ }
742
+ if ("not" in condition) {
743
+ return !evaluateCondition(condition.not, response);
744
+ }
745
+ return true;
746
+ }
747
+
748
+ // src/validation/baseSchema.ts
749
+ var import_zod2 = require("zod");
750
+
662
751
  // src/fields/defaults.ts
663
- var DEFAULT_FIELD_LABEL = "Untitled question";
752
+ var DEFAULT_FIELD_LABEL = "Untitled field";
664
753
  var CORE_FIELD_DEFAULTS = {
665
754
  shortText: {
755
+ type: "shortText",
666
756
  label: DEFAULT_FIELD_LABEL,
667
757
  placeholder: "Short answer text"
668
758
  },
669
759
  longText: {
760
+ type: "longText",
670
761
  label: DEFAULT_FIELD_LABEL,
671
762
  placeholder: "Long answer text"
672
763
  },
673
764
  number: {
765
+ type: "number",
674
766
  label: DEFAULT_FIELD_LABEL,
675
767
  placeholder: "123"
676
768
  },
677
769
  date: {
770
+ type: "date",
678
771
  label: DEFAULT_FIELD_LABEL
679
772
  },
680
773
  checkbox: {
681
- label: DEFAULT_FIELD_LABEL,
682
- placeholder: "Check to confirm"
774
+ type: "checkbox",
775
+ label: DEFAULT_FIELD_LABEL
683
776
  },
684
777
  singleSelect: {
778
+ type: "singleSelect",
685
779
  label: DEFAULT_FIELD_LABEL,
686
780
  options: ["Option 1", "Option 2", "Option 3"]
687
781
  },
688
782
  multiSelect: {
783
+ type: "multiSelect",
689
784
  label: DEFAULT_FIELD_LABEL,
690
785
  options: ["Option 1", "Option 2", "Option 3"],
691
786
  allowOther: false
692
787
  },
693
788
  repeatGroup: {
694
- // Minimal safe defaults – your builder UI is expected
695
- // to configure nested fields + min/max as needed.
789
+ type: "repeatGroup",
696
790
  label: DEFAULT_FIELD_LABEL,
791
+ // The actual shape is filled in by the builder;
792
+ // for tests we just need this to be an array.
697
793
  fields: []
698
- // nested fields go here in the UI layer
699
794
  }
700
795
  };
701
796
 
702
- // src/fields/ids.ts
703
- function createUniqueId(prefix, existing) {
704
- const used = new Set(existing);
705
- let attempt = used.size + 1;
706
- let candidate = `${prefix}-${attempt}`;
707
- while (used.has(candidate)) {
708
- attempt++;
709
- candidate = `${prefix}-${attempt}`;
710
- }
711
- return candidate;
712
- }
713
-
714
797
  // src/fields/registry.ts
715
798
  var FieldRegistryClass = class {
716
799
  constructor() {
@@ -734,52 +817,191 @@ var FieldRegistryClass = class {
734
817
  }
735
818
  };
736
819
  var FieldRegistry = new FieldRegistryClass();
737
- var CORE_TYPES = [
738
- "shortText",
739
- "longText",
740
- "number",
741
- "date",
742
- "checkbox",
743
- "singleSelect",
744
- "multiSelect",
745
- "repeatGroup"
746
- ];
747
- for (const type of CORE_TYPES) {
820
+ for (const type of REPORT_TEMPLATE_FIELD_TYPES) {
748
821
  FieldRegistry.register(type, {
749
822
  defaults: CORE_FIELD_DEFAULTS[type]
750
823
  // Core types rely on existing validation logic.
751
- // buildResponseSchema is optional — fallback handles it.
824
+ // buildResponseSchema is optional — the validation engine provides
825
+ // built-in handlers for all core types.
752
826
  });
753
827
  }
754
828
 
755
- // src/validation/conditions.ts
756
- function evaluateCondition(condition, response) {
757
- if ("equals" in condition) {
758
- return Object.entries(condition.equals).every(([key, val]) => {
759
- return response[key] === val;
760
- });
829
+ // src/validation/baseSchema.ts
830
+ function buildBaseFieldSchema(field) {
831
+ const isRequired = Boolean(field.required);
832
+ const registry = FieldRegistry.get(field.type);
833
+ if (registry?.buildResponseSchema) {
834
+ return registry.buildResponseSchema(field);
761
835
  }
762
- if ("any" in condition) {
763
- return condition.any.some(
764
- (sub) => evaluateCondition(sub, response)
765
- );
836
+ switch (field.type) {
837
+ case "shortText":
838
+ case "longText": {
839
+ let schema = import_zod2.z.string();
840
+ if (typeof field.minLength === "number") {
841
+ schema = schema.min(field.minLength);
842
+ }
843
+ if (typeof field.maxLength === "number") {
844
+ schema = schema.max(field.maxLength);
845
+ }
846
+ return isRequired ? schema : schema.optional();
847
+ }
848
+ case "number": {
849
+ let schema = import_zod2.z.number();
850
+ if (typeof field.minValue === "number") {
851
+ schema = schema.min(field.minValue);
852
+ }
853
+ if (typeof field.maxValue === "number") {
854
+ schema = schema.max(field.maxValue);
855
+ }
856
+ return isRequired ? schema : schema.optional();
857
+ }
858
+ case "date": {
859
+ const schema = import_zod2.z.string();
860
+ return isRequired ? schema : schema.optional();
861
+ }
862
+ case "checkbox": {
863
+ if (isRequired) return import_zod2.z.literal(true);
864
+ return import_zod2.z.boolean().optional();
865
+ }
866
+ case "singleSelect": {
867
+ let schema = import_zod2.z.string();
868
+ const opts = field.options;
869
+ const hasOptions = Array.isArray(opts) && opts.length > 0;
870
+ if (hasOptions && !field.allowOther) {
871
+ const allowed = new Set(opts);
872
+ schema = schema.refine(
873
+ (value) => allowed.has(value),
874
+ "Value must be one of the defined options."
875
+ );
876
+ }
877
+ return isRequired ? schema : schema.optional();
878
+ }
879
+ case "multiSelect": {
880
+ let schema = import_zod2.z.array(import_zod2.z.string());
881
+ const opts = field.options;
882
+ const hasOptions = Array.isArray(opts) && opts.length > 0;
883
+ if (hasOptions && !field.allowOther) {
884
+ const allowed = new Set(opts);
885
+ schema = schema.refine(
886
+ (values) => values.every((v) => allowed.has(v)),
887
+ "All values must be among the defined options."
888
+ );
889
+ }
890
+ if (typeof field.minSelections === "number") {
891
+ schema = schema.min(
892
+ field.minSelections,
893
+ `Select at least ${field.minSelections} option(s).`
894
+ );
895
+ } else if (isRequired) {
896
+ schema = schema.min(
897
+ 1,
898
+ "Select at least one option."
899
+ );
900
+ }
901
+ if (typeof field.maxSelections === "number") {
902
+ schema = schema.max(
903
+ field.maxSelections,
904
+ `Select at most ${field.maxSelections} option(s).`
905
+ );
906
+ }
907
+ return isRequired ? schema : schema.optional();
908
+ }
909
+ case "repeatGroup": {
910
+ return import_zod2.z.any();
911
+ }
912
+ default:
913
+ return import_zod2.z.any();
766
914
  }
767
- if ("all" in condition) {
768
- return condition.all.every(
769
- (sub) => evaluateCondition(sub, response)
915
+ }
916
+
917
+ // src/validation/responses.ts
918
+ var import_zod4 = require("zod");
919
+
920
+ // src/validation/errors.ts
921
+ var import_zod3 = require("zod");
922
+ function mapZodIssueToFieldErrorCode(issue) {
923
+ switch (issue.code) {
924
+ case import_zod3.z.ZodIssueCode.invalid_type:
925
+ return "field.invalid_type";
926
+ case import_zod3.z.ZodIssueCode.too_small:
927
+ return "field.too_small";
928
+ case import_zod3.z.ZodIssueCode.too_big:
929
+ return "field.too_big";
930
+ case import_zod3.z.ZodIssueCode.invalid_enum_value:
931
+ case import_zod3.z.ZodIssueCode.invalid_literal:
932
+ return "field.invalid_option";
933
+ case import_zod3.z.ZodIssueCode.custom:
934
+ return "field.custom";
935
+ default:
936
+ return "field.custom";
937
+ }
938
+ }
939
+ function buildFieldMetadataLookup(template) {
940
+ const map = /* @__PURE__ */ new Map();
941
+ for (const section of template.sections) {
942
+ for (const field of section.fields) {
943
+ map.set(field.id, {
944
+ sectionId: section.id,
945
+ sectionTitle: section.title,
946
+ label: field.label
947
+ });
948
+ }
949
+ }
950
+ return map;
951
+ }
952
+ function mapZodIssuesToResponseErrors(template, issues) {
953
+ const fieldMeta = buildFieldMetadataLookup(template);
954
+ const errors = [];
955
+ for (const issue of issues) {
956
+ const path = issue.path ?? [];
957
+ const topLevelFieldId = path.find(
958
+ (p) => typeof p === "string"
959
+ );
960
+ const rowIndex = path.find(
961
+ (p) => typeof p === "number"
770
962
  );
963
+ const nestedFieldId = path.length >= 3 && typeof path[path.length - 1] === "string" ? path[path.length - 1] : void 0;
964
+ const fieldId = topLevelFieldId ?? "";
965
+ const meta = fieldId ? fieldMeta.get(fieldId) : void 0;
966
+ const code = mapZodIssueToFieldErrorCode(issue);
967
+ const sectionLabel = meta?.sectionTitle ?? meta?.sectionId;
968
+ const fieldLabel = meta?.label ?? fieldId;
969
+ let message;
970
+ if (rowIndex != null && nestedFieldId && meta && sectionLabel) {
971
+ message = `Section "${sectionLabel}" \u2192 "${meta.label}" (row ${rowIndex + 1}, field "${nestedFieldId}"): ${issue.message}`;
972
+ } else if (meta && sectionLabel && fieldLabel) {
973
+ message = `Section "${sectionLabel}" \u2192 Field "${fieldLabel}": ${issue.message}`;
974
+ } else if (fieldLabel) {
975
+ message = `Field "${fieldLabel}": ${issue.message}`;
976
+ } else {
977
+ message = issue.message;
978
+ }
979
+ errors.push({
980
+ fieldId,
981
+ sectionId: meta?.sectionId,
982
+ sectionTitle: meta?.sectionTitle,
983
+ label: meta?.label,
984
+ code,
985
+ message,
986
+ rawIssue: issue
987
+ });
771
988
  }
772
- if ("not" in condition) {
773
- const sub = condition.not;
774
- return !evaluateCondition(sub, response);
989
+ return errors;
990
+ }
991
+ function explainValidationError(template, error) {
992
+ if (error instanceof import_zod3.ZodError) {
993
+ return mapZodIssuesToResponseErrors(template, error.issues);
775
994
  }
776
- return false;
995
+ return null;
777
996
  }
778
997
 
779
998
  // src/validation/responses.ts
780
- var import_zod2 = require("zod");
781
- function buildBaseFieldSchema(field) {
999
+ function buildBaseFieldSchema2(field) {
782
1000
  const isRequired = Boolean(field.required);
1001
+ const isComputed = Boolean(field.computed);
1002
+ if (isComputed) {
1003
+ return import_zod4.z.any().optional();
1004
+ }
783
1005
  const registry = FieldRegistry.get(field.type);
784
1006
  if (registry?.buildResponseSchema) {
785
1007
  return registry.buildResponseSchema(field);
@@ -787,7 +1009,7 @@ function buildBaseFieldSchema(field) {
787
1009
  switch (field.type) {
788
1010
  case "shortText":
789
1011
  case "longText": {
790
- let schema = import_zod2.z.string();
1012
+ let schema = import_zod4.z.string();
791
1013
  if (typeof field.minLength === "number") {
792
1014
  schema = schema.min(field.minLength);
793
1015
  }
@@ -797,7 +1019,7 @@ function buildBaseFieldSchema(field) {
797
1019
  return isRequired ? schema : schema.optional();
798
1020
  }
799
1021
  case "number": {
800
- let schema = import_zod2.z.number();
1022
+ let schema = import_zod4.z.number();
801
1023
  if (typeof field.minValue === "number") {
802
1024
  schema = schema.min(field.minValue);
803
1025
  }
@@ -807,15 +1029,15 @@ function buildBaseFieldSchema(field) {
807
1029
  return isRequired ? schema : schema.optional();
808
1030
  }
809
1031
  case "date": {
810
- let schema = import_zod2.z.string();
1032
+ let schema = import_zod4.z.string();
811
1033
  return isRequired ? schema : schema.optional();
812
1034
  }
813
1035
  case "checkbox": {
814
- if (isRequired) return import_zod2.z.literal(true);
815
- return import_zod2.z.boolean().optional();
1036
+ if (isRequired) return import_zod4.z.literal(true);
1037
+ return import_zod4.z.boolean().optional();
816
1038
  }
817
1039
  case "singleSelect": {
818
- let schema = import_zod2.z.string();
1040
+ let schema = import_zod4.z.string();
819
1041
  if (Array.isArray(field.options) && field.options.length > 0) {
820
1042
  const allowed = new Set(field.options);
821
1043
  schema = schema.refine(
@@ -826,11 +1048,13 @@ function buildBaseFieldSchema(field) {
826
1048
  return isRequired ? schema : schema.optional();
827
1049
  }
828
1050
  case "multiSelect": {
829
- let schema = import_zod2.z.array(import_zod2.z.string());
1051
+ let schema = import_zod4.z.array(import_zod4.z.string());
830
1052
  if (Array.isArray(field.options) && field.options.length > 0 && !field.allowOther) {
831
1053
  const allowed = new Set(field.options);
832
1054
  schema = schema.refine(
833
- (values) => values.every((value) => allowed.has(value)),
1055
+ (values) => values.every(
1056
+ (value) => allowed.has(value)
1057
+ ),
834
1058
  "All values must be among the defined options."
835
1059
  );
836
1060
  }
@@ -857,11 +1081,11 @@ function buildBaseFieldSchema(field) {
857
1081
  const nestedFields = Array.isArray(field.fields) ? field.fields : [];
858
1082
  const rowShape = {};
859
1083
  for (const nested of nestedFields) {
860
- rowShape[nested.id] = buildBaseFieldSchema(
1084
+ rowShape[nested.id] = buildBaseFieldSchema2(
861
1085
  nested
862
1086
  );
863
1087
  }
864
- let schema = import_zod2.z.array(import_zod2.z.object(rowShape));
1088
+ let schema = import_zod4.z.array(import_zod4.z.object(rowShape));
865
1089
  if (typeof field.min === "number") {
866
1090
  schema = schema.min(
867
1091
  field.min,
@@ -877,12 +1101,11 @@ function buildBaseFieldSchema(field) {
877
1101
  return isRequired ? schema : schema.optional();
878
1102
  }
879
1103
  default: {
880
- return import_zod2.z.any();
1104
+ return import_zod4.z.any();
881
1105
  }
882
1106
  }
883
1107
  }
884
1108
  function buildRepeatGroupSchemaWithConditions(field, response) {
885
- const isRequired = Boolean(field.required);
886
1109
  const nestedFields = Array.isArray(field.fields) ? field.fields : [];
887
1110
  const rowShape = {};
888
1111
  for (const nested of nestedFields) {
@@ -891,48 +1114,58 @@ function buildRepeatGroupSchemaWithConditions(field, response) {
891
1114
  response
892
1115
  );
893
1116
  }
894
- let schema = import_zod2.z.array(import_zod2.z.object(rowShape));
1117
+ let schema = import_zod4.z.array(import_zod4.z.object(rowShape));
895
1118
  const hasStaticMin = typeof field.min === "number";
896
- const hasMinIf = Boolean(field.minIf);
897
- const minIfCondition = field.minIf;
898
- const shouldEnforceMin = hasStaticMin && (!hasMinIf || evaluateCondition(minIfCondition, response));
899
- if (shouldEnforceMin) {
900
- const minValue = field.min;
1119
+ const hasStaticMax = typeof field.max === "number";
1120
+ let effectiveMin = hasStaticMin ? field.min : void 0;
1121
+ let effectiveMax = hasStaticMax ? field.max : void 0;
1122
+ if (field.minIf) {
1123
+ const applyDynamicMin = evaluateCondition(field.minIf, response);
1124
+ if (applyDynamicMin && typeof field.min === "number") {
1125
+ effectiveMin = field.min;
1126
+ } else if (!applyDynamicMin) {
1127
+ effectiveMin = void 0;
1128
+ }
1129
+ }
1130
+ if (field.maxIf) {
1131
+ const applyDynamicMax = evaluateCondition(field.maxIf, response);
1132
+ if (applyDynamicMax && typeof field.max === "number") {
1133
+ effectiveMax = field.max;
1134
+ } else if (!applyDynamicMax) {
1135
+ effectiveMax = void 0;
1136
+ }
1137
+ }
1138
+ if (typeof effectiveMin === "number") {
901
1139
  schema = schema.min(
902
- minValue,
903
- `Add at least ${minValue} item(s).`
1140
+ effectiveMin,
1141
+ `Add at least ${effectiveMin} item(s).`
904
1142
  );
905
1143
  }
906
- const hasStaticMax = typeof field.max === "number";
907
- const hasMaxIf = Boolean(field.maxIf);
908
- const maxIfCondition = field.maxIf;
909
- const shouldEnforceMax = hasStaticMax && (!hasMaxIf || evaluateCondition(maxIfCondition, response));
910
- if (shouldEnforceMax) {
911
- const maxValue = field.max;
1144
+ if (typeof effectiveMax === "number") {
912
1145
  schema = schema.max(
913
- maxValue,
914
- `Add at most ${maxValue} item(s).`
1146
+ effectiveMax,
1147
+ `Add at most ${effectiveMax} item(s).`
915
1148
  );
916
1149
  }
917
- return isRequired ? schema : schema.optional();
1150
+ return schema;
918
1151
  }
919
1152
  function buildConditionalFieldSchema(field, response) {
920
1153
  if (field.visibleIf) {
921
1154
  const visible = evaluateCondition(field.visibleIf, response);
922
1155
  if (!visible) {
923
- return import_zod2.z.any().optional();
1156
+ return import_zod4.z.any().optional();
924
1157
  }
925
1158
  }
926
1159
  let schema;
927
1160
  if (field.type === "repeatGroup") {
928
1161
  schema = buildRepeatGroupSchemaWithConditions(field, response);
929
1162
  } else {
930
- schema = buildBaseFieldSchema(field);
1163
+ schema = buildBaseFieldSchema2(field);
931
1164
  }
932
1165
  if (field.requiredIf) {
933
1166
  const shouldBeRequired = evaluateCondition(field.requiredIf, response);
934
1167
  if (shouldBeRequired) {
935
- if (schema instanceof import_zod2.z.ZodOptional) {
1168
+ if (schema instanceof import_zod4.z.ZodOptional) {
936
1169
  schema = schema.unwrap();
937
1170
  }
938
1171
  }
@@ -946,18 +1179,12 @@ function buildResponseSchemaWithConditions(template, response) {
946
1179
  shape[field.id] = buildConditionalFieldSchema(field, response);
947
1180
  }
948
1181
  }
949
- return import_zod2.z.object(shape);
1182
+ return import_zod4.z.object(shape);
950
1183
  }
951
1184
  function buildResponseSchema(template) {
952
1185
  return buildResponseSchemaWithConditions(template, {});
953
1186
  }
954
- function validateReportResponse(template, data) {
955
- if (typeof data !== "object" || data === null) {
956
- throw new Error("Response must be an object");
957
- }
958
- const response = data;
959
- const schema = buildResponseSchemaWithConditions(template, response);
960
- const parsed = schema.parse(response);
1187
+ function stripInvisibleFields(template, response, parsed) {
961
1188
  for (const section of template.sections) {
962
1189
  for (const field of section.fields) {
963
1190
  if (field.visibleIf) {
@@ -968,76 +1195,38 @@ function validateReportResponse(template, data) {
968
1195
  }
969
1196
  }
970
1197
  }
971
- return parsed;
972
- }
973
- function mapZodIssueToFieldErrorCode(issue) {
974
- switch (issue.code) {
975
- case import_zod2.z.ZodIssueCode.invalid_type:
976
- return "field.invalid_type";
977
- case import_zod2.z.ZodIssueCode.too_small:
978
- return "field.too_small";
979
- case import_zod2.z.ZodIssueCode.too_big:
980
- return "field.too_big";
981
- case import_zod2.z.ZodIssueCode.invalid_enum_value:
982
- case import_zod2.z.ZodIssueCode.invalid_literal:
983
- return "field.invalid_option";
984
- case import_zod2.z.ZodIssueCode.custom:
985
- return "field.custom";
986
- default:
987
- return "field.custom";
988
- }
989
1198
  }
990
- function buildFieldMetadataLookup(template) {
991
- const map = /* @__PURE__ */ new Map();
1199
+ function stripComputedFields(template, parsed) {
992
1200
  for (const section of template.sections) {
993
1201
  for (const field of section.fields) {
994
- map.set(field.id, {
995
- sectionId: section.id,
996
- sectionTitle: section.title,
997
- label: field.label
998
- });
1202
+ if (field.computed) {
1203
+ delete parsed[field.id];
1204
+ }
1205
+ if (field.type === "repeatGroup" && Array.isArray(parsed[field.id])) {
1206
+ const rows = parsed[field.id];
1207
+ const nestedFields = Array.isArray(field.fields) ? field.fields : [];
1208
+ for (const row of rows) {
1209
+ if (!row || typeof row !== "object") continue;
1210
+ for (const nested of nestedFields) {
1211
+ if (nested.computed) {
1212
+ delete row[nested.id];
1213
+ }
1214
+ }
1215
+ }
1216
+ }
999
1217
  }
1000
1218
  }
1001
- return map;
1002
1219
  }
1003
- function mapZodIssuesToResponseErrors(template, issues) {
1004
- const fieldMeta = buildFieldMetadataLookup(template);
1005
- const errors = [];
1006
- for (const issue of issues) {
1007
- const path = issue.path ?? [];
1008
- const topLevelFieldId = path.find(
1009
- (p) => typeof p === "string"
1010
- );
1011
- const rowIndex = path.find(
1012
- (p) => typeof p === "number"
1013
- );
1014
- const nestedFieldId = path.length >= 3 && typeof path[path.length - 1] === "string" ? path[path.length - 1] : void 0;
1015
- const fieldId = topLevelFieldId ?? "";
1016
- const meta = fieldId ? fieldMeta.get(fieldId) : void 0;
1017
- const code = mapZodIssueToFieldErrorCode(issue);
1018
- const sectionLabel = meta?.sectionTitle ?? meta?.sectionId;
1019
- const fieldLabel = meta?.label ?? fieldId;
1020
- let message;
1021
- if (rowIndex != null && nestedFieldId && meta && sectionLabel) {
1022
- message = `Section "${sectionLabel}" \u2192 "${meta.label}" (row ${rowIndex + 1}, field "${nestedFieldId}"): ${issue.message}`;
1023
- } else if (meta && sectionLabel && fieldLabel) {
1024
- message = `Section "${sectionLabel}" \u2192 Field "${fieldLabel}": ${issue.message}`;
1025
- } else if (fieldLabel) {
1026
- message = `Field "${fieldLabel}": ${issue.message}`;
1027
- } else {
1028
- message = issue.message;
1029
- }
1030
- errors.push({
1031
- fieldId,
1032
- sectionId: meta?.sectionId,
1033
- sectionTitle: meta?.sectionTitle,
1034
- label: meta?.label,
1035
- code,
1036
- message,
1037
- rawIssue: issue
1038
- });
1220
+ function validateReportResponse(template, data) {
1221
+ if (typeof data !== "object" || data === null) {
1222
+ throw new Error("Response must be an object");
1039
1223
  }
1040
- return errors;
1224
+ const response = data;
1225
+ const schema = buildResponseSchemaWithConditions(template, response);
1226
+ const parsed = schema.parse(response);
1227
+ stripInvisibleFields(template, response, parsed);
1228
+ stripComputedFields(template, parsed);
1229
+ return parsed;
1041
1230
  }
1042
1231
  function validateReportResponseDetailed(template, data) {
1043
1232
  if (typeof data !== "object" || data === null) {
@@ -1057,27 +1246,13 @@ function validateReportResponseDetailed(template, data) {
1057
1246
  const result = schema.safeParse(response);
1058
1247
  if (result.success) {
1059
1248
  const parsed = { ...result.data };
1060
- for (const section of template.sections) {
1061
- for (const field of section.fields) {
1062
- if (field.visibleIf) {
1063
- const visible = evaluateCondition(field.visibleIf, response);
1064
- if (!visible) {
1065
- delete parsed[field.id];
1066
- }
1067
- }
1068
- }
1069
- }
1249
+ stripInvisibleFields(template, response, parsed);
1250
+ stripComputedFields(template, parsed);
1070
1251
  return { success: true, value: parsed };
1071
1252
  }
1072
1253
  const errors = mapZodIssuesToResponseErrors(template, result.error.issues);
1073
1254
  return { success: false, errors };
1074
1255
  }
1075
- function explainValidationError(template, error) {
1076
- if (error instanceof import_zod2.ZodError) {
1077
- return mapZodIssuesToResponseErrors(template, error.issues);
1078
- }
1079
- return null;
1080
- }
1081
1256
  // Annotate the CommonJS export names for ESM import in node:
1082
1257
  0 && (module.exports = {
1083
1258
  CORE_FIELD_DEFAULTS,