@frt-platform/report-core 1.0.1 → 1.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/README.md +218 -33
- package/dist/index.d.mts +222 -284
- package/dist/index.d.ts +222 -284
- package/dist/index.js +772 -47
- package/dist/index.mjs +761 -46
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21,18 +21,28 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
CORE_FIELD_DEFAULTS: () => CORE_FIELD_DEFAULTS,
|
|
24
|
+
Condition: () => Condition,
|
|
24
25
|
DEFAULT_FIELD_LABEL: () => DEFAULT_FIELD_LABEL,
|
|
26
|
+
FieldRegistry: () => FieldRegistry,
|
|
25
27
|
REPORT_TEMPLATE_FIELD_TYPES: () => REPORT_TEMPLATE_FIELD_TYPES,
|
|
26
28
|
REPORT_TEMPLATE_VERSION: () => REPORT_TEMPLATE_VERSION,
|
|
29
|
+
RepeatGroupFieldSchema: () => RepeatGroupFieldSchema,
|
|
27
30
|
ReportTemplateFieldSchema: () => ReportTemplateFieldSchema,
|
|
28
31
|
ReportTemplateSchemaValidator: () => ReportTemplateSchemaValidator,
|
|
29
32
|
ReportTemplateSectionSchema: () => ReportTemplateSectionSchema,
|
|
33
|
+
buildBaseFieldSchema: () => buildBaseFieldSchema,
|
|
34
|
+
buildResponseSchemaWithConditions: () => buildResponseSchemaWithConditions,
|
|
30
35
|
createUniqueId: () => createUniqueId,
|
|
36
|
+
diffTemplates: () => diffTemplates,
|
|
37
|
+
evaluateCondition: () => evaluateCondition,
|
|
38
|
+
exportJSONSchema: () => exportJSONSchema,
|
|
31
39
|
migrateLegacySchema: () => migrateLegacySchema,
|
|
32
40
|
normalizeReportTemplateSchema: () => normalizeReportTemplateSchema,
|
|
33
41
|
parseReportTemplateSchema: () => parseReportTemplateSchema,
|
|
34
42
|
parseReportTemplateSchemaFromString: () => parseReportTemplateSchemaFromString,
|
|
35
|
-
serializeReportTemplateSchema: () => serializeReportTemplateSchema
|
|
43
|
+
serializeReportTemplateSchema: () => serializeReportTemplateSchema,
|
|
44
|
+
validateReportResponse: () => validateReportResponse,
|
|
45
|
+
validateReportResponseDetailed: () => validateReportResponseDetailed
|
|
36
46
|
});
|
|
37
47
|
module.exports = __toCommonJS(index_exports);
|
|
38
48
|
|
|
@@ -46,16 +56,51 @@ var REPORT_TEMPLATE_FIELD_TYPES = [
|
|
|
46
56
|
"date",
|
|
47
57
|
"checkbox",
|
|
48
58
|
"singleSelect",
|
|
49
|
-
"multiSelect"
|
|
59
|
+
"multiSelect",
|
|
60
|
+
"repeatGroup"
|
|
61
|
+
// NEW FIELD TYPE
|
|
50
62
|
];
|
|
51
|
-
var
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
var Condition = import_zod.z.lazy(
|
|
64
|
+
() => import_zod.z.union([
|
|
65
|
+
// equals: { equals: { fieldId: value } }
|
|
66
|
+
import_zod.z.object({
|
|
67
|
+
equals: import_zod.z.record(import_zod.z.any())
|
|
68
|
+
}),
|
|
69
|
+
// any: { any: [cond, cond] }
|
|
70
|
+
import_zod.z.object({
|
|
71
|
+
any: import_zod.z.array(Condition)
|
|
72
|
+
}),
|
|
73
|
+
// all: { all: [cond, cond] }
|
|
74
|
+
import_zod.z.object({
|
|
75
|
+
all: import_zod.z.array(Condition)
|
|
76
|
+
}),
|
|
77
|
+
// not: { not: cond }
|
|
78
|
+
import_zod.z.object({
|
|
79
|
+
not: Condition
|
|
80
|
+
})
|
|
81
|
+
])
|
|
82
|
+
);
|
|
83
|
+
var BaseFieldSchema = import_zod.z.object({
|
|
84
|
+
id: import_zod.z.string().min(1, "Field identifiers cannot be empty.").max(60, "Field identifiers must be 60 characters or fewer.").regex(
|
|
85
|
+
/^[a-z0-9_-]+$/,
|
|
86
|
+
"Use lowercase letters, numbers, underscores, or dashes for field identifiers."
|
|
87
|
+
),
|
|
88
|
+
type: import_zod.z.enum([
|
|
89
|
+
"shortText",
|
|
90
|
+
"longText",
|
|
91
|
+
"number",
|
|
92
|
+
"date",
|
|
93
|
+
"checkbox",
|
|
94
|
+
"singleSelect",
|
|
95
|
+
"multiSelect"
|
|
96
|
+
]),
|
|
97
|
+
label: import_zod.z.string().min(1, "Field labels cannot be empty.").max(200, "Field labels must be 200 characters or fewer."),
|
|
55
98
|
required: import_zod.z.boolean().optional(),
|
|
56
|
-
description: import_zod.z.string().max(400).optional(),
|
|
57
|
-
placeholder: import_zod.z.string().max(200).optional(),
|
|
58
|
-
options: import_zod.z.array(
|
|
99
|
+
description: import_zod.z.string().max(400, "Descriptions must be 400 characters or fewer.").optional(),
|
|
100
|
+
placeholder: import_zod.z.string().max(200, "Placeholders must be 200 characters or fewer.").optional(),
|
|
101
|
+
options: import_zod.z.array(
|
|
102
|
+
import_zod.z.string().min(1, "Options cannot be empty.").max(120, "Options must be 120 characters or fewer.")
|
|
103
|
+
).optional(),
|
|
59
104
|
allowOther: import_zod.z.boolean().optional(),
|
|
60
105
|
defaultValue: import_zod.z.union([
|
|
61
106
|
import_zod.z.string(),
|
|
@@ -64,36 +109,90 @@ var ReportTemplateFieldSchema = import_zod.z.object({
|
|
|
64
109
|
import_zod.z.array(import_zod.z.string()),
|
|
65
110
|
import_zod.z.null()
|
|
66
111
|
]).optional(),
|
|
112
|
+
// Conditional logic
|
|
113
|
+
visibleIf: Condition.optional(),
|
|
114
|
+
requiredIf: Condition.optional(),
|
|
115
|
+
// Text constraints
|
|
67
116
|
minLength: import_zod.z.number().int().min(0).optional(),
|
|
68
117
|
maxLength: import_zod.z.number().int().min(0).optional(),
|
|
118
|
+
// Number constraints
|
|
69
119
|
minValue: import_zod.z.number().optional(),
|
|
70
120
|
maxValue: import_zod.z.number().optional(),
|
|
71
121
|
step: import_zod.z.number().nonnegative().optional(),
|
|
122
|
+
// Multi-select constraints
|
|
72
123
|
minSelections: import_zod.z.number().int().min(0).optional(),
|
|
73
124
|
maxSelections: import_zod.z.number().int().min(0).optional(),
|
|
125
|
+
// Privacy
|
|
74
126
|
dataClassification: import_zod.z.enum(["none", "personal", "special"]).optional()
|
|
75
127
|
});
|
|
128
|
+
var RepeatGroupFieldSchema = import_zod.z.lazy(
|
|
129
|
+
() => import_zod.z.object({
|
|
130
|
+
id: import_zod.z.string().min(1).max(60).regex(
|
|
131
|
+
/^[a-z0-9_-]+$/,
|
|
132
|
+
"Use lowercase letters, numbers, underscores, or dashes for field identifiers."
|
|
133
|
+
),
|
|
134
|
+
type: import_zod.z.literal("repeatGroup"),
|
|
135
|
+
label: import_zod.z.string().min(1).max(200),
|
|
136
|
+
// Conditional logic for the whole group
|
|
137
|
+
visibleIf: Condition.optional(),
|
|
138
|
+
requiredIf: Condition.optional(),
|
|
139
|
+
// NEW: Dynamic and static min/max
|
|
140
|
+
min: import_zod.z.number().int().min(0).optional(),
|
|
141
|
+
max: import_zod.z.number().int().min(1).optional(),
|
|
142
|
+
minIf: Condition.optional(),
|
|
143
|
+
maxIf: Condition.optional(),
|
|
144
|
+
// Contents: nested fields (recursive)
|
|
145
|
+
fields: import_zod.z.array(
|
|
146
|
+
import_zod.z.lazy(() => ReportTemplateFieldSchema)
|
|
147
|
+
).min(1, "Repeat groups must contain at least one field."),
|
|
148
|
+
// Pre-populated rows allowed
|
|
149
|
+
defaultValue: import_zod.z.array(import_zod.z.record(import_zod.z.any())).optional()
|
|
150
|
+
})
|
|
151
|
+
);
|
|
152
|
+
var ReportTemplateFieldSchema = import_zod.z.lazy(
|
|
153
|
+
() => import_zod.z.union([
|
|
154
|
+
BaseFieldSchema,
|
|
155
|
+
RepeatGroupFieldSchema
|
|
156
|
+
])
|
|
157
|
+
);
|
|
76
158
|
var ReportTemplateSectionSchema = import_zod.z.object({
|
|
77
|
-
id: import_zod.z.string().min(1).max(60).regex(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
159
|
+
id: import_zod.z.string().min(1, "Section identifiers cannot be empty.").max(60, "Section identifiers must be 60 characters or fewer.").regex(
|
|
160
|
+
/^[a-z0-9_-]+$/,
|
|
161
|
+
"Use lowercase letters, numbers, underscores, or dashes for section identifiers."
|
|
162
|
+
),
|
|
163
|
+
title: import_zod.z.string().max(200, "Section titles must be 200 characters or fewer.").optional(),
|
|
164
|
+
description: import_zod.z.string().max(500, "Section descriptions must be 500 characters or fewer.").optional(),
|
|
165
|
+
fields: import_zod.z.array(ReportTemplateFieldSchema).max(50, "Sections can include at most 50 fields.").default([])
|
|
81
166
|
});
|
|
82
167
|
var ReportTemplateSchemaValidator = import_zod.z.object({
|
|
83
|
-
version: import_zod.z.number().int().min(1).default(REPORT_TEMPLATE_VERSION),
|
|
84
|
-
title: import_zod.z.string().max(200).optional(),
|
|
85
|
-
description: import_zod.z.string().max(500).optional(),
|
|
86
|
-
sections: import_zod.z.array(ReportTemplateSectionSchema).max(25).default([])
|
|
168
|
+
version: import_zod.z.number().int().min(1).max(REPORT_TEMPLATE_VERSION).default(REPORT_TEMPLATE_VERSION),
|
|
169
|
+
title: import_zod.z.string().max(200, "Template titles must be 200 characters or fewer.").optional(),
|
|
170
|
+
description: import_zod.z.string().max(500, "Template descriptions must be 500 characters or fewer.").optional(),
|
|
171
|
+
sections: import_zod.z.array(ReportTemplateSectionSchema).max(25, "Templates can include at most 25 sections.").default([])
|
|
87
172
|
});
|
|
88
173
|
|
|
89
174
|
// src/fields.ts
|
|
90
175
|
var DEFAULT_FIELD_LABEL = "Untitled question";
|
|
91
176
|
var CORE_FIELD_DEFAULTS = {
|
|
92
|
-
shortText: {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
177
|
+
shortText: {
|
|
178
|
+
label: DEFAULT_FIELD_LABEL,
|
|
179
|
+
placeholder: "Short answer text"
|
|
180
|
+
},
|
|
181
|
+
longText: {
|
|
182
|
+
label: DEFAULT_FIELD_LABEL,
|
|
183
|
+
placeholder: "Long answer text"
|
|
184
|
+
},
|
|
185
|
+
number: {
|
|
186
|
+
label: DEFAULT_FIELD_LABEL,
|
|
187
|
+
placeholder: "123"
|
|
188
|
+
},
|
|
189
|
+
date: {
|
|
190
|
+
label: DEFAULT_FIELD_LABEL
|
|
191
|
+
},
|
|
192
|
+
checkbox: {
|
|
193
|
+
label: DEFAULT_FIELD_LABEL,
|
|
194
|
+
placeholder: "Check to confirm"
|
|
195
|
+
},
|
|
97
196
|
singleSelect: {
|
|
98
197
|
label: DEFAULT_FIELD_LABEL,
|
|
99
198
|
options: ["Option 1", "Option 2", "Option 3"]
|
|
@@ -102,6 +201,13 @@ var CORE_FIELD_DEFAULTS = {
|
|
|
102
201
|
label: DEFAULT_FIELD_LABEL,
|
|
103
202
|
options: ["Option 1", "Option 2", "Option 3"],
|
|
104
203
|
allowOther: false
|
|
204
|
+
},
|
|
205
|
+
repeatGroup: {
|
|
206
|
+
// Minimal safe defaults – your builder UI is expected
|
|
207
|
+
// to configure nested fields + min/max as needed.
|
|
208
|
+
label: DEFAULT_FIELD_LABEL,
|
|
209
|
+
fields: []
|
|
210
|
+
// nested fields go here in the UI layer
|
|
105
211
|
}
|
|
106
212
|
};
|
|
107
213
|
|
|
@@ -121,16 +227,20 @@ function createUniqueId(prefix, existing) {
|
|
|
121
227
|
var LEGACY_FIELD_TYPE_MAP = {
|
|
122
228
|
shorttext: "shortText",
|
|
123
229
|
text: "shortText",
|
|
230
|
+
"text-box": "shortText",
|
|
124
231
|
longtext: "longText",
|
|
125
232
|
textarea: "longText",
|
|
233
|
+
paragraph: "longText",
|
|
126
234
|
number: "number",
|
|
127
235
|
numeric: "number",
|
|
128
236
|
date: "date",
|
|
129
237
|
checkbox: "checkbox",
|
|
130
238
|
boolean: "checkbox",
|
|
239
|
+
singleselect: "singleSelect",
|
|
131
240
|
select: "singleSelect",
|
|
132
241
|
dropdown: "singleSelect",
|
|
133
242
|
radio: "singleSelect",
|
|
243
|
+
"multi-select": "multiSelect",
|
|
134
244
|
multiselect: "multiSelect",
|
|
135
245
|
checkboxes: "multiSelect"
|
|
136
246
|
};
|
|
@@ -168,63 +278,678 @@ function migrateLegacySchema(raw) {
|
|
|
168
278
|
}
|
|
169
279
|
|
|
170
280
|
// src/normalize.ts
|
|
171
|
-
function
|
|
172
|
-
|
|
173
|
-
const
|
|
174
|
-
return
|
|
281
|
+
function normalizeId(raw, fallback) {
|
|
282
|
+
if (!raw || typeof raw !== "string") return fallback;
|
|
283
|
+
const cleaned = raw.trim().toLowerCase().replace(/\s+/g, "_").replace(/[^a-z0-9_-]/g, "");
|
|
284
|
+
return cleaned.length > 0 ? cleaned : fallback;
|
|
285
|
+
}
|
|
286
|
+
function ensureUniqueId(id, used) {
|
|
287
|
+
let final = id;
|
|
288
|
+
let counter = 1;
|
|
289
|
+
while (used.has(final)) {
|
|
290
|
+
final = `${id}-${counter++}`;
|
|
291
|
+
}
|
|
292
|
+
used.add(final);
|
|
293
|
+
return final;
|
|
294
|
+
}
|
|
295
|
+
function normalizeField(field, usedFieldIds, index) {
|
|
296
|
+
const fallbackId = `field_${index + 1}`;
|
|
297
|
+
const normalizedId = normalizeId(field.id, fallbackId);
|
|
298
|
+
const uniqueId = ensureUniqueId(normalizedId, usedFieldIds);
|
|
299
|
+
const base = {
|
|
300
|
+
...field,
|
|
301
|
+
id: uniqueId
|
|
302
|
+
};
|
|
303
|
+
if (field.type === "repeatGroup") {
|
|
304
|
+
const nestedUsed = /* @__PURE__ */ new Set();
|
|
305
|
+
const nestedFields = Array.isArray(field.fields) ? field.fields : [];
|
|
306
|
+
base.fields = nestedFields.map(
|
|
307
|
+
(inner, idx) => normalizeField(inner, nestedUsed, idx)
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
return base;
|
|
311
|
+
}
|
|
312
|
+
function normalizeSection(section, sectionIndex) {
|
|
313
|
+
const fallbackId = `section_${sectionIndex + 1}`;
|
|
314
|
+
const normalizedId = normalizeId(section.id, fallbackId);
|
|
315
|
+
const base = {
|
|
316
|
+
...section,
|
|
317
|
+
id: normalizedId
|
|
318
|
+
};
|
|
319
|
+
const usedFieldIds = /* @__PURE__ */ new Set();
|
|
320
|
+
base.fields = (section.fields ?? []).map(
|
|
321
|
+
(field, idx) => normalizeField(field, usedFieldIds, idx)
|
|
322
|
+
);
|
|
323
|
+
return base;
|
|
175
324
|
}
|
|
176
325
|
function normalizeReportTemplateSchema(schema) {
|
|
177
326
|
const sections = schema.sections.length > 0 ? schema.sections : [
|
|
178
327
|
{
|
|
179
|
-
id: "
|
|
328
|
+
id: "section_1",
|
|
180
329
|
title: "",
|
|
181
330
|
description: "",
|
|
182
331
|
fields: []
|
|
183
332
|
}
|
|
184
333
|
];
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const safeId = seen.has(trimmed) ? `field-${idx + 1}` : trimmed;
|
|
191
|
-
seen.add(safeId);
|
|
192
|
-
return {
|
|
193
|
-
...field,
|
|
194
|
-
id: safeId
|
|
195
|
-
};
|
|
196
|
-
});
|
|
197
|
-
return {
|
|
198
|
-
...sec,
|
|
199
|
-
id: safeSectionId,
|
|
200
|
-
fields
|
|
201
|
-
};
|
|
334
|
+
const usedSectionIds = /* @__PURE__ */ new Set();
|
|
335
|
+
const normalizedSections = sections.map((section, idx) => {
|
|
336
|
+
const normalized = normalizeSection(section, idx);
|
|
337
|
+
normalized.id = ensureUniqueId(normalized.id, usedSectionIds);
|
|
338
|
+
return normalized;
|
|
202
339
|
});
|
|
203
340
|
return {
|
|
204
341
|
...schema,
|
|
205
342
|
sections: normalizedSections
|
|
206
343
|
};
|
|
207
344
|
}
|
|
345
|
+
function parseReportTemplateSchema(raw) {
|
|
346
|
+
const migrated = migrateLegacySchema(raw);
|
|
347
|
+
const parsed = ReportTemplateSchemaValidator.parse(migrated);
|
|
348
|
+
return normalizeReportTemplateSchema(parsed);
|
|
349
|
+
}
|
|
208
350
|
function parseReportTemplateSchemaFromString(raw) {
|
|
209
|
-
|
|
210
|
-
return parseReportTemplateSchema(obj);
|
|
351
|
+
return parseReportTemplateSchema(JSON.parse(raw));
|
|
211
352
|
}
|
|
212
353
|
function serializeReportTemplateSchema(schema) {
|
|
213
354
|
return JSON.stringify(schema, null, 2);
|
|
214
355
|
}
|
|
356
|
+
|
|
357
|
+
// src/responses.ts
|
|
358
|
+
var import_zod2 = require("zod");
|
|
359
|
+
|
|
360
|
+
// src/fieldRegistry.ts
|
|
361
|
+
var FieldRegistryClass = class {
|
|
362
|
+
constructor() {
|
|
363
|
+
this.registry = /* @__PURE__ */ new Map();
|
|
364
|
+
}
|
|
365
|
+
/** Register or override a field type. */
|
|
366
|
+
register(type, entry) {
|
|
367
|
+
this.registry.set(type, entry);
|
|
368
|
+
}
|
|
369
|
+
/** Get registry entry for a field type. */
|
|
370
|
+
get(type) {
|
|
371
|
+
return this.registry.get(type);
|
|
372
|
+
}
|
|
373
|
+
/** Check if field type is registered. */
|
|
374
|
+
has(type) {
|
|
375
|
+
return this.registry.has(type);
|
|
376
|
+
}
|
|
377
|
+
/** Return all field types currently registered. */
|
|
378
|
+
list() {
|
|
379
|
+
return [...this.registry.keys()];
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
var FieldRegistry = new FieldRegistryClass();
|
|
383
|
+
var CORE_TYPES = [
|
|
384
|
+
"shortText",
|
|
385
|
+
"longText",
|
|
386
|
+
"number",
|
|
387
|
+
"date",
|
|
388
|
+
"checkbox",
|
|
389
|
+
"singleSelect",
|
|
390
|
+
"multiSelect"
|
|
391
|
+
];
|
|
392
|
+
for (const type of CORE_TYPES) {
|
|
393
|
+
FieldRegistry.register(type, {
|
|
394
|
+
defaults: CORE_FIELD_DEFAULTS[type]
|
|
395
|
+
// Core types rely on existing validation logic.
|
|
396
|
+
// buildResponseSchema is optional — fallback handles it.
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// src/conditions.ts
|
|
401
|
+
function evaluateCondition(condition, response) {
|
|
402
|
+
if ("equals" in condition) {
|
|
403
|
+
return Object.entries(condition.equals).every(([key, val]) => {
|
|
404
|
+
return response[key] === val;
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
if ("any" in condition) {
|
|
408
|
+
return condition.any.some(
|
|
409
|
+
(sub) => evaluateCondition(sub, response)
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
if ("all" in condition) {
|
|
413
|
+
return condition.all.every(
|
|
414
|
+
(sub) => evaluateCondition(sub, response)
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
if ("not" in condition) {
|
|
418
|
+
const sub = condition.not;
|
|
419
|
+
return !evaluateCondition(sub, response);
|
|
420
|
+
}
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// src/responses.ts
|
|
425
|
+
function buildBaseFieldSchema(field) {
|
|
426
|
+
const isRequired = Boolean(field.required);
|
|
427
|
+
const registry = FieldRegistry.get(field.type);
|
|
428
|
+
if (registry?.buildResponseSchema) {
|
|
429
|
+
return registry.buildResponseSchema(field);
|
|
430
|
+
}
|
|
431
|
+
switch (field.type) {
|
|
432
|
+
case "shortText":
|
|
433
|
+
case "longText": {
|
|
434
|
+
let schema = import_zod2.z.string();
|
|
435
|
+
if (typeof field.minLength === "number") {
|
|
436
|
+
schema = schema.min(field.minLength);
|
|
437
|
+
}
|
|
438
|
+
if (typeof field.maxLength === "number") {
|
|
439
|
+
schema = schema.max(field.maxLength);
|
|
440
|
+
}
|
|
441
|
+
return isRequired ? schema : schema.optional();
|
|
442
|
+
}
|
|
443
|
+
case "number": {
|
|
444
|
+
let schema = import_zod2.z.number();
|
|
445
|
+
if (typeof field.minValue === "number") {
|
|
446
|
+
schema = schema.min(field.minValue);
|
|
447
|
+
}
|
|
448
|
+
if (typeof field.maxValue === "number") {
|
|
449
|
+
schema = schema.max(field.maxValue);
|
|
450
|
+
}
|
|
451
|
+
return isRequired ? schema : schema.optional();
|
|
452
|
+
}
|
|
453
|
+
case "date": {
|
|
454
|
+
let schema = import_zod2.z.string();
|
|
455
|
+
return isRequired ? schema : schema.optional();
|
|
456
|
+
}
|
|
457
|
+
case "checkbox": {
|
|
458
|
+
if (isRequired) return import_zod2.z.literal(true);
|
|
459
|
+
return import_zod2.z.boolean().optional();
|
|
460
|
+
}
|
|
461
|
+
case "singleSelect": {
|
|
462
|
+
let schema = import_zod2.z.string();
|
|
463
|
+
if (Array.isArray(field.options) && field.options.length > 0) {
|
|
464
|
+
const allowed = new Set(field.options);
|
|
465
|
+
schema = schema.refine(
|
|
466
|
+
(value) => allowed.has(value),
|
|
467
|
+
"Value must be one of the defined options."
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
return isRequired ? schema : schema.optional();
|
|
471
|
+
}
|
|
472
|
+
case "multiSelect": {
|
|
473
|
+
let schema = import_zod2.z.array(import_zod2.z.string());
|
|
474
|
+
if (Array.isArray(field.options) && field.options.length > 0 && !field.allowOther) {
|
|
475
|
+
const allowed = new Set(field.options);
|
|
476
|
+
schema = schema.refine(
|
|
477
|
+
(values) => values.every((value) => allowed.has(value)),
|
|
478
|
+
"All values must be among the defined options."
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
if (typeof field.minSelections === "number") {
|
|
482
|
+
schema = schema.min(
|
|
483
|
+
field.minSelections,
|
|
484
|
+
`Select at least ${field.minSelections} option(s).`
|
|
485
|
+
);
|
|
486
|
+
} else if (isRequired) {
|
|
487
|
+
schema = schema.min(
|
|
488
|
+
1,
|
|
489
|
+
"Select at least one option."
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
if (typeof field.maxSelections === "number") {
|
|
493
|
+
schema = schema.max(
|
|
494
|
+
field.maxSelections,
|
|
495
|
+
`Select at most ${field.maxSelections} option(s).`
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
return isRequired ? schema : schema.optional();
|
|
499
|
+
}
|
|
500
|
+
default: {
|
|
501
|
+
return import_zod2.z.any();
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
function buildConditionalFieldSchema(field, response) {
|
|
506
|
+
if (field.visibleIf) {
|
|
507
|
+
const visible = evaluateCondition(field.visibleIf, response);
|
|
508
|
+
if (!visible) {
|
|
509
|
+
return import_zod2.z.any().optional();
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
let schema = buildBaseFieldSchema(field);
|
|
513
|
+
if (field.requiredIf) {
|
|
514
|
+
const shouldBeRequired = evaluateCondition(field.requiredIf, response);
|
|
515
|
+
if (shouldBeRequired) {
|
|
516
|
+
if (schema instanceof import_zod2.z.ZodOptional) {
|
|
517
|
+
schema = schema.unwrap();
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return schema;
|
|
522
|
+
}
|
|
523
|
+
function buildResponseSchemaWithConditions(template, response) {
|
|
524
|
+
const shape = {};
|
|
525
|
+
for (const section of template.sections) {
|
|
526
|
+
for (const field of section.fields) {
|
|
527
|
+
shape[field.id] = buildConditionalFieldSchema(field, response);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return import_zod2.z.object(shape);
|
|
531
|
+
}
|
|
532
|
+
function validateReportResponse(template, data) {
|
|
533
|
+
if (typeof data !== "object" || data === null) {
|
|
534
|
+
throw new Error("Response must be an object");
|
|
535
|
+
}
|
|
536
|
+
const response = data;
|
|
537
|
+
const schema = buildResponseSchemaWithConditions(template, response);
|
|
538
|
+
const parsed = schema.parse(response);
|
|
539
|
+
for (const section of template.sections) {
|
|
540
|
+
for (const field of section.fields) {
|
|
541
|
+
if (field.visibleIf) {
|
|
542
|
+
const visible = evaluateCondition(field.visibleIf, response);
|
|
543
|
+
if (!visible) {
|
|
544
|
+
delete parsed[field.id];
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
return parsed;
|
|
550
|
+
}
|
|
551
|
+
function mapZodIssueToFieldErrorCode(issue) {
|
|
552
|
+
switch (issue.code) {
|
|
553
|
+
case import_zod2.z.ZodIssueCode.invalid_type:
|
|
554
|
+
return "field.invalid_type";
|
|
555
|
+
case import_zod2.z.ZodIssueCode.too_small:
|
|
556
|
+
return "field.too_small";
|
|
557
|
+
case import_zod2.z.ZodIssueCode.too_big:
|
|
558
|
+
return "field.too_big";
|
|
559
|
+
case import_zod2.z.ZodIssueCode.invalid_enum_value:
|
|
560
|
+
case import_zod2.z.ZodIssueCode.invalid_literal:
|
|
561
|
+
return "field.invalid_option";
|
|
562
|
+
case import_zod2.z.ZodIssueCode.custom:
|
|
563
|
+
return "field.custom";
|
|
564
|
+
default:
|
|
565
|
+
return "field.custom";
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
function buildFieldMetadataLookup(template) {
|
|
569
|
+
const map = /* @__PURE__ */ new Map();
|
|
570
|
+
for (const section of template.sections) {
|
|
571
|
+
for (const field of section.fields) {
|
|
572
|
+
map.set(field.id, {
|
|
573
|
+
sectionId: section.id,
|
|
574
|
+
sectionTitle: section.title,
|
|
575
|
+
label: field.label
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return map;
|
|
580
|
+
}
|
|
581
|
+
function validateReportResponseDetailed(template, data) {
|
|
582
|
+
if (typeof data !== "object" || data === null) {
|
|
583
|
+
return {
|
|
584
|
+
success: false,
|
|
585
|
+
errors: [
|
|
586
|
+
{
|
|
587
|
+
fieldId: "",
|
|
588
|
+
code: "response.invalid_root",
|
|
589
|
+
message: "Response must be an object."
|
|
590
|
+
}
|
|
591
|
+
]
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
const response = data;
|
|
595
|
+
const schema = buildResponseSchemaWithConditions(template, response);
|
|
596
|
+
const fieldMeta = buildFieldMetadataLookup(template);
|
|
597
|
+
const result = schema.safeParse(response);
|
|
598
|
+
if (result.success) {
|
|
599
|
+
const parsed = { ...result.data };
|
|
600
|
+
for (const section of template.sections) {
|
|
601
|
+
for (const field of section.fields) {
|
|
602
|
+
if (field.visibleIf) {
|
|
603
|
+
const visible = evaluateCondition(field.visibleIf, response);
|
|
604
|
+
if (!visible) {
|
|
605
|
+
delete parsed[field.id];
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
return { success: true, value: parsed };
|
|
611
|
+
}
|
|
612
|
+
const errors = [];
|
|
613
|
+
for (const issue of result.error.issues) {
|
|
614
|
+
const path = issue.path ?? [];
|
|
615
|
+
const fieldId = typeof path[0] === "string" ? path[0] : "";
|
|
616
|
+
const meta = fieldId ? fieldMeta.get(fieldId) : void 0;
|
|
617
|
+
const code = mapZodIssueToFieldErrorCode(issue);
|
|
618
|
+
const sectionLabel = meta?.sectionTitle ?? meta?.sectionId;
|
|
619
|
+
const fieldLabel = meta?.label ?? fieldId;
|
|
620
|
+
let message;
|
|
621
|
+
if (meta && sectionLabel && fieldLabel) {
|
|
622
|
+
message = `Section "${sectionLabel}" \u2192 Field "${fieldLabel}": ${issue.message}`;
|
|
623
|
+
} else if (fieldLabel) {
|
|
624
|
+
message = `Field "${fieldLabel}": ${issue.message}`;
|
|
625
|
+
} else {
|
|
626
|
+
message = issue.message;
|
|
627
|
+
}
|
|
628
|
+
errors.push({
|
|
629
|
+
fieldId,
|
|
630
|
+
sectionId: meta?.sectionId,
|
|
631
|
+
sectionTitle: meta?.sectionTitle,
|
|
632
|
+
label: meta?.label,
|
|
633
|
+
code,
|
|
634
|
+
message,
|
|
635
|
+
rawIssue: issue
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
return { success: false, errors };
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// src/diff.ts
|
|
642
|
+
function indexById(items) {
|
|
643
|
+
const map = {};
|
|
644
|
+
items.forEach((item, i) => map[item.id] = i);
|
|
645
|
+
return map;
|
|
646
|
+
}
|
|
647
|
+
function diffObjectProperties(before, after, ignoreKeys = []) {
|
|
648
|
+
const changes = [];
|
|
649
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)]);
|
|
650
|
+
for (const key of keys) {
|
|
651
|
+
if (ignoreKeys.includes(key)) continue;
|
|
652
|
+
const b = before[key];
|
|
653
|
+
const a = after[key];
|
|
654
|
+
const changed = Array.isArray(b) && Array.isArray(a) ? JSON.stringify(b) !== JSON.stringify(a) : b !== a;
|
|
655
|
+
if (changed) {
|
|
656
|
+
changes.push({ key, before: b, after: a });
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
return changes;
|
|
660
|
+
}
|
|
661
|
+
function diffRepeatGroupFields(beforeFields, afterFields, sectionId, groupId) {
|
|
662
|
+
const empty = {
|
|
663
|
+
addedSections: [],
|
|
664
|
+
removedSections: [],
|
|
665
|
+
reorderedSections: [],
|
|
666
|
+
modifiedSections: [],
|
|
667
|
+
addedFields: [],
|
|
668
|
+
removedFields: [],
|
|
669
|
+
reorderedFields: [],
|
|
670
|
+
modifiedFields: [],
|
|
671
|
+
nestedFieldDiffs: []
|
|
672
|
+
};
|
|
673
|
+
const beforeIndex = indexById(beforeFields);
|
|
674
|
+
const afterIndex = indexById(afterFields);
|
|
675
|
+
for (const f of afterFields) {
|
|
676
|
+
if (!(f.id in beforeIndex)) {
|
|
677
|
+
empty.addedFields.push({ sectionId, fieldId: f.id, index: afterIndex[f.id] });
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
for (const f of beforeFields) {
|
|
681
|
+
if (!(f.id in afterIndex)) {
|
|
682
|
+
empty.removedFields.push({ sectionId, fieldId: f.id, index: beforeIndex[f.id] });
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
for (const f of afterFields) {
|
|
686
|
+
if (f.id in beforeIndex) {
|
|
687
|
+
const from = beforeIndex[f.id];
|
|
688
|
+
const to = afterIndex[f.id];
|
|
689
|
+
if (from !== to) {
|
|
690
|
+
empty.reorderedFields.push({
|
|
691
|
+
sectionId,
|
|
692
|
+
fieldId: f.id,
|
|
693
|
+
from,
|
|
694
|
+
to
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
for (const f of afterFields) {
|
|
700
|
+
const idx = beforeIndex[f.id];
|
|
701
|
+
if (idx == null) continue;
|
|
702
|
+
const before = beforeFields[idx];
|
|
703
|
+
const changes = diffObjectProperties(before, f, ["id", "fields"]);
|
|
704
|
+
if (changes.length > 0) {
|
|
705
|
+
empty.modifiedFields.push({
|
|
706
|
+
sectionId,
|
|
707
|
+
fieldId: f.id,
|
|
708
|
+
before,
|
|
709
|
+
after: f,
|
|
710
|
+
changes
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
if (f.type === "repeatGroup" && Array.isArray(f.fields)) {
|
|
714
|
+
const innerBefore = before.fields ?? [];
|
|
715
|
+
const innerAfter = f.fields;
|
|
716
|
+
const nested = diffRepeatGroupFields(
|
|
717
|
+
innerBefore,
|
|
718
|
+
innerAfter,
|
|
719
|
+
sectionId,
|
|
720
|
+
f.id
|
|
721
|
+
);
|
|
722
|
+
if (nested.addedFields.length > 0 || nested.removedFields.length > 0 || nested.reorderedFields.length > 0 || nested.modifiedFields.length > 0) {
|
|
723
|
+
empty.nestedFieldDiffs.push({
|
|
724
|
+
sectionId,
|
|
725
|
+
groupId: f.id,
|
|
726
|
+
diffs: nested
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return empty;
|
|
732
|
+
}
|
|
733
|
+
function diffTemplates(before, after) {
|
|
734
|
+
const diff = {
|
|
735
|
+
addedSections: [],
|
|
736
|
+
removedSections: [],
|
|
737
|
+
reorderedSections: [],
|
|
738
|
+
modifiedSections: [],
|
|
739
|
+
addedFields: [],
|
|
740
|
+
removedFields: [],
|
|
741
|
+
reorderedFields: [],
|
|
742
|
+
modifiedFields: [],
|
|
743
|
+
nestedFieldDiffs: []
|
|
744
|
+
};
|
|
745
|
+
const beforeSections = before.sections;
|
|
746
|
+
const afterSections = after.sections;
|
|
747
|
+
const beforeSecIndex = indexById(beforeSections);
|
|
748
|
+
const afterSecIndex = indexById(afterSections);
|
|
749
|
+
for (const sec of afterSections) {
|
|
750
|
+
if (!(sec.id in beforeSecIndex)) {
|
|
751
|
+
diff.addedSections.push({
|
|
752
|
+
sectionId: sec.id,
|
|
753
|
+
index: afterSecIndex[sec.id]
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
for (const sec of beforeSections) {
|
|
758
|
+
if (!(sec.id in afterSecIndex)) {
|
|
759
|
+
diff.removedSections.push({
|
|
760
|
+
sectionId: sec.id,
|
|
761
|
+
index: beforeSecIndex[sec.id]
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
for (const sec of afterSections) {
|
|
766
|
+
if (sec.id in beforeSecIndex) {
|
|
767
|
+
const from = beforeSecIndex[sec.id];
|
|
768
|
+
const to = afterSecIndex[sec.id];
|
|
769
|
+
if (from !== to) {
|
|
770
|
+
diff.reorderedSections.push({ sectionId: sec.id, from, to });
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
for (const sec of afterSections) {
|
|
775
|
+
const idx = beforeSecIndex[sec.id];
|
|
776
|
+
if (idx == null) continue;
|
|
777
|
+
const beforeSec = beforeSections[idx];
|
|
778
|
+
const changes = diffObjectProperties(beforeSec, sec, ["id", "fields"]);
|
|
779
|
+
if (changes.length > 0) {
|
|
780
|
+
diff.modifiedSections.push({
|
|
781
|
+
sectionId: sec.id,
|
|
782
|
+
changes
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
for (const afterSec of afterSections) {
|
|
787
|
+
const secId = afterSec.id;
|
|
788
|
+
const beforeIdx = beforeSecIndex[secId];
|
|
789
|
+
if (beforeIdx == null) continue;
|
|
790
|
+
const beforeSec = beforeSections[beforeIdx];
|
|
791
|
+
const beforeFields = beforeSec.fields ?? [];
|
|
792
|
+
const afterFields = afterSec.fields ?? [];
|
|
793
|
+
const nestedDiff = diffRepeatGroupFields(
|
|
794
|
+
beforeFields,
|
|
795
|
+
afterFields,
|
|
796
|
+
secId,
|
|
797
|
+
""
|
|
798
|
+
);
|
|
799
|
+
diff.addedFields.push(...nestedDiff.addedFields);
|
|
800
|
+
diff.removedFields.push(...nestedDiff.removedFields);
|
|
801
|
+
diff.reorderedFields.push(...nestedDiff.reorderedFields);
|
|
802
|
+
diff.modifiedFields.push(...nestedDiff.modifiedFields);
|
|
803
|
+
diff.nestedFieldDiffs.push(...nestedDiff.nestedFieldDiffs);
|
|
804
|
+
}
|
|
805
|
+
return diff;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// src/jsonSchema.ts
|
|
809
|
+
function mapFieldTypeToJSONSchemaCore(field) {
|
|
810
|
+
const common = {
|
|
811
|
+
title: field.label,
|
|
812
|
+
description: field.description,
|
|
813
|
+
default: field.defaultValue ?? void 0,
|
|
814
|
+
"x-frt-placeholder": field.placeholder,
|
|
815
|
+
"x-frt-dataClassification": field.dataClassification,
|
|
816
|
+
"x-frt-visibleIf": field.visibleIf,
|
|
817
|
+
"x-frt-requiredIf": field.requiredIf
|
|
818
|
+
};
|
|
819
|
+
switch (field.type) {
|
|
820
|
+
case "shortText":
|
|
821
|
+
case "longText": {
|
|
822
|
+
const schema = {
|
|
823
|
+
...common,
|
|
824
|
+
type: "string"
|
|
825
|
+
};
|
|
826
|
+
if (typeof field.minLength === "number") {
|
|
827
|
+
schema.minLength = field.minLength;
|
|
828
|
+
}
|
|
829
|
+
if (typeof field.maxLength === "number") {
|
|
830
|
+
schema.maxLength = field.maxLength;
|
|
831
|
+
}
|
|
832
|
+
return schema;
|
|
833
|
+
}
|
|
834
|
+
case "number": {
|
|
835
|
+
const schema = {
|
|
836
|
+
...common,
|
|
837
|
+
type: "number"
|
|
838
|
+
};
|
|
839
|
+
if (typeof field.minValue === "number") {
|
|
840
|
+
schema.minimum = field.minValue;
|
|
841
|
+
}
|
|
842
|
+
if (typeof field.maxValue === "number") {
|
|
843
|
+
schema.maximum = field.maxValue;
|
|
844
|
+
}
|
|
845
|
+
return schema;
|
|
846
|
+
}
|
|
847
|
+
case "date": {
|
|
848
|
+
const schema = {
|
|
849
|
+
...common,
|
|
850
|
+
type: "string",
|
|
851
|
+
format: "date-time"
|
|
852
|
+
};
|
|
853
|
+
return schema;
|
|
854
|
+
}
|
|
855
|
+
case "checkbox": {
|
|
856
|
+
const schema = {
|
|
857
|
+
...common,
|
|
858
|
+
type: "boolean"
|
|
859
|
+
};
|
|
860
|
+
if (field.required && !field.requiredIf) {
|
|
861
|
+
schema.const = true;
|
|
862
|
+
}
|
|
863
|
+
return schema;
|
|
864
|
+
}
|
|
865
|
+
case "singleSelect": {
|
|
866
|
+
const schema = {
|
|
867
|
+
...common,
|
|
868
|
+
type: "string"
|
|
869
|
+
};
|
|
870
|
+
if (Array.isArray(field.options) && field.options.length > 0) {
|
|
871
|
+
schema.enum = field.options;
|
|
872
|
+
}
|
|
873
|
+
return schema;
|
|
874
|
+
}
|
|
875
|
+
case "multiSelect": {
|
|
876
|
+
const schema = {
|
|
877
|
+
...common,
|
|
878
|
+
type: "array",
|
|
879
|
+
items: {
|
|
880
|
+
type: "string"
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
const hasOptions = Array.isArray(field.options) && field.options.length > 0;
|
|
884
|
+
if (hasOptions && !field.allowOther) {
|
|
885
|
+
schema.items = {
|
|
886
|
+
type: "string",
|
|
887
|
+
enum: field.options
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
if (typeof field.minSelections === "number") {
|
|
891
|
+
schema.minItems = field.minSelections;
|
|
892
|
+
}
|
|
893
|
+
if (typeof field.maxSelections === "number") {
|
|
894
|
+
schema.maxItems = field.maxSelections;
|
|
895
|
+
}
|
|
896
|
+
return schema;
|
|
897
|
+
}
|
|
898
|
+
default: {
|
|
899
|
+
return {
|
|
900
|
+
...common
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
function exportJSONSchema(template) {
|
|
906
|
+
const properties = {};
|
|
907
|
+
const required = [];
|
|
908
|
+
for (const section of template.sections) {
|
|
909
|
+
for (const field of section.fields) {
|
|
910
|
+
properties[field.id] = mapFieldTypeToJSONSchemaCore(field);
|
|
911
|
+
if (field.required === true && !field.requiredIf && !field.visibleIf) {
|
|
912
|
+
required.push(field.id);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
const schema = {
|
|
917
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
918
|
+
type: "object",
|
|
919
|
+
properties,
|
|
920
|
+
additionalProperties: false,
|
|
921
|
+
"x-frt-templateTitle": template.title,
|
|
922
|
+
"x-frt-templateDescription": template.description,
|
|
923
|
+
"x-frt-version": template.version
|
|
924
|
+
};
|
|
925
|
+
if (required.length > 0) {
|
|
926
|
+
schema.required = required;
|
|
927
|
+
}
|
|
928
|
+
return schema;
|
|
929
|
+
}
|
|
215
930
|
// Annotate the CommonJS export names for ESM import in node:
|
|
216
931
|
0 && (module.exports = {
|
|
217
932
|
CORE_FIELD_DEFAULTS,
|
|
933
|
+
Condition,
|
|
218
934
|
DEFAULT_FIELD_LABEL,
|
|
935
|
+
FieldRegistry,
|
|
219
936
|
REPORT_TEMPLATE_FIELD_TYPES,
|
|
220
937
|
REPORT_TEMPLATE_VERSION,
|
|
938
|
+
RepeatGroupFieldSchema,
|
|
221
939
|
ReportTemplateFieldSchema,
|
|
222
940
|
ReportTemplateSchemaValidator,
|
|
223
941
|
ReportTemplateSectionSchema,
|
|
942
|
+
buildBaseFieldSchema,
|
|
943
|
+
buildResponseSchemaWithConditions,
|
|
224
944
|
createUniqueId,
|
|
945
|
+
diffTemplates,
|
|
946
|
+
evaluateCondition,
|
|
947
|
+
exportJSONSchema,
|
|
225
948
|
migrateLegacySchema,
|
|
226
949
|
normalizeReportTemplateSchema,
|
|
227
950
|
parseReportTemplateSchema,
|
|
228
951
|
parseReportTemplateSchemaFromString,
|
|
229
|
-
serializeReportTemplateSchema
|
|
952
|
+
serializeReportTemplateSchema,
|
|
953
|
+
validateReportResponse,
|
|
954
|
+
validateReportResponseDetailed
|
|
230
955
|
});
|