@umituz/react-native-firebase 3.0.3 → 3.0.6

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.
Files changed (80) hide show
  1. package/package.json +7 -1
  2. package/src/domains/account-deletion/index.ts +15 -10
  3. package/src/domains/account-deletion/infrastructure/services/account-deletion.service.ts +235 -26
  4. package/src/domains/account-deletion/infrastructure/services/reauthentication.service.ts +160 -0
  5. package/src/domains/auth/domain/value-objects/FirebaseAuthConfig.ts +1 -1
  6. package/src/domains/auth/index.ts +156 -6
  7. package/src/domains/auth/infrastructure/config/FirebaseAuthClient.ts +60 -48
  8. package/src/domains/auth/infrastructure/config/initializers/FirebaseAuthInitializer.ts +41 -5
  9. package/src/domains/auth/infrastructure/stores/auth.store.ts +4 -1
  10. package/src/domains/auth/presentation/hooks/useAnonymousAuth.ts +3 -1
  11. package/src/domains/auth/presentation/hooks/useGoogleOAuth.ts +115 -20
  12. package/src/domains/auth/presentation/hooks/utils/auth-state-change.handler.ts +5 -11
  13. package/src/domains/firestore/domain/constants/QuotaLimits.ts +101 -0
  14. package/src/domains/firestore/domain/entities/QuotaMetrics.ts +26 -0
  15. package/src/domains/firestore/domain/entities/RequestLog.ts +28 -0
  16. package/src/domains/firestore/domain/services/QuotaCalculator.ts +71 -0
  17. package/src/domains/firestore/index.ts +85 -31
  18. package/src/domains/firestore/infrastructure/config/FirestoreClient.ts +82 -45
  19. package/src/domains/firestore/infrastructure/config/initializers/FirebaseFirestoreInitializer.ts +249 -4
  20. package/src/domains/firestore/infrastructure/middleware/QueryDeduplicationMiddleware.ts +306 -0
  21. package/src/domains/firestore/infrastructure/middleware/QuotaTrackingMiddleware.ts +92 -0
  22. package/src/domains/firestore/infrastructure/repositories/BasePaginatedRepository.ts +9 -1
  23. package/src/domains/firestore/infrastructure/repositories/BaseQueryRepository.ts +34 -8
  24. package/src/domains/firestore/infrastructure/repositories/BaseRepository.ts +48 -9
  25. package/src/domains/firestore/infrastructure/services/RequestLoggerService.ts +168 -0
  26. package/src/domains/firestore/presentation/hooks/index.ts +10 -0
  27. package/src/domains/firestore/presentation/hooks/useFirestoreMutation.ts +1 -1
  28. package/src/domains/firestore/presentation/hooks/useFirestoreQuery.ts +1 -1
  29. package/src/domains/firestore/presentation/hooks/useFirestoreSnapshot.ts +2 -1
  30. package/src/domains/firestore/presentation/hooks/useSmartFirestoreSnapshot.ts +362 -0
  31. package/src/domains/firestore/presentation/query-keys/createFirestoreKeys.ts +32 -0
  32. package/src/domains/firestore/presentation/query-keys/index.ts +1 -0
  33. package/src/domains/firestore/utils/deduplication/pending-query-manager.util.ts +126 -0
  34. package/src/domains/firestore/utils/deduplication/query-key-generator.util.ts +41 -0
  35. package/src/domains/firestore/utils/deduplication/timer-manager.util.ts +83 -0
  36. package/src/domains/firestore/utils/pagination.helper.ts +5 -2
  37. package/src/domains/firestore/utils/transaction/transaction.util.ts +8 -2
  38. package/src/index.ts +324 -32
  39. package/src/shared/domain/utils/calculation.util.ts +305 -17
  40. package/src/shared/domain/utils/error-handlers/error-messages.ts +0 -15
  41. package/src/shared/domain/utils/index.ts +5 -0
  42. package/src/shared/infrastructure/config/base/ClientStateManager.ts +82 -0
  43. package/src/shared/infrastructure/config/base/ServiceClientSingleton.ts +136 -20
  44. package/src/shared/infrastructure/config/clients/FirebaseClientSingleton.ts +1 -1
  45. package/src/shared/infrastructure/config/initializers/FirebaseAppInitializer.ts +9 -0
  46. package/src/shared/infrastructure/config/services/FirebaseInitializationService.ts +1 -1
  47. package/src/shared/infrastructure/config/state/FirebaseClientState.ts +14 -36
  48. package/src/application/auth/index.ts +0 -10
  49. package/src/application/auth/use-cases/index.ts +0 -6
  50. package/src/domains/account-deletion/domain/index.ts +0 -8
  51. package/src/domains/account-deletion/infrastructure/services/AccountDeletionExecutor.ts +0 -79
  52. package/src/domains/account-deletion/infrastructure/services/AccountDeletionTypes.ts +0 -32
  53. package/src/domains/auth/domain.ts +0 -16
  54. package/src/domains/auth/infrastructure/config/index.ts +0 -2
  55. package/src/domains/auth/infrastructure/config/initializers/index.ts +0 -1
  56. package/src/domains/auth/infrastructure/services/index.ts +0 -16
  57. package/src/domains/auth/infrastructure/services/utils/index.ts +0 -1
  58. package/src/domains/auth/infrastructure/stores/index.ts +0 -1
  59. package/src/domains/auth/infrastructure/utils/index.ts +0 -1
  60. package/src/domains/auth/infrastructure.ts +0 -11
  61. package/src/domains/auth/presentation/hooks/useAppleAuth.ts +0 -82
  62. package/src/domains/auth/presentation.ts +0 -31
  63. package/src/domains/firestore/domain/entities/Collection.ts +0 -122
  64. package/src/domains/firestore/domain/entities/CollectionFactory.ts +0 -55
  65. package/src/domains/firestore/domain/entities/CollectionHelpers.ts +0 -143
  66. package/src/domains/firestore/domain/entities/CollectionUtils.ts +0 -72
  67. package/src/domains/firestore/domain/entities/CollectionValidation.ts +0 -138
  68. package/src/domains/firestore/domain/index.ts +0 -61
  69. package/src/domains/firestore/domain/value-objects/QueryOptions.ts +0 -143
  70. package/src/domains/firestore/domain/value-objects/QueryOptionsFactory.ts +0 -95
  71. package/src/domains/firestore/domain/value-objects/QueryOptionsHelpers.ts +0 -110
  72. package/src/domains/firestore/domain/value-objects/WhereClause.ts +0 -114
  73. package/src/domains/firestore/domain/value-objects/WhereClauseFactory.ts +0 -101
  74. package/src/domains/firestore/domain/value-objects/WhereClauseHelpers.ts +0 -123
  75. package/src/domains/firestore/domain/value-objects/WhereClauseValidation.ts +0 -83
  76. package/src/shared/infrastructure/base/ErrorHandler.ts +0 -81
  77. package/src/shared/infrastructure/base/ServiceBase.ts +0 -62
  78. package/src/shared/infrastructure/base/TypedGuard.ts +0 -131
  79. package/src/shared/infrastructure/base/index.ts +0 -34
  80. package/src/shared/types/firebase.types.ts +0 -274
@@ -0,0 +1,306 @@
1
+ /**
2
+ * Query Deduplication Middleware (Enhanced)
3
+ *
4
+ * Prevents duplicate Firestore queries within a configurable time window
5
+ * with quota-aware adaptive deduplication
6
+ *
7
+ * FEATURES:
8
+ * - Configurable deduplication window (default: 10s, was 1s)
9
+ * - Quota-aware adaptive window adjustment
10
+ * - Statistics and monitoring
11
+ * - Memory leak prevention
12
+ * - Automatic cleanup optimization
13
+ *
14
+ * COST SAVINGS: ~90% reduction in duplicate query reads
15
+ */
16
+
17
+ import type { QueryKey } from '../../utils/deduplication/query-key-generator.util';
18
+ import { generateQueryKey } from '../../utils/deduplication/query-key-generator.util';
19
+ import { PendingQueryManager } from '../../utils/deduplication/pending-query-manager.util';
20
+ import { TimerManager } from '../../utils/deduplication/timer-manager.util';
21
+
22
+ /**
23
+ * Default configuration
24
+ * Optimized for cost savings while maintaining freshness
25
+ */
26
+ const DEFAULT_DEDUPLICATION_WINDOW_MS = 10000; // 10s (was 1s)
27
+ const DEFAULT_CLEANUP_INTERVAL_MS = 15000; // 15s (was 3s)
28
+
29
+ /**
30
+ * Quota-based window adjustment thresholds
31
+ */
32
+ const QUOTA_THRESHOLDS = {
33
+ HIGH_USAGE: 0.80, // 80% - extend window to 60s (1 min)
34
+ MEDIUM_USAGE: 0.60, // 60% - extend window to 20s
35
+ NORMAL: 0.50, // < 50% - use default 10s
36
+ } as const;
37
+
38
+ /**
39
+ * Deduplication statistics
40
+ */
41
+ export interface DeduplicationStatistics {
42
+ /** Total queries processed */
43
+ totalQueries: number;
44
+ /** Queries served from cache (deduplicated) */
45
+ cachedQueries: number;
46
+ /** Queries executed (not cached) */
47
+ executedQueries: number;
48
+ /** Current deduplication window in ms */
49
+ currentWindowMs: number;
50
+ /** Cache hit rate (0-1) */
51
+ cacheHitRate: number;
52
+ /** Memory usage (number of cached queries) */
53
+ pendingQueries: number;
54
+ }
55
+
56
+ /**
57
+ * Configuration options for deduplication middleware
58
+ */
59
+ export interface QueryDeduplicationConfig {
60
+ /** Base deduplication window in ms (default: 10000) */
61
+ baseWindowMs?: number;
62
+ /** Cleanup interval in ms (default: 15000) */
63
+ cleanupIntervalMs?: number;
64
+ /** Enable quota-aware adaptive window (default: true) */
65
+ quotaAware?: boolean;
66
+ /** Maximum window size in ms (default: 60000 = 1 minute) */
67
+ maxWindowMs?: number;
68
+ /** Minimum window size in ms (default: 1000 = 1 second) */
69
+ minWindowMs?: number;
70
+ }
71
+
72
+ /**
73
+ * Enhanced Query Deduplication Middleware
74
+ * Prevents duplicate queries with adaptive quota-aware behavior
75
+ */
76
+ export class QueryDeduplicationMiddleware {
77
+ private readonly queryManager: PendingQueryManager;
78
+ private readonly timerManager: TimerManager;
79
+ private readonly baseWindowMs: number;
80
+ private readonly maxWindowMs: number;
81
+ private readonly minWindowMs: number;
82
+ private readonly quotaAware: boolean;
83
+ private destroyed = false;
84
+
85
+ // Statistics tracking
86
+ private stats: DeduplicationStatistics = {
87
+ totalQueries: 0,
88
+ cachedQueries: 0,
89
+ executedQueries: 0,
90
+ currentWindowMs: DEFAULT_DEDUPLICATION_WINDOW_MS,
91
+ cacheHitRate: 0,
92
+ pendingQueries: 0,
93
+ };
94
+
95
+ constructor(config: QueryDeduplicationConfig = {}) {
96
+ this.baseWindowMs = config.baseWindowMs ?? DEFAULT_DEDUPLICATION_WINDOW_MS;
97
+ this.maxWindowMs = config.maxWindowMs ?? 60000; // 1 minute max
98
+ this.minWindowMs = config.minWindowMs ?? 1000; // 1 second min
99
+ this.quotaAware = config.quotaAware ?? true;
100
+
101
+ const cleanupIntervalMs = config.cleanupIntervalMs ?? DEFAULT_CLEANUP_INTERVAL_MS;
102
+
103
+ this.queryManager = new PendingQueryManager(this.baseWindowMs);
104
+ this.timerManager = new TimerManager({
105
+ cleanupIntervalMs,
106
+ onCleanup: () => {
107
+ if (!this.destroyed) {
108
+ this.queryManager.cleanup();
109
+ this.updateStats();
110
+ }
111
+ },
112
+ });
113
+ this.timerManager.start();
114
+ }
115
+
116
+ /**
117
+ * Execute query with deduplication
118
+ * Returns cached result if available within window, otherwise executes
119
+ */
120
+ async deduplicate<T>(
121
+ queryKey: QueryKey,
122
+ queryFn: () => Promise<T>,
123
+ ): Promise<T> {
124
+ if (this.destroyed) {
125
+ // If middleware is destroyed, execute query directly without deduplication
126
+ return queryFn();
127
+ }
128
+
129
+ this.stats.totalQueries++;
130
+ const key = generateQueryKey(queryKey);
131
+
132
+ // Check for existing promise (atomic get-or-create pattern)
133
+ const existingPromise = this.queryManager.get(key);
134
+ if (existingPromise) {
135
+ this.stats.cachedQueries++;
136
+ this.updateCacheHitRate();
137
+ return existingPromise as Promise<T>;
138
+ }
139
+
140
+ // Create promise with cleanup on completion
141
+ this.stats.executedQueries++;
142
+ const promise = (async () => {
143
+ try {
144
+ return await queryFn();
145
+ } finally {
146
+ // Immediate cleanup after completion (success or error)
147
+ this.queryManager.remove(key);
148
+ this.stats.pendingQueries = this.queryManager.size();
149
+ }
150
+ })();
151
+
152
+ // Add before any await - this prevents race between check and add
153
+ this.queryManager.add(key, promise);
154
+ this.stats.pendingQueries = this.queryManager.size();
155
+
156
+ return promise;
157
+ }
158
+
159
+ /**
160
+ * Adjust deduplication window based on quota usage
161
+ * Call this periodically with current quota percentage
162
+ *
163
+ * @param quotaPercentage - Current quota usage (0-1)
164
+ *
165
+ * @example
166
+ * ```typescript
167
+ * const quotaStatus = getQuotaStatus();
168
+ * middleware.adjustWindowForQuota(quotaStatus.readPercentage / 100);
169
+ * ```
170
+ */
171
+ adjustWindowForQuota(quotaPercentage: number): void {
172
+ if (!this.quotaAware || this.destroyed) {
173
+ return;
174
+ }
175
+
176
+ let newWindowMs: number;
177
+
178
+ if (quotaPercentage >= QUOTA_THRESHOLDS.HIGH_USAGE) {
179
+ // High usage: extend window to maximum (1 minute)
180
+ newWindowMs = this.maxWindowMs;
181
+ } else if (quotaPercentage >= QUOTA_THRESHOLDS.MEDIUM_USAGE) {
182
+ // Medium usage: extend window to 20s
183
+ newWindowMs = Math.min(20000, this.maxWindowMs);
184
+ } else {
185
+ // Normal usage: use base window (10s)
186
+ newWindowMs = this.baseWindowMs;
187
+ }
188
+
189
+ // Clamp to min/max bounds
190
+ newWindowMs = Math.max(this.minWindowMs, Math.min(newWindowMs, this.maxWindowMs));
191
+
192
+ // Only update if changed
193
+ if (newWindowMs !== this.stats.currentWindowMs) {
194
+ this.queryManager.setWindow(newWindowMs);
195
+ this.stats.currentWindowMs = newWindowMs;
196
+
197
+ if (__DEV__) {
198
+ console.log(
199
+ `[Deduplication] Adjusted window to ${newWindowMs}ms ` +
200
+ `(quota: ${(quotaPercentage * 100).toFixed(1)}%)`
201
+ );
202
+ }
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Get current deduplication statistics
208
+ */
209
+ getStatistics(): DeduplicationStatistics {
210
+ return { ...this.stats };
211
+ }
212
+
213
+ /**
214
+ * Reset statistics
215
+ */
216
+ resetStatistics(): void {
217
+ this.stats = {
218
+ totalQueries: 0,
219
+ cachedQueries: 0,
220
+ executedQueries: 0,
221
+ currentWindowMs: this.stats.currentWindowMs,
222
+ cacheHitRate: 0,
223
+ pendingQueries: this.queryManager.size(),
224
+ };
225
+ }
226
+
227
+ /**
228
+ * Update cache hit rate
229
+ */
230
+ private updateCacheHitRate(): void {
231
+ this.stats.cacheHitRate =
232
+ this.stats.totalQueries > 0
233
+ ? this.stats.cachedQueries / this.stats.totalQueries
234
+ : 0;
235
+ }
236
+
237
+ /**
238
+ * Update statistics
239
+ */
240
+ private updateStats(): void {
241
+ this.stats.pendingQueries = this.queryManager.size();
242
+ this.updateCacheHitRate();
243
+ }
244
+
245
+ /**
246
+ * Clear all cached queries
247
+ */
248
+ clear(): void {
249
+ this.queryManager.clear();
250
+ this.stats.pendingQueries = 0;
251
+ }
252
+
253
+ /**
254
+ * Destroy middleware and cleanup resources
255
+ */
256
+ destroy(): void {
257
+ this.destroyed = true;
258
+ this.timerManager.destroy();
259
+ this.queryManager.clear();
260
+ this.stats.pendingQueries = 0;
261
+ }
262
+
263
+ /**
264
+ * Get number of pending queries
265
+ */
266
+ getPendingCount(): number {
267
+ return this.queryManager.size();
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Default singleton instance with recommended settings
273
+ */
274
+ export const queryDeduplicationMiddleware = new QueryDeduplicationMiddleware({
275
+ baseWindowMs: DEFAULT_DEDUPLICATION_WINDOW_MS,
276
+ cleanupIntervalMs: DEFAULT_CLEANUP_INTERVAL_MS,
277
+ quotaAware: true,
278
+ maxWindowMs: 60000, // 1 minute
279
+ minWindowMs: 1000, // 1 second
280
+ });
281
+
282
+ /**
283
+ * Helper function to integrate deduplication with quota tracking
284
+ * Automatically adjusts window based on quota usage
285
+ *
286
+ * Note: This is NOT a React hook, but a helper function.
287
+ * Call this from your own hook or effect as needed.
288
+ *
289
+ * @example
290
+ * ```typescript
291
+ * // In your own hook or component:
292
+ * useEffect(() => {
293
+ * syncDeduplicationWithQuota(queryDeduplicationMiddleware, quotaMiddleware, quotaLimits);
294
+ * }, [quotaMiddleware.getCounts().reads]);
295
+ * ```
296
+ */
297
+ export function syncDeduplicationWithQuota(
298
+ deduplication: QueryDeduplicationMiddleware,
299
+ quotaMiddleware: { getCounts: () => { reads: number; writes: number; deletes: number } },
300
+ quotaLimits: { dailyReadLimit: number }
301
+ ): void {
302
+ // Adjust deduplication window based on quota
303
+ const counts = quotaMiddleware.getCounts();
304
+ const quotaPercentage = counts.reads / quotaLimits.dailyReadLimit;
305
+ deduplication.adjustWindowForQuota(quotaPercentage);
306
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Quota Tracking Middleware
3
+ * Tracks Firestore operations for quota monitoring
4
+ */
5
+
6
+ interface OperationInfo {
7
+ type: 'read' | 'write' | 'delete';
8
+ collection: string;
9
+ count: number;
10
+ cached: boolean;
11
+ }
12
+
13
+ export class QuotaTrackingMiddleware {
14
+ private readCount = 0;
15
+ private writeCount = 0;
16
+ private deleteCount = 0;
17
+
18
+ /**
19
+ * Track a Firestore operation
20
+ */
21
+ async trackOperation<T>(
22
+ info: OperationInfo,
23
+ operation: () => Promise<T>
24
+ ): Promise<T> {
25
+ try {
26
+ return await operation();
27
+ } finally {
28
+ switch (info.type) {
29
+ case 'read':
30
+ if (!info.cached) {
31
+ this.readCount += info.count;
32
+ }
33
+ break;
34
+ case 'write':
35
+ this.writeCount += info.count;
36
+ break;
37
+ case 'delete':
38
+ this.deleteCount += info.count;
39
+ break;
40
+ }
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Track read operation
46
+ * @param count - Number of documents read
47
+ * @param cached - Whether result was from cache
48
+ */
49
+ trackRead(count: number = 1, cached: boolean = false): void {
50
+ if (!cached) {
51
+ this.readCount += count;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Track write operation
57
+ * @param count - Number of documents written
58
+ */
59
+ trackWrite(count: number = 1): void {
60
+ this.writeCount += count;
61
+ }
62
+
63
+ /**
64
+ * Track delete operation
65
+ * @param count - Number of documents deleted
66
+ */
67
+ trackDelete(count: number = 1): void {
68
+ this.deleteCount += count;
69
+ }
70
+
71
+ /**
72
+ * Get current counts
73
+ */
74
+ getCounts(): { reads: number; writes: number; deletes: number } {
75
+ return {
76
+ reads: this.readCount,
77
+ writes: this.writeCount,
78
+ deletes: this.deleteCount,
79
+ };
80
+ }
81
+
82
+ /**
83
+ * Reset counts
84
+ */
85
+ reset(): void {
86
+ this.readCount = 0;
87
+ this.writeCount = 0;
88
+ this.deleteCount = 0;
89
+ }
90
+ }
91
+
92
+ export const quotaTrackingMiddleware = new QuotaTrackingMiddleware();
@@ -43,9 +43,11 @@ export abstract class BasePaginatedRepository extends BaseQueryRepository {
43
43
 
44
44
  const collectionRef = collection(db, collectionName);
45
45
  let q: import("firebase/firestore").Query<DocumentData>;
46
+ let cursorKey = 'start';
46
47
 
47
48
  // Handle cursor-based pagination
48
49
  if (helper.hasCursor(params) && params?.cursor) {
50
+ cursorKey = params.cursor;
49
51
 
50
52
  // FIX: Validate cursor and throw error instead of silent failure
51
53
  validateCursorOrThrow(params.cursor);
@@ -75,13 +77,19 @@ export abstract class BasePaginatedRepository extends BaseQueryRepository {
75
77
  );
76
78
  }
77
79
 
80
+ // Generate a unique key for deduplication (after cursor is resolved)
81
+ // FIX: Escape cursor to prevent key collisions from special characters
82
+ const escapedCursor = cursorKey.replace(/[|]/g, '_');
83
+ const uniqueKey = `${collectionName}_list_${orderByField}_${orderDirection}_${fetchLimit}_${escapedCursor}`;
84
+
78
85
  return this.executeQuery(
79
86
  collectionName,
80
87
  async () => {
81
88
  const snapshot = await getDocs(q);
82
89
  return snapshot.docs;
83
90
  },
84
- false
91
+ false,
92
+ uniqueKey
85
93
  );
86
94
  }
87
95
 
@@ -1,18 +1,44 @@
1
- import { BaseRepository } from './BaseRepository';
1
+ /**
2
+ * Base Repository - Query Operations
3
+ *
4
+ * Provides query and tracking operations for Firestore repositories.
5
+ * Extends BaseRepository with query-specific functionality.
6
+ */
7
+
8
+ import { queryDeduplicationMiddleware } from "../middleware/QueryDeduplicationMiddleware";
9
+ import { BaseRepository } from "./BaseRepository";
2
10
 
3
11
  export abstract class BaseQueryRepository extends BaseRepository {
12
+ /**
13
+ * Execute query with deduplication and quota tracking
14
+ * Prevents duplicate queries and tracks quota usage
15
+ */
4
16
  protected async executeQuery<T>(
5
17
  collection: string,
6
18
  queryFn: () => Promise<T>,
7
- cached: boolean = false
19
+ cached: boolean = false,
20
+ uniqueKey?: string
8
21
  ): Promise<T> {
9
- const result = await queryFn();
22
+ const safeKey = uniqueKey || `${collection}_query`;
23
+
24
+ const queryKey = {
25
+ collection,
26
+ filters: safeKey,
27
+ limit: undefined,
28
+ orderBy: undefined,
29
+ };
30
+
31
+ return queryDeduplicationMiddleware.deduplicate(queryKey, async () => {
32
+ const result = await queryFn();
10
33
 
11
- if (!cached) {
12
- const count = Array.isArray(result) ? result.length : 1;
13
- this.trackRead(collection, count, cached);
14
- }
34
+ // Optimize: Only calculate count if tracking is needed
35
+ // Check if quota tracking is enabled before computing array length
36
+ if (!cached) {
37
+ const count = Array.isArray(result) ? result.length : 1;
38
+ this.trackRead(collection, count, cached);
39
+ }
15
40
 
16
- return result;
41
+ return result;
42
+ });
17
43
  }
18
44
  }
@@ -10,6 +10,9 @@
10
10
 
11
11
  import type { Firestore, CollectionReference, DocumentReference, DocumentData } from 'firebase/firestore';
12
12
  import { getFirestore, collection, doc } from 'firebase/firestore';
13
+ import { isQuotaError as checkQuotaError } from '../../../../shared/domain/utils/error-handlers/error-checkers';
14
+ import { ERROR_MESSAGES } from '../../../../shared/domain/utils/error-handlers/error-messages';
15
+ import { quotaTrackingMiddleware } from '../middleware/QuotaTrackingMiddleware';
13
16
 
14
17
  enum RepositoryState {
15
18
  ACTIVE = 'active',
@@ -46,11 +49,11 @@ export abstract class BaseRepository implements IPathResolver {
46
49
  */
47
50
  protected getDbOrThrow(): Firestore {
48
51
  if (this.state === RepositoryState.DESTROYED) {
49
- throw new Error('Repository is destroyed');
52
+ throw new Error(ERROR_MESSAGES.REPOSITORY.DESTROYED);
50
53
  }
51
54
  const db = getFirestore();
52
55
  if (!db) {
53
- throw new Error('Firestore is not initialized');
56
+ throw new Error(ERROR_MESSAGES.FIRESTORE.NOT_INITIALIZED);
54
57
  }
55
58
  return db;
56
59
  }
@@ -104,19 +107,55 @@ export abstract class BaseRepository implements IPathResolver {
104
107
  protected async executeOperation<T>(
105
108
  operation: () => Promise<T>
106
109
  ): Promise<T> {
107
- return await operation();
110
+ try {
111
+ return await operation();
112
+ } catch (error) {
113
+ if (checkQuotaError(error)) {
114
+ throw new Error(ERROR_MESSAGES.FIRESTORE.QUOTA_EXCEEDED);
115
+ }
116
+ throw error;
117
+ }
108
118
  }
109
119
 
110
- protected trackRead(_collection: string, _count: number = 1, _cached: boolean = false): void {
111
- // Quota tracking removed - use Firebase console for monitoring
120
+ /**
121
+ * Track read operation for quota monitoring
122
+ *
123
+ * @param collection - Collection name (for documentation purposes)
124
+ * @param count - Number of documents read
125
+ * @param cached - Whether the result is from cache
126
+ */
127
+ protected trackRead(
128
+ collection: string,
129
+ count: number = 1,
130
+ cached: boolean = false,
131
+ ): void {
132
+ quotaTrackingMiddleware.trackRead(count, cached);
112
133
  }
113
134
 
114
- protected trackWrite(_collection: string, _count: number = 1): void {
115
- // Quota tracking removed - use Firebase console for monitoring
135
+ /**
136
+ * Track write operation for quota monitoring
137
+ *
138
+ * @param collection - Collection name (for documentation purposes)
139
+ * @param count - Number of documents written
140
+ */
141
+ protected trackWrite(
142
+ collection: string,
143
+ count: number = 1,
144
+ ): void {
145
+ quotaTrackingMiddleware.trackWrite(count);
116
146
  }
117
147
 
118
- protected trackDelete(_collection: string, _count: number = 1): void {
119
- // Quota tracking removed - use Firebase console for monitoring
148
+ /**
149
+ * Track delete operation for quota monitoring
150
+ *
151
+ * @param collection - Collection name (for documentation purposes)
152
+ * @param count - Number of documents deleted
153
+ */
154
+ protected trackDelete(
155
+ collection: string,
156
+ count: number = 1,
157
+ ): void {
158
+ quotaTrackingMiddleware.trackDelete(count);
120
159
  }
121
160
 
122
161
  /**