@umituz/react-native-firebase 1.13.2 → 1.13.4

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 (44) hide show
  1. package/package.json +10 -2
  2. package/src/auth/domain/entities/AnonymousUser.ts +44 -0
  3. package/src/auth/domain/errors/FirebaseAuthError.ts +18 -0
  4. package/src/auth/domain/value-objects/FirebaseAuthConfig.ts +45 -0
  5. package/src/auth/index.ts +146 -0
  6. package/src/auth/infrastructure/config/FirebaseAuthClient.ts +210 -0
  7. package/src/auth/infrastructure/config/initializers/FirebaseAuthInitializer.ts +148 -0
  8. package/src/auth/infrastructure/services/account-deletion.service.ts +250 -0
  9. package/src/auth/infrastructure/services/anonymous-auth.service.ts +135 -0
  10. package/src/auth/infrastructure/services/apple-auth.service.ts +146 -0
  11. package/src/auth/infrastructure/services/auth-guard.service.ts +97 -0
  12. package/src/auth/infrastructure/services/auth-utils.service.ts +168 -0
  13. package/src/auth/infrastructure/services/firestore-utils.service.ts +155 -0
  14. package/src/auth/infrastructure/services/google-auth.service.ts +100 -0
  15. package/src/auth/infrastructure/services/reauthentication.service.ts +216 -0
  16. package/src/auth/presentation/hooks/useAnonymousAuth.ts +201 -0
  17. package/src/auth/presentation/hooks/useFirebaseAuth.ts +84 -0
  18. package/src/auth/presentation/hooks/useSocialAuth.ts +162 -0
  19. package/src/firestore/__tests__/BaseRepository.test.ts +133 -0
  20. package/src/firestore/__tests__/QueryDeduplicationMiddleware.test.ts +147 -0
  21. package/src/firestore/__tests__/mocks/react-native-firebase.ts +23 -0
  22. package/src/firestore/__tests__/setup.ts +36 -0
  23. package/src/firestore/domain/constants/QuotaLimits.ts +97 -0
  24. package/src/firestore/domain/entities/QuotaMetrics.ts +28 -0
  25. package/src/firestore/domain/entities/RequestLog.ts +30 -0
  26. package/src/firestore/domain/errors/FirebaseFirestoreError.ts +52 -0
  27. package/src/firestore/domain/services/QuotaCalculator.ts +70 -0
  28. package/src/firestore/index.ts +174 -0
  29. package/src/firestore/infrastructure/config/FirestoreClient.ts +181 -0
  30. package/src/firestore/infrastructure/config/initializers/FirebaseFirestoreInitializer.ts +46 -0
  31. package/src/firestore/infrastructure/middleware/QueryDeduplicationMiddleware.ts +153 -0
  32. package/src/firestore/infrastructure/middleware/QuotaTrackingMiddleware.ts +165 -0
  33. package/src/firestore/infrastructure/repositories/BasePaginatedRepository.ts +90 -0
  34. package/src/firestore/infrastructure/repositories/BaseQueryRepository.ts +80 -0
  35. package/src/firestore/infrastructure/repositories/BaseRepository.ts +147 -0
  36. package/src/firestore/infrastructure/services/QuotaMonitorService.ts +108 -0
  37. package/src/firestore/infrastructure/services/RequestLoggerService.ts +139 -0
  38. package/src/firestore/types/pagination.types.ts +60 -0
  39. package/src/firestore/utils/dateUtils.ts +31 -0
  40. package/src/firestore/utils/document-mapper.helper.ts +145 -0
  41. package/src/firestore/utils/pagination.helper.ts +93 -0
  42. package/src/firestore/utils/query-builder.ts +188 -0
  43. package/src/firestore/utils/quota-error-detector.util.ts +100 -0
  44. package/src/index.ts +16 -0
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Pagination Helper
3
+ *
4
+ * Utilities for cursor-based pagination in Firestore.
5
+ * Handles pagination logic, cursor management, and hasMore detection.
6
+ *
7
+ * App-agnostic: Works with any document type and any collection.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { PaginationHelper } from '@umituz/react-native-firestore';
12
+ *
13
+ * const helper = new PaginationHelper<Post>();
14
+ * const result = helper.buildResult(posts, 10, post => post.id);
15
+ * ```
16
+ */
17
+
18
+ import type { PaginatedResult, PaginationParams } from '../types/pagination.types';
19
+
20
+ export class PaginationHelper<T> {
21
+ /**
22
+ * Build paginated result from items
23
+ *
24
+ * @param items - All items fetched (should be limit + 1)
25
+ * @param pageLimit - Requested page size
26
+ * @param getCursor - Function to extract cursor from item
27
+ * @returns Paginated result with hasMore and nextCursor
28
+ */
29
+ buildResult(
30
+ items: T[],
31
+ pageLimit: number,
32
+ getCursor: (item: T) => string,
33
+ ): PaginatedResult<T> {
34
+ const hasMore = items.length > pageLimit;
35
+ const resultItems = hasMore ? items.slice(0, pageLimit) : items;
36
+ const lastItem = resultItems[resultItems.length - 1];
37
+ const nextCursor = hasMore && lastItem
38
+ ? getCursor(lastItem)
39
+ : null;
40
+
41
+ return {
42
+ items: resultItems,
43
+ nextCursor,
44
+ hasMore,
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Get default limit from params or use default
50
+ *
51
+ * @param params - Pagination params
52
+ * @param defaultLimit - Default limit if not specified
53
+ * @returns Page limit
54
+ */
55
+ getLimit(params?: PaginationParams, defaultLimit: number = 10): number {
56
+ return params?.limit || defaultLimit;
57
+ }
58
+
59
+ /**
60
+ * Calculate fetch limit (page limit + 1 for hasMore detection)
61
+ *
62
+ * @param pageLimit - Requested page size
63
+ * @returns Fetch limit (pageLimit + 1)
64
+ */
65
+ getFetchLimit(pageLimit: number): number {
66
+ return pageLimit + 1;
67
+ }
68
+
69
+ /**
70
+ * Check if params has cursor
71
+ *
72
+ * @param params - Pagination params
73
+ * @returns true if cursor exists
74
+ */
75
+ hasCursor(params?: PaginationParams): boolean {
76
+ return !!params?.cursor;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Create pagination helper for a specific type
82
+ *
83
+ * @returns PaginationHelper instance
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * const helper = createPaginationHelper<Post>();
88
+ * const result = helper.buildResult(posts, 10, post => post.id);
89
+ * ```
90
+ */
91
+ export function createPaginationHelper<T>(): PaginationHelper<T> {
92
+ return new PaginationHelper<T>();
93
+ }
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Query Builder Utility
3
+ * Single Responsibility: Build Firestore queries with advanced filtering
4
+ *
5
+ * App-agnostic utility for building Firestore queries.
6
+ * Supports:
7
+ * - Firestore 'in' operator (up to 10 values)
8
+ * - Firestore 'or' operator (for >10 values via chunking)
9
+ * - Single value filtering
10
+ * - Multiple field filtering
11
+ * - Date range filtering
12
+ * - Sorting
13
+ * - Limiting
14
+ *
15
+ * This utility is designed to be used across hundreds of apps.
16
+ * It provides a consistent interface for Firestore query building.
17
+ */
18
+
19
+ import {
20
+ collection,
21
+ query,
22
+ where,
23
+ orderBy,
24
+ limit as limitQuery,
25
+ startAfter,
26
+ or,
27
+ type Firestore,
28
+ type Query,
29
+ Timestamp,
30
+ type WhereFilterOp,
31
+ } from "firebase/firestore";
32
+
33
+ export interface FieldFilter {
34
+ field: string;
35
+ operator: WhereFilterOp;
36
+ value: string | number | boolean | string[] | number[];
37
+ }
38
+
39
+ export interface QueryBuilderOptions {
40
+ collectionName: string;
41
+ baseFilters?: FieldFilter[];
42
+ dateRange?: {
43
+ field: string;
44
+ startDate?: number;
45
+ endDate?: number;
46
+ };
47
+ sort?: {
48
+ field: string;
49
+ order?: "asc" | "desc";
50
+ };
51
+ limitValue?: number;
52
+ /**
53
+ * Cursor value for pagination (timestamp in milliseconds)
54
+ * Used with startAfter for cursor-based pagination
55
+ */
56
+ cursorValue?: number;
57
+ }
58
+
59
+ const MAX_IN_OPERATOR_VALUES = 10;
60
+
61
+ /**
62
+ * Build Firestore query with advanced filtering support
63
+ *
64
+ * @param db - Firestore database instance
65
+ * @param options - Query builder options
66
+ * @returns Firestore Query object
67
+ */
68
+ export function buildQuery(
69
+ db: Firestore,
70
+ options: QueryBuilderOptions,
71
+ ): Query {
72
+ const {
73
+ collectionName,
74
+ baseFilters = [],
75
+ dateRange,
76
+ sort,
77
+ limitValue,
78
+ cursorValue,
79
+ } = options;
80
+
81
+ const collectionRef = collection(db, collectionName);
82
+ let q: Query = collectionRef;
83
+
84
+ // Apply base filters
85
+ for (const filter of baseFilters) {
86
+ q = applyFieldFilter(q, filter);
87
+ }
88
+
89
+ // Apply date range filters
90
+ if (dateRange) {
91
+ if (dateRange.startDate) {
92
+ q = query(
93
+ q,
94
+ where(
95
+ dateRange.field,
96
+ ">=",
97
+ Timestamp.fromMillis(dateRange.startDate),
98
+ ),
99
+ );
100
+ }
101
+ if (dateRange.endDate) {
102
+ q = query(
103
+ q,
104
+ where(
105
+ dateRange.field,
106
+ "<=",
107
+ Timestamp.fromMillis(dateRange.endDate),
108
+ ),
109
+ );
110
+ }
111
+ }
112
+
113
+ // Apply sorting
114
+ if (sort) {
115
+ const sortOrder = sort.order || "desc";
116
+ q = query(q, orderBy(sort.field, sortOrder));
117
+ }
118
+
119
+ // Apply cursor for pagination (must come after orderBy)
120
+ if (cursorValue !== undefined) {
121
+ q = query(q, startAfter(Timestamp.fromMillis(cursorValue)));
122
+ }
123
+
124
+ // Apply limit
125
+ if (limitValue !== undefined) {
126
+ q = query(q, limitQuery(limitValue));
127
+ }
128
+
129
+ return q;
130
+ }
131
+
132
+ /**
133
+ * Apply field filter with support for 'in' operator and chunking
134
+ * Handles arrays by using 'in' operator (up to 10 values)
135
+ * For arrays >10 values, splits into chunks and uses 'or' operator
136
+ */
137
+ function applyFieldFilter(q: Query, filter: FieldFilter): Query {
138
+ const { field, operator, value } = filter;
139
+
140
+ // Handle 'in' operator with array values
141
+ if (operator === "in" && Array.isArray(value)) {
142
+ // Firestore 'in' operator supports up to 10 values
143
+ if (value.length <= MAX_IN_OPERATOR_VALUES) {
144
+ return query(q, where(field, "in", value));
145
+ }
146
+
147
+ // Split into chunks of 10 and use 'or' operator
148
+ const chunks: (string[] | number[])[] = [];
149
+ for (let i = 0; i < value.length; i += MAX_IN_OPERATOR_VALUES) {
150
+ chunks.push(value.slice(i, i + MAX_IN_OPERATOR_VALUES));
151
+ }
152
+
153
+ const orConditions = chunks.map((chunk) => where(field, "in", chunk));
154
+ return query(q, or(...orConditions));
155
+ }
156
+
157
+ // Standard filter
158
+ return query(q, where(field, operator, value));
159
+ }
160
+
161
+ /**
162
+ * Helper: Create a field filter for 'in' operator
163
+ * Automatically handles chunking if array >10 values
164
+ */
165
+ export function createInFilter(
166
+ field: string,
167
+ values: string[] | number[],
168
+ ): FieldFilter {
169
+ return {
170
+ field,
171
+ operator: "in",
172
+ value: values,
173
+ };
174
+ }
175
+
176
+ /**
177
+ * Helper: Create a field filter for equality
178
+ */
179
+ export function createEqualFilter(
180
+ field: string,
181
+ value: string | number | boolean,
182
+ ): FieldFilter {
183
+ return {
184
+ field,
185
+ operator: "==",
186
+ value,
187
+ };
188
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Quota Error Detector Utility
3
+ * Single Responsibility: Detect Firebase quota errors
4
+ *
5
+ * Firebase quota limits:
6
+ * - Free tier: 50K reads/day, 20K writes/day, 20K deletes/day
7
+ * - Blaze plan: Pay as you go, higher limits
8
+ *
9
+ * Quota errors are NOT retryable - quota won't increase by retrying
10
+ */
11
+
12
+ /**
13
+ * Check if error is a Firebase quota error
14
+ * Quota errors indicate daily read/write/delete limits are exceeded
15
+ *
16
+ * @param error - Error object to check
17
+ * @returns true if error is a quota error
18
+ */
19
+ export function isQuotaError(error: unknown): boolean {
20
+ if (!error || typeof error !== "object") {
21
+ return false;
22
+ }
23
+
24
+ const firebaseError = error as { code?: string; message?: string; name?: string };
25
+
26
+ // Check error code
27
+ if (firebaseError.code === "resource-exhausted") {
28
+ return true;
29
+ }
30
+
31
+ // Check error message
32
+ const errorMessage = firebaseError.message?.toLowerCase() || "";
33
+ if (
34
+ errorMessage.includes("quota") ||
35
+ errorMessage.includes("quota exceeded") ||
36
+ errorMessage.includes("resource-exhausted") ||
37
+ errorMessage.includes("daily limit")
38
+ ) {
39
+ return true;
40
+ }
41
+
42
+ // Check error name
43
+ const errorName = firebaseError.name?.toLowerCase() || "";
44
+ if (errorName.includes("quota") || errorName.includes("resource-exhausted")) {
45
+ return true;
46
+ }
47
+
48
+ return false;
49
+ }
50
+
51
+ /**
52
+ * Check if error is retryable
53
+ * Quota errors are NOT retryable
54
+ *
55
+ * @param error - Error object to check
56
+ * @returns true if error is retryable
57
+ */
58
+ export function isRetryableError(error: unknown): boolean {
59
+ // Quota errors are NOT retryable
60
+ if (isQuotaError(error)) {
61
+ return false;
62
+ }
63
+
64
+ if (!error || typeof error !== "object") {
65
+ return false;
66
+ }
67
+
68
+ const firebaseError = error as { code?: string; message?: string };
69
+
70
+ // Firestore transaction conflicts are retryable
71
+ if (firebaseError.code === "failed-precondition") {
72
+ return true;
73
+ }
74
+
75
+ // Network errors are retryable
76
+ if (
77
+ firebaseError.code === "unavailable" ||
78
+ firebaseError.code === "deadline-exceeded"
79
+ ) {
80
+ return true;
81
+ }
82
+
83
+ // Timeout errors are retryable
84
+ const errorMessage = firebaseError.message?.toLowerCase() || "";
85
+ if (errorMessage.includes("timeout")) {
86
+ return true;
87
+ }
88
+
89
+ return false;
90
+ }
91
+
92
+ /**
93
+ * Get user-friendly quota error message
94
+ *
95
+ * @returns User-friendly error message
96
+ */
97
+ export function getQuotaErrorMessage(): string {
98
+ return "Firebase quota exceeded. Please try again later or upgrade your Firebase plan.";
99
+ }
100
+
package/src/index.ts CHANGED
@@ -4,11 +4,15 @@
4
4
  * Domain-Driven Design (DDD) Architecture
5
5
  *
6
6
  * This package provides Firebase App initialization and core services:
7
+ * - Auth
8
+ * - Firestore
7
9
  * - Analytics
8
10
  * - Crashlytics
9
11
  *
10
12
  * Usage:
11
13
  * import { initializeFirebase, getFirebaseApp } from '@umituz/react-native-firebase';
14
+ * import { useFirebaseAuth } from '@umituz/react-native-firebase';
15
+ * import { getFirestore, BaseRepository } from '@umituz/react-native-firebase';
12
16
  * import { firebaseAnalyticsService } from '@umituz/react-native-firebase';
13
17
  */
14
18
 
@@ -43,6 +47,18 @@ export type {
43
47
  ServiceInitializationResult,
44
48
  } from './infrastructure/config/FirebaseClient';
45
49
 
50
+ // =============================================================================
51
+ // AUTH MODULE
52
+ // =============================================================================
53
+
54
+ export * from './auth';
55
+
56
+ // =============================================================================
57
+ // FIRESTORE MODULE
58
+ // =============================================================================
59
+
60
+ export * from './firestore';
61
+
46
62
  // =============================================================================
47
63
  // ANALYTICS MODULE
48
64
  // =============================================================================