@umituz/react-native-firebase 1.13.149 → 1.13.150
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 +1 -1
- package/src/auth/infrastructure/services/apple-auth.service.ts +10 -2
- package/src/auth/infrastructure/services/google-auth.service.ts +10 -2
- package/src/auth/infrastructure/services/reauthentication.service.ts +5 -1
- package/src/auth/presentation/hooks/shared/auth-hooks.util.ts +60 -0
- package/src/auth/presentation/hooks/shared/hook-utils.util.ts +14 -216
- package/src/auth/presentation/hooks/shared/safe-state-hooks.util.ts +76 -0
- package/src/auth/presentation/hooks/shared/state-hooks.util.ts +97 -0
- package/src/domain/utils/async-executor.util.ts +16 -169
- package/src/domain/utils/error-handler.util.ts +18 -170
- package/src/domain/utils/error-handlers/error-checkers.ts +120 -0
- package/src/domain/utils/error-handlers/error-converters.ts +48 -0
- package/src/domain/utils/error-handlers/error-messages.ts +18 -0
- package/src/domain/utils/executors/advanced-executors.util.ts +59 -0
- package/src/domain/utils/executors/basic-executors.util.ts +56 -0
- package/src/domain/utils/executors/batch-executors.util.ts +42 -0
- package/src/domain/utils/executors/error-converters.util.ts +45 -0
- package/src/domain/utils/result/result-creators.ts +49 -0
- package/src/domain/utils/result/result-helpers.ts +60 -0
- package/src/domain/utils/result/result-types.ts +40 -0
- package/src/domain/utils/result.util.ts +28 -127
- package/src/domain/utils/validation.util.ts +38 -209
- package/src/domain/utils/validators/composite.validator.ts +24 -0
- package/src/domain/utils/validators/firebase.validator.ts +45 -0
- package/src/domain/utils/validators/generic.validator.ts +59 -0
- package/src/domain/utils/validators/string.validator.ts +25 -0
- package/src/domain/utils/validators/url.validator.ts +36 -0
- package/src/domain/utils/validators/user-input.validator.ts +54 -0
- package/src/firestore/infrastructure/middleware/QuotaTrackingMiddleware.ts +10 -3
- package/src/firestore/utils/deduplication/timer-manager.util.ts +2 -2
- package/src/firestore/utils/pagination.helper.ts +3 -1
- package/src/infrastructure/config/FirebaseClient.ts +28 -189
- package/src/infrastructure/config/clients/FirebaseClientSingleton.ts +82 -0
- package/src/infrastructure/config/services/FirebaseInitializationService.ts +115 -0
- package/src/init/createFirebaseInitModule.ts +9 -3
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase-Specific Validators
|
|
3
|
+
* Validation utilities for Firebase configuration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { isValidString } from './string.validator';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Validate Firebase API key format
|
|
10
|
+
* Firebase API keys typically start with "AIza" followed by 35 characters
|
|
11
|
+
*/
|
|
12
|
+
export function isValidFirebaseApiKey(apiKey: string): boolean {
|
|
13
|
+
if (!isValidString(apiKey)) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
const apiKeyPattern = /^AIza[0-9A-Za-z_-]{35}$/;
|
|
17
|
+
return apiKeyPattern.test(apiKey.trim());
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Validate Firebase authDomain format
|
|
22
|
+
* Expected format: "projectId.firebaseapp.com" or "projectId.web.app"
|
|
23
|
+
*/
|
|
24
|
+
export function isValidFirebaseAuthDomain(authDomain: string): boolean {
|
|
25
|
+
if (!isValidString(authDomain)) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
const trimmed = authDomain.trim();
|
|
29
|
+
return (
|
|
30
|
+
trimmed.includes('.firebaseapp.com') ||
|
|
31
|
+
trimmed.includes('.web.app')
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Validate Firebase projectId format
|
|
37
|
+
* Project IDs must be 6-30 characters, lowercase, alphanumeric, and may contain hyphens
|
|
38
|
+
*/
|
|
39
|
+
export function isValidFirebaseProjectId(projectId: string): boolean {
|
|
40
|
+
if (!isValidString(projectId)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const pattern = /^[a-z0-9][a-z0-9-]{4,28}[a-z0-9]$/;
|
|
44
|
+
return pattern.test(projectId.trim());
|
|
45
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic Validators
|
|
3
|
+
* Generic validation utilities for arrays, numbers, and objects
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Check if array is not empty
|
|
8
|
+
*/
|
|
9
|
+
export function isNonEmptyArray<T>(value: unknown): value is [T, ...T[]] {
|
|
10
|
+
return Array.isArray(value) && value.length > 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Check if number is in range
|
|
15
|
+
*/
|
|
16
|
+
export function isInRange(value: number, min: number, max: number): boolean {
|
|
17
|
+
return typeof value === 'number' && value >= min && value <= max;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Check if number is positive
|
|
22
|
+
*/
|
|
23
|
+
export function isPositive(value: number): boolean {
|
|
24
|
+
return typeof value === 'number' && value > 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if number is non-negative
|
|
29
|
+
*/
|
|
30
|
+
export function isNonNegative(value: number): boolean {
|
|
31
|
+
return typeof value === 'number' && value >= 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Validate object has required properties
|
|
36
|
+
*/
|
|
37
|
+
export function hasRequiredProperties<T extends Record<string, unknown>>(
|
|
38
|
+
obj: unknown,
|
|
39
|
+
requiredProps: (keyof T)[]
|
|
40
|
+
): obj is T {
|
|
41
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
return requiredProps.every((prop) => prop in obj);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Validate all items in array match predicate
|
|
49
|
+
*/
|
|
50
|
+
export function allMatch<T>(items: unknown[], predicate: (item: unknown) => item is T): boolean {
|
|
51
|
+
return Array.isArray(items) && items.every(predicate);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Validate at least one item in array matches predicate
|
|
56
|
+
*/
|
|
57
|
+
export function anyMatch<T>(items: unknown[], predicate: (item: unknown) => item is T): boolean {
|
|
58
|
+
return Array.isArray(items) && items.some(predicate);
|
|
59
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String Validators
|
|
3
|
+
* Basic string validation utilities
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Check if a string is a valid non-empty value
|
|
8
|
+
*/
|
|
9
|
+
export function isValidString(value: unknown): value is string {
|
|
10
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Check if a string is empty or whitespace only
|
|
15
|
+
*/
|
|
16
|
+
export function isEmptyString(value: unknown): boolean {
|
|
17
|
+
return typeof value === 'string' && value.trim().length === 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Check if value is defined (not null or undefined)
|
|
22
|
+
*/
|
|
23
|
+
export function isDefined<T>(value: T | null | undefined): value is T {
|
|
24
|
+
return value !== null && value !== undefined;
|
|
25
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL Validators
|
|
3
|
+
* Validation utilities for URLs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { isValidString } from './string.validator';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Validate URL format
|
|
10
|
+
*/
|
|
11
|
+
export function isValidUrl(url: string): boolean {
|
|
12
|
+
if (!isValidString(url)) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
new URL(url.trim());
|
|
17
|
+
return true;
|
|
18
|
+
} catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Validate HTTPS URL
|
|
25
|
+
*/
|
|
26
|
+
export function isValidHttpsUrl(url: string): boolean {
|
|
27
|
+
if (!isValidString(url)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const urlObj = new URL(url.trim());
|
|
32
|
+
return urlObj.protocol === 'https:';
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Input Validators
|
|
3
|
+
* Validation utilities for user input (email, password, username, phone)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { isValidString } from './string.validator';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Validate email format
|
|
10
|
+
*/
|
|
11
|
+
export function isValidEmail(email: string): boolean {
|
|
12
|
+
if (!isValidString(email)) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
16
|
+
return emailPattern.test(email.trim());
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Validate password strength
|
|
21
|
+
* At least 8 characters, containing uppercase, lowercase, and number
|
|
22
|
+
*/
|
|
23
|
+
export function isStrongPassword(password: string): boolean {
|
|
24
|
+
if (!isValidString(password) || password.length < 8) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
const hasUpperCase = /[A-Z]/.test(password);
|
|
28
|
+
const hasLowerCase = /[a-z]/.test(password);
|
|
29
|
+
const hasNumber = /[0-9]/.test(password);
|
|
30
|
+
return hasUpperCase && hasLowerCase && hasNumber;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Validate username format
|
|
35
|
+
* Alphanumeric, underscores, and hyphens, 3-20 characters
|
|
36
|
+
*/
|
|
37
|
+
export function isValidUsername(username: string): boolean {
|
|
38
|
+
if (!isValidString(username)) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
const pattern = /^[a-zA-Z0-9_-]{3,20}$/;
|
|
42
|
+
return pattern.test(username);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Validate phone number format (basic check)
|
|
47
|
+
*/
|
|
48
|
+
export function isValidPhoneNumber(phone: string): boolean {
|
|
49
|
+
if (!isValidString(phone)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const cleaned = phone.replace(/\s+/g, '').replace(/[-+()]/g, '');
|
|
53
|
+
return /^[0-9]{10,15}$/.test(cleaned);
|
|
54
|
+
}
|
|
@@ -43,8 +43,11 @@ export class QuotaTrackingMiddleware {
|
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
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
|
|
46
49
|
*/
|
|
47
|
-
trackRead(
|
|
50
|
+
trackRead(_collection: string, count: number = 1, cached: boolean = false): void {
|
|
48
51
|
if (!cached) {
|
|
49
52
|
this.readCount += count;
|
|
50
53
|
}
|
|
@@ -52,15 +55,19 @@ export class QuotaTrackingMiddleware {
|
|
|
52
55
|
|
|
53
56
|
/**
|
|
54
57
|
* Track write operation
|
|
58
|
+
* @param _collection - Collection name (reserved for future per-collection tracking)
|
|
59
|
+
* @param count - Number of documents written
|
|
55
60
|
*/
|
|
56
|
-
trackWrite(
|
|
61
|
+
trackWrite(_collection: string, count: number = 1): void {
|
|
57
62
|
this.writeCount += count;
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
/**
|
|
61
66
|
* Track delete operation
|
|
67
|
+
* @param _collection - Collection name (reserved for future per-collection tracking)
|
|
68
|
+
* @param count - Number of documents deleted
|
|
62
69
|
*/
|
|
63
|
-
trackDelete(
|
|
70
|
+
trackDelete(_collection: string, count: number = 1): void {
|
|
64
71
|
this.deleteCount += count;
|
|
65
72
|
}
|
|
66
73
|
|
|
@@ -31,8 +31,8 @@ export class TimerManager {
|
|
|
31
31
|
this.options.onCleanup();
|
|
32
32
|
} catch (error) {
|
|
33
33
|
// Silently handle cleanup errors to prevent timer from causing issues
|
|
34
|
-
// Log error in development for debugging
|
|
35
|
-
if (
|
|
34
|
+
// Log error in development for debugging (use __DEV__ for React Native)
|
|
35
|
+
if (__DEV__) {
|
|
36
36
|
console.error('TimerManager cleanup error:', error);
|
|
37
37
|
}
|
|
38
38
|
}
|
|
@@ -33,7 +33,9 @@ export class PaginationHelper<T> {
|
|
|
33
33
|
): PaginatedResult<T> {
|
|
34
34
|
const hasMore = items.length > pageLimit;
|
|
35
35
|
const resultItems = hasMore ? items.slice(0, pageLimit) : items;
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
// Safe access: check array is not empty before accessing last item
|
|
38
|
+
const lastItem = resultItems.length > 0 ? resultItems[resultItems.length - 1] : undefined;
|
|
37
39
|
const nextCursor = hasMore && lastItem
|
|
38
40
|
? getCursor(lastItem)
|
|
39
41
|
: null;
|
|
@@ -6,192 +6,31 @@
|
|
|
6
6
|
*
|
|
7
7
|
* IMPORTANT: This package does NOT read from .env files.
|
|
8
8
|
* Configuration must be provided by the application.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
auth: boolean | null;
|
|
38
|
-
authError?: string;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Firebase Client Singleton
|
|
43
|
-
* Orchestrates Firebase initialization using specialized initializers
|
|
44
|
-
*/
|
|
45
|
-
class FirebaseClientSingleton implements IFirebaseClient {
|
|
46
|
-
private static instance: FirebaseClientSingleton | null = null;
|
|
47
|
-
private state: FirebaseClientState;
|
|
48
|
-
private lastError: string | null = null;
|
|
49
|
-
|
|
50
|
-
private constructor() {
|
|
51
|
-
this.state = new FirebaseClientState();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
static getInstance(): FirebaseClientSingleton {
|
|
55
|
-
if (!FirebaseClientSingleton.instance) {
|
|
56
|
-
FirebaseClientSingleton.instance = new FirebaseClientSingleton();
|
|
57
|
-
}
|
|
58
|
-
return FirebaseClientSingleton.instance;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
initialize(config: FirebaseConfig): FirebaseApp | null {
|
|
62
|
-
try {
|
|
63
|
-
const result = FirebaseInitializationOrchestrator.initialize(config);
|
|
64
|
-
// Sync state with orchestrator result
|
|
65
|
-
this.state.setInstance(result);
|
|
66
|
-
this.lastError = null;
|
|
67
|
-
return result;
|
|
68
|
-
} catch (error) {
|
|
69
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
70
|
-
this.lastError = errorMessage;
|
|
71
|
-
this.state.setInitializationError(errorMessage);
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
getApp(): FirebaseApp | null {
|
|
77
|
-
// Check local state first
|
|
78
|
-
const localApp = this.state.getApp();
|
|
79
|
-
if (localApp) return localApp;
|
|
80
|
-
|
|
81
|
-
// Try to get from orchestrator
|
|
82
|
-
const result = FirebaseInitializationOrchestrator.autoInitialize();
|
|
83
|
-
if (result) {
|
|
84
|
-
this.state.setInstance(result);
|
|
85
|
-
}
|
|
86
|
-
return result;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
isInitialized(): boolean {
|
|
90
|
-
// Check both local state and orchestrator for consistency
|
|
91
|
-
if (this.state.isInitialized()) return true;
|
|
92
|
-
|
|
93
|
-
// Check if Firebase has any apps initialized
|
|
94
|
-
return FirebaseInitializationOrchestrator.autoInitialize() !== null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
getInitializationError(): string | null {
|
|
98
|
-
// Check local state first
|
|
99
|
-
const localError = this.state.getInitializationError();
|
|
100
|
-
if (localError) return localError;
|
|
101
|
-
// Return last error
|
|
102
|
-
return this.lastError;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
reset(): void {
|
|
106
|
-
// Reset local state
|
|
107
|
-
this.state.reset();
|
|
108
|
-
this.lastError = null;
|
|
109
|
-
// Note: We don't reset Firebase apps as they might be in use
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export const firebaseClient = FirebaseClientSingleton.getInstance();
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Initialize Firebase client with configuration
|
|
117
|
-
*/
|
|
118
|
-
export function initializeFirebase(config: FirebaseConfig): FirebaseApp | null {
|
|
119
|
-
return firebaseClient.initialize(config);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Get Firebase app instance
|
|
124
|
-
* Auto-initializes from Constants/environment if not already initialized
|
|
125
|
-
*/
|
|
126
|
-
export function getFirebaseApp(): FirebaseApp | null {
|
|
127
|
-
return firebaseClient.getApp();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Auto-initialize Firebase from Constants/environment
|
|
132
|
-
*/
|
|
133
|
-
export function autoInitializeFirebase(): FirebaseApp | null {
|
|
134
|
-
const config = loadFirebaseConfig();
|
|
135
|
-
if (config) {
|
|
136
|
-
return initializeFirebase(config);
|
|
137
|
-
}
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Initialize all Firebase services (App and Auth)
|
|
143
|
-
* This is the main entry point for applications
|
|
144
|
-
*/
|
|
145
|
-
export async function initializeAllFirebaseServices(
|
|
146
|
-
config?: FirebaseConfig,
|
|
147
|
-
options?: ServiceInitializationOptions
|
|
148
|
-
): Promise<ServiceInitializationResult> {
|
|
149
|
-
const app = config ? initializeFirebase(config) : autoInitializeFirebase();
|
|
150
|
-
|
|
151
|
-
if (!app) {
|
|
152
|
-
return {
|
|
153
|
-
app: null,
|
|
154
|
-
auth: null,
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Initialize Auth if provided
|
|
159
|
-
let authSuccess = null;
|
|
160
|
-
let authError: string | undefined;
|
|
161
|
-
|
|
162
|
-
if (options?.authInitializer) {
|
|
163
|
-
try {
|
|
164
|
-
await options.authInitializer();
|
|
165
|
-
authSuccess = true;
|
|
166
|
-
} catch (error) {
|
|
167
|
-
authError = error instanceof Error ? error.message : 'Auth initialization failed';
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return {
|
|
172
|
-
app,
|
|
173
|
-
auth: authSuccess,
|
|
174
|
-
authError,
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Check if Firebase client is initialized
|
|
180
|
-
*/
|
|
181
|
-
export function isFirebaseInitialized(): boolean {
|
|
182
|
-
return firebaseClient.isInitialized();
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Get initialization error if any
|
|
187
|
-
*/
|
|
188
|
-
export function getFirebaseInitializationError(): string | null {
|
|
189
|
-
return firebaseClient.getInitializationError();
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Reset Firebase client instance
|
|
194
|
-
*/
|
|
195
|
-
export function resetFirebaseClient(): void {
|
|
196
|
-
firebaseClient.reset();
|
|
197
|
-
}
|
|
9
|
+
*
|
|
10
|
+
* @deprecated Import from specific files instead:
|
|
11
|
+
* - FirebaseClientSingleton from './clients/FirebaseClientSingleton'
|
|
12
|
+
* - Initialization functions from './services/FirebaseInitializationService'
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export type { FirebaseApp } from './initializers/FirebaseAppInitializer';
|
|
16
|
+
|
|
17
|
+
// Export singleton for backward compatibility
|
|
18
|
+
export { FirebaseClientSingleton } from './clients/FirebaseClientSingleton';
|
|
19
|
+
export const firebaseClient = require('./clients/FirebaseClientSingleton').FirebaseClientSingleton.getInstance();
|
|
20
|
+
|
|
21
|
+
// Re-export types and functions
|
|
22
|
+
export type {
|
|
23
|
+
AuthInitializer,
|
|
24
|
+
ServiceInitializationOptions,
|
|
25
|
+
ServiceInitializationResult,
|
|
26
|
+
} from './services/FirebaseInitializationService';
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
initializeFirebase,
|
|
30
|
+
getFirebaseApp,
|
|
31
|
+
autoInitializeFirebase,
|
|
32
|
+
initializeAllFirebaseServices,
|
|
33
|
+
isFirebaseInitialized,
|
|
34
|
+
getFirebaseInitializationError,
|
|
35
|
+
resetFirebaseClient,
|
|
36
|
+
} from './services/FirebaseInitializationService';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase Client Singleton
|
|
3
|
+
* Core singleton implementation for Firebase client
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { FirebaseConfig } from '../../../domain/value-objects/FirebaseConfig';
|
|
7
|
+
import type { IFirebaseClient } from '../../../application/ports/IFirebaseClient';
|
|
8
|
+
import type { FirebaseApp } from '../initializers/FirebaseAppInitializer';
|
|
9
|
+
import { FirebaseClientState } from '../state/FirebaseClientState';
|
|
10
|
+
import { FirebaseInitializationOrchestrator } from '../orchestrators/FirebaseInitializationOrchestrator';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Firebase Client Singleton
|
|
14
|
+
* Orchestrates Firebase initialization using specialized initializers
|
|
15
|
+
*/
|
|
16
|
+
export class FirebaseClientSingleton implements IFirebaseClient {
|
|
17
|
+
private static instance: FirebaseClientSingleton | null = null;
|
|
18
|
+
private state: FirebaseClientState;
|
|
19
|
+
private lastError: string | null = null;
|
|
20
|
+
|
|
21
|
+
private constructor() {
|
|
22
|
+
this.state = new FirebaseClientState();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static getInstance(): FirebaseClientSingleton {
|
|
26
|
+
if (!FirebaseClientSingleton.instance) {
|
|
27
|
+
FirebaseClientSingleton.instance = new FirebaseClientSingleton();
|
|
28
|
+
}
|
|
29
|
+
return FirebaseClientSingleton.instance;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
initialize(config: FirebaseConfig): FirebaseApp | null {
|
|
33
|
+
try {
|
|
34
|
+
const result = FirebaseInitializationOrchestrator.initialize(config);
|
|
35
|
+
// Sync state with orchestrator result
|
|
36
|
+
this.state.setInstance(result);
|
|
37
|
+
this.lastError = null;
|
|
38
|
+
return result;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
41
|
+
this.lastError = errorMessage;
|
|
42
|
+
this.state.setInitializationError(errorMessage);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getApp(): FirebaseApp | null {
|
|
48
|
+
// Check local state first
|
|
49
|
+
const localApp = this.state.getApp();
|
|
50
|
+
if (localApp) return localApp;
|
|
51
|
+
|
|
52
|
+
// Try to get from orchestrator
|
|
53
|
+
const result = FirebaseInitializationOrchestrator.autoInitialize();
|
|
54
|
+
if (result) {
|
|
55
|
+
this.state.setInstance(result);
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
isInitialized(): boolean {
|
|
61
|
+
// Check both local state and orchestrator for consistency
|
|
62
|
+
if (this.state.isInitialized()) return true;
|
|
63
|
+
|
|
64
|
+
// Check if Firebase has any apps initialized
|
|
65
|
+
return FirebaseInitializationOrchestrator.autoInitialize() !== null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
getInitializationError(): string | null {
|
|
69
|
+
// Check local state first
|
|
70
|
+
const localError = this.state.getInitializationError();
|
|
71
|
+
if (localError) return localError;
|
|
72
|
+
// Return last error
|
|
73
|
+
return this.lastError;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
reset(): void {
|
|
77
|
+
// Reset local state
|
|
78
|
+
this.state.reset();
|
|
79
|
+
this.lastError = null;
|
|
80
|
+
// Note: We don't reset Firebase apps as they might be in use
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase Initialization Service
|
|
3
|
+
* Service layer for Firebase initialization with auth support
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { FirebaseConfig } from '../../../domain/value-objects/FirebaseConfig';
|
|
7
|
+
import type { FirebaseApp } from '../initializers/FirebaseAppInitializer';
|
|
8
|
+
import { FirebaseClientSingleton } from '../clients/FirebaseClientSingleton';
|
|
9
|
+
import { loadFirebaseConfig } from '../FirebaseConfigLoader';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Auth initializer callback type
|
|
13
|
+
* Returns a Promise that resolves when auth initialization is complete
|
|
14
|
+
*/
|
|
15
|
+
export type AuthInitializer = () => Promise<void>;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Service initialization options
|
|
19
|
+
*/
|
|
20
|
+
export interface ServiceInitializationOptions {
|
|
21
|
+
authInitializer?: AuthInitializer;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Service initialization result interface
|
|
26
|
+
*/
|
|
27
|
+
export interface ServiceInitializationResult {
|
|
28
|
+
app: FirebaseApp | null;
|
|
29
|
+
auth: boolean | null;
|
|
30
|
+
authError?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Initialize Firebase client with configuration
|
|
35
|
+
*/
|
|
36
|
+
export function initializeFirebase(config: FirebaseConfig): FirebaseApp | null {
|
|
37
|
+
return FirebaseClientSingleton.getInstance().initialize(config);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get Firebase app instance
|
|
42
|
+
* Auto-initializes from Constants/environment if not already initialized
|
|
43
|
+
*/
|
|
44
|
+
export function getFirebaseApp(): FirebaseApp | null {
|
|
45
|
+
return FirebaseClientSingleton.getInstance().getApp();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Auto-initialize Firebase from Constants/environment
|
|
50
|
+
*/
|
|
51
|
+
export function autoInitializeFirebase(): FirebaseApp | null {
|
|
52
|
+
const config = loadFirebaseConfig();
|
|
53
|
+
if (config) {
|
|
54
|
+
return initializeFirebase(config);
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Initialize all Firebase services (App and Auth)
|
|
61
|
+
* This is the main entry point for applications
|
|
62
|
+
*/
|
|
63
|
+
export async function initializeAllFirebaseServices(
|
|
64
|
+
config?: FirebaseConfig,
|
|
65
|
+
options?: ServiceInitializationOptions
|
|
66
|
+
): Promise<ServiceInitializationResult> {
|
|
67
|
+
const app = config ? initializeFirebase(config) : autoInitializeFirebase();
|
|
68
|
+
|
|
69
|
+
if (!app) {
|
|
70
|
+
return {
|
|
71
|
+
app: null,
|
|
72
|
+
auth: null,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Initialize Auth if provided
|
|
77
|
+
let authSuccess = null;
|
|
78
|
+
let authError: string | undefined;
|
|
79
|
+
|
|
80
|
+
if (options?.authInitializer) {
|
|
81
|
+
try {
|
|
82
|
+
await options.authInitializer();
|
|
83
|
+
authSuccess = true;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
authError = error instanceof Error ? error.message : 'Auth initialization failed';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
app,
|
|
91
|
+
auth: authSuccess,
|
|
92
|
+
authError,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Check if Firebase client is initialized
|
|
98
|
+
*/
|
|
99
|
+
export function isFirebaseInitialized(): boolean {
|
|
100
|
+
return FirebaseClientSingleton.getInstance().isInitialized();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get initialization error if any
|
|
105
|
+
*/
|
|
106
|
+
export function getFirebaseInitializationError(): string | null {
|
|
107
|
+
return FirebaseClientSingleton.getInstance().getInitializationError();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Reset Firebase client instance
|
|
112
|
+
*/
|
|
113
|
+
export function resetFirebaseClient(): void {
|
|
114
|
+
FirebaseClientSingleton.getInstance().reset();
|
|
115
|
+
}
|