perfect-payload 1.5.0 → 1.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.
Files changed (3) hide show
  1. package/README.md +791 -350
  2. package/index.js +172 -6
  3. package/package.json +1 -1
package/index.js CHANGED
@@ -537,6 +537,45 @@ export function perfectPayload(
537
537
  );
538
538
  }
539
539
 
540
+ export async function perfectPayloadAsync(
541
+ data = {},
542
+ dataValidationRule = {},
543
+ validPayloadResponse = { statusCode: 200, valid: true },
544
+ inValidPayloadResponse = {
545
+ statusCode: 400,
546
+ valid: false,
547
+ message: "One or more attribute values are invalid",
548
+ },
549
+ ) {
550
+ const validationResult = perfectPayloadStructured(
551
+ data,
552
+ dataValidationRule,
553
+ validPayloadResponse,
554
+ inValidPayloadResponse,
555
+ "",
556
+ {
557
+ skipCustomValidator: true,
558
+ },
559
+ );
560
+
561
+ if (validationResult?.errors?.length) {
562
+ return validationResult;
563
+ }
564
+
565
+ const rowErrors = await runAsyncCustomValidators(
566
+ validationResult?.validatedPayload ?? {},
567
+ dataValidationRule,
568
+ );
569
+ if (rowErrors.length > 0) {
570
+ return {
571
+ ...inValidPayloadResponse,
572
+ errors: rowErrors,
573
+ };
574
+ }
575
+
576
+ return validationResult;
577
+ }
578
+
540
579
  function perfectPayloadStructured(
541
580
  data = {},
542
581
  dataValidationRule = {},
@@ -547,9 +586,11 @@ function perfectPayloadStructured(
547
586
  message: "One or more attribute values are invalid",
548
587
  },
549
588
  basePath = "",
589
+ options = {},
550
590
  ) {
551
591
  let validatedPayload = {};
552
592
  let rowErrors = [];
593
+ const skipCustomValidator = options?.skipCustomValidator === true;
553
594
 
554
595
  for (const attributeName in dataValidationRule) {
555
596
  let addNextError = true;
@@ -765,6 +806,14 @@ function perfectPayloadStructured(
765
806
  {
766
807
  [attributeName]: attributeRules[ruleName],
767
808
  },
809
+ { statusCode: 200, valid: true },
810
+ {
811
+ statusCode: 400,
812
+ valid: false,
813
+ message: "One or more attribute values are invalid",
814
+ },
815
+ "",
816
+ options,
768
817
  );
769
818
 
770
819
  if (errors.length > 0) {
@@ -808,7 +857,6 @@ function perfectPayloadStructured(
808
857
  }
809
858
 
810
859
  break;
811
-
812
860
  case "minItems":
813
861
  if (
814
862
  addNextError &&
@@ -1331,16 +1379,14 @@ function perfectPayloadStructured(
1331
1379
  perfectPayloadStructured(
1332
1380
  attributeValue,
1333
1381
  attributeRules?.[ruleName],
1334
- {
1335
- statusCode: 200,
1336
- valid: true,
1337
- },
1382
+ { statusCode: 200, valid: true },
1338
1383
  {
1339
1384
  statusCode: 400,
1340
1385
  valid: false,
1341
1386
  message: "One or more attribute values are invalid",
1342
1387
  },
1343
1388
  attributePath,
1389
+ options,
1344
1390
  );
1345
1391
 
1346
1392
  if (errors.length === 0) {
@@ -1415,7 +1461,12 @@ function perfectPayloadStructured(
1415
1461
  // ==================================================
1416
1462
 
1417
1463
  case "customValidator":
1418
- if (addNextError && attrExist && attributeValue !== null) {
1464
+ if (
1465
+ !skipCustomValidator &&
1466
+ addNextError &&
1467
+ attrExist &&
1468
+ attributeValue !== null
1469
+ ) {
1419
1470
  const validator = attributeRules?.[ruleName];
1420
1471
 
1421
1472
  if (typeof validator !== "function") {
@@ -1598,3 +1649,118 @@ function isObjectId(id) {
1598
1649
  function isPassedRegex(reExpression, value) {
1599
1650
  return reExpression.test(value);
1600
1651
  }
1652
+
1653
+ async function runAsyncCustomValidators(
1654
+ data = {},
1655
+ dataValidationRule = {},
1656
+ basePath = "",
1657
+ ) {
1658
+ const errors = [];
1659
+
1660
+ for (const attributeName in dataValidationRule) {
1661
+ const attributeRules = dataValidationRule?.[attributeName];
1662
+
1663
+ const attrExist = Object.keys(data ?? {}).includes(attributeName);
1664
+
1665
+ if (!attrExist) {
1666
+ continue;
1667
+ }
1668
+
1669
+ const attributeValue = data?.[attributeName];
1670
+
1671
+ const attributePath = getAttributePath(
1672
+ attributeName,
1673
+ attributeRules,
1674
+ basePath,
1675
+ );
1676
+
1677
+ // customValidator
1678
+ if (
1679
+ Object.prototype.hasOwnProperty.call(
1680
+ attributeRules ?? {},
1681
+ "customValidator",
1682
+ )
1683
+ ) {
1684
+ if (typeof attributeRules.customValidator !== "function") {
1685
+ throw new Error(
1686
+ `perfect-payload:- customValidator must be a function for attribute ${attributePath}`,
1687
+ );
1688
+ }
1689
+
1690
+ if (attributeValue !== null) {
1691
+ const validationResponse = await attributeRules.customValidator(
1692
+ attributeValue,
1693
+ data,
1694
+ );
1695
+
1696
+ if (validationResponse !== true) {
1697
+ addStructuredError(
1698
+ errors,
1699
+ attributePath,
1700
+ attributeRules?.customValidatorCode || "CUSTOM_VALIDATION_FAILED",
1701
+ attributeRules?.customValidatorError ||
1702
+ `Custom validation failed for attribute ${attributePath}`,
1703
+ );
1704
+ }
1705
+ }
1706
+ }
1707
+
1708
+ // nested objectAttr
1709
+ if (
1710
+ attributeValue !== null &&
1711
+ typeof attributeValue === "object" &&
1712
+ !Array.isArray(attributeValue) &&
1713
+ attributeRules?.objectAttr
1714
+ ) {
1715
+ const nestedErrors = await runAsyncCustomValidators(
1716
+ attributeValue,
1717
+ attributeRules.objectAttr,
1718
+ attributePath,
1719
+ );
1720
+
1721
+ errors.push(...nestedErrors);
1722
+ }
1723
+
1724
+ // nested elementConstraints
1725
+ if (Array.isArray(attributeValue) && attributeRules?.elementConstraints) {
1726
+ for (
1727
+ let elementIndex = 0;
1728
+ elementIndex < attributeValue.length;
1729
+ elementIndex++
1730
+ ) {
1731
+ const element = attributeValue[elementIndex];
1732
+
1733
+ const elementErrors = await runAsyncCustomValidators(
1734
+ {
1735
+ [attributeName]: element,
1736
+ },
1737
+ {
1738
+ [attributeName]: attributeRules.elementConstraints,
1739
+ },
1740
+ );
1741
+
1742
+ for (const elementError of elementErrors) {
1743
+ const indexedPath = replaceRootPath(
1744
+ elementError.path,
1745
+ attributeName,
1746
+ `${attributePath}[${elementIndex}]`,
1747
+ );
1748
+
1749
+ errors.push({
1750
+ ...elementError,
1751
+ path: indexedPath,
1752
+
1753
+ message:
1754
+ elementError.code === "CUSTOM_VALIDATION_FAILED" &&
1755
+ elementError.message ===
1756
+ `Custom validation failed for attribute ${elementError.path}`
1757
+ ? `Custom validation failed for attribute ${indexedPath}`
1758
+ : elementError.message,
1759
+ });
1760
+ }
1761
+ }
1762
+ }
1763
+ }
1764
+
1765
+ return errors;
1766
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perfect-payload",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "type": "module",
5
5
  "description": "Lightweight JSON payload validation library with structured errors, nested validation, field paths, and customizable validation rules.",
6
6
  "main": "index.js",