@frt-platform/report-core 1.0.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 +367 -0
- package/dist/index.d.mts +350 -0
- package/dist/index.d.ts +350 -0
- package/dist/index.js +236 -0
- package/dist/index.mjs +197 -0
- package/package.json +36 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// src/schema.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var REPORT_TEMPLATE_VERSION = 1;
|
|
4
|
+
var REPORT_TEMPLATE_FIELD_TYPES = [
|
|
5
|
+
"shortText",
|
|
6
|
+
"longText",
|
|
7
|
+
"number",
|
|
8
|
+
"date",
|
|
9
|
+
"checkbox",
|
|
10
|
+
"singleSelect",
|
|
11
|
+
"multiSelect",
|
|
12
|
+
"starRating"
|
|
13
|
+
];
|
|
14
|
+
var ReportTemplateFieldSchema = z.object({
|
|
15
|
+
id: z.string().min(1).max(60).regex(/^[a-z0-9_-]+$/),
|
|
16
|
+
type: z.string(),
|
|
17
|
+
label: z.string().min(1).max(200),
|
|
18
|
+
required: z.boolean().optional(),
|
|
19
|
+
description: z.string().max(400).optional(),
|
|
20
|
+
placeholder: z.string().max(200).optional(),
|
|
21
|
+
options: z.array(z.string().min(1).max(120)).optional(),
|
|
22
|
+
allowOther: z.boolean().optional(),
|
|
23
|
+
defaultValue: z.union([
|
|
24
|
+
z.string(),
|
|
25
|
+
z.number(),
|
|
26
|
+
z.boolean(),
|
|
27
|
+
z.array(z.string()),
|
|
28
|
+
z.null()
|
|
29
|
+
]).optional(),
|
|
30
|
+
minLength: z.number().int().min(0).optional(),
|
|
31
|
+
maxLength: z.number().int().min(0).optional(),
|
|
32
|
+
minValue: z.number().optional(),
|
|
33
|
+
maxValue: z.number().optional(),
|
|
34
|
+
step: z.number().nonnegative().optional(),
|
|
35
|
+
minSelections: z.number().int().min(0).optional(),
|
|
36
|
+
maxSelections: z.number().int().min(0).optional(),
|
|
37
|
+
dataClassification: z.enum(["none", "personal", "special"]).optional()
|
|
38
|
+
});
|
|
39
|
+
var ReportTemplateSectionSchema = z.object({
|
|
40
|
+
id: z.string().min(1).max(60).regex(/^[a-z0-9_-]+$/),
|
|
41
|
+
title: z.string().max(200).optional(),
|
|
42
|
+
description: z.string().max(500).optional(),
|
|
43
|
+
fields: z.array(ReportTemplateFieldSchema).default([])
|
|
44
|
+
});
|
|
45
|
+
var ReportTemplateSchemaValidator = z.object({
|
|
46
|
+
version: z.number().int().min(1).default(REPORT_TEMPLATE_VERSION),
|
|
47
|
+
title: z.string().max(200).optional(),
|
|
48
|
+
description: z.string().max(500).optional(),
|
|
49
|
+
sections: z.array(ReportTemplateSectionSchema).max(25).default([])
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// src/fields.ts
|
|
53
|
+
var DEFAULT_FIELD_LABEL = "Untitled question";
|
|
54
|
+
var CORE_FIELD_DEFAULTS = {
|
|
55
|
+
shortText: { label: DEFAULT_FIELD_LABEL, placeholder: "Short answer text" },
|
|
56
|
+
longText: { label: DEFAULT_FIELD_LABEL, placeholder: "Long answer text" },
|
|
57
|
+
number: { label: DEFAULT_FIELD_LABEL, placeholder: "123" },
|
|
58
|
+
date: { label: DEFAULT_FIELD_LABEL },
|
|
59
|
+
checkbox: { label: DEFAULT_FIELD_LABEL, placeholder: "Check to confirm" },
|
|
60
|
+
singleSelect: {
|
|
61
|
+
label: DEFAULT_FIELD_LABEL,
|
|
62
|
+
options: ["Option 1", "Option 2", "Option 3"]
|
|
63
|
+
},
|
|
64
|
+
multiSelect: {
|
|
65
|
+
label: DEFAULT_FIELD_LABEL,
|
|
66
|
+
options: ["Option 1", "Option 2", "Option 3"],
|
|
67
|
+
allowOther: false
|
|
68
|
+
},
|
|
69
|
+
starRating: {
|
|
70
|
+
label: DEFAULT_FIELD_LABEL,
|
|
71
|
+
minValue: 1,
|
|
72
|
+
maxValue: 5
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// src/ids.ts
|
|
77
|
+
function createUniqueId(prefix, existing) {
|
|
78
|
+
const used = new Set(existing);
|
|
79
|
+
let attempt = used.size + 1;
|
|
80
|
+
let candidate = `${prefix}-${attempt}`;
|
|
81
|
+
while (used.has(candidate)) {
|
|
82
|
+
attempt++;
|
|
83
|
+
candidate = `${prefix}-${attempt}`;
|
|
84
|
+
}
|
|
85
|
+
return candidate;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/migrate.ts
|
|
89
|
+
var LEGACY_FIELD_TYPE_MAP = {
|
|
90
|
+
shorttext: "shortText",
|
|
91
|
+
text: "shortText",
|
|
92
|
+
longtext: "longText",
|
|
93
|
+
textarea: "longText",
|
|
94
|
+
number: "number",
|
|
95
|
+
numeric: "number",
|
|
96
|
+
date: "date",
|
|
97
|
+
checkbox: "checkbox",
|
|
98
|
+
boolean: "checkbox",
|
|
99
|
+
select: "singleSelect",
|
|
100
|
+
dropdown: "singleSelect",
|
|
101
|
+
radio: "singleSelect",
|
|
102
|
+
multiselect: "multiSelect",
|
|
103
|
+
checkboxes: "multiSelect"
|
|
104
|
+
};
|
|
105
|
+
function migrateLegacySchema(raw) {
|
|
106
|
+
if (!raw || typeof raw !== "object") return raw;
|
|
107
|
+
const obj = structuredClone(raw);
|
|
108
|
+
if (obj.fields) {
|
|
109
|
+
obj.sections = [
|
|
110
|
+
{
|
|
111
|
+
id: "section-1",
|
|
112
|
+
title: obj.title,
|
|
113
|
+
description: obj.description,
|
|
114
|
+
fields: obj.fields.map((f, i) => ({
|
|
115
|
+
...f,
|
|
116
|
+
id: f.id || `field-${i + 1}`,
|
|
117
|
+
type: LEGACY_FIELD_TYPE_MAP[f.type?.toLowerCase()] ?? f.type
|
|
118
|
+
}))
|
|
119
|
+
}
|
|
120
|
+
];
|
|
121
|
+
delete obj.fields;
|
|
122
|
+
return obj;
|
|
123
|
+
}
|
|
124
|
+
if (Array.isArray(obj.sections)) {
|
|
125
|
+
obj.sections = obj.sections.map((sec, i) => ({
|
|
126
|
+
...sec,
|
|
127
|
+
id: sec.id || `section-${i + 1}`,
|
|
128
|
+
fields: (sec.fields || []).map((f, idx) => ({
|
|
129
|
+
...f,
|
|
130
|
+
id: f.id || `field-${idx + 1}`,
|
|
131
|
+
type: LEGACY_FIELD_TYPE_MAP[f.type?.toLowerCase()] ?? f.type
|
|
132
|
+
}))
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
135
|
+
return obj;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// src/normalize.ts
|
|
139
|
+
function parseReportTemplateSchema(raw) {
|
|
140
|
+
const migrated = migrateLegacySchema(raw);
|
|
141
|
+
const parsed = ReportTemplateSchemaValidator.parse(migrated);
|
|
142
|
+
return normalizeReportTemplateSchema(parsed);
|
|
143
|
+
}
|
|
144
|
+
function normalizeReportTemplateSchema(schema) {
|
|
145
|
+
const sections = schema.sections.length > 0 ? schema.sections : [
|
|
146
|
+
{
|
|
147
|
+
id: "section-1",
|
|
148
|
+
title: "",
|
|
149
|
+
description: "",
|
|
150
|
+
fields: []
|
|
151
|
+
}
|
|
152
|
+
];
|
|
153
|
+
const normalizedSections = sections.map((sec, i) => {
|
|
154
|
+
const seen = /* @__PURE__ */ new Set();
|
|
155
|
+
const safeSectionId = sec.id.trim() || `section-${i + 1}`;
|
|
156
|
+
const fields = sec.fields.map((field, idx) => {
|
|
157
|
+
const trimmed = field.id.trim() || `field-${idx + 1}`;
|
|
158
|
+
const safeId = seen.has(trimmed) ? `field-${idx + 1}` : trimmed;
|
|
159
|
+
seen.add(safeId);
|
|
160
|
+
return {
|
|
161
|
+
...field,
|
|
162
|
+
id: safeId
|
|
163
|
+
};
|
|
164
|
+
});
|
|
165
|
+
return {
|
|
166
|
+
...sec,
|
|
167
|
+
id: safeSectionId,
|
|
168
|
+
fields
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
return {
|
|
172
|
+
...schema,
|
|
173
|
+
sections: normalizedSections
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function parseReportTemplateSchemaFromString(raw) {
|
|
177
|
+
const obj = JSON.parse(raw);
|
|
178
|
+
return parseReportTemplateSchema(obj);
|
|
179
|
+
}
|
|
180
|
+
function serializeReportTemplateSchema(schema) {
|
|
181
|
+
return JSON.stringify(schema, null, 2);
|
|
182
|
+
}
|
|
183
|
+
export {
|
|
184
|
+
CORE_FIELD_DEFAULTS,
|
|
185
|
+
DEFAULT_FIELD_LABEL,
|
|
186
|
+
REPORT_TEMPLATE_FIELD_TYPES,
|
|
187
|
+
REPORT_TEMPLATE_VERSION,
|
|
188
|
+
ReportTemplateFieldSchema,
|
|
189
|
+
ReportTemplateSchemaValidator,
|
|
190
|
+
ReportTemplateSectionSchema,
|
|
191
|
+
createUniqueId,
|
|
192
|
+
migrateLegacySchema,
|
|
193
|
+
normalizeReportTemplateSchema,
|
|
194
|
+
parseReportTemplateSchema,
|
|
195
|
+
parseReportTemplateSchemaFromString,
|
|
196
|
+
serializeReportTemplateSchema
|
|
197
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@frt-platform/report-core",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Core report template engine: schema, validation, normalization, and migration.",
|
|
5
|
+
"author": "Sebastian Mostert",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./package.json": "./package.json"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup src/index.ts --format esm,cjs --dts",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"zod": "^3.23.8"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"tsup": "^8.5.1",
|
|
34
|
+
"typescript": "^5.6.3"
|
|
35
|
+
}
|
|
36
|
+
}
|