@umituz/react-native-firebase 3.0.2 → 3.0.5

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 (74) 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 +226 -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/presentation/hooks/useGoogleOAuth.ts +115 -20
  10. package/src/domains/firestore/domain/constants/QuotaLimits.ts +101 -0
  11. package/src/domains/firestore/domain/entities/QuotaMetrics.ts +26 -0
  12. package/src/domains/firestore/domain/entities/RequestLog.ts +28 -0
  13. package/src/domains/firestore/domain/services/QuotaCalculator.ts +71 -0
  14. package/src/domains/firestore/index.ts +86 -28
  15. package/src/domains/firestore/infrastructure/config/FirestoreClient.ts +92 -19
  16. package/src/domains/firestore/infrastructure/config/initializers/FirebaseFirestoreInitializer.ts +249 -4
  17. package/src/domains/firestore/infrastructure/middleware/QueryDeduplicationMiddleware.ts +312 -0
  18. package/src/domains/firestore/infrastructure/middleware/QuotaTrackingMiddleware.ts +95 -0
  19. package/src/domains/firestore/infrastructure/repositories/BasePaginatedRepository.ts +7 -1
  20. package/src/domains/firestore/infrastructure/repositories/BaseQueryRepository.ts +34 -8
  21. package/src/domains/firestore/infrastructure/repositories/BaseRepository.ts +48 -9
  22. package/src/domains/firestore/infrastructure/services/RequestLoggerService.ts +165 -0
  23. package/src/domains/firestore/presentation/hooks/index.ts +10 -0
  24. package/src/domains/firestore/presentation/hooks/useFirestoreMutation.ts +1 -1
  25. package/src/domains/firestore/presentation/hooks/useFirestoreQuery.ts +1 -1
  26. package/src/domains/firestore/presentation/hooks/useSmartFirestoreSnapshot.ts +361 -0
  27. package/src/domains/firestore/presentation/query-keys/createFirestoreKeys.ts +32 -0
  28. package/src/domains/firestore/presentation/query-keys/index.ts +1 -0
  29. package/src/domains/firestore/utils/deduplication/pending-query-manager.util.ts +119 -0
  30. package/src/domains/firestore/utils/deduplication/query-key-generator.util.ts +34 -0
  31. package/src/domains/firestore/utils/deduplication/timer-manager.util.ts +83 -0
  32. package/src/index.ts +2 -30
  33. package/src/shared/domain/utils/calculation.util.ts +305 -17
  34. package/src/shared/domain/utils/error-handlers/error-messages.ts +0 -11
  35. package/src/shared/domain/utils/index.ts +5 -0
  36. package/src/shared/infrastructure/config/base/ClientStateManager.ts +82 -0
  37. package/src/shared/infrastructure/config/base/ServiceClientSingleton.ts +136 -20
  38. package/src/shared/infrastructure/config/clients/FirebaseClientSingleton.ts +1 -1
  39. package/src/shared/infrastructure/config/initializers/FirebaseAppInitializer.ts +9 -0
  40. package/src/shared/infrastructure/config/services/FirebaseInitializationService.ts +1 -1
  41. package/src/shared/infrastructure/config/state/FirebaseClientState.ts +14 -36
  42. package/src/application/auth/index.ts +0 -10
  43. package/src/application/auth/use-cases/index.ts +0 -6
  44. package/src/domains/account-deletion/domain/index.ts +0 -8
  45. package/src/domains/account-deletion/infrastructure/services/AccountDeletionExecutor.ts +0 -79
  46. package/src/domains/account-deletion/infrastructure/services/AccountDeletionTypes.ts +0 -32
  47. package/src/domains/auth/domain.ts +0 -16
  48. package/src/domains/auth/infrastructure/config/index.ts +0 -2
  49. package/src/domains/auth/infrastructure/config/initializers/index.ts +0 -1
  50. package/src/domains/auth/infrastructure/services/index.ts +0 -16
  51. package/src/domains/auth/infrastructure/services/utils/index.ts +0 -1
  52. package/src/domains/auth/infrastructure/stores/index.ts +0 -1
  53. package/src/domains/auth/infrastructure/utils/index.ts +0 -1
  54. package/src/domains/auth/infrastructure.ts +0 -11
  55. package/src/domains/auth/presentation/hooks/useAppleAuth.ts +0 -82
  56. package/src/domains/auth/presentation.ts +0 -31
  57. package/src/domains/firestore/domain/entities/Collection.ts +0 -122
  58. package/src/domains/firestore/domain/entities/CollectionFactory.ts +0 -55
  59. package/src/domains/firestore/domain/entities/CollectionHelpers.ts +0 -143
  60. package/src/domains/firestore/domain/entities/CollectionUtils.ts +0 -72
  61. package/src/domains/firestore/domain/entities/CollectionValidation.ts +0 -138
  62. package/src/domains/firestore/domain/index.ts +0 -61
  63. package/src/domains/firestore/domain/value-objects/QueryOptions.ts +0 -143
  64. package/src/domains/firestore/domain/value-objects/QueryOptionsFactory.ts +0 -95
  65. package/src/domains/firestore/domain/value-objects/QueryOptionsHelpers.ts +0 -110
  66. package/src/domains/firestore/domain/value-objects/WhereClause.ts +0 -114
  67. package/src/domains/firestore/domain/value-objects/WhereClauseFactory.ts +0 -101
  68. package/src/domains/firestore/domain/value-objects/WhereClauseHelpers.ts +0 -123
  69. package/src/domains/firestore/domain/value-objects/WhereClauseValidation.ts +0 -83
  70. package/src/shared/infrastructure/base/ErrorHandler.ts +0 -81
  71. package/src/shared/infrastructure/base/ServiceBase.ts +0 -62
  72. package/src/shared/infrastructure/base/TypedGuard.ts +0 -131
  73. package/src/shared/infrastructure/base/index.ts +0 -34
  74. package/src/shared/types/firebase.types.ts +0 -274
@@ -0,0 +1,312 @@
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
+ }
307
+
308
+ /**
309
+ * @deprecated Use syncDeduplicationWithQuota instead (not a hook)
310
+ * This will be removed in a future version
311
+ */
312
+ export const useDeduplicationWithQuota = syncDeduplicationWithQuota;
@@ -0,0 +1,95 @@
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 _collection - Collection name (reserved for future per-collection tracking)
47
+ * @param count - Number of documents read
48
+ * @param cached - Whether result was from cache
49
+ */
50
+ trackRead(_collection: string, count: number = 1, cached: boolean = false): void {
51
+ if (!cached) {
52
+ this.readCount += count;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Track write operation
58
+ * @param _collection - Collection name (reserved for future per-collection tracking)
59
+ * @param count - Number of documents written
60
+ */
61
+ trackWrite(_collection: string, count: number = 1): void {
62
+ this.writeCount += count;
63
+ }
64
+
65
+ /**
66
+ * Track delete operation
67
+ * @param _collection - Collection name (reserved for future per-collection tracking)
68
+ * @param count - Number of documents deleted
69
+ */
70
+ trackDelete(_collection: string, count: number = 1): void {
71
+ this.deleteCount += count;
72
+ }
73
+
74
+ /**
75
+ * Get current counts
76
+ */
77
+ getCounts(): { reads: number; writes: number; deletes: number } {
78
+ return {
79
+ reads: this.readCount,
80
+ writes: this.writeCount,
81
+ deletes: this.deleteCount,
82
+ };
83
+ }
84
+
85
+ /**
86
+ * Reset counts
87
+ */
88
+ reset(): void {
89
+ this.readCount = 0;
90
+ this.writeCount = 0;
91
+ this.deleteCount = 0;
92
+ }
93
+ }
94
+
95
+ 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,17 @@ export abstract class BasePaginatedRepository extends BaseQueryRepository {
75
77
  );
76
78
  }
77
79
 
80
+ // Generate a unique key for deduplication (after cursor is resolved)
81
+ const uniqueKey = `${collectionName}_list_${orderByField}_${orderDirection}_${fetchLimit}_${cursorKey}`;
82
+
78
83
  return this.executeQuery(
79
84
  collectionName,
80
85
  async () => {
81
86
  const snapshot = await getDocs(q);
82
87
  return snapshot.docs;
83
88
  },
84
- false
89
+ false,
90
+ uniqueKey
85
91
  );
86
92
  }
87
93
 
@@ -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
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(collection, 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
139
+ * @param count - Number of documents written
140
+ */
141
+ protected trackWrite(
142
+ collection: string,
143
+ count: number = 1,
144
+ ): void {
145
+ quotaTrackingMiddleware.trackWrite(collection, 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
152
+ * @param count - Number of documents deleted
153
+ */
154
+ protected trackDelete(
155
+ collection: string,
156
+ count: number = 1,
157
+ ): void {
158
+ quotaTrackingMiddleware.trackDelete(collection, count);
120
159
  }
121
160
 
122
161
  /**