@formatica/core 0.1.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/LICENSE +21 -0
- package/README.md +34 -0
- package/dist/conditions.d.ts +7 -0
- package/dist/conditions.d.ts.map +1 -0
- package/dist/extractFields.d.ts +11 -0
- package/dist/extractFields.d.ts.map +1 -0
- package/dist/formatica-core.es.js +459 -0
- package/dist/formatica-core.es.js.map +1 -0
- package/dist/formatica-core.umd.cjs +2 -0
- package/dist/formatica-core.umd.cjs.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/schemaParser.d.ts +17 -0
- package/dist/schemaParser.d.ts.map +1 -0
- package/dist/types/i18n.d.ts +18 -0
- package/dist/types/i18n.d.ts.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/layout.d.ts +76 -0
- package/dist/types/layout.d.ts.map +1 -0
- package/dist/types/schema.d.ts +254 -0
- package/dist/types/schema.d.ts.map +1 -0
- package/dist/types/theme.d.ts +69 -0
- package/dist/types/theme.d.ts.map +1 -0
- package/dist/types/validation.d.ts +26 -0
- package/dist/types/validation.d.ts.map +1 -0
- package/dist/utils/deepMerge.d.ts +8 -0
- package/dist/utils/deepMerge.d.ts.map +1 -0
- package/dist/utils/sanitize.d.ts +6 -0
- package/dist/utils/sanitize.d.ts.map +1 -0
- package/dist/utils/titleCase.d.ts +10 -0
- package/dist/utils/titleCase.d.ts.map +1 -0
- package/dist/validation/ruleRegistry.d.ts +6 -0
- package/dist/validation/ruleRegistry.d.ts.map +1 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kim Boender
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# @formatica/core
|
|
2
|
+
|
|
3
|
+
Framework-agnostic schema-driven form engine. Provides types, validation, parsing, and conditional logic used by `@formatica/vue` and `@formatica/react`.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @formatica/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
`@formatica/core` is typically used indirectly through `@formatica/vue` or `@formatica/react`. You can also use it directly to work with form schemas, validators, and parsers.
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import type { FormSchema, FieldConfig } from "@formatica/core";
|
|
17
|
+
import { validateField, parseFieldValue } from "@formatica/core";
|
|
18
|
+
|
|
19
|
+
const schema: FormSchema = {
|
|
20
|
+
fields: [
|
|
21
|
+
{ name: "email", type: "email", label: "Email", required: true },
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Validate a field value
|
|
26
|
+
const error = validateField(schema.fields[0], "user@example.com");
|
|
27
|
+
|
|
28
|
+
// Parse a field value
|
|
29
|
+
const parsed = parseFieldValue(schema.fields[0], "user@example.com");
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## License
|
|
33
|
+
|
|
34
|
+
[MIT](../../LICENSE)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Condition, ConditionGroup } from "./types/schema";
|
|
2
|
+
/**
|
|
3
|
+
* Evaluate a single condition or a compound condition group against the
|
|
4
|
+
* current form values.
|
|
5
|
+
*/
|
|
6
|
+
export declare function evaluateCondition(condition: Condition | ConditionGroup, values: Record<string, unknown>): boolean;
|
|
7
|
+
//# sourceMappingURL=conditions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditions.d.ts","sourceRoot":"","sources":["../src/conditions.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAqB,MAAM,gBAAgB,CAAC;AAyEnF;;;GAGG;AACH,wBAAgB,iBAAiB,CAC7B,SAAS,EAAE,SAAS,GAAG,cAAc,EACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,OAAO,CAcT"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FieldSchema, SchemaNode } from "./types/schema";
|
|
2
|
+
/**
|
|
3
|
+
* Recursively walks a `SchemaNode[]` tree and collects every node that is an
|
|
4
|
+
* actual field (i.e. has a `name` property), ignoring layout containers.
|
|
5
|
+
*/
|
|
6
|
+
export declare function extractFields(nodes: SchemaNode[]): FieldSchema[];
|
|
7
|
+
/**
|
|
8
|
+
* Type-guard that distinguishes field nodes from layout container nodes.
|
|
9
|
+
*/
|
|
10
|
+
export declare function isFieldNode(node: SchemaNode): node is FieldSchema;
|
|
11
|
+
//# sourceMappingURL=extractFields.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractFields.d.ts","sourceRoot":"","sources":["../src/extractFields.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE9D;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,WAAW,EAAE,CAkBhE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,IAAI,WAAW,CAEjE"}
|
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
function g(e) {
|
|
2
|
+
const t = [];
|
|
3
|
+
for (const n of e)
|
|
4
|
+
if (N(n))
|
|
5
|
+
t.push(n);
|
|
6
|
+
else if (n.type === "row" || n.type === "group")
|
|
7
|
+
t.push(...g(n.children));
|
|
8
|
+
else if (n.type === "steps")
|
|
9
|
+
for (const r of n.steps)
|
|
10
|
+
t.push(...g(r.children));
|
|
11
|
+
else if (n.type === "tabs")
|
|
12
|
+
for (const r of n.tabs)
|
|
13
|
+
t.push(...g(r.children));
|
|
14
|
+
return t;
|
|
15
|
+
}
|
|
16
|
+
function N(e) {
|
|
17
|
+
return "name" in e && typeof e.name == "string";
|
|
18
|
+
}
|
|
19
|
+
class b extends Error {
|
|
20
|
+
constructor(t) {
|
|
21
|
+
const n = t.map((r) => `[${r.field}] ${r.message}`).join("; ");
|
|
22
|
+
super(`Schema validation failed: ${n}`), this.name = "SchemaValidationError", this.errors = t;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const S = /* @__PURE__ */ new Set([
|
|
26
|
+
"text",
|
|
27
|
+
"number",
|
|
28
|
+
"textarea",
|
|
29
|
+
"select",
|
|
30
|
+
"checkbox",
|
|
31
|
+
"checkbox-group",
|
|
32
|
+
"radio",
|
|
33
|
+
"switch",
|
|
34
|
+
"date",
|
|
35
|
+
"file",
|
|
36
|
+
"slider",
|
|
37
|
+
"tags",
|
|
38
|
+
"richtext",
|
|
39
|
+
"hidden",
|
|
40
|
+
"phone"
|
|
41
|
+
]), x = /* @__PURE__ */ new Set(["row", "group", "steps", "tabs", "divider", "html"]);
|
|
42
|
+
let p = null;
|
|
43
|
+
function E(e) {
|
|
44
|
+
p = e;
|
|
45
|
+
}
|
|
46
|
+
function m(e) {
|
|
47
|
+
return typeof e == "object" && e !== null && !Array.isArray(e);
|
|
48
|
+
}
|
|
49
|
+
function A(e, t, n, r) {
|
|
50
|
+
if (!m(e)) {
|
|
51
|
+
r.push({
|
|
52
|
+
field: t,
|
|
53
|
+
rule: "condition",
|
|
54
|
+
message: "Condition must be an object"
|
|
55
|
+
});
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const i = e;
|
|
59
|
+
if ("and" in i || "or" in i) {
|
|
60
|
+
const a = i, l = a.and ?? a.or ?? [];
|
|
61
|
+
if (!Array.isArray(l)) {
|
|
62
|
+
r.push({
|
|
63
|
+
field: t,
|
|
64
|
+
rule: "condition",
|
|
65
|
+
message: "Compound condition must have an array of sub-conditions"
|
|
66
|
+
});
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
for (let d = 0; d < l.length; d++)
|
|
70
|
+
A(l[d], `${t}.condition[${d}]`, n, r);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const s = i;
|
|
74
|
+
typeof s.field != "string" ? r.push({
|
|
75
|
+
field: t,
|
|
76
|
+
rule: "condition",
|
|
77
|
+
message: 'Condition must have a "field" property of type string'
|
|
78
|
+
}) : n.has(s.field) || r.push({
|
|
79
|
+
field: t,
|
|
80
|
+
rule: "condition",
|
|
81
|
+
message: `Condition references unknown field "${s.field}"`
|
|
82
|
+
}), typeof s.operator != "string" && r.push({
|
|
83
|
+
field: t,
|
|
84
|
+
rule: "condition",
|
|
85
|
+
message: 'Condition must have an "operator" property'
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
function j(e, t, n, r) {
|
|
89
|
+
if (!m(e)) {
|
|
90
|
+
r.push({ field: t, rule: "type", message: "Field must be an object" });
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const i = e;
|
|
94
|
+
(typeof i.name != "string" || i.name.length === 0) && r.push({ field: t, rule: "name", message: 'Field must have a non-empty "name"' }), typeof i.type != "string" ? r.push({ field: t, rule: "type", message: 'Field must have a "type" string' }) : !S.has(i.type) && !(p != null && p(i.type)) && r.push({
|
|
95
|
+
field: `${t}.${i.name}`,
|
|
96
|
+
rule: "type",
|
|
97
|
+
message: `Unknown field type "${i.type}"`
|
|
98
|
+
}), ["select", "radio", "checkbox-group"].includes(i.type) && (i.options === void 0 || i.options === null) && r.push({
|
|
99
|
+
field: `${t}.${i.name}`,
|
|
100
|
+
rule: "options",
|
|
101
|
+
message: `Field type "${i.type}" requires an "options" property`
|
|
102
|
+
}), i.condition && A(i.condition, `${t}.${i.name}`, n, r);
|
|
103
|
+
}
|
|
104
|
+
function T(e, t, n, r) {
|
|
105
|
+
if (!m(e)) {
|
|
106
|
+
r.push({ field: t, rule: "type", message: "Node must be an object" });
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const i = e, s = i.type;
|
|
110
|
+
if (typeof s != "string") {
|
|
111
|
+
r.push({ field: t, rule: "type", message: 'Node must have a "type" string' });
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (typeof i.name == "string") {
|
|
115
|
+
j(e, t, n, r);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (!x.has(s)) {
|
|
119
|
+
r.push({
|
|
120
|
+
field: t,
|
|
121
|
+
rule: "type",
|
|
122
|
+
message: `Unknown node type "${s}"`
|
|
123
|
+
});
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (s === "row" || s === "group")
|
|
127
|
+
Array.isArray(i.children) ? y(i.children, `${t}.children`, n, r) : r.push({
|
|
128
|
+
field: t,
|
|
129
|
+
rule: "children",
|
|
130
|
+
message: `"${s}" node must have a "children" array`
|
|
131
|
+
}), s === "group" && i.condition && A(i.condition, `${t}.condition`, n, r);
|
|
132
|
+
else if (s === "steps")
|
|
133
|
+
if (!Array.isArray(i.steps))
|
|
134
|
+
r.push({
|
|
135
|
+
field: t,
|
|
136
|
+
rule: "steps",
|
|
137
|
+
message: '"steps" node must have a "steps" array'
|
|
138
|
+
});
|
|
139
|
+
else
|
|
140
|
+
for (let a = 0; a < i.steps.length; a++) {
|
|
141
|
+
const l = i.steps[a];
|
|
142
|
+
if (!m(l)) {
|
|
143
|
+
r.push({
|
|
144
|
+
field: `${t}.steps[${a}]`,
|
|
145
|
+
rule: "type",
|
|
146
|
+
message: "Step item must be an object"
|
|
147
|
+
});
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
typeof l.title != "string" && r.push({
|
|
151
|
+
field: `${t}.steps[${a}]`,
|
|
152
|
+
rule: "title",
|
|
153
|
+
message: 'Step item must have a "title" string'
|
|
154
|
+
}), Array.isArray(l.children) && y(
|
|
155
|
+
l.children,
|
|
156
|
+
`${t}.steps[${a}].children`,
|
|
157
|
+
n,
|
|
158
|
+
r
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
else if (s === "tabs")
|
|
162
|
+
if (!Array.isArray(i.tabs))
|
|
163
|
+
r.push({
|
|
164
|
+
field: t,
|
|
165
|
+
rule: "tabs",
|
|
166
|
+
message: '"tabs" node must have a "tabs" array'
|
|
167
|
+
});
|
|
168
|
+
else
|
|
169
|
+
for (let a = 0; a < i.tabs.length; a++) {
|
|
170
|
+
const l = i.tabs[a];
|
|
171
|
+
if (!m(l)) {
|
|
172
|
+
r.push({
|
|
173
|
+
field: `${t}.tabs[${a}]`,
|
|
174
|
+
rule: "type",
|
|
175
|
+
message: "Tab item must be an object"
|
|
176
|
+
});
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
typeof l.title != "string" && r.push({
|
|
180
|
+
field: `${t}.tabs[${a}]`,
|
|
181
|
+
rule: "title",
|
|
182
|
+
message: 'Tab item must have a "title" string'
|
|
183
|
+
}), Array.isArray(l.children) && y(
|
|
184
|
+
l.children,
|
|
185
|
+
`${t}.tabs[${a}].children`,
|
|
186
|
+
n,
|
|
187
|
+
r
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
else s === "html" && typeof i.content != "string" && r.push({
|
|
191
|
+
field: t,
|
|
192
|
+
rule: "content",
|
|
193
|
+
message: '"html" node must have a "content" string'
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
function y(e, t, n, r) {
|
|
197
|
+
for (let i = 0; i < e.length; i++)
|
|
198
|
+
T(e[i], `${t}[${i}]`, n, r);
|
|
199
|
+
}
|
|
200
|
+
function z(e, t) {
|
|
201
|
+
if (!m(e)) {
|
|
202
|
+
t.push({ field: "settings", rule: "type", message: "Settings must be an object" });
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const n = e;
|
|
206
|
+
n.layout !== void 0 && !["vertical", "horizontal", "inline"].includes(n.layout) && t.push({
|
|
207
|
+
field: "settings.layout",
|
|
208
|
+
rule: "enum",
|
|
209
|
+
message: 'Layout must be "vertical", "horizontal", or "inline"'
|
|
210
|
+
}), n.size !== void 0 && !["small", "medium", "large"].includes(n.size) && t.push({
|
|
211
|
+
field: "settings.size",
|
|
212
|
+
rule: "enum",
|
|
213
|
+
message: 'Size must be "small", "medium", or "large"'
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
function R(e) {
|
|
217
|
+
const t = [];
|
|
218
|
+
if (!m(e))
|
|
219
|
+
throw t.push({ field: "$root", rule: "type", message: "Schema must be a plain object" }), new b(t);
|
|
220
|
+
const n = e;
|
|
221
|
+
if (!Array.isArray(n.fields))
|
|
222
|
+
throw t.push({ field: "fields", rule: "type", message: '"fields" must be an array' }), new b(t);
|
|
223
|
+
const r = g(n.fields), i = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Map();
|
|
224
|
+
for (const a of r) {
|
|
225
|
+
i.add(a.name);
|
|
226
|
+
const l = (s.get(a.name) ?? 0) + 1;
|
|
227
|
+
s.set(a.name, l), l > 1 && t.push({
|
|
228
|
+
field: a.name,
|
|
229
|
+
rule: "unique",
|
|
230
|
+
message: `Duplicate field name "${a.name}"`
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
if (y(n.fields, "fields", i, t), n.settings !== void 0 && z(n.settings, t), t.length > 0)
|
|
234
|
+
throw new b(t);
|
|
235
|
+
return {
|
|
236
|
+
id: typeof n.id == "string" ? n.id : void 0,
|
|
237
|
+
version: typeof n.version == "string" ? n.version : void 0,
|
|
238
|
+
fields: n.fields,
|
|
239
|
+
settings: n.settings,
|
|
240
|
+
translations: n.translations
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
const h = /* @__PURE__ */ new Map();
|
|
244
|
+
function o(e, t) {
|
|
245
|
+
h.set(e, t);
|
|
246
|
+
}
|
|
247
|
+
function v(e) {
|
|
248
|
+
h.delete(e);
|
|
249
|
+
}
|
|
250
|
+
function F(e) {
|
|
251
|
+
return h.get(e);
|
|
252
|
+
}
|
|
253
|
+
function I(e) {
|
|
254
|
+
return h.has(e);
|
|
255
|
+
}
|
|
256
|
+
function u(e) {
|
|
257
|
+
return !!(e == null || e === "" || Array.isArray(e) && e.length === 0);
|
|
258
|
+
}
|
|
259
|
+
function c(e, t) {
|
|
260
|
+
return e.replace(/\{(\w+)\}/g, (n, r) => t[r] !== void 0 ? String(t[r]) : `{${r}}`);
|
|
261
|
+
}
|
|
262
|
+
function f(e) {
|
|
263
|
+
if (typeof e == "number") return e;
|
|
264
|
+
if (typeof e == "string") {
|
|
265
|
+
const t = Number(e);
|
|
266
|
+
return Number.isNaN(t) ? null : t;
|
|
267
|
+
}
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
o("required", (e) => u(e) ? "This field is required" : !0);
|
|
271
|
+
o("email", (e) => u(e) ? !0 : /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(e)) || "Please enter a valid email address");
|
|
272
|
+
o("phone", async (e) => {
|
|
273
|
+
if (u(e)) return !0;
|
|
274
|
+
const t = String(e);
|
|
275
|
+
try {
|
|
276
|
+
return (await import("libphonenumber-js")).isValidPhoneNumber(t) || "Please enter a valid phone number";
|
|
277
|
+
} catch {
|
|
278
|
+
return /^\+[1-9]\d{6,14}$/.test(t.replace(/[\s\-().]/g, "")) ? !0 : "Please enter a valid phone number";
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
o("url", (e) => {
|
|
282
|
+
if (u(e)) return !0;
|
|
283
|
+
try {
|
|
284
|
+
return new URL(String(e)), !0;
|
|
285
|
+
} catch {
|
|
286
|
+
return "Please enter a valid URL";
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
o("min", (e, t) => {
|
|
290
|
+
if (u(e)) return !0;
|
|
291
|
+
const n = f(e), r = f(t.min);
|
|
292
|
+
return n === null || r === null ? !0 : n >= r || c("Must be at least {min}", t);
|
|
293
|
+
});
|
|
294
|
+
o("max", (e, t) => {
|
|
295
|
+
if (u(e)) return !0;
|
|
296
|
+
const n = f(e), r = f(t.max);
|
|
297
|
+
return n === null || r === null ? !0 : n <= r || c("Must be at most {max}", t);
|
|
298
|
+
});
|
|
299
|
+
o("minLength", (e, t) => {
|
|
300
|
+
if (u(e)) return !0;
|
|
301
|
+
const n = typeof e == "string" || Array.isArray(e) ? e.length : 0, r = f(t.min) ?? f(t.minLength) ?? 0;
|
|
302
|
+
return n >= r || c("Must be at least {min} characters", { min: r, ...t });
|
|
303
|
+
});
|
|
304
|
+
o("maxLength", (e, t) => {
|
|
305
|
+
if (u(e)) return !0;
|
|
306
|
+
const n = typeof e == "string" || Array.isArray(e) ? e.length : 0, r = f(t.max) ?? f(t.maxLength) ?? 1 / 0;
|
|
307
|
+
return n <= r || c("Must be at most {max} characters", { max: r, ...t });
|
|
308
|
+
});
|
|
309
|
+
o("between", (e, t) => {
|
|
310
|
+
if (u(e)) return !0;
|
|
311
|
+
const n = f(e), r = f(t.min), i = f(t.max);
|
|
312
|
+
return n === null || r === null || i === null ? !0 : n >= r && n <= i || c("Must be between {min} and {max}", t);
|
|
313
|
+
});
|
|
314
|
+
o("pattern", (e, t) => {
|
|
315
|
+
if (u(e)) return !0;
|
|
316
|
+
const n = t.pattern ?? t.regex;
|
|
317
|
+
return typeof n != "string" ? !0 : new RegExp(n).test(String(e)) || c("Does not match the required pattern", t);
|
|
318
|
+
});
|
|
319
|
+
o("numeric", (e) => u(e) ? !0 : f(e) !== null || "Must be a number");
|
|
320
|
+
o("integer", (e) => {
|
|
321
|
+
if (u(e)) return !0;
|
|
322
|
+
const t = f(e);
|
|
323
|
+
return t !== null && Number.isInteger(t) || "Must be an integer";
|
|
324
|
+
});
|
|
325
|
+
o("alpha", (e) => u(e) ? !0 : /^[a-zA-Z]+$/.test(String(e)) || "Must contain only letters");
|
|
326
|
+
o("alphaNumeric", (e) => u(e) ? !0 : /^[a-zA-Z0-9]+$/.test(String(e)) || "Must contain only letters and numbers");
|
|
327
|
+
o("confirmed", (e, t, n) => {
|
|
328
|
+
if (u(e)) return !0;
|
|
329
|
+
const r = String(t.target ?? ""), i = n.values[r] ?? n.values[`${r}_confirmation`];
|
|
330
|
+
return e === i || "Confirmation does not match";
|
|
331
|
+
});
|
|
332
|
+
o("requiredIf", (e, t, n) => {
|
|
333
|
+
const r = String(t.field ?? ""), i = t.value, s = n.values[r];
|
|
334
|
+
return (i !== void 0 ? s === i : !u(s)) ? !u(e) || c("This field is required when {field} is set", t) : !0;
|
|
335
|
+
});
|
|
336
|
+
o("date", (e) => {
|
|
337
|
+
if (u(e)) return !0;
|
|
338
|
+
const t = new Date(String(e));
|
|
339
|
+
return !Number.isNaN(t.getTime()) || "Please enter a valid date";
|
|
340
|
+
});
|
|
341
|
+
o("before", (e, t) => {
|
|
342
|
+
if (u(e)) return !0;
|
|
343
|
+
const n = new Date(String(e)), r = new Date(String(t.date ?? t.before));
|
|
344
|
+
return Number.isNaN(n.getTime()) || Number.isNaN(r.getTime()) ? !0 : n < r || c("Must be before {date}", t);
|
|
345
|
+
});
|
|
346
|
+
o("after", (e, t) => {
|
|
347
|
+
if (u(e)) return !0;
|
|
348
|
+
const n = new Date(String(e)), r = new Date(String(t.date ?? t.after));
|
|
349
|
+
return Number.isNaN(n.getTime()) || Number.isNaN(r.getTime()) ? !0 : n > r || c("Must be after {date}", t);
|
|
350
|
+
});
|
|
351
|
+
o("in", (e, t) => u(e) ? !0 : (Array.isArray(t.values) ? t.values : []).includes(e) || "The selected value is not valid");
|
|
352
|
+
o("notIn", (e, t) => u(e) ? !0 : !(Array.isArray(t.values) ? t.values : []).includes(e) || "The selected value is not allowed");
|
|
353
|
+
o("fileSize", (e, t) => {
|
|
354
|
+
if (u(e)) return !0;
|
|
355
|
+
const n = f(t.max) ?? 1 / 0, r = Array.isArray(e) ? e : [e];
|
|
356
|
+
for (const i of r) {
|
|
357
|
+
const s = typeof i == "object" && i !== null && "size" in i ? f(i.size) : null;
|
|
358
|
+
if (s !== null && s > n)
|
|
359
|
+
return c("File must be smaller than {max} bytes", t);
|
|
360
|
+
}
|
|
361
|
+
return !0;
|
|
362
|
+
});
|
|
363
|
+
o("mimeType", (e, t) => {
|
|
364
|
+
if (u(e)) return !0;
|
|
365
|
+
const n = Array.isArray(t.types) ? t.types : typeof t.types == "string" ? t.types.split(",").map((i) => i.trim()) : [], r = Array.isArray(e) ? e : [e];
|
|
366
|
+
for (const i of r) {
|
|
367
|
+
const s = typeof i == "object" && i !== null && "type" in i ? String(i.type) : "";
|
|
368
|
+
if (s && !n.some((a) => s.match(new RegExp(a.replace("*", ".*")))))
|
|
369
|
+
return c("File type is not allowed. Allowed: {types}", t);
|
|
370
|
+
}
|
|
371
|
+
return !0;
|
|
372
|
+
});
|
|
373
|
+
o("maxFiles", (e, t) => {
|
|
374
|
+
if (u(e)) return !0;
|
|
375
|
+
const n = f(t.max) ?? 1 / 0;
|
|
376
|
+
return (Array.isArray(e) ? e.length : 1) <= n || c("Maximum {max} files allowed", t);
|
|
377
|
+
});
|
|
378
|
+
o("unique", (e) => u(e) || !Array.isArray(e) ? !0 : new Set(e).size === e.length || "All values must be unique");
|
|
379
|
+
o("custom", (e, t) => !0);
|
|
380
|
+
function M(e, t, n) {
|
|
381
|
+
switch (t) {
|
|
382
|
+
case "eq":
|
|
383
|
+
return e === n;
|
|
384
|
+
case "neq":
|
|
385
|
+
return e !== n;
|
|
386
|
+
case "gt":
|
|
387
|
+
return Number(e) > Number(n);
|
|
388
|
+
case "gte":
|
|
389
|
+
return Number(e) >= Number(n);
|
|
390
|
+
case "lt":
|
|
391
|
+
return Number(e) < Number(n);
|
|
392
|
+
case "lte":
|
|
393
|
+
return Number(e) <= Number(n);
|
|
394
|
+
case "in":
|
|
395
|
+
return Array.isArray(n) && n.includes(e);
|
|
396
|
+
case "notIn":
|
|
397
|
+
return Array.isArray(n) && !n.includes(e);
|
|
398
|
+
case "contains":
|
|
399
|
+
return typeof e == "string" ? e.includes(String(n)) : Array.isArray(e) ? e.includes(n) : !1;
|
|
400
|
+
case "empty":
|
|
401
|
+
return e == null || e === "" || Array.isArray(e) && e.length === 0;
|
|
402
|
+
case "notEmpty":
|
|
403
|
+
return !(e == null || e === "" || Array.isArray(e) && e.length === 0);
|
|
404
|
+
case "matches": {
|
|
405
|
+
if (typeof n != "string") return !1;
|
|
406
|
+
try {
|
|
407
|
+
return new RegExp(n).test(String(e));
|
|
408
|
+
} catch {
|
|
409
|
+
return !1;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
default:
|
|
413
|
+
return !1;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
function q(e) {
|
|
417
|
+
return "and" in e || "or" in e;
|
|
418
|
+
}
|
|
419
|
+
function w(e, t) {
|
|
420
|
+
if (q(e))
|
|
421
|
+
return e.and ? e.and.every((r) => w(r, t)) : e.or ? e.or.some((r) => w(r, t)) : !0;
|
|
422
|
+
const n = t[e.field];
|
|
423
|
+
return M(n, e.operator, e.value);
|
|
424
|
+
}
|
|
425
|
+
function $(e) {
|
|
426
|
+
if (typeof e != "object" || e === null) return !1;
|
|
427
|
+
const t = Object.getPrototypeOf(e);
|
|
428
|
+
return t === Object.prototype || t === null;
|
|
429
|
+
}
|
|
430
|
+
function C(e, t) {
|
|
431
|
+
const n = { ...e };
|
|
432
|
+
for (const r of Object.keys(t)) {
|
|
433
|
+
const i = t[r], s = n[r];
|
|
434
|
+
$(i) && $(s) ? n[r] = C(s, i) : n[r] = i;
|
|
435
|
+
}
|
|
436
|
+
return n;
|
|
437
|
+
}
|
|
438
|
+
function O(e) {
|
|
439
|
+
return e.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").replace(/\b\w/g, (t) => t.toUpperCase()).trim();
|
|
440
|
+
}
|
|
441
|
+
function D(e) {
|
|
442
|
+
return e.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "").replace(/\s+on\w+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "").replace(/(href|src|action)\s*=\s*(?:"javascript:[^"]*"|'javascript:[^']*')/gi, '$1=""');
|
|
443
|
+
}
|
|
444
|
+
export {
|
|
445
|
+
b as SchemaValidationError,
|
|
446
|
+
C as deepMerge,
|
|
447
|
+
w as evaluateCondition,
|
|
448
|
+
g as extractFields,
|
|
449
|
+
F as getRule,
|
|
450
|
+
I as hasRule,
|
|
451
|
+
N as isFieldNode,
|
|
452
|
+
R as parseFormSchema,
|
|
453
|
+
o as registerRule,
|
|
454
|
+
D as sanitizeHtml,
|
|
455
|
+
E as setFieldTypeChecker,
|
|
456
|
+
O as titleCase,
|
|
457
|
+
v as unregisterRule
|
|
458
|
+
};
|
|
459
|
+
//# sourceMappingURL=formatica-core.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatica-core.es.js","sources":["../src/extractFields.ts","../src/schemaParser.ts","../src/validation/ruleRegistry.ts","../src/conditions.ts","../src/utils/deepMerge.ts","../src/utils/titleCase.ts","../src/utils/sanitize.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Formatica Core – Utility to extract field schemas from a unified schema tree\n// ---------------------------------------------------------------------------\n\nimport type { FieldSchema, SchemaNode } from \"./types/schema\";\n\n/**\n * Recursively walks a `SchemaNode[]` tree and collects every node that is an\n * actual field (i.e. has a `name` property), ignoring layout containers.\n */\nexport function extractFields(nodes: SchemaNode[]): FieldSchema[] {\n const fields: FieldSchema[] = [];\n for (const node of nodes) {\n if (isFieldNode(node)) {\n fields.push(node);\n } else if (node.type === \"row\" || node.type === \"group\") {\n fields.push(...extractFields(node.children));\n } else if (node.type === \"steps\") {\n for (const step of node.steps) {\n fields.push(...extractFields(step.children));\n }\n } else if (node.type === \"tabs\") {\n for (const tab of node.tabs) {\n fields.push(...extractFields(tab.children));\n }\n }\n }\n return fields;\n}\n\n/**\n * Type-guard that distinguishes field nodes from layout container nodes.\n */\nexport function isFieldNode(node: SchemaNode): node is FieldSchema {\n return \"name\" in node && typeof node.name === \"string\";\n}\n","// ---------------------------------------------------------------------------\n// Formatica Core – Schema parser & validator\n// ---------------------------------------------------------------------------\n\nimport type {\n Condition,\n ConditionGroup,\n FormSchema,\n FormSettings,\n SchemaNode,\n} from \"./types/schema\";\nimport type { SchemaError } from \"./types/validation\";\nimport { extractFields } from \"./extractFields\";\n\n// ---------------------------------------------------------------------------\n// Error class\n// ---------------------------------------------------------------------------\n\nexport class SchemaValidationError extends Error {\n public readonly errors: SchemaError[];\n\n constructor(errors: SchemaError[]) {\n const summary = errors.map((e) => `[${e.field}] ${e.message}`).join(\"; \");\n super(`Schema validation failed: ${summary}`);\n this.name = \"SchemaValidationError\";\n this.errors = errors;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Known field types\n// ---------------------------------------------------------------------------\n\nconst KNOWN_FIELD_TYPES = new Set([\n \"text\",\n \"number\",\n \"textarea\",\n \"select\",\n \"checkbox\",\n \"checkbox-group\",\n \"radio\",\n \"switch\",\n \"date\",\n \"file\",\n \"slider\",\n \"tags\",\n \"richtext\",\n \"hidden\",\n \"phone\",\n]);\n\nconst CONTAINER_TYPES = new Set([\"row\", \"group\", \"steps\", \"tabs\", \"divider\", \"html\"]);\n\n// ---------------------------------------------------------------------------\n// Custom field type checker (pluggable for framework-specific registries)\n// ---------------------------------------------------------------------------\n\nlet customFieldTypeChecker: ((type: string) => boolean) | null = null;\n\n/**\n * Register a custom field type checker. This allows framework-specific packages\n * (e.g. @formatica/vue) to hook their field registry into schema validation.\n */\nexport function setFieldTypeChecker(checker: ((type: string) => boolean) | null): void {\n customFieldTypeChecker = checker;\n}\n\n// ---------------------------------------------------------------------------\n// Validation helpers\n// ---------------------------------------------------------------------------\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction validateCondition(\n condition: unknown,\n fieldPath: string,\n allFieldNames: Set<string>,\n errors: SchemaError[],\n): void {\n if (!isPlainObject(condition)) {\n errors.push({\n field: fieldPath,\n rule: \"condition\",\n message: \"Condition must be an object\",\n });\n return;\n }\n\n const cond = condition as Record<string, unknown>;\n\n // Compound conditions\n if (\"and\" in cond || \"or\" in cond) {\n const group = cond as ConditionGroup;\n const children = group.and ?? group.or ?? [];\n if (!Array.isArray(children)) {\n errors.push({\n field: fieldPath,\n rule: \"condition\",\n message: \"Compound condition must have an array of sub-conditions\",\n });\n return;\n }\n for (let i = 0; i < children.length; i++) {\n validateCondition(children[i], `${fieldPath}.condition[${i}]`, allFieldNames, errors);\n }\n return;\n }\n\n // Simple condition\n const simple = cond as unknown as Condition;\n if (typeof simple.field !== \"string\") {\n errors.push({\n field: fieldPath,\n rule: \"condition\",\n message: 'Condition must have a \"field\" property of type string',\n });\n } else if (!allFieldNames.has(simple.field)) {\n errors.push({\n field: fieldPath,\n rule: \"condition\",\n message: `Condition references unknown field \"${simple.field}\"`,\n });\n }\n\n if (typeof simple.operator !== \"string\") {\n errors.push({\n field: fieldPath,\n rule: \"condition\",\n message: 'Condition must have an \"operator\" property',\n });\n }\n}\n\nfunction validateFieldNode(\n field: unknown,\n path: string,\n allFieldNames: Set<string>,\n errors: SchemaError[],\n): void {\n if (!isPlainObject(field)) {\n errors.push({ field: path, rule: \"type\", message: \"Field must be an object\" });\n return;\n }\n\n const f = field as Record<string, unknown>;\n\n // name\n if (typeof f.name !== \"string\" || f.name.length === 0) {\n errors.push({ field: path, rule: \"name\", message: 'Field must have a non-empty \"name\"' });\n }\n\n // type\n if (typeof f.type !== \"string\") {\n errors.push({ field: path, rule: \"type\", message: 'Field must have a \"type\" string' });\n } else if (!KNOWN_FIELD_TYPES.has(f.type) && !customFieldTypeChecker?.(f.type)) {\n errors.push({\n field: `${path}.${f.name as string}`,\n rule: \"type\",\n message: `Unknown field type \"${f.type}\"`,\n });\n }\n\n // options required for select, radio, checkbox-group\n if ([\"select\", \"radio\", \"checkbox-group\"].includes(f.type as string)) {\n if (f.options === undefined || f.options === null) {\n errors.push({\n field: `${path}.${f.name as string}`,\n rule: \"options\",\n message: `Field type \"${f.type as string}\" requires an \"options\" property`,\n });\n }\n }\n\n // condition references\n if (f.condition) {\n validateCondition(f.condition, `${path}.${f.name as string}`, allFieldNames, errors);\n }\n}\n\nfunction validateNode(\n node: unknown,\n path: string,\n allFieldNames: Set<string>,\n errors: SchemaError[],\n): void {\n if (!isPlainObject(node)) {\n errors.push({ field: path, rule: \"type\", message: \"Node must be an object\" });\n return;\n }\n\n const n = node as Record<string, unknown>;\n const type = n.type as string;\n\n if (typeof type !== \"string\") {\n errors.push({ field: path, rule: \"type\", message: 'Node must have a \"type\" string' });\n return;\n }\n\n // If it's a field node (has a name), validate as field\n if (typeof n.name === \"string\") {\n validateFieldNode(node, path, allFieldNames, errors);\n return;\n }\n\n // Container nodes\n if (!CONTAINER_TYPES.has(type)) {\n errors.push({\n field: path,\n rule: \"type\",\n message: `Unknown node type \"${type}\"`,\n });\n return;\n }\n\n if (type === \"row\" || type === \"group\") {\n if (!Array.isArray(n.children)) {\n errors.push({\n field: path,\n rule: \"children\",\n message: `\"${type}\" node must have a \"children\" array`,\n });\n } else {\n validateNodes(n.children, `${path}.children`, allFieldNames, errors);\n }\n if (type === \"group\" && n.condition) {\n validateCondition(n.condition, `${path}.condition`, allFieldNames, errors);\n }\n } else if (type === \"steps\") {\n if (!Array.isArray(n.steps)) {\n errors.push({\n field: path,\n rule: \"steps\",\n message: '\"steps\" node must have a \"steps\" array',\n });\n } else {\n for (let i = 0; i < n.steps.length; i++) {\n const step = n.steps[i] as Record<string, unknown>;\n if (!isPlainObject(step)) {\n errors.push({\n field: `${path}.steps[${i}]`,\n rule: \"type\",\n message: \"Step item must be an object\",\n });\n continue;\n }\n if (typeof step.title !== \"string\") {\n errors.push({\n field: `${path}.steps[${i}]`,\n rule: \"title\",\n message: 'Step item must have a \"title\" string',\n });\n }\n if (Array.isArray(step.children)) {\n validateNodes(\n step.children,\n `${path}.steps[${i}].children`,\n allFieldNames,\n errors,\n );\n }\n }\n }\n } else if (type === \"tabs\") {\n if (!Array.isArray(n.tabs)) {\n errors.push({\n field: path,\n rule: \"tabs\",\n message: '\"tabs\" node must have a \"tabs\" array',\n });\n } else {\n for (let i = 0; i < n.tabs.length; i++) {\n const tab = n.tabs[i] as Record<string, unknown>;\n if (!isPlainObject(tab)) {\n errors.push({\n field: `${path}.tabs[${i}]`,\n rule: \"type\",\n message: \"Tab item must be an object\",\n });\n continue;\n }\n if (typeof tab.title !== \"string\") {\n errors.push({\n field: `${path}.tabs[${i}]`,\n rule: \"title\",\n message: 'Tab item must have a \"title\" string',\n });\n }\n if (Array.isArray(tab.children)) {\n validateNodes(\n tab.children,\n `${path}.tabs[${i}].children`,\n allFieldNames,\n errors,\n );\n }\n }\n }\n } else if (type === \"html\") {\n if (typeof n.content !== \"string\") {\n errors.push({\n field: path,\n rule: \"content\",\n message: '\"html\" node must have a \"content\" string',\n });\n }\n }\n // \"divider\" has no required children/content — always valid if type matches\n}\n\nfunction validateNodes(\n nodes: unknown[],\n basePath: string,\n allFieldNames: Set<string>,\n errors: SchemaError[],\n): void {\n for (let i = 0; i < nodes.length; i++) {\n validateNode(nodes[i], `${basePath}[${i}]`, allFieldNames, errors);\n }\n}\n\nfunction validateSettings(settings: unknown, errors: SchemaError[]): void {\n if (!isPlainObject(settings)) {\n errors.push({ field: \"settings\", rule: \"type\", message: \"Settings must be an object\" });\n return;\n }\n\n const s = settings as Record<string, unknown>;\n\n if (\n s.layout !== undefined &&\n ![\"vertical\", \"horizontal\", \"inline\"].includes(s.layout as string)\n ) {\n errors.push({\n field: \"settings.layout\",\n rule: \"enum\",\n message: 'Layout must be \"vertical\", \"horizontal\", or \"inline\"',\n });\n }\n\n if (s.size !== undefined && ![\"small\", \"medium\", \"large\"].includes(s.size as string)) {\n errors.push({\n field: \"settings.size\",\n rule: \"enum\",\n message: 'Size must be \"small\", \"medium\", or \"large\"',\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Parse and validate a JSON schema, returning a typed FormSchema.\n * Collects all validation errors before throwing a single SchemaValidationError.\n */\nexport function parseFormSchema(json: unknown): FormSchema {\n const errors: SchemaError[] = [];\n\n if (!isPlainObject(json)) {\n errors.push({ field: \"$root\", rule: \"type\", message: \"Schema must be a plain object\" });\n throw new SchemaValidationError(errors);\n }\n\n const raw = json as Record<string, unknown>;\n\n // fields\n if (!Array.isArray(raw.fields)) {\n errors.push({ field: \"fields\", rule: \"type\", message: '\"fields\" must be an array' });\n throw new SchemaValidationError(errors);\n }\n\n // Collect all field names recursively for cross-reference validation\n const allFields = extractFields(raw.fields as SchemaNode[]);\n const allFieldNames = new Set<string>();\n const nameTracker = new Map<string, number>();\n for (const f of allFields) {\n allFieldNames.add(f.name);\n const count = (nameTracker.get(f.name) ?? 0) + 1;\n nameTracker.set(f.name, count);\n if (count > 1) {\n errors.push({\n field: f.name,\n rule: \"unique\",\n message: `Duplicate field name \"${f.name}\"`,\n });\n }\n }\n\n // Validate each node recursively (fields and containers)\n validateNodes(raw.fields, \"fields\", allFieldNames, errors);\n\n // Validate settings\n if (raw.settings !== undefined) {\n validateSettings(raw.settings, errors);\n }\n\n if (errors.length > 0) {\n throw new SchemaValidationError(errors);\n }\n\n return {\n id: typeof raw.id === \"string\" ? raw.id : undefined,\n version: typeof raw.version === \"string\" ? raw.version : undefined,\n fields: raw.fields as SchemaNode[],\n settings: raw.settings as FormSettings | undefined,\n translations: raw.translations as FormSchema[\"translations\"],\n };\n}\n","// ---------------------------------------------------------------------------\n// Formatica Core – Validation rule registry\n// ---------------------------------------------------------------------------\n\nimport type { RuleFn } from \"../types/validation\";\n\n// ---------------------------------------------------------------------------\n// Registry\n// ---------------------------------------------------------------------------\n\nconst rules = new Map<string, RuleFn>();\n\nexport function registerRule(name: string, fn: RuleFn): void {\n rules.set(name, fn);\n}\n\nexport function unregisterRule(name: string): void {\n rules.delete(name);\n}\n\nexport function getRule(name: string): RuleFn | undefined {\n return rules.get(name);\n}\n\nexport function hasRule(name: string): boolean {\n return rules.has(name);\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction isEmpty(value: unknown): boolean {\n if (value === null || value === undefined || value === \"\") return true;\n if (Array.isArray(value) && value.length === 0) return true;\n return false;\n}\n\nfunction interpolate(template: string, params: Record<string, unknown>): string {\n return template.replace(/\\{(\\w+)\\}/g, (_, key: string) => {\n return params[key] !== undefined ? String(params[key]) : `{${key}}`;\n });\n}\n\nfunction toNumber(value: unknown): number | null {\n if (typeof value === \"number\") return value;\n if (typeof value === \"string\") {\n const n = Number(value);\n return Number.isNaN(n) ? null : n;\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Built-in rules\n// ---------------------------------------------------------------------------\n\nregisterRule(\"required\", (value) => {\n if (isEmpty(value)) return \"This field is required\";\n return true;\n});\n\nregisterRule(\"email\", (value) => {\n if (isEmpty(value)) return true;\n const re = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n return re.test(String(value)) || \"Please enter a valid email address\";\n});\n\nregisterRule(\"phone\", async (value) => {\n if (isEmpty(value)) return true;\n const str = String(value);\n try {\n const lib = await import(\"libphonenumber-js\");\n return lib.isValidPhoneNumber(str) || \"Please enter a valid phone number\";\n } catch {\n // Fallback if libphonenumber-js not available\n if (/^\\+[1-9]\\d{6,14}$/.test(str.replace(/[\\s\\-().]/g, \"\"))) return true;\n return \"Please enter a valid phone number\";\n }\n});\n\nregisterRule(\"url\", (value) => {\n if (isEmpty(value)) return true;\n try {\n new URL(String(value));\n return true;\n } catch {\n return \"Please enter a valid URL\";\n }\n});\n\nregisterRule(\"min\", (value, params) => {\n if (isEmpty(value)) return true;\n const num = toNumber(value);\n const min = toNumber(params.min);\n if (num === null || min === null) return true;\n return num >= min || interpolate(\"Must be at least {min}\", params);\n});\n\nregisterRule(\"max\", (value, params) => {\n if (isEmpty(value)) return true;\n const num = toNumber(value);\n const max = toNumber(params.max);\n if (num === null || max === null) return true;\n return num <= max || interpolate(\"Must be at most {max}\", params);\n});\n\nregisterRule(\"minLength\", (value, params) => {\n if (isEmpty(value)) return true;\n const len = typeof value === \"string\" ? value.length : Array.isArray(value) ? value.length : 0;\n const min = toNumber(params.min) ?? toNumber(params.minLength) ?? 0;\n return len >= min || interpolate(\"Must be at least {min} characters\", { min, ...params });\n});\n\nregisterRule(\"maxLength\", (value, params) => {\n if (isEmpty(value)) return true;\n const len = typeof value === \"string\" ? value.length : Array.isArray(value) ? value.length : 0;\n const max = toNumber(params.max) ?? toNumber(params.maxLength) ?? Infinity;\n return len <= max || interpolate(\"Must be at most {max} characters\", { max, ...params });\n});\n\nregisterRule(\"between\", (value, params) => {\n if (isEmpty(value)) return true;\n const num = toNumber(value);\n const min = toNumber(params.min);\n const max = toNumber(params.max);\n if (num === null || min === null || max === null) return true;\n return (num >= min && num <= max) || interpolate(\"Must be between {min} and {max}\", params);\n});\n\nregisterRule(\"pattern\", (value, params) => {\n if (isEmpty(value)) return true;\n const pattern = params.pattern ?? params.regex;\n if (typeof pattern !== \"string\") return true;\n const re = new RegExp(pattern);\n return re.test(String(value)) || interpolate(\"Does not match the required pattern\", params);\n});\n\nregisterRule(\"numeric\", (value) => {\n if (isEmpty(value)) return true;\n return toNumber(value) !== null || \"Must be a number\";\n});\n\nregisterRule(\"integer\", (value) => {\n if (isEmpty(value)) return true;\n const num = toNumber(value);\n return (num !== null && Number.isInteger(num)) || \"Must be an integer\";\n});\n\nregisterRule(\"alpha\", (value) => {\n if (isEmpty(value)) return true;\n return /^[a-zA-Z]+$/.test(String(value)) || \"Must contain only letters\";\n});\n\nregisterRule(\"alphaNumeric\", (value) => {\n if (isEmpty(value)) return true;\n return /^[a-zA-Z0-9]+$/.test(String(value)) || \"Must contain only letters and numbers\";\n});\n\nregisterRule(\"confirmed\", (value, params, ctx) => {\n if (isEmpty(value)) return true;\n const target = String(params.target ?? \"\");\n const other = ctx.values[target] ?? ctx.values[`${target}_confirmation`];\n return value === other || \"Confirmation does not match\";\n});\n\nregisterRule(\"requiredIf\", (value, params, ctx) => {\n const targetField = String(params.field ?? \"\");\n const targetValue = params.value;\n const actual = ctx.values[targetField];\n const shouldRequire = targetValue !== undefined ? actual === targetValue : !isEmpty(actual);\n if (!shouldRequire) return true;\n return !isEmpty(value) || interpolate(\"This field is required when {field} is set\", params);\n});\n\nregisterRule(\"date\", (value) => {\n if (isEmpty(value)) return true;\n const d = new Date(String(value));\n return !Number.isNaN(d.getTime()) || \"Please enter a valid date\";\n});\n\nregisterRule(\"before\", (value, params) => {\n if (isEmpty(value)) return true;\n const d = new Date(String(value));\n const before = new Date(String(params.date ?? params.before));\n if (Number.isNaN(d.getTime()) || Number.isNaN(before.getTime())) return true;\n return d < before || interpolate(\"Must be before {date}\", params);\n});\n\nregisterRule(\"after\", (value, params) => {\n if (isEmpty(value)) return true;\n const d = new Date(String(value));\n const after = new Date(String(params.date ?? params.after));\n if (Number.isNaN(d.getTime()) || Number.isNaN(after.getTime())) return true;\n return d > after || interpolate(\"Must be after {date}\", params);\n});\n\nregisterRule(\"in\", (value, params) => {\n if (isEmpty(value)) return true;\n const allowed = Array.isArray(params.values) ? params.values : [];\n return (allowed as unknown[]).includes(value) || \"The selected value is not valid\";\n});\n\nregisterRule(\"notIn\", (value, params) => {\n if (isEmpty(value)) return true;\n const disallowed = Array.isArray(params.values) ? params.values : [];\n return !(disallowed as unknown[]).includes(value) || \"The selected value is not allowed\";\n});\n\nregisterRule(\"fileSize\", (value, params) => {\n if (isEmpty(value)) return true;\n const maxBytes = toNumber(params.max) ?? Infinity;\n const files = Array.isArray(value) ? value : [value];\n for (const file of files) {\n const size =\n typeof file === \"object\" && file !== null && \"size\" in file\n ? toNumber((file as { size: unknown }).size)\n : null;\n if (size !== null && size > maxBytes) {\n return interpolate(\"File must be smaller than {max} bytes\", params);\n }\n }\n return true;\n});\n\nregisterRule(\"mimeType\", (value, params) => {\n if (isEmpty(value)) return true;\n const allowed = Array.isArray(params.types)\n ? (params.types as string[])\n : typeof params.types === \"string\"\n ? params.types.split(\",\").map((s: string) => s.trim())\n : [];\n const files = Array.isArray(value) ? value : [value];\n for (const file of files) {\n const fileType =\n typeof file === \"object\" && file !== null && \"type\" in file\n ? String((file as { type: unknown }).type)\n : \"\";\n if (fileType && !allowed.some((t) => fileType.match(new RegExp(t.replace(\"*\", \".*\"))))) {\n return interpolate(\"File type is not allowed. Allowed: {types}\", params);\n }\n }\n return true;\n});\n\nregisterRule(\"maxFiles\", (value, params) => {\n if (isEmpty(value)) return true;\n const max = toNumber(params.max) ?? Infinity;\n const count = Array.isArray(value) ? value.length : 1;\n return count <= max || interpolate(\"Maximum {max} files allowed\", params);\n});\n\nregisterRule(\"unique\", (value) => {\n if (isEmpty(value)) return true;\n if (!Array.isArray(value)) return true;\n const set = new Set(value as unknown[]);\n return set.size === value.length || \"All values must be unique\";\n});\n\nregisterRule(\"custom\", (value, params) => {\n // Custom rules are handled at the ValidationRule level via validator fn.\n // This is a no-op fallback.\n void value;\n void params;\n return true;\n});\n","// ---------------------------------------------------------------------------\n// Formatica Core – Condition evaluation\n// ---------------------------------------------------------------------------\n\nimport type { Condition, ConditionGroup, ConditionOperator } from \"./types/schema\";\n\n// ---------------------------------------------------------------------------\n// Single condition evaluation\n// ---------------------------------------------------------------------------\n\nfunction evaluateOperator(\n fieldValue: unknown,\n operator: ConditionOperator,\n conditionValue: unknown,\n): boolean {\n switch (operator) {\n case \"eq\":\n return fieldValue === conditionValue;\n case \"neq\":\n return fieldValue !== conditionValue;\n case \"gt\":\n return Number(fieldValue) > Number(conditionValue);\n case \"gte\":\n return Number(fieldValue) >= Number(conditionValue);\n case \"lt\":\n return Number(fieldValue) < Number(conditionValue);\n case \"lte\":\n return Number(fieldValue) <= Number(conditionValue);\n case \"in\":\n return (\n Array.isArray(conditionValue) && (conditionValue as unknown[]).includes(fieldValue)\n );\n case \"notIn\":\n return (\n Array.isArray(conditionValue) && !(conditionValue as unknown[]).includes(fieldValue)\n );\n case \"contains\": {\n if (typeof fieldValue === \"string\") return fieldValue.includes(String(conditionValue));\n if (Array.isArray(fieldValue))\n return (fieldValue as unknown[]).includes(conditionValue);\n return false;\n }\n case \"empty\":\n return (\n fieldValue === null ||\n fieldValue === undefined ||\n fieldValue === \"\" ||\n (Array.isArray(fieldValue) && fieldValue.length === 0)\n );\n case \"notEmpty\":\n return !(\n fieldValue === null ||\n fieldValue === undefined ||\n fieldValue === \"\" ||\n (Array.isArray(fieldValue) && fieldValue.length === 0)\n );\n case \"matches\": {\n if (typeof conditionValue !== \"string\") return false;\n try {\n return new RegExp(conditionValue).test(String(fieldValue));\n } catch {\n return false;\n }\n }\n default:\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nfunction isConditionGroup(cond: Condition | ConditionGroup): cond is ConditionGroup {\n return \"and\" in cond || \"or\" in cond;\n}\n\n/**\n * Evaluate a single condition or a compound condition group against the\n * current form values.\n */\nexport function evaluateCondition(\n condition: Condition | ConditionGroup,\n values: Record<string, unknown>,\n): boolean {\n if (isConditionGroup(condition)) {\n if (condition.and) {\n return condition.and.every((c) => evaluateCondition(c, values));\n }\n if (condition.or) {\n return condition.or.some((c) => evaluateCondition(c, values));\n }\n // Empty group evaluates to true\n return true;\n }\n\n const fieldValue = values[condition.field];\n return evaluateOperator(fieldValue, condition.operator, condition.value);\n}\n","// ---------------------------------------------------------------------------\n// Formatica Core – Deep merge utility\n// ---------------------------------------------------------------------------\n\ntype PlainObject = Record<string, unknown>;\n\nfunction isPlainObject(value: unknown): value is PlainObject {\n if (typeof value !== \"object\" || value === null) return false;\n const proto = Object.getPrototypeOf(value) as unknown;\n return proto === Object.prototype || proto === null;\n}\n\n/**\n * Deep-merge two objects. Arrays are replaced (not concatenated).\n * Returns a new object – neither source is mutated.\n */\nexport function deepMerge<T extends PlainObject>(target: T, source: Partial<T>): T {\n const result: PlainObject = { ...target };\n\n for (const key of Object.keys(source)) {\n const srcVal = (source as PlainObject)[key];\n const tgtVal = result[key];\n\n if (isPlainObject(srcVal) && isPlainObject(tgtVal)) {\n result[key] = deepMerge(tgtVal, srcVal);\n } else {\n result[key] = srcVal;\n }\n }\n\n return result as T;\n}\n","// ---------------------------------------------------------------------------\n// Formatica Core – Title case conversion utility\n// ---------------------------------------------------------------------------\n\n/**\n * Convert camelCase, kebab-case, or snake_case field names to Title Case.\n *\n * @example\n * titleCase('firstName') // \"First Name\"\n * titleCase('last-name') // \"Last Name\"\n * titleCase('email_address') // \"Email Address\"\n */\nexport function titleCase(input: string): string {\n return (\n input\n // Insert space before uppercase letters in camelCase\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n // Replace kebab-case and snake_case separators with spaces\n .replace(/[-_]+/g, \" \")\n // Capitalize first letter of each word\n .replace(/\\b\\w/g, (char) => char.toUpperCase())\n .trim()\n );\n}\n","// ---------------------------------------------------------------------------\n// Formatica Core – Simple HTML sanitizer for HtmlLayout content\n// ---------------------------------------------------------------------------\n\n/**\n * Strip `<script>` tags and inline event handlers (on*=\"...\") from HTML strings.\n * This is a lightweight sanitizer for use with the HtmlLayout node type.\n */\nexport function sanitizeHtml(html: string): string {\n return (\n html\n // Remove <script> tags and their content\n .replace(/<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi, \"\")\n // Remove event handler attributes (onclick, onload, onerror, etc.)\n .replace(/\\s+on\\w+\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s>]+)/gi, \"\")\n // Remove javascript: protocol in href/src/action attributes\n .replace(/(href|src|action)\\s*=\\s*(?:\"javascript:[^\"]*\"|'javascript:[^']*')/gi, '$1=\"\"')\n );\n}\n"],"names":["extractFields","nodes","fields","node","isFieldNode","step","tab","SchemaValidationError","errors","summary","e","KNOWN_FIELD_TYPES","CONTAINER_TYPES","customFieldTypeChecker","setFieldTypeChecker","checker","isPlainObject","value","validateCondition","condition","fieldPath","allFieldNames","cond","group","children","i","simple","validateFieldNode","field","path","f","validateNode","n","type","validateNodes","basePath","validateSettings","settings","s","parseFormSchema","json","raw","allFields","nameTracker","count","rules","registerRule","name","fn","unregisterRule","getRule","hasRule","isEmpty","interpolate","template","params","_","key","toNumber","str","num","min","max","len","pattern","ctx","target","other","targetField","targetValue","actual","d","before","after","maxBytes","files","file","size","allowed","fileType","t","evaluateOperator","fieldValue","operator","conditionValue","isConditionGroup","evaluateCondition","values","c","proto","deepMerge","source","result","srcVal","tgtVal","titleCase","input","char","sanitizeHtml","html"],"mappings":"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"}
|