@umituz/react-native-validation 1.4.5 → 1.4.6
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/package.json +1 -1
- package/src/index.ts +21 -7
- package/src/infrastructure/utils/validators.ts +264 -23
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
// Domain Layer - Entities
|
|
11
|
-
export type { ValidationResult } from
|
|
11
|
+
export type { ValidationResult } from "./domain/entities/ValidationResult";
|
|
12
12
|
|
|
13
13
|
// Infrastructure Layer - Validators
|
|
14
14
|
export {
|
|
@@ -24,7 +24,19 @@ export {
|
|
|
24
24
|
validateDateOfBirth,
|
|
25
25
|
validateAge,
|
|
26
26
|
batchValidate,
|
|
27
|
-
} from
|
|
27
|
+
} from "./infrastructure/utils/validators";
|
|
28
|
+
|
|
29
|
+
// Dream-specific validators
|
|
30
|
+
export {
|
|
31
|
+
validateDreamTitle,
|
|
32
|
+
validateDreamDescription,
|
|
33
|
+
validateDreamMood,
|
|
34
|
+
validateDreamCategory,
|
|
35
|
+
validateDreamDate,
|
|
36
|
+
validateDreamTags,
|
|
37
|
+
validateDreamPrivacy,
|
|
38
|
+
validateDreamForm,
|
|
39
|
+
} from "./infrastructure/utils/validators";
|
|
28
40
|
|
|
29
41
|
// Infrastructure Layer - Sanitization
|
|
30
42
|
export {
|
|
@@ -36,7 +48,7 @@ export {
|
|
|
36
48
|
sanitizeText,
|
|
37
49
|
containsDangerousChars,
|
|
38
50
|
isWithinLengthLimit,
|
|
39
|
-
} from
|
|
51
|
+
} from "./infrastructure/utils/sanitization";
|
|
40
52
|
|
|
41
53
|
// Infrastructure Layer - MIME Type Validation
|
|
42
54
|
export {
|
|
@@ -44,7 +56,7 @@ export {
|
|
|
44
56
|
SUPPORTED_IMAGE_MIME_TYPES,
|
|
45
57
|
EXTENSION_TO_MIME_TYPE,
|
|
46
58
|
MIME_TYPE_TO_EXTENSION,
|
|
47
|
-
} from
|
|
59
|
+
} from "./infrastructure/utils/mime-types.constants";
|
|
48
60
|
|
|
49
61
|
export {
|
|
50
62
|
getFileExtension,
|
|
@@ -53,8 +65,10 @@ export {
|
|
|
53
65
|
validateImageMimeType,
|
|
54
66
|
validateImageExtension,
|
|
55
67
|
validateImageDataUrl,
|
|
56
|
-
} from
|
|
68
|
+
} from "./infrastructure/utils/mime-type-validator";
|
|
57
69
|
|
|
58
70
|
// Infrastructure Layer - Image Validation
|
|
59
|
-
export {
|
|
60
|
-
|
|
71
|
+
export {
|
|
72
|
+
validateImageUri,
|
|
73
|
+
getImageMimeType,
|
|
74
|
+
} from "./infrastructure/utils/image-validator";
|
|
@@ -3,19 +3,19 @@
|
|
|
3
3
|
* Comprehensive validation functions for React Native forms
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { ValidationResult } from
|
|
6
|
+
import type { ValidationResult } from "../../domain/entities/ValidationResult";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Validate email format
|
|
10
10
|
*/
|
|
11
11
|
export const validateEmail = (email: string): ValidationResult => {
|
|
12
|
-
if (!email || email.trim() ===
|
|
13
|
-
return { isValid: false, error:
|
|
12
|
+
if (!email || email.trim() === "") {
|
|
13
|
+
return { isValid: false, error: "Email is required" };
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
17
17
|
if (!emailRegex.test(email)) {
|
|
18
|
-
return { isValid: false, error:
|
|
18
|
+
return { isValid: false, error: "Please enter a valid email address" };
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
return { isValid: true };
|
|
@@ -26,9 +26,9 @@ export const validateEmail = (email: string): ValidationResult => {
|
|
|
26
26
|
*/
|
|
27
27
|
export const validateRequired = (
|
|
28
28
|
value: string,
|
|
29
|
-
fieldName: string =
|
|
29
|
+
fieldName: string = "This field",
|
|
30
30
|
): ValidationResult => {
|
|
31
|
-
if (!value || value.trim() ===
|
|
31
|
+
if (!value || value.trim() === "") {
|
|
32
32
|
return { isValid: false, error: `${fieldName} is required` };
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -40,10 +40,10 @@ export const validateRequired = (
|
|
|
40
40
|
*/
|
|
41
41
|
export const validateName = (
|
|
42
42
|
name: string,
|
|
43
|
-
fieldName: string =
|
|
44
|
-
minLength: number = 2
|
|
43
|
+
fieldName: string = "Name",
|
|
44
|
+
minLength: number = 2,
|
|
45
45
|
): ValidationResult => {
|
|
46
|
-
if (!name || name.trim() ===
|
|
46
|
+
if (!name || name.trim() === "") {
|
|
47
47
|
return { isValid: false, error: `${fieldName} is required` };
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -61,13 +61,13 @@ export const validateName = (
|
|
|
61
61
|
* Validate phone number (E.164 format)
|
|
62
62
|
*/
|
|
63
63
|
export const validatePhone = (phone: string): ValidationResult => {
|
|
64
|
-
if (!phone || phone.trim() ===
|
|
65
|
-
return { isValid: false, error:
|
|
64
|
+
if (!phone || phone.trim() === "") {
|
|
65
|
+
return { isValid: false, error: "Phone number is required" };
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
const phoneRegex = /^\+[1-9]\d{1,14}$/;
|
|
69
69
|
if (!phoneRegex.test(phone)) {
|
|
70
|
-
return { isValid: false, error:
|
|
70
|
+
return { isValid: false, error: "Please enter a valid phone number" };
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
return { isValid: true };
|
|
@@ -80,7 +80,7 @@ export const validateNumberRange = (
|
|
|
80
80
|
value: number,
|
|
81
81
|
min: number,
|
|
82
82
|
max: number,
|
|
83
|
-
fieldName: string =
|
|
83
|
+
fieldName: string = "Value",
|
|
84
84
|
): ValidationResult => {
|
|
85
85
|
if (isNaN(value)) {
|
|
86
86
|
return { isValid: false, error: `${fieldName} must be a number` };
|
|
@@ -101,7 +101,7 @@ export const validateNumberRange = (
|
|
|
101
101
|
*/
|
|
102
102
|
export const validatePositiveNumber = (
|
|
103
103
|
value: number,
|
|
104
|
-
fieldName: string =
|
|
104
|
+
fieldName: string = "Value",
|
|
105
105
|
): ValidationResult => {
|
|
106
106
|
if (isNaN(value)) {
|
|
107
107
|
return { isValid: false, error: `${fieldName} must be a number` };
|
|
@@ -120,7 +120,7 @@ export const validatePositiveNumber = (
|
|
|
120
120
|
export const validateMinLength = (
|
|
121
121
|
value: string,
|
|
122
122
|
minLength: number,
|
|
123
|
-
fieldName: string =
|
|
123
|
+
fieldName: string = "Field",
|
|
124
124
|
): ValidationResult => {
|
|
125
125
|
if (!value || value.trim().length < minLength) {
|
|
126
126
|
return {
|
|
@@ -138,7 +138,7 @@ export const validateMinLength = (
|
|
|
138
138
|
export const validateMaxLength = (
|
|
139
139
|
value: string,
|
|
140
140
|
maxLength: number,
|
|
141
|
-
fieldName: string =
|
|
141
|
+
fieldName: string = "Field",
|
|
142
142
|
): ValidationResult => {
|
|
143
143
|
if (value && value.trim().length > maxLength) {
|
|
144
144
|
return {
|
|
@@ -156,8 +156,8 @@ export const validateMaxLength = (
|
|
|
156
156
|
export const validatePattern = (
|
|
157
157
|
value: string,
|
|
158
158
|
pattern: RegExp,
|
|
159
|
-
fieldName: string =
|
|
160
|
-
errorMessage?: string
|
|
159
|
+
fieldName: string = "Field",
|
|
160
|
+
errorMessage?: string,
|
|
161
161
|
): ValidationResult => {
|
|
162
162
|
if (!value) {
|
|
163
163
|
return { isValid: false, error: `${fieldName} is required` };
|
|
@@ -178,18 +178,18 @@ export const validatePattern = (
|
|
|
178
178
|
*/
|
|
179
179
|
export const validateDateOfBirth = (date: Date): ValidationResult => {
|
|
180
180
|
if (!date || !(date instanceof Date) || isNaN(date.getTime())) {
|
|
181
|
-
return { isValid: false, error:
|
|
181
|
+
return { isValid: false, error: "Please enter a valid date" };
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
const today = new Date();
|
|
185
185
|
const age = today.getFullYear() - date.getFullYear();
|
|
186
186
|
|
|
187
187
|
if (age < 13) {
|
|
188
|
-
return { isValid: false, error:
|
|
188
|
+
return { isValid: false, error: "You must be at least 13 years old" };
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
if (age > 120) {
|
|
192
|
-
return { isValid: false, error:
|
|
192
|
+
return { isValid: false, error: "Please enter a valid date of birth" };
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
return { isValid: true };
|
|
@@ -199,7 +199,7 @@ export const validateDateOfBirth = (date: Date): ValidationResult => {
|
|
|
199
199
|
* Validate age
|
|
200
200
|
*/
|
|
201
201
|
export const validateAge = (age: number): ValidationResult => {
|
|
202
|
-
return validateNumberRange(age, 13, 120,
|
|
202
|
+
return validateNumberRange(age, 13, 120, "Age");
|
|
203
203
|
};
|
|
204
204
|
|
|
205
205
|
/**
|
|
@@ -207,7 +207,7 @@ export const validateAge = (age: number): ValidationResult => {
|
|
|
207
207
|
* Validates multiple fields and returns all errors
|
|
208
208
|
*/
|
|
209
209
|
export const batchValidate = (
|
|
210
|
-
validations: Array<{ field: string; validator: () => ValidationResult }
|
|
210
|
+
validations: Array<{ field: string; validator: () => ValidationResult }>,
|
|
211
211
|
): { isValid: boolean; errors: Record<string, string> } => {
|
|
212
212
|
const errors: Record<string, string> = {};
|
|
213
213
|
let isValid = true;
|
|
@@ -223,3 +223,244 @@ export const batchValidate = (
|
|
|
223
223
|
return { isValid, errors };
|
|
224
224
|
};
|
|
225
225
|
|
|
226
|
+
/**
|
|
227
|
+
* Dream-specific validation functions
|
|
228
|
+
*/
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Validate dream title
|
|
232
|
+
*/
|
|
233
|
+
export const validateDreamTitle = (
|
|
234
|
+
title: string,
|
|
235
|
+
minLength: number = 3,
|
|
236
|
+
maxLength: number = 100,
|
|
237
|
+
): ValidationResult => {
|
|
238
|
+
if (!title || title.trim() === "") {
|
|
239
|
+
return { isValid: false, error: "Dream title is required" };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (title.trim().length < minLength) {
|
|
243
|
+
return {
|
|
244
|
+
isValid: false,
|
|
245
|
+
error: `Dream title must be at least ${minLength} characters`,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (title.trim().length > maxLength) {
|
|
250
|
+
return {
|
|
251
|
+
isValid: false,
|
|
252
|
+
error: `Dream title must be at most ${maxLength} characters`,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return { isValid: true };
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Validate dream description
|
|
261
|
+
*/
|
|
262
|
+
export const validateDreamDescription = (
|
|
263
|
+
description: string,
|
|
264
|
+
minLength: number = 10,
|
|
265
|
+
maxLength: number = 2000,
|
|
266
|
+
): ValidationResult => {
|
|
267
|
+
if (!description || description.trim() === "") {
|
|
268
|
+
return { isValid: false, error: "Dream description is required" };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (description.trim().length < minLength) {
|
|
272
|
+
return {
|
|
273
|
+
isValid: false,
|
|
274
|
+
error: `Dream description must be at least ${minLength} characters`,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (description.trim().length > maxLength) {
|
|
279
|
+
return {
|
|
280
|
+
isValid: false,
|
|
281
|
+
error: `Dream description must be at most ${maxLength} characters`,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return { isValid: true };
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Validate dream mood
|
|
290
|
+
*/
|
|
291
|
+
export const validateDreamMood = (mood: string): ValidationResult => {
|
|
292
|
+
if (!mood || mood.trim() === "") {
|
|
293
|
+
return { isValid: false, error: "Dream mood is required" };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const validMoods = [
|
|
297
|
+
"happy",
|
|
298
|
+
"sad",
|
|
299
|
+
"scared",
|
|
300
|
+
"excited",
|
|
301
|
+
"peaceful",
|
|
302
|
+
"anxious",
|
|
303
|
+
"confused",
|
|
304
|
+
"angry",
|
|
305
|
+
"surprised",
|
|
306
|
+
"neutral",
|
|
307
|
+
];
|
|
308
|
+
if (!validMoods.includes(mood.toLowerCase())) {
|
|
309
|
+
return {
|
|
310
|
+
isValid: false,
|
|
311
|
+
error: "Please select a valid mood",
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return { isValid: true };
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Validate dream category
|
|
320
|
+
*/
|
|
321
|
+
export const validateDreamCategory = (category: string): ValidationResult => {
|
|
322
|
+
if (!category || category.trim() === "") {
|
|
323
|
+
return { isValid: false, error: "Dream category is required" };
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const validCategories = [
|
|
327
|
+
"lucid",
|
|
328
|
+
"nightmare",
|
|
329
|
+
"recurring",
|
|
330
|
+
"prophetic",
|
|
331
|
+
"healing",
|
|
332
|
+
"creative",
|
|
333
|
+
"adventure",
|
|
334
|
+
"relationship",
|
|
335
|
+
"work",
|
|
336
|
+
"childhood",
|
|
337
|
+
"flying",
|
|
338
|
+
"falling",
|
|
339
|
+
"chase",
|
|
340
|
+
"exam",
|
|
341
|
+
"tooth",
|
|
342
|
+
"other",
|
|
343
|
+
];
|
|
344
|
+
if (!validCategories.includes(category.toLowerCase())) {
|
|
345
|
+
return {
|
|
346
|
+
isValid: false,
|
|
347
|
+
error: "Please select a valid category",
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return { isValid: true };
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Validate dream date
|
|
356
|
+
*/
|
|
357
|
+
export const validateDreamDate = (date: Date): ValidationResult => {
|
|
358
|
+
if (!date || !(date instanceof Date) || isNaN(date.getTime())) {
|
|
359
|
+
return { isValid: false, error: "Please enter a valid dream date" };
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const today = new Date();
|
|
363
|
+
if (date > today) {
|
|
364
|
+
return { isValid: false, error: "Dream date cannot be in the future" };
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Don't allow dreams older than 1 year
|
|
368
|
+
const oneYearAgo = new Date();
|
|
369
|
+
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
|
|
370
|
+
if (date < oneYearAgo) {
|
|
371
|
+
return {
|
|
372
|
+
isValid: false,
|
|
373
|
+
error: "Dream date cannot be more than 1 year old",
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return { isValid: true };
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Validate dream tags
|
|
382
|
+
*/
|
|
383
|
+
export const validateDreamTags = (tags: string[]): ValidationResult => {
|
|
384
|
+
if (!Array.isArray(tags)) {
|
|
385
|
+
return { isValid: false, error: "Dream tags must be an array" };
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (tags.length > 10) {
|
|
389
|
+
return { isValid: false, error: "Maximum 10 tags allowed" };
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
for (const tag of tags) {
|
|
393
|
+
if (typeof tag !== "string" || tag.trim().length === 0) {
|
|
394
|
+
return { isValid: false, error: "All tags must be non-empty strings" };
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (tag.trim().length > 20) {
|
|
398
|
+
return {
|
|
399
|
+
isValid: false,
|
|
400
|
+
error: "Each tag must be at most 20 characters",
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return { isValid: true };
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Validate dream privacy setting
|
|
410
|
+
*/
|
|
411
|
+
export const validateDreamPrivacy = (privacy: string): ValidationResult => {
|
|
412
|
+
if (!privacy || privacy.trim() === "") {
|
|
413
|
+
return { isValid: false, error: "Privacy setting is required" };
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const validPrivacyOptions = ["private", "friends", "public"];
|
|
417
|
+
if (!validPrivacyOptions.includes(privacy.toLowerCase())) {
|
|
418
|
+
return {
|
|
419
|
+
isValid: false,
|
|
420
|
+
error: "Please select a valid privacy setting",
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return { isValid: true };
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Complete dream form validation
|
|
429
|
+
* Validates all dream fields at once
|
|
430
|
+
*/
|
|
431
|
+
export const validateDreamForm = (formData: {
|
|
432
|
+
title?: string;
|
|
433
|
+
description?: string;
|
|
434
|
+
mood?: string;
|
|
435
|
+
category?: string;
|
|
436
|
+
date?: Date;
|
|
437
|
+
tags?: string[];
|
|
438
|
+
privacy?: string;
|
|
439
|
+
}): { isValid: boolean; errors: Record<string, string> } => {
|
|
440
|
+
const validations = [
|
|
441
|
+
{
|
|
442
|
+
field: "title",
|
|
443
|
+
validator: () => validateDreamTitle(formData.title || ""),
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
field: "description",
|
|
447
|
+
validator: () => validateDreamDescription(formData.description || ""),
|
|
448
|
+
},
|
|
449
|
+
{ field: "mood", validator: () => validateDreamMood(formData.mood || "") },
|
|
450
|
+
{
|
|
451
|
+
field: "category",
|
|
452
|
+
validator: () => validateDreamCategory(formData.category || ""),
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
field: "date",
|
|
456
|
+
validator: () => validateDreamDate(formData.date || new Date()),
|
|
457
|
+
},
|
|
458
|
+
{ field: "tags", validator: () => validateDreamTags(formData.tags || []) },
|
|
459
|
+
{
|
|
460
|
+
field: "privacy",
|
|
461
|
+
validator: () => validateDreamPrivacy(formData.privacy || ""),
|
|
462
|
+
},
|
|
463
|
+
];
|
|
464
|
+
|
|
465
|
+
return batchValidate(validations);
|
|
466
|
+
};
|