@umituz/react-native-firebase 1.12.2 → 1.13.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-firebase",
3
- "version": "1.12.2",
3
+ "version": "1.13.1",
4
4
  "description": "Unified Firebase package for React Native apps - Centralized initialization and core services (Analytics, Crashlytics).",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -2,16 +2,21 @@
2
2
  * Native Analytics Adapter
3
3
  * Single Responsibility: Handle Firebase Analytics native implementation
4
4
  * Uses React Native Firebase v23+ modular API
5
+ *
6
+ * NOTE: This adapter uses optional import to support Expo Go.
7
+ * Native modules are only available in development builds or standalone apps.
5
8
  */
6
9
 
7
- import {
8
- getAnalytics,
9
- logEvent,
10
- setUserId,
11
- setUserProperties,
12
- resetAnalyticsData,
13
- setAnalyticsCollectionEnabled,
14
- } from '@react-native-firebase/analytics';
10
+ // Optional import - will be null in Expo Go
11
+ let nativeAnalytics: any = null;
12
+ try {
13
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
14
+ nativeAnalytics = require('@react-native-firebase/analytics');
15
+ } catch {
16
+ // Native module not available (e.g., Expo Go)
17
+ // eslint-disable-next-line no-console
18
+ if (__DEV__) console.warn('⚠️ Firebase Analytics: Native module not available');
19
+ }
15
20
 
16
21
  export interface NativeAnalyticsAdapter {
17
22
  getAnalytics(): any;
@@ -22,33 +27,37 @@ export interface NativeAnalyticsAdapter {
22
27
  setAnalyticsCollectionEnabled(analytics: any, enabled: boolean): Promise<void>;
23
28
  }
24
29
 
25
- export const nativeAnalyticsAdapter: NativeAnalyticsAdapter = {
26
- getAnalytics(): any {
27
- return getAnalytics();
28
- },
29
- async logEvent(
30
- analytics: any,
31
- eventName: string,
32
- params?: Record<string, any>,
33
- ): Promise<void> {
34
- await logEvent(analytics, eventName, params);
35
- },
36
- async setUserId(analytics: any, userId: string): Promise<void> {
37
- await setUserId(analytics, userId);
38
- },
39
- async setUserProperties(
40
- analytics: any,
41
- properties: Record<string, string>,
42
- ): Promise<void> {
43
- await setUserProperties(analytics, properties);
44
- },
45
- async resetAnalyticsData(analytics: any): Promise<void> {
46
- await resetAnalyticsData(analytics);
47
- },
48
- async setAnalyticsCollectionEnabled(
49
- analytics: any,
50
- enabled: boolean,
51
- ): Promise<void> {
52
- await setAnalyticsCollectionEnabled(analytics, enabled);
53
- },
54
- };
30
+ declare const __DEV__: boolean;
31
+
32
+ export const nativeAnalyticsAdapter: NativeAnalyticsAdapter | null = nativeAnalytics
33
+ ? {
34
+ getAnalytics(): any {
35
+ return nativeAnalytics.getAnalytics();
36
+ },
37
+ async logEvent(
38
+ analytics: any,
39
+ eventName: string,
40
+ params?: Record<string, any>,
41
+ ): Promise<void> {
42
+ await nativeAnalytics.logEvent(analytics, eventName, params);
43
+ },
44
+ async setUserId(analytics: any, userId: string): Promise<void> {
45
+ await nativeAnalytics.setUserId(analytics, userId);
46
+ },
47
+ async setUserProperties(
48
+ analytics: any,
49
+ properties: Record<string, string>,
50
+ ): Promise<void> {
51
+ await nativeAnalytics.setUserProperties(analytics, properties);
52
+ },
53
+ async resetAnalyticsData(analytics: any): Promise<void> {
54
+ await nativeAnalytics.resetAnalyticsData(analytics);
55
+ },
56
+ async setAnalyticsCollectionEnabled(
57
+ analytics: any,
58
+ enabled: boolean,
59
+ ): Promise<void> {
60
+ await nativeAnalytics.setAnalyticsCollectionEnabled(analytics, enabled);
61
+ },
62
+ }
63
+ : null;
@@ -2,18 +2,22 @@
2
2
  * Native Crashlytics Adapter
3
3
  * Single Responsibility: Handle Firebase Crashlytics native implementation
4
4
  * Uses React Native Firebase v23+ modular API
5
+ *
6
+ * NOTE: This adapter uses optional import to support Expo Go.
7
+ * Native modules are only available in development builds or standalone apps.
5
8
  */
6
9
 
7
- import {
8
- getCrashlytics,
9
- log,
10
- recordError,
11
- setAttribute,
12
- setUserId,
13
- } from '@react-native-firebase/crashlytics';
14
- import type { FirebaseCrashlyticsTypes } from '@react-native-firebase/crashlytics';
10
+ // Optional import - will be null in Expo Go
11
+ let nativeCrashlytics: any = null;
12
+ try {
13
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
14
+ nativeCrashlytics = require('@react-native-firebase/crashlytics');
15
+ } catch {
16
+ // Native module not available (e.g., Expo Go)
17
+ // Silent fail - crashlytics is optional
18
+ }
15
19
 
16
- export type CrashlyticsInstance = FirebaseCrashlyticsTypes.Module;
20
+ export type CrashlyticsInstance = any;
17
21
 
18
22
  export interface NativeCrashlyticsAdapter {
19
23
  getCrashlytics(): CrashlyticsInstance;
@@ -23,54 +27,58 @@ export interface NativeCrashlyticsAdapter {
23
27
  log(crashlytics: CrashlyticsInstance, message: string): Promise<void>;
24
28
  }
25
29
 
26
- export const nativeCrashlyticsAdapter: NativeCrashlyticsAdapter = {
27
- getCrashlytics(): CrashlyticsInstance {
28
- return getCrashlytics();
29
- },
30
- async setUserId(crashlytics: CrashlyticsInstance, userId: string): Promise<void> {
31
- try {
32
- await setUserId(crashlytics, userId);
33
- } catch (error) {
34
- /* eslint-disable-next-line no-console */
35
- if (__DEV__) {
36
- console.warn('[Crashlytics] Failed to set user ID:', error);
37
- }
38
- }
39
- },
40
- async setAttributes(
41
- crashlytics: CrashlyticsInstance,
42
- attributes: Record<string, string>,
43
- ): Promise<void> {
44
- try {
45
- // Set each attribute individually using the modular API
46
- for (const [key, value] of Object.entries(attributes)) {
47
- await setAttribute(crashlytics, key, value);
48
- }
49
- } catch (error) {
50
- /* eslint-disable-next-line no-console */
51
- if (__DEV__) {
52
- console.warn('[Crashlytics] Failed to set attributes:', error);
53
- }
54
- }
55
- },
56
- async recordError(crashlytics: CrashlyticsInstance, error: Error): Promise<void> {
57
- try {
58
- await recordError(crashlytics, error);
59
- } catch (err) {
60
- /* eslint-disable-next-line no-console */
61
- if (__DEV__) {
62
- console.warn('[Crashlytics] Failed to record error:', err);
63
- }
64
- }
65
- },
66
- async log(crashlytics: CrashlyticsInstance, message: string): Promise<void> {
67
- try {
68
- await log(crashlytics, message);
69
- } catch (error) {
70
- /* eslint-disable-next-line no-console */
71
- if (__DEV__) {
72
- console.warn('[Crashlytics] Failed to log message:', error);
73
- }
30
+ declare const __DEV__: boolean;
31
+
32
+ export const nativeCrashlyticsAdapter: NativeCrashlyticsAdapter | null = nativeCrashlytics
33
+ ? {
34
+ getCrashlytics(): CrashlyticsInstance {
35
+ return nativeCrashlytics.getCrashlytics();
36
+ },
37
+ async setUserId(crashlytics: CrashlyticsInstance, userId: string): Promise<void> {
38
+ try {
39
+ await nativeCrashlytics.setUserId(crashlytics, userId);
40
+ } catch (error) {
41
+ /* eslint-disable-next-line no-console */
42
+ if (__DEV__) {
43
+ console.warn('[Crashlytics] Failed to set user ID:', error);
44
+ }
45
+ }
46
+ },
47
+ async setAttributes(
48
+ crashlytics: CrashlyticsInstance,
49
+ attributes: Record<string, string>,
50
+ ): Promise<void> {
51
+ try {
52
+ // Set each attribute individually using the modular API
53
+ for (const [key, value] of Object.entries(attributes)) {
54
+ await nativeCrashlytics.setAttribute(crashlytics, key, value);
55
+ }
56
+ } catch (error) {
57
+ /* eslint-disable-next-line no-console */
58
+ if (__DEV__) {
59
+ console.warn('[Crashlytics] Failed to set attributes:', error);
60
+ }
61
+ }
62
+ },
63
+ async recordError(crashlytics: CrashlyticsInstance, error: Error): Promise<void> {
64
+ try {
65
+ await nativeCrashlytics.recordError(crashlytics, error);
66
+ } catch (err) {
67
+ /* eslint-disable-next-line no-console */
68
+ if (__DEV__) {
69
+ console.warn('[Crashlytics] Failed to record error:', err);
70
+ }
71
+ }
72
+ },
73
+ async log(crashlytics: CrashlyticsInstance, message: string): Promise<void> {
74
+ try {
75
+ await nativeCrashlytics.log(crashlytics, message);
76
+ } catch (error) {
77
+ /* eslint-disable-next-line no-console */
78
+ if (__DEV__) {
79
+ console.warn('[Crashlytics] Failed to log message:', error);
80
+ }
81
+ }
82
+ },
74
83
  }
75
- },
76
- };
84
+ : null;
package/src/index.ts CHANGED
@@ -38,6 +38,9 @@ export {
38
38
 
39
39
  export type {
40
40
  FirebaseApp,
41
+ AuthInitializer,
42
+ ServiceInitializationOptions,
43
+ ServiceInitializationResult,
41
44
  } from './infrastructure/config/FirebaseClient';
42
45
 
43
46
  // =============================================================================
@@ -7,6 +7,9 @@
7
7
  * IMPORTANT: This package does NOT read from .env files.
8
8
  * Configuration must be provided by the application.
9
9
  *
10
+ * NOTE: Auth initialization is handled by the main app via callback.
11
+ * This removes the need for dynamic require() which causes issues in production.
12
+ *
10
13
  * SOLID Principles:
11
14
  * - Single Responsibility: Only orchestrates initialization, delegates to specialized classes
12
15
  * - Open/Closed: Extensible through configuration, closed for modification
@@ -15,154 +18,28 @@
15
18
 
16
19
  import type { FirebaseConfig } from '../../domain/value-objects/FirebaseConfig';
17
20
  import type { IFirebaseClient } from '../../application/ports/IFirebaseClient';
18
- import { FirebaseConfigValidator } from './validators/FirebaseConfigValidator';
21
+ import type { FirebaseApp } from './initializers/FirebaseAppInitializer';
22
+ import { FirebaseClientState } from './state/FirebaseClientState';
23
+ import { FirebaseInitializationOrchestrator } from './orchestrators/FirebaseInitializationOrchestrator';
19
24
  import {
20
- FirebaseAppInitializer,
21
- type FirebaseApp,
22
- } from './initializers/FirebaseAppInitializer';
25
+ FirebaseServiceInitializer,
26
+ type AuthInitializer,
27
+ type ServiceInitializationOptions,
28
+ } from './services/FirebaseServiceInitializer';
23
29
  import { loadFirebaseConfig } from './FirebaseConfigLoader';
24
- import { firebaseAnalyticsService } from '../../analytics';
25
- import { firebaseCrashlyticsService } from '../../crashlytics';
26
30
 
27
- export type { FirebaseApp };
31
+ export type { FirebaseApp, AuthInitializer, ServiceInitializationOptions };
28
32
 
29
- // Development environment check
30
33
  declare const __DEV__: boolean;
31
34
 
32
35
  /**
33
- * Firebase Client State Manager
34
- * Manages the state of Firebase initialization
35
- */
36
- class FirebaseClientState {
37
- private app: FirebaseApp | null = null;
38
- private initializationError: string | null = null;
39
-
40
- /**
41
- * Get the current Firebase app instance
42
- */
43
- getApp(): FirebaseApp | null {
44
- return this.app;
45
- }
46
-
47
- /**
48
- * Set the Firebase app instance
49
- */
50
- setApp(app: FirebaseApp | null): void {
51
- this.app = app;
52
- }
53
-
54
- /**
55
- * Check if client is initialized
56
- */
57
- isInitialized(): boolean {
58
- return this.app !== null;
59
- }
60
-
61
- /**
62
- * Get initialization error if any
63
- */
64
- getInitializationError(): string | null {
65
- return this.initializationError;
66
- }
67
-
68
- /**
69
- * Set initialization error
70
- */
71
- setInitializationError(error: string | null): void {
72
- this.initializationError = error;
73
- }
74
-
75
- /**
76
- * Reset the client state
77
- */
78
- reset(): void {
79
- this.app = null;
80
- this.initializationError = null;
81
- }
82
- }
83
-
84
- /**
85
- * Firebase Initialization Orchestrator
86
- * Handles the initialization logic
36
+ * Service initialization result interface
87
37
  */
88
- class FirebaseInitializationOrchestrator {
89
- /**
90
- * Initialize Firebase with configuration
91
- */
92
- static initialize(
93
- config: FirebaseConfig,
94
- state: FirebaseClientState
95
- ): FirebaseApp | null {
96
- // Return existing instance if already initialized
97
- if (state.isInitialized()) {
98
- if (__DEV__) {
99
- console.log('[Firebase] Already initialized, returning existing instance');
100
- }
101
- return state.getApp();
102
- }
103
-
104
- // Don't retry if initialization already failed
105
- if (state.getInitializationError()) {
106
- if (__DEV__) {
107
- console.log('[Firebase] Previous initialization failed, skipping retry');
108
- }
109
- return null;
110
- }
111
-
112
- try {
113
- if (__DEV__) {
114
- console.log('[Firebase] Initializing with projectId:', config.projectId);
115
- }
116
-
117
- // Validate configuration
118
- FirebaseConfigValidator.validate(config);
119
-
120
- // Initialize Firebase App
121
- const app = FirebaseAppInitializer.initialize(config);
122
- state.setApp(app);
123
-
124
- if (__DEV__) {
125
- console.log('[Firebase] Successfully initialized');
126
- }
127
-
128
- return app;
129
- } catch (error) {
130
- const errorMessage =
131
- error instanceof Error
132
- ? error.message
133
- : 'Failed to initialize Firebase client';
134
- state.setInitializationError(errorMessage);
135
-
136
- if (__DEV__) {
137
- console.error('[Firebase] Initialization failed:', errorMessage);
138
- }
139
-
140
- return null;
141
- }
142
- }
143
-
144
- /**
145
- * Auto-initialize Firebase from environment
146
- */
147
- static autoInitialize(state: FirebaseClientState): FirebaseApp | null {
148
- if (state.isInitialized() || state.getInitializationError()) {
149
- return state.getApp();
150
- }
151
-
152
- const autoConfig = loadFirebaseConfig();
153
- if (autoConfig) {
154
- if (__DEV__) {
155
- console.log('[Firebase] Auto-initializing with environment config');
156
- }
157
- return this.initialize(autoConfig, state);
158
- }
159
-
160
- if (__DEV__) {
161
- console.log('[Firebase] No configuration found for auto-initialization');
162
- }
163
-
164
- return null;
165
- }
38
+ export interface ServiceInitializationResult {
39
+ app: FirebaseApp | null;
40
+ auth: unknown;
41
+ analytics: unknown;
42
+ crashlytics: unknown;
166
43
  }
167
44
 
168
45
  /**
@@ -177,9 +54,6 @@ class FirebaseClientSingleton implements IFirebaseClient {
177
54
  this.state = new FirebaseClientState();
178
55
  }
179
56
 
180
- /**
181
- * Get singleton instance
182
- */
183
57
  static getInstance(): FirebaseClientSingleton {
184
58
  if (!FirebaseClientSingleton.instance) {
185
59
  FirebaseClientSingleton.instance = new FirebaseClientSingleton();
@@ -187,86 +61,39 @@ class FirebaseClientSingleton implements IFirebaseClient {
187
61
  return FirebaseClientSingleton.instance;
188
62
  }
189
63
 
190
- /**
191
- * Initialize Firebase client with configuration
192
- * Configuration must be provided by the application (not from .env)
193
- *
194
- * @param config - Firebase configuration
195
- * @returns Firebase app instance or null if initialization fails
196
- */
197
64
  initialize(config: FirebaseConfig): FirebaseApp | null {
198
65
  return FirebaseInitializationOrchestrator.initialize(config, this.state);
199
66
  }
200
67
 
201
- /**
202
- * Get the Firebase app instance
203
- * Auto-initializes from Constants/environment if not already initialized
204
- * Returns null if config is not available (offline mode - no error)
205
- * @returns Firebase app instance or null if not initialized
206
- */
207
68
  getApp(): FirebaseApp | null {
208
69
  return FirebaseInitializationOrchestrator.autoInitialize(this.state);
209
70
  }
210
71
 
211
- /**
212
- * Check if client is initialized
213
- */
214
72
  isInitialized(): boolean {
215
73
  return this.state.isInitialized();
216
74
  }
217
75
 
218
- /**
219
- * Get initialization error if any
220
- */
221
76
  getInitializationError(): string | null {
222
77
  return this.state.getInitializationError();
223
78
  }
224
79
 
225
- /**
226
- * Reset the client instance
227
- * Useful for testing
228
- */
229
80
  reset(): void {
230
81
  this.state.reset();
231
82
  }
232
83
  }
233
84
 
234
- /**
235
- * Singleton instance
236
- */
237
85
  export const firebaseClient = FirebaseClientSingleton.getInstance();
238
86
 
239
87
  /**
240
- * Initialize Firebase client
241
- * This is the main entry point for applications
242
- *
243
- * @param config - Firebase configuration (must be provided by app, not from .env)
244
- * @returns Firebase app instance or null if initialization fails
245
- *
246
- * @example
247
- * ```typescript
248
- * import { initializeFirebase } from '@umituz/react-native-firebase';
249
- *
250
- * const config = {
251
- * apiKey: 'your-api-key',
252
- * authDomain: 'your-project.firebaseapp.com',
253
- * projectId: 'your-project-id',
254
- * };
255
- *
256
- * const app = initializeFirebase(config);
257
- * ```
88
+ * Initialize Firebase client with configuration
258
89
  */
259
- export function initializeFirebase(
260
- config: FirebaseConfig
261
- ): FirebaseApp | null {
90
+ export function initializeFirebase(config: FirebaseConfig): FirebaseApp | null {
262
91
  return firebaseClient.initialize(config);
263
92
  }
264
93
 
265
94
  /**
266
95
  * Get Firebase app instance
267
96
  * Auto-initializes from Constants/environment if not already initialized
268
- * Returns null if config is not available (offline mode - no error)
269
- * @returns Firebase app instance or null if not initialized
270
97
  */
271
98
  export function getFirebaseApp(): FirebaseApp | null {
272
99
  return firebaseClient.getApp();
@@ -274,9 +101,6 @@ export function getFirebaseApp(): FirebaseApp | null {
274
101
 
275
102
  /**
276
103
  * Auto-initialize Firebase from Constants/environment
277
- * Called automatically when getFirebaseApp() is first accessed
278
- * Can be called manually to initialize early
279
- * @returns Firebase app instance or null if initialization fails
280
104
  */
281
105
  export function autoInitializeFirebase(): FirebaseApp | null {
282
106
  const config = loadFirebaseConfig();
@@ -286,97 +110,30 @@ export function autoInitializeFirebase(): FirebaseApp | null {
286
110
  return null;
287
111
  }
288
112
 
289
- /**
290
- * Service initialization result interface
291
- */
292
- interface ServiceInitializationResult {
293
- app: FirebaseApp | null;
294
- auth: any | null;
295
- analytics: any | null;
296
- crashlytics: any | null;
297
- }
298
-
299
- /**
300
- * Service initializer class for better separation of concerns
301
- */
302
- class ServiceInitializer {
303
- /**
304
- * Initialize Firebase Auth from external package
305
- */
306
- private static initializeAuth(): any | null {
307
- try {
308
- const authModule = require('@umituz/react-native-firebase-auth');
309
- const initializeFirebaseAuth = authModule.initializeFirebaseAuth;
310
-
311
- if (typeof initializeFirebaseAuth !== 'function') {
312
- if (__DEV__) {
313
- console.warn('[Firebase] initializeFirebaseAuth is not a function');
314
- }
315
- return null;
316
- }
317
-
318
- const auth = initializeFirebaseAuth();
319
- return auth;
320
- } catch (error) {
321
- if (__DEV__) {
322
- console.warn('[Firebase] Auth package not available:', error instanceof Error ? error.message : 'Unknown error');
323
- }
324
- return null;
325
- }
326
- }
327
-
328
- /**
329
- * Initialize all optional Firebase services
330
- */
331
- static async initializeServices(): Promise<{
332
- auth: any | null;
333
- analytics: any | null;
334
- crashlytics: any | null;
335
- }> {
336
- if (__DEV__) {
337
- console.log('[Firebase] Initializing optional services...');
338
- }
339
-
340
- // Initialize auth from external package
341
- const auth = this.initializeAuth();
342
-
343
- // Analytics and Crashlytics are local modules - use imported services directly
344
- const analytics = firebaseAnalyticsService;
345
- const crashlytics = firebaseCrashlyticsService;
346
-
347
- if (__DEV__) {
348
- console.log('[Firebase] Services initialized - Auth:', !!auth, 'Analytics:', !!analytics, 'Crashlytics:', !!crashlytics);
349
- }
350
-
351
- return { auth, analytics, crashlytics };
352
- }
353
- }
354
-
355
113
  /**
356
114
  * Initialize all Firebase services (App, Auth, Analytics, Crashlytics)
357
115
  * This is the main entry point for applications - call this once at app startup
358
- * All services will be initialized automatically if Firebase App is available
359
- *
360
- * @param config - Optional Firebase configuration (if not provided, will auto-load from Constants/env)
116
+ *
117
+ * IMPORTANT: Auth initialization is handled via callback to avoid require() issues.
118
+ * The main app should pass the authInitializer callback from react-native-firebase-auth.
119
+ *
120
+ * @param config - Optional Firebase configuration
121
+ * @param options - Optional service initialization options including authInitializer
361
122
  * @returns Object with initialization results for each service
362
- *
123
+ *
363
124
  * @example
364
125
  * ```typescript
365
126
  * import { initializeAllFirebaseServices } from '@umituz/react-native-firebase';
366
- *
367
- * // Auto-initialize from Constants/env
368
- * const result = await initializeAllFirebaseServices();
369
- *
370
- * // Or provide config explicitly
371
- * const result = await initializeAllFirebaseServices({
372
- * apiKey: 'your-api-key',
373
- * projectId: 'your-project-id',
374
- * // ...
127
+ * import { initializeFirebaseAuth } from '@umituz/react-native-firebase-auth';
128
+ *
129
+ * const result = await initializeAllFirebaseServices(undefined, {
130
+ * authInitializer: () => initializeFirebaseAuth(),
375
131
  * });
376
132
  * ```
377
133
  */
378
134
  export async function initializeAllFirebaseServices(
379
- config?: FirebaseConfig
135
+ config?: FirebaseConfig,
136
+ options?: ServiceInitializationOptions
380
137
  ): Promise<ServiceInitializationResult> {
381
138
  const app = config ? initializeFirebase(config) : autoInitializeFirebase();
382
139
 
@@ -389,15 +146,24 @@ export async function initializeAllFirebaseServices(
389
146
  };
390
147
  }
391
148
 
392
- const { auth, analytics, crashlytics } = await ServiceInitializer.initializeServices();
149
+ const { auth, analytics, crashlytics } =
150
+ await FirebaseServiceInitializer.initializeServices(options);
393
151
 
394
- // Initialize services if they have an init method
395
- if (analytics && typeof analytics.init === 'function') {
396
- await analytics.init();
152
+ if (analytics && typeof (analytics as { init?: () => Promise<void> }).init === 'function') {
153
+ await (analytics as { init: () => Promise<void> }).init();
397
154
  }
398
155
 
399
- if (crashlytics && typeof crashlytics.init === 'function') {
400
- await crashlytics.init();
156
+ if (crashlytics && typeof (crashlytics as { init?: () => Promise<void> }).init === 'function') {
157
+ await (crashlytics as { init: () => Promise<void> }).init();
158
+ }
159
+
160
+ if (__DEV__) {
161
+ console.log('[Firebase] All services initialized:', {
162
+ app: !!app,
163
+ auth: !!auth,
164
+ analytics: !!analytics,
165
+ crashlytics: !!crashlytics,
166
+ });
401
167
  }
402
168
 
403
169
  return {
@@ -424,8 +190,7 @@ export function getFirebaseInitializationError(): string | null {
424
190
 
425
191
  /**
426
192
  * Reset Firebase client instance
427
- * Useful for testing
428
193
  */
429
194
  export function resetFirebaseClient(): void {
430
195
  firebaseClient.reset();
431
- }
196
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Firebase Initialization Orchestrator
3
+ * Handles the initialization logic for Firebase App
4
+ *
5
+ * Single Responsibility: Only orchestrates Firebase App initialization
6
+ */
7
+
8
+ import type { FirebaseConfig } from '../../../domain/value-objects/FirebaseConfig';
9
+ import type { FirebaseApp } from '../initializers/FirebaseAppInitializer';
10
+ import { FirebaseConfigValidator } from '../validators/FirebaseConfigValidator';
11
+ import { FirebaseAppInitializer } from '../initializers/FirebaseAppInitializer';
12
+ import { loadFirebaseConfig } from '../FirebaseConfigLoader';
13
+ import type { FirebaseClientState } from '../state/FirebaseClientState';
14
+
15
+ declare const __DEV__: boolean;
16
+
17
+ export class FirebaseInitializationOrchestrator {
18
+ static initialize(
19
+ config: FirebaseConfig,
20
+ state: FirebaseClientState
21
+ ): FirebaseApp | null {
22
+ if (state.isInitialized()) {
23
+ if (__DEV__) {
24
+ console.log('[Firebase] Already initialized, returning existing instance');
25
+ }
26
+ return state.getApp();
27
+ }
28
+
29
+ if (state.getInitializationError()) {
30
+ if (__DEV__) {
31
+ console.log('[Firebase] Previous initialization failed, skipping retry');
32
+ }
33
+ return null;
34
+ }
35
+
36
+ try {
37
+ if (__DEV__) {
38
+ console.log('[Firebase] Initializing with projectId:', config.projectId);
39
+ }
40
+
41
+ FirebaseConfigValidator.validate(config);
42
+ const app = FirebaseAppInitializer.initialize(config);
43
+ state.setApp(app);
44
+
45
+ if (__DEV__) {
46
+ console.log('[Firebase] Successfully initialized');
47
+ }
48
+
49
+ return app;
50
+ } catch (error) {
51
+ const errorMessage =
52
+ error instanceof Error
53
+ ? error.message
54
+ : 'Failed to initialize Firebase client';
55
+ state.setInitializationError(errorMessage);
56
+
57
+ if (__DEV__) {
58
+ console.error('[Firebase] Initialization failed:', errorMessage);
59
+ }
60
+
61
+ return null;
62
+ }
63
+ }
64
+
65
+ static autoInitialize(state: FirebaseClientState): FirebaseApp | null {
66
+ if (state.isInitialized() || state.getInitializationError()) {
67
+ return state.getApp();
68
+ }
69
+
70
+ const autoConfig = loadFirebaseConfig();
71
+ if (autoConfig) {
72
+ if (__DEV__) {
73
+ console.log('[Firebase] Auto-initializing with environment config');
74
+ }
75
+ return this.initialize(autoConfig, state);
76
+ }
77
+
78
+ if (__DEV__) {
79
+ console.log('[Firebase] No configuration found for auto-initialization');
80
+ }
81
+
82
+ return null;
83
+ }
84
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Firebase Service Initializer
3
+ * Handles initialization of Firebase services (Analytics, Crashlytics)
4
+ *
5
+ * NOTE: Auth initialization is handled by the main app via callback.
6
+ * This removes the need for dynamic require() which causes issues in production.
7
+ *
8
+ * Single Responsibility: Only initializes Firebase services
9
+ */
10
+
11
+ import { firebaseAnalyticsService } from '../../../analytics';
12
+ import { firebaseCrashlyticsService } from '../../../crashlytics';
13
+
14
+ declare const __DEV__: boolean;
15
+
16
+ export type AuthInitializer = () => unknown;
17
+
18
+ export interface ServiceInitializationOptions {
19
+ authInitializer?: AuthInitializer;
20
+ }
21
+
22
+ export interface ServiceInitializationResult {
23
+ auth: unknown;
24
+ analytics: typeof firebaseAnalyticsService;
25
+ crashlytics: typeof firebaseCrashlyticsService;
26
+ }
27
+
28
+ export class FirebaseServiceInitializer {
29
+ static async initializeServices(
30
+ options?: ServiceInitializationOptions
31
+ ): Promise<ServiceInitializationResult> {
32
+ if (__DEV__) {
33
+ console.log('[Firebase] Initializing services...');
34
+ }
35
+
36
+ let auth: unknown = null;
37
+ if (options?.authInitializer) {
38
+ try {
39
+ auth = options.authInitializer();
40
+ if (__DEV__) {
41
+ console.log('[Firebase] Auth initialized via callback');
42
+ }
43
+ } catch (error) {
44
+ if (__DEV__) {
45
+ console.warn(
46
+ '[Firebase] Auth initialization failed:',
47
+ error instanceof Error ? error.message : 'Unknown error'
48
+ );
49
+ }
50
+ }
51
+ }
52
+
53
+ const analytics = firebaseAnalyticsService;
54
+ const crashlytics = firebaseCrashlyticsService;
55
+
56
+ if (__DEV__) {
57
+ console.log(
58
+ '[Firebase] Services initialized - Auth:',
59
+ !!auth,
60
+ 'Analytics:',
61
+ !!analytics,
62
+ 'Crashlytics:',
63
+ !!crashlytics
64
+ );
65
+ }
66
+
67
+ return { auth, analytics, crashlytics };
68
+ }
69
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Firebase Client State Manager
3
+ * Manages the state of Firebase initialization
4
+ *
5
+ * Single Responsibility: Only manages initialization state
6
+ */
7
+
8
+ import type { FirebaseApp } from '../initializers/FirebaseAppInitializer';
9
+
10
+ export class FirebaseClientState {
11
+ private app: FirebaseApp | null = null;
12
+ private initializationError: string | null = null;
13
+
14
+ getApp(): FirebaseApp | null {
15
+ return this.app;
16
+ }
17
+
18
+ setApp(app: FirebaseApp | null): void {
19
+ this.app = app;
20
+ }
21
+
22
+ isInitialized(): boolean {
23
+ return this.app !== null;
24
+ }
25
+
26
+ getInitializationError(): string | null {
27
+ return this.initializationError;
28
+ }
29
+
30
+ setInitializationError(error: string | null): void {
31
+ this.initializationError = error;
32
+ }
33
+
34
+ reset(): void {
35
+ this.app = null;
36
+ this.initializationError = null;
37
+ }
38
+ }