@umituz/react-native-firebase 2.4.30 → 2.4.33
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/domains/account-deletion/application/ports/reauthentication.types.ts +0 -14
- package/src/domains/account-deletion/infrastructure/services/account-deletion.service.ts +0 -2
- package/src/domains/account-deletion/infrastructure/services/reauthentication.service.ts +0 -6
- package/src/domains/auth/domain/entities/AnonymousUser.ts +0 -13
- package/src/domains/auth/domain/utils/user-validation.util.ts +0 -19
- package/src/domains/auth/infrastructure/config/FirebaseAuthClient.ts +0 -1
- package/src/domains/auth/infrastructure/services/apple-auth.types.ts +0 -19
- package/src/domains/auth/infrastructure/services/base/base-auth.service.ts +0 -49
- package/src/domains/auth/infrastructure/services/google-auth.types.ts +0 -12
- package/src/domains/auth/infrastructure/services/user-document.service.ts +0 -6
- package/src/domains/auth/infrastructure/services/utils/auth-result-converter.util.ts +3 -3
- package/src/domains/auth/infrastructure/utils/auth-guard.util.ts +2 -48
- package/src/domains/auth/presentation/hooks/useGoogleOAuth.ts +1 -1
- package/src/domains/auth/presentation/hooks/utils/auth-state-change.handler.ts +2 -2
- package/src/domains/firestore/infrastructure/middleware/QueryDeduplicationMiddleware.ts +0 -1
- package/src/domains/firestore/infrastructure/repositories/BaseRepository.ts +1 -1
- package/src/domains/firestore/utils/deduplication/pending-query-manager.util.ts +1 -1
- package/src/domains/firestore/utils/deduplication/query-key-generator.util.ts +0 -16
- package/src/domains/firestore/utils/deduplication/timer-manager.util.ts +1 -1
- package/src/domains/firestore/utils/firestore-helper.ts +0 -3
- package/src/domains/firestore/utils/query/filters.util.ts +0 -10
- package/src/domains/firestore/utils/query-builder.ts +3 -3
- package/src/domains/firestore/utils/result/result.util.ts +2 -5
- package/src/init/index.ts +0 -2
- package/src/shared/domain/guards/firebase-error.guard.ts +3 -3
- package/src/shared/domain/utils/async-executor.util.ts +1 -10
- package/src/shared/domain/utils/error-handlers/error-checkers.ts +1 -37
- package/src/shared/domain/utils/error-handlers/error-messages.ts +0 -3
- package/src/shared/domain/utils/executors/basic-executors.util.ts +1 -22
- package/src/shared/domain/utils/id-generator.util.ts +0 -37
- package/src/shared/domain/utils/index.ts +0 -90
- package/src/shared/domain/utils/result/result-creators.ts +1 -1
- package/src/shared/domain/utils/result/result-helpers.ts +1 -48
- package/src/shared/domain/utils/service-config.util.ts +2 -21
- package/src/shared/domain/utils/type-guards.util.ts +1 -55
- package/src/shared/domain/utils/validators/firebase.validator.ts +0 -11
- package/src/shared/domain/utils/validators/string.validator.ts +0 -14
- package/src/shared/infrastructure/config/base/ClientStateManager.ts +1 -1
- package/src/shared/infrastructure/config/base/ServiceClientSingleton.ts +2 -2
- package/src/shared/infrastructure/config/initializers/FirebaseAppInitializer.ts +2 -92
- package/src/domains/auth/domain/errors/FirebaseAuthError.ts +0 -18
- package/src/domains/auth/infrastructure/services/auth-guard.service.ts +0 -101
- package/src/domains/auth/presentation/hooks/shared/auth-hooks.util.ts +0 -60
- package/src/domains/auth/presentation/hooks/shared/safe-state-hooks.util.ts +0 -76
- package/src/domains/auth/presentation/hooks/shared/state-hooks.util.ts +0 -97
- package/src/shared/domain/utils/credential.util.ts +0 -102
- package/src/shared/domain/utils/executors/advanced-executors.util.ts +0 -59
- package/src/shared/domain/utils/executors/batch-executors.util.ts +0 -42
- package/src/shared/domain/utils/validators/composite.validator.ts +0 -24
- package/src/shared/domain/utils/validators/generic.validator.ts +0 -59
- package/src/shared/domain/utils/validators/url.validator.ts +0 -36
- package/src/shared/domain/utils/validators/user-input.validator.ts +0 -54
- package/src/shared/domain/utils/validators/validation.util.ts +0 -63
- package/src/shared/infrastructure/config/validators/FirebaseConfigValidator.ts +0 -89
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Safe State Hooks
|
|
3
|
-
* React hooks for safe state management and mounted state tracking
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Hook for tracking mounted state
|
|
10
|
-
*/
|
|
11
|
-
export function useIsMounted(): () => boolean {
|
|
12
|
-
const isMountedRef = useRef<boolean>(true);
|
|
13
|
-
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
isMountedRef.current = true;
|
|
16
|
-
return () => {
|
|
17
|
-
isMountedRef.current = false;
|
|
18
|
-
};
|
|
19
|
-
}, []);
|
|
20
|
-
|
|
21
|
-
return useCallback(() => isMountedRef.current, []);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Hook for safe state updates (only if mounted)
|
|
26
|
-
*/
|
|
27
|
-
export function useSafeState<T>(
|
|
28
|
-
initialValue: T
|
|
29
|
-
): [T, (value: T | ((prev: T) => T)) => void] {
|
|
30
|
-
const isMounted = useIsMounted();
|
|
31
|
-
const [state, setState] = useState<T>(initialValue);
|
|
32
|
-
|
|
33
|
-
const setSafeState = useCallback(
|
|
34
|
-
(value: T | ((prev: T) => T)) => {
|
|
35
|
-
if (isMounted()) {
|
|
36
|
-
setState(value);
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
[isMounted]
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
return [state, setSafeState];
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Hook for debounced value updates
|
|
47
|
-
*/
|
|
48
|
-
export function useDebouncedValue<T>(value: T, delayMs: number): T {
|
|
49
|
-
const [debouncedValue, setDebouncedValue] = useState<T>(value);
|
|
50
|
-
|
|
51
|
-
useEffect(() => {
|
|
52
|
-
const handler = setTimeout(() => {
|
|
53
|
-
setDebouncedValue(value);
|
|
54
|
-
}, delayMs);
|
|
55
|
-
|
|
56
|
-
return () => {
|
|
57
|
-
clearTimeout(handler);
|
|
58
|
-
};
|
|
59
|
-
}, [value, delayMs]);
|
|
60
|
-
|
|
61
|
-
return debouncedValue;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Hook for managing cleanup callbacks
|
|
66
|
-
*/
|
|
67
|
-
export function useCleanup(
|
|
68
|
-
cleanupFn: () => void,
|
|
69
|
-
deps: React.DependencyList = []
|
|
70
|
-
): void {
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
return () => {
|
|
73
|
-
cleanupFn();
|
|
74
|
-
};
|
|
75
|
-
}, deps);
|
|
76
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* State Management Hooks
|
|
3
|
-
* React hooks for managing component state with loading and error handling
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { useState, useCallback, useRef } from 'react';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Hook state management result
|
|
10
|
-
*/
|
|
11
|
-
export interface HookState<T> {
|
|
12
|
-
value: T;
|
|
13
|
-
loading: boolean;
|
|
14
|
-
error: Error | null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Hook state actions
|
|
19
|
-
*/
|
|
20
|
-
export interface HookStateActions<T> {
|
|
21
|
-
setValue: (value: T) => void;
|
|
22
|
-
setLoading: (loading: boolean) => void;
|
|
23
|
-
setError: (error: Error | null) => void;
|
|
24
|
-
clearError: () => void;
|
|
25
|
-
reset: () => void;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Create a stateful hook with loading and error handling
|
|
30
|
-
*/
|
|
31
|
-
export function useHookState<T>(
|
|
32
|
-
initialValue: T
|
|
33
|
-
): [HookState<T>, HookStateActions<T>] {
|
|
34
|
-
const [value, setValue] = useState<T>(initialValue);
|
|
35
|
-
const [loading, setLoading] = useState<boolean>(false);
|
|
36
|
-
const [error, setError] = useState<Error | null>(null);
|
|
37
|
-
|
|
38
|
-
const clearError = useCallback(() => {
|
|
39
|
-
setError(null);
|
|
40
|
-
}, []);
|
|
41
|
-
|
|
42
|
-
const reset = useCallback(() => {
|
|
43
|
-
setValue(initialValue);
|
|
44
|
-
setLoading(false);
|
|
45
|
-
setError(null);
|
|
46
|
-
}, [initialValue]);
|
|
47
|
-
|
|
48
|
-
const state: HookState<T> = { value, loading, error };
|
|
49
|
-
const actions: HookStateActions<T> = {
|
|
50
|
-
setValue,
|
|
51
|
-
setLoading,
|
|
52
|
-
setError,
|
|
53
|
-
clearError,
|
|
54
|
-
reset,
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
return [state, actions];
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Hook for managing async operation state
|
|
62
|
-
*/
|
|
63
|
-
export function useAsyncOperation<T = void>() {
|
|
64
|
-
const [loading, setLoading] = useState<boolean>(false);
|
|
65
|
-
const [error, setError] = useState<Error | null>(null);
|
|
66
|
-
const operationRef = useRef<Promise<T> | null>(null);
|
|
67
|
-
|
|
68
|
-
const execute = useCallback(async (operation: () => Promise<T>): Promise<T> => {
|
|
69
|
-
setLoading(true);
|
|
70
|
-
setError(null);
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
const promise = operation();
|
|
74
|
-
operationRef.current = promise;
|
|
75
|
-
const result = await promise;
|
|
76
|
-
return result;
|
|
77
|
-
} catch (err) {
|
|
78
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
79
|
-
setError(error);
|
|
80
|
-
throw error;
|
|
81
|
-
} finally {
|
|
82
|
-
setLoading(false);
|
|
83
|
-
operationRef.current = null;
|
|
84
|
-
}
|
|
85
|
-
}, []);
|
|
86
|
-
|
|
87
|
-
const clearError = useCallback(() => {
|
|
88
|
-
setError(null);
|
|
89
|
-
}, []);
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
loading,
|
|
93
|
-
error,
|
|
94
|
-
execute,
|
|
95
|
-
clearError,
|
|
96
|
-
};
|
|
97
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Credential Utility
|
|
3
|
-
* Centralized credential generation for authentication providers
|
|
4
|
-
* Eliminates code duplication across auth services
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
GoogleAuthProvider,
|
|
9
|
-
OAuthProvider,
|
|
10
|
-
type AuthCredential,
|
|
11
|
-
} from 'firebase/auth';
|
|
12
|
-
import * as AppleAuthentication from 'expo-apple-authentication';
|
|
13
|
-
import { Platform } from 'react-native';
|
|
14
|
-
import { generateNonce, hashNonce } from '../../../domains/auth/infrastructure/services/crypto.util';
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Generate Google credential from ID token
|
|
18
|
-
*/
|
|
19
|
-
export function generateGoogleCredential(idToken: string): AuthCredential {
|
|
20
|
-
return GoogleAuthProvider.credential(idToken);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Generate Apple credential from identity token and nonce
|
|
25
|
-
*/
|
|
26
|
-
export function generateAppleCredential(
|
|
27
|
-
identityToken: string,
|
|
28
|
-
rawNonce: string
|
|
29
|
-
): AuthCredential {
|
|
30
|
-
const provider = new OAuthProvider('apple.com');
|
|
31
|
-
return provider.credential({
|
|
32
|
-
idToken: identityToken,
|
|
33
|
-
rawNonce,
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Check if Apple Sign-In is available
|
|
39
|
-
*/
|
|
40
|
-
export async function isAppleSignInAvailable(): Promise<boolean> {
|
|
41
|
-
if (Platform.OS !== 'ios') {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
try {
|
|
45
|
-
return await AppleAuthentication.isAvailableAsync();
|
|
46
|
-
} catch {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Perform Apple Sign-In with default scopes
|
|
53
|
-
*/
|
|
54
|
-
export async function performAppleSignIn(): Promise<{
|
|
55
|
-
identityToken: string | null;
|
|
56
|
-
nonce: string;
|
|
57
|
-
rawNonce: string;
|
|
58
|
-
}> {
|
|
59
|
-
const rawNonce = await generateNonce();
|
|
60
|
-
const hashedNonce = await hashNonce(rawNonce);
|
|
61
|
-
|
|
62
|
-
const result = await AppleAuthentication.signInAsync({
|
|
63
|
-
requestedScopes: [
|
|
64
|
-
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
|
|
65
|
-
AppleAuthentication.AppleAuthenticationScope.EMAIL,
|
|
66
|
-
],
|
|
67
|
-
nonce: hashedNonce,
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
return {
|
|
71
|
-
identityToken: result.identityToken,
|
|
72
|
-
nonce: hashedNonce,
|
|
73
|
-
rawNonce,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Email/Password credential interface
|
|
79
|
-
*/
|
|
80
|
-
export interface EmailPasswordCredential {
|
|
81
|
-
email: string;
|
|
82
|
-
password: string;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Validate email/password credential
|
|
87
|
-
*/
|
|
88
|
-
export function isValidEmailPassword(credential: EmailPasswordCredential): boolean {
|
|
89
|
-
return (
|
|
90
|
-
typeof credential.email === 'string' &&
|
|
91
|
-
credential.email.length > 0 &&
|
|
92
|
-
typeof credential.password === 'string' &&
|
|
93
|
-
credential.password.length > 0
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Custom credential generator interface
|
|
99
|
-
*/
|
|
100
|
-
export interface CredentialGenerator {
|
|
101
|
-
generate(): AuthCredential | Promise<AuthCredential>;
|
|
102
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Advanced Async Executors
|
|
3
|
-
* Retry and timeout support for async operations
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Result } from '../result/result-types';
|
|
7
|
-
import { failureResultFromError, successResult } from '../result/result-creators';
|
|
8
|
-
import { executeOperation } from './basic-executors.util';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Execute operation with retry
|
|
12
|
-
* @param operation - Operation to execute
|
|
13
|
-
* @param maxRetries - Maximum number of retries (default: 3)
|
|
14
|
-
* @param delayMs - Base delay between retries in milliseconds (default: 1000)
|
|
15
|
-
*/
|
|
16
|
-
export async function executeWithRetry<T>(
|
|
17
|
-
operation: () => Promise<T>,
|
|
18
|
-
maxRetries = 3,
|
|
19
|
-
delayMs = 1000
|
|
20
|
-
): Promise<Result<T>> {
|
|
21
|
-
let lastError: unknown;
|
|
22
|
-
for (let i = 0; i <= maxRetries; i++) {
|
|
23
|
-
try {
|
|
24
|
-
const data = await operation();
|
|
25
|
-
return successResult(data);
|
|
26
|
-
} catch (error) {
|
|
27
|
-
lastError = error;
|
|
28
|
-
if (i < maxRetries) {
|
|
29
|
-
// Exponential backoff: delay * (attempt + 1)
|
|
30
|
-
await new Promise((resolve) => setTimeout(() => resolve(undefined), delayMs * (i + 1)));
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return failureResultFromError(lastError);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Execute operation with timeout
|
|
39
|
-
* @param operation - Operation to execute
|
|
40
|
-
* @param timeoutMs - Timeout in milliseconds
|
|
41
|
-
*/
|
|
42
|
-
export async function executeWithTimeout<T>(
|
|
43
|
-
operation: () => Promise<T>,
|
|
44
|
-
timeoutMs: number
|
|
45
|
-
): Promise<Result<T>> {
|
|
46
|
-
return Promise.race([
|
|
47
|
-
executeOperation(operation),
|
|
48
|
-
new Promise<Result<T>>((resolve) =>
|
|
49
|
-
setTimeout(
|
|
50
|
-
() =>
|
|
51
|
-
resolve({
|
|
52
|
-
success: false,
|
|
53
|
-
error: { code: 'timeout', message: `Operation timed out after ${timeoutMs}ms` },
|
|
54
|
-
}),
|
|
55
|
-
timeoutMs
|
|
56
|
-
)
|
|
57
|
-
),
|
|
58
|
-
]);
|
|
59
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { Result } from '../result/result-types';
|
|
2
|
-
import { failureResultFromError, successResult } from '../result/result-creators';
|
|
3
|
-
import { isSuccess, isFailure } from '../result/result-helpers';
|
|
4
|
-
|
|
5
|
-
export async function executeAll<T>(
|
|
6
|
-
...operations: (() => Promise<Result<T>>)[]
|
|
7
|
-
): Promise<Result<T[]>> {
|
|
8
|
-
try {
|
|
9
|
-
const results = await Promise.all(operations.map((op) => op()));
|
|
10
|
-
|
|
11
|
-
// FIX: Use isFailure() type guard instead of manual check
|
|
12
|
-
for (const result of results) {
|
|
13
|
-
if (isFailure(result)) {
|
|
14
|
-
return result;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const data: T[] = [];
|
|
19
|
-
for (const result of results) {
|
|
20
|
-
if (isSuccess(result) && result.data !== undefined) {
|
|
21
|
-
data.push(result.data);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return successResult(data);
|
|
26
|
-
} catch (error) {
|
|
27
|
-
return failureResultFromError(error);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export async function executeSequence<T>(
|
|
32
|
-
...operations: (() => Promise<Result<T>>)[]
|
|
33
|
-
): Promise<Result<void>> {
|
|
34
|
-
for (const operation of operations) {
|
|
35
|
-
const result = await operation();
|
|
36
|
-
// FIX: Use isFailure() type guard instead of manual check
|
|
37
|
-
if (isFailure(result)) {
|
|
38
|
-
return { success: false, error: result.error };
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return successResult();
|
|
42
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generic Validators
|
|
3
|
-
* Generic validation utilities for arrays, numbers, and objects
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Check if array is not empty
|
|
8
|
-
*/
|
|
9
|
-
export function isNonEmptyArray<T>(value: unknown): value is [T, ...T[]] {
|
|
10
|
-
return Array.isArray(value) && value.length > 0;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Check if number is in range
|
|
15
|
-
*/
|
|
16
|
-
export function isInRange(value: number, min: number, max: number): boolean {
|
|
17
|
-
return typeof value === 'number' && value >= min && value <= max;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Check if number is positive
|
|
22
|
-
*/
|
|
23
|
-
export function isPositive(value: number): boolean {
|
|
24
|
-
return typeof value === 'number' && value > 0;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Check if number is non-negative
|
|
29
|
-
*/
|
|
30
|
-
export function isNonNegative(value: number): boolean {
|
|
31
|
-
return typeof value === 'number' && value >= 0;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Validate object has required properties
|
|
36
|
-
*/
|
|
37
|
-
export function hasRequiredProperties<T extends Record<string, unknown>>(
|
|
38
|
-
obj: unknown,
|
|
39
|
-
requiredProps: (keyof T)[]
|
|
40
|
-
): obj is T {
|
|
41
|
-
if (typeof obj !== 'object' || obj === null) {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
return requiredProps.every((prop) => prop in obj);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Validate all items in array match predicate
|
|
49
|
-
*/
|
|
50
|
-
export function allMatch<T>(items: unknown[], predicate: (item: unknown) => item is T): boolean {
|
|
51
|
-
return Array.isArray(items) && items.every(predicate);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Validate at least one item in array matches predicate
|
|
56
|
-
*/
|
|
57
|
-
export function anyMatch<T>(items: unknown[], predicate: (item: unknown) => item is T): boolean {
|
|
58
|
-
return Array.isArray(items) && items.some(predicate);
|
|
59
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* URL Validators
|
|
3
|
-
* Validation utilities for URLs
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { isValidString } from './string.validator';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Validate URL format
|
|
10
|
-
*/
|
|
11
|
-
export function isValidUrl(url: string): boolean {
|
|
12
|
-
if (!isValidString(url)) {
|
|
13
|
-
return false;
|
|
14
|
-
}
|
|
15
|
-
try {
|
|
16
|
-
new URL(url);
|
|
17
|
-
return true;
|
|
18
|
-
} catch {
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Validate HTTPS URL
|
|
25
|
-
*/
|
|
26
|
-
export function isValidHttpsUrl(url: string): boolean {
|
|
27
|
-
if (!isValidString(url)) {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
try {
|
|
31
|
-
const urlObj = new URL(url);
|
|
32
|
-
return (urlObj as any).protocol === 'https:';
|
|
33
|
-
} catch {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* User Input Validators
|
|
3
|
-
* Validation utilities for user input (email, password, username, phone)
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { isValidString } from './string.validator';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Validate email format
|
|
10
|
-
*/
|
|
11
|
-
export function isValidEmail(email: string): boolean {
|
|
12
|
-
if (!isValidString(email)) {
|
|
13
|
-
return false;
|
|
14
|
-
}
|
|
15
|
-
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
16
|
-
return emailPattern.test(email);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Validate password strength
|
|
21
|
-
* At least 8 characters, containing uppercase, lowercase, and number
|
|
22
|
-
*/
|
|
23
|
-
export function isStrongPassword(password: string): boolean {
|
|
24
|
-
if (!isValidString(password) || password.length < 8) {
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
const hasUpperCase = /[A-Z]/.test(password);
|
|
28
|
-
const hasLowerCase = /[a-z]/.test(password);
|
|
29
|
-
const hasNumber = /[0-9]/.test(password);
|
|
30
|
-
return hasUpperCase && hasLowerCase && hasNumber;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Validate username format
|
|
35
|
-
* Alphanumeric, underscores, and hyphens, 3-20 characters
|
|
36
|
-
*/
|
|
37
|
-
export function isValidUsername(username: string): boolean {
|
|
38
|
-
if (!isValidString(username)) {
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
const pattern = /^[a-zA-Z0-9_-]{3,20}$/;
|
|
42
|
-
return pattern.test(username);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Validate phone number format (basic check)
|
|
47
|
-
*/
|
|
48
|
-
export function isValidPhoneNumber(phone: string): boolean {
|
|
49
|
-
if (!isValidString(phone)) {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
const cleaned = phone.replace(/\s+/g, '').replace(/[-+()]/g, '');
|
|
53
|
-
return /^[0-9]{10,15}$/.test(cleaned);
|
|
54
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generic Validation Utilities
|
|
3
|
-
* Provides reusable validation patterns to eliminate duplication
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Generic validation utility that throws error if validation fails
|
|
8
|
-
* Eliminates duplicate validate-or-throw patterns across the codebase
|
|
9
|
-
*
|
|
10
|
-
* @param value - Value to validate
|
|
11
|
-
* @param validator - Validation function that returns true if valid
|
|
12
|
-
* @param errorMessage - Error message to throw if validation fails
|
|
13
|
-
* @param ErrorClass - Error class to instantiate (defaults to Error)
|
|
14
|
-
* @throws {ErrorClass} When validation fails
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```typescript
|
|
18
|
-
* validateOrThrow(
|
|
19
|
-
* cursor,
|
|
20
|
-
* isValidCursor,
|
|
21
|
-
* ERROR_MESSAGES.FIRESTORE.INVALID_CURSOR,
|
|
22
|
-
* CursorValidationError
|
|
23
|
-
* );
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
export function validateOrThrow<T>(
|
|
27
|
-
value: T,
|
|
28
|
-
validator: (value: T) => boolean,
|
|
29
|
-
errorMessage: string,
|
|
30
|
-
ErrorClass: new (message: string) => Error = Error
|
|
31
|
-
): void {
|
|
32
|
-
if (!validator(value)) {
|
|
33
|
-
throw new ErrorClass(errorMessage);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Validate multiple values, throw on first failure
|
|
39
|
-
* Useful for validating multiple preconditions before an operation
|
|
40
|
-
*
|
|
41
|
-
* @param validations - Array of validation configurations
|
|
42
|
-
* @throws {Error} When any validation fails
|
|
43
|
-
*
|
|
44
|
-
* @example
|
|
45
|
-
* ```typescript
|
|
46
|
-
* validateAllOrThrow([
|
|
47
|
-
* { value: email, validator: isValidEmail, errorMessage: 'Invalid email' },
|
|
48
|
-
* { value: password, validator: isValidPassword, errorMessage: 'Invalid password' },
|
|
49
|
-
* ]);
|
|
50
|
-
* ```
|
|
51
|
-
*/
|
|
52
|
-
export function validateAllOrThrow<T = unknown>(
|
|
53
|
-
validations: Array<{
|
|
54
|
-
value: T;
|
|
55
|
-
validator: (value: T) => boolean;
|
|
56
|
-
errorMessage: string;
|
|
57
|
-
ErrorClass?: new (message: string) => Error;
|
|
58
|
-
}>
|
|
59
|
-
): void {
|
|
60
|
-
for (const { value, validator, errorMessage, ErrorClass = Error } of validations) {
|
|
61
|
-
validateOrThrow(value, validator, errorMessage, ErrorClass);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Firebase Configuration Validator
|
|
3
|
-
*
|
|
4
|
-
* Single Responsibility: Validates Firebase configuration
|
|
5
|
-
* Uses centralized validation utilities from validation.util.ts
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { FirebaseConfig } from '../../../domain/value-objects/FirebaseConfig';
|
|
9
|
-
import { FirebaseConfigurationError } from '../../../domain/errors/FirebaseError';
|
|
10
|
-
import { isValidString } from '../../../domain/utils/validators/string.validator';
|
|
11
|
-
import { isValidFirebaseApiKey, isValidFirebaseProjectId, isValidFirebaseAuthDomain } from '../../../domain/utils/validators/firebase.validator';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Validation rule interface
|
|
15
|
-
*/
|
|
16
|
-
interface ValidationRule {
|
|
17
|
-
validate(config: FirebaseConfig): void;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Required field validation rule using centralized validation
|
|
22
|
-
*/
|
|
23
|
-
class RequiredFieldRule implements ValidationRule {
|
|
24
|
-
constructor(
|
|
25
|
-
private fieldName: string,
|
|
26
|
-
private getter: (config: FirebaseConfig) => string | undefined,
|
|
27
|
-
private customValidator?: (value: string) => boolean
|
|
28
|
-
) {}
|
|
29
|
-
|
|
30
|
-
validate(config: FirebaseConfig): void {
|
|
31
|
-
const value = this.getter(config);
|
|
32
|
-
|
|
33
|
-
if (!isValidString(value)) {
|
|
34
|
-
throw new FirebaseConfigurationError(
|
|
35
|
-
`Firebase ${this.fieldName} is required and must be a non-empty string`
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (this.customValidator && !this.customValidator(value)) {
|
|
40
|
-
throw new FirebaseConfigurationError(
|
|
41
|
-
`Firebase ${this.fieldName} format is invalid`
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Placeholder validation rule
|
|
49
|
-
*/
|
|
50
|
-
class PlaceholderRule implements ValidationRule {
|
|
51
|
-
constructor(
|
|
52
|
-
private fieldName: string,
|
|
53
|
-
private getter: (config: FirebaseConfig) => string | undefined,
|
|
54
|
-
private placeholder: string
|
|
55
|
-
) {}
|
|
56
|
-
|
|
57
|
-
validate(config: FirebaseConfig): void {
|
|
58
|
-
const value = this.getter(config);
|
|
59
|
-
|
|
60
|
-
if (value && value.includes(this.placeholder)) {
|
|
61
|
-
throw new FirebaseConfigurationError(
|
|
62
|
-
`Please replace placeholder values with actual Firebase credentials for ${this.fieldName}`
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Firebase Configuration Validator
|
|
70
|
-
*/
|
|
71
|
-
export class FirebaseConfigValidator {
|
|
72
|
-
private static rules: ValidationRule[] = [
|
|
73
|
-
new RequiredFieldRule('API Key', config => config.apiKey, isValidFirebaseApiKey),
|
|
74
|
-
new RequiredFieldRule('Auth Domain', config => config.authDomain, isValidFirebaseAuthDomain),
|
|
75
|
-
new RequiredFieldRule('Project ID', config => config.projectId, isValidFirebaseProjectId),
|
|
76
|
-
new PlaceholderRule('API Key', config => config.apiKey, 'your_firebase_api_key'),
|
|
77
|
-
new PlaceholderRule('Project ID', config => config.projectId, 'your-project-id'),
|
|
78
|
-
];
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Validate Firebase configuration
|
|
82
|
-
* @throws {FirebaseConfigurationError} If configuration is invalid
|
|
83
|
-
*/
|
|
84
|
-
static validate(config: FirebaseConfig): void {
|
|
85
|
-
for (const rule of this.rules) {
|
|
86
|
-
rule.validate(config);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|