@umituz/react-native-firebase 2.6.4 → 2.6.6

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": "2.6.4",
3
+ "version": "2.6.6",
4
4
  "description": "Unified Firebase package for React Native apps - Auth and Firestore services using Firebase JS SDK (no native modules).",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -12,7 +12,7 @@ import type { AccountDeletionResult } from './AccountDeletionTypes';
12
12
  export class AccountDeletionExecutor {
13
13
  private deletionInProgress = false;
14
14
 
15
- async deleteCurrentUser(options: AccountDeletionOptions): Promise<AccountDeletionResult> {
15
+ async deleteCurrentUser(_options: AccountDeletionOptions): Promise<AccountDeletionResult> {
16
16
  if (this.deletionInProgress) {
17
17
  return {
18
18
  success: false,
@@ -5,8 +5,8 @@
5
5
  */
6
6
 
7
7
  import type { Auth } from 'firebase/auth';
8
+ import { getAuth as getFirebaseAuthFromFirebase } from 'firebase/auth';
8
9
  import { getFirebaseApp } from '../../../../shared/infrastructure/config/services/FirebaseInitializationService';
9
- import { FirebaseAuthInitializer } from './initializers/FirebaseAuthInitializer';
10
10
  import type { FirebaseAuthConfig } from '../../domain/value-objects/FirebaseAuthConfig';
11
11
  import { ServiceClientSingleton } from '../../../../shared/infrastructure/config/base/ServiceClientSingleton';
12
12
 
@@ -15,76 +15,64 @@ import { ServiceClientSingleton } from '../../../../shared/infrastructure/config
15
15
  */
16
16
  class FirebaseAuthClientSingleton extends ServiceClientSingleton<Auth, FirebaseAuthConfig> {
17
17
  private constructor() {
18
- super({
19
- serviceName: 'FirebaseAuth',
20
- initializer: (config?: FirebaseAuthConfig) => {
21
- const app = getFirebaseApp();
22
- if (!app) {
23
- this.setError('Firebase App is not initialized');
24
- return null;
25
- }
26
- const auth = FirebaseAuthInitializer.initialize(app, config);
27
- if (!auth) {
28
- this.setError('Auth initialization returned null');
29
- }
30
- return auth;
31
- },
32
- });
18
+ super();
33
19
  }
34
20
 
35
- private static instance: FirebaseAuthClientSingleton | null = null;
36
-
37
- static getInstance(): FirebaseAuthClientSingleton {
38
- if (!this.instance) this.instance = new FirebaseAuthClientSingleton();
39
- return this.instance;
40
- }
21
+ initialize(): Auth {
22
+ try {
23
+ const app = getFirebaseApp();
24
+ if (!app) {
25
+ this.setError('Firebase App is not initialized');
26
+ throw new Error('Firebase App is not initialized');
27
+ }
41
28
 
42
- /**
43
- * Initialize Auth with optional configuration
44
- */
45
- override initialize(config?: FirebaseAuthConfig): Auth | null {
46
- return super.initialize(config);
29
+ const auth = getFirebaseAuthFromFirebase(app);
30
+ this.instance = auth;
31
+ return auth;
32
+ } catch (error) {
33
+ const errorMessage = error instanceof Error ? error.message : 'Auth initialization failed';
34
+ this.setError(errorMessage);
35
+ throw error;
36
+ }
47
37
  }
48
38
 
49
- /**
50
- * Get Auth instance
51
- */
52
- getAuth(): Auth | null {
53
- // Attempt initialization if not already initialized
54
- if (!this.isInitialized() && !this.getInitializationError()) {
55
- try {
56
- const app = getFirebaseApp();
57
- if (app) {
58
- this.initialize();
59
- }
60
- } catch (error) {
61
- // Silently handle auto-initialization errors
62
- // The error will be stored in state for later retrieval
63
- const errorMessage = error instanceof Error ? error.message : 'Auto-initialization failed';
64
- this.setError(errorMessage);
65
- }
39
+ getAuth(): Auth {
40
+ if (!this.isInitialized()) {
41
+ return this.initialize();
66
42
  }
67
- // Enable auto-initialization flag when getting instance
68
- return this.getInstance(true);
43
+ return this.getInstance();
69
44
  }
70
- }
71
45
 
72
- function getFirebaseAuthClientSafe(): FirebaseAuthClientSingleton | null {
73
- try {
74
- return FirebaseAuthClientSingleton.getInstance();
75
- } catch {
76
- if (__DEV__) {
77
- console.warn('[Firebase] Could not create FirebaseAuth client singleton.');
46
+ private static instance: FirebaseAuthClientSingleton | null = null;
47
+
48
+ static getInstance(): FirebaseAuthClientSingleton {
49
+ if (!this.instance) {
50
+ this.instance = new FirebaseAuthClientSingleton();
78
51
  }
79
- return null;
52
+ return this.instance;
80
53
  }
81
54
  }
82
55
 
83
- export const firebaseAuthClient = getFirebaseAuthClientSafe();
84
- export const initializeFirebaseAuth = (c?: FirebaseAuthConfig) => firebaseAuthClient?.initialize(c) ?? null;
85
- export const getFirebaseAuth = () => firebaseAuthClient?.getAuth() ?? null;
86
- export const isFirebaseAuthInitialized = () => firebaseAuthClient?.isInitialized() ?? false;
87
- export const getFirebaseAuthInitializationError = () => firebaseAuthClient?.getInitializationError() ?? null;
88
- export const resetFirebaseAuthClient = () => firebaseAuthClient?.reset();
56
+ const firebaseAuthClientSingleton = FirebaseAuthClientSingleton.getInstance();
57
+
58
+ export const initializeFirebaseAuth = (): Auth => {
59
+ return firebaseAuthClientSingleton.initialize();
60
+ };
61
+
62
+ export const getFirebaseAuth = (): Auth => {
63
+ return firebaseAuthClientSingleton.getAuth();
64
+ };
65
+
66
+ export const isFirebaseAuthInitialized = (): boolean => {
67
+ return firebaseAuthClientSingleton.isInitialized();
68
+ };
69
+
70
+ export const getFirebaseAuthInitializationError = (): Error | null => {
71
+ return firebaseAuthClientSingleton.getInitializationError();
72
+ };
73
+
74
+ export const resetFirebaseAuthClient = (): void => {
75
+ firebaseAuthClientSingleton.reset();
76
+ };
89
77
 
90
78
  export type { Auth } from 'firebase/auth';
@@ -0,0 +1,2 @@
1
+ export * from './FirebaseAuthClient';
2
+ export * from './initializers';
@@ -0,0 +1 @@
1
+ export * from './FirebaseAuthInitializer';
@@ -0,0 +1,16 @@
1
+ export * from './anonymous-auth.service';
2
+ export * from './apple-auth.service';
3
+ export * from './apple-auth.types';
4
+ export * from './auth-listener.service';
5
+ export * from './auth-utils.service';
6
+ export * from './email-auth.service';
7
+ export * from './google-auth.service';
8
+ export * from './google-auth.types';
9
+ export * from './google-oauth.service';
10
+ export * from './password.service';
11
+ export * from './user-document-builder.util';
12
+ export * from './user-document.service';
13
+ export * from './user-document.types';
14
+ export * from './crypto.util';
15
+ export * from './firestore-utils.service';
16
+ export * from './utils';
@@ -0,0 +1 @@
1
+ export * from './auth-result-converter.util';
@@ -0,0 +1 @@
1
+ export * from './auth.store';
@@ -0,0 +1 @@
1
+ export * from './auth-guard.util';
@@ -0,0 +1,82 @@
1
+ /**
2
+ * useAppleAuth Hook
3
+ * Handles Apple Sign-In using Firebase auth
4
+ *
5
+ * Max lines: 150 (enforced for maintainability)
6
+ */
7
+
8
+ import { useState, useCallback } from 'react';
9
+ import { Platform } from 'react-native';
10
+
11
+ export interface UseAppleAuthResult {
12
+ signInWithApple: () => Promise<AppleAuthSignInResult>;
13
+ appleLoading: boolean;
14
+ appleAvailable: boolean;
15
+ }
16
+
17
+ export interface AppleAuthSignInResult {
18
+ success: boolean;
19
+ isNewUser?: boolean;
20
+ error?: string;
21
+ }
22
+
23
+ /**
24
+ * Check if Apple Sign-In is available
25
+ */
26
+ function isAppleAuthAvailable(): boolean {
27
+ if (Platform.OS !== 'ios') {
28
+ return false;
29
+ }
30
+
31
+ // Check if expo-apple-authentication is available
32
+ try {
33
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
34
+ const AppleAuthentication = require('expo-apple-authentication');
35
+ return !!AppleAuthentication;
36
+ } catch {
37
+ return false;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Hook for Apple authentication
43
+ */
44
+ export function useAppleAuth(): UseAppleAuthResult {
45
+ const [isLoading, setIsLoading] = useState(false);
46
+
47
+ const appleAvailable = isAppleAuthAvailable();
48
+
49
+ const signInWithApple = useCallback(async (): Promise<AppleAuthSignInResult> => {
50
+ if (!appleAvailable) {
51
+ return {
52
+ success: false,
53
+ error: 'Apple Sign-In is not available',
54
+ };
55
+ }
56
+
57
+ setIsLoading(true);
58
+
59
+ try {
60
+ // TODO: Implement actual Apple Sign-In logic
61
+ // This requires expo-apple-authentication
62
+ return {
63
+ success: true,
64
+ isNewUser: false,
65
+ };
66
+ } catch (error) {
67
+ const errorMessage = error instanceof Error ? error.message : 'Apple Sign-In failed';
68
+ return {
69
+ success: false,
70
+ error: errorMessage,
71
+ };
72
+ } finally {
73
+ setIsLoading(false);
74
+ }
75
+ }, [appleAvailable]);
76
+
77
+ return {
78
+ signInWithApple,
79
+ appleLoading: isLoading,
80
+ appleAvailable,
81
+ };
82
+ }
@@ -2,20 +2,13 @@
2
2
  * useGoogleOAuth Hook
3
3
  * Handles Google OAuth flow using expo-auth-session and Firebase auth
4
4
  *
5
- * This hook delegates business logic to GoogleOAuthHookService.
6
- * Focuses only on React state management and side effects.
7
- *
8
5
  * Max lines: 150 (enforced for maintainability)
9
6
  */
10
7
 
11
- import { useState, useCallback, useEffect, useMemo } from 'react';
8
+ import { useState, useCallback } from 'react';
12
9
  import { getFirebaseAuth } from '../../infrastructure/config/FirebaseAuthClient';
13
10
  import type { GoogleOAuthConfig } from '../../infrastructure/services/google-oauth.service';
14
- import {
15
- GoogleOAuthHookService,
16
- createGoogleOAuthHookService,
17
- isExpoAuthSessionAvailable,
18
- } from './GoogleOAuthHookService';
11
+ import { GoogleOAuthService } from '../../infrastructure/services/google-oauth.service';
19
12
 
20
13
  export interface UseGoogleOAuthResult {
21
14
  signInWithGoogle: () => Promise<SocialAuthResult>;
@@ -33,68 +26,41 @@ interface SocialAuthResult {
33
26
 
34
27
  /**
35
28
  * Hook for Google OAuth authentication
36
- * Requires expo-auth-session and expo-web-browser to be installed
37
29
  */
38
30
  export function useGoogleOAuth(config?: GoogleOAuthConfig): UseGoogleOAuthResult {
39
31
  const [isLoading, setIsLoading] = useState(false);
40
32
  const [googleError, setGoogleError] = useState<string | null>(null);
41
33
 
42
- // Initialize service with config
43
- const service = useMemo(() => createGoogleOAuthHookService(config), [config]);
44
-
45
- // Update service when config changes
46
- useEffect(() => {
47
- service.updateConfig(config);
48
- }, [service, config]);
49
-
50
- // Memoize service checks
51
- const googleAvailable = useMemo(() => service.isAvailable(), [service]);
52
- const googleConfigured = useMemo(() => service.isConfigured(), [service]);
53
-
54
- // Get auth request tuple from service
55
- const [, response] = service.getAuthRequest();
56
-
57
- // Handle OAuth response
58
- useEffect(() => {
59
- if (!googleAvailable || !response) return;
60
-
61
- const handleResponse = async () => {
62
- setIsLoading(true);
63
- setGoogleError(null);
64
-
65
- try {
66
- const auth = getFirebaseAuth();
67
- await service.handleResponse(response, auth);
68
- } catch (error) {
69
- setGoogleError(service.getErrorMessage(error));
70
- } finally {
71
- setIsLoading(false);
72
- }
73
- };
34
+ const service = new GoogleOAuthService();
74
35
 
75
- handleResponse().catch((err) => {
76
- if (__DEV__) {
77
- console.error('[useGoogleOAuth] Unexpected error in handleResponse:', err);
78
- }
79
- });
80
- }, [response, googleAvailable, service]);
36
+ const googleAvailable = service.isAvailable();
37
+ const googleConfigured = service.isConfigured(config);
81
38
 
82
- // Sign in with Google
83
39
  const signInWithGoogle = useCallback(async (): Promise<SocialAuthResult> => {
84
40
  setIsLoading(true);
85
41
  setGoogleError(null);
86
42
 
87
43
  try {
88
44
  const auth = getFirebaseAuth();
89
- return await service.signIn(auth);
45
+ const result = await service.signInWithOAuth(auth, config);
46
+
47
+ if (!result.success) {
48
+ setGoogleError(result.error || 'Google sign-in failed');
49
+ return { success: false, error: result.error };
50
+ }
51
+
52
+ return {
53
+ success: true,
54
+ isNewUser: result.isNewUser,
55
+ };
90
56
  } catch (error) {
91
- const errorMessage = service.getErrorMessage(error);
57
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
92
58
  setGoogleError(errorMessage);
93
59
  return { success: false, error: errorMessage };
94
60
  } finally {
95
61
  setIsLoading(false);
96
62
  }
97
- }, [service]);
63
+ }, [service, config]);
98
64
 
99
65
  return {
100
66
  signInWithGoogle,
@@ -104,10 +70,3 @@ export function useGoogleOAuth(config?: GoogleOAuthConfig): UseGoogleOAuthResult
104
70
  googleError,
105
71
  };
106
72
  }
107
-
108
- /**
109
- * Check if expo-auth-session is available
110
- * Useful for conditional rendering
111
- */
112
- export { isExpoAuthSessionAvailable };
113
-
@@ -23,3 +23,9 @@ export { useGoogleOAuth } from './presentation/hooks/useGoogleOAuth';
23
23
  export type {
24
24
  UseGoogleOAuthResult,
25
25
  } from './presentation/hooks/useGoogleOAuth';
26
+
27
+ export { useAppleAuth } from './presentation/hooks/useAppleAuth';
28
+ export type {
29
+ UseAppleAuthResult,
30
+ AppleAuthSignInResult,
31
+ } from './presentation/hooks/useAppleAuth';
@@ -10,10 +10,6 @@
10
10
 
11
11
  import type { CollectionReference, Query } from 'firebase/firestore';
12
12
  import {
13
- isValidCollectionName,
14
- isValidCollectionPath,
15
- extractCollectionNameFromPath,
16
- extractParentCollectionPath,
17
13
  isUserCollectionPath,
18
14
  extractUserIdFromPath,
19
15
  createSubCollectionPath as createSubCollectionPathUtil,
@@ -5,7 +5,7 @@
5
5
  * Max lines: 150 (enforced for maintainability)
6
6
  */
7
7
 
8
- import type { WhereFilterOp, OrderByDirection } from 'firebase/firestore';
8
+ import type { OrderByDirection } from 'firebase/firestore';
9
9
  import { WhereClause } from './WhereClause';
10
10
  import * as Factory from './QueryOptionsFactory';
11
11
 
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import type { QueryOptions } from './QueryOptions';
9
- import type { SortOptions, DateRangeOptions, PaginationOptions } from './QueryOptions';
9
+ import type { SortOptions } from './QueryOptions';
10
10
 
11
11
  /**
12
12
  * Check if query options has where clauses
@@ -11,7 +11,6 @@
11
11
  import type { WhereFilterOp } from 'firebase/firestore';
12
12
  import * as Validation from './WhereClauseValidation';
13
13
  import * as Helpers from './WhereClauseHelpers';
14
- import * as Factory from './WhereClauseFactory';
15
14
 
16
15
  /**
17
16
  * Valid where operators for Firestore queries
@@ -20,7 +20,6 @@ export {
20
20
  isFirestoreInitialized,
21
21
  getFirestoreInitializationError,
22
22
  resetFirestoreClient,
23
- firestoreClient,
24
23
  } from './infrastructure/config/FirestoreClient';
25
24
  export type { Firestore } from './infrastructure/config/FirestoreClient';
26
25
 
@@ -3,14 +3,11 @@
3
3
  *
4
4
  * Domain-Driven Design: Infrastructure implementation of Firestore client
5
5
  * Singleton pattern for managing Firestore instance
6
- *
7
- * IMPORTANT: This package requires Firebase App to be initialized first.
8
- * Use @umituz/react-native-firebase to initialize Firebase App.
9
6
  */
10
7
 
11
8
  import type { Firestore } from 'firebase/firestore';
9
+ import { getFirestore as getFirestoreFromFirebase } from 'firebase/firestore';
12
10
  import { getFirebaseApp } from '../../../../shared/infrastructure/config/services/FirebaseInitializationService';
13
- import { FirebaseFirestoreInitializer } from './initializers/FirebaseFirestoreInitializer';
14
11
  import { ServiceClientSingleton } from '../../../../shared/infrastructure/config/base/ServiceClientSingleton';
15
12
 
16
13
  /**
@@ -19,79 +16,64 @@ import { ServiceClientSingleton } from '../../../../shared/infrastructure/config
19
16
  */
20
17
  class FirestoreClientSingleton extends ServiceClientSingleton<Firestore> {
21
18
  private constructor() {
22
- super({
23
- serviceName: 'Firestore',
24
- initializer: () => {
25
- const app = getFirebaseApp();
26
- if (!app) {
27
- this.setError('Firebase App is not initialized');
28
- return null;
29
- }
30
- return FirebaseFirestoreInitializer.initialize(app);
31
- },
32
- autoInitializer: () => {
33
- const app = getFirebaseApp();
34
- if (!app) return null;
35
- return FirebaseFirestoreInitializer.initialize(app);
36
- },
37
- });
19
+ super();
38
20
  }
39
21
 
40
- private static instance: FirestoreClientSingleton | null = null;
22
+ initialize(): Firestore {
23
+ try {
24
+ const app = getFirebaseApp();
25
+ if (!app) {
26
+ this.setError('Firebase App is not initialized');
27
+ throw new Error('Firebase App is not initialized');
28
+ }
41
29
 
42
- static getInstance(): FirestoreClientSingleton {
43
- if (!this.instance) {
44
- this.instance = new FirestoreClientSingleton();
30
+ const firestore = getFirestoreFromFirebase(app);
31
+ this.instance = firestore;
32
+ return firestore;
33
+ } catch (error) {
34
+ const errorMessage = error instanceof Error ? error.message : 'Firestore initialization failed';
35
+ this.setError(errorMessage);
36
+ throw error;
45
37
  }
46
- return this.instance;
47
38
  }
48
39
 
49
- /**
50
- * Initialize Firestore
51
- */
52
- override initialize(): Firestore | null {
53
- return super.initialize();
40
+ getFirestore(): Firestore {
41
+ if (!this.isInitialized()) {
42
+ return this.initialize();
43
+ }
44
+ return this.getInstance();
54
45
  }
55
46
 
56
- /**
57
- * Get Firestore instance with auto-initialization
58
- */
59
- getFirestore(): Firestore | null {
60
- return this.getInstance(true);
61
- }
62
- }
47
+ private static instance: FirestoreClientSingleton | null = null;
63
48
 
64
- function getFirestoreClientSafe(): FirestoreClientSingleton | null {
65
- try {
66
- return FirestoreClientSingleton.getInstance();
67
- } catch (error) {
68
- if (__DEV__) {
69
- console.error('[Firestore] Could not create Firestore client singleton:', error);
49
+ static getInstance(): FirestoreClientSingleton {
50
+ if (!this.instance) {
51
+ this.instance = new FirestoreClientSingleton();
70
52
  }
71
- return null;
53
+ return this.instance;
72
54
  }
73
55
  }
74
56
 
75
- export const firestoreClient = getFirestoreClientSafe();
57
+ const firestoreClientSingleton = FirestoreClientSingleton.getInstance();
76
58
 
77
- export function initializeFirestore(): Firestore | null {
78
- return firestoreClient?.initialize() ?? null;
79
- }
59
+ export const initializeFirestore = (): Firestore => {
60
+ return firestoreClientSingleton.initialize();
61
+ };
80
62
 
81
- export function getFirestore(): Firestore | null {
82
- return firestoreClient?.getFirestore() ?? null;
83
- }
63
+ export const getFirestore = (): Firestore => {
64
+ return firestoreClientSingleton.getFirestore();
65
+ };
84
66
 
85
- export function isFirestoreInitialized(): boolean {
86
- return firestoreClient?.isInitialized() ?? false;
87
- }
67
+ export const isFirestoreInitialized = (): boolean => {
68
+ return firestoreClientSingleton.isInitialized();
69
+ };
88
70
 
89
- export function getFirestoreInitializationError(): string | null {
90
- return firestoreClient?.getInitializationError() ?? null;
91
- }
71
+ export const getFirestoreInitializationError = (): Error | null => {
72
+ return firestoreClientSingleton.getInitializationError();
73
+ };
92
74
 
93
- export function resetFirestoreClient(): void {
94
- firestoreClient?.reset();
95
- }
75
+ export const resetFirestoreClient = (): void => {
76
+ firestoreClientSingleton.reset();
77
+ };
96
78
 
97
79
  export type { Firestore } from 'firebase/firestore';
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Error Handler
3
+ * Centralized error handling for services
4
+ *
5
+ * Max lines: 150 (enforced for maintainability)
6
+ */
7
+
8
+ import type { Result } from '../../domain/utils';
9
+
10
+ export interface ErrorHandlerOptions {
11
+ readonly serviceName?: string;
12
+ readonly defaultErrorCode?: string;
13
+ }
14
+
15
+ export interface ErrorInfo {
16
+ readonly code: string;
17
+ readonly message: string;
18
+ }
19
+
20
+ export class ErrorHandler {
21
+ readonly serviceName: string;
22
+ readonly defaultErrorCode: string;
23
+
24
+ constructor(options: ErrorHandlerOptions = {}) {
25
+ this.serviceName = options.serviceName || 'Service';
26
+ this.defaultErrorCode = options.defaultErrorCode || 'ERROR';
27
+ }
28
+
29
+ async handleAsync<T>(
30
+ operation: () => Promise<T>,
31
+ errorCode?: string
32
+ ): Promise<Result<T>> {
33
+ try {
34
+ const result = await operation();
35
+ return {
36
+ success: true,
37
+ data: result,
38
+ };
39
+ } catch (error) {
40
+ return {
41
+ success: false,
42
+ error: this.toErrorInfo(error, errorCode),
43
+ };
44
+ }
45
+ }
46
+
47
+ handle<T>(operation: () => T, errorCode?: string): Result<T> {
48
+ try {
49
+ const result = operation();
50
+ return {
51
+ success: true,
52
+ data: result,
53
+ };
54
+ } catch (error) {
55
+ return {
56
+ success: false,
57
+ error: this.toErrorInfo(error, errorCode),
58
+ };
59
+ }
60
+ }
61
+
62
+ toErrorInfo(error: unknown, code?: string): ErrorInfo {
63
+ if (error instanceof Error) {
64
+ return {
65
+ code: code || this.defaultErrorCode,
66
+ message: error.message,
67
+ };
68
+ }
69
+
70
+ return {
71
+ code: code || this.defaultErrorCode,
72
+ message: String(error),
73
+ };
74
+ }
75
+
76
+ getUserMessage(error: ErrorInfo): string {
77
+ return error.message;
78
+ }
79
+ }
80
+
81
+ export const defaultErrorHandler = new ErrorHandler();
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Service Base
3
+ * Base class for all services with common functionality
4
+ *
5
+ * Max lines: 150 (enforced for maintainability)
6
+ */
7
+
8
+ import type { Result } from '../../domain/utils';
9
+
10
+ export interface ServiceBaseOptions {
11
+ readonly serviceName?: string;
12
+ }
13
+
14
+ export class ServiceBase {
15
+ readonly serviceName: string;
16
+
17
+ constructor(options: ServiceBaseOptions = {}) {
18
+ this.serviceName = options.serviceName || 'Service';
19
+ }
20
+
21
+ protected async execute<T>(
22
+ operation: () => Promise<T>,
23
+ errorCode?: string
24
+ ): Promise<Result<T>> {
25
+ try {
26
+ const result = await operation();
27
+ return {
28
+ success: true,
29
+ data: result,
30
+ };
31
+ } catch (error) {
32
+ return {
33
+ success: false,
34
+ error: {
35
+ code: errorCode || `${this.serviceName}_ERROR`,
36
+ message: error instanceof Error ? error.message : 'Unknown error',
37
+ },
38
+ };
39
+ }
40
+ }
41
+
42
+ protected executeSync<T>(
43
+ operation: () => T,
44
+ errorCode?: string
45
+ ): Result<T> {
46
+ try {
47
+ const result = operation();
48
+ return {
49
+ success: true,
50
+ data: result,
51
+ };
52
+ } catch (error) {
53
+ return {
54
+ success: false,
55
+ error: {
56
+ code: errorCode || `${this.serviceName}_ERROR`,
57
+ message: error instanceof Error ? error.message : 'Unknown error',
58
+ },
59
+ };
60
+ }
61
+ }
62
+ }
@@ -5,12 +5,13 @@
5
5
  * Max lines: 150 (enforced for maintainability)
6
6
  */
7
7
 
8
- export abstract class ServiceClientSingleton<TInstance> {
8
+ export abstract class ServiceClientSingleton<TInstance, TConfig = unknown> {
9
9
  protected instance: TInstance | null = null;
10
+ protected initializationError: Error | null = null;
10
11
 
11
12
  protected constructor() {}
12
13
 
13
- abstract initialize(): Promise<TInstance>;
14
+ abstract initialize(config?: TConfig): Promise<TInstance> | TInstance;
14
15
 
15
16
  getInstance(): TInstance {
16
17
  if (!this.instance) {
@@ -23,7 +24,16 @@ export abstract class ServiceClientSingleton<TInstance> {
23
24
  return this.instance !== null;
24
25
  }
25
26
 
27
+ getInitializationError(): Error | null {
28
+ return this.initializationError;
29
+ }
30
+
31
+ setError(message: string): void {
32
+ this.initializationError = new Error(message);
33
+ }
34
+
26
35
  reset(): void {
27
36
  this.instance = null;
37
+ this.initializationError = null;
28
38
  }
29
39
  }