@umituz/react-native-firebase 1.13.55 → 1.13.57
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-firebase",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.57",
|
|
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",
|
|
@@ -9,27 +9,9 @@ import { checkAuthState, verifyUserId } from "./auth-utils.service";
|
|
|
9
9
|
declare const __DEV__: boolean;
|
|
10
10
|
|
|
11
11
|
export interface FirestoreQueryOptions {
|
|
12
|
-
/**
|
|
13
|
-
* Skip query if user is anonymous/guest
|
|
14
|
-
* Default: true (guest users don't have Firestore data)
|
|
15
|
-
*/
|
|
16
12
|
readonly skipForGuest?: boolean;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Skip query if user is not authenticated
|
|
20
|
-
* Default: true
|
|
21
|
-
*/
|
|
22
13
|
readonly skipIfNotAuthenticated?: boolean;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Verify userId matches current user
|
|
26
|
-
* Default: true
|
|
27
|
-
*/
|
|
28
14
|
readonly verifyUserId?: boolean;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* User ID to verify (required if verifyUserId is true)
|
|
32
|
-
*/
|
|
33
15
|
readonly userId?: string;
|
|
34
16
|
}
|
|
35
17
|
|
|
@@ -48,9 +30,25 @@ export interface FirestoreQueryResult {
|
|
|
48
30
|
readonly isAuthenticated: boolean;
|
|
49
31
|
}
|
|
50
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Create query result helper
|
|
35
|
+
*/
|
|
36
|
+
function createResult(
|
|
37
|
+
shouldSkip: boolean,
|
|
38
|
+
authState: ReturnType<typeof checkAuthState>,
|
|
39
|
+
reason?: FirestoreQuerySkipReason
|
|
40
|
+
): FirestoreQueryResult {
|
|
41
|
+
return {
|
|
42
|
+
shouldSkip,
|
|
43
|
+
reason,
|
|
44
|
+
userId: authState.userId,
|
|
45
|
+
isAnonymous: authState.isAnonymous,
|
|
46
|
+
isAuthenticated: authState.isAuthenticated,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
51
50
|
/**
|
|
52
51
|
* Check if Firestore query should be skipped
|
|
53
|
-
* Centralized logic for all Firestore operations
|
|
54
52
|
*/
|
|
55
53
|
export function shouldSkipFirestoreQuery(
|
|
56
54
|
auth: Auth | null,
|
|
@@ -64,79 +62,30 @@ export function shouldSkipFirestoreQuery(
|
|
|
64
62
|
} = options;
|
|
65
63
|
|
|
66
64
|
try {
|
|
67
|
-
// No auth available
|
|
68
65
|
if (!auth) {
|
|
69
|
-
return
|
|
70
|
-
shouldSkip: true,
|
|
71
|
-
reason: "no_auth",
|
|
72
|
-
userId: null,
|
|
73
|
-
isAnonymous: false,
|
|
74
|
-
isAuthenticated: false,
|
|
75
|
-
};
|
|
66
|
+
return createResult(true, checkAuthState(null), "no_auth");
|
|
76
67
|
}
|
|
77
68
|
|
|
78
|
-
// Check auth state
|
|
79
69
|
const authState = checkAuthState(auth);
|
|
80
70
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (skipIfNotAuthenticated) {
|
|
84
|
-
return {
|
|
85
|
-
shouldSkip: true,
|
|
86
|
-
reason: "not_authenticated",
|
|
87
|
-
userId: null,
|
|
88
|
-
isAnonymous: false,
|
|
89
|
-
isAuthenticated: false,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
71
|
+
if (!authState.isAuthenticated && skipIfNotAuthenticated) {
|
|
72
|
+
return createResult(true, authState, "not_authenticated");
|
|
92
73
|
}
|
|
93
74
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (skipForGuest) {
|
|
97
|
-
return {
|
|
98
|
-
shouldSkip: true,
|
|
99
|
-
reason: "is_guest",
|
|
100
|
-
userId: authState.userId,
|
|
101
|
-
isAnonymous: true,
|
|
102
|
-
isAuthenticated: false,
|
|
103
|
-
};
|
|
104
|
-
}
|
|
75
|
+
if (authState.isAnonymous && skipForGuest) {
|
|
76
|
+
return createResult(true, authState, "is_guest");
|
|
105
77
|
}
|
|
106
78
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (!verifyUserId(auth, userId)) {
|
|
110
|
-
return {
|
|
111
|
-
shouldSkip: true,
|
|
112
|
-
reason: "user_id_mismatch",
|
|
113
|
-
userId: authState.userId,
|
|
114
|
-
isAnonymous: authState.isAnonymous,
|
|
115
|
-
isAuthenticated: authState.isAuthenticated,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
79
|
+
if (shouldVerify && userId && !verifyUserId(auth, userId)) {
|
|
80
|
+
return createResult(true, authState, "user_id_mismatch");
|
|
118
81
|
}
|
|
119
82
|
|
|
120
|
-
|
|
121
|
-
return {
|
|
122
|
-
shouldSkip: false,
|
|
123
|
-
userId: authState.userId,
|
|
124
|
-
isAnonymous: authState.isAnonymous,
|
|
125
|
-
isAuthenticated: authState.isAuthenticated,
|
|
126
|
-
};
|
|
83
|
+
return createResult(false, authState);
|
|
127
84
|
} catch (error) {
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
console.error("[FirestoreUtils] Error checking if query should be skipped", error);
|
|
85
|
+
if (__DEV__) {
|
|
86
|
+
console.error("[FirestoreUtils] Error checking query", error);
|
|
131
87
|
}
|
|
132
|
-
|
|
133
|
-
return {
|
|
134
|
-
shouldSkip: true,
|
|
135
|
-
reason: "invalid_options",
|
|
136
|
-
userId: null,
|
|
137
|
-
isAnonymous: false,
|
|
138
|
-
isAuthenticated: false,
|
|
139
|
-
};
|
|
88
|
+
return createResult(true, checkAuthState(null), "invalid_options");
|
|
140
89
|
}
|
|
141
90
|
}
|
|
142
91
|
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useSocialAuth Hook
|
|
3
3
|
* Provides Google and Apple Sign-In functionality
|
|
4
|
-
*
|
|
5
|
-
* Note: This hook handles the Firebase authentication part.
|
|
6
|
-
* The OAuth flow (expo-auth-session for Google) should be set up in the consuming app.
|
|
7
4
|
*/
|
|
8
5
|
|
|
9
6
|
import { useState, useCallback, useEffect } from "react";
|
|
@@ -14,54 +11,45 @@ import {
|
|
|
14
11
|
} from "../../infrastructure/services/google-auth.service";
|
|
15
12
|
import { appleAuthService } from "../../infrastructure/services/apple-auth.service";
|
|
16
13
|
|
|
17
|
-
/**
|
|
18
|
-
* Social auth configuration
|
|
19
|
-
*/
|
|
20
14
|
export interface SocialAuthConfig {
|
|
21
15
|
google?: GoogleAuthConfig;
|
|
22
16
|
apple?: { enabled: boolean };
|
|
23
17
|
}
|
|
24
18
|
|
|
25
|
-
/**
|
|
26
|
-
* Social auth result
|
|
27
|
-
*/
|
|
28
19
|
export interface SocialAuthResult {
|
|
29
20
|
success: boolean;
|
|
30
21
|
isNewUser?: boolean;
|
|
31
22
|
error?: string;
|
|
32
23
|
}
|
|
33
24
|
|
|
34
|
-
/**
|
|
35
|
-
* Hook result
|
|
36
|
-
*/
|
|
37
25
|
export interface UseSocialAuthResult {
|
|
38
|
-
/** Sign in with Google using ID token (call after OAuth flow) */
|
|
39
26
|
signInWithGoogleToken: (idToken: string) => Promise<SocialAuthResult>;
|
|
40
|
-
/** Sign in with Apple (handles full flow) */
|
|
41
27
|
signInWithApple: () => Promise<SocialAuthResult>;
|
|
42
|
-
/** Whether Google is loading */
|
|
43
28
|
googleLoading: boolean;
|
|
44
|
-
/** Whether Apple is loading */
|
|
45
29
|
appleLoading: boolean;
|
|
46
|
-
/** Whether Google is configured */
|
|
47
30
|
googleConfigured: boolean;
|
|
48
|
-
/** Whether Apple is available */
|
|
49
31
|
appleAvailable: boolean;
|
|
50
32
|
}
|
|
51
33
|
|
|
52
34
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* Usage:
|
|
56
|
-
* 1. For Google: Set up expo-auth-session in your app, get idToken, then call signInWithGoogleToken
|
|
57
|
-
* 2. For Apple: Just call signInWithApple (handles complete flow)
|
|
35
|
+
* Common sign-in wrapper
|
|
58
36
|
*/
|
|
37
|
+
async function signInWrapper(
|
|
38
|
+
signInFn: () => Promise<{ success: boolean; isNewUser?: boolean; error?: string }>
|
|
39
|
+
): Promise<SocialAuthResult> {
|
|
40
|
+
const auth = getFirebaseAuth();
|
|
41
|
+
if (!auth) {
|
|
42
|
+
return { success: false, error: "Firebase Auth not initialized" };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return signInFn();
|
|
46
|
+
}
|
|
47
|
+
|
|
59
48
|
export function useSocialAuth(config?: SocialAuthConfig): UseSocialAuthResult {
|
|
60
49
|
const [googleLoading, setGoogleLoading] = useState(false);
|
|
61
50
|
const [appleLoading, setAppleLoading] = useState(false);
|
|
62
51
|
const [appleAvailable, setAppleAvailable] = useState(false);
|
|
63
52
|
|
|
64
|
-
// Configure Google Auth
|
|
65
53
|
const googleConfig = config?.google;
|
|
66
54
|
const googleConfigured = !!(
|
|
67
55
|
googleConfig?.webClientId ||
|
|
@@ -69,14 +57,12 @@ export function useSocialAuth(config?: SocialAuthConfig): UseSocialAuthResult {
|
|
|
69
57
|
googleConfig?.androidClientId
|
|
70
58
|
);
|
|
71
59
|
|
|
72
|
-
// Configure Google service on mount
|
|
73
60
|
useEffect(() => {
|
|
74
61
|
if (googleConfig) {
|
|
75
62
|
googleAuthService.configure(googleConfig);
|
|
76
63
|
}
|
|
77
64
|
}, [googleConfig]);
|
|
78
65
|
|
|
79
|
-
// Check Apple availability on mount
|
|
80
66
|
useEffect(() => {
|
|
81
67
|
const checkApple = async () => {
|
|
82
68
|
const available = await appleAuthService.isAvailable();
|
|
@@ -85,7 +71,6 @@ export function useSocialAuth(config?: SocialAuthConfig): UseSocialAuthResult {
|
|
|
85
71
|
checkApple();
|
|
86
72
|
}, [config?.apple?.enabled]);
|
|
87
73
|
|
|
88
|
-
// Sign in with Google using ID token
|
|
89
74
|
const signInWithGoogleToken = useCallback(
|
|
90
75
|
async (idToken: string): Promise<SocialAuthResult> => {
|
|
91
76
|
if (!googleConfigured) {
|
|
@@ -94,21 +79,9 @@ export function useSocialAuth(config?: SocialAuthConfig): UseSocialAuthResult {
|
|
|
94
79
|
|
|
95
80
|
setGoogleLoading(true);
|
|
96
81
|
try {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return { success: false, error: "Firebase Auth not initialized" };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const signInResult = await googleAuthService.signInWithIdToken(
|
|
103
|
-
auth,
|
|
104
|
-
idToken,
|
|
82
|
+
return await signInWrapper(() =>
|
|
83
|
+
googleAuthService.signInWithIdToken(getFirebaseAuth()!, idToken)
|
|
105
84
|
);
|
|
106
|
-
|
|
107
|
-
return {
|
|
108
|
-
success: signInResult.success,
|
|
109
|
-
isNewUser: signInResult.isNewUser,
|
|
110
|
-
error: signInResult.error,
|
|
111
|
-
};
|
|
112
85
|
} catch (error) {
|
|
113
86
|
return {
|
|
114
87
|
success: false,
|
|
@@ -118,10 +91,9 @@ export function useSocialAuth(config?: SocialAuthConfig): UseSocialAuthResult {
|
|
|
118
91
|
setGoogleLoading(false);
|
|
119
92
|
}
|
|
120
93
|
},
|
|
121
|
-
[googleConfigured]
|
|
94
|
+
[googleConfigured]
|
|
122
95
|
);
|
|
123
96
|
|
|
124
|
-
// Sign in with Apple
|
|
125
97
|
const signInWithApple = useCallback(async (): Promise<SocialAuthResult> => {
|
|
126
98
|
if (!appleAvailable) {
|
|
127
99
|
return { success: false, error: "Apple Sign-In is not available" };
|
|
@@ -129,18 +101,9 @@ export function useSocialAuth(config?: SocialAuthConfig): UseSocialAuthResult {
|
|
|
129
101
|
|
|
130
102
|
setAppleLoading(true);
|
|
131
103
|
try {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const result = await appleAuthService.signIn(auth);
|
|
138
|
-
|
|
139
|
-
return {
|
|
140
|
-
success: result.success,
|
|
141
|
-
isNewUser: result.isNewUser,
|
|
142
|
-
error: result.error,
|
|
143
|
-
};
|
|
104
|
+
return await signInWrapper(() =>
|
|
105
|
+
appleAuthService.signIn(getFirebaseAuth()!)
|
|
106
|
+
);
|
|
144
107
|
} catch (error) {
|
|
145
108
|
return {
|
|
146
109
|
success: false,
|
package/src/firestore/index.ts
CHANGED
|
@@ -3,20 +3,14 @@
|
|
|
3
3
|
* Domain-Driven Design (DDD) Architecture
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
//
|
|
7
|
-
// DOMAIN LAYER - Errors
|
|
8
|
-
// =============================================================================
|
|
9
|
-
|
|
6
|
+
// Domain Errors
|
|
10
7
|
export {
|
|
11
8
|
FirebaseFirestoreError,
|
|
12
9
|
FirebaseFirestoreInitializationError,
|
|
13
10
|
FirebaseFirestoreQuotaError,
|
|
14
11
|
} from './domain/errors/FirebaseFirestoreError';
|
|
15
12
|
|
|
16
|
-
//
|
|
17
|
-
// INFRASTRUCTURE LAYER - Firestore Client
|
|
18
|
-
// =============================================================================
|
|
19
|
-
|
|
13
|
+
// Firestore Client
|
|
20
14
|
export {
|
|
21
15
|
initializeFirestore,
|
|
22
16
|
getFirestore,
|
|
@@ -25,21 +19,14 @@ export {
|
|
|
25
19
|
resetFirestoreClient,
|
|
26
20
|
firestoreClient,
|
|
27
21
|
} from './infrastructure/config/FirestoreClient';
|
|
28
|
-
|
|
29
22
|
export type { Firestore } from './infrastructure/config/FirestoreClient';
|
|
30
23
|
|
|
31
|
-
//
|
|
32
|
-
// INFRASTRUCTURE LAYER - BaseRepository
|
|
33
|
-
// =============================================================================
|
|
34
|
-
|
|
24
|
+
// Repositories
|
|
35
25
|
export { BaseRepository } from './infrastructure/repositories/BaseRepository';
|
|
36
26
|
export { BaseQueryRepository } from './infrastructure/repositories/BaseQueryRepository';
|
|
37
27
|
export { BasePaginatedRepository } from './infrastructure/repositories/BasePaginatedRepository';
|
|
38
28
|
|
|
39
|
-
//
|
|
40
|
-
// UTILS - Date Utilities
|
|
41
|
-
// =============================================================================
|
|
42
|
-
|
|
29
|
+
// Date Utilities
|
|
43
30
|
export {
|
|
44
31
|
isoToTimestamp,
|
|
45
32
|
timestampToISO,
|
|
@@ -47,56 +34,38 @@ export {
|
|
|
47
34
|
getCurrentISOString,
|
|
48
35
|
} from './utils/dateUtils';
|
|
49
36
|
|
|
50
|
-
//
|
|
51
|
-
// UTILS - Query Builder
|
|
52
|
-
// =============================================================================
|
|
53
|
-
|
|
37
|
+
// Query Builder
|
|
54
38
|
export {
|
|
55
39
|
buildQuery,
|
|
56
40
|
createInFilter,
|
|
57
41
|
createEqualFilter,
|
|
58
42
|
} from './utils/query-builder';
|
|
59
|
-
|
|
60
43
|
export type {
|
|
61
44
|
QueryBuilderOptions,
|
|
62
45
|
FieldFilter,
|
|
63
46
|
} from './utils/query-builder';
|
|
64
47
|
|
|
65
|
-
//
|
|
66
|
-
// UTILS - Pagination
|
|
67
|
-
// =============================================================================
|
|
68
|
-
|
|
48
|
+
// Pagination
|
|
69
49
|
export {
|
|
70
50
|
PaginationHelper,
|
|
71
51
|
createPaginationHelper,
|
|
72
52
|
} from './utils/pagination.helper';
|
|
73
|
-
|
|
74
53
|
export type {
|
|
75
54
|
PaginatedResult,
|
|
76
55
|
PaginationParams,
|
|
77
56
|
} from './types/pagination.types';
|
|
78
|
-
|
|
79
57
|
export { EMPTY_PAGINATED_RESULT } from './types/pagination.types';
|
|
80
58
|
|
|
81
|
-
//
|
|
82
|
-
// UTILS - Document Mapper
|
|
83
|
-
// =============================================================================
|
|
84
|
-
|
|
59
|
+
// Document Mapper
|
|
85
60
|
export {
|
|
86
61
|
DocumentMapperHelper,
|
|
87
62
|
createDocumentMapper,
|
|
88
63
|
} from './utils/document-mapper.helper';
|
|
89
64
|
|
|
90
|
-
//
|
|
91
|
-
// UTILS - Path Resolver
|
|
92
|
-
// =============================================================================
|
|
93
|
-
|
|
65
|
+
// Path Resolver
|
|
94
66
|
export { FirestorePathResolver } from './utils/path-resolver';
|
|
95
67
|
|
|
96
|
-
//
|
|
97
|
-
// DOMAIN LAYER - Constants
|
|
98
|
-
// =============================================================================
|
|
99
|
-
|
|
68
|
+
// Domain Constants
|
|
100
69
|
export {
|
|
101
70
|
FREE_TIER_LIMITS,
|
|
102
71
|
QUOTA_THRESHOLDS,
|
|
@@ -105,60 +74,43 @@ export {
|
|
|
105
74
|
getRemainingQuota,
|
|
106
75
|
} from './domain/constants/QuotaLimits';
|
|
107
76
|
|
|
108
|
-
//
|
|
109
|
-
// DOMAIN LAYER - Entities
|
|
110
|
-
// =============================================================================
|
|
111
|
-
|
|
77
|
+
// Domain Entities
|
|
112
78
|
export type {
|
|
113
79
|
QuotaMetrics,
|
|
114
80
|
QuotaLimits,
|
|
115
81
|
QuotaStatus,
|
|
116
82
|
} from './domain/entities/QuotaMetrics';
|
|
117
|
-
|
|
118
83
|
export type {
|
|
119
84
|
RequestLog,
|
|
120
85
|
RequestStats,
|
|
121
86
|
RequestType,
|
|
122
87
|
} from './domain/entities/RequestLog';
|
|
123
88
|
|
|
124
|
-
//
|
|
125
|
-
// DOMAIN LAYER - Services
|
|
126
|
-
// =============================================================================
|
|
127
|
-
|
|
89
|
+
// Domain Services
|
|
128
90
|
export { QuotaCalculator } from './domain/services/QuotaCalculator';
|
|
129
91
|
|
|
130
|
-
//
|
|
131
|
-
// UTILS - Quota Error Detection
|
|
132
|
-
// =============================================================================
|
|
133
|
-
|
|
92
|
+
// Quota Error Detection
|
|
134
93
|
export {
|
|
135
94
|
isQuotaError,
|
|
136
95
|
isRetryableError,
|
|
137
96
|
getQuotaErrorMessage,
|
|
138
97
|
} from './utils/quota-error-detector.util';
|
|
139
98
|
|
|
140
|
-
//
|
|
141
|
-
// INFRASTRUCTURE LAYER - Middleware
|
|
142
|
-
// =============================================================================
|
|
143
|
-
|
|
99
|
+
// Middleware
|
|
144
100
|
export {
|
|
145
101
|
QueryDeduplicationMiddleware,
|
|
146
102
|
queryDeduplicationMiddleware,
|
|
147
103
|
} from './infrastructure/middleware/QueryDeduplicationMiddleware';
|
|
148
|
-
|
|
149
104
|
export {
|
|
150
105
|
QuotaTrackingMiddleware,
|
|
151
106
|
quotaTrackingMiddleware,
|
|
152
107
|
} from './infrastructure/middleware/QuotaTrackingMiddleware';
|
|
153
108
|
|
|
154
|
-
//
|
|
155
|
-
// INFRASTRUCTURE LAYER - Services
|
|
156
|
-
// =============================================================================
|
|
157
|
-
|
|
109
|
+
// Services
|
|
158
110
|
export {
|
|
159
111
|
RequestLoggerService,
|
|
160
112
|
requestLoggerService,
|
|
161
113
|
} from './infrastructure/services/RequestLoggerService';
|
|
162
114
|
|
|
163
|
-
// Re-export Firestore types
|
|
115
|
+
// Re-export Firestore types
|
|
164
116
|
export type { Timestamp } from 'firebase/firestore';
|
|
@@ -4,168 +4,81 @@
|
|
|
4
4
|
* Automatically loads Firebase configuration from:
|
|
5
5
|
* 1. expo-constants (Constants.expoConfig?.extra)
|
|
6
6
|
* 2. Environment variables (process.env)
|
|
7
|
-
*
|
|
8
|
-
* This allows zero-configuration Firebase initialization.
|
|
9
7
|
*/
|
|
10
8
|
|
|
11
9
|
import type { FirebaseConfig } from '../../domain/value-objects/FirebaseConfig';
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
11
|
+
type ConfigKey = 'apiKey' | 'authDomain' | 'projectId' | 'storageBucket' | 'messagingSenderId' | 'appId';
|
|
12
|
+
|
|
13
|
+
const EXPO_PREFIX = 'EXPO_PUBLIC_';
|
|
14
|
+
const ENV_KEYS: Record<ConfigKey, string[]> = {
|
|
15
|
+
apiKey: ['FIREBASE_API_KEY'],
|
|
16
|
+
authDomain: ['FIREBASE_AUTH_DOMAIN'],
|
|
17
|
+
projectId: ['FIREBASE_PROJECT_ID'],
|
|
18
|
+
storageBucket: ['FIREBASE_STORAGE_BUCKET'],
|
|
19
|
+
messagingSenderId: ['FIREBASE_MESSAGING_SENDER_ID'],
|
|
20
|
+
appId: ['FIREBASE_APP_ID'],
|
|
21
|
+
};
|
|
24
22
|
|
|
25
23
|
/**
|
|
26
|
-
*
|
|
24
|
+
* Get environment variable value
|
|
27
25
|
*/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
Constants = require('expo-constants');
|
|
36
|
-
} catch {
|
|
37
|
-
// expo-constants not available
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const expoConfig = Constants?.expoConfig || Constants?.default?.expoConfig;
|
|
41
|
-
this.extra = expoConfig?.extra || {};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
getApiKey(): string {
|
|
45
|
-
return this.extra?.firebaseApiKey || '';
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
getAuthDomain(): string {
|
|
49
|
-
return this.extra?.firebaseAuthDomain || '';
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
getProjectId(): string {
|
|
53
|
-
return this.extra?.firebaseProjectId || '';
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
getStorageBucket(): string {
|
|
57
|
-
return this.extra?.firebaseStorageBucket || '';
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
getMessagingSenderId(): string {
|
|
61
|
-
return this.extra?.firebaseMessagingSenderId || '';
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
getAppId(): string {
|
|
65
|
-
return this.extra?.firebaseAppId || '';
|
|
26
|
+
function getEnvValue(key: ConfigKey): string {
|
|
27
|
+
const keys = ENV_KEYS[key];
|
|
28
|
+
for (const envKey of keys) {
|
|
29
|
+
const value = process.env[`${EXPO_PREFIX}${envKey}`] || process.env[envKey];
|
|
30
|
+
if (value?.trim()) return value;
|
|
66
31
|
}
|
|
32
|
+
return '';
|
|
67
33
|
}
|
|
68
34
|
|
|
69
35
|
/**
|
|
70
|
-
*
|
|
36
|
+
* Load configuration from expo-constants
|
|
71
37
|
*/
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
getAuthDomain(): string {
|
|
80
|
-
return process.env.EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN ||
|
|
81
|
-
process.env.FIREBASE_AUTH_DOMAIN ||
|
|
82
|
-
'';
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
getProjectId(): string {
|
|
86
|
-
return process.env.EXPO_PUBLIC_FIREBASE_PROJECT_ID ||
|
|
87
|
-
process.env.FIREBASE_PROJECT_ID ||
|
|
88
|
-
'';
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
getStorageBucket(): string {
|
|
92
|
-
return process.env.EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET ||
|
|
93
|
-
process.env.FIREBASE_STORAGE_BUCKET ||
|
|
94
|
-
'';
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
getMessagingSenderId(): string {
|
|
98
|
-
return process.env.EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID ||
|
|
99
|
-
process.env.FIREBASE_MESSAGING_SENDER_ID ||
|
|
100
|
-
'';
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
getAppId(): string {
|
|
104
|
-
return process.env.EXPO_PUBLIC_FIREBASE_APP_ID ||
|
|
105
|
-
process.env.FIREBASE_APP_ID ||
|
|
106
|
-
'';
|
|
38
|
+
function loadExpoConfig(): Record<string, string> {
|
|
39
|
+
try {
|
|
40
|
+
const Constants = require('expo-constants');
|
|
41
|
+
const expoConfig = Constants?.expoConfig || Constants?.default?.expoConfig;
|
|
42
|
+
return expoConfig?.extra || {};
|
|
43
|
+
} catch {
|
|
44
|
+
return {};
|
|
107
45
|
}
|
|
108
46
|
}
|
|
109
47
|
|
|
110
48
|
/**
|
|
111
|
-
*
|
|
49
|
+
* Get config value from expo constants
|
|
112
50
|
*/
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Get configuration value from first available source
|
|
125
|
-
*/
|
|
126
|
-
private getValue(getter: (source: ConfigSource) => string): string {
|
|
127
|
-
for (const source of this.sources) {
|
|
128
|
-
const value = getter(source);
|
|
129
|
-
if (value && value.trim() !== '') {
|
|
130
|
-
return value;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return '';
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Build Firebase configuration from all sources
|
|
138
|
-
*/
|
|
139
|
-
buildConfig(): Partial<FirebaseConfig> {
|
|
140
|
-
return {
|
|
141
|
-
apiKey: this.getValue(source => source.getApiKey()),
|
|
142
|
-
authDomain: this.getValue(source => source.getAuthDomain()),
|
|
143
|
-
projectId: this.getValue(source => source.getProjectId()),
|
|
144
|
-
storageBucket: this.getValue(source => source.getStorageBucket()),
|
|
145
|
-
messagingSenderId: this.getValue(source => source.getMessagingSenderId()),
|
|
146
|
-
appId: this.getValue(source => source.getAppId()),
|
|
147
|
-
};
|
|
148
|
-
}
|
|
51
|
+
function getExpoValue(key: ConfigKey, expoConfig: Record<string, string>): string {
|
|
52
|
+
const mapping: Record<ConfigKey, string> = {
|
|
53
|
+
apiKey: 'firebaseApiKey',
|
|
54
|
+
authDomain: 'firebaseAuthDomain',
|
|
55
|
+
projectId: 'firebaseProjectId',
|
|
56
|
+
storageBucket: 'firebaseStorageBucket',
|
|
57
|
+
messagingSenderId: 'firebaseMessagingSenderId',
|
|
58
|
+
appId: 'firebaseAppId',
|
|
59
|
+
};
|
|
60
|
+
return expoConfig[mapping[key]] || '';
|
|
149
61
|
}
|
|
150
62
|
|
|
151
63
|
/**
|
|
152
64
|
* Load Firebase configuration from Constants and environment variables
|
|
153
|
-
* Returns null if no valid configuration is found
|
|
154
65
|
*/
|
|
155
66
|
export function loadFirebaseConfig(): FirebaseConfig | null {
|
|
156
|
-
const
|
|
157
|
-
|
|
67
|
+
const expoConfig = loadExpoConfig();
|
|
68
|
+
|
|
69
|
+
const config: Partial<Record<ConfigKey, string>> = {};
|
|
70
|
+
const keys: ConfigKey[] = ['apiKey', 'authDomain', 'projectId', 'storageBucket', 'messagingSenderId', 'appId'];
|
|
71
|
+
|
|
72
|
+
for (const key of keys) {
|
|
73
|
+
const expoValue = getExpoValue(key, expoConfig);
|
|
74
|
+
config[key] = expoValue || getEnvValue(key);
|
|
75
|
+
}
|
|
158
76
|
|
|
159
|
-
//
|
|
160
|
-
if (
|
|
161
|
-
|
|
162
|
-
config.projectId &&
|
|
163
|
-
config.apiKey.trim() !== '' &&
|
|
164
|
-
config.projectId.trim() !== ''
|
|
165
|
-
) {
|
|
166
|
-
return config as FirebaseConfig;
|
|
77
|
+
// Validate required fields
|
|
78
|
+
if (!config.apiKey?.trim() || !config.projectId?.trim()) {
|
|
79
|
+
return null;
|
|
167
80
|
}
|
|
168
81
|
|
|
169
|
-
return
|
|
82
|
+
return config as FirebaseConfig;
|
|
170
83
|
}
|
|
171
84
|
|