@umituz/react-native-firebase 2.4.43 → 2.4.45
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/auth/infrastructure/config/initializers/FirebaseAuthInitializer.ts +1 -6
- package/src/domains/auth/infrastructure/services/apple-auth.service.ts +15 -3
- package/src/domains/auth/infrastructure/services/google-oauth.service.ts +7 -7
- package/src/domains/auth/presentation/hooks/useGoogleOAuth.ts +16 -3
- package/src/domains/firestore/infrastructure/repositories/BasePaginatedRepository.ts +1 -2
- package/src/domains/firestore/infrastructure/repositories/BaseQueryRepository.ts +1 -48
- package/src/index.ts +0 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-firebase",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.45",
|
|
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",
|
|
@@ -35,12 +35,7 @@ export class FirebaseAuthInitializer {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
private static handleInitializationError(error: unknown, app: FirebaseApp): Auth | null {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (errorCode === 'auth/already-initialized') {
|
|
41
|
-
return this.getExistingAuth(app);
|
|
42
|
-
}
|
|
43
|
-
|
|
38
|
+
// Any initialization error: try to get existing auth instance
|
|
44
39
|
return this.getExistingAuth(app);
|
|
45
40
|
}
|
|
46
41
|
|
|
@@ -21,16 +21,24 @@ import { isNewUser as checkIsNewUser } from "../../domain/utils/user-metadata.ut
|
|
|
21
21
|
import { convertToOAuthResult } from "./utils/auth-result-converter.util";
|
|
22
22
|
|
|
23
23
|
// Conditional import - expo-apple-authentication is optional
|
|
24
|
-
|
|
24
|
+
interface AppleAuthModule {
|
|
25
|
+
isAvailableAsync: () => Promise<boolean>;
|
|
26
|
+
signInAsync: (options: {
|
|
27
|
+
requestedScopes: number[];
|
|
28
|
+
nonce: string;
|
|
29
|
+
}) => Promise<{ identityToken: string | null }>;
|
|
30
|
+
AppleAuthenticationScope: { FULL_NAME: number; EMAIL: number };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let AppleAuthentication: AppleAuthModule | null = null;
|
|
25
34
|
let isAppleAuthAvailable = false;
|
|
26
35
|
|
|
27
36
|
try {
|
|
28
37
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
29
|
-
AppleAuthentication = require("expo-apple-authentication");
|
|
38
|
+
AppleAuthentication = require("expo-apple-authentication") as AppleAuthModule;
|
|
30
39
|
isAppleAuthAvailable = true;
|
|
31
40
|
} catch {
|
|
32
41
|
// expo-apple-authentication not available - this is fine if not using Apple auth
|
|
33
|
-
console.info("expo-apple-authentication is not installed. Apple authentication will not be available.");
|
|
34
42
|
}
|
|
35
43
|
|
|
36
44
|
export class AppleAuthService {
|
|
@@ -67,6 +75,10 @@ export class AppleAuthService {
|
|
|
67
75
|
}
|
|
68
76
|
|
|
69
77
|
const result = await executeAuthOperation(async () => {
|
|
78
|
+
if (!AppleAuthentication) {
|
|
79
|
+
throw new Error("Apple authentication module not loaded");
|
|
80
|
+
}
|
|
81
|
+
|
|
70
82
|
const nonce = await generateNonce();
|
|
71
83
|
const hashedNonce = await hashNonce(nonce);
|
|
72
84
|
|
|
@@ -9,21 +9,21 @@ import type { GoogleAuthResult } from "./google-auth.types";
|
|
|
9
9
|
import { googleAuthService } from "./google-auth.service";
|
|
10
10
|
|
|
11
11
|
// Conditional import - expo-web-browser is optional
|
|
12
|
-
|
|
12
|
+
interface ExpoWebBrowserModule {
|
|
13
|
+
maybeCompleteAuthSession: () => { type: string };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let ExpoWebBrowser: ExpoWebBrowserModule | null = null;
|
|
13
17
|
let isExpoAuthAvailable = false;
|
|
14
18
|
|
|
15
19
|
try {
|
|
16
20
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
17
|
-
ExpoWebBrowser = require("expo-web-browser");
|
|
21
|
+
ExpoWebBrowser = require("expo-web-browser") as ExpoWebBrowserModule;
|
|
18
22
|
isExpoAuthAvailable = true;
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
if (ExpoWebBrowser?.maybeCompleteAuthSession) {
|
|
22
|
-
ExpoWebBrowser.maybeCompleteAuthSession();
|
|
23
|
-
}
|
|
24
|
+
ExpoWebBrowser.maybeCompleteAuthSession();
|
|
24
25
|
} catch {
|
|
25
26
|
// expo-web-browser not available - this is fine if not using Google OAuth
|
|
26
|
-
console.info("expo-web-browser is not installed. Google OAuth will not be available.");
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export interface GoogleOAuthConfig {
|
|
@@ -10,12 +10,25 @@ import { getFirebaseAuth } from "../../infrastructure/config/FirebaseAuthClient"
|
|
|
10
10
|
import type { GoogleOAuthConfig } from "../../infrastructure/services/google-oauth.service";
|
|
11
11
|
|
|
12
12
|
// Conditional import for expo-auth-session
|
|
13
|
-
|
|
13
|
+
interface AuthSessionResponse {
|
|
14
|
+
type: string;
|
|
15
|
+
authentication?: { idToken?: string } | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface ExpoAuthSessionModule {
|
|
19
|
+
useAuthRequest: (config: {
|
|
20
|
+
iosClientId: string;
|
|
21
|
+
webClientId: string;
|
|
22
|
+
androidClientId: string;
|
|
23
|
+
}) => [unknown, AuthSessionResponse | null, (() => Promise<AuthSessionResponse>) | null];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let ExpoAuthSession: ExpoAuthSessionModule | null = null;
|
|
14
27
|
let isExpoAuthAvailable = false;
|
|
15
28
|
|
|
16
29
|
try {
|
|
17
30
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
18
|
-
ExpoAuthSession = require("expo-auth-session/providers/google");
|
|
31
|
+
ExpoAuthSession = require("expo-auth-session/providers/google") as ExpoAuthSessionModule;
|
|
19
32
|
isExpoAuthAvailable = true;
|
|
20
33
|
} catch {
|
|
21
34
|
// expo-auth-session not available - hook will return unavailable state
|
|
@@ -50,7 +63,7 @@ export function useGoogleOAuth(config?: GoogleOAuthConfig): UseGoogleOAuthResult
|
|
|
50
63
|
|
|
51
64
|
// Call the Hook directly (only valid in React component)
|
|
52
65
|
// If expo-auth-session is not available, these will be null
|
|
53
|
-
const [request, response, promptAsync] = isExpoAuthAvailable && ExpoAuthSession
|
|
66
|
+
const [request, response, promptAsync] = isExpoAuthAvailable && ExpoAuthSession
|
|
54
67
|
? ExpoAuthSession.useAuthRequest({
|
|
55
68
|
iosClientId: config?.iosClientId || PLACEHOLDER_CLIENT_ID,
|
|
56
69
|
webClientId: config?.webClientId || PLACEHOLDER_CLIENT_ID,
|
|
@@ -82,12 +82,11 @@ export abstract class BasePaginatedRepository extends BaseQueryRepository {
|
|
|
82
82
|
|
|
83
83
|
return this.executeQuery(
|
|
84
84
|
collectionName,
|
|
85
|
-
q,
|
|
86
85
|
async () => {
|
|
87
86
|
const snapshot = await getDocs(q);
|
|
88
87
|
return snapshot.docs;
|
|
89
88
|
},
|
|
90
|
-
false,
|
|
89
|
+
false,
|
|
91
90
|
uniqueKey
|
|
92
91
|
);
|
|
93
92
|
}
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
* Extends BaseRepository with query-specific functionality.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { Query } from "firebase/firestore";
|
|
9
|
-
import { quotaTrackingMiddleware } from "../middleware/QuotaTrackingMiddleware";
|
|
10
8
|
import { queryDeduplicationMiddleware } from "../middleware/QueryDeduplicationMiddleware";
|
|
11
9
|
import { BaseRepository } from "./BaseRepository";
|
|
12
10
|
|
|
@@ -14,72 +12,27 @@ export abstract class BaseQueryRepository extends BaseRepository {
|
|
|
14
12
|
/**
|
|
15
13
|
* Execute query with deduplication and quota tracking
|
|
16
14
|
* Prevents duplicate queries and tracks quota usage
|
|
17
|
-
*
|
|
18
|
-
* @param collection - Collection name
|
|
19
|
-
* @param query - Firestore query (kept for interface compatibility)
|
|
20
|
-
* @param queryFn - Function to execute the query
|
|
21
|
-
* @param cached - Whether the result is from cache
|
|
22
|
-
* @param uniqueKey - Unique key for deduplication (critical for correct caching)
|
|
23
|
-
* @returns Query result
|
|
24
15
|
*/
|
|
25
16
|
protected async executeQuery<T>(
|
|
26
17
|
collection: string,
|
|
27
|
-
_query: Query,
|
|
28
18
|
queryFn: () => Promise<T>,
|
|
29
19
|
cached: boolean = false,
|
|
30
20
|
uniqueKey?: string
|
|
31
21
|
): Promise<T> {
|
|
32
|
-
// Use provided uniqueKey, or generate a stable key based on collection
|
|
33
|
-
// IMPORTANT: Don't use Date.now() as it defeats deduplication!
|
|
34
22
|
const safeKey = uniqueKey || `${collection}_query`;
|
|
35
23
|
|
|
36
24
|
const queryKey = {
|
|
37
25
|
collection,
|
|
38
|
-
filters: safeKey,
|
|
26
|
+
filters: safeKey,
|
|
39
27
|
limit: undefined,
|
|
40
28
|
orderBy: undefined,
|
|
41
29
|
};
|
|
42
30
|
|
|
43
31
|
return queryDeduplicationMiddleware.deduplicate(queryKey, async () => {
|
|
44
|
-
// Execute the query function
|
|
45
32
|
const result = await queryFn();
|
|
46
|
-
|
|
47
|
-
// Track the operation after successful execution
|
|
48
|
-
// We calculate count based on result if possible, otherwise default to 1 (for list/count queries)
|
|
49
33
|
const count = Array.isArray(result) ? result.length : 1;
|
|
50
|
-
|
|
51
34
|
this.trackRead(collection, count, cached);
|
|
52
|
-
|
|
53
35
|
return result;
|
|
54
36
|
});
|
|
55
37
|
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Track read operation
|
|
59
|
-
*
|
|
60
|
-
* @param collection - Collection name
|
|
61
|
-
* @param count - Number of documents read
|
|
62
|
-
* @param cached - Whether the result is from cache
|
|
63
|
-
*/
|
|
64
|
-
protected trackRead(
|
|
65
|
-
collection: string,
|
|
66
|
-
count: number = 1,
|
|
67
|
-
cached: boolean = false,
|
|
68
|
-
): void {
|
|
69
|
-
quotaTrackingMiddleware.trackRead(collection, count, cached);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
protected trackWrite(
|
|
73
|
-
collection: string,
|
|
74
|
-
count: number = 1,
|
|
75
|
-
): void {
|
|
76
|
-
quotaTrackingMiddleware.trackWrite(collection, count);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
protected trackDelete(
|
|
80
|
-
collection: string,
|
|
81
|
-
count: number = 1,
|
|
82
|
-
): void {
|
|
83
|
-
quotaTrackingMiddleware.trackDelete(collection, count);
|
|
84
|
-
}
|
|
85
38
|
}
|
package/src/index.ts
CHANGED
|
@@ -34,21 +34,6 @@ export type {
|
|
|
34
34
|
|
|
35
35
|
export type { FirebaseApp } from "./shared/infrastructure/config/initializers/FirebaseAppInitializer";
|
|
36
36
|
|
|
37
|
-
import { FirebaseClientSingleton } from "./shared/infrastructure/config/clients/FirebaseClientSingleton";
|
|
38
|
-
|
|
39
|
-
function getFirebaseClientSafe(): FirebaseClientSingleton | null {
|
|
40
|
-
try {
|
|
41
|
-
return FirebaseClientSingleton.getInstance();
|
|
42
|
-
} catch {
|
|
43
|
-
if (__DEV__) {
|
|
44
|
-
console.warn('[Firebase] Could not create Firebase client singleton — Firebase may not be configured.');
|
|
45
|
-
}
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export const firebaseClient = getFirebaseClientSafe();
|
|
51
|
-
|
|
52
37
|
// Type Guards
|
|
53
38
|
export {
|
|
54
39
|
isFirestoreError,
|