@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 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
- * Safe parsing from JSON or raw objects
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
- * repeatGroup min/max
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: 'Section "General Info" โ†’ Field "Incident title": String must contain at least 1 character(s).'
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
- * Repeat group conditional rules (`minIf` / `maxIf`)
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 = buildBaseFieldSchema(field);
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 = buildBaseFieldSchema(field);
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frt-platform/report-core",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Core report template engine: schema, validation, normalization, and migration.",
5
5
  "author": "Sebastian Mostert",
6
6
  "license": "MIT",