@fogpipe/forma-core 0.6.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.
@@ -0,0 +1,790 @@
1
+ import {
2
+ evaluate,
3
+ evaluateBoolean
4
+ } from "./chunk-QTLXVG6P.js";
5
+
6
+ // src/engine/calculate.ts
7
+ function calculate(data, spec) {
8
+ const result = calculateWithErrors(data, spec);
9
+ return result.values;
10
+ }
11
+ function calculateWithErrors(data, spec) {
12
+ if (!spec.computed) {
13
+ return { values: {}, errors: [] };
14
+ }
15
+ const values = {};
16
+ const errors = [];
17
+ const orderedFields = getComputationOrder(spec.computed);
18
+ for (const fieldName of orderedFields) {
19
+ const fieldDef = spec.computed[fieldName];
20
+ if (!fieldDef) continue;
21
+ const result = evaluateComputedField(
22
+ fieldName,
23
+ fieldDef,
24
+ data,
25
+ values,
26
+ // Pass already-computed values for dependencies
27
+ spec.referenceData
28
+ // Pass reference data for lookups
29
+ );
30
+ if (result.success) {
31
+ values[fieldName] = result.value;
32
+ } else {
33
+ errors.push({
34
+ field: fieldName,
35
+ message: result.error,
36
+ expression: fieldDef.expression
37
+ });
38
+ values[fieldName] = null;
39
+ }
40
+ }
41
+ return { values, errors };
42
+ }
43
+ function evaluateComputedField(_name, fieldDef, data, computedSoFar, referenceData) {
44
+ const referencedComputed = findComputedReferences(fieldDef.expression);
45
+ for (const ref of referencedComputed) {
46
+ if (computedSoFar[ref] === null) {
47
+ return {
48
+ success: true,
49
+ value: null
50
+ };
51
+ }
52
+ }
53
+ const context = {
54
+ data,
55
+ computed: computedSoFar,
56
+ referenceData
57
+ };
58
+ const result = evaluate(fieldDef.expression, context);
59
+ if (!result.success) {
60
+ return {
61
+ success: false,
62
+ error: result.error
63
+ };
64
+ }
65
+ if (typeof result.value === "number" && !Number.isFinite(result.value)) {
66
+ return {
67
+ success: true,
68
+ value: null
69
+ };
70
+ }
71
+ return {
72
+ success: true,
73
+ value: result.value
74
+ };
75
+ }
76
+ function findComputedReferences(expression) {
77
+ const refs = [];
78
+ const regex = /computed\.(\w+)/g;
79
+ let match;
80
+ while ((match = regex.exec(expression)) !== null) {
81
+ refs.push(match[1]);
82
+ }
83
+ return refs;
84
+ }
85
+ function getComputationOrder(computed) {
86
+ const fieldNames = Object.keys(computed);
87
+ const deps = /* @__PURE__ */ new Map();
88
+ for (const name of fieldNames) {
89
+ deps.set(name, findComputedDependencies(computed[name].expression, fieldNames));
90
+ }
91
+ const sorted = [];
92
+ const visited = /* @__PURE__ */ new Set();
93
+ const visiting = /* @__PURE__ */ new Set();
94
+ function visit(name) {
95
+ if (visited.has(name)) return;
96
+ if (visiting.has(name)) {
97
+ console.warn(`Circular dependency detected in computed field: ${name}`);
98
+ sorted.push(name);
99
+ visited.add(name);
100
+ return;
101
+ }
102
+ visiting.add(name);
103
+ const fieldDeps = deps.get(name) ?? /* @__PURE__ */ new Set();
104
+ for (const dep of fieldDeps) {
105
+ visit(dep);
106
+ }
107
+ visiting.delete(name);
108
+ visited.add(name);
109
+ sorted.push(name);
110
+ }
111
+ for (const name of fieldNames) {
112
+ visit(name);
113
+ }
114
+ return sorted;
115
+ }
116
+ function findComputedDependencies(expression, availableFields) {
117
+ const deps = /* @__PURE__ */ new Set();
118
+ const regex = /computed\.(\w+)/g;
119
+ let match;
120
+ while ((match = regex.exec(expression)) !== null) {
121
+ const fieldName = match[1];
122
+ if (availableFields.includes(fieldName)) {
123
+ deps.add(fieldName);
124
+ }
125
+ }
126
+ return deps;
127
+ }
128
+ function getFormattedValue(fieldName, data, spec) {
129
+ var _a;
130
+ if (!((_a = spec.computed) == null ? void 0 : _a[fieldName])) {
131
+ return null;
132
+ }
133
+ const fieldDef = spec.computed[fieldName];
134
+ const computed = calculate(data, spec);
135
+ const value = computed[fieldName];
136
+ if (value === null || value === void 0) {
137
+ return null;
138
+ }
139
+ return formatValue(value, fieldDef.format);
140
+ }
141
+ function formatValue(value, format) {
142
+ if (!format) {
143
+ return String(value);
144
+ }
145
+ const decimalMatch = format.match(/^decimal\((\d+)\)$/);
146
+ if (decimalMatch) {
147
+ const decimals = parseInt(decimalMatch[1], 10);
148
+ return typeof value === "number" ? value.toFixed(decimals) : String(value);
149
+ }
150
+ if (format === "currency") {
151
+ return typeof value === "number" ? new Intl.NumberFormat("en-US", {
152
+ style: "currency",
153
+ currency: "USD"
154
+ }).format(value) : String(value);
155
+ }
156
+ if (format === "percent") {
157
+ return typeof value === "number" ? new Intl.NumberFormat("en-US", {
158
+ style: "percent",
159
+ minimumFractionDigits: 1
160
+ }).format(value) : String(value);
161
+ }
162
+ return String(value);
163
+ }
164
+ function calculateField(fieldName, data, spec) {
165
+ const computed = calculate(data, spec);
166
+ return computed[fieldName] ?? null;
167
+ }
168
+
169
+ // src/engine/visibility.ts
170
+ function getVisibility(data, spec, options = {}) {
171
+ const computed = options.computed ?? calculate(data, spec);
172
+ const baseContext = {
173
+ data,
174
+ computed,
175
+ referenceData: spec.referenceData
176
+ };
177
+ const result = {};
178
+ for (const fieldPath of spec.fieldOrder) {
179
+ const fieldDef = spec.fields[fieldPath];
180
+ if (fieldDef) {
181
+ evaluateFieldVisibility(fieldPath, fieldDef, data, baseContext, result);
182
+ }
183
+ }
184
+ return result;
185
+ }
186
+ function evaluateFieldVisibility(path, fieldDef, data, context, result) {
187
+ if (fieldDef.visibleWhen) {
188
+ result[path] = evaluateBoolean(fieldDef.visibleWhen, context);
189
+ } else {
190
+ result[path] = true;
191
+ }
192
+ if (!result[path]) {
193
+ return;
194
+ }
195
+ if (fieldDef.itemFields) {
196
+ const arrayData = data[path];
197
+ if (Array.isArray(arrayData)) {
198
+ evaluateArrayItemVisibility(path, fieldDef, arrayData, context, result);
199
+ }
200
+ }
201
+ }
202
+ function evaluateArrayItemVisibility(arrayPath, fieldDef, arrayData, baseContext, result) {
203
+ if (!fieldDef.itemFields) return;
204
+ for (let i = 0; i < arrayData.length; i++) {
205
+ const item = arrayData[i];
206
+ const itemContext = {
207
+ ...baseContext,
208
+ item,
209
+ itemIndex: i
210
+ };
211
+ for (const [fieldName, itemFieldDef] of Object.entries(fieldDef.itemFields)) {
212
+ const itemFieldPath = `${arrayPath}[${i}].${fieldName}`;
213
+ if (itemFieldDef.visibleWhen) {
214
+ result[itemFieldPath] = evaluateBoolean(itemFieldDef.visibleWhen, itemContext);
215
+ } else {
216
+ result[itemFieldPath] = true;
217
+ }
218
+ }
219
+ }
220
+ }
221
+ function isFieldVisible(fieldPath, data, spec, options = {}) {
222
+ const fieldDef = spec.fields[fieldPath];
223
+ if (!fieldDef) {
224
+ return true;
225
+ }
226
+ if (!fieldDef.visibleWhen) {
227
+ return true;
228
+ }
229
+ const computed = options.computed ?? calculate(data, spec);
230
+ const context = {
231
+ data,
232
+ computed,
233
+ referenceData: spec.referenceData
234
+ };
235
+ return evaluateBoolean(fieldDef.visibleWhen, context);
236
+ }
237
+ function getPageVisibility(data, spec, options = {}) {
238
+ if (!spec.pages) {
239
+ return {};
240
+ }
241
+ const computed = options.computed ?? calculate(data, spec);
242
+ const context = {
243
+ data,
244
+ computed,
245
+ referenceData: spec.referenceData
246
+ };
247
+ const result = {};
248
+ for (const page of spec.pages) {
249
+ if (page.visibleWhen) {
250
+ result[page.id] = evaluateBoolean(page.visibleWhen, context);
251
+ } else {
252
+ result[page.id] = true;
253
+ }
254
+ }
255
+ return result;
256
+ }
257
+
258
+ // src/engine/required.ts
259
+ function getRequired(data, spec, options = {}) {
260
+ const computed = options.computed ?? calculate(data, spec);
261
+ const context = {
262
+ data,
263
+ computed,
264
+ referenceData: spec.referenceData
265
+ };
266
+ const result = {};
267
+ for (const fieldPath of spec.fieldOrder) {
268
+ const fieldDef = spec.fields[fieldPath];
269
+ if (fieldDef) {
270
+ result[fieldPath] = isFieldRequired(fieldPath, fieldDef, spec, context);
271
+ }
272
+ }
273
+ for (const [fieldPath, fieldDef] of Object.entries(spec.fields)) {
274
+ if (fieldDef.itemFields) {
275
+ const arrayData = data[fieldPath];
276
+ if (Array.isArray(arrayData)) {
277
+ for (let i = 0; i < arrayData.length; i++) {
278
+ const item = arrayData[i];
279
+ const itemContext = {
280
+ data,
281
+ computed,
282
+ referenceData: spec.referenceData,
283
+ item,
284
+ itemIndex: i
285
+ };
286
+ for (const [itemFieldName, itemFieldDef] of Object.entries(fieldDef.itemFields)) {
287
+ const itemFieldPath = `${fieldPath}[${i}].${itemFieldName}`;
288
+ result[itemFieldPath] = isFieldRequired(
289
+ itemFieldPath,
290
+ itemFieldDef,
291
+ spec,
292
+ itemContext
293
+ );
294
+ }
295
+ }
296
+ }
297
+ }
298
+ }
299
+ return result;
300
+ }
301
+ function isFieldRequired(fieldPath, fieldDef, spec, context) {
302
+ var _a;
303
+ if (fieldDef.requiredWhen) {
304
+ return evaluateBoolean(fieldDef.requiredWhen, context);
305
+ }
306
+ return ((_a = spec.schema.required) == null ? void 0 : _a.includes(fieldPath)) ?? false;
307
+ }
308
+ function isRequired(fieldPath, data, spec) {
309
+ var _a;
310
+ const fieldDef = spec.fields[fieldPath];
311
+ if (!fieldDef) {
312
+ return ((_a = spec.schema.required) == null ? void 0 : _a.includes(fieldPath)) ?? false;
313
+ }
314
+ const computed = calculate(data, spec);
315
+ const context = {
316
+ data,
317
+ computed,
318
+ referenceData: spec.referenceData
319
+ };
320
+ return isFieldRequired(fieldPath, fieldDef, spec, context);
321
+ }
322
+
323
+ // src/engine/enabled.ts
324
+ function getEnabled(data, spec, options = {}) {
325
+ const computed = options.computed ?? calculate(data, spec);
326
+ const context = {
327
+ data,
328
+ computed,
329
+ referenceData: spec.referenceData
330
+ };
331
+ const result = {};
332
+ for (const fieldPath of spec.fieldOrder) {
333
+ const fieldDef = spec.fields[fieldPath];
334
+ if (fieldDef) {
335
+ result[fieldPath] = isFieldEnabled(fieldDef, context);
336
+ }
337
+ }
338
+ for (const [fieldPath, fieldDef] of Object.entries(spec.fields)) {
339
+ if (fieldDef.itemFields) {
340
+ const arrayData = data[fieldPath];
341
+ if (Array.isArray(arrayData)) {
342
+ for (let i = 0; i < arrayData.length; i++) {
343
+ const item = arrayData[i];
344
+ const itemContext = {
345
+ data,
346
+ computed,
347
+ referenceData: spec.referenceData,
348
+ item,
349
+ itemIndex: i
350
+ };
351
+ for (const [itemFieldName, itemFieldDef] of Object.entries(fieldDef.itemFields)) {
352
+ const itemFieldPath = `${fieldPath}[${i}].${itemFieldName}`;
353
+ result[itemFieldPath] = isFieldEnabled(itemFieldDef, itemContext);
354
+ }
355
+ }
356
+ }
357
+ }
358
+ }
359
+ return result;
360
+ }
361
+ function isFieldEnabled(fieldDef, context) {
362
+ if (fieldDef.enabledWhen) {
363
+ return evaluateBoolean(fieldDef.enabledWhen, context);
364
+ }
365
+ return true;
366
+ }
367
+ function isEnabled(fieldPath, data, spec) {
368
+ const fieldDef = spec.fields[fieldPath];
369
+ if (!fieldDef) {
370
+ return true;
371
+ }
372
+ if (!fieldDef.enabledWhen) {
373
+ return true;
374
+ }
375
+ const computed = calculate(data, spec);
376
+ const context = {
377
+ data,
378
+ computed,
379
+ referenceData: spec.referenceData
380
+ };
381
+ return evaluateBoolean(fieldDef.enabledWhen, context);
382
+ }
383
+
384
+ // src/engine/validate.ts
385
+ function validate(data, spec, options = {}) {
386
+ const { onlyVisible = true } = options;
387
+ const computed = options.computed ?? calculate(data, spec);
388
+ const visibility = options.visibility ?? getVisibility(data, spec, { computed });
389
+ const errors = [];
390
+ for (const fieldPath of spec.fieldOrder) {
391
+ const fieldDef = spec.fields[fieldPath];
392
+ if (!fieldDef) continue;
393
+ if (onlyVisible && visibility[fieldPath] === false) {
394
+ continue;
395
+ }
396
+ const schemaProperty = spec.schema.properties[fieldPath];
397
+ const fieldErrors = validateField(
398
+ fieldPath,
399
+ data[fieldPath],
400
+ fieldDef,
401
+ schemaProperty,
402
+ spec,
403
+ data,
404
+ computed,
405
+ visibility,
406
+ onlyVisible
407
+ );
408
+ errors.push(...fieldErrors);
409
+ }
410
+ return {
411
+ valid: errors.filter((e) => e.severity === "error").length === 0,
412
+ errors
413
+ };
414
+ }
415
+ function validateField(path, value, fieldDef, schemaProperty, spec, data, computed, visibility, onlyVisible) {
416
+ const errors = [];
417
+ const context = {
418
+ data,
419
+ computed,
420
+ referenceData: spec.referenceData,
421
+ value
422
+ };
423
+ const required = isFieldRequired(path, fieldDef, spec, context);
424
+ if (required && isEmpty(value)) {
425
+ errors.push({
426
+ field: path,
427
+ message: fieldDef.label ? `${fieldDef.label} is required` : "This field is required",
428
+ severity: "error"
429
+ });
430
+ }
431
+ if (!isEmpty(value) && schemaProperty) {
432
+ const typeError = validateType(path, value, schemaProperty, fieldDef);
433
+ if (typeError) {
434
+ errors.push(typeError);
435
+ }
436
+ }
437
+ if (fieldDef.validations && !isEmpty(value)) {
438
+ const customErrors = validateCustomRules(path, fieldDef.validations, context);
439
+ errors.push(...customErrors);
440
+ }
441
+ if (Array.isArray(value) && fieldDef.itemFields) {
442
+ const arrayErrors = validateArray(
443
+ path,
444
+ value,
445
+ fieldDef,
446
+ spec,
447
+ data,
448
+ computed,
449
+ visibility,
450
+ onlyVisible
451
+ );
452
+ errors.push(...arrayErrors);
453
+ }
454
+ return errors;
455
+ }
456
+ function isEmpty(value) {
457
+ if (value === null || value === void 0) return true;
458
+ if (typeof value === "string" && value.trim() === "") return true;
459
+ if (Array.isArray(value) && value.length === 0) return true;
460
+ return false;
461
+ }
462
+ function validateType(path, value, schema, fieldDef) {
463
+ const label = fieldDef.label ?? path;
464
+ switch (schema.type) {
465
+ case "string": {
466
+ if (typeof value !== "string") {
467
+ return {
468
+ field: path,
469
+ message: `${label} must be a string`,
470
+ severity: "error"
471
+ };
472
+ }
473
+ if ("minLength" in schema && schema.minLength !== void 0) {
474
+ if (value.length < schema.minLength) {
475
+ return {
476
+ field: path,
477
+ message: `${label} must be at least ${schema.minLength} characters`,
478
+ severity: "error"
479
+ };
480
+ }
481
+ }
482
+ if ("maxLength" in schema && schema.maxLength !== void 0) {
483
+ if (value.length > schema.maxLength) {
484
+ return {
485
+ field: path,
486
+ message: `${label} must be no more than ${schema.maxLength} characters`,
487
+ severity: "error"
488
+ };
489
+ }
490
+ }
491
+ if ("pattern" in schema && schema.pattern) {
492
+ const regex = new RegExp(schema.pattern);
493
+ if (!regex.test(value)) {
494
+ return {
495
+ field: path,
496
+ message: `${label} format is invalid`,
497
+ severity: "error"
498
+ };
499
+ }
500
+ }
501
+ if ("enum" in schema && schema.enum) {
502
+ if (!schema.enum.includes(value)) {
503
+ return {
504
+ field: path,
505
+ message: `${label} must be one of: ${schema.enum.join(", ")}`,
506
+ severity: "error"
507
+ };
508
+ }
509
+ }
510
+ if ("format" in schema && schema.format) {
511
+ const formatError = validateFormat(path, value, schema.format, label);
512
+ if (formatError) return formatError;
513
+ }
514
+ return null;
515
+ }
516
+ case "number":
517
+ case "integer": {
518
+ if (typeof value !== "number") {
519
+ return {
520
+ field: path,
521
+ message: `${label} must be a number`,
522
+ severity: "error"
523
+ };
524
+ }
525
+ if (schema.type === "integer" && !Number.isInteger(value)) {
526
+ return {
527
+ field: path,
528
+ message: `${label} must be a whole number`,
529
+ severity: "error"
530
+ };
531
+ }
532
+ if ("minimum" in schema && schema.minimum !== void 0) {
533
+ if (value < schema.minimum) {
534
+ return {
535
+ field: path,
536
+ message: `${label} must be at least ${schema.minimum}`,
537
+ severity: "error"
538
+ };
539
+ }
540
+ }
541
+ if ("maximum" in schema && schema.maximum !== void 0) {
542
+ if (value > schema.maximum) {
543
+ return {
544
+ field: path,
545
+ message: `${label} must be no more than ${schema.maximum}`,
546
+ severity: "error"
547
+ };
548
+ }
549
+ }
550
+ if ("exclusiveMinimum" in schema && schema.exclusiveMinimum !== void 0) {
551
+ if (value <= schema.exclusiveMinimum) {
552
+ return {
553
+ field: path,
554
+ message: `${label} must be greater than ${schema.exclusiveMinimum}`,
555
+ severity: "error"
556
+ };
557
+ }
558
+ }
559
+ if ("exclusiveMaximum" in schema && schema.exclusiveMaximum !== void 0) {
560
+ if (value >= schema.exclusiveMaximum) {
561
+ return {
562
+ field: path,
563
+ message: `${label} must be less than ${schema.exclusiveMaximum}`,
564
+ severity: "error"
565
+ };
566
+ }
567
+ }
568
+ return null;
569
+ }
570
+ case "boolean": {
571
+ if (typeof value !== "boolean") {
572
+ return {
573
+ field: path,
574
+ message: `${label} must be true or false`,
575
+ severity: "error"
576
+ };
577
+ }
578
+ return null;
579
+ }
580
+ case "array": {
581
+ if (!Array.isArray(value)) {
582
+ return {
583
+ field: path,
584
+ message: `${label} must be a list`,
585
+ severity: "error"
586
+ };
587
+ }
588
+ return null;
589
+ }
590
+ case "object": {
591
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
592
+ return {
593
+ field: path,
594
+ message: `${label} must be an object`,
595
+ severity: "error"
596
+ };
597
+ }
598
+ return null;
599
+ }
600
+ default:
601
+ return null;
602
+ }
603
+ }
604
+ function validateFormat(path, value, format, label) {
605
+ switch (format) {
606
+ case "email": {
607
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
608
+ if (!emailRegex.test(value)) {
609
+ return {
610
+ field: path,
611
+ message: `${label} must be a valid email address`,
612
+ severity: "error"
613
+ };
614
+ }
615
+ return null;
616
+ }
617
+ case "date": {
618
+ const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
619
+ if (!dateRegex.test(value)) {
620
+ return {
621
+ field: path,
622
+ message: `${label} must be a valid date`,
623
+ severity: "error"
624
+ };
625
+ }
626
+ const parsed = /* @__PURE__ */ new Date(value + "T00:00:00Z");
627
+ if (isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== value) {
628
+ return {
629
+ field: path,
630
+ message: `${label} must be a valid date`,
631
+ severity: "error"
632
+ };
633
+ }
634
+ return null;
635
+ }
636
+ case "date-time": {
637
+ if (isNaN(Date.parse(value))) {
638
+ return {
639
+ field: path,
640
+ message: `${label} must be a valid date and time`,
641
+ severity: "error"
642
+ };
643
+ }
644
+ return null;
645
+ }
646
+ case "uri": {
647
+ try {
648
+ new URL(value);
649
+ return null;
650
+ } catch {
651
+ return {
652
+ field: path,
653
+ message: `${label} must be a valid URL`,
654
+ severity: "error"
655
+ };
656
+ }
657
+ }
658
+ case "uuid": {
659
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
660
+ if (!uuidRegex.test(value)) {
661
+ return {
662
+ field: path,
663
+ message: `${label} must be a valid UUID`,
664
+ severity: "error"
665
+ };
666
+ }
667
+ return null;
668
+ }
669
+ default:
670
+ return null;
671
+ }
672
+ }
673
+ function validateCustomRules(path, rules, context) {
674
+ const errors = [];
675
+ for (const rule of rules) {
676
+ const isValid = evaluateBoolean(rule.rule, context);
677
+ if (!isValid) {
678
+ errors.push({
679
+ field: path,
680
+ message: rule.message,
681
+ severity: rule.severity ?? "error"
682
+ });
683
+ }
684
+ }
685
+ return errors;
686
+ }
687
+ function validateArray(path, value, fieldDef, spec, data, computed, visibility, onlyVisible) {
688
+ const errors = [];
689
+ const label = fieldDef.label ?? path;
690
+ if (fieldDef.minItems !== void 0 && value.length < fieldDef.minItems) {
691
+ errors.push({
692
+ field: path,
693
+ message: `${label} must have at least ${fieldDef.minItems} items`,
694
+ severity: "error"
695
+ });
696
+ }
697
+ if (fieldDef.maxItems !== void 0 && value.length > fieldDef.maxItems) {
698
+ errors.push({
699
+ field: path,
700
+ message: `${label} must have no more than ${fieldDef.maxItems} items`,
701
+ severity: "error"
702
+ });
703
+ }
704
+ if (fieldDef.itemFields) {
705
+ for (let i = 0; i < value.length; i++) {
706
+ const item = value[i];
707
+ const itemErrors = validateArrayItem(
708
+ path,
709
+ i,
710
+ item,
711
+ fieldDef.itemFields,
712
+ spec,
713
+ data,
714
+ computed,
715
+ visibility,
716
+ onlyVisible
717
+ );
718
+ errors.push(...itemErrors);
719
+ }
720
+ }
721
+ return errors;
722
+ }
723
+ function validateArrayItem(arrayPath, index, item, itemFields, spec, data, computed, visibility, onlyVisible) {
724
+ const errors = [];
725
+ for (const [fieldName, fieldDef] of Object.entries(itemFields)) {
726
+ const itemFieldPath = `${arrayPath}[${index}].${fieldName}`;
727
+ if (onlyVisible && visibility[itemFieldPath] === false) {
728
+ continue;
729
+ }
730
+ const value = item[fieldName];
731
+ const context = {
732
+ data,
733
+ computed,
734
+ referenceData: spec.referenceData,
735
+ item,
736
+ itemIndex: index,
737
+ value
738
+ };
739
+ const isRequired2 = fieldDef.requiredWhen ? evaluateBoolean(fieldDef.requiredWhen, context) : false;
740
+ if (isRequired2 && isEmpty(value)) {
741
+ errors.push({
742
+ field: itemFieldPath,
743
+ message: fieldDef.label ? `${fieldDef.label} is required` : "This field is required",
744
+ severity: "error"
745
+ });
746
+ }
747
+ if (fieldDef.validations && !isEmpty(value)) {
748
+ const customErrors = validateCustomRules(itemFieldPath, fieldDef.validations, context);
749
+ errors.push(...customErrors);
750
+ }
751
+ }
752
+ return errors;
753
+ }
754
+ function validateSingleField(fieldPath, data, spec) {
755
+ const fieldDef = spec.fields[fieldPath];
756
+ if (!fieldDef) {
757
+ return [];
758
+ }
759
+ const computed = calculate(data, spec);
760
+ const visibility = getVisibility(data, spec, { computed });
761
+ const schemaProperty = spec.schema.properties[fieldPath];
762
+ return validateField(
763
+ fieldPath,
764
+ data[fieldPath],
765
+ fieldDef,
766
+ schemaProperty,
767
+ spec,
768
+ data,
769
+ computed,
770
+ visibility,
771
+ true
772
+ );
773
+ }
774
+
775
+ export {
776
+ calculate,
777
+ calculateWithErrors,
778
+ getFormattedValue,
779
+ calculateField,
780
+ getVisibility,
781
+ isFieldVisible,
782
+ getPageVisibility,
783
+ getRequired,
784
+ isRequired,
785
+ getEnabled,
786
+ isEnabled,
787
+ validate,
788
+ validateSingleField
789
+ };
790
+ //# sourceMappingURL=chunk-IRLYWN3R.js.map