nox-validation 1.0.1

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/lib/helpers.js ADDED
@@ -0,0 +1,1011 @@
1
+ const constants = require("./constant");
2
+
3
+ const getAllFields = (obj, parentPath = "") => {
4
+ return Object.keys(obj).flatMap((key) => {
5
+ const path = parentPath ? `${parentPath}.${key}` : key;
6
+ if (Array.isArray(obj[key])) {
7
+ return obj[key].flatMap((item, index) =>
8
+ typeof item === "object" && item !== null
9
+ ? getAllFields(item, `${path}[${index}]`)
10
+ : `${path}[${index}]`
11
+ );
12
+ }
13
+ return typeof obj[key] === "object" && obj[key] !== null && !Array.isArray(obj[key])
14
+ ? getAllFields(obj[key], path)
15
+ : path;
16
+ });
17
+ };
18
+
19
+ const getValue = (obj, key, separator = ".") => {
20
+ if (!key) return undefined;
21
+ const fieldPathParts = key.replace(/\[(\d+)\]/g, ".$1").split(separator);
22
+ return fieldPathParts.reduce((acc, part) => acc && acc[part], obj);
23
+ };
24
+
25
+ const setValue = (obj, key, value, separator = ".") => {
26
+ if (!key) return;
27
+
28
+ const keys = key.replace(/\[(\d+)\]/g, ".$1").split(separator);
29
+ const lastKey = keys.pop();
30
+
31
+ let deepObj = obj;
32
+
33
+ for (const part of keys) {
34
+ if (typeof deepObj[part] !== "object" || deepObj[part] === null) {
35
+ deepObj[part] = {};
36
+ }
37
+ deepObj = deepObj[part];
38
+ }
39
+
40
+ if (typeof deepObj[lastKey] === "object" && deepObj[lastKey] !== null) {
41
+ Object.assign(deepObj[lastKey], value);
42
+ } else {
43
+ deepObj[lastKey] = value;
44
+ }
45
+ };
46
+
47
+ const keyExists = (obj, key, separator = ".") => {
48
+ if (!key) return false;
49
+
50
+ const keys = key.replace(/\[(\d+)\]/g, ".$1").split(separator);
51
+ return keys.every((part) => {
52
+ if (obj && Object.prototype.hasOwnProperty.call(obj, part)) {
53
+ obj = obj[part];
54
+ return true;
55
+ }
56
+ return false;
57
+ });
58
+ };
59
+
60
+ const formatLabel = (str) => {
61
+ return str
62
+ ?.replace(/_/g, " ")
63
+ ?.toLowerCase()
64
+ ?.replace(/^./, (char) => char?.toUpperCase());
65
+ };
66
+
67
+ const getLastChildKey = (key) => {
68
+ if (!key) return "";
69
+ return key
70
+ .replace(/\[\d+\]/g, "")
71
+ .split(".")
72
+ .pop();
73
+ };
74
+
75
+ const getParentKey = (key) => {
76
+ if (!key) return "";
77
+
78
+ const cleanedKey = key.replace(/\[\d+\]/g, "").split(".");
79
+ cleanedKey.pop();
80
+ return cleanedKey.join(".");
81
+ };
82
+
83
+ const generateDynamicKeys = (data, keys, parentKey = "") => {
84
+ const addKey = (key) => {
85
+ if (key) {
86
+ keys.push(key);
87
+ }
88
+ };
89
+
90
+ if (Array.isArray(data)) {
91
+ addKey(parentKey);
92
+ data.forEach((item, index) => {
93
+ const key = `${parentKey}[${index}]`;
94
+ generateDynamicKeys(item, keys, key);
95
+ });
96
+ } else if (typeof data === "object" && data !== null) {
97
+ addKey(parentKey);
98
+ for (const key in data) {
99
+ if (Object.prototype.hasOwnProperty.call(data, key)) {
100
+ const newKey = parentKey ? `${parentKey}.${key}` : key;
101
+ generateDynamicKeys(data[key], keys, newKey);
102
+ }
103
+ }
104
+ } else {
105
+ addKey(parentKey);
106
+ }
107
+ };
108
+
109
+ const checkIsArrayKey = (key) => {
110
+ const match = key.match(/\[\d+\]/g);
111
+ const cleanedKey = key
112
+ .replace(/\[\d+\]/g, "")
113
+ .split(".")
114
+ .join(".");
115
+ return match
116
+ ? {
117
+ status: true,
118
+ fieldPath: cleanedKey,
119
+ formPath: key,
120
+ parentKey: getParentKey(key),
121
+ }
122
+ : { status: false, fieldPath: key, formPath: key, parentKey: key };
123
+ };
124
+
125
+ const getFormPath = (inputFields, fieldPath) => {
126
+ if (!fieldPath) return "";
127
+
128
+ const pathParts = fieldPath.split(".");
129
+ let formPath = "";
130
+
131
+ for (let i = 0; i < pathParts.length; i++) {
132
+ const currentPath = pathParts.slice(0, i + 1).join(".");
133
+ const field = inputFields.find((f) => f.path === currentPath);
134
+
135
+ if (field) {
136
+ formPath += field.type === constants.types.ARRAY ? `${field.field}[0]` : field.field;
137
+ } else {
138
+ formPath += `.${pathParts[i]}`;
139
+ }
140
+
141
+ if (i < pathParts.length - 1) formPath += ".";
142
+ }
143
+
144
+ const cleanedFormPath = formPath.replace(/\.\[/g, "[");
145
+ return cleanedFormPath.endsWith("[0]") ? cleanedFormPath.split("[0]")[0] : cleanedFormPath;
146
+ };
147
+
148
+ const isEmpty = (val) => {
149
+ if (val === undefined) return true;
150
+ if (val == null) return true;
151
+ if (typeof val === "boolean") return false;
152
+ if (typeof val === "number") return false;
153
+ if (typeof val === "string") return val.length === 0;
154
+ if (Array.isArray(val)) return val.length === 0;
155
+ if (typeof val === "object") return Object.keys(val).length === 0;
156
+
157
+ return false;
158
+ };
159
+
160
+ const convertTypes = (field) => {
161
+ // array_type:
162
+ // item.type !== item.schema_definition.type &&
163
+ // item.schema_definition.type !== constants.types.ALIAS
164
+ // ? item.schema_definition.type
165
+ // : item.type === item.schema_definition.type &&
166
+ // item.type === constants.types.ARRAY
167
+ // ? constants.types.OBJECT
168
+ // : null,
169
+
170
+ // type:
171
+ // item.type === constants.types.ALIAS ||
172
+ // item.schema_definition.type === constants.types.ALIAS
173
+ // ? constants.types.OBJECT
174
+ // : item.type,
175
+
176
+ // 'list-o2m', // alias
177
+ // 'list-m2o', // objectid
178
+ // 'list-m2m', // alias
179
+ // 'file', // objectid
180
+ // 'file-image', // objectid
181
+ // 'files', // alias
182
+ // 'list-m2a', // alias
183
+ // 'translations', // alias
184
+
185
+ let { type, schema_definition, meta } = field;
186
+ let array_type = schema_definition?.type ?? null;
187
+ let interfaceType = meta?.interface;
188
+ let find_relations = false;
189
+
190
+ if (
191
+ [
192
+ constants.interfaces.ONE_TO_MANY,
193
+ constants.interfaces.MANY_TO_MANY,
194
+ constants.interfaces.MANY_TO_ANY,
195
+ constants.interfaces.TRANSLATIONS,
196
+ ].includes(interfaceType)
197
+ ) {
198
+ return {
199
+ type: constants.types.OBJECT,
200
+ array_type: null,
201
+ find_relations: true,
202
+ };
203
+ }
204
+
205
+ if (interfaceType === constants.interfaces.FILES) {
206
+ return {
207
+ type: constants.types.ARRAY,
208
+ array_type: constants.types.OBJECT_ID,
209
+ find_relations: true,
210
+ };
211
+ }
212
+
213
+ if (type !== schema_definition?.type && schema_definition?.type !== constants.types.ALIAS) {
214
+ array_type = schema_definition.type;
215
+ }
216
+
217
+ return { type, array_type, find_relations };
218
+ };
219
+
220
+ const convertTypesV1 = (field) => {
221
+ let { type, schema_definition, meta } = field;
222
+ let array_type = schema_definition?.type ?? null;
223
+ let interfaceType = meta?.interface;
224
+ let find_relations = false;
225
+
226
+ if ([constants.interfaces.TRANSLATIONS].includes(interfaceType)) {
227
+ return {
228
+ type: constants.types.OBJECT,
229
+ array_type: null,
230
+ find_relations: true,
231
+ };
232
+ }
233
+
234
+ if ([constants.interfaces.MANY_TO_ANY].includes(interfaceType)) {
235
+ return {
236
+ type: constants.types.ARRAY,
237
+ array_type: constants.types.OBJECT,
238
+ find_relations: true,
239
+ };
240
+ }
241
+
242
+ if ([constants.interfaces.MANY_TO_ONE].includes(interfaceType)) {
243
+ return {
244
+ type: constants.types.OBJECT_ID,
245
+ array_type: null,
246
+ find_relations: false,
247
+ };
248
+ }
249
+
250
+ if (
251
+ [
252
+ constants.interfaces.ONE_TO_MANY,
253
+ constants.interfaces.MANY_TO_MANY,
254
+ constants.interfaces.FILES,
255
+ ].includes(interfaceType)
256
+ ) {
257
+ return {
258
+ type: constants.types.ARRAY,
259
+ array_type: constants.types.OBJECT_ID,
260
+ find_relations: false,
261
+ };
262
+ }
263
+
264
+ if (type !== schema_definition?.type && schema_definition?.type !== constants.types.ALIAS) {
265
+ array_type = schema_definition.type;
266
+ find_relations = false;
267
+ }
268
+
269
+ return { type, array_type, find_relations };
270
+ };
271
+
272
+ const generateRelationalFieldV1 = (key = "", collectionFields = [], relationType) => {
273
+ const generateField = (name, path, schema_definition_type, type, childrenFields = []) => {
274
+ const relationalPath = key ? `${key}.${path}` : path;
275
+
276
+ return {
277
+ display_label: name,
278
+ key: relationalPath,
279
+ value: relationalPath,
280
+ children: childrenFields,
281
+ type: type,
282
+ meta: {
283
+ required: false,
284
+ nullable: false,
285
+ hidden: false,
286
+ },
287
+ validations: [],
288
+ array_type: schema_definition_type,
289
+ };
290
+ };
291
+
292
+ if (relationType === constants.interfaces.MANY_TO_ANY) {
293
+ return [
294
+ generateField("collection", "collection", constants.types.STRING, constants.types.STRING),
295
+ generateField("sort", "sort", constants.types.NUMBER, constants.types.NUMBER),
296
+ generateField(
297
+ "item",
298
+ "item",
299
+ constants.types.OBJECT_ID,
300
+ constants.types.OBJECT_ID,
301
+ ),
302
+ ];
303
+ } else {
304
+ return [];
305
+ }
306
+ };
307
+
308
+ const generateRelationalField = (key = "", collectionFields = [], relationType) => {
309
+ const generateField = (name, path, schema_definition_type, type, childrenFields = []) => {
310
+ const relationalPath = key ? `${key}.${path}` : path;
311
+ childrenFields = childrenFields?.map((child) => {
312
+ return {
313
+ ...child,
314
+ key: `${relationalPath}.${child.key}`,
315
+ value: `${relationalPath}.${child.value}`,
316
+ };
317
+ });
318
+
319
+ return {
320
+ display_label: name,
321
+ key: relationalPath,
322
+ value: relationalPath,
323
+ children: childrenFields,
324
+ type: type,
325
+ meta: {
326
+ required: false,
327
+ nullable: false,
328
+ hidden: false,
329
+ },
330
+ validations: [],
331
+ array_type: schema_definition_type,
332
+ };
333
+ };
334
+
335
+ if (relationType === constants.interfaces.MANY_TO_ANY) {
336
+ const existingField = generateField(
337
+ `${key}.existing`,
338
+ "existing",
339
+ constants.types.OBJECT,
340
+ constants.types.ARRAY
341
+ );
342
+ existingField.children = [
343
+ generateField(
344
+ "collection",
345
+ "existing.collection",
346
+ constants.types.STRING,
347
+ constants.types.STRING
348
+ ),
349
+ generateField("sort", "existing.sort", constants.types.NUMBER, constants.types.NUMBER),
350
+ generateField("item", "existing.item", constants.types.OBJECT_ID, constants.types.OBJECT_ID),
351
+ ];
352
+ const deleteField = generateField(
353
+ "delete",
354
+ "delete",
355
+ constants.types.OBJECT_ID,
356
+ constants.types.ARRAY
357
+ );
358
+ const createField = generateField(
359
+ "create",
360
+ "create",
361
+ constants.types.OBJECT,
362
+ constants.types.ARRAY,
363
+ collectionFields
364
+ );
365
+ createField.children = [
366
+ generateField(
367
+ "collection",
368
+ "create.collection",
369
+ constants.types.STRING,
370
+ constants.types.STRING
371
+ ),
372
+ generateField("sort", "create.sort", constants.types.NUMBER, constants.types.NUMBER),
373
+ generateField(
374
+ "item",
375
+ "create.item",
376
+ constants.types.OBJECT,
377
+ constants.types.OBJECT,
378
+ collectionFields
379
+ ),
380
+ ];
381
+ const updateField = generateField(
382
+ "update",
383
+ "update",
384
+ constants.types.OBJECT,
385
+ constants.types.ARRAY,
386
+ collectionFields
387
+ );
388
+ updateField.children = [
389
+ generateField(
390
+ "collection",
391
+ "update.collection",
392
+ constants.types.STRING,
393
+ constants.types.STRING
394
+ ),
395
+ generateField("sort", "update.sort", constants.types.NUMBER, constants.types.NUMBER),
396
+ generateField(
397
+ "item",
398
+ "update.item",
399
+ constants.types.OBJECT,
400
+ constants.types.OBJECT,
401
+ collectionFields
402
+ ),
403
+ ];
404
+ return [existingField, deleteField, createField, updateField];
405
+ } else {
406
+ return [
407
+ generateField("existing", "existing", constants.types.OBJECT_ID, constants.types.ARRAY),
408
+ generateField("delete", "delete", constants.types.OBJECT_ID, constants.types.ARRAY),
409
+ generateField(
410
+ "create",
411
+ "create",
412
+ constants.types.OBJECT,
413
+ constants.types.ARRAY,
414
+ collectionFields
415
+ ),
416
+ generateField(
417
+ "update",
418
+ "update",
419
+ constants.types.OBJECT,
420
+ constants.types.ARRAY,
421
+ collectionFields
422
+ ),
423
+ ];
424
+ }
425
+ };
426
+
427
+ const getForeignCollectionDetails = ({
428
+ relations,
429
+ collection,
430
+ field,
431
+ iFace,
432
+ findJunction = true,
433
+ getRelationshipDetails = true,
434
+ }) => {
435
+ if (!relations.length > 0) return {};
436
+
437
+ const isListInterface = [
438
+ constants.interfaces.ONE_TO_MANY,
439
+ constants.interfaces.MANY_TO_MANY,
440
+ constants.interfaces.TRANSLATIONS,
441
+ constants.interfaces.FILES,
442
+ constants.interfaces.MANY_TO_ANY,
443
+ ].includes(iFace);
444
+
445
+ const isSingleRelation = [
446
+ constants.interfaces.MANY_TO_ONE,
447
+ constants.interfaces.FILE,
448
+ constants.interfaces.FILE_IMAGE,
449
+ ].includes(iFace);
450
+
451
+ if (isListInterface) {
452
+ const mainTable = relations.find(
453
+ (d) => d.one_collection_id === collection && d.one_field_id === field
454
+ );
455
+ if (!mainTable) return {};
456
+
457
+ const isJunction = mainTable.junction_field && findJunction;
458
+ const relational = isJunction
459
+ ? relations.find(
460
+ (d) =>
461
+ d.many_collection === mainTable.many_collection &&
462
+ d.junction_field === mainTable.many_field
463
+ )
464
+ : [];
465
+
466
+ if (getRelationshipDetails) {
467
+ return {
468
+ this_collection: mainTable.one_collection_id,
469
+ this_field: "_id",
470
+ foreign_collection:
471
+ iFace === constants.interfaces.MANY_TO_MANY || iFace === constants.interfaces.TRANSLATIONS
472
+ ? relational?.one_collection_id
473
+ : iFace === constants.interfaces.MANY_TO_ANY
474
+ ? relational?.one_allowed_collections_id
475
+ : mainTable.many_collection_id,
476
+ foreign_field:
477
+ iFace === constants.interfaces.MANY_TO_MANY || iFace === constants.interfaces.TRANSLATIONS
478
+ ? "_id"
479
+ : iFace === constants.interfaces.MANY_TO_ANY
480
+ ? "Primary Key"
481
+ : mainTable.many_field_id,
482
+ ...(isJunction && {
483
+ junction_collection: relational?.many_collection_id,
484
+ junction_field_this: relational?.junction_field,
485
+ junction_field_foreign:
486
+ iFace === constants.interfaces.MANY_TO_ANY ? "item" : relational?.many_field,
487
+ }),
488
+ ...(iFace === constants.interfaces.MANY_TO_ANY && {
489
+ junction_field_ref: "collection",
490
+ foreign_collection_ref: relational?.one_allowed_collections?.join(", "),
491
+ }),
492
+ };
493
+ }
494
+
495
+ return {
496
+ foreign_collection_id: isJunction
497
+ ? iFace === constants.interfaces.MANY_TO_ANY
498
+ ? relational?.one_allowed_collections_id
499
+ : relational?.one_collection_id
500
+ : mainTable.many_collection_id,
501
+ ...(isJunction && {
502
+ junction_collection_id: relational?.many_collection_id,
503
+ junction_field: mainTable.junction_field,
504
+ junction_field_local: mainTable.many_field,
505
+ }),
506
+ };
507
+ }
508
+
509
+ if (isSingleRelation) {
510
+ const mainTable = relations.find(
511
+ (d) => d.many_collection_id === collection && d.many_field === field
512
+ );
513
+ if (!mainTable) return {};
514
+
515
+ return getRelationshipDetails
516
+ ? {
517
+ this_collection: mainTable.many_collection_id,
518
+ this_field: mainTable.many_field,
519
+ foreign_collection: mainTable.one_collection_id,
520
+ foreign_field: "_id",
521
+ }
522
+ : {
523
+ foreign_collection_id: mainTable.one_collection_id,
524
+ };
525
+ }
526
+
527
+ return {};
528
+ };
529
+
530
+ const getCachedFields = (relationDetail, relational_fields) => {
531
+ const isMultiple = Array.isArray(relationDetail.foreign_collection);
532
+ const getField = (schemaId) => {
533
+ if (relational_fields[schemaId]) {
534
+ return relational_fields[schemaId]; // Return cached fields if available
535
+ } else {
536
+ return [];
537
+ }
538
+ };
539
+
540
+ if (!isMultiple) {
541
+ return getField(relationDetail.foreign_collection);
542
+ } else {
543
+ return relationDetail.foreign_collection.flatMap((schemaId) => getField(schemaId));
544
+ }
545
+ };
546
+
547
+ const getCachedOrFetchFields = (schemaId, allFields, relational_fields) => {
548
+ if (relational_fields[schemaId]) {
549
+ return relational_fields[schemaId]; // Return cached fields if available
550
+ }
551
+
552
+ const fields = allFields?.filter((field) => field.schema_id === schemaId) || [];
553
+ relational_fields[schemaId] = fields; // Cache the fields
554
+ return fields;
555
+ };
556
+
557
+ const getChildFields = (relationDetail, allFields, relational_fields, isTranslation) => {
558
+ let key = isTranslation
559
+ ? [
560
+ ...(Array.isArray(relationDetail.junction_collection)
561
+ ? relationDetail.junction_collection
562
+ : [relationDetail.junction_collection]),
563
+ ...(Array.isArray(relationDetail.foreign_collection)
564
+ ? relationDetail.foreign_collection
565
+ : [relationDetail.foreign_collection])
566
+ ]
567
+ : relationDetail.foreign_collection;
568
+
569
+
570
+ const isMultiple = Array.isArray(key);
571
+
572
+ if (!isMultiple) {
573
+ return getCachedOrFetchFields(key, allFields, relational_fields);
574
+ } else {
575
+ return key.flatMap((schemaId) =>
576
+ getCachedOrFetchFields(schemaId, allFields, relational_fields)
577
+ );
578
+ }
579
+ };
580
+
581
+ const buildNestedStructure = (
582
+ schemaFields,
583
+ allFields,
584
+ relations,
585
+ formData,
586
+ relational_fields,
587
+ isSeparatedFields,
588
+ apiVersion
589
+ ) => {
590
+ const root = {};
591
+ const nodeMap = new Map();
592
+
593
+ schemaFields.sort((a, b) => a.path.split(".").length - b.path.split(".").length);
594
+
595
+ schemaFields.forEach((item) => {
596
+ let childFields;
597
+
598
+ const definedType =
599
+ apiVersion === constants.API_VERSION.V1 ? convertTypesV1(item) : convertTypes(item);
600
+
601
+ if (definedType.find_relations) {
602
+ const relationDetail = getForeignCollectionDetails({
603
+ relations: relations,
604
+ collection: item?.schema_id,
605
+ field: item._id,
606
+ iFace: item?.meta?.interface,
607
+ findJunction: true,
608
+ getRelationshipDetails: true,
609
+ });
610
+
611
+ if (!isSeparatedFields) {
612
+ childFields = getChildFields(
613
+ relationDetail,
614
+ allFields,
615
+ relational_fields,
616
+ item?.meta?.interface === constants.interfaces.TRANSLATIONS
617
+ );
618
+ } else {
619
+ childFields = getCachedFields(relationDetail, relational_fields);
620
+ }
621
+
622
+ if (childFields) {
623
+ childFields = buildNestedStructure(
624
+ childFields,
625
+ allFields,
626
+ relations,
627
+ formData,
628
+ relational_fields,
629
+ isSeparatedFields
630
+ );
631
+ }
632
+ }
633
+
634
+ const pathParts = item.path.split(".");
635
+ const key = pathParts.join(".");
636
+
637
+ const node = {
638
+ field_id: item?._id,
639
+ display_label: pathParts[pathParts.length - 1],
640
+ key,
641
+ value: key,
642
+ meta: {
643
+ interface: item.meta?.interface || "none",
644
+ required: item.meta?.required || false,
645
+ nullable: item.meta?.nullable || false,
646
+ hidden: item.meta?.hidden || false,
647
+ },
648
+ validations:
649
+ item?.meta?.validations?.rules?.length > 0
650
+ ? generateModifiedRules(item?.meta, formData)
651
+ : [],
652
+ children:
653
+ childFields?.length > 0
654
+ ? apiVersion === constants.API_VERSION.V1 &&
655
+ item.meta?.interface !== constants.interfaces.TRANSLATIONS
656
+ ? generateRelationalFieldV1(key, childFields, item.meta?.interface)
657
+ : generateRelationalField(key, childFields, item.meta?.interface)
658
+ : [],
659
+ type: definedType.type,
660
+ array_type: definedType.array_type,
661
+ default_value: item?.schema_definition?.default,
662
+ };
663
+
664
+ nodeMap.set(key, node);
665
+
666
+ if (pathParts.length === 1) {
667
+ root[key] = node;
668
+ } else {
669
+ const parentPath = pathParts.slice(0, -1).join(".");
670
+
671
+ if (nodeMap.has(parentPath)) {
672
+ nodeMap.get(parentPath).children.push(node);
673
+ }
674
+ }
675
+ });
676
+
677
+ const removeEmptyChildren = (nodes) => {
678
+ return nodes.map(({ children, ...node }) =>
679
+ children && children?.length ? { ...node, children: removeEmptyChildren(children) } : node
680
+ );
681
+ };
682
+
683
+ return removeEmptyChildren(Object.values(root));
684
+ };
685
+
686
+ const getAllKeys = (structure) => {
687
+ const keys = new Set();
688
+
689
+ const traverse = (nodes) => {
690
+ nodes.forEach((node) => {
691
+ keys.add(node.key);
692
+ if (node.children && node.children.length) {
693
+ traverse(node.children);
694
+ }
695
+ });
696
+ };
697
+
698
+ traverse(structure);
699
+ return keys;
700
+ };
701
+
702
+ const normalizeKey = (key) => key.replace(/\[\d+\]/g, "");
703
+
704
+ const findDisallowedKeys = (formData, structure) => {
705
+ const formKeys = [];
706
+ generateDynamicKeys(formData, formKeys);
707
+
708
+ const validKeys = getAllKeys(structure);
709
+ return formKeys.filter((key) => !validKeys.has(normalizeKey(key)));
710
+ };
711
+
712
+ const generateFieldCompareRules = (rule) => {
713
+ const modifiedRule = {
714
+ identifier: 1,
715
+ case: constants.rulesTypes.FIELD_COMPARE,
716
+ value: [],
717
+ options: {},
718
+ };
719
+
720
+ switch (rule.type) {
721
+ case "contains":
722
+ modifiedRule.case = constants.rulesTypes.REGEX;
723
+ modifiedRule.value = [rule[rule.type].value];
724
+ modifiedRule.options = {
725
+ type: constants.regexTypes.CONTAINS,
726
+ case_sensitive: !rule[rule.type].insensitive,
727
+ multiline: false,
728
+ global: false,
729
+ };
730
+ break;
731
+ case "doesNotContain":
732
+ modifiedRule.case = constants.rulesTypes.REGEX;
733
+ modifiedRule.value = [rule[rule.type].value];
734
+ modifiedRule.options = {
735
+ type: constants.regexTypes.NOT_CONTAINS,
736
+ case_sensitive: !rule[rule.type].insensitive,
737
+ multiline: false,
738
+ global: false,
739
+ };
740
+ break;
741
+ case "startsWith":
742
+ modifiedRule.case = constants.rulesTypes.REGEX;
743
+ modifiedRule.value = [rule[rule.type].value];
744
+ modifiedRule.options = {
745
+ type: constants.regexTypes.START_WITH,
746
+ case_sensitive: !rule[rule.type].insensitive,
747
+ };
748
+ break;
749
+ case "doesNotStartWith":
750
+ modifiedRule.case = constants.rulesTypes.REGEX;
751
+ modifiedRule.value = [rule[rule.type].value];
752
+ modifiedRule.options = {
753
+ type: constants.regexTypes.NOT_START_WITH,
754
+ case_sensitive: !rule[rule.type].insensitive,
755
+ };
756
+ break;
757
+ case "endsWith":
758
+ modifiedRule.case = constants.rulesTypes.REGEX;
759
+ modifiedRule.value = [rule[rule.type].value];
760
+ modifiedRule.options = {
761
+ type: constants.regexTypes.ENDS_WITH,
762
+ case_sensitive: !rule[rule.type].insensitive,
763
+ };
764
+ break;
765
+ case "doesNotEndWith":
766
+ modifiedRule.case = constants.rulesTypes.REGEX;
767
+ modifiedRule.value = [rule[rule.type].value];
768
+ modifiedRule.options = {
769
+ type: constants.regexTypes.NOT_ENDS_WITH,
770
+ case_sensitive: !rule[rule.type].insensitive,
771
+ };
772
+ break;
773
+ case "matchesRegExp":
774
+ modifiedRule.case = constants.rulesTypes.REGEX;
775
+ modifiedRule.value = [rule.matchesRegExp.value];
776
+ modifiedRule.options.type = constants.regexTypes.MATCH;
777
+ break;
778
+ case "equals":
779
+ modifiedRule.case = constants.rulesTypes.OPERATOR;
780
+ modifiedRule.options.operator = constants.operatorTypes.EQUAL;
781
+ modifiedRule.value = [rule[rule.type].value];
782
+ break;
783
+ case "doesNotEqual":
784
+ modifiedRule.case = constants.rulesTypes.OPERATOR;
785
+ modifiedRule.options.operator = constants.operatorTypes.NOT_EQUAL;
786
+ modifiedRule.value = [rule[rule.type].value];
787
+ break;
788
+ case "lessThan":
789
+ modifiedRule.case = constants.rulesTypes.OPERATOR;
790
+ modifiedRule.options.operator = constants.operatorTypes.LESS_THAN;
791
+ modifiedRule.value = [rule[rule.type].value];
792
+ break;
793
+ case "lessThanOrEqualTo":
794
+ modifiedRule.case = constants.rulesTypes.OPERATOR;
795
+ modifiedRule.options.operator = constants.operatorTypes.LESS_THAN_EQUAL;
796
+ modifiedRule.value = [rule[rule.type].value];
797
+ break;
798
+ case "greaterThan":
799
+ modifiedRule.case = constants.rulesTypes.OPERATOR;
800
+ modifiedRule.options.operator = constants.operatorTypes.GREATER_THAN;
801
+ modifiedRule.value = [rule[rule.type].value];
802
+ break;
803
+ case "greaterThanOrEqualTo":
804
+ modifiedRule.case = constants.rulesTypes.OPERATOR;
805
+ modifiedRule.options.operator = constants.operatorTypes.GREATER_THAN_EQUAL;
806
+ modifiedRule.value = [rule[rule.type].value];
807
+ break;
808
+ case "isEmpty":
809
+ modifiedRule.case = constants.rulesTypes.EMPTY;
810
+ modifiedRule.value = [];
811
+ break;
812
+ case "isNotEmpty":
813
+ modifiedRule.case = constants.rulesTypes.NOT_EMPTY;
814
+ modifiedRule.value = [];
815
+ break;
816
+ case "isOneOf":
817
+ modifiedRule.case = constants.rulesTypes.ONE_OF;
818
+ modifiedRule.value = rule[rule.type].value;
819
+ break;
820
+ case "isNotOneOf":
821
+ modifiedRule.case = constants.rulesTypes.NOT_ONE_OF;
822
+ modifiedRule.value = [rule[rule.type].value];
823
+ break;
824
+ }
825
+ modifiedRule.options.isFieldCompare = true;
826
+ return modifiedRule;
827
+ };
828
+
829
+ const generateModifiedRules = (meta) => {
830
+ return meta?.validations?.rules?.map((rule, index) => {
831
+ let modifiedRule = {
832
+ identifier: String(index),
833
+ case: constants.rulesTypes.RULES_COMPARE,
834
+ value: [],
835
+ options: {},
836
+ };
837
+ switch (rule.rule) {
838
+ case "contains":
839
+ modifiedRule.case = constants.rulesTypes.REGEX;
840
+ modifiedRule.value = [rule[rule.rule].value];
841
+ modifiedRule.options = {
842
+ type: constants.regexTypes.CONTAINS,
843
+ case_sensitive: !rule[rule.rule].insensitive,
844
+ multiline: false,
845
+ global: false,
846
+ };
847
+ break;
848
+ case "doesNotContain":
849
+ modifiedRule.case = constants.rulesTypes.REGEX;
850
+ modifiedRule.value = [rule[rule.rule].value];
851
+ modifiedRule.options = {
852
+ type: constants.regexTypes.NOT_CONTAINS,
853
+ case_sensitive: !rule[rule.rule].insensitive,
854
+ multiline: false,
855
+ global: false,
856
+ };
857
+ break;
858
+ case "startsWith":
859
+ modifiedRule.case = constants.rulesTypes.REGEX;
860
+ modifiedRule.value = [rule[rule.rule].value];
861
+ modifiedRule.options = {
862
+ type: constants.regexTypes.START_WITH,
863
+ case_sensitive: !rule[rule.rule].insensitive,
864
+ };
865
+ break;
866
+ case "doesNotStartWith":
867
+ modifiedRule.case = constants.rulesTypes.REGEX;
868
+ modifiedRule.value = [rule[rule.rule].value];
869
+ modifiedRule.options = {
870
+ type: constants.regexTypes.NOT_START_WITH,
871
+ case_sensitive: !rule[rule.rule].insensitive,
872
+ };
873
+ break;
874
+ case "endsWith":
875
+ modifiedRule.case = constants.rulesTypes.REGEX;
876
+ modifiedRule.value = [rule[rule.rule].value];
877
+ modifiedRule.options = {
878
+ type: constants.regexTypes.ENDS_WITH,
879
+ case_sensitive: !rule[rule.rule].insensitive,
880
+ };
881
+ break;
882
+ case "doesNotEndWith":
883
+ modifiedRule.case = constants.rulesTypes.REGEX;
884
+ modifiedRule.value = [rule[rule.rule].value];
885
+ modifiedRule.options = {
886
+ type: constants.regexTypes.NOT_ENDS_WITH,
887
+ case_sensitive: !rule[rule.rule].insensitive,
888
+ };
889
+ break;
890
+ case "matchesRegExp":
891
+ modifiedRule.case = constants.rulesTypes.REGEX;
892
+ modifiedRule.value = [rule.matchesRegExp.value];
893
+ modifiedRule.options.type = constants.regexTypes.MATCH;
894
+ break;
895
+ case "equals":
896
+ modifiedRule.case = constants.rulesTypes.OPERATOR;
897
+ modifiedRule.options.operator = constants.operatorTypes.EQUAL;
898
+ modifiedRule.value = [rule[rule.rule].value];
899
+ break;
900
+ case "doesNotEqual":
901
+ modifiedRule.case = constants.rulesTypes.OPERATOR;
902
+ modifiedRule.options.operator = constants.operatorTypes.NOT_EQUAL;
903
+ modifiedRule.value = [rule[rule.rule].value];
904
+ break;
905
+ case "lessThan":
906
+ modifiedRule.case = constants.rulesTypes.OPERATOR;
907
+ modifiedRule.options.operator = constants.operatorTypes.LESS_THAN;
908
+ modifiedRule.value = [rule[rule.rule].value];
909
+ break;
910
+ case "lessThanOrEqualTo":
911
+ modifiedRule.case = constants.rulesTypes.OPERATOR;
912
+ modifiedRule.options.operator = constants.operatorTypes.LESS_THAN_EQUAL;
913
+ modifiedRule.value = [rule[rule.rule].value];
914
+ break;
915
+ case "greaterThan":
916
+ modifiedRule.case = constants.rulesTypes.OPERATOR;
917
+ modifiedRule.options.operator = constants.operatorTypes.GREATER_THAN;
918
+ modifiedRule.value = [rule[rule.rule].value];
919
+ break;
920
+ case "greaterThanOrEqualTo":
921
+ modifiedRule.case = constants.rulesTypes.OPERATOR;
922
+ modifiedRule.options.operator = constants.operatorTypes.GREATER_THAN_EQUAL;
923
+ modifiedRule.value = [rule[rule.rule].value];
924
+ break;
925
+ case "isEmpty":
926
+ modifiedRule.case = constants.rulesTypes.EMPTY;
927
+ modifiedRule.value = [];
928
+ break;
929
+ case "isNotEmpty":
930
+ modifiedRule.case = constants.rulesTypes.NOT_EMPTY;
931
+ modifiedRule.value = [];
932
+ break;
933
+ case "isOneOf":
934
+ modifiedRule.case = constants.rulesTypes.ONE_OF;
935
+ modifiedRule.value = rule[rule.rule].value;
936
+ break;
937
+ case "isNotOneOf":
938
+ modifiedRule.case = constants.rulesTypes.NOT_ONE_OF;
939
+ modifiedRule.value = [rule[rule.rule].value];
940
+ break;
941
+ case constants.rulesTypes.FIELD_COMPARE: {
942
+ const fieldRule = generateFieldCompareRules(rule);
943
+ modifiedRule.case = fieldRule.case;
944
+ modifiedRule.options = fieldRule.options;
945
+ modifiedRule.value = fieldRule.value;
946
+ break;
947
+ }
948
+ }
949
+
950
+ return modifiedRule;
951
+ });
952
+ };
953
+
954
+ const getFieldsGroupBySchemaId = (arr) => {
955
+ return arr.reduce((acc, item) => {
956
+ const key = item.schema_id;
957
+ if (!acc[key]) {
958
+ acc[key] = [];
959
+ }
960
+ acc[key].push(item);
961
+ return acc;
962
+ }, {});
963
+ };
964
+
965
+ const getDefaultValues = (tree) => {
966
+ const defaultValues = {};
967
+
968
+ tree?.forEach((field) => {
969
+ let { key, type, array_type, children, default_value } = field;
970
+ let defaultValue = default_value !== undefined ? default_value : null;
971
+
972
+ // Extract last part of the key (remove parent references)
973
+ const keyParts = key.split(".");
974
+ const cleanKey = keyParts[keyParts.length - 1];
975
+
976
+ if (type === "Object") {
977
+ setValue(defaultValues, cleanKey, children?.length ? getDefaultValues(children) : {});
978
+ } else if (type === "Array") {
979
+ if (array_type === "String" || array_type === "Number") {
980
+ setValue(defaultValues, cleanKey, []);
981
+ } else {
982
+ // Prevent extra nesting by ensuring the array contains objects, not arrays
983
+ setValue(defaultValues, cleanKey, children?.length ? [getDefaultValues(children)] : []);
984
+ }
985
+ } else {
986
+ setValue(defaultValues, cleanKey, defaultValue);
987
+ }
988
+ });
989
+
990
+ return defaultValues;
991
+ };
992
+
993
+ module.exports = {
994
+ generateModifiedRules,
995
+ getFieldsGroupBySchemaId,
996
+ buildNestedStructure,
997
+ getValue,
998
+ setValue,
999
+ keyExists,
1000
+ formatLabel,
1001
+ generateDynamicKeys,
1002
+ getLastChildKey,
1003
+ checkIsArrayKey,
1004
+ isEmpty,
1005
+ getParentKey,
1006
+ getFormPath,
1007
+ findDisallowedKeys,
1008
+ getAllFields,
1009
+ getForeignCollectionDetails,
1010
+ getDefaultValues,
1011
+ };