perfect-payload 1.1.2 → 1.2.0-beta.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.
package/index.js CHANGED
@@ -1,567 +1,1429 @@
1
- export function perfectPayloadV1(
2
- data = {},
3
- dataValidationRule = {},
4
- validPayloadResponse = { statusCode: 200, valid: true },
5
- inValidPayloadResponse = {
6
- statusCode: 400,
7
- valid: false,
8
- message: "One or more attribute values are invalid",
9
- }
10
- ) {
11
- let validatedPayload = {};
12
- let rowErrors = [];
13
- for (const attributeName in dataValidationRule) {
14
- let addNextError = true;
15
- const attributeRules = dataValidationRule?.[attributeName];
16
- const attrPath = dataValidationRule?.[attributeName]?.path || null;
17
- for (const ruleName in attributeRules) {
18
- const attributePath = `${attrPath ? attrPath + "." : ""}${attributeName}`;
19
- const attributeValue = data?.[attributeName] ?? null;
20
- const nullAllowed =
21
- dataValidationRule?.[attributeName]?.["allowNull"] ?? true;
22
- const isMandatoryField = attributeRules?.["mandatory"] ?? false;
23
- const attrExist = Object.keys(data)?.includes(attributeName); //Mandatory value check
24
-
25
- switch (ruleName) {
26
- case "mandatory":
27
- //mandatory value check
28
- if (isMandatoryField && !attrExist) {
29
- addNextError = false;
30
- rowErrors.push(
31
- attributeRules?.["mandatoryError"] ||
32
- `${attributePath} is mandatory`
33
- );
34
- } else if (!attrExist) {
35
- addNextError = false;
36
- }
37
- break;
38
- case "allowNull":
39
- //allowNull value check
40
- if (addNextError && attributeValue == null) {
41
- if (!dataValidationRule?.[attributeName]?.[ruleName]) {
42
- addNextError = false;
43
- rowErrors.push(
44
- attributeRules?.["allowNullError"] ||
45
- `value null/'' not valid for attribute ${attributePath}`
46
- );
47
- }
48
- }
49
- break;
50
- case "allowEmptyObject":
51
- //allowEmptyObject check
52
- if (addNextError) {
53
- if (
54
- !attributeRules?.["allowEmptyObject"] &&
55
- Object.keys(attributeValue).length == 0
56
- ) {
57
- rowErrors.push(
58
- attributeRules?.["emptyObjectError"] ||
59
- `value {} not valid for attribute ${attributePath}`
60
- );
61
- addNextError = false;
62
- }
63
- }
64
-
65
- break;
66
- case "allowEmptyArray":
67
- //allowEmptyArray check
68
- if (addNextError) {
69
- if (
70
- !attributeRules?.["allowEmptyArray"] &&
71
- isArray(attributeValue) &&
72
- attributeValue?.length == 0
73
- ) {
74
- rowErrors.push(
75
- attributeRules?.["emptyArrayError"] ||
76
- `${attributePath} cannot be an empty array`
77
- );
78
- addNextError = false;
79
- }
80
- }
81
-
82
- break;
83
- case "elementConstraints":
84
- //check array ele type
85
- if (addNextError) {
86
- if (isArray(attributeValue) && attributeValue?.length > 0) {
87
- let elementError = false;
88
- for (const element of attributeValue) {
89
- const { statusCode = 200, errors } = perfectPayloadV1(
90
- { [attributeName]: element },
91
- { [attributeName]: attributeRules[ruleName] }
92
- );
93
- if (statusCode == 400) {
94
- elementError = errors?.[0];
95
- // addNextError = false;
96
- break;
97
- }
98
- }
99
- if (elementError) {
100
- rowErrors.push(
101
- attributeRules?.["elementConstraintsError"] || elementError
102
- );
103
- addNextError = false;
104
- }
105
- }
106
- }
107
-
108
- break;
109
- case "regex":
110
- //custom regex check
111
- if (addNextError) {
112
- if (!isPassedRegex(attributeRules[ruleName], attributeValue)) {
113
- rowErrors.push(
114
- attributeRules?.["regexError"] ||
115
- `${attributePath} failed to pass the regex ${attributeRules[ruleName]}`
116
- );
117
- addNextError = false;
118
- }
119
- }
120
- break;
121
- case "type":
122
- //value type check
123
- if (addNextError) {
124
- if (attributeValue == null && nullAllowed) {
125
- addNextError = true;
126
- } else {
127
- switch (attributeRules[ruleName]) {
128
- case "number":
129
- if (!isNumber(attributeValue)) {
130
- rowErrors.push(
131
- attributeRules?.["typeError"] ||
132
- `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required ${
133
- attributeRules[ruleName]
134
- } value`
135
- );
136
- addNextError = false;
137
- }
138
- break;
139
-
140
- case "string":
141
- if (!isString(attributeValue)) {
142
- rowErrors.push(
143
- attributeRules?.["typeError"] ||
144
- `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required ${
145
- attributeRules[ruleName]
146
- } value`
147
- );
148
- addNextError = false;
149
- }
150
- break;
151
- case "boolean":
152
- if (!isBoolean(attributeValue)) {
153
- rowErrors.push(
154
- attributeRules?.["typeError"] ||
155
- `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required ${
156
- attributeRules[ruleName]
157
- } value`
158
- );
159
- addNextError = false;
160
- }
161
- break;
162
- case "email":
163
- if (!isValidEmail(attributeValue)) {
164
- rowErrors.push(
165
- attributeRules?.["typeError"] ||
166
- `Invalid email ID(${attributeValue}) found in attribute ${attributePath}`
167
- );
168
- addNextError = false;
169
- }
170
- break;
171
-
172
- case "url":
173
- if (!isValidUrl(attributeValue)) {
174
- rowErrors.push(
175
- attributeRules?.["typeError"] ||
176
- `Invalid URL format(${attributeValue}) found in attribute ${attributePath}`
177
- );
178
- addNextError = false;
179
- }
180
- break;
181
- case "enum":
182
- if (attributeValue == null && nullAllowed) {
183
- addNextError = true;
184
- } else {
185
- const allEnumValues = attributeRules["enumValues"];
186
- if (!allEnumValues?.includes(attributeValue)) {
187
- rowErrors.push(
188
- attributeRules?.["typeError"] ||
189
- `Invalid value(${attributeValue}) found in attribute ${attributePath}, valid values are ${allEnumValues?.join(
190
- ", "
191
- )}`
192
- );
193
- addNextError = false;
194
- }
195
- }
196
- break;
197
- case "uuid":
198
- if (attributeValue == null && nullAllowed) {
199
- addNextError = true;
200
- } else if (!isUUID(attributeValue)) {
201
- rowErrors.push(
202
- attributeRules?.["typeError"] ||
203
- `Invalid UUID(${attributeValue}) found in attribute ${attributePath}`
204
- );
205
- addNextError = false;
206
- }
207
- break;
208
- case "uuidv1":
209
- if (attributeValue == null && nullAllowed) {
210
- addNextError = true;
211
- } else if (!isUUIDv1(attributeValue)) {
212
- rowErrors.push(
213
- attributeRules?.["typeError"] ||
214
- `Invalid v1 UUID(${attributeValue}) found in attribute ${attributePath}`
215
- );
216
- addNextError = false;
217
- }
218
- break;
219
- case "uuidv3":
220
- if (attributeValue == null && nullAllowed) {
221
- addNextError = true;
222
- } else if (!isUUIDv3(attributeValue)) {
223
- rowErrors.push(
224
- attributeRules?.["typeError"] ||
225
- `Invalid v3 UUID(${attributeValue}) found in attribute ${attributePath}`
226
- );
227
- addNextError = false;
228
- }
229
- break;
230
- case "uuidv4":
231
- if (attributeValue == null && nullAllowed) {
232
- addNextError = true;
233
- } else if (!isUUIDv4(attributeValue)) {
234
- rowErrors.push(
235
- attributeRules?.["typeError"] ||
236
- `Invalid v4 UUID(${attributeValue}) found in attribute ${attributePath}`
237
- );
238
- addNextError = false;
239
- }
240
- break;
241
- case "uuidv5":
242
- if (attributeValue == null && nullAllowed) {
243
- addNextError = true;
244
- } else if (!isUUIDv5(attributeValue)) {
245
- rowErrors.push(
246
- attributeRules?.["typeError"] ||
247
- `Invalid v5 UUID(${attributeValue}) found in attribute ${attributePath}`
248
- );
249
- addNextError = false;
250
- }
251
- break;
252
- case "objectId":
253
- if (attributeValue == null && nullAllowed) {
254
- addNextError = true;
255
- } else {
256
- if (!isObjectId(attributeValue)) {
257
- rowErrors.push(
258
- attributeRules?.["typeError"] ||
259
- `Invalid ObjectId(${attributeValue}) found in attribute ${attributePath}`
260
- );
261
- addNextError = false;
262
- }
263
- }
264
- break;
265
-
266
- case "array":
267
- if (!isArray(attributeValue)) {
268
- rowErrors.push(
269
- attributeRules?.["typeError"] ||
270
- `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required ${
271
- attributeRules[ruleName]
272
- } value`
273
- );
274
- addNextError = false;
275
- }
276
- break;
277
-
278
- case "object":
279
- if (!isObject(attributeValue)) {
280
- rowErrors.push(
281
- attributeRules?.["typeError"] ||
282
- `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required ${
283
- attributeRules[ruleName]
284
- } value`
285
- );
286
- addNextError = false;
287
- }
288
- break;
289
-
290
- default:
291
- break;
292
- }
293
- }
294
- }
295
-
296
- break;
297
- case "minLength":
298
- //minimum string length check
299
- if (addNextError) {
300
- if (attributeValue == null && nullAllowed) {
301
- addNextError = true;
302
- } else if (typeof attributeValue == "string") {
303
- if (attributeValue.length < +attributeRules[ruleName])
304
- rowErrors.push(
305
- attributeRules?.["minLengthError"] ||
306
- `${attributePath} should be minimum of ${attributeRules[ruleName]} character`
307
- );
308
- addNextError = false;
309
- } else {
310
- rowErrors.push(
311
- `${attributePath} value should be a string type(minLength specified)`
312
- );
313
- addNextError = false;
314
- }
315
- }
316
- break;
317
- case "maxLength":
318
- //max string length check
319
- if (addNextError) {
320
- if (attributeValue == null && nullAllowed) {
321
- addNextError = true;
322
- } else if (typeof attributeValue == "string") {
323
- if (attributeValue.length > +attributeRules[ruleName])
324
- rowErrors.push(
325
- attributeRules?.["maxLengthError"] ||
326
- `${attributePath} can have maximum of ${attributeRules[ruleName]} character`
327
- );
328
- addNextError = false;
329
- } else {
330
- rowErrors.push(
331
- `${attributePath} value should be a string type(maxLength specified)`
332
- );
333
- addNextError = false;
334
- }
335
- }
336
- break;
337
- case "preventDecimal":
338
- //preventFraction
339
- if (addNextError) {
340
- if (attributeValue == null && nullAllowed) {
341
- addNextError = true;
342
- } else if (!isNumber(attributeValue)) {
343
- rowErrors.push(
344
- `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required number value`
345
- );
346
- addNextError = false;
347
- } else if (+attributeValue % 1 !== 0) {
348
- rowErrors.push(
349
- attributeRules?.["preventDecimalError"] ||
350
- `Decimal value not allowed in attribute ${attributePath}(${attributeValue})`
351
- );
352
- addNextError = false;
353
- }
354
- }
355
- break;
356
- case "min":
357
- //minimum value check
358
- if (addNextError) {
359
- if (attributeValue == null && nullAllowed) {
360
- addNextError = true;
361
- } else if (!isNumber(attributeValue)) {
362
- rowErrors.push(
363
- `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required number value`
364
- );
365
- addNextError = false;
366
- } else if (+attributeValue < +attributeRules[ruleName]) {
367
- rowErrors.push(
368
- attributeRules?.["minError"] ||
369
- `minimum value ${
370
- attributeRules[ruleName]
371
- } is allowed in attribute ${attributePath}, found value ${
372
- data[attributeName] ?? "Nil"
373
- }`
374
- );
375
- addNextError = false;
376
- }
377
- }
378
- break;
379
- case "max":
380
- //maximum value check
381
- if (addNextError) {
382
- if (attributeValue == null && nullAllowed) {
383
- addNextError = true;
384
- } else if (!isNumber(attributeValue)) {
385
- rowErrors.push(
386
- `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required number value`
387
- );
388
- addNextError = false;
389
- } else if (+attributeValue > +attributeRules[ruleName]) {
390
- rowErrors.push(
391
- attributeRules?.["maxError"] ||
392
- `maximum value ${
393
- attributeRules[ruleName]
394
- } is allowed in attribute ${attributePath}, found value ${
395
- data[attributeName] ?? "Nil"
396
- }`
397
- );
398
- addNextError = false;
399
- }
400
- }
401
- break;
402
- case "range":
403
- //value range check
404
- if (addNextError) {
405
- if (attributeValue == null && nullAllowed) {
406
- addNextError = true;
407
- } else if (!isNumber(attributeValue)) {
408
- rowErrors.push(
409
- `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required number value`
410
- );
411
- addNextError = false;
412
- } else {
413
- const [min, max] = attributeRules[ruleName].split("-");
414
- if (
415
- !isNumber(attributeValue) ||
416
- +attributeValue < +min ||
417
- +attributeValue > +max
418
- ) {
419
- rowErrors.push(
420
- attributeRules?.["rangeError"] ||
421
- `Invalid values(${attributeValue}) found in attribute ${attributePath}, value should be between ${min} and ${max} `
422
- );
423
- addNextError = false;
424
- }
425
- }
426
- }
427
- break;
428
- case "objectAttr":
429
- //nested object attributes check
430
- if (addNextError) {
431
- const allObjectAttr = Object.keys(attributeRules?.[ruleName]);
432
- const objectAttrRules = {};
433
- for (const attr of allObjectAttr) {
434
- objectAttrRules[attr] = {
435
- ...attributeRules?.[ruleName]?.[attr],
436
- path: attributeRules?.[ruleName]?.[attr]?.path
437
- ? attributeRules?.[ruleName]?.[attr]?.path + `.${attr}`
438
- : `${attributePath}`,
439
- };
440
- }
441
-
442
- const { errors = [] } = perfectPayloadV1(
443
- attributeValue,
444
- objectAttrRules
445
- );
446
- rowErrors = [...rowErrors, ...errors];
447
- addNextError = true;
448
- }
449
- break;
450
- case "dependency":
451
- //dependency check
452
- if (addNextError) {
453
- const allDependencyAttr = Object.keys(attributeRules?.[ruleName]);
454
- for (const attr of allDependencyAttr) {
455
- if (
456
- typeof attributeRules?.[ruleName]?.[attr]?.setDependencyRule ==
457
- "function"
458
- ) {
459
- const newRule = attributeRules?.[ruleName]?.[
460
- attr
461
- ]?.setDependencyRule(attributeValue, data?.[attr]);
462
- let newData = { [attributeName]: attributeValue };
463
- if (Object.keys(data).includes(attr)) {
464
- newData = { ...newData, [attr]: data?.[attr] };
465
- }
466
- const { errors = [] } = perfectPayloadV1(newData, {
467
- [attr]: newRule,
468
- });
469
- rowErrors = [...rowErrors, ...errors];
470
- addNextError = true;
471
- } else {
472
- throw new Error(
473
- `perfect-payload:- function setDependencyRule not found in ${attr} dependency `
474
- );
475
- }
476
- }
477
- }
478
- break;
479
- default:
480
- break;
481
- }
482
-
483
- if (attrExist && rowErrors.length == 0)
484
- validatedPayload[attributeName] = attributeValue;
485
- }
486
- }
487
-
488
- if (rowErrors?.length) {
489
- return { ...inValidPayloadResponse, errors: rowErrors };
490
- } else {
491
- return { ...validPayloadResponse, validatedPayload };
492
- }
493
- }
494
-
495
- function isNumber(value) {
496
- return typeof value == "number";
497
- }
498
-
499
- function isString(value) {
500
- return typeof value == "string";
501
- }
502
-
503
- function isBoolean(value) {
504
- return typeof value == "boolean";
505
- }
506
-
507
- function isArray(value) {
508
- return Array.isArray(value);
509
- }
510
-
511
- function isObject(value) {
512
- return typeof value == "object";
513
- }
514
-
515
- function isValidEmail(email) {
516
- const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
517
- return emailRegex.test(email);
518
- }
519
-
520
- function isValidUrl(url) {
521
- const urlRegex =
522
- /^(https?:\/\/)([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})(:[0-9]+)?(\/[^\s]*)?(\?[^\s]*)?$/;
523
- return urlRegex.test(url);
524
- }
525
-
526
- function isUUID(uuid) {
527
- const uuidRegex =
528
- /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
529
-
530
- return uuidRegex.test(uuid);
531
- }
532
-
533
- function isUUIDv1(uuid) {
534
- const uuidRegex =
535
- /^[0-9a-f]{8}-[0-9a-f]{4}-1[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
536
-
537
- return uuidRegex.test(uuid);
538
- }
539
-
540
- function isUUIDv3(uuid) {
541
- const uuidRegex =
542
- /^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
543
-
544
- return uuidRegex.test(uuid);
545
- }
546
-
547
- function isUUIDv4(uuid) {
548
- const uuidRegex =
549
- /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
550
-
551
- return uuidRegex.test(uuid);
552
- }
553
-
554
- function isUUIDv5(uuid) {
555
- const uuidRegex =
556
- /^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
557
-
558
- return uuidRegex.test(uuid);
559
- }
560
-
561
- function isObjectId(id) {
562
- return /^[0-9a-fA-F]{24}$/.test(id);
563
- }
564
-
565
- function isPassedRegex(reExpression, value) {
566
- return reExpression.test(value);
567
- }
1
+ let perfectPayloadV1DeprecationWarningShown = false;
2
+
3
+ function showPerfectPayloadV1DeprecationWarning() {
4
+ if (perfectPayloadV1DeprecationWarningShown) return;
5
+
6
+ perfectPayloadV1DeprecationWarningShown = true;
7
+
8
+ const message =
9
+ "perfectPayloadV1() is deprecated and will no longer be supported after March 31, 2027. Use perfectPayload() instead.";
10
+
11
+ if (
12
+ typeof process !== "undefined" &&
13
+ typeof process.emitWarning === "function"
14
+ ) {
15
+ process.emitWarning(message, {
16
+ type: "DeprecationWarning",
17
+ code: "PERFECT_PAYLOAD_V1_DEPRECATED",
18
+ });
19
+ } else if (
20
+ typeof console !== "undefined" &&
21
+ typeof console.warn === "function"
22
+ ) {
23
+ console.warn(`[perfect-payload] DeprecationWarning: ${message}`);
24
+ }
25
+ }
26
+
27
+ export function perfectPayloadV1(
28
+ data = {},
29
+ dataValidationRule = {},
30
+ validPayloadResponse = { statusCode: 200, valid: true },
31
+ inValidPayloadResponse = {
32
+ statusCode: 400,
33
+ valid: false,
34
+ message: "One or more attribute values are invalid",
35
+ },
36
+ ) {
37
+ showPerfectPayloadV1DeprecationWarning();
38
+ let validatedPayload = {};
39
+ let rowErrors = [];
40
+ for (const attributeName in dataValidationRule) {
41
+ let addNextError = true;
42
+ const attributeRules = dataValidationRule?.[attributeName];
43
+ const attrPath = dataValidationRule?.[attributeName]?.path || null;
44
+ for (const ruleName in attributeRules) {
45
+ const attributePath = `${attrPath ? attrPath + "." : ""}${attributeName}`;
46
+ const attributeValue = data?.[attributeName] ?? null;
47
+ const nullAllowed =
48
+ dataValidationRule?.[attributeName]?.["allowNull"] ?? true;
49
+ const isMandatoryField = attributeRules?.["mandatory"] ?? false;
50
+ const attrExist = Object.keys(data)?.includes(attributeName); //Mandatory value check
51
+
52
+ switch (ruleName) {
53
+ case "mandatory":
54
+ //mandatory value check
55
+ if (isMandatoryField && !attrExist) {
56
+ addNextError = false;
57
+ rowErrors.push(
58
+ attributeRules?.["mandatoryError"] ||
59
+ `${attributePath} is mandatory`,
60
+ );
61
+ } else if (!attrExist) {
62
+ addNextError = false;
63
+ }
64
+ break;
65
+ case "allowNull":
66
+ //allowNull value check
67
+ if (addNextError && attributeValue == null) {
68
+ if (!dataValidationRule?.[attributeName]?.[ruleName]) {
69
+ addNextError = false;
70
+ rowErrors.push(
71
+ attributeRules?.["allowNullError"] ||
72
+ `value null/'' not valid for attribute ${attributePath}`,
73
+ );
74
+ }
75
+ }
76
+ break;
77
+ case "allowEmptyObject":
78
+ //allowEmptyObject check
79
+ if (addNextError) {
80
+ if (
81
+ !attributeRules?.["allowEmptyObject"] &&
82
+ Object.keys(attributeValue).length == 0
83
+ ) {
84
+ rowErrors.push(
85
+ attributeRules?.["emptyObjectError"] ||
86
+ `value {} not valid for attribute ${attributePath}`,
87
+ );
88
+ addNextError = false;
89
+ }
90
+ }
91
+
92
+ break;
93
+ case "allowEmptyArray":
94
+ //allowEmptyArray check
95
+ if (addNextError) {
96
+ if (
97
+ !attributeRules?.["allowEmptyArray"] &&
98
+ isArray(attributeValue) &&
99
+ attributeValue?.length == 0
100
+ ) {
101
+ rowErrors.push(
102
+ attributeRules?.["emptyArrayError"] ||
103
+ `${attributePath} cannot be an empty array`,
104
+ );
105
+ addNextError = false;
106
+ }
107
+ }
108
+
109
+ break;
110
+ case "elementConstraints":
111
+ //check array ele type
112
+ if (addNextError) {
113
+ if (isArray(attributeValue) && attributeValue?.length > 0) {
114
+ let elementError = false;
115
+ for (const element of attributeValue) {
116
+ const { statusCode = 200, errors } = perfectPayloadV1(
117
+ { [attributeName]: element },
118
+ { [attributeName]: attributeRules[ruleName] },
119
+ );
120
+ if (statusCode == 400) {
121
+ elementError = errors?.[0];
122
+ // addNextError = false;
123
+ break;
124
+ }
125
+ }
126
+ if (elementError) {
127
+ rowErrors.push(
128
+ attributeRules?.["elementConstraintsError"] || elementError,
129
+ );
130
+ addNextError = false;
131
+ }
132
+ }
133
+ }
134
+
135
+ break;
136
+ case "regex":
137
+ //custom regex check
138
+ if (addNextError) {
139
+ if (!isPassedRegex(attributeRules[ruleName], attributeValue)) {
140
+ rowErrors.push(
141
+ attributeRules?.["regexError"] ||
142
+ `${attributePath} failed to pass the regex ${attributeRules[ruleName]}`,
143
+ );
144
+ addNextError = false;
145
+ }
146
+ }
147
+ break;
148
+ case "type":
149
+ //value type check
150
+ if (addNextError) {
151
+ if (attributeValue == null && nullAllowed) {
152
+ addNextError = true;
153
+ } else {
154
+ switch (attributeRules[ruleName]) {
155
+ case "number":
156
+ if (!isNumber(attributeValue)) {
157
+ rowErrors.push(
158
+ attributeRules?.["typeError"] ||
159
+ `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required ${
160
+ attributeRules[ruleName]
161
+ } value`,
162
+ );
163
+ addNextError = false;
164
+ }
165
+ break;
166
+
167
+ case "string":
168
+ if (!isString(attributeValue)) {
169
+ rowErrors.push(
170
+ attributeRules?.["typeError"] ||
171
+ `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required ${
172
+ attributeRules[ruleName]
173
+ } value`,
174
+ );
175
+ addNextError = false;
176
+ }
177
+ break;
178
+ case "boolean":
179
+ if (!isBoolean(attributeValue)) {
180
+ rowErrors.push(
181
+ attributeRules?.["typeError"] ||
182
+ `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required ${
183
+ attributeRules[ruleName]
184
+ } value`,
185
+ );
186
+ addNextError = false;
187
+ }
188
+ break;
189
+ case "email":
190
+ if (!isValidEmail(attributeValue)) {
191
+ rowErrors.push(
192
+ attributeRules?.["typeError"] ||
193
+ `Invalid email ID(${attributeValue}) found in attribute ${attributePath}`,
194
+ );
195
+ addNextError = false;
196
+ }
197
+ break;
198
+
199
+ case "url":
200
+ if (!isValidUrl(attributeValue)) {
201
+ rowErrors.push(
202
+ attributeRules?.["typeError"] ||
203
+ `Invalid URL format(${attributeValue}) found in attribute ${attributePath}`,
204
+ );
205
+ addNextError = false;
206
+ }
207
+ break;
208
+ case "enum":
209
+ if (attributeValue == null && nullAllowed) {
210
+ addNextError = true;
211
+ } else {
212
+ const allEnumValues = attributeRules["enumValues"];
213
+ if (!allEnumValues?.includes(attributeValue)) {
214
+ rowErrors.push(
215
+ attributeRules?.["typeError"] ||
216
+ `Invalid value(${attributeValue}) found in attribute ${attributePath}, valid values are ${allEnumValues?.join(
217
+ ", ",
218
+ )}`,
219
+ );
220
+ addNextError = false;
221
+ }
222
+ }
223
+ break;
224
+ case "uuid":
225
+ if (attributeValue == null && nullAllowed) {
226
+ addNextError = true;
227
+ } else if (!isUUID(attributeValue)) {
228
+ rowErrors.push(
229
+ attributeRules?.["typeError"] ||
230
+ `Invalid UUID(${attributeValue}) found in attribute ${attributePath}`,
231
+ );
232
+ addNextError = false;
233
+ }
234
+ break;
235
+ case "uuidv1":
236
+ if (attributeValue == null && nullAllowed) {
237
+ addNextError = true;
238
+ } else if (!isUUIDv1(attributeValue)) {
239
+ rowErrors.push(
240
+ attributeRules?.["typeError"] ||
241
+ `Invalid v1 UUID(${attributeValue}) found in attribute ${attributePath}`,
242
+ );
243
+ addNextError = false;
244
+ }
245
+ break;
246
+ case "uuidv3":
247
+ if (attributeValue == null && nullAllowed) {
248
+ addNextError = true;
249
+ } else if (!isUUIDv3(attributeValue)) {
250
+ rowErrors.push(
251
+ attributeRules?.["typeError"] ||
252
+ `Invalid v3 UUID(${attributeValue}) found in attribute ${attributePath}`,
253
+ );
254
+ addNextError = false;
255
+ }
256
+ break;
257
+ case "uuidv4":
258
+ if (attributeValue == null && nullAllowed) {
259
+ addNextError = true;
260
+ } else if (!isUUIDv4(attributeValue)) {
261
+ rowErrors.push(
262
+ attributeRules?.["typeError"] ||
263
+ `Invalid v4 UUID(${attributeValue}) found in attribute ${attributePath}`,
264
+ );
265
+ addNextError = false;
266
+ }
267
+ break;
268
+ case "uuidv5":
269
+ if (attributeValue == null && nullAllowed) {
270
+ addNextError = true;
271
+ } else if (!isUUIDv5(attributeValue)) {
272
+ rowErrors.push(
273
+ attributeRules?.["typeError"] ||
274
+ `Invalid v5 UUID(${attributeValue}) found in attribute ${attributePath}`,
275
+ );
276
+ addNextError = false;
277
+ }
278
+ break;
279
+ case "objectId":
280
+ if (attributeValue == null && nullAllowed) {
281
+ addNextError = true;
282
+ } else {
283
+ if (!isObjectId(attributeValue)) {
284
+ rowErrors.push(
285
+ attributeRules?.["typeError"] ||
286
+ `Invalid ObjectId(${attributeValue}) found in attribute ${attributePath}`,
287
+ );
288
+ addNextError = false;
289
+ }
290
+ }
291
+ break;
292
+
293
+ case "array":
294
+ if (!isArray(attributeValue)) {
295
+ rowErrors.push(
296
+ attributeRules?.["typeError"] ||
297
+ `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required ${
298
+ attributeRules[ruleName]
299
+ } value`,
300
+ );
301
+ addNextError = false;
302
+ }
303
+ break;
304
+
305
+ case "object":
306
+ if (!isObject(attributeValue)) {
307
+ rowErrors.push(
308
+ attributeRules?.["typeError"] ||
309
+ `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required ${
310
+ attributeRules[ruleName]
311
+ } value`,
312
+ );
313
+ addNextError = false;
314
+ }
315
+ break;
316
+
317
+ default:
318
+ break;
319
+ }
320
+ }
321
+ }
322
+
323
+ break;
324
+ case "minLength":
325
+ //minimum string length check
326
+ if (addNextError) {
327
+ if (attributeValue == null && nullAllowed) {
328
+ addNextError = true;
329
+ } else if (typeof attributeValue == "string") {
330
+ if (attributeValue.length < +attributeRules[ruleName])
331
+ rowErrors.push(
332
+ attributeRules?.["minLengthError"] ||
333
+ `${attributePath} should be minimum of ${attributeRules[ruleName]} character`,
334
+ );
335
+ addNextError = false;
336
+ } else {
337
+ rowErrors.push(
338
+ `${attributePath} value should be a string type(minLength specified)`,
339
+ );
340
+ addNextError = false;
341
+ }
342
+ }
343
+ break;
344
+ case "maxLength":
345
+ //max string length check
346
+ if (addNextError) {
347
+ if (attributeValue == null && nullAllowed) {
348
+ addNextError = true;
349
+ } else if (typeof attributeValue == "string") {
350
+ if (attributeValue.length > +attributeRules[ruleName])
351
+ rowErrors.push(
352
+ attributeRules?.["maxLengthError"] ||
353
+ `${attributePath} can have maximum of ${attributeRules[ruleName]} character`,
354
+ );
355
+ addNextError = false;
356
+ } else {
357
+ rowErrors.push(
358
+ `${attributePath} value should be a string type(maxLength specified)`,
359
+ );
360
+ addNextError = false;
361
+ }
362
+ }
363
+ break;
364
+ case "preventDecimal":
365
+ //preventFraction
366
+ if (addNextError) {
367
+ if (attributeValue == null && nullAllowed) {
368
+ addNextError = true;
369
+ } else if (!isNumber(attributeValue)) {
370
+ rowErrors.push(
371
+ `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required number value`,
372
+ );
373
+ addNextError = false;
374
+ } else if (+attributeValue % 1 !== 0) {
375
+ rowErrors.push(
376
+ attributeRules?.["preventDecimalError"] ||
377
+ `Decimal value not allowed in attribute ${attributePath}(${attributeValue})`,
378
+ );
379
+ addNextError = false;
380
+ }
381
+ }
382
+ break;
383
+ case "min":
384
+ //minimum value check
385
+ if (addNextError) {
386
+ if (attributeValue == null && nullAllowed) {
387
+ addNextError = true;
388
+ } else if (!isNumber(attributeValue)) {
389
+ rowErrors.push(
390
+ `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required number value`,
391
+ );
392
+ addNextError = false;
393
+ } else if (+attributeValue < +attributeRules[ruleName]) {
394
+ rowErrors.push(
395
+ attributeRules?.["minError"] ||
396
+ `minimum value ${
397
+ attributeRules[ruleName]
398
+ } is allowed in attribute ${attributePath}, found value ${
399
+ data[attributeName] ?? "Nil"
400
+ }`,
401
+ );
402
+ addNextError = false;
403
+ }
404
+ }
405
+ break;
406
+ case "max":
407
+ //maximum value check
408
+ if (addNextError) {
409
+ if (attributeValue == null && nullAllowed) {
410
+ addNextError = true;
411
+ } else if (!isNumber(attributeValue)) {
412
+ rowErrors.push(
413
+ `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required number value`,
414
+ );
415
+ addNextError = false;
416
+ } else if (+attributeValue > +attributeRules[ruleName]) {
417
+ rowErrors.push(
418
+ attributeRules?.["maxError"] ||
419
+ `maximum value ${
420
+ attributeRules[ruleName]
421
+ } is allowed in attribute ${attributePath}, found value ${
422
+ data[attributeName] ?? "Nil"
423
+ }`,
424
+ );
425
+ addNextError = false;
426
+ }
427
+ }
428
+ break;
429
+ case "range":
430
+ //value range check
431
+ if (addNextError) {
432
+ if (attributeValue == null && nullAllowed) {
433
+ addNextError = true;
434
+ } else if (!isNumber(attributeValue)) {
435
+ rowErrors.push(
436
+ `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required number value`,
437
+ );
438
+ addNextError = false;
439
+ } else {
440
+ const [min, max] = attributeRules[ruleName].split("-");
441
+ if (
442
+ !isNumber(attributeValue) ||
443
+ +attributeValue < +min ||
444
+ +attributeValue > +max
445
+ ) {
446
+ rowErrors.push(
447
+ attributeRules?.["rangeError"] ||
448
+ `Invalid values(${attributeValue}) found in attribute ${attributePath}, value should be between ${min} and ${max} `,
449
+ );
450
+ addNextError = false;
451
+ }
452
+ }
453
+ }
454
+ break;
455
+ case "objectAttr":
456
+ //nested object attributes check
457
+ if (addNextError) {
458
+ const allObjectAttr = Object.keys(attributeRules?.[ruleName]);
459
+ const objectAttrRules = {};
460
+ for (const attr of allObjectAttr) {
461
+ objectAttrRules[attr] = {
462
+ ...attributeRules?.[ruleName]?.[attr],
463
+ path: attributeRules?.[ruleName]?.[attr]?.path
464
+ ? attributeRules?.[ruleName]?.[attr]?.path + `.${attr}`
465
+ : `${attributePath}`,
466
+ };
467
+ }
468
+
469
+ const { errors = [] } = perfectPayloadV1(
470
+ attributeValue,
471
+ objectAttrRules,
472
+ );
473
+ rowErrors = [...rowErrors, ...errors];
474
+ addNextError = true;
475
+ }
476
+ break;
477
+ case "dependency":
478
+ //dependency check
479
+ if (addNextError) {
480
+ const allDependencyAttr = Object.keys(attributeRules?.[ruleName]);
481
+ for (const attr of allDependencyAttr) {
482
+ if (
483
+ typeof attributeRules?.[ruleName]?.[attr]?.setDependencyRule ==
484
+ "function"
485
+ ) {
486
+ const newRule = attributeRules?.[ruleName]?.[
487
+ attr
488
+ ]?.setDependencyRule(attributeValue, data?.[attr]);
489
+ let newData = { [attributeName]: attributeValue };
490
+ if (Object.keys(data).includes(attr)) {
491
+ newData = { ...newData, [attr]: data?.[attr] };
492
+ }
493
+ const { errors = [] } = perfectPayloadV1(newData, {
494
+ [attr]: newRule,
495
+ });
496
+ rowErrors = [...rowErrors, ...errors];
497
+ addNextError = true;
498
+ } else {
499
+ throw new Error(
500
+ `perfect-payload:- function setDependencyRule not found in ${attr} dependency `,
501
+ );
502
+ }
503
+ }
504
+ }
505
+ break;
506
+ default:
507
+ break;
508
+ }
509
+
510
+ if (attrExist && rowErrors.length == 0)
511
+ validatedPayload[attributeName] = attributeValue;
512
+ }
513
+ }
514
+
515
+ if (rowErrors?.length) {
516
+ return { ...inValidPayloadResponse, errors: rowErrors };
517
+ } else {
518
+ return { ...validPayloadResponse, validatedPayload };
519
+ }
520
+ }
521
+
522
+ export function perfectPayload(
523
+ data = {},
524
+ dataValidationRule = {},
525
+ validPayloadResponse = { statusCode: 200, valid: true },
526
+ inValidPayloadResponse = {
527
+ statusCode: 400,
528
+ valid: false,
529
+ message: "One or more attribute values are invalid",
530
+ },
531
+ ) {
532
+ return perfectPayloadStructured(
533
+ data,
534
+ dataValidationRule,
535
+ validPayloadResponse,
536
+ inValidPayloadResponse,
537
+ );
538
+ }
539
+
540
+ function perfectPayloadStructured(
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
+ basePath = "",
550
+ ) {
551
+ let validatedPayload = {};
552
+ let rowErrors = [];
553
+
554
+ for (const attributeName in dataValidationRule) {
555
+ let addNextError = true;
556
+
557
+ const attributeRules = dataValidationRule?.[attributeName];
558
+
559
+ const attributePath = getAttributePath(
560
+ attributeName,
561
+ attributeRules,
562
+ basePath,
563
+ );
564
+
565
+ const attributeValue = data?.[attributeName] ?? null;
566
+
567
+ const nullAllowed =
568
+ dataValidationRule?.[attributeName]?.["allowNull"] ?? true;
569
+
570
+ const isMandatoryField = attributeRules?.["mandatory"] ?? false;
571
+
572
+ const attrExist = Object.keys(data ?? {}).includes(attributeName);
573
+
574
+ for (const ruleName in attributeRules) {
575
+ switch (ruleName) {
576
+ // ==================================================
577
+ // MANDATORY
578
+ // ==================================================
579
+
580
+ case "mandatory":
581
+ if (isMandatoryField && !attrExist) {
582
+ addNextError = false;
583
+
584
+ addStructuredError(
585
+ rowErrors,
586
+ attributePath,
587
+ "REQUIRED",
588
+ attributeRules?.["mandatoryError"] ||
589
+ `${attributePath} is mandatory`,
590
+ );
591
+ } else if (!attrExist) {
592
+ addNextError = false;
593
+ }
594
+
595
+ break;
596
+
597
+ // ==================================================
598
+ // NULL
599
+ // ==================================================
600
+
601
+ case "allowNull":
602
+ if (addNextError && attributeValue == null) {
603
+ if (!attributeRules?.[ruleName]) {
604
+ addNextError = false;
605
+
606
+ addStructuredError(
607
+ rowErrors,
608
+ attributePath,
609
+ "NULL_NOT_ALLOWED",
610
+ attributeRules?.["allowNullError"] ||
611
+ `value null/'' not valid for attribute ${attributePath}`,
612
+ );
613
+ }
614
+ }
615
+
616
+ break;
617
+
618
+ // ==================================================
619
+ // EMPTY OBJECT
620
+ // ==================================================
621
+
622
+ case "allowEmptyObject":
623
+ if (addNextError) {
624
+ if (
625
+ !attributeRules?.["allowEmptyObject"] &&
626
+ attributeValue != null &&
627
+ Object.keys(attributeValue).length === 0
628
+ ) {
629
+ addStructuredError(
630
+ rowErrors,
631
+ attributePath,
632
+ "EMPTY_OBJECT_NOT_ALLOWED",
633
+ attributeRules?.["emptyObjectError"] ||
634
+ `value {} not valid for attribute ${attributePath}`,
635
+ );
636
+
637
+ addNextError = false;
638
+ }
639
+ }
640
+
641
+ break;
642
+
643
+ // ==================================================
644
+ // EMPTY ARRAY
645
+ // ==================================================
646
+
647
+ case "allowEmptyArray":
648
+ if (addNextError) {
649
+ if (
650
+ !attributeRules?.["allowEmptyArray"] &&
651
+ isArray(attributeValue) &&
652
+ attributeValue.length === 0
653
+ ) {
654
+ addStructuredError(
655
+ rowErrors,
656
+ attributePath,
657
+ "EMPTY_ARRAY_NOT_ALLOWED",
658
+ attributeRules?.["emptyArrayError"] ||
659
+ `${attributePath} cannot be an empty array`,
660
+ );
661
+
662
+ addNextError = false;
663
+ }
664
+ }
665
+
666
+ break;
667
+
668
+ // ==================================================
669
+ // ARRAY ELEMENT CONSTRAINTS
670
+ // ==================================================
671
+
672
+ case "elementConstraints":
673
+ if (addNextError) {
674
+ if (isArray(attributeValue) && attributeValue.length > 0) {
675
+ let elementError = null;
676
+
677
+ for (
678
+ let elementIndex = 0;
679
+ elementIndex < attributeValue.length;
680
+ elementIndex++
681
+ ) {
682
+ const element = attributeValue[elementIndex];
683
+
684
+ const { errors = [] } = perfectPayloadStructured(
685
+ {
686
+ [attributeName]: element,
687
+ },
688
+ {
689
+ [attributeName]: attributeRules[ruleName],
690
+ },
691
+ );
692
+
693
+ if (errors.length > 0) {
694
+ const firstElementError = errors[0];
695
+
696
+ const indexedPath = replaceRootPath(
697
+ firstElementError.path,
698
+ attributeName,
699
+ `${attributePath}[${elementIndex}]`,
700
+ );
701
+
702
+ if (attributeRules?.["elementConstraintsError"]) {
703
+ elementError = {
704
+ path: indexedPath,
705
+ code: "INVALID_ARRAY_ELEMENT",
706
+ message: attributeRules["elementConstraintsError"],
707
+ };
708
+ } else {
709
+ elementError = {
710
+ ...firstElementError,
711
+ path: indexedPath,
712
+ };
713
+ }
714
+
715
+ break;
716
+ }
717
+ }
718
+
719
+ if (elementError) {
720
+ rowErrors.push(elementError);
721
+ addNextError = false;
722
+ }
723
+ }
724
+ }
725
+
726
+ break;
727
+
728
+ // ==================================================
729
+ // REGEX
730
+ // ==================================================
731
+
732
+ case "regex":
733
+ if (addNextError) {
734
+ if (!isPassedRegex(attributeRules[ruleName], attributeValue)) {
735
+ addStructuredError(
736
+ rowErrors,
737
+ attributePath,
738
+ "REGEX_MISMATCH",
739
+ attributeRules?.["regexError"] ||
740
+ `${attributePath} failed to pass the regex ${attributeRules[ruleName]}`,
741
+ );
742
+
743
+ addNextError = false;
744
+ }
745
+ }
746
+
747
+ break;
748
+
749
+ // ==================================================
750
+ // TYPE
751
+ // ==================================================
752
+
753
+ case "type":
754
+ if (addNextError) {
755
+ if (attributeValue == null && nullAllowed) {
756
+ addNextError = true;
757
+ } else {
758
+ const expectedType = attributeRules[ruleName];
759
+
760
+ switch (expectedType) {
761
+ case "number":
762
+ if (!isNumber(attributeValue)) {
763
+ addStructuredError(
764
+ rowErrors,
765
+ attributePath,
766
+ "INVALID_TYPE",
767
+ attributeRules?.["typeError"] ||
768
+ getInvalidTypeMessage(
769
+ attributeValue,
770
+ attributePath,
771
+ expectedType,
772
+ ),
773
+ );
774
+
775
+ addNextError = false;
776
+ }
777
+
778
+ break;
779
+
780
+ case "string":
781
+ if (!isString(attributeValue)) {
782
+ addStructuredError(
783
+ rowErrors,
784
+ attributePath,
785
+ "INVALID_TYPE",
786
+ attributeRules?.["typeError"] ||
787
+ getInvalidTypeMessage(
788
+ attributeValue,
789
+ attributePath,
790
+ expectedType,
791
+ ),
792
+ );
793
+
794
+ addNextError = false;
795
+ }
796
+
797
+ break;
798
+
799
+ case "boolean":
800
+ if (!isBoolean(attributeValue)) {
801
+ addStructuredError(
802
+ rowErrors,
803
+ attributePath,
804
+ "INVALID_TYPE",
805
+ attributeRules?.["typeError"] ||
806
+ getInvalidTypeMessage(
807
+ attributeValue,
808
+ attributePath,
809
+ expectedType,
810
+ ),
811
+ );
812
+
813
+ addNextError = false;
814
+ }
815
+
816
+ break;
817
+
818
+ case "email":
819
+ if (!isValidEmail(attributeValue)) {
820
+ addStructuredError(
821
+ rowErrors,
822
+ attributePath,
823
+ "INVALID_EMAIL",
824
+ attributeRules?.["typeError"] ||
825
+ `Invalid email format for attribute ${attributePath}`,
826
+ );
827
+
828
+ addNextError = false;
829
+ }
830
+
831
+ break;
832
+ case "url":
833
+ if (!isValidUrl(attributeValue)) {
834
+ addStructuredError(
835
+ rowErrors,
836
+ attributePath,
837
+ "INVALID_URL",
838
+ attributeRules?.["typeError"] ||
839
+ `Invalid URL format(${attributeValue}) found in attribute ${attributePath}`,
840
+ );
841
+
842
+ addNextError = false;
843
+ }
844
+
845
+ break;
846
+
847
+ case "enum": {
848
+ const allEnumValues = attributeRules["enumValues"];
849
+
850
+ if (!allEnumValues?.includes(attributeValue)) {
851
+ addStructuredError(
852
+ rowErrors,
853
+ attributePath,
854
+ "INVALID_ENUM",
855
+ attributeRules?.["typeError"] ||
856
+ `Invalid value(${attributeValue}) found in attribute ${attributePath}, valid values are ${allEnumValues?.join(
857
+ ", ",
858
+ )}`,
859
+ );
860
+
861
+ addNextError = false;
862
+ }
863
+
864
+ break;
865
+ }
866
+
867
+ case "uuid":
868
+ if (!isUUID(attributeValue)) {
869
+ addStructuredError(
870
+ rowErrors,
871
+ attributePath,
872
+ "INVALID_UUID",
873
+ attributeRules?.["typeError"] ||
874
+ `Invalid UUID(${attributeValue}) found in attribute ${attributePath}`,
875
+ );
876
+
877
+ addNextError = false;
878
+ }
879
+
880
+ break;
881
+
882
+ case "uuidv1":
883
+ if (!isUUIDv1(attributeValue)) {
884
+ addStructuredError(
885
+ rowErrors,
886
+ attributePath,
887
+ "INVALID_UUID_V1",
888
+ attributeRules?.["typeError"] ||
889
+ `Invalid v1 UUID(${attributeValue}) found in attribute ${attributePath}`,
890
+ );
891
+
892
+ addNextError = false;
893
+ }
894
+
895
+ break;
896
+
897
+ case "uuidv3":
898
+ if (!isUUIDv3(attributeValue)) {
899
+ addStructuredError(
900
+ rowErrors,
901
+ attributePath,
902
+ "INVALID_UUID_V3",
903
+ attributeRules?.["typeError"] ||
904
+ `Invalid v3 UUID(${attributeValue}) found in attribute ${attributePath}`,
905
+ );
906
+
907
+ addNextError = false;
908
+ }
909
+
910
+ break;
911
+
912
+ case "uuidv4":
913
+ if (!isUUIDv4(attributeValue)) {
914
+ addStructuredError(
915
+ rowErrors,
916
+ attributePath,
917
+ "INVALID_UUID_V4",
918
+ attributeRules?.["typeError"] ||
919
+ `Invalid v4 UUID(${attributeValue}) found in attribute ${attributePath}`,
920
+ );
921
+
922
+ addNextError = false;
923
+ }
924
+
925
+ break;
926
+
927
+ case "uuidv5":
928
+ if (!isUUIDv5(attributeValue)) {
929
+ addStructuredError(
930
+ rowErrors,
931
+ attributePath,
932
+ "INVALID_UUID_V5",
933
+ attributeRules?.["typeError"] ||
934
+ `Invalid v5 UUID(${attributeValue}) found in attribute ${attributePath}`,
935
+ );
936
+
937
+ addNextError = false;
938
+ }
939
+
940
+ break;
941
+
942
+ case "objectId":
943
+ if (!isObjectId(attributeValue)) {
944
+ addStructuredError(
945
+ rowErrors,
946
+ attributePath,
947
+ "INVALID_OBJECT_ID",
948
+ attributeRules?.["typeError"] ||
949
+ `Invalid ObjectId(${attributeValue}) found in attribute ${attributePath}`,
950
+ );
951
+
952
+ addNextError = false;
953
+ }
954
+
955
+ break;
956
+
957
+ case "array":
958
+ if (!isArray(attributeValue)) {
959
+ addStructuredError(
960
+ rowErrors,
961
+ attributePath,
962
+ "INVALID_TYPE",
963
+ attributeRules?.["typeError"] ||
964
+ getInvalidTypeMessage(
965
+ attributeValue,
966
+ attributePath,
967
+ expectedType,
968
+ ),
969
+ );
970
+
971
+ addNextError = false;
972
+ }
973
+
974
+ break;
975
+
976
+ case "object":
977
+ if (!isObject(attributeValue)) {
978
+ addStructuredError(
979
+ rowErrors,
980
+ attributePath,
981
+ "INVALID_TYPE",
982
+ attributeRules?.["typeError"] ||
983
+ getInvalidTypeMessage(
984
+ attributeValue,
985
+ attributePath,
986
+ expectedType,
987
+ ),
988
+ );
989
+
990
+ addNextError = false;
991
+ }
992
+
993
+ break;
994
+
995
+ default:
996
+ break;
997
+ }
998
+ }
999
+ }
1000
+
1001
+ break;
1002
+
1003
+ // ==================================================
1004
+ // MIN LENGTH
1005
+ // ==================================================
1006
+
1007
+ case "minLength":
1008
+ if (addNextError) {
1009
+ if (attributeValue == null && nullAllowed) {
1010
+ addNextError = true;
1011
+ } else if (typeof attributeValue === "string") {
1012
+ if (attributeValue.length < +attributeRules[ruleName]) {
1013
+ addStructuredError(
1014
+ rowErrors,
1015
+ attributePath,
1016
+ "MIN_LENGTH",
1017
+ attributeRules?.["minLengthError"] ||
1018
+ `${attributePath} should be minimum of ${attributeRules[ruleName]} character`,
1019
+ );
1020
+
1021
+ addNextError = false;
1022
+ }
1023
+ } else {
1024
+ addStructuredError(
1025
+ rowErrors,
1026
+ attributePath,
1027
+ "INVALID_TYPE",
1028
+ `${attributePath} value should be a string type(minLength specified)`,
1029
+ );
1030
+
1031
+ addNextError = false;
1032
+ }
1033
+ }
1034
+
1035
+ break;
1036
+
1037
+ // ==================================================
1038
+ // MAX LENGTH
1039
+ // ==================================================
1040
+
1041
+ case "maxLength":
1042
+ if (addNextError) {
1043
+ if (attributeValue == null && nullAllowed) {
1044
+ addNextError = true;
1045
+ } else if (typeof attributeValue === "string") {
1046
+ if (attributeValue.length > +attributeRules[ruleName]) {
1047
+ addStructuredError(
1048
+ rowErrors,
1049
+ attributePath,
1050
+ "MAX_LENGTH",
1051
+ attributeRules?.["maxLengthError"] ||
1052
+ `${attributePath} can have maximum of ${attributeRules[ruleName]} character`,
1053
+ );
1054
+
1055
+ addNextError = false;
1056
+ }
1057
+ } else {
1058
+ addStructuredError(
1059
+ rowErrors,
1060
+ attributePath,
1061
+ "INVALID_TYPE",
1062
+ `${attributePath} value should be a string type(maxLength specified)`,
1063
+ );
1064
+
1065
+ addNextError = false;
1066
+ }
1067
+ }
1068
+
1069
+ break;
1070
+
1071
+ // ==================================================
1072
+ // PREVENT DECIMAL
1073
+ // ==================================================
1074
+
1075
+ case "preventDecimal":
1076
+ if (addNextError) {
1077
+ if (attributeValue == null && nullAllowed) {
1078
+ addNextError = true;
1079
+ } else if (!isNumber(attributeValue)) {
1080
+ addStructuredError(
1081
+ rowErrors,
1082
+ attributePath,
1083
+ "INVALID_TYPE",
1084
+ `Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required number value`,
1085
+ );
1086
+
1087
+ addNextError = false;
1088
+ } else if (+attributeValue % 1 !== 0) {
1089
+ addStructuredError(
1090
+ rowErrors,
1091
+ attributePath,
1092
+ "DECIMAL_NOT_ALLOWED",
1093
+ attributeRules?.["preventDecimalError"] ||
1094
+ `Decimal value not allowed in attribute ${attributePath}(${attributeValue})`,
1095
+ );
1096
+
1097
+ addNextError = false;
1098
+ }
1099
+ }
1100
+
1101
+ break;
1102
+
1103
+ // ==================================================
1104
+ // MIN
1105
+ // ==================================================
1106
+
1107
+ case "min":
1108
+ if (addNextError) {
1109
+ if (attributeValue == null && nullAllowed) {
1110
+ addNextError = true;
1111
+ } else if (!isNumber(attributeValue)) {
1112
+ addStructuredError(
1113
+ rowErrors,
1114
+ attributePath,
1115
+ "INVALID_TYPE",
1116
+ `Invalid type for attribute ${attributePath}, required number value`,
1117
+ );
1118
+
1119
+ addNextError = false;
1120
+ } else if (+attributeValue < +attributeRules[ruleName]) {
1121
+ addStructuredError(
1122
+ rowErrors,
1123
+ attributePath,
1124
+ "MIN_VALUE",
1125
+ attributeRules?.["minError"] ||
1126
+ `Minimum value ${attributeRules[ruleName]} is allowed in attribute ${attributePath}`,
1127
+ );
1128
+
1129
+ addNextError = false;
1130
+ }
1131
+ }
1132
+
1133
+ break;
1134
+ // ==================================================
1135
+ // MAX
1136
+ // ==================================================
1137
+ case "max":
1138
+ if (addNextError) {
1139
+ if (attributeValue == null && nullAllowed) {
1140
+ addNextError = true;
1141
+ } else if (!isNumber(attributeValue)) {
1142
+ addStructuredError(
1143
+ rowErrors,
1144
+ attributePath,
1145
+ "INVALID_TYPE",
1146
+ `Invalid type for attribute ${attributePath}, required number value`,
1147
+ );
1148
+
1149
+ addNextError = false;
1150
+ } else if (+attributeValue > +attributeRules[ruleName]) {
1151
+ addStructuredError(
1152
+ rowErrors,
1153
+ attributePath,
1154
+ "MAX_VALUE",
1155
+ attributeRules?.["maxError"] ||
1156
+ `Maximum value ${attributeRules[ruleName]} is allowed in attribute ${attributePath}`,
1157
+ );
1158
+
1159
+ addNextError = false;
1160
+ }
1161
+ }
1162
+
1163
+ break;
1164
+
1165
+ // ==================================================
1166
+ // RANGE
1167
+ // ==================================================
1168
+
1169
+ case "range":
1170
+ if (addNextError) {
1171
+ if (attributeValue == null && nullAllowed) {
1172
+ addNextError = true;
1173
+ } else if (!isNumber(attributeValue)) {
1174
+ addStructuredError(
1175
+ rowErrors,
1176
+ attributePath,
1177
+ "INVALID_TYPE",
1178
+ `Invalid type for attribute ${attributePath}, required number value`,
1179
+ );
1180
+
1181
+ addNextError = false;
1182
+ } else {
1183
+ const [min, max] = attributeRules[ruleName].split("-");
1184
+
1185
+ if (+attributeValue < +min || +attributeValue > +max) {
1186
+ addStructuredError(
1187
+ rowErrors,
1188
+ attributePath,
1189
+ "OUT_OF_RANGE",
1190
+ attributeRules?.["rangeError"] ||
1191
+ `Attribute ${attributePath} should have a value between ${min} and ${max}`,
1192
+ );
1193
+
1194
+ addNextError = false;
1195
+ }
1196
+ }
1197
+ }
1198
+
1199
+ break;
1200
+ // ==================================================
1201
+ // NESTED OBJECT
1202
+ // ==================================================
1203
+
1204
+ case "objectAttr":
1205
+ if (addNextError) {
1206
+ const { errors = [] } = perfectPayloadStructured(
1207
+ attributeValue,
1208
+ attributeRules[ruleName],
1209
+ { statusCode: 200, valid: true },
1210
+ {
1211
+ statusCode: 400,
1212
+ valid: false,
1213
+ message: "One or more attribute values are invalid",
1214
+ },
1215
+ attributePath,
1216
+ );
1217
+
1218
+ rowErrors = [...rowErrors, ...errors];
1219
+
1220
+ addNextError = true;
1221
+ }
1222
+
1223
+ break;
1224
+
1225
+ // ==================================================
1226
+ // DEPENDENCY
1227
+ // ==================================================
1228
+
1229
+ case "dependency":
1230
+ if (addNextError) {
1231
+ const allDependencyAttr = Object.keys(attributeRules?.[ruleName]);
1232
+
1233
+ for (const attr of allDependencyAttr) {
1234
+ const dependencyRule = attributeRules?.[ruleName]?.[attr];
1235
+
1236
+ if (typeof dependencyRule?.setDependencyRule === "function") {
1237
+ const newRule = dependencyRule.setDependencyRule(
1238
+ attributeValue,
1239
+ data?.[attr],
1240
+ );
1241
+
1242
+ let newData = {
1243
+ [attributeName]: attributeValue,
1244
+ };
1245
+
1246
+ if (Object.keys(data ?? {}).includes(attr)) {
1247
+ newData = {
1248
+ ...newData,
1249
+ [attr]: data?.[attr],
1250
+ };
1251
+ }
1252
+
1253
+ const { errors = [] } = perfectPayloadStructured(
1254
+ newData,
1255
+ {
1256
+ [attr]: newRule,
1257
+ },
1258
+ {
1259
+ statusCode: 200,
1260
+ valid: true,
1261
+ },
1262
+ {
1263
+ statusCode: 400,
1264
+ valid: false,
1265
+ message: "One or more attribute values are invalid",
1266
+ },
1267
+ basePath,
1268
+ );
1269
+
1270
+ rowErrors = [...rowErrors, ...errors];
1271
+
1272
+ addNextError = true;
1273
+ } else {
1274
+ throw new Error(
1275
+ `perfect-payload:- function setDependencyRule not found in ${attr} dependency `,
1276
+ );
1277
+ }
1278
+ }
1279
+ }
1280
+
1281
+ break;
1282
+
1283
+ default:
1284
+ break;
1285
+ }
1286
+ }
1287
+
1288
+ if (
1289
+ attrExist &&
1290
+ !rowErrors.some(
1291
+ (error) =>
1292
+ error.path === attributePath ||
1293
+ error.path.startsWith(`${attributePath}.`) ||
1294
+ error.path.startsWith(`${attributePath}[`),
1295
+ )
1296
+ ) {
1297
+ validatedPayload[attributeName] = attributeValue;
1298
+ }
1299
+ }
1300
+
1301
+ if (rowErrors.length > 0) {
1302
+ return {
1303
+ ...inValidPayloadResponse,
1304
+ errors: rowErrors,
1305
+ };
1306
+ }
1307
+
1308
+ return {
1309
+ ...validPayloadResponse,
1310
+ validatedPayload,
1311
+ };
1312
+ }
1313
+
1314
+ function addStructuredError(errors, path, code, message) {
1315
+ errors.push({
1316
+ path,
1317
+ code,
1318
+ message,
1319
+ });
1320
+ }
1321
+
1322
+ function getAttributePath(attributeName, attributeRules, basePath) {
1323
+ const customPath = attributeRules?.path;
1324
+
1325
+ if (customPath) {
1326
+ return `${customPath}.${attributeName}`;
1327
+ }
1328
+
1329
+ if (basePath) {
1330
+ return `${basePath}.${attributeName}`;
1331
+ }
1332
+
1333
+ return attributeName;
1334
+ }
1335
+
1336
+ function replaceRootPath(currentPath, currentRoot, replacementRoot) {
1337
+ if (currentPath === currentRoot) {
1338
+ return replacementRoot;
1339
+ }
1340
+
1341
+ if (currentPath.startsWith(`${currentRoot}.`)) {
1342
+ return `${replacementRoot}${currentPath.slice(currentRoot.length)}`;
1343
+ }
1344
+
1345
+ if (currentPath.startsWith(`${currentRoot}[`)) {
1346
+ return `${replacementRoot}${currentPath.slice(currentRoot.length)}`;
1347
+ }
1348
+
1349
+ return replacementRoot;
1350
+ }
1351
+
1352
+ function getInvalidTypeMessage(value, attributePath, expectedType) {
1353
+ return `Invalid type for attribute ${attributePath}, required ${expectedType} value`;
1354
+ // return `Invalid ${typeof value} value(${value}) found in attribute ${attributePath}, required ${expectedType} value`;
1355
+ }
1356
+
1357
+ function isNumber(value) {
1358
+ return typeof value == "number";
1359
+ }
1360
+
1361
+ function isString(value) {
1362
+ return typeof value == "string";
1363
+ }
1364
+
1365
+ function isBoolean(value) {
1366
+ return typeof value == "boolean";
1367
+ }
1368
+
1369
+ function isArray(value) {
1370
+ return Array.isArray(value);
1371
+ }
1372
+
1373
+ function isObject(value) {
1374
+ return typeof value == "object";
1375
+ }
1376
+
1377
+ function isValidEmail(email) {
1378
+ const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
1379
+ return emailRegex.test(email);
1380
+ }
1381
+
1382
+ function isValidUrl(url) {
1383
+ const urlRegex =
1384
+ /^(https?:\/\/)([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})(:[0-9]+)?(\/[^\s]*)?(\?[^\s]*)?$/;
1385
+ return urlRegex.test(url);
1386
+ }
1387
+
1388
+ function isUUID(uuid) {
1389
+ const uuidRegex =
1390
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1391
+
1392
+ return uuidRegex.test(uuid);
1393
+ }
1394
+
1395
+ function isUUIDv1(uuid) {
1396
+ const uuidRegex =
1397
+ /^[0-9a-f]{8}-[0-9a-f]{4}-1[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1398
+
1399
+ return uuidRegex.test(uuid);
1400
+ }
1401
+
1402
+ function isUUIDv3(uuid) {
1403
+ const uuidRegex =
1404
+ /^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1405
+
1406
+ return uuidRegex.test(uuid);
1407
+ }
1408
+
1409
+ function isUUIDv4(uuid) {
1410
+ const uuidRegex =
1411
+ /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1412
+
1413
+ return uuidRegex.test(uuid);
1414
+ }
1415
+
1416
+ function isUUIDv5(uuid) {
1417
+ const uuidRegex =
1418
+ /^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1419
+
1420
+ return uuidRegex.test(uuid);
1421
+ }
1422
+
1423
+ function isObjectId(id) {
1424
+ return /^[0-9a-fA-F]{24}$/.test(id);
1425
+ }
1426
+
1427
+ function isPassedRegex(reExpression, value) {
1428
+ return reExpression.test(value);
1429
+ }