nox-validation 1.6.1 → 1.6.3

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 CHANGED
@@ -319,7 +319,7 @@ const generateField = (
319
319
  hidden: false,
320
320
  }
321
321
  ) => {
322
- const { staticType } = meta;
322
+ const { staticType = null, parentInterface = null } = meta;
323
323
  childrenFields = childrenFields?.map((child) => {
324
324
  const childKey = path ? `${path}.${child.key}` : child.key;
325
325
  const childValue = path ? `${path}.${child.value}` : child.value;
@@ -327,7 +327,10 @@ const generateField = (
327
327
  return {
328
328
  ...child,
329
329
  value: childKey,
330
- meta: staticType ? { ...child?.meta, staticType } : child?.meta,
330
+ meta:
331
+ staticType || parentInterface
332
+ ? { ...child?.meta, staticType, parentInterface }
333
+ : child?.meta,
331
334
  key: childValue,
332
335
  isRelationalUpdate: path.includes("update"),
333
336
  };
@@ -418,7 +421,7 @@ const generateRelationalField = (
418
421
  [],
419
422
  "none",
420
423
  [],
421
- { staticType: "existing" }
424
+ { staticType: "existing", parentInterface: relationType }
422
425
  );
423
426
  existingField.children = [
424
427
  generateField(
@@ -458,7 +461,7 @@ const generateRelationalField = (
458
461
  [],
459
462
  "none",
460
463
  [],
461
- { staticType: "delete" }
464
+ { staticType: "delete", parentInterface: relationType }
462
465
  );
463
466
  deleteField.children = [
464
467
  generateField(
@@ -499,7 +502,7 @@ const generateRelationalField = (
499
502
  [],
500
503
  "none",
501
504
  [],
502
- { staticType: "create" }
505
+ { staticType: "create", parentInterface: relationType }
503
506
  );
504
507
  createField.children = [
505
508
  generateField(
@@ -544,7 +547,7 @@ const generateRelationalField = (
544
547
  [],
545
548
  "none",
546
549
  [],
547
- { staticType: "update" }
550
+ { staticType: "update", parentInterface: relationType }
548
551
  );
549
552
  updateField.children = [
550
553
  generateField(
@@ -591,7 +594,7 @@ const generateRelationalField = (
591
594
  [],
592
595
  "none",
593
596
  [],
594
- { staticType: "existing" }
597
+ { staticType: "existing", parentInterface: relationType }
595
598
  ),
596
599
  generateField(
597
600
  "delete",
@@ -601,7 +604,7 @@ const generateRelationalField = (
601
604
  [],
602
605
  "none",
603
606
  [],
604
- { staticType: "delete" }
607
+ { staticType: "delete", parentInterface: relationType }
605
608
  ),
606
609
  generateField(
607
610
  "create",
@@ -611,7 +614,7 @@ const generateRelationalField = (
611
614
  collectionFields,
612
615
  "none",
613
616
  [],
614
- { staticType: "create" }
617
+ { staticType: "create", parentInterface: relationType }
615
618
  ),
616
619
  generateField(
617
620
  "update",
@@ -621,7 +624,7 @@ const generateRelationalField = (
621
624
  collectionFields,
622
625
  "none",
623
626
  [],
624
- { staticType: "update" }
627
+ { staticType: "update", parentInterface: relationType }
625
628
  ),
626
629
  ];
627
630
  }
@@ -1401,6 +1404,20 @@ const getM2AItemParentPath = (path) => {
1401
1404
  return match ? path.replace(".item", "") : null;
1402
1405
  };
1403
1406
 
1407
+ const addIndexToStaticType = (key, staticType) => {
1408
+ const parts = key.split(".");
1409
+
1410
+ return parts
1411
+ .map((part) => {
1412
+ if (part === staticType) {
1413
+ // If staticType is found AND missing index → add [0]
1414
+ return part.includes("[") ? part : `${part}[0]`;
1415
+ }
1416
+ return part;
1417
+ })
1418
+ .join(".");
1419
+ };
1420
+
1404
1421
  const getSelectedNodes = ({
1405
1422
  node,
1406
1423
  skipFn = (node) => node.meta?.interface === constants.interfaces.MANY_TO_ANY,
@@ -1427,10 +1444,47 @@ const getSelectedNodes = ({
1427
1444
  return updatedNode;
1428
1445
  }
1429
1446
 
1430
- function traverse(currentNode) {
1447
+ function traverse(currentNode, parentKey = null) {
1448
+ function mergePathLevels(currentPath, parentPath) {
1449
+ const cur = currentPath.split(".");
1450
+ const par = parentPath.split(".");
1451
+
1452
+ let matchIndex = -1;
1453
+
1454
+ // Find the first matching level
1455
+ for (let i = 0; i < par.length; i++) {
1456
+ if (par[i].replace(/\[\d+\]/, "") === cur[0].replace(/\[\d+\]/, "")) {
1457
+ matchIndex = i;
1458
+ break;
1459
+ }
1460
+ }
1461
+
1462
+ // If match found
1463
+ if (matchIndex !== -1) {
1464
+ const parentPart = par.slice(0, matchIndex + 1);
1465
+ const currentTail = cur.slice(1); // everything after the matched level
1466
+ return [...parentPart, ...currentTail].join(".");
1467
+ }
1468
+
1469
+ // No match → normal join
1470
+ return [...par, ...cur].join(".");
1471
+ }
1472
+
1473
+ if (currentNode?.meta?.staticType) {
1474
+ currentNode.key = addIndexToStaticType(
1475
+ currentNode.key,
1476
+ currentNode?.meta?.staticType
1477
+ );
1478
+ }
1479
+
1480
+ if (parentKey)
1481
+ currentNode.key = mergePathLevels(currentNode.key, parentKey);
1482
+
1431
1483
  if (conditionFn(currentNode)) {
1432
1484
  actionFn(currentNode);
1433
- result.push(mapFn(currentNode));
1485
+ const mapped = mapFn(currentNode);
1486
+
1487
+ result.push(mapped);
1434
1488
  }
1435
1489
 
1436
1490
  if (Array.isArray(currentNode.children)) {
@@ -1447,12 +1501,15 @@ const getSelectedNodes = ({
1447
1501
  }
1448
1502
  }
1449
1503
 
1450
- if (!skipFn(currentNode)) traverse(childNode);
1504
+ if (!skipFn(currentNode)) {
1505
+ traverse(childNode, currentNode?.key);
1506
+ }
1451
1507
  });
1452
1508
  }
1453
1509
  }
1454
1510
 
1455
- traverse(node);
1511
+ traverse(node, node?.key);
1512
+
1456
1513
  return result;
1457
1514
  };
1458
1515
 
@@ -1524,6 +1581,23 @@ const formatDate = ({
1524
1581
  .replace("A", dayPeriod);
1525
1582
  };
1526
1583
 
1584
+ const rebuildFullPath = (nodePath, fullPath, modifierFn) => {
1585
+ // 1. Find parent path
1586
+ const index = fullPath.lastIndexOf(nodePath);
1587
+ if (index === -1) {
1588
+ return fullPath;
1589
+ }
1590
+
1591
+ const parentPath = fullPath.slice(0, index).replace(/\.$/, "");
1592
+
1593
+ // 2. Modify node path (if function provided)
1594
+ const modifiedNodePath =
1595
+ typeof modifierFn === "function" ? modifierFn(nodePath) : nodePath;
1596
+
1597
+ // 3. Rebuild final full path
1598
+ return `${parentPath}.${modifiedNodePath}`.replace(/\.+/g, ".");
1599
+ };
1600
+
1527
1601
  module.exports = {
1528
1602
  generateModifiedRules,
1529
1603
  getFieldsGroupBySchemaId,
@@ -1547,4 +1621,5 @@ module.exports = {
1547
1621
  getSelectedNodes,
1548
1622
  remainingItems,
1549
1623
  formatDate,
1624
+ rebuildFullPath,
1550
1625
  };
package/lib/validate.js CHANGED
@@ -13,6 +13,7 @@ const {
13
13
  getSelectedNodes,
14
14
  remainingItems,
15
15
  formatDate,
16
+ rebuildFullPath,
16
17
  } = require("./helpers");
17
18
 
18
19
  const choices = ["radio", "checkboxes", "dropdown_multiple", "dropdown"];
@@ -431,23 +432,22 @@ const validateMetaRules = (
431
432
  constants.interfaces.ONE_TO_MANY,
432
433
  constants.interfaces.MANY_TO_ONE,
433
434
  constants.interfaces.MANY_TO_ANY,
434
- constants.interfaces.SEO,
435
+
435
436
  "none",
436
437
  ].includes(field?.meta?.interface)) ||
437
438
  (field?.meta?.interface === constants.interfaces.TRANSLATIONS &&
438
439
  !field?.meta?.hidden &&
439
440
  language_codes?.length &&
440
441
  !onlyFormFields) ||
441
- ([constants.interfaces.OBJECT, constants.interfaces.ITEMS].includes(
442
- field?.meta?.interface
443
- ) &&
442
+ ([
443
+ constants.interfaces.OBJECT,
444
+ constants.interfaces.SEO,
445
+ constants.interfaces.ITEMS,
446
+ ].includes(field?.meta?.interface) &&
444
447
  !field?.meta?.hidden &&
445
448
  !onlyFormFields &&
446
449
  getNodes)
447
450
  ) {
448
- const isTranslationChild =
449
- field?.meta?.interface === constants.interfaces.TRANSLATIONS;
450
-
451
451
  getSelectedNodes({
452
452
  node: field,
453
453
  skipFn: (node) =>
@@ -459,12 +459,30 @@ const validateMetaRules = (
459
459
  label: node.display_label,
460
460
  }),
461
461
  actionFn: (node) => {
462
- const staticType = node?.meta?.staticType;
462
+ const isTranslationNode =
463
+ node?.meta?.parentInterface === constants.interfaces.TRANSLATIONS ||
464
+ field?.meta?.interface === constants.interfaces.TRANSLATIONS;
463
465
  let fPath = node.key?.replace("[0][0]", "[0]");
466
+ const types = ["update", "delete", "existing", "create"];
467
+
468
+ const extractFirstType = (key) => {
469
+ const regex = new RegExp(`(${types.join("|")})\\[`, "i");
470
+ const match = key.match(regex);
471
+ return match ? match[1] : null;
472
+ };
473
+
474
+ const staticType =
475
+ node?.meta?.staticType && types.includes(node?.meta?.staticType)
476
+ ? extractFirstType(node.key)
477
+ : null;
478
+
479
+ if (!fPath.includes(currentPath)) {
480
+ fPath = `${currentPath}.${fPath}`;
481
+ }
464
482
 
465
483
  if (staticType === "update") return;
466
- if (staticType === "delete" && !isTranslationChild) return;
467
- if (staticType === "existing" && !isTranslationChild) return;
484
+ if (staticType === "delete" && !isTranslationNode) return;
485
+ if (staticType === "existing" && !isTranslationNode) return;
468
486
 
469
487
  const label = formatLabel(node.display_label);
470
488
  const message = error_messages.REQUIRED.replace("{field}", label);
@@ -484,7 +502,7 @@ const validateMetaRules = (
484
502
  };
485
503
 
486
504
  const isMultiLang =
487
- isTranslationChild &&
505
+ isTranslationNode &&
488
506
  Array.isArray(language_codes) &&
489
507
  language_codes.length > 1;
490
508
 
@@ -494,17 +512,24 @@ const validateMetaRules = (
494
512
  transformedPath,
495
513
  isAdded = false;
496
514
 
497
- if (fPath.includes("create[0].")) {
515
+ if (node?.value?.includes("create[0].")) {
498
516
  const exist = fieldValue?.create?.find(
499
517
  (item) => item.languages_code === lang
500
518
  );
501
519
  if (exist) isAdded = true;
502
-
503
- langPath = fPath.replace("create[0].", `create[${index}].`);
504
- transformedPath = fPath.replace("create[0].", `${lang}.`);
520
+ langPath = rebuildFullPath(node.value, node.key, (path) =>
521
+ path.replace("create[0].", `create[${index}].`)
522
+ );
523
+ transformedPath = rebuildFullPath(node.value, node.key, (path) =>
524
+ path.replace("create[0].", `${lang}.`)
525
+ );
505
526
  } else {
506
- langPath = fPath.replace("create.", `create[${index}].`);
507
- transformedPath = fPath.replace("create.", `${lang}.`);
527
+ langPath = rebuildFullPath(node.value, node.key, (path) =>
528
+ path.replace("create.", `create[${index}].`)
529
+ );
530
+ transformedPath = rebuildFullPath(node.value, node.key, (path) =>
531
+ path.replace("create.", `${lang}.`)
532
+ );
508
533
  }
509
534
 
510
535
  if (!isAdded) buildError(langPath, transformedPath);
@@ -512,7 +537,7 @@ const validateMetaRules = (
512
537
  } else {
513
538
  const singlePath = fPath.replace(
514
539
  "create.",
515
- isTranslationChild ? "create[lang_code]." : "create[0]."
540
+ isTranslationNode ? "create[lang_code]." : "create[0]."
516
541
  );
517
542
  if (
518
543
  constants.interfaces.ITEMS === field?.meta?.interface
@@ -1555,6 +1580,7 @@ const validate = (data) => {
1555
1580
  fieldOptions,
1556
1581
  data.language_codes,
1557
1582
  existingForm,
1583
+ true,
1558
1584
  timeZone
1559
1585
  );
1560
1586
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nox-validation",
3
- "version": "1.6.1",
3
+ "version": "1.6.3",
4
4
  "description": "validate dynamic schema",
5
5
  "main": "index.js",
6
6
  "scripts": {