@umituz/react-native-firebase 1.13.149 → 1.13.150
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/auth/infrastructure/services/apple-auth.service.ts +10 -2
- package/src/auth/infrastructure/services/google-auth.service.ts +10 -2
- package/src/auth/infrastructure/services/reauthentication.service.ts +5 -1
- package/src/auth/presentation/hooks/shared/auth-hooks.util.ts +60 -0
- package/src/auth/presentation/hooks/shared/hook-utils.util.ts +14 -216
- package/src/auth/presentation/hooks/shared/safe-state-hooks.util.ts +76 -0
- package/src/auth/presentation/hooks/shared/state-hooks.util.ts +97 -0
- package/src/domain/utils/async-executor.util.ts +16 -169
- package/src/domain/utils/error-handler.util.ts +18 -170
- package/src/domain/utils/error-handlers/error-checkers.ts +120 -0
- package/src/domain/utils/error-handlers/error-converters.ts +48 -0
- package/src/domain/utils/error-handlers/error-messages.ts +18 -0
- package/src/domain/utils/executors/advanced-executors.util.ts +59 -0
- package/src/domain/utils/executors/basic-executors.util.ts +56 -0
- package/src/domain/utils/executors/batch-executors.util.ts +42 -0
- package/src/domain/utils/executors/error-converters.util.ts +45 -0
- package/src/domain/utils/result/result-creators.ts +49 -0
- package/src/domain/utils/result/result-helpers.ts +60 -0
- package/src/domain/utils/result/result-types.ts +40 -0
- package/src/domain/utils/result.util.ts +28 -127
- package/src/domain/utils/validation.util.ts +38 -209
- package/src/domain/utils/validators/composite.validator.ts +24 -0
- package/src/domain/utils/validators/firebase.validator.ts +45 -0
- package/src/domain/utils/validators/generic.validator.ts +59 -0
- package/src/domain/utils/validators/string.validator.ts +25 -0
- package/src/domain/utils/validators/url.validator.ts +36 -0
- package/src/domain/utils/validators/user-input.validator.ts +54 -0
- package/src/firestore/infrastructure/middleware/QuotaTrackingMiddleware.ts +10 -3
- package/src/firestore/utils/deduplication/timer-manager.util.ts +2 -2
- package/src/firestore/utils/pagination.helper.ts +3 -1
- package/src/infrastructure/config/FirebaseClient.ts +28 -189
- package/src/infrastructure/config/clients/FirebaseClientSingleton.ts +82 -0
- package/src/infrastructure/config/services/FirebaseInitializationService.ts +115 -0
- package/src/init/createFirebaseInitModule.ts +9 -3
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batch Async Executors
|
|
3
|
+
* Execute multiple operations in parallel or sequence
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Result, FailureResult } from '../result.util';
|
|
7
|
+
import { failureResultFromError, successResult } from '../result.util';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Execute multiple operations in parallel
|
|
11
|
+
* Returns success only if all operations succeed
|
|
12
|
+
*/
|
|
13
|
+
export async function executeAll<T>(
|
|
14
|
+
...operations: (() => Promise<Result<T>>)[]
|
|
15
|
+
): Promise<Result<T[]>> {
|
|
16
|
+
try {
|
|
17
|
+
const results = await Promise.all(operations.map((op) => op()));
|
|
18
|
+
const failures = results.filter((r) => !r.success);
|
|
19
|
+
if (failures.length > 0) {
|
|
20
|
+
return failures[0] as FailureResult;
|
|
21
|
+
}
|
|
22
|
+
const data = results.map((r) => (r as { success: true; data: T }).data);
|
|
23
|
+
return successResult(data);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
return failureResultFromError(error);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Execute operations in sequence, stopping at first failure
|
|
31
|
+
*/
|
|
32
|
+
export async function executeSequence<T>(
|
|
33
|
+
...operations: (() => Promise<Result<T>>)[]
|
|
34
|
+
): Promise<Result<void>> {
|
|
35
|
+
for (const operation of operations) {
|
|
36
|
+
const result = await operation();
|
|
37
|
+
if (!result.success) {
|
|
38
|
+
return result as Result<void>;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return successResult();
|
|
42
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Converters
|
|
3
|
+
* Convert unknown errors to structured ErrorInfo
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Error converter function type
|
|
8
|
+
* Converts unknown errors to ErrorInfo
|
|
9
|
+
*/
|
|
10
|
+
export type ErrorConverter = (error: unknown) => { code: string; message: string };
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Default error converter for auth operations
|
|
14
|
+
*/
|
|
15
|
+
export function authErrorConverter(error: unknown): { code: string; message: string } {
|
|
16
|
+
if (error instanceof Error) {
|
|
17
|
+
return {
|
|
18
|
+
code: (error as { code?: string }).code ?? 'auth/failed',
|
|
19
|
+
message: error.message,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
code: 'auth/failed',
|
|
24
|
+
message: typeof error === 'string' ? error : 'Authentication failed',
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Default error converter for operations
|
|
30
|
+
*/
|
|
31
|
+
export function defaultErrorConverter(
|
|
32
|
+
error: unknown,
|
|
33
|
+
defaultCode = 'operation/failed'
|
|
34
|
+
): { code: string; message: string } {
|
|
35
|
+
if (error instanceof Error) {
|
|
36
|
+
return {
|
|
37
|
+
code: (error as { code?: string }).code ?? defaultCode,
|
|
38
|
+
message: error.message,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
code: defaultCode,
|
|
43
|
+
message: typeof error === 'string' ? error : 'Operation failed',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result Creators
|
|
3
|
+
* Factory functions for creating Result instances
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { SuccessResult, FailureResult, ErrorInfo } from './result-types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Create a success result with optional data
|
|
10
|
+
*/
|
|
11
|
+
export function successResult<T = void>(data?: T): SuccessResult<T> {
|
|
12
|
+
return { success: true, data: data as T };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Create a failure result with error information
|
|
17
|
+
*/
|
|
18
|
+
export function failureResult(error: ErrorInfo): FailureResult {
|
|
19
|
+
return { success: false, error };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create a failure result from error code and message
|
|
24
|
+
*/
|
|
25
|
+
export function failureResultFrom(code: string, message: string): FailureResult {
|
|
26
|
+
return { success: false, error: { code, message } };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Create a failure result from an unknown error
|
|
31
|
+
*/
|
|
32
|
+
export function failureResultFromError(error: unknown, defaultCode = 'operation/failed'): FailureResult {
|
|
33
|
+
if (error instanceof Error) {
|
|
34
|
+
return {
|
|
35
|
+
success: false,
|
|
36
|
+
error: {
|
|
37
|
+
code: (error as { code?: string }).code ?? defaultCode,
|
|
38
|
+
message: error.message,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
success: false,
|
|
44
|
+
error: {
|
|
45
|
+
code: defaultCode,
|
|
46
|
+
message: typeof error === 'string' ? error : 'Unknown error occurred',
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result Helpers
|
|
3
|
+
* Utility functions for working with Result type
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Result, SuccessResult, FailureResult } from './result-types';
|
|
7
|
+
import { successResult } from './result-creators';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Check if result is successful
|
|
11
|
+
*/
|
|
12
|
+
export function isSuccess<T>(result: Result<T>): result is SuccessResult<T> {
|
|
13
|
+
return result.success === true && result.error === undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Check if result is a failure
|
|
18
|
+
*/
|
|
19
|
+
export function isFailure<T>(result: Result<T>): result is FailureResult {
|
|
20
|
+
return result.success === false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get data from result or return default
|
|
25
|
+
*/
|
|
26
|
+
export function getDataOrDefault<T>(result: Result<T>, defaultValue: T): T {
|
|
27
|
+
return isSuccess(result) ? (result.data ?? defaultValue) : defaultValue;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Map success result data to another type
|
|
32
|
+
*/
|
|
33
|
+
export function mapResult<T, U>(
|
|
34
|
+
result: Result<T>,
|
|
35
|
+
mapper: (data: T) => U
|
|
36
|
+
): Result<U> {
|
|
37
|
+
if (isSuccess(result) && result.data !== undefined) {
|
|
38
|
+
return successResult(mapper(result.data));
|
|
39
|
+
}
|
|
40
|
+
// Return a new failure result to avoid type conflicts
|
|
41
|
+
if (isFailure(result)) {
|
|
42
|
+
return { success: false, error: result.error };
|
|
43
|
+
}
|
|
44
|
+
return successResult();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Chain multiple results, stopping at first failure
|
|
49
|
+
*/
|
|
50
|
+
export async function chainResults<T>(
|
|
51
|
+
...operations: (() => Promise<Result<T>>)[]
|
|
52
|
+
): Promise<Result<T>> {
|
|
53
|
+
for (const operation of operations) {
|
|
54
|
+
const result = await operation();
|
|
55
|
+
if (isFailure(result)) {
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return successResult();
|
|
60
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result Types
|
|
3
|
+
* Type definitions for Result pattern
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Standard error information structure
|
|
8
|
+
*/
|
|
9
|
+
export interface ErrorInfo {
|
|
10
|
+
code: string;
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Standard result type for operations
|
|
16
|
+
* Success contains data, failure contains error info
|
|
17
|
+
*/
|
|
18
|
+
export interface Result<T = void> {
|
|
19
|
+
readonly success: boolean;
|
|
20
|
+
readonly data?: T;
|
|
21
|
+
readonly error?: ErrorInfo;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Success result type guard
|
|
26
|
+
*/
|
|
27
|
+
export type SuccessResult<T = void> = Result<T> & {
|
|
28
|
+
readonly success: true;
|
|
29
|
+
readonly data: T;
|
|
30
|
+
readonly error?: never;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Failure result type guard
|
|
35
|
+
*/
|
|
36
|
+
export type FailureResult = Result & {
|
|
37
|
+
readonly success: false;
|
|
38
|
+
readonly data?: never;
|
|
39
|
+
readonly error: ErrorInfo;
|
|
40
|
+
};
|
|
@@ -1,129 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Result Utility
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
* Failure result type guard
|
|
32
|
-
*/
|
|
33
|
-
export type FailureResult = Result & { readonly success: false; readonly data?: never; readonly error: ErrorInfo };
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Create a success result with optional data
|
|
37
|
-
*/
|
|
38
|
-
export function successResult<T = void>(data?: T): SuccessResult<T> {
|
|
39
|
-
return { success: true, data: data as T };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Create a failure result with error information
|
|
44
|
-
*/
|
|
45
|
-
export function failureResult(error: ErrorInfo): FailureResult {
|
|
46
|
-
return { success: false, error };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Create a failure result from error code and message
|
|
51
|
-
*/
|
|
52
|
-
export function failureResultFrom(code: string, message: string): FailureResult {
|
|
53
|
-
return { success: false, error: { code, message } };
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Create a failure result from an unknown error
|
|
58
|
-
*/
|
|
59
|
-
export function failureResultFromError(error: unknown, defaultCode = 'operation/failed'): FailureResult {
|
|
60
|
-
if (error instanceof Error) {
|
|
61
|
-
return {
|
|
62
|
-
success: false,
|
|
63
|
-
error: {
|
|
64
|
-
code: (error as { code?: string }).code ?? defaultCode,
|
|
65
|
-
message: error.message,
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
return {
|
|
70
|
-
success: false,
|
|
71
|
-
error: {
|
|
72
|
-
code: defaultCode,
|
|
73
|
-
message: typeof error === 'string' ? error : 'Unknown error occurred',
|
|
74
|
-
},
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Check if result is successful
|
|
80
|
-
*/
|
|
81
|
-
export function isSuccess<T>(result: Result<T>): result is SuccessResult<T> {
|
|
82
|
-
return result.success === true && result.error === undefined;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Check if result is a failure
|
|
87
|
-
*/
|
|
88
|
-
export function isFailure<T>(result: Result<T>): result is FailureResult {
|
|
89
|
-
return result.success === false;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Get data from result or return default
|
|
94
|
-
*/
|
|
95
|
-
export function getDataOrDefault<T>(result: Result<T>, defaultValue: T): T {
|
|
96
|
-
return isSuccess(result) ? (result.data ?? defaultValue) : defaultValue;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Map success result data to another type
|
|
101
|
-
*/
|
|
102
|
-
export function mapResult<T, U>(
|
|
103
|
-
result: Result<T>,
|
|
104
|
-
mapper: (data: T) => U
|
|
105
|
-
): Result<U> {
|
|
106
|
-
if (isSuccess(result) && result.data !== undefined) {
|
|
107
|
-
return successResult(mapper(result.data));
|
|
108
|
-
}
|
|
109
|
-
// Return a new failure result to avoid type conflicts
|
|
110
|
-
if (isFailure(result)) {
|
|
111
|
-
return { success: false, error: result.error };
|
|
112
|
-
}
|
|
113
|
-
return successResult();
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Chain multiple results, stopping at first failure
|
|
118
|
-
*/
|
|
119
|
-
export async function chainResults<T>(
|
|
120
|
-
...operations: (() => Promise<Result<T>>)[]
|
|
121
|
-
): Promise<Result<T>> {
|
|
122
|
-
for (const operation of operations) {
|
|
123
|
-
const result = await operation();
|
|
124
|
-
if (isFailure(result)) {
|
|
125
|
-
return result;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return successResult();
|
|
129
|
-
}
|
|
3
|
+
* Re-exports all result utilities for backward compatibility
|
|
4
|
+
* @deprecated Import from specific result files instead
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Types
|
|
8
|
+
export type {
|
|
9
|
+
ErrorInfo,
|
|
10
|
+
Result,
|
|
11
|
+
SuccessResult,
|
|
12
|
+
FailureResult,
|
|
13
|
+
} from './result/result-types';
|
|
14
|
+
|
|
15
|
+
// Creators
|
|
16
|
+
export {
|
|
17
|
+
successResult,
|
|
18
|
+
failureResult,
|
|
19
|
+
failureResultFrom,
|
|
20
|
+
failureResultFromError,
|
|
21
|
+
} from './result/result-creators';
|
|
22
|
+
|
|
23
|
+
// Helpers
|
|
24
|
+
export {
|
|
25
|
+
isSuccess,
|
|
26
|
+
isFailure,
|
|
27
|
+
getDataOrDefault,
|
|
28
|
+
mapResult,
|
|
29
|
+
chainResults,
|
|
30
|
+
} from './result/result-helpers';
|
|
@@ -1,211 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Validation Utility
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return (
|
|
42
|
-
trimmed.includes('.firebaseapp.com') ||
|
|
43
|
-
trimmed.includes('.web.app')
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Validate Firebase projectId format
|
|
49
|
-
* Project IDs must be 6-30 characters, lowercase, alphanumeric, and may contain hyphens
|
|
50
|
-
*/
|
|
51
|
-
export function isValidFirebaseProjectId(projectId: string): boolean {
|
|
52
|
-
if (!isValidString(projectId)) {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
const pattern = /^[a-z0-9][a-z0-9-]{4,28}[a-z0-9]$/;
|
|
56
|
-
return pattern.test(projectId.trim());
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Validate URL format
|
|
61
|
-
*/
|
|
62
|
-
export function isValidUrl(url: string): boolean {
|
|
63
|
-
if (!isValidString(url)) {
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
try {
|
|
67
|
-
new URL(url.trim());
|
|
68
|
-
return true;
|
|
69
|
-
} catch {
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Validate HTTPS URL
|
|
76
|
-
*/
|
|
77
|
-
export function isValidHttpsUrl(url: string): boolean {
|
|
78
|
-
if (!isValidString(url)) {
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
81
|
-
try {
|
|
82
|
-
const urlObj = new URL(url.trim());
|
|
83
|
-
return urlObj.protocol === 'https:';
|
|
84
|
-
} catch {
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Validate email format
|
|
91
|
-
*/
|
|
92
|
-
export function isValidEmail(email: string): boolean {
|
|
93
|
-
if (!isValidString(email)) {
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
97
|
-
return emailPattern.test(email.trim());
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Check if value is defined (not null or undefined)
|
|
102
|
-
*/
|
|
103
|
-
export function isDefined<T>(value: T | null | undefined): value is T {
|
|
104
|
-
return value !== null && value !== undefined;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Check if array is not empty
|
|
109
|
-
*/
|
|
110
|
-
export function isNonEmptyArray<T>(value: unknown): value is [T, ...T[]] {
|
|
111
|
-
return Array.isArray(value) && value.length > 0;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Check if number is in range
|
|
116
|
-
*/
|
|
117
|
-
export function isInRange(value: number, min: number, max: number): boolean {
|
|
118
|
-
return typeof value === 'number' && value >= min && value <= max;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Check if number is positive
|
|
123
|
-
*/
|
|
124
|
-
export function isPositive(value: number): boolean {
|
|
125
|
-
return typeof value === 'number' && value > 0;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Check if number is non-negative
|
|
130
|
-
*/
|
|
131
|
-
export function isNonNegative(value: number): boolean {
|
|
132
|
-
return typeof value === 'number' && value >= 0;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Validate password strength
|
|
137
|
-
* At least 8 characters, containing uppercase, lowercase, and number
|
|
138
|
-
*/
|
|
139
|
-
export function isStrongPassword(password: string): boolean {
|
|
140
|
-
if (!isValidString(password) || password.length < 8) {
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
const hasUpperCase = /[A-Z]/.test(password);
|
|
144
|
-
const hasLowerCase = /[a-z]/.test(password);
|
|
145
|
-
const hasNumber = /[0-9]/.test(password);
|
|
146
|
-
return hasUpperCase && hasLowerCase && hasNumber;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Validate username format
|
|
151
|
-
* Alphanumeric, underscores, and hyphens, 3-20 characters
|
|
152
|
-
*/
|
|
153
|
-
export function isValidUsername(username: string): boolean {
|
|
154
|
-
if (!isValidString(username)) {
|
|
155
|
-
return false;
|
|
156
|
-
}
|
|
157
|
-
const pattern = /^[a-zA-Z0-9_-]{3,20}$/;
|
|
158
|
-
return pattern.test(username);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Validate phone number format (basic check)
|
|
163
|
-
*/
|
|
164
|
-
export function isValidPhoneNumber(phone: string): boolean {
|
|
165
|
-
if (!isValidString(phone)) {
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
const cleaned = phone.replace(/\s+/g, '').replace(/[-+()]/g, '');
|
|
169
|
-
return /^[0-9]{10,15}$/.test(cleaned);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Validate object has required properties
|
|
174
|
-
*/
|
|
175
|
-
export function hasRequiredProperties<T extends Record<string, unknown>>(
|
|
176
|
-
obj: unknown,
|
|
177
|
-
requiredProps: (keyof T)[]
|
|
178
|
-
): obj is T {
|
|
179
|
-
if (typeof obj !== 'object' || obj === null) {
|
|
180
|
-
return false;
|
|
181
|
-
}
|
|
182
|
-
return requiredProps.every((prop) => prop in obj);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Validate all items in array match predicate
|
|
187
|
-
*/
|
|
188
|
-
export function allMatch<T>(items: unknown[], predicate: (item: unknown) => item is T): boolean {
|
|
189
|
-
return Array.isArray(items) && items.every(predicate);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Validate at least one item in array matches predicate
|
|
194
|
-
*/
|
|
195
|
-
export function anyMatch<T>(items: unknown[], predicate: (item: unknown) => item is T): boolean {
|
|
196
|
-
return Array.isArray(items) && items.some(predicate);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Create a validator that combines multiple validators
|
|
201
|
-
*/
|
|
202
|
-
export function combineValidators(...validators: ((value: string) => boolean)[]): (value: string) => boolean {
|
|
203
|
-
return (value: string) => validators.every((validator) => validator(value));
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Create a validator that checks if value matches one of validators
|
|
208
|
-
*/
|
|
209
|
-
export function anyValidator(...validators: ((value: string) => boolean)[]): (value: string) => boolean {
|
|
210
|
-
return (value: string) => validators.some((validator) => validator(value));
|
|
211
|
-
}
|
|
3
|
+
* Re-exports all validators for backward compatibility
|
|
4
|
+
* @deprecated Import from specific validator files instead
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// String Validators
|
|
8
|
+
export { isValidString, isEmptyString, isDefined } from './validators/string.validator';
|
|
9
|
+
|
|
10
|
+
// Firebase Validators
|
|
11
|
+
export {
|
|
12
|
+
isValidFirebaseApiKey,
|
|
13
|
+
isValidFirebaseAuthDomain,
|
|
14
|
+
isValidFirebaseProjectId,
|
|
15
|
+
} from './validators/firebase.validator';
|
|
16
|
+
|
|
17
|
+
// URL Validators
|
|
18
|
+
export { isValidUrl, isValidHttpsUrl } from './validators/url.validator';
|
|
19
|
+
|
|
20
|
+
// User Input Validators
|
|
21
|
+
export {
|
|
22
|
+
isValidEmail,
|
|
23
|
+
isStrongPassword,
|
|
24
|
+
isValidUsername,
|
|
25
|
+
isValidPhoneNumber,
|
|
26
|
+
} from './validators/user-input.validator';
|
|
27
|
+
|
|
28
|
+
// Generic Validators
|
|
29
|
+
export {
|
|
30
|
+
isNonEmptyArray,
|
|
31
|
+
isInRange,
|
|
32
|
+
isPositive,
|
|
33
|
+
isNonNegative,
|
|
34
|
+
hasRequiredProperties,
|
|
35
|
+
allMatch,
|
|
36
|
+
anyMatch,
|
|
37
|
+
} from './validators/generic.validator';
|
|
38
|
+
|
|
39
|
+
// Composite Validators
|
|
40
|
+
export { combineValidators, anyValidator } from './validators/composite.validator';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composite Validators
|
|
3
|
+
* Higher-order validators for combining multiple validation rules
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Create a validator that combines multiple validators (AND logic)
|
|
8
|
+
* All validators must pass
|
|
9
|
+
*/
|
|
10
|
+
export function combineValidators(
|
|
11
|
+
...validators: ((value: string) => boolean)[]
|
|
12
|
+
): (value: string) => boolean {
|
|
13
|
+
return (value: string) => validators.every((validator) => validator(value));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create a validator that checks if value matches one of validators (OR logic)
|
|
18
|
+
* At least one validator must pass
|
|
19
|
+
*/
|
|
20
|
+
export function anyValidator(
|
|
21
|
+
...validators: ((value: string) => boolean)[]
|
|
22
|
+
): (value: string) => boolean {
|
|
23
|
+
return (value: string) => validators.some((validator) => validator(value));
|
|
24
|
+
}
|