@fogpipe/forma-core 0.6.0 → 0.8.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/dist/{chunk-IRLYWN3R.js → chunk-VHKUCCAC.js} +13 -1
- package/dist/{chunk-IRLYWN3R.js.map → chunk-VHKUCCAC.js.map} +1 -1
- package/dist/engine/calculate.d.ts +64 -0
- package/dist/engine/calculate.d.ts.map +1 -0
- package/dist/engine/enabled.d.ts +40 -0
- package/dist/engine/enabled.d.ts.map +1 -0
- package/dist/engine/index.cjs +12 -0
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.ts +12 -255
- package/dist/engine/index.d.ts.map +1 -0
- package/dist/engine/index.js +1 -1
- package/dist/engine/required.d.ts +45 -0
- package/dist/engine/required.d.ts.map +1 -0
- package/dist/engine/validate.d.ts +59 -0
- package/dist/engine/validate.d.ts.map +1 -0
- package/dist/engine/visibility.d.ts +52 -0
- package/dist/engine/visibility.d.ts.map +1 -0
- package/dist/feel/index.d.ts +12 -15
- package/dist/feel/index.d.ts.map +1 -0
- package/dist/index.cjs +12 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -1
- package/dist/{types-Bs3CG9JZ.d.cts → types.d.ts} +31 -30
- package/dist/types.d.ts.map +1 -0
- package/package.json +3 -2
- package/src/__tests__/validate.test.ts +278 -0
- package/src/engine/validate.ts +14 -0
- package/src/index.ts +36 -2
- package/src/types.ts +2 -0
- package/dist/engine/index.d.cts +0 -258
- package/dist/feel/index.d.cts +0 -105
- package/dist/index.d.cts +0 -3
- package/dist/types-Bs3CG9JZ.d.ts +0 -283
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for validation engine
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from "vitest";
|
|
6
|
+
import { validate } from "../engine/validate.js";
|
|
7
|
+
import type { Forma } from "../types.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Helper to create a minimal Forma spec for testing
|
|
11
|
+
*/
|
|
12
|
+
function createTestSpec(options: {
|
|
13
|
+
schemaProperties: Record<string, unknown>;
|
|
14
|
+
fields?: Record<string, unknown>;
|
|
15
|
+
required?: string[];
|
|
16
|
+
}): Forma {
|
|
17
|
+
const { schemaProperties, fields = {}, required = [] } = options;
|
|
18
|
+
|
|
19
|
+
// Build fields from schema if not provided
|
|
20
|
+
const fieldDefs = Object.keys(schemaProperties).reduce(
|
|
21
|
+
(acc, key) => {
|
|
22
|
+
acc[key] = fields[key] || { label: key };
|
|
23
|
+
return acc;
|
|
24
|
+
},
|
|
25
|
+
{} as Record<string, unknown>
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
version: "1.0",
|
|
30
|
+
meta: { id: "test", title: "Test" },
|
|
31
|
+
schema: {
|
|
32
|
+
type: "object",
|
|
33
|
+
properties: schemaProperties as Forma["schema"]["properties"],
|
|
34
|
+
required: required.length > 0 ? required : undefined,
|
|
35
|
+
},
|
|
36
|
+
fields: fieldDefs as Forma["fields"],
|
|
37
|
+
fieldOrder: Object.keys(schemaProperties),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe("validate", () => {
|
|
42
|
+
// ============================================================================
|
|
43
|
+
// multipleOf validation
|
|
44
|
+
// ============================================================================
|
|
45
|
+
|
|
46
|
+
describe("multipleOf", () => {
|
|
47
|
+
it("should pass when value is a multiple of the constraint", () => {
|
|
48
|
+
const spec = createTestSpec({
|
|
49
|
+
schemaProperties: {
|
|
50
|
+
amount: { type: "number", multipleOf: 0.01 },
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const result = validate({ amount: 10.25 }, spec);
|
|
55
|
+
|
|
56
|
+
expect(result.valid).toBe(true);
|
|
57
|
+
expect(result.errors).toHaveLength(0);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("should fail when value is not a multiple of the constraint", () => {
|
|
61
|
+
const spec = createTestSpec({
|
|
62
|
+
schemaProperties: {
|
|
63
|
+
amount: { type: "number", multipleOf: 0.01 },
|
|
64
|
+
},
|
|
65
|
+
fields: { amount: { label: "Amount" } },
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const result = validate({ amount: 10.255 }, spec);
|
|
69
|
+
|
|
70
|
+
expect(result.valid).toBe(false);
|
|
71
|
+
expect(result.errors).toHaveLength(1);
|
|
72
|
+
expect(result.errors[0].field).toBe("amount");
|
|
73
|
+
expect(result.errors[0].message).toBe("Amount must be a multiple of 0.01");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should handle multipleOf for integers", () => {
|
|
77
|
+
const spec = createTestSpec({
|
|
78
|
+
schemaProperties: {
|
|
79
|
+
quantity: { type: "integer", multipleOf: 5 },
|
|
80
|
+
},
|
|
81
|
+
fields: { quantity: { label: "Quantity" } },
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Valid - multiple of 5
|
|
85
|
+
expect(validate({ quantity: 15 }, spec).valid).toBe(true);
|
|
86
|
+
expect(validate({ quantity: 100 }, spec).valid).toBe(true);
|
|
87
|
+
|
|
88
|
+
// Invalid - not multiple of 5
|
|
89
|
+
const result = validate({ quantity: 17 }, spec);
|
|
90
|
+
expect(result.valid).toBe(false);
|
|
91
|
+
expect(result.errors[0].message).toBe("Quantity must be a multiple of 5");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("should handle multipleOf: 0.5 for half-units", () => {
|
|
95
|
+
const spec = createTestSpec({
|
|
96
|
+
schemaProperties: {
|
|
97
|
+
rating: { type: "number", multipleOf: 0.5 },
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Valid half-units
|
|
102
|
+
expect(validate({ rating: 3.5 }, spec).valid).toBe(true);
|
|
103
|
+
expect(validate({ rating: 4.0 }, spec).valid).toBe(true);
|
|
104
|
+
expect(validate({ rating: 0.5 }, spec).valid).toBe(true);
|
|
105
|
+
|
|
106
|
+
// Invalid
|
|
107
|
+
expect(validate({ rating: 3.3 }, spec).valid).toBe(false);
|
|
108
|
+
expect(validate({ rating: 4.25 }, spec).valid).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("should handle floating point precision correctly", () => {
|
|
112
|
+
const spec = createTestSpec({
|
|
113
|
+
schemaProperties: {
|
|
114
|
+
value: { type: "number", multipleOf: 0.1 },
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// These should pass despite floating point precision issues
|
|
119
|
+
// 0.1 + 0.2 = 0.30000000000000004 in JS
|
|
120
|
+
expect(validate({ value: 0.3 }, spec).valid).toBe(true);
|
|
121
|
+
expect(validate({ value: 0.7 }, spec).valid).toBe(true);
|
|
122
|
+
expect(validate({ value: 1.1 }, spec).valid).toBe(true);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("should pass validation when value is null/undefined (empty)", () => {
|
|
126
|
+
const spec = createTestSpec({
|
|
127
|
+
schemaProperties: {
|
|
128
|
+
amount: { type: "number", multipleOf: 0.01 },
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Empty values should not trigger multipleOf validation
|
|
133
|
+
expect(validate({ amount: null }, spec).valid).toBe(true);
|
|
134
|
+
expect(validate({ amount: undefined }, spec).valid).toBe(true);
|
|
135
|
+
expect(validate({}, spec).valid).toBe(true);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("should work with minimum and maximum constraints together", () => {
|
|
139
|
+
const spec = createTestSpec({
|
|
140
|
+
schemaProperties: {
|
|
141
|
+
friction: {
|
|
142
|
+
type: "number",
|
|
143
|
+
minimum: 0.05,
|
|
144
|
+
maximum: 0.3,
|
|
145
|
+
multipleOf: 0.01,
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
fields: { friction: { label: "Friction Coefficient" } },
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Valid - within range and correct precision
|
|
152
|
+
expect(validate({ friction: 0.14 }, spec).valid).toBe(true);
|
|
153
|
+
expect(validate({ friction: 0.05 }, spec).valid).toBe(true);
|
|
154
|
+
expect(validate({ friction: 0.3 }, spec).valid).toBe(true);
|
|
155
|
+
|
|
156
|
+
// Invalid - wrong precision
|
|
157
|
+
const precisionResult = validate({ friction: 0.145 }, spec);
|
|
158
|
+
expect(precisionResult.valid).toBe(false);
|
|
159
|
+
expect(precisionResult.errors[0].message).toContain("multiple of 0.01");
|
|
160
|
+
|
|
161
|
+
// Invalid - below minimum
|
|
162
|
+
const minResult = validate({ friction: 0.01 }, spec);
|
|
163
|
+
expect(minResult.valid).toBe(false);
|
|
164
|
+
expect(minResult.errors[0].message).toContain("at least 0.05");
|
|
165
|
+
|
|
166
|
+
// Invalid - above maximum
|
|
167
|
+
const maxResult = validate({ friction: 0.5 }, spec);
|
|
168
|
+
expect(maxResult.valid).toBe(false);
|
|
169
|
+
expect(maxResult.errors[0].message).toContain("no more than 0.3");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("should handle multipleOf: 1 for whole numbers", () => {
|
|
173
|
+
const spec = createTestSpec({
|
|
174
|
+
schemaProperties: {
|
|
175
|
+
count: { type: "number", multipleOf: 1 },
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
expect(validate({ count: 5 }, spec).valid).toBe(true);
|
|
180
|
+
expect(validate({ count: 100 }, spec).valid).toBe(true);
|
|
181
|
+
expect(validate({ count: 5.5 }, spec).valid).toBe(false);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("should handle very small multipleOf values", () => {
|
|
185
|
+
const spec = createTestSpec({
|
|
186
|
+
schemaProperties: {
|
|
187
|
+
precision: { type: "number", multipleOf: 0.001 },
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
expect(validate({ precision: 1.234 }, spec).valid).toBe(true);
|
|
192
|
+
expect(validate({ precision: 1.2345 }, spec).valid).toBe(false);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("should handle large multipleOf values", () => {
|
|
196
|
+
const spec = createTestSpec({
|
|
197
|
+
schemaProperties: {
|
|
198
|
+
angle: { type: "integer", multipleOf: 15 },
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
expect(validate({ angle: 0 }, spec).valid).toBe(true);
|
|
203
|
+
expect(validate({ angle: 15 }, spec).valid).toBe(true);
|
|
204
|
+
expect(validate({ angle: 90 }, spec).valid).toBe(true);
|
|
205
|
+
expect(validate({ angle: 360 }, spec).valid).toBe(true);
|
|
206
|
+
expect(validate({ angle: 45 }, spec).valid).toBe(true);
|
|
207
|
+
expect(validate({ angle: 17 }, spec).valid).toBe(false);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// ============================================================================
|
|
212
|
+
// Basic type validation (existing behavior)
|
|
213
|
+
// ============================================================================
|
|
214
|
+
|
|
215
|
+
describe("basic type validation", () => {
|
|
216
|
+
it("should validate required fields", () => {
|
|
217
|
+
const spec = createTestSpec({
|
|
218
|
+
schemaProperties: {
|
|
219
|
+
name: { type: "string" },
|
|
220
|
+
},
|
|
221
|
+
required: ["name"],
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const result = validate({ name: "" }, spec);
|
|
225
|
+
|
|
226
|
+
expect(result.valid).toBe(false);
|
|
227
|
+
expect(result.errors[0].message).toContain("required");
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("should validate minimum for numbers", () => {
|
|
231
|
+
const spec = createTestSpec({
|
|
232
|
+
schemaProperties: {
|
|
233
|
+
age: { type: "integer", minimum: 0 },
|
|
234
|
+
},
|
|
235
|
+
fields: { age: { label: "Age" } },
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
expect(validate({ age: 25 }, spec).valid).toBe(true);
|
|
239
|
+
expect(validate({ age: 0 }, spec).valid).toBe(true);
|
|
240
|
+
|
|
241
|
+
const result = validate({ age: -1 }, spec);
|
|
242
|
+
expect(result.valid).toBe(false);
|
|
243
|
+
expect(result.errors[0].message).toBe("Age must be at least 0");
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("should validate maximum for numbers", () => {
|
|
247
|
+
const spec = createTestSpec({
|
|
248
|
+
schemaProperties: {
|
|
249
|
+
percentage: { type: "number", maximum: 100 },
|
|
250
|
+
},
|
|
251
|
+
fields: { percentage: { label: "Percentage" } },
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
expect(validate({ percentage: 50 }, spec).valid).toBe(true);
|
|
255
|
+
expect(validate({ percentage: 100 }, spec).valid).toBe(true);
|
|
256
|
+
|
|
257
|
+
const result = validate({ percentage: 101 }, spec);
|
|
258
|
+
expect(result.valid).toBe(false);
|
|
259
|
+
expect(result.errors[0].message).toBe("Percentage must be no more than 100");
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("should validate string minLength", () => {
|
|
263
|
+
const spec = createTestSpec({
|
|
264
|
+
schemaProperties: {
|
|
265
|
+
name: { type: "string", minLength: 2 },
|
|
266
|
+
},
|
|
267
|
+
fields: { name: { label: "Name" } },
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
expect(validate({ name: "Jo" }, spec).valid).toBe(true);
|
|
271
|
+
expect(validate({ name: "John" }, spec).valid).toBe(true);
|
|
272
|
+
|
|
273
|
+
const result = validate({ name: "J" }, spec);
|
|
274
|
+
expect(result.valid).toBe(false);
|
|
275
|
+
expect(result.errors[0].message).toBe("Name must be at least 2 characters");
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
});
|
package/src/engine/validate.ts
CHANGED
|
@@ -333,6 +333,20 @@ function validateType(
|
|
|
333
333
|
}
|
|
334
334
|
}
|
|
335
335
|
|
|
336
|
+
if ("multipleOf" in schema && schema.multipleOf !== undefined) {
|
|
337
|
+
const multipleOf = schema.multipleOf;
|
|
338
|
+
// Use epsilon comparison to handle floating point precision issues
|
|
339
|
+
const remainder = Math.abs(value % multipleOf);
|
|
340
|
+
const isValid = remainder < 1e-10 || Math.abs(remainder - multipleOf) < 1e-10;
|
|
341
|
+
if (!isValid) {
|
|
342
|
+
return {
|
|
343
|
+
field: path,
|
|
344
|
+
message: `${label} must be a multiple of ${multipleOf}`,
|
|
345
|
+
severity: "error",
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
336
350
|
return null;
|
|
337
351
|
}
|
|
338
352
|
|
package/src/index.ts
CHANGED
|
@@ -5,8 +5,42 @@
|
|
|
5
5
|
* Provides types, FEEL expression evaluation, and form state engines.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
// Types
|
|
9
|
-
export
|
|
8
|
+
// Types (explicit type-only exports)
|
|
9
|
+
export type {
|
|
10
|
+
// FEEL
|
|
11
|
+
FEELExpression,
|
|
12
|
+
// JSON Schema
|
|
13
|
+
JSONSchema,
|
|
14
|
+
JSONSchemaProperty,
|
|
15
|
+
JSONSchemaBase,
|
|
16
|
+
JSONSchemaString,
|
|
17
|
+
JSONSchemaNumber,
|
|
18
|
+
JSONSchemaInteger,
|
|
19
|
+
JSONSchemaBoolean,
|
|
20
|
+
JSONSchemaArray,
|
|
21
|
+
JSONSchemaObject,
|
|
22
|
+
JSONSchemaEnum,
|
|
23
|
+
// Field types
|
|
24
|
+
FieldType,
|
|
25
|
+
ValidationRule,
|
|
26
|
+
SelectOption,
|
|
27
|
+
FieldDefinition,
|
|
28
|
+
ComputedField,
|
|
29
|
+
PageDefinition,
|
|
30
|
+
FormMeta,
|
|
31
|
+
Forma,
|
|
32
|
+
// Evaluation
|
|
33
|
+
EvaluationContext,
|
|
34
|
+
// Results
|
|
35
|
+
VisibilityResult,
|
|
36
|
+
RequiredResult,
|
|
37
|
+
RequiredFieldsResult,
|
|
38
|
+
EnabledResult,
|
|
39
|
+
FieldError,
|
|
40
|
+
ValidationResult,
|
|
41
|
+
CalculationError,
|
|
42
|
+
CalculationResult,
|
|
43
|
+
} from "./types.js";
|
|
10
44
|
|
|
11
45
|
// FEEL expression evaluation
|
|
12
46
|
export * from "./feel/index.js";
|
package/src/types.ts
CHANGED
|
@@ -71,12 +71,14 @@ export interface JSONSchemaNumber extends JSONSchemaBase {
|
|
|
71
71
|
maximum?: number;
|
|
72
72
|
exclusiveMinimum?: number;
|
|
73
73
|
exclusiveMaximum?: number;
|
|
74
|
+
multipleOf?: number;
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
export interface JSONSchemaInteger extends JSONSchemaBase {
|
|
77
78
|
type: "integer";
|
|
78
79
|
minimum?: number;
|
|
79
80
|
maximum?: number;
|
|
81
|
+
multipleOf?: number;
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
export interface JSONSchemaBoolean extends JSONSchemaBase {
|
package/dist/engine/index.d.cts
DELETED
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
import { m as Forma, t as CalculationResult, n as VisibilityResult, o as RequiredFieldsResult, p as EnabledResult, r as ValidationResult, q as FieldError } from '../types-Bs3CG9JZ.cjs';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Calculation Engine
|
|
5
|
-
*
|
|
6
|
-
* Evaluates computed fields based on form data.
|
|
7
|
-
* Computed values are derived from form data using FEEL expressions.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Calculate all computed values from form data
|
|
12
|
-
*
|
|
13
|
-
* Evaluates each computed field's FEEL expression and returns the results.
|
|
14
|
-
* Errors are collected rather than thrown, allowing partial results.
|
|
15
|
-
*
|
|
16
|
-
* @param data - Current form data
|
|
17
|
-
* @param spec - Form specification with computed fields
|
|
18
|
-
* @returns Computed values and any calculation errors
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* const spec = {
|
|
22
|
-
* computed: {
|
|
23
|
-
* bmi: {
|
|
24
|
-
* expression: "weight / (height / 100) ** 2",
|
|
25
|
-
* label: "BMI",
|
|
26
|
-
* format: "decimal(1)"
|
|
27
|
-
* },
|
|
28
|
-
* isObese: {
|
|
29
|
-
* expression: "$computed.bmi >= 30"
|
|
30
|
-
* }
|
|
31
|
-
* }
|
|
32
|
-
* };
|
|
33
|
-
*
|
|
34
|
-
* const result = calculate({ weight: 85, height: 175 }, spec);
|
|
35
|
-
* // => { values: { bmi: 27.76, isObese: false }, errors: [] }
|
|
36
|
-
*/
|
|
37
|
-
declare function calculate(data: Record<string, unknown>, spec: Forma): Record<string, unknown>;
|
|
38
|
-
/**
|
|
39
|
-
* Calculate computed values with error reporting
|
|
40
|
-
*
|
|
41
|
-
* Same as calculate() but also returns any errors that occurred.
|
|
42
|
-
*
|
|
43
|
-
* @param data - Current form data
|
|
44
|
-
* @param spec - Form specification
|
|
45
|
-
* @returns Values and errors
|
|
46
|
-
*/
|
|
47
|
-
declare function calculateWithErrors(data: Record<string, unknown>, spec: Forma): CalculationResult;
|
|
48
|
-
/**
|
|
49
|
-
* Get a computed value formatted according to its format specification
|
|
50
|
-
*
|
|
51
|
-
* @param fieldName - Name of the computed field
|
|
52
|
-
* @param data - Current form data
|
|
53
|
-
* @param spec - Form specification
|
|
54
|
-
* @returns Formatted string or null if not displayable
|
|
55
|
-
*/
|
|
56
|
-
declare function getFormattedValue(fieldName: string, data: Record<string, unknown>, spec: Forma): string | null;
|
|
57
|
-
/**
|
|
58
|
-
* Calculate a single computed field
|
|
59
|
-
*
|
|
60
|
-
* @param fieldName - Name of the computed field
|
|
61
|
-
* @param data - Current form data
|
|
62
|
-
* @param spec - Form specification
|
|
63
|
-
* @returns Computed value or null if calculation failed
|
|
64
|
-
*/
|
|
65
|
-
declare function calculateField(fieldName: string, data: Record<string, unknown>, spec: Forma): unknown;
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Visibility Engine
|
|
69
|
-
*
|
|
70
|
-
* Determines which fields should be visible based on form data
|
|
71
|
-
* and Forma visibility rules.
|
|
72
|
-
*/
|
|
73
|
-
|
|
74
|
-
interface VisibilityOptions {
|
|
75
|
-
/** Pre-calculated computed values (avoids recalculation) */
|
|
76
|
-
computed?: Record<string, unknown>;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Determine visibility for all fields in a form
|
|
80
|
-
*
|
|
81
|
-
* Returns a map of field paths to boolean visibility states.
|
|
82
|
-
* Fields without visibleWhen expressions are always visible.
|
|
83
|
-
*
|
|
84
|
-
* @param data - Current form data
|
|
85
|
-
* @param spec - Form specification
|
|
86
|
-
* @param options - Optional pre-calculated computed values
|
|
87
|
-
* @returns Map of field paths to visibility states
|
|
88
|
-
*
|
|
89
|
-
* @example
|
|
90
|
-
* const visibility = getVisibility(
|
|
91
|
-
* { age: 21, hasLicense: true },
|
|
92
|
-
* forma
|
|
93
|
-
* );
|
|
94
|
-
* // => { age: true, hasLicense: true, vehicleType: true, ... }
|
|
95
|
-
*/
|
|
96
|
-
declare function getVisibility(data: Record<string, unknown>, spec: Forma, options?: VisibilityOptions): VisibilityResult;
|
|
97
|
-
/**
|
|
98
|
-
* Check if a single field is visible
|
|
99
|
-
*
|
|
100
|
-
* Useful for checking visibility of one field without computing all.
|
|
101
|
-
*
|
|
102
|
-
* @param fieldPath - Field path to check
|
|
103
|
-
* @param data - Current form data
|
|
104
|
-
* @param spec - Form specification
|
|
105
|
-
* @param options - Optional pre-calculated computed values
|
|
106
|
-
* @returns True if the field is visible
|
|
107
|
-
*/
|
|
108
|
-
declare function isFieldVisible(fieldPath: string, data: Record<string, unknown>, spec: Forma, options?: VisibilityOptions): boolean;
|
|
109
|
-
/**
|
|
110
|
-
* Determine which pages are visible in a wizard form
|
|
111
|
-
*
|
|
112
|
-
* @param data - Current form data
|
|
113
|
-
* @param spec - Form specification with pages
|
|
114
|
-
* @param options - Optional pre-calculated computed values
|
|
115
|
-
* @returns Map of page IDs to visibility states
|
|
116
|
-
*/
|
|
117
|
-
declare function getPageVisibility(data: Record<string, unknown>, spec: Forma, options?: VisibilityOptions): Record<string, boolean>;
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Required Fields Engine
|
|
121
|
-
*
|
|
122
|
-
* Determines which fields are currently required based on
|
|
123
|
-
* conditional requiredWhen expressions and schema required array.
|
|
124
|
-
*/
|
|
125
|
-
|
|
126
|
-
interface RequiredOptions {
|
|
127
|
-
/** Pre-calculated computed values */
|
|
128
|
-
computed?: Record<string, unknown>;
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Determine which fields are currently required
|
|
132
|
-
*
|
|
133
|
-
* Returns a map of field paths to boolean required states.
|
|
134
|
-
* Evaluates requiredWhen expressions for conditional requirements.
|
|
135
|
-
*
|
|
136
|
-
* @param data - Current form data
|
|
137
|
-
* @param spec - Form specification
|
|
138
|
-
* @param options - Optional pre-calculated computed values
|
|
139
|
-
* @returns Map of field paths to required states
|
|
140
|
-
*
|
|
141
|
-
* @example
|
|
142
|
-
* const required = getRequired(
|
|
143
|
-
* { hasInsurance: true },
|
|
144
|
-
* forma
|
|
145
|
-
* );
|
|
146
|
-
* // => { hasInsurance: true, insuranceProvider: true, policyNumber: true }
|
|
147
|
-
*/
|
|
148
|
-
declare function getRequired(data: Record<string, unknown>, spec: Forma, options?: RequiredOptions): RequiredFieldsResult;
|
|
149
|
-
/**
|
|
150
|
-
* Check if a single field is currently required
|
|
151
|
-
*
|
|
152
|
-
* @param fieldPath - Path to the field
|
|
153
|
-
* @param data - Current form data
|
|
154
|
-
* @param spec - Form specification
|
|
155
|
-
* @returns True if the field is required
|
|
156
|
-
*/
|
|
157
|
-
declare function isRequired(fieldPath: string, data: Record<string, unknown>, spec: Forma): boolean;
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Enabled Fields Engine
|
|
161
|
-
*
|
|
162
|
-
* Determines which fields are currently enabled (editable) based on
|
|
163
|
-
* conditional enabledWhen expressions.
|
|
164
|
-
*/
|
|
165
|
-
|
|
166
|
-
interface EnabledOptions {
|
|
167
|
-
/** Pre-calculated computed values */
|
|
168
|
-
computed?: Record<string, unknown>;
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Determine which fields are currently enabled (editable)
|
|
172
|
-
*
|
|
173
|
-
* Returns a map of field paths to boolean enabled states.
|
|
174
|
-
* Fields without enabledWhen expressions are always enabled.
|
|
175
|
-
*
|
|
176
|
-
* @param data - Current form data
|
|
177
|
-
* @param spec - Form specification
|
|
178
|
-
* @param options - Optional pre-calculated computed values
|
|
179
|
-
* @returns Map of field paths to enabled states
|
|
180
|
-
*
|
|
181
|
-
* @example
|
|
182
|
-
* const enabled = getEnabled(
|
|
183
|
-
* { isLocked: true },
|
|
184
|
-
* forma
|
|
185
|
-
* );
|
|
186
|
-
* // => { isLocked: true, lockedField: false, ... }
|
|
187
|
-
*/
|
|
188
|
-
declare function getEnabled(data: Record<string, unknown>, spec: Forma, options?: EnabledOptions): EnabledResult;
|
|
189
|
-
/**
|
|
190
|
-
* Check if a single field is currently enabled
|
|
191
|
-
*
|
|
192
|
-
* @param fieldPath - Path to the field
|
|
193
|
-
* @param data - Current form data
|
|
194
|
-
* @param spec - Form specification
|
|
195
|
-
* @returns True if the field is enabled
|
|
196
|
-
*/
|
|
197
|
-
declare function isEnabled(fieldPath: string, data: Record<string, unknown>, spec: Forma): boolean;
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Validation Engine
|
|
201
|
-
*
|
|
202
|
-
* Validates form data against Forma rules including:
|
|
203
|
-
* - JSON Schema type validation
|
|
204
|
-
* - Required field validation (with conditional requiredWhen)
|
|
205
|
-
* - Custom FEEL validation rules
|
|
206
|
-
* - Array item validation
|
|
207
|
-
*/
|
|
208
|
-
|
|
209
|
-
interface ValidateOptions {
|
|
210
|
-
/** Pre-calculated computed values */
|
|
211
|
-
computed?: Record<string, unknown>;
|
|
212
|
-
/** Pre-calculated visibility */
|
|
213
|
-
visibility?: Record<string, boolean>;
|
|
214
|
-
/** Only validate visible fields (default: true) */
|
|
215
|
-
onlyVisible?: boolean;
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* Validate form data against a Forma
|
|
219
|
-
*
|
|
220
|
-
* Performs comprehensive validation including:
|
|
221
|
-
* - Required field checks (respecting conditional requiredWhen)
|
|
222
|
-
* - JSON Schema type validation
|
|
223
|
-
* - Custom FEEL validation rules
|
|
224
|
-
* - Array min/max items validation
|
|
225
|
-
* - Array item field validation
|
|
226
|
-
*
|
|
227
|
-
* By default, only visible fields are validated.
|
|
228
|
-
*
|
|
229
|
-
* @param data - Current form data
|
|
230
|
-
* @param spec - Form specification
|
|
231
|
-
* @param options - Validation options
|
|
232
|
-
* @returns Validation result with valid flag and errors array
|
|
233
|
-
*
|
|
234
|
-
* @example
|
|
235
|
-
* const result = validate(
|
|
236
|
-
* { name: "", age: 15 },
|
|
237
|
-
* forma
|
|
238
|
-
* );
|
|
239
|
-
* // => {
|
|
240
|
-
* // valid: false,
|
|
241
|
-
* // errors: [
|
|
242
|
-
* // { field: "name", message: "Name is required", severity: "error" },
|
|
243
|
-
* // { field: "age", message: "Must be 18 or older", severity: "error" }
|
|
244
|
-
* // ]
|
|
245
|
-
* // }
|
|
246
|
-
*/
|
|
247
|
-
declare function validate(data: Record<string, unknown>, spec: Forma, options?: ValidateOptions): ValidationResult;
|
|
248
|
-
/**
|
|
249
|
-
* Validate a single field
|
|
250
|
-
*
|
|
251
|
-
* @param fieldPath - Path to the field
|
|
252
|
-
* @param data - Current form data
|
|
253
|
-
* @param spec - Form specification
|
|
254
|
-
* @returns Array of errors for this field
|
|
255
|
-
*/
|
|
256
|
-
declare function validateSingleField(fieldPath: string, data: Record<string, unknown>, spec: Forma): FieldError[];
|
|
257
|
-
|
|
258
|
-
export { type EnabledOptions, type RequiredOptions, type ValidateOptions, type VisibilityOptions, calculate, calculateField, calculateWithErrors, getEnabled, getFormattedValue, getPageVisibility, getRequired, getVisibility, isEnabled, isFieldVisible, isRequired, validate, validateSingleField };
|