@umituz/react-native-firebase 1.13.121 → 1.13.122

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 (35) hide show
  1. package/package.json +1 -1
  2. package/src/auth/infrastructure/services/account-deletion.service.ts +4 -4
  3. package/src/auth/infrastructure/services/auth-utils.service.ts +2 -1
  4. package/src/auth/infrastructure/services/reauthentication.service.ts +15 -28
  5. package/src/domain/utils/error-handler.util.ts +90 -0
  6. package/src/domain/utils/id-generator.util.ts +50 -0
  7. package/src/domain/utils/validation.util.ts +133 -0
  8. package/src/firestore/infrastructure/middleware/QueryDeduplicationMiddleware.ts +26 -117
  9. package/src/firestore/infrastructure/services/RequestLoggerService.ts +2 -8
  10. package/src/firestore/utils/deduplication/index.ts +13 -0
  11. package/src/firestore/utils/deduplication/pending-query-manager.util.ts +93 -0
  12. package/src/firestore/utils/deduplication/query-key-generator.util.ts +41 -0
  13. package/src/firestore/utils/deduplication/timer-manager.util.ts +59 -0
  14. package/src/firestore/utils/document-mapper.helper.ts +45 -37
  15. package/src/firestore/utils/firestore-helper.ts +21 -85
  16. package/src/firestore/utils/mapper/base-mapper.util.ts +42 -0
  17. package/src/firestore/utils/mapper/enrichment-mapper.util.ts +82 -0
  18. package/src/firestore/utils/mapper/index.ts +8 -0
  19. package/src/firestore/utils/mapper/multi-enrichment-mapper.util.ts +39 -0
  20. package/src/firestore/utils/operation/operation-executor.util.ts +49 -0
  21. package/src/firestore/utils/query/filters.util.ts +75 -0
  22. package/src/firestore/utils/query/index.ts +10 -0
  23. package/src/firestore/utils/query/modifiers.util.ts +65 -0
  24. package/src/firestore/utils/query-builder.ts +7 -108
  25. package/src/firestore/utils/result/result.util.ts +45 -0
  26. package/src/firestore/utils/transaction/transaction.util.ts +45 -0
  27. package/src/index.ts +0 -1
  28. package/src/infrastructure/config/FirebaseConfigLoader.ts +9 -13
  29. package/src/storage/deleter/README.md +0 -370
  30. package/src/storage/deleter.ts +0 -109
  31. package/src/storage/index.ts +0 -8
  32. package/src/storage/storage-instance.ts +0 -11
  33. package/src/storage/types/README.md +0 -313
  34. package/src/storage/types.ts +0 -19
  35. package/src/storage/uploader.ts +0 -106
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Query Filters Utility
3
+ * Utilities for creating Firestore field filters
4
+ */
5
+
6
+ import {
7
+ query,
8
+ where,
9
+ or,
10
+ type WhereFilterOp,
11
+ type Query,
12
+ } from "firebase/firestore";
13
+
14
+ export interface FieldFilter {
15
+ field: string;
16
+ operator: WhereFilterOp;
17
+ value: string | number | boolean | string[] | number[];
18
+ }
19
+
20
+ const MAX_IN_OPERATOR_VALUES = 10;
21
+
22
+ /**
23
+ * Apply field filter with 'in' operator and chunking support
24
+ */
25
+ export function applyFieldFilter(q: Query, filter: FieldFilter): Query {
26
+ const { field, operator, value } = filter;
27
+
28
+ if (operator === "in" && Array.isArray(value)) {
29
+ if (value.length <= MAX_IN_OPERATOR_VALUES) {
30
+ return query(q, where(field, "in", value));
31
+ }
32
+
33
+ // Split into chunks of 10 and use 'or' operator
34
+ const chunks: (string[] | number[])[] = [];
35
+ for (let i = 0; i < value.length; i += MAX_IN_OPERATOR_VALUES) {
36
+ chunks.push(value.slice(i, i + MAX_IN_OPERATOR_VALUES));
37
+ }
38
+
39
+ const orConditions = chunks.map((chunk) => where(field, "in", chunk));
40
+ return query(q, or(...orConditions));
41
+ }
42
+
43
+ return query(q, where(field, operator, value));
44
+ }
45
+
46
+ /**
47
+ * Create a field filter for 'in' operator
48
+ */
49
+ export function createInFilter(
50
+ field: string,
51
+ values: string[] | number[],
52
+ ): FieldFilter {
53
+ return { field, operator: "in", value: values };
54
+ }
55
+
56
+ /**
57
+ * Create a field filter for equality
58
+ */
59
+ export function createEqualFilter(
60
+ field: string,
61
+ value: string | number | boolean,
62
+ ): FieldFilter {
63
+ return { field, operator: "==", value };
64
+ }
65
+
66
+ /**
67
+ * Create a field filter for custom operator
68
+ */
69
+ export function createFieldFilter(
70
+ field: string,
71
+ operator: WhereFilterOp,
72
+ value: string | number | boolean | string[] | number[],
73
+ ): FieldFilter {
74
+ return { field, operator, value };
75
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Query Utilities
3
+ * Utilities for building Firestore queries
4
+ */
5
+
6
+ export { applyFieldFilter, createInFilter, createEqualFilter, createFieldFilter } from './filters.util';
7
+ export type { FieldFilter } from './filters.util';
8
+
9
+ export { applyDateRange, applySort, applyCursor, applyLimit } from './modifiers.util';
10
+ export type { SortOptions, DateRangeOptions } from './modifiers.util';
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Query Modifiers Utility
3
+ * Utilities for applying query modifiers (sort, limit, cursor)
4
+ */
5
+
6
+ import {
7
+ query,
8
+ orderBy,
9
+ where,
10
+ limit as limitQuery,
11
+ startAfter,
12
+ type Query,
13
+ Timestamp,
14
+ } from "firebase/firestore";
15
+
16
+ export interface SortOptions {
17
+ field: string;
18
+ order?: "asc" | "desc";
19
+ }
20
+
21
+ export interface DateRangeOptions {
22
+ field: string;
23
+ startDate?: number;
24
+ endDate?: number;
25
+ }
26
+
27
+ /**
28
+ * Apply date range filters to query
29
+ */
30
+ export function applyDateRange(q: Query, dateRange: DateRangeOptions | undefined): Query {
31
+ if (!dateRange) return q;
32
+
33
+ if (dateRange.startDate) {
34
+ q = query(q, where(dateRange.field, ">=", Timestamp.fromMillis(dateRange.startDate)));
35
+ }
36
+ if (dateRange.endDate) {
37
+ q = query(q, where(dateRange.field, "<=", Timestamp.fromMillis(dateRange.endDate)));
38
+ }
39
+ return q;
40
+ }
41
+
42
+ /**
43
+ * Apply sorting to query
44
+ */
45
+ export function applySort(q: Query, sort?: SortOptions): Query {
46
+ if (!sort) return q;
47
+ const sortOrder = sort.order || "desc";
48
+ return query(q, orderBy(sort.field, sortOrder));
49
+ }
50
+
51
+ /**
52
+ * Apply cursor for pagination
53
+ */
54
+ export function applyCursor(q: Query, cursorValue?: number): Query {
55
+ if (cursorValue === undefined) return q;
56
+ return query(q, startAfter(Timestamp.fromMillis(cursorValue)));
57
+ }
58
+
59
+ /**
60
+ * Apply limit to query
61
+ */
62
+ export function applyLimit(q: Query, limitValue?: number): Query {
63
+ if (limitValue === undefined) return q;
64
+ return query(q, limitQuery(limitValue));
65
+ }
@@ -5,106 +5,21 @@
5
5
 
6
6
  import {
7
7
  collection,
8
- query,
9
- where,
10
- orderBy,
11
- limit as limitQuery,
12
- startAfter,
13
- or,
14
8
  type Firestore,
15
9
  type Query,
16
- Timestamp,
17
- type WhereFilterOp,
18
10
  } from "firebase/firestore";
19
-
20
- export interface FieldFilter {
21
- field: string;
22
- operator: WhereFilterOp;
23
- value: string | number | boolean | string[] | number[];
24
- }
11
+ import { applyFieldFilter, createInFilter, createEqualFilter, createFieldFilter, type FieldFilter } from "./query/filters.util";
12
+ import { applyDateRange, applySort, applyCursor, applyLimit, type SortOptions, type DateRangeOptions } from "./query/modifiers.util";
25
13
 
26
14
  export interface QueryBuilderOptions {
27
15
  collectionName: string;
28
16
  baseFilters?: FieldFilter[];
29
- dateRange?: {
30
- field: string;
31
- startDate?: number;
32
- endDate?: number;
33
- };
34
- sort?: {
35
- field: string;
36
- order?: "asc" | "desc";
37
- };
17
+ dateRange?: DateRangeOptions;
18
+ sort?: SortOptions;
38
19
  limitValue?: number;
39
20
  cursorValue?: number;
40
21
  }
41
22
 
42
- const MAX_IN_OPERATOR_VALUES = 10;
43
-
44
- /**
45
- * Apply date range filters to query
46
- */
47
- function applyDateRange(q: Query, dateRange: QueryBuilderOptions['dateRange']): Query {
48
- if (!dateRange) return q;
49
-
50
- if (dateRange.startDate) {
51
- q = query(q, where(dateRange.field, ">=", Timestamp.fromMillis(dateRange.startDate)));
52
- }
53
- if (dateRange.endDate) {
54
- q = query(q, where(dateRange.field, "<=", Timestamp.fromMillis(dateRange.endDate)));
55
- }
56
- return q;
57
- }
58
-
59
- /**
60
- * Apply sorting to query
61
- */
62
- function applySort(q: Query, sort?: QueryBuilderOptions['sort']): Query {
63
- if (!sort) return q;
64
- const sortOrder = sort.order || "desc";
65
- return query(q, orderBy(sort.field, sortOrder));
66
- }
67
-
68
- /**
69
- * Apply cursor for pagination
70
- */
71
- function applyCursor(q: Query, cursorValue?: number): Query {
72
- if (cursorValue === undefined) return q;
73
- return query(q, startAfter(Timestamp.fromMillis(cursorValue)));
74
- }
75
-
76
- /**
77
- * Apply limit to query
78
- */
79
- function applyLimit(q: Query, limitValue?: number): Query {
80
- if (limitValue === undefined) return q;
81
- return query(q, limitQuery(limitValue));
82
- }
83
-
84
- /**
85
- * Apply field filter with 'in' operator and chunking support
86
- */
87
- function applyFieldFilter(q: Query, filter: FieldFilter): Query {
88
- const { field, operator, value } = filter;
89
-
90
- if (operator === "in" && Array.isArray(value)) {
91
- if (value.length <= MAX_IN_OPERATOR_VALUES) {
92
- return query(q, where(field, "in", value));
93
- }
94
-
95
- // Split into chunks of 10 and use 'or' operator
96
- const chunks: (string[] | number[])[] = [];
97
- for (let i = 0; i < value.length; i += MAX_IN_OPERATOR_VALUES) {
98
- chunks.push(value.slice(i, i + MAX_IN_OPERATOR_VALUES));
99
- }
100
-
101
- const orConditions = chunks.map((chunk) => where(field, "in", chunk));
102
- return query(q, or(...orConditions));
103
- }
104
-
105
- return query(q, where(field, operator, value));
106
- }
107
-
108
23
  /**
109
24
  * Build Firestore query with advanced filtering support
110
25
  */
@@ -138,22 +53,6 @@ export function buildQuery(
138
53
  return q;
139
54
  }
140
55
 
141
- /**
142
- * Create a field filter for 'in' operator
143
- */
144
- export function createInFilter(
145
- field: string,
146
- values: string[] | number[],
147
- ): FieldFilter {
148
- return { field, operator: "in", value: values };
149
- }
150
-
151
- /**
152
- * Create a field filter for equality
153
- */
154
- export function createEqualFilter(
155
- field: string,
156
- value: string | number | boolean,
157
- ): FieldFilter {
158
- return { field, operator: "==", value };
159
- }
56
+ // Re-export filter utilities for convenience
57
+ export { createInFilter, createEqualFilter, createFieldFilter };
58
+ export type { FieldFilter, SortOptions, DateRangeOptions };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Result Utility
3
+ * Utilities for creating Firestore result objects
4
+ */
5
+
6
+ export interface FirestoreResult<T> {
7
+ success: boolean;
8
+ data?: T;
9
+ error?: { message: string; code: string };
10
+ }
11
+
12
+ export type NoDbResult = FirestoreResult<never>;
13
+
14
+ export const NO_DB_ERROR: NoDbResult = {
15
+ success: false,
16
+ error: { message: "No DB", code: "DB_ERR" },
17
+ };
18
+
19
+ /**
20
+ * Create a standard error result
21
+ */
22
+ export function createErrorResult<T>(message: string, code: string): FirestoreResult<T> {
23
+ return { success: false, error: { message, code } };
24
+ }
25
+
26
+ /**
27
+ * Create a standard success result
28
+ */
29
+ export function createSuccessResult<T>(data?: T): FirestoreResult<T> {
30
+ return { success: true, data };
31
+ }
32
+
33
+ /**
34
+ * Check if result is successful
35
+ */
36
+ export function isSuccess<T>(result: FirestoreResult<T>): result is FirestoreResult<T> & { success: true } {
37
+ return result.success;
38
+ }
39
+
40
+ /**
41
+ * Check if result is an error
42
+ */
43
+ export function isError<T>(result: FirestoreResult<T>): result is FirestoreResult<T> & { success: false } {
44
+ return !result.success;
45
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Transaction Utility
3
+ * Utilities for executing Firestore transactions
4
+ */
5
+
6
+ import {
7
+ runTransaction as fbRunTransaction,
8
+ serverTimestamp as fbServerTimestamp,
9
+ type Transaction,
10
+ } from "firebase/firestore";
11
+ import { getDb } from "../firestore-helper";
12
+ import type { Firestore } from "../../infrastructure/config/FirestoreClient";
13
+
14
+ /**
15
+ * Execute a transaction with automatic DB instance check.
16
+ * Wraps the Firebase runTransaction to ensure the DB is initialized.
17
+ */
18
+ export async function runTransaction<T>(
19
+ updateFunction: (transaction: Transaction) => Promise<T>
20
+ ): Promise<T> {
21
+ const db = getDb();
22
+ if (!db) {
23
+ throw new Error("[runTransaction] Firestore database is not initialized. Please ensure Firebase is properly initialized before running transactions.");
24
+ }
25
+ try {
26
+ return await fbRunTransaction(db as Firestore, updateFunction);
27
+ } catch (error) {
28
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
29
+ const errorCode = error instanceof Error ? (error as { code?: string }).code : 'unknown';
30
+
31
+ if (__DEV__) {
32
+ console.error(`[runTransaction] Transaction failed (Code: ${errorCode}):`, errorMessage);
33
+ }
34
+
35
+ throw new Error(`[runTransaction] Transaction failed: ${errorMessage} (Code: ${errorCode})`);
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Get the server timestamp (Sentinel value).
41
+ * Wraps Firebase serverTimestamp to avoid direct dependency.
42
+ */
43
+ export function serverTimestamp() {
44
+ return fbServerTimestamp();
45
+ }
package/src/index.ts CHANGED
@@ -8,7 +8,6 @@
8
8
  * Module-specific exports available via:
9
9
  * - @umituz/react-native-firebase/auth
10
10
  * - @umituz/react-native-firebase/firestore
11
- * - @umituz/react-native-firebase/storage
12
11
  */
13
12
 
14
13
  // Core Errors
@@ -7,6 +7,11 @@
7
7
  */
8
8
 
9
9
  import type { FirebaseConfig } from '../../domain/value-objects/FirebaseConfig';
10
+ import {
11
+ isValidString,
12
+ isValidFirebaseApiKey,
13
+ isValidFirebaseAuthDomain,
14
+ } from '../../domain/utils/validation.util';
10
15
 
11
16
  type ConfigKey = 'apiKey' | 'authDomain' | 'projectId' | 'storageBucket' | 'messagingSenderId' | 'appId';
12
17
 
@@ -27,7 +32,7 @@ function getEnvValue(key: ConfigKey): string {
27
32
  const keys = ENV_KEYS[key];
28
33
  for (const envKey of keys) {
29
34
  const value = process.env[`${EXPO_PREFIX}${envKey}`] || process.env[envKey];
30
- if (value?.trim()) return value;
35
+ if (isValidString(value)) return value;
31
36
  }
32
37
  return '';
33
38
  }
@@ -62,15 +67,6 @@ function getExpoValue(key: ConfigKey, expoConfig: Record<string, string>): strin
62
67
  return expoConfig[mapping[key]] || '';
63
68
  }
64
69
 
65
- /**
66
- * Validate Firebase API key format
67
- */
68
- function validateApiKey(apiKey: string): boolean {
69
- // Firebase API keys typically start with "AIza" followed by 35 characters
70
- const apiKeyPattern = /^AIza[0-9A-Za-z_-]{35}$/;
71
- return apiKeyPattern.test(apiKey);
72
- }
73
-
74
70
  /**
75
71
  * Load Firebase configuration from Constants and environment variables
76
72
  */
@@ -90,7 +86,7 @@ export function loadFirebaseConfig(): FirebaseConfig | null {
90
86
  const authDomain = config.authDomain?.trim();
91
87
  const projectId = config.projectId?.trim();
92
88
 
93
- if (!apiKey || !authDomain || !projectId) {
89
+ if (!isValidString(apiKey) || !isValidString(authDomain) || !isValidString(projectId)) {
94
90
  if (__DEV__) {
95
91
  console.error('[FirebaseConfigLoader] Missing required configuration fields');
96
92
  }
@@ -98,7 +94,7 @@ export function loadFirebaseConfig(): FirebaseConfig | null {
98
94
  }
99
95
 
100
96
  // Validate API key format
101
- if (!validateApiKey(apiKey)) {
97
+ if (!isValidFirebaseApiKey(apiKey)) {
102
98
  if (__DEV__) {
103
99
  console.error('[FirebaseConfigLoader] Invalid API key format');
104
100
  }
@@ -106,7 +102,7 @@ export function loadFirebaseConfig(): FirebaseConfig | null {
106
102
  }
107
103
 
108
104
  // Validate authDomain format (should be like "projectId.firebaseapp.com")
109
- if (!authDomain.includes('.firebaseapp.com') && !authDomain.includes('.web.app')) {
105
+ if (!isValidFirebaseAuthDomain(authDomain)) {
110
106
  if (__DEV__) {
111
107
  console.warn('[FirebaseConfigLoader] Unusual authDomain format, expected "projectId.firebaseapp.com" or similar');
112
108
  }