@umituz/react-native-firebase 1.13.119 → 1.13.121
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 +3 -3
- package/src/auth/index.ts +1 -0
- package/src/auth/infrastructure/config/FirebaseAuthClient.ts +12 -1
- package/src/auth/infrastructure/services/apple-auth.service.ts +18 -3
- package/src/auth/infrastructure/services/apple-auth.types.ts +21 -6
- package/src/auth/infrastructure/services/auth-utils.service.ts +15 -4
- package/src/auth/infrastructure/services/firestore-utils.service.ts +14 -2
- package/src/auth/infrastructure/services/google-auth.service.ts +7 -1
- package/src/auth/infrastructure/services/google-auth.types.ts +14 -5
- package/src/auth/infrastructure/services/reauthentication.types.ts +11 -6
- package/src/auth/infrastructure/stores/auth.store.ts +9 -3
- package/src/auth/presentation/hooks/useFirebaseAuth.ts +9 -1
- package/src/firestore/infrastructure/middleware/QueryDeduplicationMiddleware.ts +14 -6
- package/src/firestore/infrastructure/repositories/BasePaginatedRepository.ts +30 -15
- package/src/firestore/infrastructure/repositories/BaseRepository.ts +17 -0
- package/src/firestore/utils/firestore-helper.ts +7 -1
- package/src/firestore/utils/quota-error-detector.util.ts +11 -4
- package/src/index.ts +0 -26
- package/src/infrastructure/config/FirebaseClient.ts +2 -1
- package/src/infrastructure/config/services/FirebaseServiceInitializer.ts +16 -3
- package/src/storage/uploader.ts +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-firebase",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.121",
|
|
4
4
|
"description": "Unified Firebase package for React Native apps - Auth and Firestore services using Firebase JS SDK (no native modules).",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"expo-apple-authentication": ">=6.0.0",
|
|
37
37
|
"expo-crypto": ">=13.0.0",
|
|
38
38
|
"firebase": ">=10.0.0",
|
|
39
|
-
"react": ">=
|
|
40
|
-
"react-native": ">=0.
|
|
39
|
+
"react": ">=19.0.0",
|
|
40
|
+
"react-native": ">=0.81.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@expo/vector-icons": "^15.0.3",
|
package/src/auth/index.ts
CHANGED
|
@@ -38,7 +38,18 @@ class FirebaseAuthClientSingleton {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
getAuth(): Auth | null {
|
|
41
|
-
if
|
|
41
|
+
// Don't retry if we already have an auth instance
|
|
42
|
+
if (this.auth) return this.auth;
|
|
43
|
+
|
|
44
|
+
// Don't retry if we already failed to initialize
|
|
45
|
+
if (this.initializationError) return null;
|
|
46
|
+
|
|
47
|
+
// Don't retry if Firebase app is not available
|
|
48
|
+
const app = getFirebaseApp();
|
|
49
|
+
if (!app) return null;
|
|
50
|
+
|
|
51
|
+
// Attempt initialization
|
|
52
|
+
this.initialize();
|
|
42
53
|
return this.auth;
|
|
43
54
|
}
|
|
44
55
|
|
|
@@ -31,6 +31,7 @@ export class AppleAuthService {
|
|
|
31
31
|
return {
|
|
32
32
|
success: false,
|
|
33
33
|
error: "Apple Sign-In is not available on this device",
|
|
34
|
+
code: "unavailable",
|
|
34
35
|
};
|
|
35
36
|
}
|
|
36
37
|
|
|
@@ -46,7 +47,11 @@ export class AppleAuthService {
|
|
|
46
47
|
});
|
|
47
48
|
|
|
48
49
|
if (!appleCredential.identityToken) {
|
|
49
|
-
return {
|
|
50
|
+
return {
|
|
51
|
+
success: false,
|
|
52
|
+
error: "No identity token received",
|
|
53
|
+
code: "no_token"
|
|
54
|
+
};
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
const provider = new OAuthProvider("apple.com");
|
|
@@ -67,12 +72,22 @@ export class AppleAuthService {
|
|
|
67
72
|
};
|
|
68
73
|
} catch (error) {
|
|
69
74
|
if (error instanceof Error && error.message.includes("ERR_CANCELED")) {
|
|
70
|
-
return {
|
|
75
|
+
return {
|
|
76
|
+
success: false,
|
|
77
|
+
error: "Apple Sign-In was cancelled",
|
|
78
|
+
code: "canceled"
|
|
79
|
+
};
|
|
71
80
|
}
|
|
81
|
+
|
|
72
82
|
if (__DEV__) console.error('[Firebase Auth] Apple Sign-In failed:', error);
|
|
83
|
+
|
|
84
|
+
const errorCode = (error as { code?: string })?.code || 'unknown';
|
|
85
|
+
const errorMessage = error instanceof Error ? error.message : 'Apple sign-in failed';
|
|
86
|
+
|
|
73
87
|
return {
|
|
74
88
|
success: false,
|
|
75
|
-
error:
|
|
89
|
+
error: errorMessage,
|
|
90
|
+
code: errorCode,
|
|
76
91
|
};
|
|
77
92
|
}
|
|
78
93
|
}
|
|
@@ -3,14 +3,29 @@
|
|
|
3
3
|
* Type definitions for Apple authentication
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
import type { UserCredential } from 'firebase/auth';
|
|
7
|
+
|
|
8
|
+
export interface AppleAuthConfig {
|
|
9
|
+
clientId?: string;
|
|
10
|
+
scope?: string;
|
|
11
|
+
redirectURI?: string;
|
|
12
|
+
state?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface AppleAuthSuccessResult {
|
|
16
|
+
success: true;
|
|
17
|
+
userCredential: UserCredential;
|
|
18
|
+
isNewUser: boolean;
|
|
12
19
|
}
|
|
13
20
|
|
|
21
|
+
export interface AppleAuthErrorResult {
|
|
22
|
+
success: false;
|
|
23
|
+
error: string;
|
|
24
|
+
code?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type AppleAuthResult = AppleAuthSuccessResult | AppleAuthErrorResult;
|
|
28
|
+
|
|
14
29
|
export interface AppleAuthCredential {
|
|
15
30
|
idToken: string;
|
|
16
31
|
rawNonce: string;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { User, Auth } from 'firebase/auth';
|
|
7
|
+
import { getFirebaseAuth } from '../config/FirebaseAuthClient';
|
|
7
8
|
|
|
8
9
|
export interface AuthCheckResult {
|
|
9
10
|
isAuthenticated: boolean;
|
|
@@ -67,16 +68,26 @@ export function getCurrentUser(auth: Auth): User | null {
|
|
|
67
68
|
* Get current user ID from global auth instance
|
|
68
69
|
*/
|
|
69
70
|
export function getCurrentUserIdFromGlobal(): string | null {
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
const auth = getFirebaseAuth();
|
|
72
|
+
|
|
73
|
+
if (!auth || !auth.currentUser) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return auth.currentUser.uid;
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
/**
|
|
75
81
|
* Get current user from global auth instance
|
|
76
82
|
*/
|
|
77
83
|
export function getCurrentUserFromGlobal(): User | null {
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
const auth = getFirebaseAuth();
|
|
85
|
+
|
|
86
|
+
if (!auth) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return auth.currentUser;
|
|
80
91
|
}
|
|
81
92
|
|
|
82
93
|
/**
|
|
@@ -61,7 +61,13 @@ export function shouldSkipFirestoreQuery(
|
|
|
61
61
|
|
|
62
62
|
try {
|
|
63
63
|
if (!auth) {
|
|
64
|
-
|
|
64
|
+
// Return a default result when no auth instance is available
|
|
65
|
+
return createResult(true, {
|
|
66
|
+
isAuthenticated: false,
|
|
67
|
+
isAnonymous: false,
|
|
68
|
+
currentUser: null,
|
|
69
|
+
userId: null,
|
|
70
|
+
}, "no_auth");
|
|
65
71
|
}
|
|
66
72
|
|
|
67
73
|
const authState = checkAuthState(auth);
|
|
@@ -83,7 +89,13 @@ export function shouldSkipFirestoreQuery(
|
|
|
83
89
|
if (__DEV__) {
|
|
84
90
|
console.error("[FirestoreUtils] Error checking query", error);
|
|
85
91
|
}
|
|
86
|
-
|
|
92
|
+
// Return a default result on error
|
|
93
|
+
return createResult(true, {
|
|
94
|
+
isAuthenticated: false,
|
|
95
|
+
isAnonymous: false,
|
|
96
|
+
currentUser: null,
|
|
97
|
+
userId: null,
|
|
98
|
+
}, "invalid_options");
|
|
87
99
|
}
|
|
88
100
|
}
|
|
89
101
|
|
|
@@ -55,9 +55,15 @@ export class GoogleAuthService {
|
|
|
55
55
|
if (__DEV__) {
|
|
56
56
|
console.error('[Firebase Auth] Google Sign-In failed:', error);
|
|
57
57
|
}
|
|
58
|
+
|
|
59
|
+
// Extract error code for better error handling
|
|
60
|
+
const errorCode = (error as { code?: string })?.code || 'unknown';
|
|
61
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
62
|
+
|
|
58
63
|
return {
|
|
59
64
|
success: false,
|
|
60
|
-
error:
|
|
65
|
+
error: errorMessage,
|
|
66
|
+
code: errorCode,
|
|
61
67
|
};
|
|
62
68
|
}
|
|
63
69
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Type definitions for Google authentication
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import type { UserCredential } from 'firebase/auth';
|
|
7
|
+
|
|
6
8
|
export interface GoogleAuthConfig {
|
|
7
9
|
clientId?: string;
|
|
8
10
|
webClientId?: string;
|
|
@@ -10,13 +12,20 @@ export interface GoogleAuthConfig {
|
|
|
10
12
|
androidClientId?: string;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
export interface
|
|
14
|
-
success:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
export interface GoogleAuthSuccessResult {
|
|
16
|
+
success: true;
|
|
17
|
+
userCredential: UserCredential;
|
|
18
|
+
isNewUser: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface GoogleAuthErrorResult {
|
|
22
|
+
success: false;
|
|
23
|
+
error: string;
|
|
24
|
+
code?: string;
|
|
18
25
|
}
|
|
19
26
|
|
|
27
|
+
export type GoogleAuthResult = GoogleAuthSuccessResult | GoogleAuthErrorResult;
|
|
28
|
+
|
|
20
29
|
export interface GoogleAuthCredential {
|
|
21
30
|
idToken: string;
|
|
22
31
|
accessToken?: string;
|
|
@@ -3,25 +3,30 @@
|
|
|
3
3
|
* Type definitions for reauthentication operations
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import type { AuthCredential } from 'firebase/auth';
|
|
7
|
+
|
|
6
8
|
export interface ReauthenticationCredential {
|
|
7
9
|
provider: 'password' | 'google.com' | 'apple.com';
|
|
8
|
-
credential:
|
|
10
|
+
credential: AuthCredential;
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
export interface ReauthenticationResult {
|
|
12
14
|
success: boolean;
|
|
13
15
|
error?: {
|
|
14
|
-
code
|
|
15
|
-
message
|
|
16
|
+
code: string;
|
|
17
|
+
message: string;
|
|
16
18
|
};
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
export type AuthProviderType = 'password' | 'google.com' | 'apple.com';
|
|
21
|
+
export type AuthProviderType = 'anonymous' | 'password' | 'google.com' | 'apple.com' | 'unknown';
|
|
20
22
|
|
|
21
23
|
export interface ReauthCredentialResult {
|
|
22
24
|
success: boolean;
|
|
23
|
-
credential?:
|
|
24
|
-
error?:
|
|
25
|
+
credential?: AuthCredential;
|
|
26
|
+
error?: {
|
|
27
|
+
code: string;
|
|
28
|
+
message: string;
|
|
29
|
+
};
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
export interface AccountDeletionResult {
|
|
@@ -40,13 +40,15 @@ export const useFirebaseAuthStore = createStore<AuthState, AuthActions>({
|
|
|
40
40
|
const state = get();
|
|
41
41
|
|
|
42
42
|
// Atomic check: both state flag AND in-progress mutex
|
|
43
|
+
// This prevents multiple simultaneous calls from setting up listeners
|
|
43
44
|
if (state.listenerSetup || unsubscribe || setupInProgress) {
|
|
44
45
|
return;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
// Set mutex immediately (synchronous, before any async operation)
|
|
49
|
+
// This ensures no other call can pass the check above
|
|
48
50
|
setupInProgress = true;
|
|
49
|
-
set({ listenerSetup: true });
|
|
51
|
+
set({ listenerSetup: true, loading: true });
|
|
50
52
|
|
|
51
53
|
try {
|
|
52
54
|
unsubscribe = onAuthStateChanged(auth, (currentUser: User | null) => {
|
|
@@ -59,10 +61,14 @@ export const useFirebaseAuthStore = createStore<AuthState, AuthActions>({
|
|
|
59
61
|
|
|
60
62
|
// Listener setup complete - keep mutex locked until cleanup
|
|
61
63
|
// (setupInProgress remains true to indicate active listener)
|
|
62
|
-
} catch {
|
|
64
|
+
} catch (error) {
|
|
63
65
|
// On error, release the mutex so retry is possible
|
|
64
66
|
setupInProgress = false;
|
|
65
|
-
set({ listenerSetup: false });
|
|
67
|
+
set({ listenerSetup: false, loading: false });
|
|
68
|
+
if (__DEV__) {
|
|
69
|
+
console.error('[Auth Store] Failed to setup auth listener:', error);
|
|
70
|
+
}
|
|
71
|
+
throw error; // Re-throw to allow caller to handle
|
|
66
72
|
}
|
|
67
73
|
},
|
|
68
74
|
|
|
@@ -37,7 +37,15 @@ export function useFirebaseAuth(): UseFirebaseAuthResult {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
setupListener(auth);
|
|
40
|
-
|
|
40
|
+
|
|
41
|
+
// Cleanup function - called when component unmounts
|
|
42
|
+
return () => {
|
|
43
|
+
// Note: We don't call cleanupListener here because the store manages
|
|
44
|
+
// the shared listener. Multiple components can use this hook simultaneously,
|
|
45
|
+
// and we want to keep the listener active until all components are unmounted.
|
|
46
|
+
// The store will handle cleanup when appropriate.
|
|
47
|
+
};
|
|
48
|
+
}, [setupListener]); // setupListener is stable from the store
|
|
41
49
|
|
|
42
50
|
return {
|
|
43
51
|
user,
|
|
@@ -94,17 +94,25 @@ export class QueryDeduplicationMiddleware {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
|
-
* Add query to pending list
|
|
97
|
+
* Add query to pending list with guaranteed cleanup
|
|
98
98
|
*/
|
|
99
99
|
private addPendingQuery(key: string, promise: Promise<unknown>): void {
|
|
100
|
+
// Wrap the promise to ensure cleanup happens regardless of outcome
|
|
101
|
+
const wrappedPromise = promise
|
|
102
|
+
.catch((error) => {
|
|
103
|
+
// Re-throw to maintain original promise behavior
|
|
104
|
+
throw error;
|
|
105
|
+
})
|
|
106
|
+
.finally(() => {
|
|
107
|
+
// Guaranteed cleanup - runs for both resolve and reject
|
|
108
|
+
this.pendingQueries.delete(key);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Store the wrapped promise with the finally handler attached
|
|
100
112
|
this.pendingQueries.set(key, {
|
|
101
|
-
promise,
|
|
113
|
+
promise: wrappedPromise,
|
|
102
114
|
timestamp: Date.now(),
|
|
103
115
|
});
|
|
104
|
-
|
|
105
|
-
promise.finally(() => {
|
|
106
|
-
this.pendingQueries.delete(key);
|
|
107
|
-
});
|
|
108
116
|
}
|
|
109
117
|
|
|
110
118
|
/**
|
|
@@ -36,27 +36,42 @@ export abstract class BasePaginatedRepository extends BaseQueryRepository {
|
|
|
36
36
|
const fetchLimit = helper.getFetchLimit(pageLimit);
|
|
37
37
|
|
|
38
38
|
const collectionRef = collection(db, collectionName);
|
|
39
|
-
let q
|
|
40
|
-
collectionRef,
|
|
41
|
-
orderBy(orderByField, orderDirection),
|
|
42
|
-
limit(fetchLimit),
|
|
43
|
-
);
|
|
44
|
-
|
|
39
|
+
let q: import("firebase/firestore").Query<DocumentData>;
|
|
45
40
|
let cursorKey = 'start';
|
|
41
|
+
|
|
42
|
+
// Handle cursor-based pagination
|
|
46
43
|
if (helper.hasCursor(params) && params?.cursor) {
|
|
47
44
|
cursorKey = params.cursor;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
)
|
|
45
|
+
|
|
46
|
+
// Fetch cursor document first
|
|
47
|
+
const cursorDocRef = doc(db, collectionName, params.cursor);
|
|
48
|
+
const cursorDoc = await getDoc(cursorDocRef);
|
|
49
|
+
|
|
50
|
+
if (!cursorDoc.exists()) {
|
|
51
|
+
// Cursor document doesn't exist - return empty result
|
|
52
|
+
if (__DEV__) {
|
|
53
|
+
console.warn(`[BasePaginatedRepository] Cursor document not found: ${params.cursor}`);
|
|
54
|
+
}
|
|
55
|
+
return [];
|
|
56
56
|
}
|
|
57
|
+
|
|
58
|
+
// Build query with startAfter using the cursor document
|
|
59
|
+
q = query(
|
|
60
|
+
collectionRef,
|
|
61
|
+
orderBy(orderByField, orderDirection),
|
|
62
|
+
startAfter(cursorDoc),
|
|
63
|
+
limit(fetchLimit),
|
|
64
|
+
);
|
|
65
|
+
} else {
|
|
66
|
+
// No cursor - build standard query
|
|
67
|
+
q = query(
|
|
68
|
+
collectionRef,
|
|
69
|
+
orderBy(orderByField, orderDirection),
|
|
70
|
+
limit(fetchLimit),
|
|
71
|
+
);
|
|
57
72
|
}
|
|
58
73
|
|
|
59
|
-
// Generate a unique key for deduplication
|
|
74
|
+
// Generate a unique key for deduplication (after cursor is resolved)
|
|
60
75
|
const uniqueKey = `${collectionName}_list_${orderByField}_${orderDirection}_${fetchLimit}_${cursorKey}`;
|
|
61
76
|
|
|
62
77
|
return this.executeQuery(
|
|
@@ -128,8 +128,25 @@ export class BaseRepository {
|
|
|
128
128
|
|
|
129
129
|
/**
|
|
130
130
|
* Destroy repository and cleanup resources
|
|
131
|
+
* Child classes can override onDestroy() to add custom cleanup logic
|
|
131
132
|
*/
|
|
132
133
|
destroy(): void {
|
|
134
|
+
if (__DEV__) {
|
|
135
|
+
console.log(`[BaseRepository] Destroying repository for ${this.constructor.name}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Call child class cleanup if implemented
|
|
139
|
+
this.onDestroy();
|
|
140
|
+
|
|
141
|
+
// Mark as destroyed
|
|
133
142
|
this.isDestroyed = true;
|
|
134
143
|
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Cleanup hook for child classes
|
|
147
|
+
* Override this method to add custom cleanup logic (e.g., unsubscribe from listeners)
|
|
148
|
+
*/
|
|
149
|
+
protected onDestroy(): void {
|
|
150
|
+
// Override in child classes if needed
|
|
151
|
+
}
|
|
135
152
|
}
|
|
@@ -107,7 +107,13 @@ export async function runTransaction<T>(
|
|
|
107
107
|
return await fbRunTransaction(db, updateFunction);
|
|
108
108
|
} catch (error) {
|
|
109
109
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
110
|
-
|
|
110
|
+
const errorCode = error instanceof Error ? (error as { code?: string }).code : 'unknown';
|
|
111
|
+
|
|
112
|
+
if (__DEV__) {
|
|
113
|
+
console.error(`[runTransaction] Transaction failed (Code: ${errorCode}):`, errorMessage);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
throw new Error(`[runTransaction] Transaction failed: ${errorMessage} (Code: ${errorCode})`);
|
|
111
117
|
}
|
|
112
118
|
}
|
|
113
119
|
|
|
@@ -38,15 +38,22 @@ export function isQuotaError(error: unknown): boolean {
|
|
|
38
38
|
|
|
39
39
|
if (hasCodeProperty(error)) {
|
|
40
40
|
const code = error.code;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
// Use more specific matching - exact match or ends with pattern
|
|
42
|
+
return QUOTA_ERROR_CODES.some((c) =>
|
|
43
|
+
code === c || code.endsWith(`/${c}`) || code.startsWith(`${c}/`)
|
|
44
|
+
);
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
if (hasMessageProperty(error)) {
|
|
47
48
|
const message = error.message;
|
|
48
49
|
const lowerMessage = message.toLowerCase();
|
|
49
|
-
|
|
50
|
+
// Use word boundaries to avoid partial matches
|
|
51
|
+
return QUOTA_ERROR_MESSAGES.some((m) =>
|
|
52
|
+
lowerMessage.includes(` ${m} `) ||
|
|
53
|
+
lowerMessage.startsWith(`${m} `) ||
|
|
54
|
+
lowerMessage.endsWith(` ${m}`) ||
|
|
55
|
+
lowerMessage === m
|
|
56
|
+
);
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
return false;
|
package/src/index.ts
CHANGED
|
@@ -59,24 +59,12 @@ export {
|
|
|
59
59
|
initializeFirebaseAuth,
|
|
60
60
|
} from "./auth/infrastructure/config/FirebaseAuthClient";
|
|
61
61
|
|
|
62
|
-
export { anonymousAuthService } from "./auth/infrastructure/services/anonymous-auth.service";
|
|
63
|
-
export { deleteCurrentUser } from "./auth/infrastructure/services/account-deletion.service";
|
|
64
|
-
export { appleAuthService } from "./auth/infrastructure/services/apple-auth.service";
|
|
65
|
-
export { googleAuthService } from "./auth/infrastructure/services/google-auth.service";
|
|
66
|
-
export type { GoogleAuthConfig } from "./auth/infrastructure/services/google-auth.types";
|
|
67
|
-
export { useAnonymousAuth } from "./auth/presentation/hooks/useAnonymousAuth";
|
|
68
|
-
export type { UseAnonymousAuthResult } from "./auth/presentation/hooks/useAnonymousAuth";
|
|
69
|
-
|
|
70
62
|
// Commonly Used Firestore Exports (for convenience)
|
|
71
63
|
export {
|
|
72
64
|
getFirestore,
|
|
73
65
|
initializeFirestore,
|
|
74
66
|
} from "./firestore/infrastructure/config/FirestoreClient";
|
|
75
67
|
|
|
76
|
-
export { BaseRepository } from "./firestore/infrastructure/repositories/BaseRepository";
|
|
77
|
-
export { FirestorePathResolver } from "./firestore/utils/path-resolver";
|
|
78
|
-
export { PaginationHelper } from "./firestore/utils/pagination.helper";
|
|
79
|
-
|
|
80
68
|
export { Timestamp } from "firebase/firestore";
|
|
81
69
|
export type {
|
|
82
70
|
Transaction,
|
|
@@ -100,20 +88,6 @@ export {
|
|
|
100
88
|
} from "./firestore/utils/firestore-helper";
|
|
101
89
|
export type { FirestoreResult, NoDbResult } from "./firestore/utils/firestore-helper";
|
|
102
90
|
|
|
103
|
-
// Auth Hooks (commonly used)
|
|
104
|
-
export { useSocialAuth } from "./auth/presentation/hooks/useSocialAuth";
|
|
105
|
-
export type {
|
|
106
|
-
SocialAuthConfig,
|
|
107
|
-
SocialAuthResult,
|
|
108
|
-
} from "./auth/presentation/hooks/useSocialAuth";
|
|
109
|
-
|
|
110
|
-
export { updateUserPassword } from "./auth/infrastructure/services/password.service";
|
|
111
|
-
export type { PasswordUpdateResult } from "./auth/infrastructure/services/password.service";
|
|
112
|
-
|
|
113
|
-
export { reauthenticateWithPassword } from "./auth/infrastructure/services/reauthentication.service";
|
|
114
|
-
|
|
115
|
-
export { getCurrentUserFromGlobal } from "./auth/infrastructure/services/auth-utils.service";
|
|
116
|
-
|
|
117
91
|
// Init Module Factory
|
|
118
92
|
export {
|
|
119
93
|
createFirebaseInitModule,
|
|
@@ -35,7 +35,8 @@ export type { FirebaseApp, AuthInitializer, ServiceInitializationOptions };
|
|
|
35
35
|
*/
|
|
36
36
|
export interface ServiceInitializationResult {
|
|
37
37
|
app: FirebaseApp | null;
|
|
38
|
-
auth: unknown;
|
|
38
|
+
auth: unknown; // Auth result from authInitializer callback (can be Auth, UserCredential, or any custom type)
|
|
39
|
+
authError?: string; // Error message if auth initialization failed
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
/**
|
|
@@ -18,19 +18,32 @@ export interface ServiceInitializationResult {
|
|
|
18
18
|
auth: unknown;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
export interface ServiceInitializationResult {
|
|
22
|
+
auth: unknown;
|
|
23
|
+
authError?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
export class FirebaseServiceInitializer {
|
|
22
27
|
static async initializeServices(
|
|
23
28
|
options?: ServiceInitializationOptions
|
|
24
29
|
): Promise<ServiceInitializationResult> {
|
|
25
30
|
let auth: unknown = null;
|
|
31
|
+
let authError: string | undefined;
|
|
32
|
+
|
|
26
33
|
if (options?.authInitializer) {
|
|
27
34
|
try {
|
|
28
35
|
auth = await options.authInitializer();
|
|
29
|
-
} catch {
|
|
30
|
-
//
|
|
36
|
+
} catch (error) {
|
|
37
|
+
// Auth initialization is optional but we should log the error
|
|
38
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
39
|
+
authError = errorMessage;
|
|
40
|
+
|
|
41
|
+
if (__DEV__) {
|
|
42
|
+
console.error('[FirebaseServiceInitializer] Auth initialization failed:', errorMessage);
|
|
43
|
+
}
|
|
31
44
|
}
|
|
32
45
|
}
|
|
33
46
|
|
|
34
|
-
return { auth };
|
|
47
|
+
return { auth, authError };
|
|
35
48
|
}
|
|
36
49
|
}
|
package/src/storage/uploader.ts
CHANGED
|
@@ -51,6 +51,9 @@ export async function uploadBase64Image(
|
|
|
51
51
|
const storageRef = ref(storage, storagePath);
|
|
52
52
|
|
|
53
53
|
const response = await fetch(dataUrl);
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
throw new Error(`Failed to fetch base64 data: HTTP ${response.status} ${response.statusText}`);
|
|
56
|
+
}
|
|
54
57
|
const blob = await response.blob();
|
|
55
58
|
|
|
56
59
|
const metadata: UploadMetadata = {
|
|
@@ -82,6 +85,9 @@ export async function uploadFile(
|
|
|
82
85
|
const storageRef = ref(storage, storagePath);
|
|
83
86
|
|
|
84
87
|
const response = await fetch(uri);
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
throw new Error(`Failed to fetch file from URI: HTTP ${response.status} ${response.statusText}`);
|
|
90
|
+
}
|
|
85
91
|
const blob = await response.blob();
|
|
86
92
|
|
|
87
93
|
const contentType = options?.mimeType ?? "image/jpeg";
|