@umituz/react-native-firebase 1.13.111 → 1.13.116

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.111",
3
+ "version": "1.13.116",
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",
@@ -62,6 +62,7 @@
62
62
  "expo-apple-authentication": "^8.0.8",
63
63
  "expo-application": "^7.0.8",
64
64
  "expo-clipboard": "^8.0.8",
65
+ "expo-constants": "^18.0.13",
65
66
  "expo-crypto": "^15.0.8",
66
67
  "expo-device": "^8.0.10",
67
68
  "expo-file-system": "^19.0.21",
@@ -120,6 +120,8 @@ export {
120
120
  withFirestoreBool,
121
121
  createErrorResult,
122
122
  createSuccessResult,
123
+ runTransaction,
124
+ serverTimestamp,
123
125
  } from './utils/firestore-helper';
124
126
  export type { FirestoreResult, NoDbResult } from './utils/firestore-helper';
125
127
 
@@ -33,7 +33,7 @@ export class FirebaseFirestoreInitializer {
33
33
  return initializeFirestore(app, {
34
34
  localCache: memoryLocalCache(),
35
35
  });
36
- } catch (error: unknown) {
36
+ } catch {
37
37
  // If already initialized, get existing instance
38
38
  return getFirestore(app);
39
39
  }
@@ -42,7 +42,9 @@ export abstract class BasePaginatedRepository extends BaseQueryRepository {
42
42
  limit(fetchLimit),
43
43
  );
44
44
 
45
+ let cursorKey = 'start';
45
46
  if (helper.hasCursor(params) && params?.cursor) {
47
+ cursorKey = params.cursor;
46
48
  const cursorDoc = await getDoc(doc(db, collectionName, params.cursor));
47
49
  if (cursorDoc.exists()) {
48
50
  q = query(
@@ -54,9 +56,19 @@ export abstract class BasePaginatedRepository extends BaseQueryRepository {
54
56
  }
55
57
  }
56
58
 
57
- const snapshot = await getDocs(q);
58
- this.trackRead(collectionName, snapshot.docs.length, snapshot.metadata.fromCache);
59
- return snapshot.docs;
59
+ // Generate a unique key for deduplication
60
+ const uniqueKey = `${collectionName}_list_${orderByField}_${orderDirection}_${fetchLimit}_${cursorKey}`;
61
+
62
+ return this.executeQuery(
63
+ collectionName,
64
+ q,
65
+ async () => {
66
+ const snapshot = await getDocs(q);
67
+ return snapshot.docs;
68
+ },
69
+ false, // Default to false as we don't know yet
70
+ uniqueKey
71
+ );
60
72
  }
61
73
 
62
74
  /**
@@ -16,34 +16,41 @@ export abstract class BaseQueryRepository extends BaseRepository {
16
16
  * Prevents duplicate queries and tracks quota usage
17
17
  *
18
18
  * @param collection - Collection name
19
- * @param query - Firestore query
19
+ * @param query - Firestore query (kept for interface compatibility)
20
20
  * @param queryFn - Function to execute the query
21
21
  * @param cached - Whether the result is from cache
22
+ * @param uniqueKey - Unique key for deduplication (critical for correct caching)
22
23
  * @returns Query result
23
24
  */
24
25
  protected async executeQuery<T>(
25
26
  collection: string,
26
- query: Query,
27
+ _query: Query,
27
28
  queryFn: () => Promise<T>,
28
29
  cached: boolean = false,
30
+ uniqueKey?: string
29
31
  ): Promise<T> {
32
+ // FIX: query.toString() returns "[object Object]" which breaks deduplication
33
+ // We must rely on the caller providing a uniqueKey or fallback to a collection-based key (less efficient but safe)
34
+ const safeKey = uniqueKey || `${collection}_generic_query_${Date.now()}`;
35
+
30
36
  const queryKey = {
31
37
  collection,
32
- filters: query.toString(),
38
+ filters: safeKey, // Use the unique key as the filter identifier
33
39
  limit: undefined,
34
40
  orderBy: undefined,
35
41
  };
36
42
 
37
43
  return queryDeduplicationMiddleware.deduplicate(queryKey, async () => {
38
- return quotaTrackingMiddleware.trackOperation(
39
- {
40
- type: 'read',
41
- collection,
42
- count: 1,
43
- cached,
44
- },
45
- queryFn,
46
- );
44
+ // Execute the query function
45
+ 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
+ const count = Array.isArray(result) ? result.length : 1;
50
+
51
+ this.trackRead(collection, count, cached);
52
+
53
+ return result;
47
54
  });
48
55
  }
49
56
 
@@ -90,3 +90,35 @@ export function createErrorResult<T>(message: string, code: string): FirestoreRe
90
90
  export function createSuccessResult<T>(data?: T): FirestoreResult<T> {
91
91
  return { success: true, data };
92
92
  }
93
+
94
+ // ---------------------------------------------------------------------------
95
+ // Transaction & Timestamp Wrappers (Facade Pattern)
96
+ // ---------------------------------------------------------------------------
97
+
98
+ import {
99
+ runTransaction as fbRunTransaction,
100
+ serverTimestamp as fbServerTimestamp,
101
+ type Transaction
102
+ } from "firebase/firestore";
103
+
104
+ /**
105
+ * Execute a transaction with automatic DB instance check.
106
+ * Wraps the Firebase runTransaction to ensure the DB is initialized.
107
+ */
108
+ export async function runTransaction<T>(
109
+ updateFunction: (transaction: Transaction) => Promise<T>
110
+ ): Promise<T> {
111
+ const db = getDb();
112
+ if (!db) {
113
+ throw new Error("[runTransaction] Firestore not initialized");
114
+ }
115
+ return fbRunTransaction(db, updateFunction);
116
+ }
117
+
118
+ /**
119
+ * Get the server timestamp (Sentinel value).
120
+ * Wraps Firebase serverTimestamp to avoid direct dependency.
121
+ */
122
+ export function serverTimestamp() {
123
+ return fbServerTimestamp();
124
+ }
package/src/index.ts CHANGED
@@ -86,7 +86,16 @@ export {
86
86
  export { BaseRepository } from "./firestore/infrastructure/repositories/BaseRepository";
87
87
  export { FirestorePathResolver } from "./firestore/utils/path-resolver";
88
88
  export { PaginationHelper } from "./firestore/utils/pagination.helper";
89
- export type { Timestamp } from "firebase/firestore";
89
+
90
+ export { Timestamp } from "firebase/firestore";
91
+ export type {
92
+ Transaction,
93
+ DocumentReference,
94
+ WriteBatch,
95
+ DocumentSnapshot,
96
+ QuerySnapshot,
97
+ Firestore,
98
+ } from "firebase/firestore";
90
99
 
91
100
  // Firestore Helper Utilities
92
101
  export {
@@ -96,6 +105,8 @@ export {
96
105
  withFirestoreBool,
97
106
  createErrorResult,
98
107
  createSuccessResult,
108
+ runTransaction,
109
+ serverTimestamp,
99
110
  } from "./firestore/utils/firestore-helper";
100
111
  export type { FirestoreResult, NoDbResult } from "./firestore/utils/firestore-helper";
101
112
 
@@ -37,6 +37,7 @@ function getEnvValue(key: ConfigKey): string {
37
37
  */
38
38
  function loadExpoConfig(): Record<string, string> {
39
39
  try {
40
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
40
41
  const Constants = require('expo-constants');
41
42
  const expoConfig = Constants?.expoConfig || Constants?.default?.expoConfig;
42
43
  return expoConfig?.extra || {};