@umituz/react-native-firebase 1.13.140 → 1.13.141
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/config/FirebaseAuthClient.ts +11 -2
- package/src/auth/infrastructure/services/account-deletion.service.ts +40 -20
- package/src/auth/infrastructure/services/anonymous-auth.service.ts +7 -11
- package/src/auth/infrastructure/services/apple-auth.service.ts +44 -18
- package/src/auth/infrastructure/services/base/base-auth.service.ts +9 -40
- package/src/auth/infrastructure/services/firestore-utils.service.ts +2 -2
- package/src/auth/infrastructure/services/google-auth.service.ts +27 -23
- package/src/auth/infrastructure/services/password.service.ts +6 -21
- package/src/auth/infrastructure/services/reauthentication.service.ts +19 -29
- package/src/auth/presentation/hooks/shared/hook-utils.util.ts +222 -0
- package/src/auth/presentation/hooks/useSocialAuth.ts +10 -1
- package/src/domain/utils/async-executor.util.ts +176 -0
- package/src/domain/utils/credential.util.ts +102 -0
- package/src/domain/utils/index.ts +101 -0
- package/src/domain/utils/result.util.ts +129 -0
- package/src/domain/utils/service-config.util.ts +99 -0
- package/src/domain/utils/validation.util.ts +78 -0
- package/src/firestore/infrastructure/middleware/QueryDeduplicationMiddleware.ts +3 -5
- package/src/firestore/infrastructure/repositories/BaseQueryRepository.ts +3 -3
- package/src/firestore/utils/dateUtils.ts +21 -5
- package/src/firestore/utils/deduplication/pending-query-manager.util.ts +3 -7
- package/src/firestore/utils/deduplication/query-key-generator.util.ts +12 -3
- package/src/firestore/utils/deduplication/timer-manager.util.ts +8 -2
- package/src/infrastructure/config/FirebaseClient.ts +36 -4
- package/src/init/createFirebaseInitModule.ts +18 -3
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Hook Utilities
|
|
3
|
+
* Common utilities and patterns for React hooks
|
|
4
|
+
* Reduces code duplication across presentation hooks
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
8
|
+
import { onAuthStateChanged, type Auth, type User } from 'firebase/auth';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Hook state management result
|
|
12
|
+
*/
|
|
13
|
+
export interface HookState<T> {
|
|
14
|
+
value: T;
|
|
15
|
+
loading: boolean;
|
|
16
|
+
error: Error | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Hook state actions
|
|
21
|
+
*/
|
|
22
|
+
export interface HookStateActions<T> {
|
|
23
|
+
setValue: (value: T) => void;
|
|
24
|
+
setLoading: (loading: boolean) => void;
|
|
25
|
+
setError: (error: Error | null) => void;
|
|
26
|
+
clearError: () => void;
|
|
27
|
+
reset: () => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create a stateful hook with loading and error handling
|
|
32
|
+
*/
|
|
33
|
+
export function useHookState<T>(
|
|
34
|
+
initialValue: T
|
|
35
|
+
): [HookState<T>, HookStateActions<T>] {
|
|
36
|
+
const [value, setValue] = useState<T>(initialValue);
|
|
37
|
+
const [loading, setLoading] = useState<boolean>(false);
|
|
38
|
+
const [error, setError] = useState<Error | null>(null);
|
|
39
|
+
|
|
40
|
+
const clearError = useCallback(() => {
|
|
41
|
+
setError(null);
|
|
42
|
+
}, []);
|
|
43
|
+
|
|
44
|
+
const reset = useCallback(() => {
|
|
45
|
+
setValue(initialValue);
|
|
46
|
+
setLoading(false);
|
|
47
|
+
setError(null);
|
|
48
|
+
}, [initialValue]);
|
|
49
|
+
|
|
50
|
+
const state: HookState<T> = { value, loading, error };
|
|
51
|
+
const actions: HookStateActions<T> = {
|
|
52
|
+
setValue,
|
|
53
|
+
setLoading,
|
|
54
|
+
setError,
|
|
55
|
+
clearError,
|
|
56
|
+
reset,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return [state, actions];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Hook for managing cleanup callbacks
|
|
64
|
+
*/
|
|
65
|
+
export function useCleanup(
|
|
66
|
+
cleanupFn: () => void,
|
|
67
|
+
deps: React.DependencyList = []
|
|
68
|
+
): void {
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
return () => {
|
|
71
|
+
cleanupFn();
|
|
72
|
+
};
|
|
73
|
+
}, deps);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Hook for managing async operation state
|
|
78
|
+
*/
|
|
79
|
+
export function useAsyncOperation<T = void>() {
|
|
80
|
+
const [loading, setLoading] = useState<boolean>(false);
|
|
81
|
+
const [error, setError] = useState<Error | null>(null);
|
|
82
|
+
const operationRef = useRef<Promise<T> | null>(null);
|
|
83
|
+
|
|
84
|
+
const execute = useCallback(async (operation: () => Promise<T>): Promise<T> => {
|
|
85
|
+
setLoading(true);
|
|
86
|
+
setError(null);
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const promise = operation();
|
|
90
|
+
operationRef.current = promise;
|
|
91
|
+
const result = await promise;
|
|
92
|
+
return result;
|
|
93
|
+
} catch (err) {
|
|
94
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
95
|
+
setError(error);
|
|
96
|
+
throw error;
|
|
97
|
+
} finally {
|
|
98
|
+
setLoading(false);
|
|
99
|
+
operationRef.current = null;
|
|
100
|
+
}
|
|
101
|
+
}, []);
|
|
102
|
+
|
|
103
|
+
const clearError = useCallback(() => {
|
|
104
|
+
setError(null);
|
|
105
|
+
}, []);
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
loading,
|
|
109
|
+
error,
|
|
110
|
+
execute,
|
|
111
|
+
clearError,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Hook for debounced value updates
|
|
117
|
+
*/
|
|
118
|
+
export function useDebouncedValue<T>(value: T, delayMs: number): T {
|
|
119
|
+
const [debouncedValue, setDebouncedValue] = useState<T>(value);
|
|
120
|
+
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
const handler = setTimeout(() => {
|
|
123
|
+
setDebouncedValue(value);
|
|
124
|
+
}, delayMs);
|
|
125
|
+
|
|
126
|
+
return () => {
|
|
127
|
+
clearTimeout(handler);
|
|
128
|
+
};
|
|
129
|
+
}, [value, delayMs]);
|
|
130
|
+
|
|
131
|
+
return debouncedValue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Hook for tracking mounted state
|
|
136
|
+
*/
|
|
137
|
+
export function useIsMounted(): () => boolean {
|
|
138
|
+
const isMountedRef = useRef<boolean>(true);
|
|
139
|
+
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
isMountedRef.current = true;
|
|
142
|
+
return () => {
|
|
143
|
+
isMountedRef.current = false;
|
|
144
|
+
};
|
|
145
|
+
}, []);
|
|
146
|
+
|
|
147
|
+
return useCallback(() => isMountedRef.current, []);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Hook for safe state updates (only if mounted)
|
|
152
|
+
*/
|
|
153
|
+
export function useSafeState<T>(
|
|
154
|
+
initialValue: T
|
|
155
|
+
): [T, (value: T | ((prev: T) => T)) => void] {
|
|
156
|
+
const isMounted = useIsMounted();
|
|
157
|
+
const [state, setState] = useState<T>(initialValue);
|
|
158
|
+
|
|
159
|
+
const setSafeState = useCallback(
|
|
160
|
+
(value: T | ((prev: T) => T)) => {
|
|
161
|
+
if (isMounted()) {
|
|
162
|
+
setState(value);
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
[isMounted]
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
return [state, setSafeState];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Create an auth state handler
|
|
173
|
+
*/
|
|
174
|
+
export function createAuthStateHandler(
|
|
175
|
+
setState: (user: User | null) => void,
|
|
176
|
+
setLoading: (loading: boolean) => void,
|
|
177
|
+
setError: (error: Error | null) => void
|
|
178
|
+
): (user: User | null) => void {
|
|
179
|
+
return (user: User | null) => {
|
|
180
|
+
try {
|
|
181
|
+
setState(user);
|
|
182
|
+
setError(null);
|
|
183
|
+
} catch (err) {
|
|
184
|
+
const error = err instanceof Error ? err : new Error('Auth state update failed');
|
|
185
|
+
setError(error);
|
|
186
|
+
} finally {
|
|
187
|
+
setLoading(false);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Hook for managing auth listener lifecycle
|
|
194
|
+
*/
|
|
195
|
+
export function useAuthListener(
|
|
196
|
+
auth: Auth | null,
|
|
197
|
+
onAuthStateChange: (user: User | null) => void
|
|
198
|
+
): void {
|
|
199
|
+
const unsubscribeRef = useRef<(() => void) | null>(null);
|
|
200
|
+
|
|
201
|
+
useEffect(() => {
|
|
202
|
+
// Cleanup previous listener
|
|
203
|
+
if (unsubscribeRef.current) {
|
|
204
|
+
unsubscribeRef.current();
|
|
205
|
+
unsubscribeRef.current = null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!auth) {
|
|
209
|
+
onAuthStateChange(null);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
unsubscribeRef.current = onAuthStateChanged(auth, onAuthStateChange);
|
|
214
|
+
|
|
215
|
+
return () => {
|
|
216
|
+
if (unsubscribeRef.current) {
|
|
217
|
+
unsubscribeRef.current();
|
|
218
|
+
unsubscribeRef.current = null;
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
}, [auth, onAuthStateChange]);
|
|
222
|
+
}
|
|
@@ -48,11 +48,20 @@ export function useSocialAuth(config?: SocialAuthConfig): UseSocialAuthResult {
|
|
|
48
48
|
}, [googleConfig]);
|
|
49
49
|
|
|
50
50
|
useEffect(() => {
|
|
51
|
+
let cancelled = false;
|
|
52
|
+
|
|
51
53
|
const checkApple = async () => {
|
|
52
54
|
const available = await appleAuthService.isAvailable();
|
|
53
|
-
|
|
55
|
+
if (!cancelled) {
|
|
56
|
+
setAppleAvailable(available && (config?.apple?.enabled ?? false));
|
|
57
|
+
}
|
|
54
58
|
};
|
|
59
|
+
|
|
55
60
|
checkApple();
|
|
61
|
+
|
|
62
|
+
return () => {
|
|
63
|
+
cancelled = true;
|
|
64
|
+
};
|
|
56
65
|
}, [config?.apple?.enabled]);
|
|
57
66
|
|
|
58
67
|
const signInWithGoogleToken = useCallback(
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async Operation Executor Utility
|
|
3
|
+
* Centralized async operation execution with error handling
|
|
4
|
+
* Eliminates code duplication across services
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Result, FailureResult } from './result.util';
|
|
8
|
+
import { failureResultFromError, successResult } from './result.util';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Error converter function type
|
|
12
|
+
* Converts unknown errors to ErrorInfo
|
|
13
|
+
*/
|
|
14
|
+
export type ErrorConverter = (error: unknown) => { code: string; message: string };
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Default error converter for auth operations
|
|
18
|
+
*/
|
|
19
|
+
export function authErrorConverter(error: unknown): { code: string; message: string } {
|
|
20
|
+
if (error instanceof Error) {
|
|
21
|
+
return {
|
|
22
|
+
code: (error as { code?: string }).code ?? 'auth/failed',
|
|
23
|
+
message: error.message,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
code: 'auth/failed',
|
|
28
|
+
message: typeof error === 'string' ? error : 'Authentication failed',
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Default error converter for operations
|
|
34
|
+
*/
|
|
35
|
+
export function defaultErrorConverter(
|
|
36
|
+
error: unknown,
|
|
37
|
+
defaultCode = 'operation/failed'
|
|
38
|
+
): { code: string; message: string } {
|
|
39
|
+
if (error instanceof Error) {
|
|
40
|
+
return {
|
|
41
|
+
code: (error as { code?: string }).code ?? defaultCode,
|
|
42
|
+
message: error.message,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
code: defaultCode,
|
|
47
|
+
message: typeof error === 'string' ? error : 'Operation failed',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Execute async operation with error handling
|
|
53
|
+
* Returns Result type with success/failure
|
|
54
|
+
*/
|
|
55
|
+
export async function executeOperation<T>(
|
|
56
|
+
operation: () => Promise<T>,
|
|
57
|
+
errorConverter?: ErrorConverter
|
|
58
|
+
): Promise<Result<T>> {
|
|
59
|
+
try {
|
|
60
|
+
const data = await operation();
|
|
61
|
+
return successResult(data);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
const converter = errorConverter ?? defaultErrorConverter;
|
|
64
|
+
return { success: false, error: converter(error) };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Execute async operation with error handling and default code
|
|
70
|
+
*/
|
|
71
|
+
export async function executeOperationWithCode<T>(
|
|
72
|
+
operation: () => Promise<T>,
|
|
73
|
+
defaultErrorCode = 'operation/failed'
|
|
74
|
+
): Promise<Result<T>> {
|
|
75
|
+
return executeOperation(operation, (error) => defaultErrorConverter(error, defaultErrorCode));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Execute async void operation
|
|
80
|
+
* Useful for operations that don't return data
|
|
81
|
+
*/
|
|
82
|
+
export async function executeVoidOperation(
|
|
83
|
+
operation: () => Promise<void>,
|
|
84
|
+
errorConverter?: ErrorConverter
|
|
85
|
+
): Promise<Result<void>> {
|
|
86
|
+
return executeOperation(operation, errorConverter) as Promise<Result<void>>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Execute async operation with auth error handling
|
|
91
|
+
*/
|
|
92
|
+
export async function executeAuthOperation<T>(
|
|
93
|
+
operation: () => Promise<T>
|
|
94
|
+
): Promise<Result<T>> {
|
|
95
|
+
return executeOperation(operation, authErrorConverter);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Execute multiple operations in parallel
|
|
100
|
+
* Returns success only if all operations succeed
|
|
101
|
+
*/
|
|
102
|
+
export async function executeAll<T>(
|
|
103
|
+
...operations: (() => Promise<Result<T>>)[]
|
|
104
|
+
): Promise<Result<T[]>> {
|
|
105
|
+
try {
|
|
106
|
+
const results = await Promise.all(operations.map((op) => op()));
|
|
107
|
+
const failures = results.filter((r) => !r.success);
|
|
108
|
+
if (failures.length > 0) {
|
|
109
|
+
return failures[0] as FailureResult;
|
|
110
|
+
}
|
|
111
|
+
const data = results.map((r) => (r as { success: true; data: T }).data);
|
|
112
|
+
return successResult(data);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
return failureResultFromError(error);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Execute operations in sequence, stopping at first failure
|
|
120
|
+
*/
|
|
121
|
+
export async function executeSequence<T>(
|
|
122
|
+
...operations: (() => Promise<Result<T>>)[]
|
|
123
|
+
): Promise<Result<void>> {
|
|
124
|
+
for (const operation of operations) {
|
|
125
|
+
const result = await operation();
|
|
126
|
+
if (!result.success) {
|
|
127
|
+
return result as Result<void>;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return successResult();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Execute operation with retry
|
|
135
|
+
*/
|
|
136
|
+
export async function executeWithRetry<T>(
|
|
137
|
+
operation: () => Promise<T>,
|
|
138
|
+
maxRetries = 3,
|
|
139
|
+
delayMs = 1000
|
|
140
|
+
): Promise<Result<T>> {
|
|
141
|
+
let lastError: unknown;
|
|
142
|
+
for (let i = 0; i <= maxRetries; i++) {
|
|
143
|
+
try {
|
|
144
|
+
const data = await operation();
|
|
145
|
+
return successResult(data);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
lastError = error;
|
|
148
|
+
if (i < maxRetries) {
|
|
149
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs * (i + 1)));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return failureResultFromError(lastError);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Execute operation with timeout
|
|
158
|
+
*/
|
|
159
|
+
export async function executeWithTimeout<T>(
|
|
160
|
+
operation: () => Promise<T>,
|
|
161
|
+
timeoutMs: number
|
|
162
|
+
): Promise<Result<T>> {
|
|
163
|
+
return Promise.race([
|
|
164
|
+
executeOperation(operation),
|
|
165
|
+
new Promise<Result<T>>((resolve) =>
|
|
166
|
+
setTimeout(
|
|
167
|
+
() =>
|
|
168
|
+
resolve({
|
|
169
|
+
success: false,
|
|
170
|
+
error: { code: 'timeout', message: `Operation timed out after ${timeoutMs}ms` },
|
|
171
|
+
}),
|
|
172
|
+
timeoutMs
|
|
173
|
+
)
|
|
174
|
+
),
|
|
175
|
+
]);
|
|
176
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
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 '../../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
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain Utils
|
|
3
|
+
* Centralized utilities for domain operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Result types
|
|
7
|
+
export {
|
|
8
|
+
successResult,
|
|
9
|
+
failureResult,
|
|
10
|
+
failureResultFrom,
|
|
11
|
+
failureResultFromError,
|
|
12
|
+
isSuccess,
|
|
13
|
+
isFailure,
|
|
14
|
+
getDataOrDefault,
|
|
15
|
+
mapResult,
|
|
16
|
+
chainResults,
|
|
17
|
+
type Result,
|
|
18
|
+
type SuccessResult,
|
|
19
|
+
type FailureResult,
|
|
20
|
+
type ErrorInfo,
|
|
21
|
+
} from './result.util';
|
|
22
|
+
|
|
23
|
+
// Async operation execution
|
|
24
|
+
export {
|
|
25
|
+
executeOperation,
|
|
26
|
+
executeOperationWithCode,
|
|
27
|
+
executeVoidOperation,
|
|
28
|
+
executeAuthOperation,
|
|
29
|
+
executeAll,
|
|
30
|
+
executeSequence,
|
|
31
|
+
executeWithRetry,
|
|
32
|
+
executeWithTimeout,
|
|
33
|
+
authErrorConverter,
|
|
34
|
+
defaultErrorConverter,
|
|
35
|
+
type ErrorConverter,
|
|
36
|
+
} from './async-executor.util';
|
|
37
|
+
|
|
38
|
+
// Service configuration
|
|
39
|
+
export {
|
|
40
|
+
ConfigurableService,
|
|
41
|
+
createConfigurableService,
|
|
42
|
+
type ConfigState,
|
|
43
|
+
type IConfigurableService,
|
|
44
|
+
} from './service-config.util';
|
|
45
|
+
|
|
46
|
+
// Credential utilities
|
|
47
|
+
export {
|
|
48
|
+
generateGoogleCredential,
|
|
49
|
+
generateAppleCredential,
|
|
50
|
+
isAppleSignInAvailable,
|
|
51
|
+
performAppleSignIn,
|
|
52
|
+
isValidEmailPassword,
|
|
53
|
+
type EmailPasswordCredential,
|
|
54
|
+
type CredentialGenerator,
|
|
55
|
+
} from './credential.util';
|
|
56
|
+
|
|
57
|
+
// Error handling
|
|
58
|
+
export {
|
|
59
|
+
toErrorInfo,
|
|
60
|
+
toAuthErrorInfo,
|
|
61
|
+
hasErrorCode,
|
|
62
|
+
isCancelledError,
|
|
63
|
+
isQuotaErrorInfo,
|
|
64
|
+
isNetworkError,
|
|
65
|
+
isAuthError,
|
|
66
|
+
isQuotaError,
|
|
67
|
+
isRetryableError,
|
|
68
|
+
getQuotaErrorMessage,
|
|
69
|
+
getRetryableErrorMessage,
|
|
70
|
+
type ErrorInfo as ErrorHandlerErrorInfo,
|
|
71
|
+
} from './error-handler.util';
|
|
72
|
+
|
|
73
|
+
// Type guards
|
|
74
|
+
export {
|
|
75
|
+
hasCodeProperty,
|
|
76
|
+
hasMessageProperty,
|
|
77
|
+
hasCodeAndMessageProperties,
|
|
78
|
+
} from './type-guards.util';
|
|
79
|
+
|
|
80
|
+
// Validation
|
|
81
|
+
export {
|
|
82
|
+
isValidString,
|
|
83
|
+
isEmptyString,
|
|
84
|
+
isValidFirebaseApiKey,
|
|
85
|
+
isValidFirebaseAuthDomain,
|
|
86
|
+
isValidFirebaseProjectId,
|
|
87
|
+
isValidUrl,
|
|
88
|
+
isValidHttpsUrl,
|
|
89
|
+
isValidEmail,
|
|
90
|
+
isDefined,
|
|
91
|
+
isNonEmptyArray,
|
|
92
|
+
isInRange,
|
|
93
|
+
isPositive,
|
|
94
|
+
isNonNegative,
|
|
95
|
+
} from './validation.util';
|
|
96
|
+
|
|
97
|
+
// ID generation
|
|
98
|
+
export {
|
|
99
|
+
generateUniqueId,
|
|
100
|
+
generateShortId,
|
|
101
|
+
} from './id-generator.util';
|