@frt-platform/report-core 1.3.0 โ 1.4.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 +95 -9
- package/dist/index.d.mts +23 -1
- package/dist/index.d.ts +23 -1
- package/dist/index.js +46 -1
- package/dist/index.mjs +45 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ This package is:
|
|
|
9
9
|
* ๐ **Safe by design (Zod-based)**
|
|
10
10
|
* ๐งฑ **The foundation of the FRT incident & reporting platform**
|
|
11
11
|
|
|
12
|
-
It contains **no React**, **no database code**, and **no styling**.
|
|
12
|
+
It contains **no React**, **no database code**, and **no styling**.
|
|
13
13
|
You can use it in **Node**, **Next.js**, **backend services**, or custom form engines.
|
|
14
14
|
|
|
15
15
|
---
|
|
@@ -49,6 +49,7 @@ You can use it in **Node**, **Next.js**, **backend services**, or custom form en
|
|
|
49
49
|
### ๐ฅ Validation Engine
|
|
50
50
|
|
|
51
51
|
* Build response schema dynamically with conditions
|
|
52
|
+
* DX helper: `buildResponseSchema(template)` (no response needed)
|
|
52
53
|
* Throwing API: `validateReportResponse()`
|
|
53
54
|
* Non-throwing API: `validateReportResponseDetailed()`
|
|
54
55
|
* Rich `ResponseFieldError` objects with:
|
|
@@ -63,7 +64,8 @@ You can use it in **Node**, **Next.js**, **backend services**, or custom form en
|
|
|
63
64
|
|
|
64
65
|
* Legacy format migration (`fields โ sections`)
|
|
65
66
|
* Automatic ID normalization & uniqueness enforcement
|
|
66
|
-
*
|
|
67
|
+
* Stable `"lowercase_with_underscores"`-style IDs
|
|
68
|
+
* Fallback IDs for missing values: `section_1`, `field_1`, etc.
|
|
67
69
|
|
|
68
70
|
### ๐ Schema Diffing
|
|
69
71
|
|
|
@@ -81,7 +83,7 @@ Detect:
|
|
|
81
83
|
|
|
82
84
|
* `x-frt-visibleIf`
|
|
83
85
|
* `x-frt-requiredIf`
|
|
84
|
-
*
|
|
86
|
+
* `x-frt-minIf` / `x-frt-maxIf` for repeatGroup
|
|
85
87
|
* placeholders
|
|
86
88
|
* Useful for OpenAPI, Postman, or other backend runtimes.
|
|
87
89
|
|
|
@@ -92,6 +94,7 @@ Extend the system at runtime:
|
|
|
92
94
|
* Add custom types (`richText`, `fileUpload`, etc.)
|
|
93
95
|
* Override validation logic
|
|
94
96
|
* Provide metadata for UI packages
|
|
97
|
+
* Unknown/unregistered field types safely fall back to `z.any()` so they never break the core engine.
|
|
95
98
|
|
|
96
99
|
### ๐งฌ Type Inference
|
|
97
100
|
|
|
@@ -99,7 +102,7 @@ Get a fully typed response type from a template:
|
|
|
99
102
|
|
|
100
103
|
```ts
|
|
101
104
|
type MyResponse = InferResponse<typeof template>;
|
|
102
|
-
|
|
105
|
+
````
|
|
103
106
|
|
|
104
107
|
### ๐งพ Serialization Helpers
|
|
105
108
|
|
|
@@ -127,6 +130,63 @@ npm install @frt-platform/report-core zod
|
|
|
127
130
|
|
|
128
131
|
---
|
|
129
132
|
|
|
133
|
+
# ๐งน Parsing & Normalization
|
|
134
|
+
|
|
135
|
+
The core exposes helpers for safely **parsing**, **migrating**, and **normalizing** templates.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import {
|
|
139
|
+
parseReportTemplateSchema,
|
|
140
|
+
parseReportTemplateSchemaFromString,
|
|
141
|
+
} from "@frt-platform/report-core";
|
|
142
|
+
|
|
143
|
+
// From a raw JS object (e.g. loaded from DB, file, etc.)
|
|
144
|
+
const template = parseReportTemplateSchema(rawTemplateObject);
|
|
145
|
+
|
|
146
|
+
// From a JSON string
|
|
147
|
+
const templateFromString = parseReportTemplateSchemaFromString(jsonString);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Under the hood, `parseReportTemplateSchema` does:
|
|
151
|
+
|
|
152
|
+
1. **Legacy migration**
|
|
153
|
+
|
|
154
|
+
* Accepts both the new `{ sections: [...] }` format and legacy flat:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
{
|
|
158
|
+
version: 1,
|
|
159
|
+
fields: [ /* ... */ ]
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
* Legacy `fields` are automatically wrapped into a single `section_1`.
|
|
164
|
+
|
|
165
|
+
2. **Schema validation**
|
|
166
|
+
|
|
167
|
+
* Uses a Zod validator for `ReportTemplateSchema` to ensure the template is structurally valid.
|
|
168
|
+
|
|
169
|
+
3. **Normalization**
|
|
170
|
+
|
|
171
|
+
* Section and field IDs are normalized to **lowercase** with spaces โ underscores:
|
|
172
|
+
|
|
173
|
+
* `"My Field!"` โ `"my_field"`
|
|
174
|
+
|
|
175
|
+
* Invalid characters are stripped (only `[a-z0-9_-]` are kept).
|
|
176
|
+
|
|
177
|
+
* IDs are made **unique per namespace** by appending `-1`, `-2`, โฆ as needed:
|
|
178
|
+
|
|
179
|
+
* `my_field`, `my_field-1`, `my_field-2`, โฆ
|
|
180
|
+
|
|
181
|
+
* Missing IDs get deterministic fallbacks:
|
|
182
|
+
|
|
183
|
+
* Sections: `section_1`, `section_2`, โฆ
|
|
184
|
+
* Fields: `field_1`, `field_2`, โฆ
|
|
185
|
+
|
|
186
|
+
This makes templates safe to store, diff, and round-trip in a stable way.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
130
190
|
# ๐ Quickstart
|
|
131
191
|
|
|
132
192
|
## 1. Define a template
|
|
@@ -209,7 +269,8 @@ Produces:
|
|
|
209
269
|
sectionTitle: "General Info",
|
|
210
270
|
label: "Incident title",
|
|
211
271
|
code: "field.too_small",
|
|
212
|
-
message:
|
|
272
|
+
message:
|
|
273
|
+
'Section "General Info" โ Field "Incident title": String must contain at least 1 character(s).'
|
|
213
274
|
}
|
|
214
275
|
]
|
|
215
276
|
```
|
|
@@ -257,6 +318,29 @@ Response shape:
|
|
|
257
318
|
injured: Array<{ name: string; injury?: string }>;
|
|
258
319
|
```
|
|
259
320
|
|
|
321
|
+
### ๐ repeatGroup behavior & limitations
|
|
322
|
+
|
|
323
|
+
* **Base constraints**
|
|
324
|
+
|
|
325
|
+
* `min` / `max` are always enforced on the row array.
|
|
326
|
+
* Each row is an object keyed by nested field IDs.
|
|
327
|
+
|
|
328
|
+
* **Conditional `minIf` / `maxIf`**
|
|
329
|
+
|
|
330
|
+
* If `minIf` / `maxIf` are present, they are evaluated against the **current response**.
|
|
331
|
+
* When the condition is `true`, the conditional value overrides the static `min` / `max` for that validation pass.
|
|
332
|
+
* When the condition is `false`, the engine falls back to the static `min` / `max` (if any).
|
|
333
|
+
|
|
334
|
+
* **Conditional logic inside rows**
|
|
335
|
+
|
|
336
|
+
* Nested fields in a repeatGroup support the same `visibleIf` / `requiredIf` semantics as top-level fields.
|
|
337
|
+
* Hidden nested fields are treated as **optional** and are **stripped** from the parsed response, just like hidden top-level fields.
|
|
338
|
+
* For now, row-level conditions see the **full response object**, not just the row. This matches top-level behavior and keeps the logic model simple.
|
|
339
|
+
|
|
340
|
+
* **JSON Schema export**
|
|
341
|
+
|
|
342
|
+
* repeatGroup constraints and conditions are exported via `x-frt-*` vendor extensions (e.g. `x-frt-minIf`, `x-frt-maxIf`, `x-frt-visibleIf`, `x-frt-requiredIf`), so you can mirror this behavior in other runtimes.
|
|
343
|
+
|
|
260
344
|
---
|
|
261
345
|
|
|
262
346
|
# ๐งฉ Field Registry (Custom Types)
|
|
@@ -281,6 +365,8 @@ Now templates may include fields like:
|
|
|
281
365
|
{ id: "body", type: "richText", label: "Report body" }
|
|
282
366
|
```
|
|
283
367
|
|
|
368
|
+
If a template uses a field type that is **not** registered and not one of the built-in core types, the engine safely falls back to `z.any()` so unknown types never crash validation.
|
|
369
|
+
|
|
284
370
|
---
|
|
285
371
|
|
|
286
372
|
# ๐งพ JSON Schema Export
|
|
@@ -372,7 +458,7 @@ const json = serializeReportTemplateSchema(template, {
|
|
|
372
458
|
});
|
|
373
459
|
```
|
|
374
460
|
|
|
375
|
-
Useful for deterministic output in Git.
|
|
461
|
+
Useful for deterministic output in Git and stable diffs across environments.
|
|
376
462
|
|
|
377
463
|
---
|
|
378
464
|
|
|
@@ -386,11 +472,11 @@ Useful for deterministic output in Git.
|
|
|
386
472
|
* Field Registry
|
|
387
473
|
* Error helpers
|
|
388
474
|
* Serialization features
|
|
475
|
+
* Parsing & normalization helpers
|
|
389
476
|
|
|
390
477
|
### Phase 2 โ Advanced Field System (IN PROGRESS)
|
|
391
478
|
|
|
392
|
-
*
|
|
393
|
-
* Conditional logic inside group rows
|
|
479
|
+
* Richer repeatGroup UX
|
|
394
480
|
* Computed fields (design)
|
|
395
481
|
* RichText / FileUpload via registry
|
|
396
482
|
|
|
@@ -411,4 +497,4 @@ Useful for deterministic output in Git.
|
|
|
411
497
|
|
|
412
498
|
# ๐ License
|
|
413
499
|
|
|
414
|
-
MIT โ feel free to use, extend, or contribute.
|
|
500
|
+
MIT โ feel free to use, extend, or contribute.
|
package/dist/index.d.mts
CHANGED
|
@@ -195,6 +195,13 @@ declare function exportJSONSchema(template: ReportTemplateSchema): JSONSchema;
|
|
|
195
195
|
declare const DEFAULT_FIELD_LABEL = "Untitled question";
|
|
196
196
|
declare const CORE_FIELD_DEFAULTS: Record<ReportTemplateFieldType, Record<string, unknown>>;
|
|
197
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Helper for generating a unique id in a *local* namespace, typically in
|
|
200
|
+
* UI/builders when adding new sections/fields client-side.
|
|
201
|
+
*
|
|
202
|
+
* Note: Core template normalization uses its own normalizeId/ensureUniqueId
|
|
203
|
+
* logic so that persisted IDs stay stable and backwards compatible.
|
|
204
|
+
*/
|
|
198
205
|
declare function createUniqueId(prefix: string, existing: Iterable<string>): string;
|
|
199
206
|
|
|
200
207
|
/** -------------------------------------------------------------
|
|
@@ -248,6 +255,21 @@ declare function evaluateCondition(condition: SimpleCondition, response: Record<
|
|
|
248
255
|
|
|
249
256
|
declare function buildBaseFieldSchema(field: ReportTemplateField): ZodTypeAny;
|
|
250
257
|
declare function buildResponseSchemaWithConditions(template: ReportTemplateSchema, response: Record<string, any>): z.ZodObject<Record<string, ZodTypeAny>>;
|
|
258
|
+
/**
|
|
259
|
+
* Build a Zod object schema for a template *without* needing a response object.
|
|
260
|
+
*
|
|
261
|
+
* Notes:
|
|
262
|
+
* - All conditional logic is evaluated against an empty response `{}`.
|
|
263
|
+
* - This means:
|
|
264
|
+
* - `visibleIf` / `requiredIf` that depend on other fields will typically
|
|
265
|
+
* evaluate to false.
|
|
266
|
+
*
|
|
267
|
+
* This is mainly useful for:
|
|
268
|
+
* - Static type inference
|
|
269
|
+
* - Generic tooling / OpenAPI style usage
|
|
270
|
+
* - Situations where you just want the overall "shape" of a response.
|
|
271
|
+
*/
|
|
272
|
+
declare function buildResponseSchema(template: ReportTemplateSchema): z.ZodObject<Record<string, ZodTypeAny>>;
|
|
251
273
|
declare function validateReportResponse(template: ReportTemplateSchema, data: unknown): Record<string, unknown>;
|
|
252
274
|
type FieldErrorCode = "response.invalid_root" | "field.required" | "field.invalid_type" | "field.too_small" | "field.too_big" | "field.invalid_option" | "field.custom";
|
|
253
275
|
interface ResponseFieldError {
|
|
@@ -317,4 +339,4 @@ type InferResponse<TTemplate extends {
|
|
|
317
339
|
[F in TemplateFieldUnion<TTemplate> as F["id"]]: FieldResponseValue<F>;
|
|
318
340
|
};
|
|
319
341
|
|
|
320
|
-
export { CORE_FIELD_DEFAULTS, Condition, DEFAULT_FIELD_LABEL, type FieldErrorCode, FieldRegistry, type FieldRegistryEntry, type InferResponse, type JSONSchema, REPORT_TEMPLATE_FIELD_TYPES, REPORT_TEMPLATE_VERSION, RepeatGroupFieldSchema, type ReportTemplateField, ReportTemplateFieldSchema, type ReportTemplateFieldType, type ReportTemplateSchema, ReportTemplateSchemaValidator, type ReportTemplateSection, ReportTemplateSectionSchema, type ResponseFieldError, type SerializeOptions, type SimpleCondition, type TemplateDiff, buildBaseFieldSchema, buildResponseSchemaWithConditions, createUniqueId, diffTemplates, evaluateCondition, explainValidationError, exportJSONSchema, migrateLegacySchema, normalizeReportTemplateSchema, parseReportTemplateSchema, parseReportTemplateSchemaFromString, serializeReportTemplateSchema, validateReportResponse, validateReportResponseDetailed };
|
|
342
|
+
export { CORE_FIELD_DEFAULTS, Condition, DEFAULT_FIELD_LABEL, type FieldErrorCode, FieldRegistry, type FieldRegistryEntry, type InferResponse, type JSONSchema, REPORT_TEMPLATE_FIELD_TYPES, REPORT_TEMPLATE_VERSION, RepeatGroupFieldSchema, type ReportTemplateField, ReportTemplateFieldSchema, type ReportTemplateFieldType, type ReportTemplateSchema, ReportTemplateSchemaValidator, type ReportTemplateSection, ReportTemplateSectionSchema, type ResponseFieldError, type SerializeOptions, type SimpleCondition, type TemplateDiff, buildBaseFieldSchema, buildResponseSchema, buildResponseSchemaWithConditions, createUniqueId, diffTemplates, evaluateCondition, explainValidationError, exportJSONSchema, migrateLegacySchema, normalizeReportTemplateSchema, parseReportTemplateSchema, parseReportTemplateSchemaFromString, serializeReportTemplateSchema, validateReportResponse, validateReportResponseDetailed };
|
package/dist/index.d.ts
CHANGED
|
@@ -195,6 +195,13 @@ declare function exportJSONSchema(template: ReportTemplateSchema): JSONSchema;
|
|
|
195
195
|
declare const DEFAULT_FIELD_LABEL = "Untitled question";
|
|
196
196
|
declare const CORE_FIELD_DEFAULTS: Record<ReportTemplateFieldType, Record<string, unknown>>;
|
|
197
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Helper for generating a unique id in a *local* namespace, typically in
|
|
200
|
+
* UI/builders when adding new sections/fields client-side.
|
|
201
|
+
*
|
|
202
|
+
* Note: Core template normalization uses its own normalizeId/ensureUniqueId
|
|
203
|
+
* logic so that persisted IDs stay stable and backwards compatible.
|
|
204
|
+
*/
|
|
198
205
|
declare function createUniqueId(prefix: string, existing: Iterable<string>): string;
|
|
199
206
|
|
|
200
207
|
/** -------------------------------------------------------------
|
|
@@ -248,6 +255,21 @@ declare function evaluateCondition(condition: SimpleCondition, response: Record<
|
|
|
248
255
|
|
|
249
256
|
declare function buildBaseFieldSchema(field: ReportTemplateField): ZodTypeAny;
|
|
250
257
|
declare function buildResponseSchemaWithConditions(template: ReportTemplateSchema, response: Record<string, any>): z.ZodObject<Record<string, ZodTypeAny>>;
|
|
258
|
+
/**
|
|
259
|
+
* Build a Zod object schema for a template *without* needing a response object.
|
|
260
|
+
*
|
|
261
|
+
* Notes:
|
|
262
|
+
* - All conditional logic is evaluated against an empty response `{}`.
|
|
263
|
+
* - This means:
|
|
264
|
+
* - `visibleIf` / `requiredIf` that depend on other fields will typically
|
|
265
|
+
* evaluate to false.
|
|
266
|
+
*
|
|
267
|
+
* This is mainly useful for:
|
|
268
|
+
* - Static type inference
|
|
269
|
+
* - Generic tooling / OpenAPI style usage
|
|
270
|
+
* - Situations where you just want the overall "shape" of a response.
|
|
271
|
+
*/
|
|
272
|
+
declare function buildResponseSchema(template: ReportTemplateSchema): z.ZodObject<Record<string, ZodTypeAny>>;
|
|
251
273
|
declare function validateReportResponse(template: ReportTemplateSchema, data: unknown): Record<string, unknown>;
|
|
252
274
|
type FieldErrorCode = "response.invalid_root" | "field.required" | "field.invalid_type" | "field.too_small" | "field.too_big" | "field.invalid_option" | "field.custom";
|
|
253
275
|
interface ResponseFieldError {
|
|
@@ -317,4 +339,4 @@ type InferResponse<TTemplate extends {
|
|
|
317
339
|
[F in TemplateFieldUnion<TTemplate> as F["id"]]: FieldResponseValue<F>;
|
|
318
340
|
};
|
|
319
341
|
|
|
320
|
-
export { CORE_FIELD_DEFAULTS, Condition, DEFAULT_FIELD_LABEL, type FieldErrorCode, FieldRegistry, type FieldRegistryEntry, type InferResponse, type JSONSchema, REPORT_TEMPLATE_FIELD_TYPES, REPORT_TEMPLATE_VERSION, RepeatGroupFieldSchema, type ReportTemplateField, ReportTemplateFieldSchema, type ReportTemplateFieldType, type ReportTemplateSchema, ReportTemplateSchemaValidator, type ReportTemplateSection, ReportTemplateSectionSchema, type ResponseFieldError, type SerializeOptions, type SimpleCondition, type TemplateDiff, buildBaseFieldSchema, buildResponseSchemaWithConditions, createUniqueId, diffTemplates, evaluateCondition, explainValidationError, exportJSONSchema, migrateLegacySchema, normalizeReportTemplateSchema, parseReportTemplateSchema, parseReportTemplateSchemaFromString, serializeReportTemplateSchema, validateReportResponse, validateReportResponseDetailed };
|
|
342
|
+
export { CORE_FIELD_DEFAULTS, Condition, DEFAULT_FIELD_LABEL, type FieldErrorCode, FieldRegistry, type FieldRegistryEntry, type InferResponse, type JSONSchema, REPORT_TEMPLATE_FIELD_TYPES, REPORT_TEMPLATE_VERSION, RepeatGroupFieldSchema, type ReportTemplateField, ReportTemplateFieldSchema, type ReportTemplateFieldType, type ReportTemplateSchema, ReportTemplateSchemaValidator, type ReportTemplateSection, ReportTemplateSectionSchema, type ResponseFieldError, type SerializeOptions, type SimpleCondition, type TemplateDiff, buildBaseFieldSchema, buildResponseSchema, buildResponseSchemaWithConditions, createUniqueId, diffTemplates, evaluateCondition, explainValidationError, exportJSONSchema, migrateLegacySchema, normalizeReportTemplateSchema, parseReportTemplateSchema, parseReportTemplateSchemaFromString, serializeReportTemplateSchema, validateReportResponse, validateReportResponseDetailed };
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ __export(index_exports, {
|
|
|
31
31
|
ReportTemplateSchemaValidator: () => ReportTemplateSchemaValidator,
|
|
32
32
|
ReportTemplateSectionSchema: () => ReportTemplateSectionSchema,
|
|
33
33
|
buildBaseFieldSchema: () => buildBaseFieldSchema,
|
|
34
|
+
buildResponseSchema: () => buildResponseSchema,
|
|
34
35
|
buildResponseSchemaWithConditions: () => buildResponseSchemaWithConditions,
|
|
35
36
|
createUniqueId: () => createUniqueId,
|
|
36
37
|
diffTemplates: () => diffTemplates,
|
|
@@ -880,6 +881,41 @@ function buildBaseFieldSchema(field) {
|
|
|
880
881
|
}
|
|
881
882
|
}
|
|
882
883
|
}
|
|
884
|
+
function buildRepeatGroupSchemaWithConditions(field, response) {
|
|
885
|
+
const isRequired = Boolean(field.required);
|
|
886
|
+
const nestedFields = Array.isArray(field.fields) ? field.fields : [];
|
|
887
|
+
const rowShape = {};
|
|
888
|
+
for (const nested of nestedFields) {
|
|
889
|
+
rowShape[nested.id] = buildConditionalFieldSchema(
|
|
890
|
+
nested,
|
|
891
|
+
response
|
|
892
|
+
);
|
|
893
|
+
}
|
|
894
|
+
let schema = import_zod2.z.array(import_zod2.z.object(rowShape));
|
|
895
|
+
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;
|
|
901
|
+
schema = schema.min(
|
|
902
|
+
minValue,
|
|
903
|
+
`Add at least ${minValue} item(s).`
|
|
904
|
+
);
|
|
905
|
+
}
|
|
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;
|
|
912
|
+
schema = schema.max(
|
|
913
|
+
maxValue,
|
|
914
|
+
`Add at most ${maxValue} item(s).`
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
return isRequired ? schema : schema.optional();
|
|
918
|
+
}
|
|
883
919
|
function buildConditionalFieldSchema(field, response) {
|
|
884
920
|
if (field.visibleIf) {
|
|
885
921
|
const visible = evaluateCondition(field.visibleIf, response);
|
|
@@ -887,7 +923,12 @@ function buildConditionalFieldSchema(field, response) {
|
|
|
887
923
|
return import_zod2.z.any().optional();
|
|
888
924
|
}
|
|
889
925
|
}
|
|
890
|
-
let schema
|
|
926
|
+
let schema;
|
|
927
|
+
if (field.type === "repeatGroup") {
|
|
928
|
+
schema = buildRepeatGroupSchemaWithConditions(field, response);
|
|
929
|
+
} else {
|
|
930
|
+
schema = buildBaseFieldSchema(field);
|
|
931
|
+
}
|
|
891
932
|
if (field.requiredIf) {
|
|
892
933
|
const shouldBeRequired = evaluateCondition(field.requiredIf, response);
|
|
893
934
|
if (shouldBeRequired) {
|
|
@@ -907,6 +948,9 @@ function buildResponseSchemaWithConditions(template, response) {
|
|
|
907
948
|
}
|
|
908
949
|
return import_zod2.z.object(shape);
|
|
909
950
|
}
|
|
951
|
+
function buildResponseSchema(template) {
|
|
952
|
+
return buildResponseSchemaWithConditions(template, {});
|
|
953
|
+
}
|
|
910
954
|
function validateReportResponse(template, data) {
|
|
911
955
|
if (typeof data !== "object" || data === null) {
|
|
912
956
|
throw new Error("Response must be an object");
|
|
@@ -1047,6 +1091,7 @@ function explainValidationError(template, error) {
|
|
|
1047
1091
|
ReportTemplateSchemaValidator,
|
|
1048
1092
|
ReportTemplateSectionSchema,
|
|
1049
1093
|
buildBaseFieldSchema,
|
|
1094
|
+
buildResponseSchema,
|
|
1050
1095
|
buildResponseSchemaWithConditions,
|
|
1051
1096
|
createUniqueId,
|
|
1052
1097
|
diffTemplates,
|
package/dist/index.mjs
CHANGED
|
@@ -834,6 +834,41 @@ function buildBaseFieldSchema(field) {
|
|
|
834
834
|
}
|
|
835
835
|
}
|
|
836
836
|
}
|
|
837
|
+
function buildRepeatGroupSchemaWithConditions(field, response) {
|
|
838
|
+
const isRequired = Boolean(field.required);
|
|
839
|
+
const nestedFields = Array.isArray(field.fields) ? field.fields : [];
|
|
840
|
+
const rowShape = {};
|
|
841
|
+
for (const nested of nestedFields) {
|
|
842
|
+
rowShape[nested.id] = buildConditionalFieldSchema(
|
|
843
|
+
nested,
|
|
844
|
+
response
|
|
845
|
+
);
|
|
846
|
+
}
|
|
847
|
+
let schema = z2.array(z2.object(rowShape));
|
|
848
|
+
const hasStaticMin = typeof field.min === "number";
|
|
849
|
+
const hasMinIf = Boolean(field.minIf);
|
|
850
|
+
const minIfCondition = field.minIf;
|
|
851
|
+
const shouldEnforceMin = hasStaticMin && (!hasMinIf || evaluateCondition(minIfCondition, response));
|
|
852
|
+
if (shouldEnforceMin) {
|
|
853
|
+
const minValue = field.min;
|
|
854
|
+
schema = schema.min(
|
|
855
|
+
minValue,
|
|
856
|
+
`Add at least ${minValue} item(s).`
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
const hasStaticMax = typeof field.max === "number";
|
|
860
|
+
const hasMaxIf = Boolean(field.maxIf);
|
|
861
|
+
const maxIfCondition = field.maxIf;
|
|
862
|
+
const shouldEnforceMax = hasStaticMax && (!hasMaxIf || evaluateCondition(maxIfCondition, response));
|
|
863
|
+
if (shouldEnforceMax) {
|
|
864
|
+
const maxValue = field.max;
|
|
865
|
+
schema = schema.max(
|
|
866
|
+
maxValue,
|
|
867
|
+
`Add at most ${maxValue} item(s).`
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
return isRequired ? schema : schema.optional();
|
|
871
|
+
}
|
|
837
872
|
function buildConditionalFieldSchema(field, response) {
|
|
838
873
|
if (field.visibleIf) {
|
|
839
874
|
const visible = evaluateCondition(field.visibleIf, response);
|
|
@@ -841,7 +876,12 @@ function buildConditionalFieldSchema(field, response) {
|
|
|
841
876
|
return z2.any().optional();
|
|
842
877
|
}
|
|
843
878
|
}
|
|
844
|
-
let schema
|
|
879
|
+
let schema;
|
|
880
|
+
if (field.type === "repeatGroup") {
|
|
881
|
+
schema = buildRepeatGroupSchemaWithConditions(field, response);
|
|
882
|
+
} else {
|
|
883
|
+
schema = buildBaseFieldSchema(field);
|
|
884
|
+
}
|
|
845
885
|
if (field.requiredIf) {
|
|
846
886
|
const shouldBeRequired = evaluateCondition(field.requiredIf, response);
|
|
847
887
|
if (shouldBeRequired) {
|
|
@@ -861,6 +901,9 @@ function buildResponseSchemaWithConditions(template, response) {
|
|
|
861
901
|
}
|
|
862
902
|
return z2.object(shape);
|
|
863
903
|
}
|
|
904
|
+
function buildResponseSchema(template) {
|
|
905
|
+
return buildResponseSchemaWithConditions(template, {});
|
|
906
|
+
}
|
|
864
907
|
function validateReportResponse(template, data) {
|
|
865
908
|
if (typeof data !== "object" || data === null) {
|
|
866
909
|
throw new Error("Response must be an object");
|
|
@@ -1000,6 +1043,7 @@ export {
|
|
|
1000
1043
|
ReportTemplateSchemaValidator,
|
|
1001
1044
|
ReportTemplateSectionSchema,
|
|
1002
1045
|
buildBaseFieldSchema,
|
|
1046
|
+
buildResponseSchema,
|
|
1003
1047
|
buildResponseSchemaWithConditions,
|
|
1004
1048
|
createUniqueId,
|
|
1005
1049
|
diffTemplates,
|