perfect-payload 1.2.3 → 1.3.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 +919 -340
  2. package/index.js +41 -0
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # perfect-payload
2
2
 
3
3
  A lightweight JavaScript payload validation utility for validating API
4
+
4
5
  and JSON payloads with simple rule-based configuration.
5
6
 
6
7
  ## Installation
@@ -8,6 +9,7 @@ and JSON payloads with simple rule-based configuration.
8
9
  ```bash
9
10
 
10
11
  npm install perfect-payload
12
+
11
13
  ```
12
14
 
13
15
  ## Basic Usage
@@ -19,24 +21,30 @@ import { perfectPayload } from "perfect-payload";
19
21
 
20
22
  const payload = {
21
23
  name: "Kiran",
24
+
22
25
  email: "kiran@example.com",
26
+
23
27
  age: 29,
24
28
  };
25
29
 
26
30
  const validationRules = {
27
31
  name: {
28
32
  mandatory: true,
33
+
29
34
  type: "string",
30
35
  },
31
36
 
32
37
  email: {
33
38
  mandatory: true,
39
+
34
40
  type: "email",
35
41
  },
36
42
 
37
43
  age: {
38
44
  mandatory: true,
45
+
39
46
  type: "number",
47
+
40
48
  min: 18,
41
49
  },
42
50
  };
@@ -51,19 +59,31 @@ console.log(result);
51
59
  ```js
52
60
 
53
61
  {
54
-   statusCode: 200,
55
-   valid: true,
56
-   validatedPayload: {
57
-     name: "Kiran",
58
-     email: "kiran@example.com",
59
-     age: 29
60
-   }
62
+
63
+ statusCode: 200,
64
+
65
+ valid: true,
66
+
67
+ validatedPayload: {
68
+
69
+ name: "Kiran",
70
+
71
+ email: "kiran@example.com",
72
+
73
+ age: 29
74
+
75
+ }
76
+
61
77
  }
78
+
62
79
  ```
63
80
 
64
81
  **Note:** The validatedPayload contains only the fields defined in the
82
+
65
83
  schema, automatically filtering out any extra attributes. You can use it
84
+
66
85
  to safely overwrite request.body or assign it to a new request property
86
+
67
87
  (such as validatedBody, sanitisedData or parsedBody).
68
88
 
69
89
  ### Invalid Response
@@ -71,17 +91,29 @@ to safely overwrite request.body or assign it to a new request property
71
91
  ```js
72
92
 
73
93
  {
74
-   statusCode: 400,
75
-   valid: false,
76
-   message: "One or more attribute values are invalid",
77
-   errors: [
78
-     {
79
-       path: "email",
80
-       code: "INVALID_EMAIL",
81
-       message: "Invalid email format for attribute email"
82
-     }
83
-   ]
94
+
95
+ statusCode: 400,
96
+
97
+ valid: false,
98
+
99
+ message: "One or more attribute values are invalid",
100
+
101
+ errors: [
102
+
103
+ {
104
+
105
+ path: "email",
106
+
107
+ code: "INVALID_EMAIL",
108
+
109
+ message: "Invalid email format for attribute email"
110
+
111
+ }
112
+
113
+ ]
114
+
84
115
  }
116
+
85
117
  ```
86
118
 
87
119
  Each error returned by `perfectPayload()` contains:
@@ -89,17 +121,26 @@ Each error returned by `perfectPayload()` contains:
89
121
  ```js
90
122
 
91
123
  {
92
-   path: "field.path",
93
-   code: "ERROR_CODE",
94
-   message: "Human readable validation message"
124
+
125
+ path: "field.path",
126
+
127
+ code: "ERROR_CODE",
128
+
129
+ message: "Human readable validation message"
130
+
95
131
  }
132
+
96
133
  ```
97
134
 
98
- - `path` identifies the exact field that failed validation.
99
- - `code` provides a stable machine-readable validation error code.
100
- - `message` provides a human-readable description of the validation
101
- failure.
102
- - Submitted payload values are not included in default error messages.
135
+ \- `path` identifies the exact field that failed validation.
136
+
137
+ \- `code` provides a stable machine-readable validation error code.
138
+
139
+ \- `message` provides a human-readable description of the validation
140
+
141
+ failure.
142
+
143
+ \- Submitted payload values are not included in default error messages.
103
144
 
104
145
  ## Legacy API
105
146
 
@@ -110,9 +151,11 @@ import { perfectPayloadV1 } from "perfect-payload";
110
151
  ```
111
152
 
112
153
  `perfectPayloadV1()` is deprecated and will no longer be supported after
113
- **\*\*March 31, 2027\*\***.
154
+
155
+ **March 31, 2027**.
114
156
 
115
157
  Existing applications can continue using it during the migration period,
158
+
116
159
  but all new implementations should use:
117
160
 
118
161
  ```js
@@ -131,15 +174,20 @@ while the new `perfectPayload()` API returns structured errors:
131
174
  errors: [
132
175
  {
133
176
  path: "email",
177
+
134
178
  code: "INVALID_EMAIL",
179
+
135
180
  message: "Invalid email format for attribute email",
136
181
  },
137
182
  ];
138
183
  ```
139
184
 
140
185
  **Note:** If an inValidPayloadResponse is provided, the system returns
186
+
141
187
  it alongside an automatically generated errors property. Do not include
188
+
142
189
  your own errors attribute inside the custom inValidPayloadResponse
190
+
143
191
  object.
144
192
 
145
193
  ## Validation Rules
@@ -162,7 +210,7 @@ const rules = {
162
210
 
163
211
  Error code: `REQUIRED`
164
212
 
165
- ---
213
+ **---**
166
214
 
167
215
  ### `allowNull`
168
216
 
@@ -182,7 +230,7 @@ const rules = {
182
230
 
183
231
  Error code: `NULL_NOT_ALLOWED`
184
232
 
185
- ---
233
+ **---**
186
234
 
187
235
  ### `allowEmptyObject`
188
236
 
@@ -196,6 +244,7 @@ Example:
196
244
  const rules = {
197
245
  address: {
198
246
  type: "object",
247
+
199
248
  allowEmptyObject: false,
200
249
  },
201
250
  };
@@ -203,7 +252,7 @@ const rules = {
203
252
 
204
253
  Error code: `EMPTY_OBJECT_NOT_ALLOWED`
205
254
 
206
- ---
255
+ **---**
207
256
 
208
257
  ### `allowEmptyArray`
209
258
 
@@ -217,6 +266,7 @@ Example:
217
266
  const rules = {
218
267
  products: {
219
268
  type: "array",
269
+
220
270
  allowEmptyArray: false,
221
271
  },
222
272
  };
@@ -224,7 +274,7 @@ const rules = {
224
274
 
225
275
  Error code: `EMPTY_ARRAY_NOT_ALLOWED`
226
276
 
227
- ---
277
+ **---**
228
278
 
229
279
  ### `type`
230
280
 
@@ -235,19 +285,33 @@ Supported values:
235
285
  ```text
236
286
 
237
287
  number
288
+
238
289
  string
290
+
239
291
  boolean
292
+
240
293
  email
294
+
241
295
  url
296
+
242
297
  enum
298
+
243
299
  uuid
300
+
244
301
  uuidv1
302
+
245
303
  uuidv3
304
+
246
305
  uuidv4
306
+
247
307
  uuidv5
308
+
248
309
  objectId
310
+
249
311
  array
312
+
250
313
  object
314
+
251
315
  ```
252
316
 
253
317
  Example:
@@ -257,9 +321,11 @@ const rules = {
257
321
  age: {
258
322
  type: "number",
259
323
  },
324
+
260
325
  email: {
261
326
  type: "email",
262
327
  },
328
+
263
329
  active: {
264
330
  type: "boolean",
265
331
  },
@@ -280,6 +346,7 @@ Example:
280
346
  const rules = {
281
347
  status: {
282
348
  type: "enum",
349
+
283
350
  enumValues: ["active", "inactive", "blocked", 1, 0],
284
351
  },
285
352
  };
@@ -299,6 +366,7 @@ Example:
299
366
  const rules = {
300
367
  status: {
301
368
  type: "enum",
369
+
302
370
  enumValues: ["active", "inactive", "blocked"],
303
371
  },
304
372
  };
@@ -311,18 +379,28 @@ Possible error codes for types:
311
379
  ```text
312
380
 
313
381
  INVALID_TYPE
382
+
314
383
  INVALID_EMAIL
384
+
315
385
  INVALID_URL
386
+
316
387
  INVALID_ENUM
388
+
317
389
  INVALID_UUID
390
+
318
391
  INVALID_UUID_V1
392
+
319
393
  INVALID_UUID_V3
394
+
320
395
  INVALID_UUID_V4
396
+
321
397
  INVALID_UUID_V5
398
+
322
399
  INVALID_OBJECT_ID
400
+
323
401
  ```
324
402
 
325
- ---
403
+ **---**
326
404
 
327
405
  ### `regex`
328
406
 
@@ -336,6 +414,7 @@ Example:
336
414
  const rules = {
337
415
  employeeCode: {
338
416
  type: "string",
417
+
339
418
  regex: /^[A-Z]{3}[0-9]{3}$/,
340
419
  },
341
420
  };
@@ -343,7 +422,7 @@ const rules = {
343
422
 
344
423
  Error code: `REGEX_MISMATCH`
345
424
 
346
- ---
425
+ **---**
347
426
 
348
427
  ### `minLength`
349
428
 
@@ -357,6 +436,7 @@ Example:
357
436
  const rules = {
358
437
  username: {
359
438
  type: "string",
439
+
360
440
  minLength: 5,
361
441
  },
362
442
  };
@@ -364,7 +444,7 @@ const rules = {
364
444
 
365
445
  Error code: `MIN_LENGTH`
366
446
 
367
- ---
447
+ **---**
368
448
 
369
449
  ### `maxLength`
370
450
 
@@ -378,6 +458,7 @@ Example:
378
458
  const rules = {
379
459
  username: {
380
460
  type: "string",
461
+
381
462
  maxLength: 20,
382
463
  },
383
464
  };
@@ -385,7 +466,7 @@ const rules = {
385
466
 
386
467
  Error code: `MAX_LENGTH`
387
468
 
388
- ---
469
+ **---**
389
470
 
390
471
  ### `preventDecimal`
391
472
 
@@ -399,6 +480,7 @@ Example:
399
480
  const rules = {
400
481
  quantity: {
401
482
  type: "number",
483
+
402
484
  preventDecimal: true,
403
485
  },
404
486
  };
@@ -406,7 +488,7 @@ const rules = {
406
488
 
407
489
  Error code: `DECIMAL_NOT_ALLOWED`
408
490
 
409
- ---
491
+ **---**
410
492
 
411
493
  ### `min`
412
494
 
@@ -420,6 +502,7 @@ Example:
420
502
  const rules = {
421
503
  age: {
422
504
  type: "number",
505
+
423
506
  min: 18,
424
507
  },
425
508
  };
@@ -427,7 +510,7 @@ const rules = {
427
510
 
428
511
  Error code: `MIN_VALUE`
429
512
 
430
- ---
513
+ **---**
431
514
 
432
515
  ### `max`
433
516
 
@@ -441,6 +524,7 @@ Example:
441
524
  const rules = {
442
525
  quantity: {
443
526
  type: "number",
527
+
444
528
  max: 100,
445
529
  },
446
530
  };
@@ -448,7 +532,7 @@ const rules = {
448
532
 
449
533
  Error code: `MAX_VALUE`
450
534
 
451
- ---
535
+ **---**
452
536
 
453
537
  ### `range`
454
538
 
@@ -462,6 +546,7 @@ Example:
462
546
  const rules = {
463
547
  marks: {
464
548
  type: "number",
549
+
465
550
  range: "0-100",
466
551
  },
467
552
  };
@@ -469,7 +554,7 @@ const rules = {
469
554
 
470
555
  Error code: `OUT_OF_RANGE`
471
556
 
472
- ---
557
+ **---**
473
558
 
474
559
  ### `elementConstraints`
475
560
 
@@ -481,8 +566,10 @@ Example:
481
566
  const rules = {
482
567
  marks: {
483
568
  type: "array",
569
+
484
570
  elementConstraints: {
485
571
  type: "number",
572
+
486
573
  range: "0-100",
487
574
  },
488
575
  },
@@ -495,14 +582,20 @@ Example error:
495
582
 
496
583
  {
497
584
 
498
-   path: "marks[2]",
499
-   code: "OUT_OF_RANGE",
500
-   message:
501
-     "Attribute marks[2] should have a value between 0 and 100"
585
+ path: "marks[2]",
586
+
587
+ code: "OUT_OF_RANGE",
588
+
589
+ message:
590
+
591
+ "Attribute marks[2] should have a value between 0 and 100"
592
+
502
593
  }
594
+
503
595
  ```
504
596
 
505
597
  When `elementConstraintsError` is explicitly provided, the error code
598
+
506
599
  is: `INVALID_ARRAY_ELEMENT`
507
600
 
508
601
  Example:
@@ -511,15 +604,17 @@ Example:
511
604
  const rules = {
512
605
  marks: {
513
606
  type: "array",
607
+
514
608
  elementConstraints: {
515
609
  type: "number",
516
610
  },
611
+
517
612
  elementConstraintsError: "Every marks element must be a number",
518
613
  },
519
614
  };
520
615
  ```
521
616
 
522
- ---
617
+ **---**
523
618
 
524
619
  ### `objectAttr`
525
620
 
@@ -531,17 +626,22 @@ Example:
531
626
  const rules = {
532
627
  address: {
533
628
  type: "object",
629
+
534
630
  objectAttr: {
535
631
  city: {
536
632
  mandatory: true,
633
+
537
634
  type: "string",
538
635
  },
636
+
539
637
  location: {
540
638
  type: "object",
639
+
541
640
  objectAttr: {
542
641
  latitude: {
543
642
  type: "number",
544
643
  },
644
+
545
645
  longitude: {
546
646
  type: "number",
547
647
  },
@@ -557,14 +657,20 @@ Nested errors include the complete field path:
557
657
  ```js
558
658
 
559
659
  {
560
-   path: "address.location.latitude",
561
-   code: "INVALID_TYPE",
562
-   message:
563
-     "Invalid type for attribute address.location.latitude, required number value"
660
+
661
+ path: "address.location.latitude",
662
+
663
+ code: "INVALID_TYPE",
664
+
665
+ message:
666
+
667
+ "Invalid type for attribute address.location.latitude, required number value"
668
+
564
669
  }
670
+
565
671
  ```
566
672
 
567
- ---
673
+ **---**
568
674
 
569
675
  ### `dependency`
570
676
 
@@ -576,11 +682,14 @@ Example:
576
682
  const rules = {
577
683
  minSalary: {
578
684
  type: "number",
685
+
579
686
  dependency: {
580
687
  maxSalary: {
581
688
  setDependencyRule: (minSalary, maxSalary) => ({
582
689
  type: "number",
690
+
583
691
  min: minSalary + 1,
692
+
584
693
  minError: "maxSalary must be more than minSalary",
585
694
  }),
586
695
  },
@@ -595,61 +704,182 @@ Example error:
595
704
 
596
705
  {
597
706
 
598
-   path: "maxSalary",
599
-   code: "MIN_VALUE",
600
-   message:
601
-     "maxSalary must be more than minSalary"
707
+ path: "maxSalary",
708
+
709
+ code: "MIN_VALUE",
710
+
711
+ message:
712
+
713
+ "maxSalary must be more than minSalary"
714
+
715
+ }
716
+
717
+ ```
718
+
719
+ **---**
720
+
721
+ ### `customValidator`
722
+
723
+ Allows you to define custom synchronous validation logic for a field when the built-in validation rules are not enough.
724
+
725
+ The validator receives the field value and the complete payload:
726
+
727
+ ```js
728
+ customValidator: (value, payload) => {
729
+ return true;
730
+ };
731
+ ```
732
+
733
+ The validator must return `true` to pass validation. Any other return value causes validation to fail.
734
+
735
+ Example:
736
+
737
+ ```js
738
+ const rules = {
739
+ username: {
740
+ mandatory: true,
741
+ type: "string",
742
+ customValidator: (value) => {
743
+ return !value.toLowerCase().includes("admin");
744
+ },
745
+ customValidatorCode: "RESERVED_USERNAME",
746
+ customValidatorError: "Username cannot contain admin",
747
+ },
748
+ };
749
+ ```
750
+
751
+ For this payload:
752
+
753
+ ```js
754
+ const payload = {
755
+ username: "admin_kiran",
756
+ };
757
+ ```
758
+
759
+ The validation error is:
760
+
761
+ ```js
762
+ {
763
+ path: "username",
764
+ code: "RESERVED_USERNAME",
765
+ message: "Username cannot contain admin"
766
+ }
767
+ ```
768
+
769
+ If `customValidatorCode` and `customValidatorError` are not provided, the default error is:
770
+
771
+ ```js
772
+ {
773
+ path: "username",
774
+ code: "CUSTOM_VALIDATION_FAILED",
775
+ message: "Custom validation failed for attribute username"
602
776
  }
603
777
  ```
604
778
 
779
+ The complete payload can be used as the second argument when required:
780
+
781
+ ```js
782
+ const rules = {
783
+ limit: {
784
+ type: "number",
785
+ },
786
+ amount: {
787
+ type: "number",
788
+ customValidator: (value, payload) => {
789
+ return value <= payload.limit;
790
+ },
791
+ customValidatorCode: "LIMIT_EXCEEDED",
792
+ customValidatorError: "Amount cannot exceed limit",
793
+ },
794
+ };
795
+ ```
796
+
797
+ `customValidator` also works with nested objects and array `elementConstraints`. The generated structured error automatically contains the corresponding nested or array path.
798
+
799
+ **Important:** `customValidator` is synchronous. An `async` validator or a validator that returns a Promise is not supported and throws an error. Asynchronous validation is not part of this feature.
800
+
801
+ Error code when no custom code is provided: `CUSTOM_VALIDATION_FAILED`
802
+
605
803
  ## Error Codes
606
804
 
607
805
  `perfectPayload()` currently exposes the following machine-readable
806
+
608
807
  validation error codes:
609
808
 
610
809
  ```text
611
810
 
612
811
  REQUIRED
812
+
613
813
  NULL_NOT_ALLOWED
814
+
614
815
  EMPTY_OBJECT_NOT_ALLOWED
816
+
615
817
  EMPTY_ARRAY_NOT_ALLOWED
818
+
616
819
  INVALID_ARRAY_ELEMENT
820
+
617
821
  REGEX_MISMATCH
822
+
618
823
  INVALID_TYPE
824
+
619
825
  INVALID_EMAIL
826
+
620
827
  INVALID_URL
828
+
621
829
  INVALID_ENUM
830
+
622
831
  INVALID_UUID
832
+
623
833
  INVALID_UUID_V1
834
+
624
835
  INVALID_UUID_V3
836
+
625
837
  INVALID_UUID_V4
838
+
626
839
  INVALID_UUID_V5
840
+
627
841
  INVALID_OBJECT_ID
842
+
628
843
  MIN_LENGTH
844
+
629
845
  MAX_LENGTH
846
+
630
847
  DECIMAL_NOT_ALLOWED
848
+
631
849
  MIN_VALUE
850
+
632
851
  MAX_VALUE
852
+
633
853
  OUT_OF_RANGE
854
+
634
855
  ```
635
856
 
636
857
  These codes are designed for programmatic handling while `message`
858
+
637
859
  remains suitable for human-readable API responses.
638
860
 
639
861
  For example:
640
862
 
641
863
  ```js
864
+
642
865
  const result = perfectPayload(payload, validationRules);
643
866
 
644
867
  if (!result.valid) {
868
+
645
869
  const emailError = result.errors.find(
870
+
646
871
  (error) => error.code === "INVALID_EMAIL",
872
+
647
873
  );
648
874
 
649
875
  if (emailError) {
650
- // Handle invalid email
876
+
877
+ *// Handle invalid email*
878
+
651
879
  }
880
+
652
881
  }
882
+
653
883
  ```
654
884
 
655
885
  ## Custom Error Messages
@@ -657,15 +887,21 @@ if (!result.valid) {
657
887
  Every validation rule can use its corresponding custom error message.
658
888
 
659
889
  Custom messages replace the default human-readable `message` while
890
+
660
891
  keeping the same structured error format:
661
892
 
662
893
  ```js
663
894
 
664
895
  {
665
-   path: "email",
666
-   code: "INVALID_EMAIL",
667
-   message: "Email address is invalid"
896
+
897
+ path: "email",
898
+
899
+ code: "INVALID_EMAIL",
900
+
901
+ message: "Email address is invalid"
902
+
668
903
  }
904
+
669
905
  ```
670
906
 
671
907
  Example:
@@ -674,8 +910,11 @@ Example:
674
910
  const rules = {
675
911
  email: {
676
912
  mandatory: true,
913
+
677
914
  type: "email",
915
+
678
916
  mandatoryError: "Email is required",
917
+
679
918
  typeError: "Email address is invalid",
680
919
  },
681
920
  };
@@ -686,10 +925,15 @@ If `email` is missing:
686
925
  ```js
687
926
 
688
927
  {
689
-   path: "email",
690
-   code: "REQUIRED",
691
-   message: "Email is required"
928
+
929
+ path: "email",
930
+
931
+ code: "REQUIRED",
932
+
933
+ message: "Email is required"
934
+
692
935
  }
936
+
693
937
  ```
694
938
 
695
939
  If `email` is present but invalid:
@@ -697,70 +941,88 @@ If `email` is present but invalid:
697
941
  ```js
698
942
 
699
943
  {
700
-   path: "email",
701
-   code: "INVALID_EMAIL",
702
-   message: "Email address is invalid"
944
+
945
+ path: "email",
946
+
947
+ code: "INVALID_EMAIL",
948
+
949
+ message: "Email address is invalid"
950
+
703
951
  }
952
+
704
953
  ```
705
954
 
706
955
  ### Supported Custom Error Properties
707
956
 
708
- \| Validation Rule      \| Custom Error Property     \|
957
+ \| Validation Rule \| Custom Error Property \|
709
958
 
710
959
  \| -------------------- \| ------------------------- \|
711
960
 
712
- \| `mandatory`          \| `mandatoryError`          \|
961
+ \| `mandatory` \| `mandatoryError` \|
713
962
 
714
- \| `allowNull`          \| `allowNullError`          \|
963
+ \| `allowNull` \| `allowNullError` \|
715
964
 
716
- \| `allowEmptyObject`   \| `emptyObjectError`        \|
965
+ \| `allowEmptyObject` \| `emptyObjectError` \|
717
966
 
718
- \| `allowEmptyArray`    \| `emptyArrayError`         \|
967
+ \| `allowEmptyArray` \| `emptyArrayError` \|
719
968
 
720
969
  \| `elementConstraints` \| `elementConstraintsError` \|
721
970
 
722
- \| `regex`              \| `regexError`              \|
971
+ \| `regex` \| `regexError` \|
723
972
 
724
- \| `type`               \| `typeError`               \|
973
+ \| `type` \| `typeError` \|
725
974
 
726
- \| `minLength`          \| `minLengthError`          \|
975
+ \| `minLength` \| `minLengthError` \|
727
976
 
728
- \| `maxLength`          \| `maxLengthError`          \|
977
+ \| `maxLength` \| `maxLengthError` \|
729
978
 
730
- \| `preventDecimal`     \| `preventDecimalError`     \|
979
+ \| `preventDecimal` \| `preventDecimalError` \|
731
980
 
732
- \| `min`                \| `minError`                \|
981
+ \| `min` \| `minError` \|
733
982
 
734
- \| `max`                \| `maxError`                \|
983
+ \| `max` \| `maxError` \|
735
984
 
736
- \| `range`              \| `rangeError`              \|
985
+ \| `range` \| `rangeError` \|
737
986
 
738
987
  ### Example with Multiple Custom Errors
739
988
 
740
989
  ```js
741
990
  const payload = {
742
991
  username: "ab",
992
+
743
993
  age: 15,
994
+
744
995
  score: 120,
745
996
  };
746
997
 
747
998
  const rules = {
748
999
  username: {
749
1000
  mandatory: true,
1001
+
750
1002
  type: "string",
1003
+
751
1004
  minLength: 3,
1005
+
752
1006
  mandatoryError: "Username is required",
1007
+
753
1008
  typeError: "Username must be a string",
1009
+
754
1010
  minLengthError: "Username must contain at least 3 characters",
755
1011
  },
1012
+
756
1013
  age: {
757
1014
  type: "number",
1015
+
758
1016
  min: 18,
1017
+
759
1018
  minError: "Age must be at least 18",
760
1019
  },
1020
+
761
1021
  score: {
762
1022
  type: "number",
1023
+
763
1024
  range: "0-100",
1025
+
764
1026
  rangeError: "Score must be between 0 and 100",
765
1027
  },
766
1028
  };
@@ -773,72 +1035,109 @@ Example result:
773
1035
  ```js
774
1036
 
775
1037
  {
776
-   statusCode: 400,
777
-   valid: false,
778
-   message:
779
-     "One or more attribute values are invalid",
780
-   errors: [
781
-     {
782
-       path: "username",
783
-       code: "MIN_LENGTH",
784
-       message:
785
-         "Username must contain at least 3 characters"
786
-     },
787
-     {
788
-       path: "age",
789
-       code: "MIN_VALUE",
790
-       message:
791
-         "Age must be at least 18"
792
-     },
793
-     {
794
-       path: "score",
795
-       code: "OUT_OF_RANGE",
796
-       message:
797
-         "Score must be between 0 and 100"
798
-     }
799
-   ]
800
- }
801
- ```
802
1038
 
803
- ### Custom Messages and Error Codes
1039
+ statusCode: 400,
804
1040
 
805
- Custom messages only replace the `message`.
1041
+ valid: false,
806
1042
 
807
- They do not change the validation error `code`.
1043
+ message:
808
1044
 
809
- For example:
1045
+ "One or more attribute values are invalid",
810
1046
 
811
- ```js
812
- const rules = {
813
- age: {
814
- type: "number",
815
- min: 18,
816
- minError: "You must be 18 or older",
817
- },
818
- };
819
- ```
1047
+ errors: [
820
1048
 
821
- Still returns:
1049
+ {
822
1050
 
823
- ```js
1051
+ path: "username",
824
1052
 
825
- {
826
-   path: "age",
827
-   code: "MIN_VALUE",
828
-   message: "You must be 18 or older"
829
- }
830
- ```
1053
+ code: "MIN_LENGTH",
1054
+
1055
+ message:
1056
+
1057
+ "Username must contain at least 3 characters"
1058
+
1059
+ },
1060
+
1061
+ {
1062
+
1063
+ path: "age",
1064
+
1065
+ code: "MIN_VALUE",
1066
+
1067
+ message:
1068
+
1069
+ "Age must be at least 18"
1070
+
1071
+ },
1072
+
1073
+ {
1074
+
1075
+ path: "score",
1076
+
1077
+ code: "OUT_OF_RANGE",
1078
+
1079
+ message:
1080
+
1081
+ "Score must be between 0 and 100"
1082
+
1083
+ }
1084
+
1085
+ ]
1086
+
1087
+ }
1088
+
1089
+ ```
1090
+
1091
+ ### Custom Messages and Error Codes
1092
+
1093
+ Custom messages only replace the `message`.
1094
+
1095
+ They do not change the validation error `code`.
1096
+
1097
+ For example:
1098
+
1099
+ ```js
1100
+ const rules = {
1101
+ age: {
1102
+ type: "number",
1103
+
1104
+ min: 18,
1105
+
1106
+ minError: "You must be 18 or older",
1107
+ },
1108
+ };
1109
+ ```
1110
+
1111
+ Still returns:
1112
+
1113
+ ```js
1114
+
1115
+ {
1116
+
1117
+ path: "age",
1118
+
1119
+ code: "MIN_VALUE",
1120
+
1121
+ message: "You must be 18 or older"
1122
+
1123
+ }
1124
+
1125
+ ```
831
1126
 
832
1127
  This makes it possible to:
833
1128
 
834
- - show custom messages to API consumers
835
- - use stable error codes in application logic
836
- - change user-facing wording without changing programmatic error
837
- handling
1129
+ \- show custom messages to API consumers
1130
+
1131
+ \- use stable error codes in application logic
1132
+
1133
+ \- change user-facing wording without changing programmatic error
1134
+
1135
+ handling
838
1136
 
839
1137
  ## Custom Response Objects
840
1138
 
841
1139
  `perfectPayload()` allows you to customize both the valid and invalid
1140
+
842
1141
  response objects.
843
1142
 
844
1143
  The third argument is the custom valid response.
@@ -852,7 +1151,9 @@ Example:
852
1151
  ```js
853
1152
  const customValidResponse = {
854
1153
  statusCode: 201,
1154
+
855
1155
  valid: true,
1156
+
856
1157
  message: "Payload validated successfully",
857
1158
  };
858
1159
 
@@ -864,15 +1165,25 @@ When validation succeeds, `validatedPayload` is automatically added:
864
1165
  ```js
865
1166
 
866
1167
  {
867
-   statusCode: 201,
868
-   valid: true,
869
-   message: "Payload validated successfully",
870
-   validatedPayload: {
871
-     name: "Kiran",
872
-     email: "kiran@example.com",
873
-     age: 29
874
-   }
1168
+
1169
+ statusCode: 201,
1170
+
1171
+ valid: true,
1172
+
1173
+ message: "Payload validated successfully",
1174
+
1175
+ validatedPayload: {
1176
+
1177
+ name: "Kiran",
1178
+
1179
+ email: "kiran@example.com",
1180
+
1181
+ age: 29
1182
+
1183
+ }
1184
+
875
1185
  }
1186
+
876
1187
  ```
877
1188
 
878
1189
  ### Custom Invalid Response
@@ -882,14 +1193,19 @@ Example:
882
1193
  ```js
883
1194
  const customInvalidResponse = {
884
1195
  statusCode: 422,
1196
+
885
1197
  valid: false,
1198
+
886
1199
  message: "Payload validation failed",
887
1200
  };
888
1201
 
889
1202
  const result = perfectPayload(
890
1203
  payload,
1204
+
891
1205
  validationRules,
1206
+
892
1207
  undefined,
1208
+
893
1209
  customInvalidResponse,
894
1210
  );
895
1211
  ```
@@ -900,18 +1216,30 @@ When validation fails, `errors` is automatically added:
900
1216
 
901
1217
  {
902
1218
 
903
-   statusCode: 422,
904
-   valid: false,
905
-   message: "Payload validation failed",
906
-   errors: [
907
-     {
908
-       path: "email",
909
-       code: "INVALID_EMAIL",
910
-       message:
911
-         "Invalid email format for attribute email"
912
-     }
913
-   ]
1219
+ statusCode: 422,
1220
+
1221
+ valid: false,
1222
+
1223
+ message: "Payload validation failed",
1224
+
1225
+ errors: [
1226
+
1227
+ {
1228
+
1229
+ path: "email",
1230
+
1231
+ code: "INVALID_EMAIL",
1232
+
1233
+ message:
1234
+
1235
+ "Invalid email format for attribute email"
1236
+
1237
+ }
1238
+
1239
+ ]
1240
+
914
1241
  }
1242
+
915
1243
  ```
916
1244
 
917
1245
  ### Custom Valid and Invalid Responses Together
@@ -919,30 +1247,39 @@ When validation fails, `errors` is automatically added:
919
1247
  ```js
920
1248
  const customValidResponse = {
921
1249
  statusCode: 201,
1250
+
922
1251
  valid: true,
1252
+
923
1253
  message: "CUSTOM_VALID_RESPONSE",
924
1254
  };
925
1255
 
926
1256
  const customInvalidResponse = {
927
1257
  statusCode: 422,
1258
+
928
1259
  valid: false,
1260
+
929
1261
  message: "CUSTOM_INVALID_RESPONSE",
930
1262
  };
931
1263
 
932
1264
  const result = perfectPayload(
933
1265
  payload,
1266
+
934
1267
  validationRules,
1268
+
935
1269
  customValidResponse,
1270
+
936
1271
  customInvalidResponse,
937
1272
  );
938
1273
  ```
939
1274
 
940
1275
  The response object you provide is preserved, while `perfectPayload()`
1276
+
941
1277
  automatically adds either:
942
1278
 
943
1279
  ```text
944
1280
 
945
1281
  validatedPayload
1282
+
946
1283
  ```
947
1284
 
948
1285
  for successful validation, or:
@@ -950,6 +1287,7 @@ for successful validation, or:
950
1287
  ```text
951
1288
 
952
1289
  errors
1290
+
953
1291
  ```
954
1292
 
955
1293
  for failed validation.
@@ -957,17 +1295,25 @@ for failed validation.
957
1295
  ## Default Responses
958
1296
 
959
1297
  If no custom response objects are provided, the default valid response
1298
+
960
1299
  is:
961
1300
 
962
1301
  ```js
963
1302
 
964
1303
  {
965
-   statusCode: 200,
966
-   valid: true,
967
-   validatedPayload: {
968
-     // validated fields
969
-   }
1304
+
1305
+ statusCode: 200,
1306
+
1307
+ valid: true,
1308
+
1309
+ validatedPayload: {
1310
+
1311
+ *// validated fields*
1312
+
1313
+ }
1314
+
970
1315
  }
1316
+
971
1317
  ```
972
1318
 
973
1319
  The default invalid response is:
@@ -975,25 +1321,39 @@ The default invalid response is:
975
1321
  ```js
976
1322
 
977
1323
  {
978
-   statusCode: 400,
979
-   valid: false,
980
-   message: "One or more attribute values are invalid",
981
-   errors: [
982
-     {
983
-       path: "field",
984
-       code: "ERROR_CODE",
985
-       message: "Validation error message"
986
-     }
987
-   ]
1324
+
1325
+ statusCode: 400,
1326
+
1327
+ valid: false,
1328
+
1329
+ message: "One or more attribute values are invalid",
1330
+
1331
+ errors: [
1332
+
1333
+ {
1334
+
1335
+ path: "field",
1336
+
1337
+ code: "ERROR_CODE",
1338
+
1339
+ message: "Validation error message"
1340
+
1341
+ }
1342
+
1343
+ ]
1344
+
988
1345
  }
1346
+
989
1347
  ```
990
1348
 
991
1349
  ## Nested Objects and Array Field Paths
992
1350
 
993
1351
  `perfectPayload()` returns the exact location of a validation failure
1352
+
994
1353
  through the `path` property.
995
1354
 
996
1355
  This makes validation errors easier to map to API fields, forms, logs,
1356
+
997
1357
  and frontend components.
998
1358
 
999
1359
  ### Top-Level Field
@@ -1011,10 +1371,15 @@ An error can be returned as:
1011
1371
  ```js
1012
1372
 
1013
1373
  {
1014
-   path: "email",
1015
-   code: "INVALID_EMAIL",
1016
-   message: "Invalid email format for attribute email"
1374
+
1375
+ path: "email",
1376
+
1377
+ code: "INVALID_EMAIL",
1378
+
1379
+ message: "Invalid email format for attribute email"
1380
+
1017
1381
  }
1382
+
1018
1383
  ```
1019
1384
 
1020
1385
  ### Nested Object
@@ -1025,8 +1390,10 @@ Use `objectAttr` to validate properties inside an object.
1025
1390
  const payload = {
1026
1391
  address: {
1027
1392
  city: "Bengaluru",
1393
+
1028
1394
  location: {
1029
1395
  latitude: "12.9716",
1396
+
1030
1397
  longitude: 77.5946,
1031
1398
  },
1032
1399
  },
@@ -1035,16 +1402,20 @@ const payload = {
1035
1402
  const rules = {
1036
1403
  address: {
1037
1404
  type: "object",
1405
+
1038
1406
  objectAttr: {
1039
1407
  city: {
1040
1408
  type: "string",
1041
1409
  },
1410
+
1042
1411
  location: {
1043
1412
  type: "object",
1413
+
1044
1414
  objectAttr: {
1045
1415
  latitude: {
1046
1416
  type: "number",
1047
1417
  },
1418
+
1048
1419
  longitude: {
1049
1420
  type: "number",
1050
1421
  },
@@ -1058,16 +1429,23 @@ const result = perfectPayload(payload, rules);
1058
1429
  ```
1059
1430
 
1060
1431
  Because `latitude` is a string instead of a number, the error contains
1432
+
1061
1433
  its complete nested path:
1062
1434
 
1063
1435
  ```js
1064
1436
 
1065
1437
  {
1066
-   path: "address.location.latitude",
1067
-   code: "INVALID_TYPE",
1068
-   message:
1069
-     "Invalid type for attribute address.location.latitude, required number value"
1438
+
1439
+ path: "address.location.latitude",
1440
+
1441
+ code: "INVALID_TYPE",
1442
+
1443
+ message:
1444
+
1445
+ "Invalid type for attribute address.location.latitude, required number value"
1446
+
1070
1447
  }
1448
+
1071
1449
  ```
1072
1450
 
1073
1451
  Nested paths use dot notation:
@@ -1075,13 +1453,17 @@ Nested paths use dot notation:
1075
1453
  ```text
1076
1454
 
1077
1455
  address.city
1456
+
1078
1457
  address.location.latitude
1458
+
1079
1459
  address.location.longitude
1460
+
1080
1461
  ```
1081
1462
 
1082
1463
  ### Array Elements
1083
1464
 
1084
1465
  When `elementConstraints` validation fails, the array index is included
1466
+
1085
1467
  in the error path.
1086
1468
 
1087
1469
  ```js
@@ -1092,8 +1474,10 @@ const payload = {
1092
1474
  const rules = {
1093
1475
  marks: {
1094
1476
  type: "array",
1477
+
1095
1478
  elementConstraints: {
1096
1479
  type: "number",
1480
+
1097
1481
  range: "0-100",
1098
1482
  },
1099
1483
  },
@@ -1107,11 +1491,17 @@ The invalid third element is reported as:
1107
1491
  ```js
1108
1492
 
1109
1493
  {
1110
-   path: "marks[2]",
1111
-   code: "OUT_OF_RANGE",
1112
-   message:
1113
-     "Attribute marks[2] should have a value between 0 and 100"
1494
+
1495
+ path: "marks[2]",
1496
+
1497
+ code: "OUT_OF_RANGE",
1498
+
1499
+ message:
1500
+
1501
+ "Attribute marks[2] should have a value between 0 and 100"
1502
+
1114
1503
  }
1504
+
1115
1505
  ```
1116
1506
 
1117
1507
  Array paths use zero-based indexes:
@@ -1119,8 +1509,11 @@ Array paths use zero-based indexes:
1119
1509
  ```text
1120
1510
 
1121
1511
  marks[0]
1512
+
1122
1513
  marks[1]
1514
+
1123
1515
  marks[2]
1516
+
1124
1517
  ```
1125
1518
 
1126
1519
  ### Nested Fields Inside Arrays
@@ -1132,16 +1525,21 @@ For example:
1132
1525
  ```text
1133
1526
 
1134
1527
  products[0].quantity
1528
+
1135
1529
  products[1].quantity
1530
+
1136
1531
  products[2].price
1532
+
1137
1533
  ```
1138
1534
 
1139
1535
  This provides enough information for consumers to identify the exact
1536
+
1140
1537
  field that caused the validation error.
1141
1538
 
1142
1539
  ### Why Structured Paths Are Useful
1143
1540
 
1144
1541
  Instead of parsing an error message to determine which field failed,
1542
+
1145
1543
  applications can directly use:
1146
1544
 
1147
1545
  ```js
@@ -1176,11 +1574,14 @@ Result:
1176
1574
 
1177
1575
  {
1178
1576
 
1179
-   "email": "Invalid email format for attribute email",
1180
-   "address.location.latitude": "Invalid type for attribute address.location.latitude, required number value",
1181
-   "marks[2]": "Attribute marks[2] should have a value between 0 and 100"
1577
+ "email": "Invalid email format for attribute email",
1578
+
1579
+ "address.location.latitude": "Invalid type for attribute address.location.latitude, required number value",
1580
+
1581
+ "marks[2]": "Attribute marks[2] should have a value between 0 and 100"
1182
1582
 
1183
1583
  }
1584
+
1184
1585
  ```
1185
1586
 
1186
1587
  ## Examples And Usage
@@ -1193,35 +1594,64 @@ sample-1
1193
1594
 
1194
1595
  {
1195
1596
 
1196
-   firstName: {
1197
-     mandatory: true,
1198
-     allowNull: false,
1199
-     type: "string",
1200
-     minLength: 3,
1201
-     minLengthError:"First name must have minimum 3 characters."
1202
-   },
1203
-   lastName: {
1204
-     mandatory: false,
1205
-     allowNull: true,
1206
-     type: "string",
1207
-   },
1208
-   email: {
1209
-     mandatory: true,
1210
-     allowNull: false,
1211
-     type: "email",
1212
-   },
1213
-   phone: {
1214
-     mandatory: true,
1215
-     allowNull: false,
1216
-     type: "string",
1217
-   },
1218
-   age: {
1219
-     mandatory: false,
1220
-     type: "number",
1221
-     min: 1,
1222
-     max: 120,
1223
-   },
1597
+ firstName: {
1598
+
1599
+ mandatory: true,
1600
+
1601
+ allowNull: false,
1602
+
1603
+ type: "string",
1604
+
1605
+ minLength: 3,
1606
+
1607
+ minLengthError:"First name must have minimum 3 characters."
1608
+
1609
+ },
1610
+
1611
+ lastName: {
1612
+
1613
+ mandatory: false,
1614
+
1615
+ allowNull: true,
1616
+
1617
+ type: "string",
1618
+
1619
+ },
1620
+
1621
+ email: {
1622
+
1623
+ mandatory: true,
1624
+
1625
+ allowNull: false,
1626
+
1627
+ type: "email",
1628
+
1629
+ },
1630
+
1631
+ phone: {
1632
+
1633
+ mandatory: true,
1634
+
1635
+ allowNull: false,
1636
+
1637
+ type: "string",
1638
+
1639
+ },
1640
+
1641
+ age: {
1642
+
1643
+ mandatory: false,
1644
+
1645
+ type: "number",
1646
+
1647
+ min: 1,
1648
+
1649
+ max: 120,
1650
+
1651
+ },
1652
+
1224
1653
  };
1654
+
1225
1655
  ```
1226
1656
 
1227
1657
  sample-2
@@ -1229,140 +1659,275 @@ sample-2
1229
1659
  ```javascript
1230
1660
 
1231
1661
  {
1232
-   id: {
1233
-     mandatory: true,
1234
-     allowNull: true,
1235
-     type: "uuidv4",
1236
-   },
1237
-   batchId: {
1238
-     mandatory: true,
1239
-     allowNull: true,
1240
-     type: "objectId",
1241
-   },
1242
-   firstName: {
1243
-     mandatory: true,
1244
-     type: "string",
1245
-     minLength: 3,
1246
-   },
1247
-   lastName: {
1248
-     mandatory: false,
1249
-     allowNull: true,
1250
-     type: "string",
1251
-   },
1252
-   age: {
1253
-     type: "number",
1254
-     min: 0.1,
1255
-     max: 120,
1256
-   },
1257
-   isAdult: {
1258
-     type: "boolean",
1259
-   },
1260
-   totalWins: {
1261
-     type: "number",
1262
-     min: 0,
1263
-     preventDecimal: true,
1264
-   },
1265
-   email: {
1266
-     regex: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$/,
1267
-   },
1268
-   githubLink: {
1269
-     type: "url",
1270
-   },
1271
-   accountStatus: {
1272
-     type: "enum",
1273
-     enumValues: ["Active", "Inactive", 200],
1274
-   },
1275
-   marks: {
1276
-     range: "0-100",
1277
-   },
1278
-   allMarks: {
1279
-     type: "array",
1280
-     allowEmptyArray: false,
1281
-     elementConstraints: {
1282
-       type: "number",
1283
-       allowNull: false,
1284
-       range: "0-100",
1285
-     },
1286
-   },
1287
-   totalScore: {
1288
-     type: "number",
1289
-     dependency: {
1290
-       result: {
1291
-         setDependencyRule: (totalScore, result) => {
1292
-           return { mandatory: true, allowNull: false, type: "string" };
1293
-         },
1294
-       },
1295
-     },
1296
-   },
1297
-   result: {
1298
-     type: "string",
1299
-     dependency: {
1300
-       totalScore: {
1301
-         setDependencyRule: (result, totalScore) => {
1302
-           return { mandatory: true, allowNull: false, type: "number" };
1303
-         },
1304
-       },
1305
-     },
1306
-   },
1307
-   minSalary: {
1308
-     mandatory: true,
1309
-     min: 1,
1310
-     type: "number",
1311
-     dependency: {
1312
-       maxSalary: {
1313
-         setDependencyRule: (minSalary, maxSalary) => {
1314
-           return {
1315
-             mandatory: true,
1316
-             min: minSalary + 1,
1317
-             minError: "maxSalary must be more than minSalary",
1318
-           };
1319
-         },
1320
-       },
1321
-     },
1322
-   },
1323
-   maxSalary: {
1324
-     dependency: {
1325
-       minSalary: {
1326
-         setDependencyRule: (maxSalary, minSalary) => {
1327
-           return {
1328
-             mandatory: true,
1329
-             max: maxSalary - 1,
1330
-             maxError: "minSalary must be less than maxSalary",
1331
-           };
1332
-         },
1333
-       },
1334
-     },
1335
-   },
1336
-   address: {
1337
-     mandatory: true,
1338
-     type: "object",
1339
-     allowEmptyObject: false,
1340
-     objectAttr: {
1341
-       country: { mandatory: true, type: "string" },
1342
-       state: {
1343
-         mandatory: true,
1344
-         type: "string",
1345
-       },
1346
-       city: {},
1347
-       zip: {
1348
-         mandatory: true,
1349
-         type: "string",
1350
-       },
1351
-       position: {
1352
-         mandatory: true,
1353
-         type: "object",
1354
-         allowEmptyObject: false,
1355
-         objectAttr: {
1356
-           lattitude: { mandatory: true, type: "number" },
1357
-           longitude: {
1358
-             mandatory: true,
1359
-             type: "number",
1360
-           },
1361
-         },
1362
-       },
1363
-     },
1364
-   },
1662
+
1663
+ id: {
1664
+
1665
+ mandatory: true,
1666
+
1667
+ allowNull: true,
1668
+
1669
+ type: "uuidv4",
1670
+
1671
+ },
1672
+
1673
+ batchId: {
1674
+
1675
+ mandatory: true,
1676
+
1677
+ allowNull: true,
1678
+
1679
+ type: "objectId",
1680
+
1681
+ },
1682
+
1683
+ firstName: {
1684
+
1685
+ mandatory: true,
1686
+
1687
+ type: "string",
1688
+
1689
+ minLength: 3,
1690
+
1691
+ },
1692
+
1693
+ lastName: {
1694
+
1695
+ mandatory: false,
1696
+
1697
+ allowNull: true,
1698
+
1699
+ type: "string",
1700
+
1701
+ },
1702
+
1703
+ age: {
1704
+
1705
+ type: "number",
1706
+
1707
+ min: 0.1,
1708
+
1709
+ max: 120,
1710
+
1711
+ },
1712
+
1713
+ isAdult: {
1714
+
1715
+ type: "boolean",
1716
+
1717
+ },
1718
+
1719
+ totalWins: {
1720
+
1721
+ type: "number",
1722
+
1723
+ min: 0,
1724
+
1725
+ preventDecimal: true,
1726
+
1727
+ },
1728
+
1729
+ email: {
1730
+
1731
+ regex: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}$/,
1732
+
1733
+ },
1734
+
1735
+ githubLink: {
1736
+
1737
+ type: "url",
1738
+
1739
+ },
1740
+
1741
+ accountStatus: {
1742
+
1743
+ type: "enum",
1744
+
1745
+ enumValues: ["Active", "Inactive", 200],
1746
+
1747
+ },
1748
+
1749
+ marks: {
1750
+
1751
+ range: "0-100",
1752
+
1753
+ },
1754
+
1755
+ allMarks: {
1756
+
1757
+ type: "array",
1758
+
1759
+ allowEmptyArray: false,
1760
+
1761
+ elementConstraints: {
1762
+
1763
+ type: "number",
1764
+
1765
+ allowNull: false,
1766
+
1767
+ range: "0-100",
1768
+
1769
+ },
1770
+
1771
+ },
1772
+
1773
+ totalScore: {
1774
+
1775
+ type: "number",
1776
+
1777
+ dependency: {
1778
+
1779
+ result: {
1780
+
1781
+ setDependencyRule: (totalScore, result) => {
1782
+
1783
+ return { mandatory: true, allowNull: false, type: "string" };
1784
+
1785
+ },
1786
+
1787
+ },
1788
+
1789
+ },
1790
+
1791
+ },
1792
+
1793
+ result: {
1794
+
1795
+ type: "string",
1796
+
1797
+ dependency: {
1798
+
1799
+ totalScore: {
1800
+
1801
+ setDependencyRule: (result, totalScore) => {
1802
+
1803
+ return { mandatory: true, allowNull: false, type: "number" };
1804
+
1805
+ },
1806
+
1807
+ },
1808
+
1809
+ },
1810
+
1811
+ },
1812
+
1813
+ minSalary: {
1814
+
1815
+ mandatory: true,
1816
+
1817
+ min: 1,
1818
+
1819
+ type: "number",
1820
+
1821
+ dependency: {
1822
+
1823
+ maxSalary: {
1824
+
1825
+ setDependencyRule: (minSalary, maxSalary) => {
1826
+
1827
+ return {
1828
+
1829
+ mandatory: true,
1830
+
1831
+ min: minSalary + 1,
1832
+
1833
+ minError: "maxSalary must be more than minSalary",
1834
+
1835
+ };
1836
+
1837
+ },
1838
+
1839
+ },
1840
+
1841
+ },
1842
+
1843
+ },
1844
+
1845
+ maxSalary: {
1846
+
1847
+ dependency: {
1848
+
1849
+ minSalary: {
1850
+
1851
+ setDependencyRule: (maxSalary, minSalary) => {
1852
+
1853
+ return {
1854
+
1855
+ mandatory: true,
1856
+
1857
+ max: maxSalary - 1,
1858
+
1859
+ maxError: "minSalary must be less than maxSalary",
1860
+
1861
+ };
1862
+
1863
+ },
1864
+
1865
+ },
1866
+
1867
+ },
1868
+
1869
+ },
1870
+
1871
+ address: {
1872
+
1873
+ mandatory: true,
1874
+
1875
+ type: "object",
1876
+
1877
+ allowEmptyObject: false,
1878
+
1879
+ objectAttr: {
1880
+
1881
+ country: { mandatory: true, type: "string" },
1882
+
1883
+ state: {
1884
+
1885
+ mandatory: true,
1886
+
1887
+ type: "string",
1888
+
1889
+ },
1890
+
1891
+ city: {},
1892
+
1893
+ zip: {
1894
+
1895
+ mandatory: true,
1896
+
1897
+ type: "string",
1898
+
1899
+ },
1900
+
1901
+ position: {
1902
+
1903
+ mandatory: true,
1904
+
1905
+ type: "object",
1906
+
1907
+ allowEmptyObject: false,
1908
+
1909
+ objectAttr: {
1910
+
1911
+ lattitude: { mandatory: true, type: "number" },
1912
+
1913
+ longitude: {
1914
+
1915
+ mandatory: true,
1916
+
1917
+ type: "number",
1918
+
1919
+ },
1920
+
1921
+ },
1922
+
1923
+ },
1924
+
1925
+ },
1926
+
1927
+ },
1928
+
1365
1929
  }
1930
+
1366
1931
  ```
1367
1932
 
1368
1933
  ### Usage
@@ -1371,13 +1936,18 @@ sample-2
1371
1936
 
1372
1937
  ```javascript
1373
1938
 
1374
- //Here validatePayload is your middleware function, where you're invoking perfect payload
1939
+ *//Here validatePayload is your middleware function, where you're invoking perfect payload*
1375
1940
 
1376
1941
  router.post(
1377
-   "/payload-validation",
1378
-   validatePayload({ rule: <your validation rule json object> }),
1379
-   (req, res) => res.send("OK")
1942
+
1943
+ "/payload-validation",
1944
+
1945
+ validatePayload({ rule: <your validation rule json object> }),
1946
+
1947
+ (req, res) => res.send("OK")
1948
+
1380
1949
  );
1950
+
1381
1951
  ```
1382
1952
 
1383
1953
  #### 1 Use perfect-payload in your middleware like below(for MODULE JS)
@@ -1389,12 +1959,15 @@ export const validatePayload = ({ rule }) => {
1389
1959
  return (req, res, next) => {
1390
1960
  try {
1391
1961
  const { statusCode, ...response } = perfectPayloadV1(req?.body, rule);
1962
+
1392
1963
  if (+statusCode >= 200 && +statusCode <= 299) {
1393
1964
  req.validatedBody = response?.validatedPayload;
1965
+
1394
1966
  next();
1395
1967
  } else res.status(statusCode).json(response);
1396
1968
  } catch (error) {
1397
1969
  console.error("Error validating payload", error);
1970
+
1398
1971
  res.status(500).json({ error: "Internal Server Error" });
1399
1972
  }
1400
1973
  };
@@ -1408,15 +1981,19 @@ function validatePayload({ rule }) {
1408
1981
  return async (req, res, next) => {
1409
1982
  try {
1410
1983
  const { perfectPayloadV1 } = await import("perfect-payload");
1984
+
1411
1985
  const { statusCode, ...response } = perfectPayloadV1(req?.body, rule);
1986
+
1412
1987
  if (+statusCode >= 200 && +statusCode <= 299) {
1413
1988
  req.validatedBody = response?.validatedPayload;
1989
+
1414
1990
  next();
1415
1991
  } else {
1416
1992
  res.status(statusCode).json(response);
1417
1993
  }
1418
1994
  } catch (error) {
1419
1995
  console.error("Error validating payload", error);
1996
+
1420
1997
  res.status(500).json({ error: "Internal Server Error" });
1421
1998
  }
1422
1999
  };
@@ -1425,8 +2002,10 @@ function validatePayload({ rule }) {
1425
2002
  module.exports = { validatePayload };
1426
2003
  ```
1427
2004
 
1428
- ---
2005
+ **---**
1429
2006
 
1430
2007
  This documentation provides a comprehensive guide to using the data
2008
+
1431
2009
  validation module effectively. Ensure to define your validation rules
2010
+
1432
2011
  clearly to maintain data quality and consistency in your applications.