@umituz/react-native-auth 4.3.77 → 4.3.78
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/exports/domain.ts +32 -0
- package/src/exports/infrastructure.ts +80 -0
- package/src/exports/presentation.ts +115 -0
- package/src/exports/shared.ts +93 -0
- package/src/exports/utils.ts +10 -0
- package/src/index.ts +7 -132
- package/src/infrastructure/utils/listener/anonymousSignIn/attemptAnonymousSignIn.ts +81 -0
- package/src/infrastructure/utils/listener/anonymousSignIn/constants.ts +7 -0
- package/src/infrastructure/utils/listener/anonymousSignIn/createAnonymousSignInHandler.ts +59 -0
- package/src/infrastructure/utils/listener/anonymousSignIn/index.ts +16 -0
- package/src/infrastructure/utils/listener/anonymousSignIn/types.ts +21 -0
- package/src/presentation/components/RegisterForm/RegisterForm.tsx +91 -0
- package/src/presentation/components/RegisterForm/RegisterFormFields.tsx +117 -0
- package/src/presentation/components/RegisterForm/index.ts +7 -0
- package/src/presentation/components/RegisterForm/styles.ts +11 -0
- package/src/presentation/components/RegisterForm/types.ts +34 -0
- package/src/presentation/hooks/auth/index.ts +7 -0
- package/src/presentation/hooks/auth/types.ts +26 -0
- package/src/presentation/hooks/auth/useAuthBottomSheet.ts +110 -0
- package/src/presentation/hooks/auth/useSocialAuthHandlers.ts +56 -0
- package/src/shared/error-handling/handlers/ErrorHandler.ts +70 -0
- package/src/shared/error-handling/handlers/FormErrorHandler.ts +99 -0
- package/src/shared/error-handling/handlers/index.ts +7 -0
- package/src/shared/error-handling/index.ts +19 -0
- package/src/shared/error-handling/mappers/ErrorMapper.ts +115 -0
- package/src/shared/error-handling/mappers/FieldErrorMapper.ts +97 -0
- package/src/shared/error-handling/mappers/index.ts +6 -0
- package/src/shared/error-handling/types/ErrorTypes.ts +23 -0
- package/src/shared/error-handling/types/index.ts +10 -0
- package/src/shared/form/builders/FieldBuilder.ts +90 -0
- package/src/shared/form/builders/FormBuilder.ts +126 -0
- package/src/shared/form/builders/index.ts +9 -0
- package/src/shared/form/index.ts +51 -0
- package/src/shared/form/state/FormState.ts +114 -0
- package/src/shared/form/state/index.ts +16 -0
- package/src/shared/form/types/FormTypes.ts +30 -0
- package/src/shared/form/types/index.ts +11 -0
- package/src/shared/form/utils/fieldUtils.ts +115 -0
- package/src/shared/form/utils/formUtils.ts +113 -0
- package/src/shared/form/utils/index.ts +6 -0
- package/src/shared/validation/index.ts +23 -0
- package/src/shared/validation/rules/ValidationRule.ts +71 -0
- package/src/shared/validation/rules/index.ts +5 -0
- package/src/shared/validation/sanitizers/EmailSanitizer.ts +22 -0
- package/src/shared/validation/sanitizers/PasswordSanitizer.ts +24 -0
- package/src/shared/validation/sanitizers/index.ts +7 -0
- package/src/shared/validation/types.ts +26 -0
- package/src/shared/validation/validators/EmailValidator.ts +61 -0
- package/src/shared/validation/validators/NameValidator.ts +52 -0
- package/src/shared/validation/validators/PasswordValidator.ts +92 -0
- package/src/shared/validation/validators/index.ts +8 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field Utilities
|
|
3
|
+
* Utility functions for individual field operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Create field change handler with callback
|
|
8
|
+
*/
|
|
9
|
+
export function createFieldChangeHandler(
|
|
10
|
+
onChange: (value: string) => void,
|
|
11
|
+
callbacks?: {
|
|
12
|
+
onErrorClear?: () => void;
|
|
13
|
+
onValidationError?: () => void;
|
|
14
|
+
}
|
|
15
|
+
) {
|
|
16
|
+
return (value: string) => {
|
|
17
|
+
onChange(value);
|
|
18
|
+
callbacks?.onErrorClear?.();
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create field blur handler
|
|
24
|
+
*/
|
|
25
|
+
export function createFieldBlurHandler(
|
|
26
|
+
onBlur: () => void,
|
|
27
|
+
validate?: () => void
|
|
28
|
+
) {
|
|
29
|
+
return () => {
|
|
30
|
+
onBlur();
|
|
31
|
+
validate?.();
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Debounce field change
|
|
37
|
+
*/
|
|
38
|
+
export function debounceFieldChange(
|
|
39
|
+
onChange: (value: string) => void,
|
|
40
|
+
delay: number = 300
|
|
41
|
+
) {
|
|
42
|
+
let timeoutId: NodeJS.Timeout | null = null;
|
|
43
|
+
|
|
44
|
+
return (value: string) => {
|
|
45
|
+
if (timeoutId) {
|
|
46
|
+
clearTimeout(timeoutId);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
timeoutId = setTimeout(() => {
|
|
50
|
+
onChange(value);
|
|
51
|
+
}, delay);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if field value is empty
|
|
57
|
+
*/
|
|
58
|
+
export function isFieldValueEmpty(value: string): boolean {
|
|
59
|
+
return !value || value.trim().length === 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Sanitize field value
|
|
64
|
+
*/
|
|
65
|
+
export function sanitizeFieldValue(
|
|
66
|
+
value: string,
|
|
67
|
+
sanitizers: Array<(value: string) => string>
|
|
68
|
+
): string {
|
|
69
|
+
return sanitizers.reduce((acc, sanitizer) => sanitizer(acc), value);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Format field value for display
|
|
74
|
+
*/
|
|
75
|
+
export function formatFieldValue(
|
|
76
|
+
value: string,
|
|
77
|
+
formatter: (value: string) => string
|
|
78
|
+
): string {
|
|
79
|
+
return formatter(value);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Validate field value
|
|
84
|
+
*/
|
|
85
|
+
export function validateFieldValue(
|
|
86
|
+
value: string,
|
|
87
|
+
validators: Array<(value: string) => boolean>
|
|
88
|
+
): boolean {
|
|
89
|
+
return validators.every((validator) => validator(value));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get field display value
|
|
94
|
+
*/
|
|
95
|
+
export function getFieldDisplayValue(
|
|
96
|
+
value: string,
|
|
97
|
+
placeholder: string = ''
|
|
98
|
+
): string {
|
|
99
|
+
return value || placeholder;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Truncate field value
|
|
104
|
+
*/
|
|
105
|
+
export function truncateFieldValue(
|
|
106
|
+
value: string,
|
|
107
|
+
maxLength: number,
|
|
108
|
+
suffix: string = '...'
|
|
109
|
+
): string {
|
|
110
|
+
if (value.length <= maxLength) {
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return value.substring(0, maxLength - suffix.length) + suffix;
|
|
115
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Form Utilities
|
|
3
|
+
* Utility functions for common form operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Sanitize form values
|
|
8
|
+
*/
|
|
9
|
+
export function sanitizeFormValues<T extends Record<string, string>>(
|
|
10
|
+
values: T,
|
|
11
|
+
sanitizers: Partial<Record<keyof T, (value: string) => string>>
|
|
12
|
+
): T {
|
|
13
|
+
const sanitized = { ...values };
|
|
14
|
+
|
|
15
|
+
for (const key in sanitizers) {
|
|
16
|
+
if (key in sanitized) {
|
|
17
|
+
const sanitizer = sanitizers[key];
|
|
18
|
+
if (sanitizer) {
|
|
19
|
+
sanitized[key] = sanitizer(sanitized[key]);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return sanitized;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Extract form values for specific fields
|
|
29
|
+
*/
|
|
30
|
+
export function extractFields<T extends Record<string, string>>(
|
|
31
|
+
values: T,
|
|
32
|
+
fields: (keyof T)[]
|
|
33
|
+
): Pick<T, keyof T> {
|
|
34
|
+
const extracted = {} as Pick<T, keyof T>;
|
|
35
|
+
|
|
36
|
+
for (const field of fields) {
|
|
37
|
+
if (field in values) {
|
|
38
|
+
extracted[field] = values[field];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return extracted;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Check if all required fields are filled
|
|
47
|
+
*/
|
|
48
|
+
export function areRequiredFieldsFilled<T extends Record<string, string>>(
|
|
49
|
+
values: T,
|
|
50
|
+
requiredFields: (keyof T)[]
|
|
51
|
+
): boolean {
|
|
52
|
+
return requiredFields.every((field) => {
|
|
53
|
+
const value = values[field];
|
|
54
|
+
return value && value.trim().length > 0;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get empty required fields
|
|
60
|
+
*/
|
|
61
|
+
export function getEmptyRequiredFields<T extends Record<string, string>>(
|
|
62
|
+
values: T,
|
|
63
|
+
requiredFields: (keyof T)[]
|
|
64
|
+
): (keyof T)[] {
|
|
65
|
+
return requiredFields.filter((field) => {
|
|
66
|
+
const value = values[field];
|
|
67
|
+
return !value || value.trim().length === 0;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Create form field options
|
|
73
|
+
*/
|
|
74
|
+
export function createFieldOptions<T extends Record<string, string>>(
|
|
75
|
+
fields: Record<keyof T, { validateOnChange?: boolean; clearErrorOnChange?: boolean }>
|
|
76
|
+
) {
|
|
77
|
+
return fields;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Merge form errors
|
|
82
|
+
*/
|
|
83
|
+
export function mergeFormErrors<T extends Record<string, string>>(
|
|
84
|
+
...errorObjects: Array<Partial<Record<keyof T, string | null>>>
|
|
85
|
+
): Record<keyof T, string | null> {
|
|
86
|
+
const merged = {} as Record<keyof T, string | null>;
|
|
87
|
+
|
|
88
|
+
for (const errors of errorObjects) {
|
|
89
|
+
for (const key in errors) {
|
|
90
|
+
if (errors[key as keyof T] !== undefined) {
|
|
91
|
+
merged[key as keyof T] = errors[key as keyof T] as string | null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return merged;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Clear specific field errors
|
|
101
|
+
*/
|
|
102
|
+
export function clearFieldErrors<T extends Record<string, string>>(
|
|
103
|
+
errors: Record<keyof T, string | null>,
|
|
104
|
+
fields: (keyof T)[]
|
|
105
|
+
): Record<keyof T, string | null> {
|
|
106
|
+
const cleared = { ...errors };
|
|
107
|
+
|
|
108
|
+
for (const field of fields) {
|
|
109
|
+
cleared[field] = null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return cleared;
|
|
113
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Module Public API
|
|
3
|
+
* Centralized validation system
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Types
|
|
7
|
+
export type {
|
|
8
|
+
ValidationResult,
|
|
9
|
+
PasswordRequirements,
|
|
10
|
+
PasswordStrengthResult,
|
|
11
|
+
ValidationRule,
|
|
12
|
+
ValidatorConfig,
|
|
13
|
+
} from './types';
|
|
14
|
+
|
|
15
|
+
// Validators
|
|
16
|
+
export { EmailValidator, PasswordValidator, NameValidator } from './validators';
|
|
17
|
+
export type { PasswordConfig } from './validators';
|
|
18
|
+
|
|
19
|
+
// Sanitizers
|
|
20
|
+
export { EmailSanitizer, PasswordSanitizer, NameSanitizer } from './sanitizers';
|
|
21
|
+
|
|
22
|
+
// Rules
|
|
23
|
+
export { BaseValidationRule, RequiredRule, RegexRule, MinLengthRule } from './rules';
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Rule
|
|
3
|
+
* Base class and common validation rules
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ValidationResult, ValidatorConfig } from '../types';
|
|
7
|
+
|
|
8
|
+
export abstract class BaseValidationRule<T = any> {
|
|
9
|
+
abstract validate(value: T): ValidationResult;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Required field validation rule
|
|
14
|
+
*/
|
|
15
|
+
export class RequiredRule extends BaseValidationRule<string | null | undefined> {
|
|
16
|
+
constructor(private fieldName: string = 'Field') {
|
|
17
|
+
super();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
validate(value: string | null | undefined): ValidationResult {
|
|
21
|
+
if (!value || value.trim() === '') {
|
|
22
|
+
return { isValid: false, error: `auth.validation.${thisFieldNameToKey()}.required` };
|
|
23
|
+
}
|
|
24
|
+
return { isValid: true };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private thisFieldNameToKey(): string {
|
|
28
|
+
const keyMap: Record<string, string> = {
|
|
29
|
+
'Field': 'field',
|
|
30
|
+
'Email': 'email',
|
|
31
|
+
'Password': 'password',
|
|
32
|
+
'Name': 'name',
|
|
33
|
+
};
|
|
34
|
+
return keyMap[this.fieldName] || 'field';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Regex validation rule
|
|
40
|
+
*/
|
|
41
|
+
export class RegexRule extends BaseValidationRule<string> {
|
|
42
|
+
constructor(
|
|
43
|
+
private regex: RegExp,
|
|
44
|
+
private errorKey: string
|
|
45
|
+
) {
|
|
46
|
+
super();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
validate(value: string): ValidationResult {
|
|
50
|
+
if (!this.regex.test(value)) {
|
|
51
|
+
return { isValid: false, error: this.errorKey };
|
|
52
|
+
}
|
|
53
|
+
return { isValid: true };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Min length validation rule
|
|
59
|
+
*/
|
|
60
|
+
export class MinLengthRule extends BaseValidationRule<string> {
|
|
61
|
+
constructor(private minLength: number, private errorKey: string) {
|
|
62
|
+
super();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
validate(value: string): ValidationResult {
|
|
66
|
+
if (value.length < this.minLength) {
|
|
67
|
+
return { isValid: false, error: this.errorKey };
|
|
68
|
+
}
|
|
69
|
+
return { isValid: true };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email Sanitizer
|
|
3
|
+
* Handles email input sanitization
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class EmailSanitizer {
|
|
7
|
+
/**
|
|
8
|
+
* Sanitize email input
|
|
9
|
+
* Trims whitespace and converts to lowercase
|
|
10
|
+
*/
|
|
11
|
+
static sanitize(email: string | null | undefined): string {
|
|
12
|
+
if (!email) return '';
|
|
13
|
+
return email.trim().toLowerCase();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Check if email is empty
|
|
18
|
+
*/
|
|
19
|
+
static isEmpty(email: string | null | undefined): boolean {
|
|
20
|
+
return !email || email.trim() === '';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Password Sanitizer
|
|
3
|
+
* Handles password input sanitization
|
|
4
|
+
* IMPORTANT: Never trim passwords - whitespace may be intentional
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export class PasswordSanitizer {
|
|
8
|
+
/**
|
|
9
|
+
* Sanitize password input
|
|
10
|
+
* NOTE: Does NOT trim - whitespace may be intentional
|
|
11
|
+
*/
|
|
12
|
+
static sanitize(password: string | null | undefined): string {
|
|
13
|
+
if (!password) return '';
|
|
14
|
+
return password; // Don't trim passwords
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check if password is empty
|
|
19
|
+
* NOTE: Does NOT trim - checks actual length
|
|
20
|
+
*/
|
|
21
|
+
static isEmpty(password: string | null | undefined): boolean {
|
|
22
|
+
return !password || password.length === 0;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Types
|
|
3
|
+
* Core types for validation system
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface ValidationResult {
|
|
7
|
+
isValid: boolean;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface PasswordRequirements {
|
|
12
|
+
hasMinLength: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface PasswordStrengthResult extends ValidationResult {
|
|
16
|
+
requirements?: PasswordRequirements;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ValidationRule<T = any> {
|
|
20
|
+
validate(value: T): ValidationResult;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ValidatorConfig {
|
|
24
|
+
emailRegex?: RegExp;
|
|
25
|
+
displayNameMinLength?: number;
|
|
26
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email Validator
|
|
3
|
+
* Handles email validation with configurable rules
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ValidationResult, ValidatorConfig } from '../types';
|
|
7
|
+
import { RegexRule, RequiredRule } from '../rules/ValidationRule';
|
|
8
|
+
import { EmailSanitizer } from '../sanitizers/EmailSanitizer';
|
|
9
|
+
|
|
10
|
+
export class EmailValidator {
|
|
11
|
+
private config: ValidatorConfig;
|
|
12
|
+
private requiredRule: RequiredRule;
|
|
13
|
+
private regexRule: RegexRule;
|
|
14
|
+
|
|
15
|
+
constructor(config: ValidatorConfig = {}) {
|
|
16
|
+
this.config = {
|
|
17
|
+
emailRegex: config.emailRegex || this.getDefaultEmailRegex(),
|
|
18
|
+
displayNameMinLength: config.displayNameMinLength,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
this.requiredRule = new RequiredRule('Email');
|
|
22
|
+
this.regexRule = new RegexRule(
|
|
23
|
+
this.config.emailRegex,
|
|
24
|
+
'auth.validation.invalidEmail'
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Validate email
|
|
30
|
+
* @param email - Email to validate
|
|
31
|
+
* @returns Validation result
|
|
32
|
+
*/
|
|
33
|
+
validate(email: string): ValidationResult {
|
|
34
|
+
// Check required
|
|
35
|
+
const requiredResult = this.requiredRule.validate(email);
|
|
36
|
+
if (!requiredResult.isValid) {
|
|
37
|
+
return requiredResult;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Sanitize
|
|
41
|
+
const sanitized = EmailSanitizer.sanitize(email);
|
|
42
|
+
|
|
43
|
+
// Check format
|
|
44
|
+
return this.regexRule.validate(sanitized);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Check if email is empty
|
|
49
|
+
*/
|
|
50
|
+
isEmpty(email: string | null | undefined): boolean {
|
|
51
|
+
return EmailSanitizer.isEmpty(email);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private getDefaultEmailRegex(): RegExp {
|
|
55
|
+
// More robust email validation:
|
|
56
|
+
// - Local part: alphanumeric, dots (not consecutive), hyphens, underscores, plus
|
|
57
|
+
// - Domain: alphanumeric and hyphens
|
|
58
|
+
// - TLD: at least 2 characters
|
|
59
|
+
return /^[a-zA-Z0-9]([a-zA-Z0-9._+-]*[a-zA-Z0-9])?@[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$/;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Name Validator
|
|
3
|
+
* Handles display name validation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ValidationResult, ValidatorConfig } from '../types';
|
|
7
|
+
import { MinLengthRule, RequiredRule } from '../rules/ValidationRule';
|
|
8
|
+
import { NameSanitizer } from '../sanitizers/NameSanitizer';
|
|
9
|
+
|
|
10
|
+
export class NameValidator {
|
|
11
|
+
private config: ValidatorConfig;
|
|
12
|
+
private requiredRule: RequiredRule;
|
|
13
|
+
private minLengthRule: MinLengthRule;
|
|
14
|
+
|
|
15
|
+
constructor(config: ValidatorConfig = {}) {
|
|
16
|
+
this.config = {
|
|
17
|
+
displayNameMinLength: config.displayNameMinLength || 2,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
this.requiredRule = new RequiredRule('Name');
|
|
21
|
+
this.minLengthRule = new MinLengthRule(
|
|
22
|
+
this.config.displayNameMinLength,
|
|
23
|
+
'auth.validation.nameTooShort'
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Validate display name
|
|
29
|
+
* @param name - Name to validate
|
|
30
|
+
* @returns Validation result
|
|
31
|
+
*/
|
|
32
|
+
validate(name: string): ValidationResult {
|
|
33
|
+
// Check required
|
|
34
|
+
const requiredResult = this.requiredRule.validate(name);
|
|
35
|
+
if (!requiredResult.isValid) {
|
|
36
|
+
return { isValid: false, error: 'auth.validation.nameRequired' };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Sanitize
|
|
40
|
+
const sanitized = NameSanitizer.sanitize(name);
|
|
41
|
+
|
|
42
|
+
// Check min length
|
|
43
|
+
return this.minLengthRule.validate(sanitized);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Check if name is empty
|
|
48
|
+
*/
|
|
49
|
+
isEmpty(name: string | null | undefined): boolean {
|
|
50
|
+
return NameSanitizer.isEmpty(name);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Password Validator
|
|
3
|
+
* Handles password validation with strength requirements
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { PasswordStrengthResult, ValidationResult, ValidatorConfig } from '../types';
|
|
7
|
+
import { MinLengthRule, RequiredRule } from '../rules/ValidationRule';
|
|
8
|
+
import { PasswordSanitizer } from '../sanitizers/PasswordSanitizer';
|
|
9
|
+
|
|
10
|
+
export interface PasswordConfig {
|
|
11
|
+
minLength: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class PasswordValidator {
|
|
15
|
+
private config: PasswordConfig;
|
|
16
|
+
private requiredRule: RequiredRule;
|
|
17
|
+
|
|
18
|
+
constructor(config: PasswordConfig) {
|
|
19
|
+
this.config = config;
|
|
20
|
+
this.requiredRule = new RequiredRule('Password');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Validate password for login (just check required)
|
|
25
|
+
* @param password - Password to validate
|
|
26
|
+
* @returns Validation result
|
|
27
|
+
*/
|
|
28
|
+
validateForLogin(password: string): ValidationResult {
|
|
29
|
+
// Don't trim passwords - whitespace may be intentional
|
|
30
|
+
if (PasswordSanitizer.isEmpty(password)) {
|
|
31
|
+
return { isValid: false, error: 'auth.validation.passwordRequired' };
|
|
32
|
+
}
|
|
33
|
+
return { isValid: true };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Validate password for registration (check requirements)
|
|
38
|
+
* @param password - Password to validate
|
|
39
|
+
* @returns Password strength result
|
|
40
|
+
*/
|
|
41
|
+
validateForRegister(password: string): PasswordStrengthResult {
|
|
42
|
+
// Don't trim passwords - whitespace may be intentional
|
|
43
|
+
if (PasswordSanitizer.isEmpty(password)) {
|
|
44
|
+
return {
|
|
45
|
+
isValid: false,
|
|
46
|
+
error: 'auth.validation.passwordRequired',
|
|
47
|
+
requirements: { hasMinLength: false },
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const hasMinLength = password.length >= this.config.minLength;
|
|
52
|
+
|
|
53
|
+
if (!hasMinLength) {
|
|
54
|
+
return {
|
|
55
|
+
isValid: false,
|
|
56
|
+
error: 'auth.validation.passwordTooShort',
|
|
57
|
+
requirements: { hasMinLength: false },
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
isValid: true,
|
|
63
|
+
requirements: { hasMinLength: true },
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Validate password confirmation matches
|
|
69
|
+
* @param password - Original password
|
|
70
|
+
* @param confirm - Confirmation password
|
|
71
|
+
* @returns Validation result
|
|
72
|
+
*/
|
|
73
|
+
validateConfirmation(password: string, confirm: string): ValidationResult {
|
|
74
|
+
// Don't trim passwords - whitespace may be intentional
|
|
75
|
+
if (PasswordSanitizer.isEmpty(confirm)) {
|
|
76
|
+
return { isValid: false, error: 'auth.validation.confirmPasswordRequired' };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (password !== confirm) {
|
|
80
|
+
return { isValid: false, error: 'auth.validation.passwordsDoNotMatch' };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { isValid: true };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Check if password is empty
|
|
88
|
+
*/
|
|
89
|
+
isEmpty(password: string | null | undefined): boolean {
|
|
90
|
+
return PasswordSanitizer.isEmpty(password);
|
|
91
|
+
}
|
|
92
|
+
}
|