@frt-platform/report-core 1.3.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/README.md +95 -9
- package/dist/index.d.mts +213 -115
- package/dist/index.d.ts +213 -115
- package/dist/index.js +569 -349
- package/dist/index.mjs +568 -352
- 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
|
@@ -7,7 +7,9 @@ declare const Condition: z.ZodType<any>;
|
|
|
7
7
|
type SimpleCondition = z.infer<typeof Condition>;
|
|
8
8
|
declare const RepeatGroupFieldSchema: z.ZodType<any>;
|
|
9
9
|
declare const ReportTemplateFieldSchema: z.ZodType<any>;
|
|
10
|
-
type ReportTemplateField = z.infer<typeof ReportTemplateFieldSchema
|
|
10
|
+
type ReportTemplateField = z.infer<typeof ReportTemplateFieldSchema> & {
|
|
11
|
+
computed?: string;
|
|
12
|
+
};
|
|
11
13
|
declare const ReportTemplateSectionSchema: z.ZodObject<{
|
|
12
14
|
id: z.ZodString;
|
|
13
15
|
title: z.ZodOptional<z.ZodString>;
|
|
@@ -68,7 +70,25 @@ declare const ReportTemplateSchemaValidator: z.ZodObject<{
|
|
|
68
70
|
}>;
|
|
69
71
|
type ReportTemplateSchema = z.infer<typeof ReportTemplateSchemaValidator>;
|
|
70
72
|
|
|
71
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Helper for generating a unique id in a *local* namespace, typically in
|
|
75
|
+
* UI/builders when adding new sections/fields client-side.
|
|
76
|
+
*
|
|
77
|
+
* Note:
|
|
78
|
+
* - This is deliberately simple and deterministic.
|
|
79
|
+
* - Core template normalization uses its own normalizeId/ensureUniqueId logic
|
|
80
|
+
* so that persisted IDs stay stable and backwards compatible.
|
|
81
|
+
*/
|
|
82
|
+
declare function createUniqueId(prefix: string, existing: Iterable<string>): string;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Migrate a legacy flat { fields: [...] } structure or partially-upgraded
|
|
86
|
+
* schema into the current { sections: [...] } shape.
|
|
87
|
+
*
|
|
88
|
+
* This is intentionally loose: it accepts `unknown` and only normalizes
|
|
89
|
+
* what we care about. The full Zod validation happens later.
|
|
90
|
+
*/
|
|
91
|
+
declare function migrateLegacySchema(raw: unknown): ReportTemplateSchema | any;
|
|
72
92
|
|
|
73
93
|
declare function normalizeReportTemplateSchema(schema: ReportTemplateSchema): ReportTemplateSchema;
|
|
74
94
|
declare function parseReportTemplateSchema(raw: unknown): ReportTemplateSchema;
|
|
@@ -99,61 +119,55 @@ interface SerializeOptions {
|
|
|
99
119
|
*/
|
|
100
120
|
declare function serializeReportTemplateSchema(schema: ReportTemplateSchema, options?: SerializeOptions): string;
|
|
101
121
|
|
|
122
|
+
interface SectionPropertyChange {
|
|
123
|
+
key: string;
|
|
124
|
+
before: unknown;
|
|
125
|
+
after: unknown;
|
|
126
|
+
}
|
|
127
|
+
interface ModifiedSection {
|
|
128
|
+
sectionId: string;
|
|
129
|
+
changes: SectionPropertyChange[];
|
|
130
|
+
}
|
|
131
|
+
interface ModifiedField {
|
|
132
|
+
sectionId: string;
|
|
133
|
+
fieldId: string;
|
|
134
|
+
changes: SectionPropertyChange[];
|
|
135
|
+
}
|
|
136
|
+
interface SectionAddRemove {
|
|
137
|
+
sectionId: string;
|
|
138
|
+
index: number;
|
|
139
|
+
}
|
|
140
|
+
interface SectionReorder {
|
|
141
|
+
sectionId: string;
|
|
142
|
+
from: number;
|
|
143
|
+
to: number;
|
|
144
|
+
}
|
|
145
|
+
interface FieldAddRemove {
|
|
146
|
+
sectionId: string;
|
|
147
|
+
fieldId: string;
|
|
148
|
+
index: number;
|
|
149
|
+
}
|
|
150
|
+
interface FieldReorder {
|
|
151
|
+
sectionId: string;
|
|
152
|
+
fieldId: string;
|
|
153
|
+
from: number;
|
|
154
|
+
to: number;
|
|
155
|
+
}
|
|
156
|
+
interface NestedFieldDiff {
|
|
157
|
+
sectionId: string;
|
|
158
|
+
groupId: string;
|
|
159
|
+
diffs: TemplateDiff;
|
|
160
|
+
}
|
|
102
161
|
interface TemplateDiff {
|
|
103
|
-
addedSections:
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
sectionId: string;
|
|
113
|
-
from: number;
|
|
114
|
-
to: number;
|
|
115
|
-
}>;
|
|
116
|
-
modifiedSections: Array<{
|
|
117
|
-
sectionId: string;
|
|
118
|
-
changes: Array<{
|
|
119
|
-
key: string;
|
|
120
|
-
before: any;
|
|
121
|
-
after: any;
|
|
122
|
-
}>;
|
|
123
|
-
}>;
|
|
124
|
-
addedFields: Array<{
|
|
125
|
-
sectionId: string;
|
|
126
|
-
fieldId: string;
|
|
127
|
-
index: number;
|
|
128
|
-
}>;
|
|
129
|
-
removedFields: Array<{
|
|
130
|
-
sectionId: string;
|
|
131
|
-
fieldId: string;
|
|
132
|
-
index: number;
|
|
133
|
-
}>;
|
|
134
|
-
reorderedFields: Array<{
|
|
135
|
-
sectionId: string;
|
|
136
|
-
fieldId: string;
|
|
137
|
-
from: number;
|
|
138
|
-
to: number;
|
|
139
|
-
}>;
|
|
140
|
-
modifiedFields: Array<{
|
|
141
|
-
sectionId: string;
|
|
142
|
-
fieldId: string;
|
|
143
|
-
before: Record<string, any>;
|
|
144
|
-
after: Record<string, any>;
|
|
145
|
-
changes: Array<{
|
|
146
|
-
key: string;
|
|
147
|
-
before: any;
|
|
148
|
-
after: any;
|
|
149
|
-
}>;
|
|
150
|
-
}>;
|
|
151
|
-
/** NEW: repeatGroup nested diffs */
|
|
152
|
-
nestedFieldDiffs: Array<{
|
|
153
|
-
sectionId: string;
|
|
154
|
-
groupId: string;
|
|
155
|
-
diffs: TemplateDiff;
|
|
156
|
-
}>;
|
|
162
|
+
addedSections: SectionAddRemove[];
|
|
163
|
+
removedSections: SectionAddRemove[];
|
|
164
|
+
reorderedSections: SectionReorder[];
|
|
165
|
+
modifiedSections: ModifiedSection[];
|
|
166
|
+
addedFields: FieldAddRemove[];
|
|
167
|
+
removedFields: FieldAddRemove[];
|
|
168
|
+
reorderedFields: FieldReorder[];
|
|
169
|
+
modifiedFields: ModifiedField[];
|
|
170
|
+
nestedFieldDiffs: NestedFieldDiff[];
|
|
157
171
|
}
|
|
158
172
|
declare function diffTemplates(before: ReportTemplateSchema, after: ReportTemplateSchema): TemplateDiff;
|
|
159
173
|
|
|
@@ -192,63 +206,33 @@ interface JSONSchema {
|
|
|
192
206
|
*/
|
|
193
207
|
declare function exportJSONSchema(template: ReportTemplateSchema): JSONSchema;
|
|
194
208
|
|
|
195
|
-
declare const DEFAULT_FIELD_LABEL = "Untitled question";
|
|
196
|
-
declare const CORE_FIELD_DEFAULTS: Record<ReportTemplateFieldType, Record<string, unknown>>;
|
|
197
|
-
|
|
198
|
-
declare function createUniqueId(prefix: string, existing: Iterable<string>): string;
|
|
199
|
-
|
|
200
|
-
/** -------------------------------------------------------------
|
|
201
|
-
* FieldRegistryEntry
|
|
202
|
-
* --------------------------------------------------------------
|
|
203
|
-
* Represents metadata + behavior for a single field type.
|
|
204
|
-
* -------------------------------------------------------------- */
|
|
205
|
-
interface FieldRegistryEntry {
|
|
206
|
-
/** Default field-level properties merged when creating new fields */
|
|
207
|
-
defaults?: Record<string, unknown>;
|
|
208
|
-
/**
|
|
209
|
-
* Build a Zod schema for validating the *response value* of this field.
|
|
210
|
-
* If not provided, the core engine falls back to the built-in handler.
|
|
211
|
-
*/
|
|
212
|
-
buildResponseSchema?: (field: ReportTemplateField) => ZodTypeAny;
|
|
213
|
-
/**
|
|
214
|
-
* Optional UI metadata for @frt/report-react
|
|
215
|
-
* (icon, color, description, grouping, etc.)
|
|
216
|
-
*/
|
|
217
|
-
ui?: Record<string, any>;
|
|
218
|
-
}
|
|
219
|
-
/** -------------------------------------------------------------
|
|
220
|
-
* FieldRegistry
|
|
221
|
-
* --------------------------------------------------------------
|
|
222
|
-
* Central store of all field types (built-in + custom).
|
|
223
|
-
* -------------------------------------------------------------- */
|
|
224
|
-
declare class FieldRegistryClass {
|
|
225
|
-
private registry;
|
|
226
|
-
/** Register or override a field type. */
|
|
227
|
-
register(type: string, entry: FieldRegistryEntry): void;
|
|
228
|
-
/** Get registry entry for a field type. */
|
|
229
|
-
get(type: string): FieldRegistryEntry | undefined;
|
|
230
|
-
/** Check if field type is registered. */
|
|
231
|
-
has(type: string): boolean;
|
|
232
|
-
/** Return all field types currently registered. */
|
|
233
|
-
list(): string[];
|
|
234
|
-
}
|
|
235
|
-
/** Singleton instance */
|
|
236
|
-
declare const FieldRegistry: FieldRegistryClass;
|
|
237
|
-
|
|
238
209
|
/**
|
|
239
|
-
* Evaluate a
|
|
210
|
+
* Evaluate a SimpleCondition against a flat response object.
|
|
240
211
|
*
|
|
241
|
-
* Supported
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
212
|
+
* Supported structure:
|
|
213
|
+
* - { equals: { fieldId: value } }
|
|
214
|
+
* - { any: [cond, cond, ...] }
|
|
215
|
+
* - { all: [cond, cond, ...] }
|
|
216
|
+
* - { not: cond }
|
|
217
|
+
*
|
|
218
|
+
* Notes:
|
|
219
|
+
* - We intentionally keep this evaluation shallow and deterministic.
|
|
220
|
+
* - No special array semantics (e.g. "contains") yet โ it's strict equality.
|
|
246
221
|
*/
|
|
247
|
-
declare function evaluateCondition(condition: SimpleCondition, response: Record<string, any>): boolean;
|
|
222
|
+
declare function evaluateCondition(condition: SimpleCondition | undefined, response: Record<string, any>): boolean;
|
|
248
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Build the base Zod schema for a field, WITHOUT:
|
|
226
|
+
* - visibleIf
|
|
227
|
+
* - requiredIf
|
|
228
|
+
* - computed fields
|
|
229
|
+
* - repeatGroup logic
|
|
230
|
+
*
|
|
231
|
+
* This is the lowest-level โfield โ Zodโ mapping.
|
|
232
|
+
* All conditional logic is layered on top in conditionalSchema.ts.
|
|
233
|
+
*/
|
|
249
234
|
declare function buildBaseFieldSchema(field: ReportTemplateField): ZodTypeAny;
|
|
250
|
-
|
|
251
|
-
declare function validateReportResponse(template: ReportTemplateSchema, data: unknown): Record<string, unknown>;
|
|
235
|
+
|
|
252
236
|
type FieldErrorCode = "response.invalid_root" | "field.required" | "field.invalid_type" | "field.too_small" | "field.too_big" | "field.invalid_option" | "field.custom";
|
|
253
237
|
interface ResponseFieldError {
|
|
254
238
|
/** field id (template-level id) */
|
|
@@ -266,6 +250,27 @@ interface ResponseFieldError {
|
|
|
266
250
|
/** original Zod issue (for debugging / logging) */
|
|
267
251
|
rawIssue?: ZodIssue;
|
|
268
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* Helper to turn a thrown ZodError (e.g. from validateReportResponse)
|
|
255
|
+
* into the same ResponseFieldError[] shape used by
|
|
256
|
+
* validateReportResponseDetailed.
|
|
257
|
+
*
|
|
258
|
+
* Returns null if the error is not a ZodError.
|
|
259
|
+
*/
|
|
260
|
+
declare function explainValidationError(template: ReportTemplateSchema, error: unknown): ResponseFieldError[] | null;
|
|
261
|
+
|
|
262
|
+
declare function buildResponseSchemaWithConditions(template: ReportTemplateSchema, response: Record<string, any>): z.ZodObject<Record<string, ZodTypeAny>>;
|
|
263
|
+
/**
|
|
264
|
+
* Build a Zod object schema for a template *without* needing a response object.
|
|
265
|
+
*
|
|
266
|
+
* Notes:
|
|
267
|
+
* - All conditional logic is evaluated against an empty response `{}`.
|
|
268
|
+
* - This is mainly useful for:
|
|
269
|
+
* - Static type inference
|
|
270
|
+
* - Generic tooling / OpenAPI-style usage
|
|
271
|
+
*/
|
|
272
|
+
declare function buildResponseSchema(template: ReportTemplateSchema): z.ZodObject<Record<string, ZodTypeAny>>;
|
|
273
|
+
declare function validateReportResponse(template: ReportTemplateSchema, data: unknown): Record<string, unknown>;
|
|
269
274
|
/**
|
|
270
275
|
* Validates a response against a template, but instead of throwing,
|
|
271
276
|
* returns a structured error object with:
|
|
@@ -282,28 +287,66 @@ declare function validateReportResponseDetailed(template: ReportTemplateSchema,
|
|
|
282
287
|
success: false;
|
|
283
288
|
errors: ResponseFieldError[];
|
|
284
289
|
};
|
|
290
|
+
|
|
285
291
|
/**
|
|
286
|
-
*
|
|
287
|
-
* into the same ResponseFieldError[] shape used by
|
|
288
|
-
* validateReportResponseDetailed.
|
|
292
|
+
* Extracts the union of all field configs from a template type.
|
|
289
293
|
*
|
|
290
|
-
*
|
|
294
|
+
* Given:
|
|
295
|
+
* {
|
|
296
|
+
* sections: [
|
|
297
|
+
* { fields: [F1, F2] },
|
|
298
|
+
* { fields: [F3] }
|
|
299
|
+
* ]
|
|
300
|
+
* }
|
|
301
|
+
*
|
|
302
|
+
* โ TemplateFieldUnion<TTemplate> = F1 | F2 | F3
|
|
291
303
|
*/
|
|
292
|
-
declare function explainValidationError(template: ReportTemplateSchema, error: unknown): ResponseFieldError[] | null;
|
|
293
304
|
type TemplateFieldUnion<TTemplate> = TTemplate extends {
|
|
294
305
|
sections: readonly (infer S)[];
|
|
295
306
|
} ? S extends {
|
|
296
307
|
fields: readonly (infer F)[];
|
|
297
308
|
} ? F : never : never;
|
|
309
|
+
/**
|
|
310
|
+
* Base value type for a field, ignoring `required`.
|
|
311
|
+
* This is deliberately simple and only keyed off `type` + `options`.
|
|
312
|
+
*/
|
|
298
313
|
type BaseFieldValue<TField extends {
|
|
299
314
|
type: ReportTemplateFieldType;
|
|
300
315
|
required?: boolean;
|
|
301
316
|
options?: readonly string[] | string[];
|
|
302
|
-
}> = TField["type"] extends "shortText" | "longText" ? string : TField["type"] extends "number" ? number : TField["type"] extends "date" ? string : TField["type"] extends "checkbox" ? boolean : TField["type"] extends "singleSelect" ?
|
|
317
|
+
}> = TField["type"] extends "shortText" | "longText" ? string : TField["type"] extends "number" ? number : TField["type"] extends "date" ? string : TField["type"] extends "checkbox" ? boolean : TField["type"] extends "singleSelect" ? TField["options"] extends readonly (infer O)[] ? O : TField["options"] extends (infer O)[] ? O : string : TField["type"] extends "multiSelect" ? TField["options"] extends readonly (infer O)[] ? O[] : TField["options"] extends (infer O)[] ? O[] : string[] : TField["type"] extends "repeatGroup" ? Array<Record<string, unknown>> : unknown;
|
|
318
|
+
/**
|
|
319
|
+
* Wrap base value in optionality depending on `required`.
|
|
320
|
+
*/
|
|
303
321
|
type FieldResponseValue<TField extends {
|
|
304
322
|
type: ReportTemplateFieldType;
|
|
305
323
|
required?: boolean;
|
|
306
324
|
}> = TField["required"] extends true ? BaseFieldValue<TField> : BaseFieldValue<TField> | undefined;
|
|
325
|
+
/**
|
|
326
|
+
* Infer the response shape from a *const* template definition.
|
|
327
|
+
*
|
|
328
|
+
* Example:
|
|
329
|
+
*
|
|
330
|
+
* export const myTemplate = {
|
|
331
|
+
* version: 1,
|
|
332
|
+
* sections: [
|
|
333
|
+
* {
|
|
334
|
+
* id: "main",
|
|
335
|
+
* fields: [
|
|
336
|
+
* { id: "title", type: "shortText", required: true },
|
|
337
|
+
* { id: "tags", type: "multiSelect", options: ["A", "B"] },
|
|
338
|
+
* ]
|
|
339
|
+
* }
|
|
340
|
+
* ]
|
|
341
|
+
* } as const;
|
|
342
|
+
*
|
|
343
|
+
* type MyResponse = InferResponse<typeof myTemplate>;
|
|
344
|
+
*
|
|
345
|
+
* // {
|
|
346
|
+
* // title: string;
|
|
347
|
+
* // tags?: ("A" | "B")[];
|
|
348
|
+
* // }
|
|
349
|
+
*/
|
|
307
350
|
type InferResponse<TTemplate extends {
|
|
308
351
|
sections: readonly {
|
|
309
352
|
fields: readonly {
|
|
@@ -317,4 +360,59 @@ type InferResponse<TTemplate extends {
|
|
|
317
360
|
[F in TemplateFieldUnion<TTemplate> as F["id"]]: FieldResponseValue<F>;
|
|
318
361
|
};
|
|
319
362
|
|
|
320
|
-
|
|
363
|
+
/** -------------------------------------------------------------
|
|
364
|
+
* FieldRegistryEntry
|
|
365
|
+
* --------------------------------------------------------------
|
|
366
|
+
* Represents metadata + behavior for a single field type.
|
|
367
|
+
* -------------------------------------------------------------- */
|
|
368
|
+
interface FieldRegistryEntry {
|
|
369
|
+
/** Default field-level properties merged when creating new fields */
|
|
370
|
+
defaults?: Record<string, unknown>;
|
|
371
|
+
/**
|
|
372
|
+
* Build a Zod schema for validating the *response value* of this field.
|
|
373
|
+
* If not provided, the core engine falls back to the built-in handler.
|
|
374
|
+
*/
|
|
375
|
+
buildResponseSchema?: (field: ReportTemplateField) => ZodTypeAny;
|
|
376
|
+
/**
|
|
377
|
+
* Optional UI metadata for @frt/report-react
|
|
378
|
+
* (icon, color, description, grouping, etc.)
|
|
379
|
+
*/
|
|
380
|
+
ui?: Record<string, any>;
|
|
381
|
+
}
|
|
382
|
+
/** -------------------------------------------------------------
|
|
383
|
+
* FieldRegistry
|
|
384
|
+
* --------------------------------------------------------------
|
|
385
|
+
* Central store of all field types (built-in + custom).
|
|
386
|
+
* -------------------------------------------------------------- */
|
|
387
|
+
declare class FieldRegistryClass {
|
|
388
|
+
private registry;
|
|
389
|
+
/** Register or override a field type. */
|
|
390
|
+
register(type: string, entry: FieldRegistryEntry): void;
|
|
391
|
+
/** Get registry entry for a field type. */
|
|
392
|
+
get(type: string): FieldRegistryEntry | undefined;
|
|
393
|
+
/** Check if field type is registered. */
|
|
394
|
+
has(type: string): boolean;
|
|
395
|
+
/** Return all field types currently registered. */
|
|
396
|
+
list(): string[];
|
|
397
|
+
}
|
|
398
|
+
/** Singleton instance */
|
|
399
|
+
declare const FieldRegistry: FieldRegistryClass;
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Shared default label used for all core field types.
|
|
403
|
+
*/
|
|
404
|
+
declare const DEFAULT_FIELD_LABEL = "Untitled field";
|
|
405
|
+
/**
|
|
406
|
+
* Shape of the core defaults map.
|
|
407
|
+
* We keep values loose (Record<string, any>) because different field
|
|
408
|
+
* types have different shapes, and these are just "seed" objects.
|
|
409
|
+
*/
|
|
410
|
+
type CoreFieldDefaults = Record<ReportTemplateFieldType, Record<string, any>>;
|
|
411
|
+
/**
|
|
412
|
+
* Default configs for every core field type.
|
|
413
|
+
* These are *not* full fields (no id), just starter values that
|
|
414
|
+
* template builders / UIs can merge into new fields.
|
|
415
|
+
*/
|
|
416
|
+
declare const CORE_FIELD_DEFAULTS: CoreFieldDefaults;
|
|
417
|
+
|
|
418
|
+
export { CORE_FIELD_DEFAULTS, Condition, type CoreFieldDefaults, DEFAULT_FIELD_LABEL, type FieldAddRemove, type FieldErrorCode, FieldRegistry, type FieldRegistryEntry, type FieldReorder, type InferResponse, type JSONSchema, type ModifiedField, type ModifiedSection, type NestedFieldDiff, REPORT_TEMPLATE_FIELD_TYPES, REPORT_TEMPLATE_VERSION, RepeatGroupFieldSchema, type ReportTemplateField, ReportTemplateFieldSchema, type ReportTemplateFieldType, type ReportTemplateSchema, ReportTemplateSchemaValidator, type ReportTemplateSection, ReportTemplateSectionSchema, type ResponseFieldError, type SectionAddRemove, type SectionPropertyChange, type SectionReorder, type SerializeOptions, type SimpleCondition, type TemplateDiff, buildBaseFieldSchema, buildResponseSchema, buildResponseSchemaWithConditions, createUniqueId, diffTemplates, evaluateCondition, explainValidationError, exportJSONSchema, migrateLegacySchema, normalizeReportTemplateSchema, parseReportTemplateSchema, parseReportTemplateSchemaFromString, serializeReportTemplateSchema, validateReportResponse, validateReportResponseDetailed };
|