@umituz/web-firebase 2.1.1 → 3.0.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.
Files changed (58) hide show
  1. package/package.json +12 -39
  2. package/src/domain/interfaces/auth.repository.interface.ts +4 -19
  3. package/src/domain/interfaces/file.repository.interface.ts +5 -102
  4. package/src/domain/interfaces/index.ts +7 -0
  5. package/src/domain/interfaces/user.repository.interface.ts +17 -44
  6. package/src/domains/auth/entities/index.ts +60 -0
  7. package/src/domains/auth/index.ts +13 -0
  8. package/src/domains/auth/services/auth.service.ts +245 -0
  9. package/src/domains/auth/services/index.ts +7 -0
  10. package/src/domains/auth/types/auth-service.interface.ts +72 -0
  11. package/src/domains/auth/types/index.ts +5 -0
  12. package/src/domains/firestore/entities/index.ts +82 -0
  13. package/src/domains/firestore/index.ts +13 -0
  14. package/src/domains/firestore/services/firestore.service.ts +191 -0
  15. package/src/domains/firestore/services/index.ts +7 -0
  16. package/src/domains/firestore/types/firestore-service.interface.ts +64 -0
  17. package/src/domains/firestore/types/index.ts +5 -0
  18. package/src/domains/storage/entities/index.ts +94 -0
  19. package/src/domains/storage/index.ts +13 -0
  20. package/src/domains/storage/services/index.ts +7 -0
  21. package/src/domains/storage/services/storage.service.ts +223 -0
  22. package/src/domains/storage/types/index.ts +5 -0
  23. package/src/domains/storage/types/storage-service.interface.ts +120 -0
  24. package/src/index.ts +12 -16
  25. package/src/presentation/hooks/useAuth.ts +69 -26
  26. package/src/presentation/providers/FirebaseProvider.tsx +9 -14
  27. package/dist/application/index.d.mts +0 -273
  28. package/dist/application/index.d.ts +0 -273
  29. package/dist/application/index.js +0 -490
  30. package/dist/application/index.mjs +0 -19
  31. package/dist/chunk-34DL2QWQ.mjs +0 -87
  32. package/dist/chunk-4FP2ELQ5.mjs +0 -96
  33. package/dist/chunk-7TX3OU3O.mjs +0 -721
  34. package/dist/chunk-I6WGBPFB.mjs +0 -439
  35. package/dist/chunk-RZ4QR6TB.mjs +0 -96
  36. package/dist/chunk-U2XI4MGO.mjs +0 -397
  37. package/dist/domain/index.d.mts +0 -325
  38. package/dist/domain/index.d.ts +0 -325
  39. package/dist/domain/index.js +0 -662
  40. package/dist/domain/index.mjs +0 -36
  41. package/dist/file.repository.interface-v5vHgVsZ.d.mts +0 -241
  42. package/dist/file.repository.interface-v5vHgVsZ.d.ts +0 -241
  43. package/dist/firebase.entity-xvfEPjXZ.d.mts +0 -15
  44. package/dist/firebase.entity-xvfEPjXZ.d.ts +0 -15
  45. package/dist/index.d.mts +0 -14
  46. package/dist/index.d.ts +0 -14
  47. package/dist/index.js +0 -1833
  48. package/dist/index.mjs +0 -98
  49. package/dist/infrastructure/index.d.mts +0 -170
  50. package/dist/infrastructure/index.d.ts +0 -170
  51. package/dist/infrastructure/index.js +0 -856
  52. package/dist/infrastructure/index.mjs +0 -46
  53. package/dist/presentation/index.d.mts +0 -25
  54. package/dist/presentation/index.d.ts +0 -25
  55. package/dist/presentation/index.js +0 -105
  56. package/dist/presentation/index.mjs +0 -6
  57. package/dist/user.repository.interface-DS74TsJ5.d.mts +0 -298
  58. package/dist/user.repository.interface-DS74TsJ5.d.ts +0 -298
package/package.json CHANGED
@@ -1,49 +1,25 @@
1
1
  {
2
2
  "name": "@umituz/web-firebase",
3
- "version": "2.1.1",
4
- "description": "Comprehensive Firebase integration with DDD architecture for web applications",
5
- "main": "./dist/index.js",
6
- "module": "./dist/index.mjs",
7
- "types": "./dist/index.d.ts",
3
+ "version": "3.0.1",
4
+ "description": "Comprehensive Firebase integration with domain-based architecture for web applications",
5
+ "main": "./src/index.ts",
6
+ "types": "./src/index.ts",
8
7
  "sideEffects": false,
9
8
  "publishConfig": {
10
9
  "access": "public"
11
10
  },
12
11
  "exports": {
13
- ".": {
14
- "types": "./dist/index.d.ts",
15
- "import": "./dist/index.mjs",
16
- "require": "./dist/index.js"
17
- },
18
- "./domain": {
19
- "types": "./dist/domain/index.d.ts",
20
- "import": "./dist/domain/index.mjs",
21
- "require": "./dist/domain/index.js"
22
- },
23
- "./application": {
24
- "types": "./dist/application/index.d.ts",
25
- "import": "./dist/application/index.mjs",
26
- "require": "./dist/application/index.js"
27
- },
28
- "./infrastructure": {
29
- "types": "./dist/infrastructure/index.d.ts",
30
- "import": "./dist/infrastructure/index.mjs",
31
- "require": "./dist/infrastructure/index.js"
32
- },
33
- "./presentation": {
34
- "types": "./dist/presentation/index.d.ts",
35
- "import": "./dist/presentation/index.mjs",
36
- "require": "./dist/presentation/index.js"
37
- }
12
+ ".": "./src/index.ts",
13
+ "./auth": "./src/domains/auth/index.ts",
14
+ "./firestore": "./src/domains/firestore/index.ts",
15
+ "./storage": "./src/domains/storage/index.ts",
16
+ "./package.json": "./package.json"
38
17
  },
39
18
  "files": [
40
- "dist",
41
19
  "src"
42
20
  ],
43
21
  "scripts": {
44
- "build": "tsup src/index.ts src/domain/index.ts src/application/index.ts src/infrastructure/index.ts src/presentation/index.ts --format cjs,esm --dts --clean --external react --external firebase --external firebase/app --external firebase/auth --external firebase/firestore --external firebase/storage --external firebase/functions",
45
- "dev": "tsup src/index.ts src/domain/index.ts src/application/index.ts src/infrastructure/index.ts src/presentation/index.ts --format cjs,esm --dts --watch --external react --external firebase",
46
- "lint": "tsc --noEmit"
22
+ "typecheck": "tsc --noEmit"
47
23
  },
48
24
  "keywords": [
49
25
  "firebase",
@@ -57,10 +33,8 @@
57
33
  "typescript",
58
34
  "ddd",
59
35
  "domain-driven-design",
60
- "clean-architecture",
61
- "repository-pattern",
62
- "use-cases",
63
- "hexagonal-architecture"
36
+ "package-driven",
37
+ "repository-pattern"
64
38
  ],
65
39
  "author": "umituz",
66
40
  "license": "MIT",
@@ -71,7 +45,6 @@
71
45
  "devDependencies": {
72
46
  "@types/react": "^18.2.0",
73
47
  "firebase": "^12.11.0",
74
- "tsup": "^8.0.0",
75
48
  "typescript": "^5.0.0"
76
49
  }
77
50
  }
@@ -1,15 +1,10 @@
1
1
  /**
2
2
  * Authentication Repository Interface
3
- * @description Defines contract for authentication operations
3
+ * @description Generic contract for authentication operations
4
4
  */
5
5
 
6
- import type { User as FirebaseUser, UserCredential } from 'firebase/auth'
7
- import type { User } from '../entities/user.entity'
6
+ import type { UserCredential, User as FirebaseUser } from 'firebase/auth'
8
7
 
9
- /**
10
- * Authentication Repository Interface
11
- * Defines operations for user authentication and management
12
- */
13
8
  export interface IAuthRepository {
14
9
  /**
15
10
  * Sign in with email and password
@@ -44,7 +39,7 @@ export interface IAuthRepository {
44
39
  /**
45
40
  * Update user profile (displayName, photoURL)
46
41
  */
47
- updateProfile(updates: Partial<Pick<User['profile'], 'displayName' | 'photoURL'>>): Promise<void>
42
+ updateProfile(updates: { displayName?: string; photoURL?: string }): Promise<void>
48
43
 
49
44
  /**
50
45
  * Update user email (requires password)
@@ -69,15 +64,5 @@ export interface IAuthRepository {
69
64
  /**
70
65
  * Subscribe to auth state changes
71
66
  */
72
- onAuthStateChanged(callback: (user: FirebaseUser | null) => void): () => void
73
-
74
- /**
75
- * Create user document in Firestore
76
- */
77
- createUserDocument(userId: string, data: Partial<Omit<User, 'profile'>> & { email: string; displayName: string }): Promise<void>
78
-
79
- /**
80
- * Update last login timestamp
81
- */
82
- updateLastLogin(userId: string): Promise<void>
67
+ onAuthStateChanged(callback: (user: FirebaseUser | null) => void, onError?: (error: Error) => void): () => void
83
68
  }
@@ -1,51 +1,23 @@
1
1
  /**
2
2
  * File Repository Interface
3
- * @description Defines contract for file storage operations
3
+ * @description Generic contract for file storage operations
4
4
  */
5
5
 
6
- import type {
7
- FileMetadata,
8
- UploadResult,
9
- UploadOptions,
10
- FileFilters,
11
- FileQueryResult,
12
- StorageStats,
13
- } from '../entities/file.entity'
14
-
15
- /**
16
- * File Repository Interface
17
- * Defines operations for file storage and management
18
- */
19
6
  export interface IFileRepository {
20
7
  /**
21
8
  * Upload file to storage
22
9
  */
23
- uploadFile(
24
- userId: string,
25
- path: string,
26
- file: File | Blob,
27
- options?: UploadOptions
28
- ): Promise<UploadResult>
10
+ uploadFile(userId: string, path: string, file: File | Blob, options?: any): Promise<any>
29
11
 
30
12
  /**
31
13
  * Upload image with automatic categorization
32
14
  */
33
- uploadImage(userId: string, file: File, filename?: string): Promise<UploadResult>
34
-
35
- /**
36
- * Upload video with automatic categorization
37
- */
38
- uploadVideo(userId: string, file: File, filename?: string): Promise<UploadResult>
39
-
40
- /**
41
- * Upload document with automatic categorization
42
- */
43
- uploadDocument(userId: string, file: File, filename?: string): Promise<UploadResult>
15
+ uploadImage(userId: string, file: File, filename?: string): Promise<any>
44
16
 
45
17
  /**
46
18
  * Upload profile picture
47
19
  */
48
- uploadProfilePicture(userId: string, file: File): Promise<UploadResult>
20
+ uploadProfilePicture(userId: string, file: File): Promise<any>
49
21
 
50
22
  /**
51
23
  * Get download URL for a file
@@ -62,82 +34,13 @@ export interface IFileRepository {
62
34
  */
63
35
  deleteUserFiles(userId: string): Promise<void>
64
36
 
65
- /**
66
- * Delete user image
67
- */
68
- deleteImage(userId: string, filename: string): Promise<void>
69
-
70
- /**
71
- * Delete user video
72
- */
73
- deleteVideo(userId: string, filename: string): Promise<void>
74
-
75
- /**
76
- * Delete profile picture
77
- */
78
- deleteProfilePicture(userId: string, filename: string): Promise<void>
79
-
80
37
  /**
81
38
  * List user files
82
39
  */
83
40
  listUserFiles(userId: string, path?: string): Promise<string[]>
84
41
 
85
- /**
86
- * List user images
87
- */
88
- listUserImages(userId: string): Promise<string[]>
89
-
90
- /**
91
- * List user videos
92
- */
93
- listUserVideos(userId: string): Promise<string[]>
94
-
95
- /**
96
- * Get file metadata
97
- */
98
- getFileMetadata(path: string): Promise<FileMetadata>
99
-
100
- /**
101
- * Query files with filters
102
- */
103
- queryFiles(userId: string, filters?: FileFilters): Promise<FileQueryResult>
104
-
105
- /**
106
- * Get storage statistics
107
- */
108
- getStorageStats(userId: string): Promise<StorageStats>
109
-
110
42
  /**
111
43
  * Validate file before upload
112
44
  */
113
- validateFile(file: File, options?: {
114
- maxSizeBytes?: number
115
- maxSizeMB?: number
116
- allowedTypes?: string[]
117
- }): boolean
118
-
119
- /**
120
- * Check if file is an image
121
- */
122
- isImageFile(file: File): boolean
123
-
124
- /**
125
- * Check if file is a video
126
- */
127
- isVideoFile(file: File): boolean
128
-
129
- /**
130
- * Check if file is a document
131
- */
132
- isDocumentFile(file: File): boolean
133
-
134
- /**
135
- * Generate unique filename
136
- */
137
- generateUniqueFilename(originalName: string): string
138
-
139
- /**
140
- * Get file extension
141
- */
142
- getFileExtension(filename: string): string
45
+ validateFile(file: File, options?: any): boolean
143
46
  }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Domain Interfaces
3
+ */
4
+
5
+ export * from './auth.repository.interface'
6
+ export * from './user.repository.interface'
7
+ export * from './file.repository.interface'
@@ -1,75 +1,48 @@
1
1
  /**
2
2
  * User Repository Interface
3
- * @description Defines contract for user data operations
3
+ * @description Generic contract for user data operations
4
4
  */
5
5
 
6
- import type { User } from '../entities/user.entity'
7
6
  import type { QueryConstraint } from 'firebase/firestore'
8
7
 
9
- /**
10
- * User Repository Interface
11
- * Defines operations for user data management
12
- */
13
8
  export interface IUserRepository {
14
9
  /**
15
- * Get user by ID
16
- */
17
- getUser(userId: string): Promise<User | null>
18
-
19
- /**
20
- * Get user by email
21
- */
22
- getUserByEmail(email: string): Promise<User | null>
23
-
24
- /**
25
- * Create user
26
- */
27
- createUser(userId: string, data: Partial<User>): Promise<void>
28
-
29
- /**
30
- * Update user
10
+ * Get document by ID
31
11
  */
32
- updateUser(userId: string, data: Partial<User>): Promise<void>
12
+ getById<T>(userId: string): Promise<T | null>
33
13
 
34
14
  /**
35
- * Delete user
15
+ * Get document by field
36
16
  */
37
- deleteUser(userId: string): Promise<void>
17
+ getByField<T>(collectionPath: string, field: string, value: any): Promise<T | null>
38
18
 
39
19
  /**
40
- * Update user profile
20
+ * Create document
41
21
  */
42
- updateProfile(userId: string, updates: Partial<Pick<User['profile'], 'displayName' | 'photoURL' | 'phoneNumber'>>): Promise<void>
22
+ create(userId: string, data: any): Promise<void>
43
23
 
44
24
  /**
45
- * Update user settings
25
+ * Update document
46
26
  */
47
- updateSettings(userId: string, settings: Partial<User['settings']>): Promise<void>
27
+ update(userId: string, data: any, options?: { merge?: boolean }): Promise<void>
48
28
 
49
29
  /**
50
- * Update user subscription
30
+ * Delete document
51
31
  */
52
- updateSubscription(
53
- userId: string,
54
- subscription: Partial<User['subscription']>
55
- ): Promise<void>
32
+ delete(userId: string): Promise<void>
56
33
 
57
34
  /**
58
- * Update last login timestamp
35
+ * Query collection with constraints
59
36
  */
60
- updateLastLogin(userId: string): Promise<void>
37
+ query<T>(collectionPath: string, constraints: QueryConstraint[]): Promise<T[]>
61
38
 
62
39
  /**
63
- * Query users with constraints
40
+ * Subscribe to document changes
64
41
  */
65
- queryUsers(constraints: QueryConstraint[]): Promise<User[]>
42
+ subscribeToDoc<T>(docPath: string, callback: (data: T | null) => void): () => void
66
43
 
67
44
  /**
68
- * Subscribe to user document changes
45
+ * Subscribe to collection changes
69
46
  */
70
- subscribeToUser(
71
- userId: string,
72
- callback: (user: User | null) => void,
73
- onError?: (error: Error) => void
74
- ): () => void
47
+ subscribeToCollection<T>(collectionPath: string, callback: (data: T[]) => void, constraints?: QueryConstraint[]): () => void
75
48
  }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Auth Domain Entities
3
+ * @description Core authentication-related entities
4
+ */
5
+
6
+ import type { User as FirebaseUser } from 'firebase/auth'
7
+
8
+ /**
9
+ * Auth User Entity
10
+ * Wrapper around Firebase User with application-specific data
11
+ */
12
+ export interface AuthUser {
13
+ readonly uid: string
14
+ readonly email: string | null
15
+ readonly emailVerified: boolean
16
+ readonly displayName: string | null
17
+ readonly photoURL: string | null
18
+ readonly phoneNumber: string | null
19
+ readonly isAnonymous: boolean
20
+ readonly tenantId: string | null
21
+ readonly providerId: string
22
+ readonly metadata: {
23
+ readonly creationTime?: number
24
+ readonly lastSignInTime?: number
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Auth State Entity
30
+ * Current authentication state
31
+ */
32
+ export interface AuthState {
33
+ readonly user: AuthUser | null
34
+ readonly isLoading: boolean
35
+ readonly isInitialized: boolean
36
+ readonly error: Error | null
37
+ }
38
+
39
+ /**
40
+ * Convert Firebase User to Auth User
41
+ */
42
+ export function toAuthUser(firebaseUser: FirebaseUser | null): AuthUser | null {
43
+ if (!firebaseUser) return null
44
+
45
+ return {
46
+ uid: firebaseUser.uid,
47
+ email: firebaseUser.email,
48
+ emailVerified: firebaseUser.emailVerified,
49
+ displayName: firebaseUser.displayName,
50
+ photoURL: firebaseUser.photoURL,
51
+ phoneNumber: firebaseUser.phoneNumber,
52
+ isAnonymous: firebaseUser.isAnonymous,
53
+ tenantId: firebaseUser.tenantId,
54
+ providerId: firebaseUser.providerId,
55
+ metadata: {
56
+ creationTime: firebaseUser.metadata.creationTime ? new Date(firebaseUser.metadata.creationTime).getTime() : undefined,
57
+ lastSignInTime: firebaseUser.metadata.lastSignInTime ? new Date(firebaseUser.metadata.lastSignInTime).getTime() : undefined,
58
+ },
59
+ }
60
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Auth Domain
3
+ * Complete Firebase Authentication integration
4
+ */
5
+
6
+ // Entities
7
+ export * from './entities'
8
+
9
+ // Types
10
+ export * from './types'
11
+
12
+ // Services
13
+ export * from './services'
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Auth Service
3
+ * @description Firebase Auth implementation of IAuthService
4
+ */
5
+
6
+ import {
7
+ signInWithEmailAndPassword,
8
+ signInWithPopup,
9
+ createUserWithEmailAndPassword,
10
+ signOut as firebaseSignOut,
11
+ sendPasswordResetEmail,
12
+ sendEmailVerification,
13
+ updateProfile as updateAuthProfile,
14
+ updateEmail as updateAuthEmail,
15
+ updatePassword as updateAuthPassword,
16
+ reauthenticateWithCredential,
17
+ EmailAuthProvider,
18
+ UserCredential,
19
+ } from 'firebase/auth'
20
+ import { GoogleAuthProvider } from 'firebase/auth'
21
+ import { getFirebaseAuth } from '../../../infrastructure/firebase/client'
22
+ import type { IAuthService } from '../types'
23
+ import type { AuthUser } from '../entities'
24
+
25
+ class AuthService implements IAuthService {
26
+ private get auth() {
27
+ return getFirebaseAuth()
28
+ }
29
+
30
+ // Authentication Methods
31
+
32
+ async signIn(email: string, password: string): Promise<UserCredential> {
33
+ try {
34
+ return await signInWithEmailAndPassword(this.auth, email, password)
35
+ } catch (error) {
36
+ throw this.handleAuthError(error)
37
+ }
38
+ }
39
+
40
+ async signUp(email: string, password: string, displayName: string): Promise<UserCredential> {
41
+ try {
42
+ const result = await createUserWithEmailAndPassword(this.auth, email, password)
43
+
44
+ // Update profile
45
+ await updateAuthProfile(result.user, { displayName })
46
+
47
+ // Send email verification
48
+ await sendEmailVerification(result.user)
49
+
50
+ return result
51
+ } catch (error) {
52
+ throw this.handleAuthError(error)
53
+ }
54
+ }
55
+
56
+ async signInWithGoogle(): Promise<UserCredential> {
57
+ try {
58
+ const provider = new GoogleAuthProvider()
59
+ provider.addScope('profile')
60
+ provider.addScope('email')
61
+
62
+ return await signInWithPopup(this.auth, provider)
63
+ } catch (error) {
64
+ throw this.handleAuthError(error)
65
+ }
66
+ }
67
+
68
+ async signOut(): Promise<void> {
69
+ try {
70
+ await firebaseSignOut(this.auth)
71
+ } catch (error) {
72
+ throw new Error('Sign out failed')
73
+ }
74
+ }
75
+
76
+ async sendPasswordReset(email: string): Promise<void> {
77
+ try {
78
+ await sendPasswordResetEmail(this.auth, email)
79
+ } catch (error) {
80
+ throw this.handleAuthError(error)
81
+ }
82
+ }
83
+
84
+ async resendEmailVerification(): Promise<void> {
85
+ try {
86
+ const user = this.auth.currentUser
87
+ if (!user) {
88
+ throw new Error('No user logged in')
89
+ }
90
+ await sendEmailVerification(user)
91
+ } catch (error) {
92
+ throw new Error('Failed to resend verification')
93
+ }
94
+ }
95
+
96
+ // Profile Management
97
+
98
+ async updateProfile(updates: { displayName?: string; photoURL?: string }): Promise<void> {
99
+ try {
100
+ const user = this.auth.currentUser
101
+ if (!user) {
102
+ throw new Error('No user logged in')
103
+ }
104
+
105
+ await updateAuthProfile(user, updates)
106
+ } catch (error) {
107
+ throw new Error('Profile update failed')
108
+ }
109
+ }
110
+
111
+ async updateEmail(newEmail: string, password: string): Promise<void> {
112
+ try {
113
+ const user = this.auth.currentUser
114
+ if (!user || !user.email) {
115
+ throw new Error('No user logged in')
116
+ }
117
+
118
+ const credential = EmailAuthProvider.credential(user.email, password)
119
+ await reauthenticateWithCredential(user, credential)
120
+ await updateAuthEmail(user, newEmail)
121
+ } catch (error) {
122
+ throw new Error('Email update failed')
123
+ }
124
+ }
125
+
126
+ async updatePassword(currentPassword: string, newPassword: string): Promise<void> {
127
+ try {
128
+ const user = this.auth.currentUser
129
+ if (!user || !user.email) {
130
+ throw new Error('No user logged in')
131
+ }
132
+
133
+ const credential = EmailAuthProvider.credential(user.email, currentPassword)
134
+ await reauthenticateWithCredential(user, credential)
135
+ await updateAuthPassword(user, newPassword)
136
+ } catch (error) {
137
+ throw new Error('Password update failed')
138
+ }
139
+ }
140
+
141
+ async deleteAccount(password: string): Promise<void> {
142
+ try {
143
+ const user = this.auth.currentUser
144
+ if (!user || !user.email) {
145
+ throw new Error('No user logged in')
146
+ }
147
+
148
+ const credential = EmailAuthProvider.credential(user.email, password)
149
+ await reauthenticateWithCredential(user, credential)
150
+ await user.delete()
151
+ } catch (error) {
152
+ throw new Error('Account deletion failed')
153
+ }
154
+ }
155
+
156
+ // State Management
157
+
158
+ getCurrentUser(): AuthUser | null {
159
+ const user = this.auth.currentUser
160
+ if (!user) return null
161
+
162
+ return {
163
+ uid: user.uid,
164
+ email: user.email,
165
+ emailVerified: user.emailVerified,
166
+ displayName: user.displayName,
167
+ photoURL: user.photoURL,
168
+ phoneNumber: user.phoneNumber,
169
+ isAnonymous: user.isAnonymous,
170
+ tenantId: user.tenantId,
171
+ providerId: user.providerId,
172
+ metadata: {
173
+ creationTime: user.metadata.creationTime ? new Date(user.metadata.creationTime).getTime() : undefined,
174
+ lastSignInTime: user.metadata.lastSignInTime ? new Date(user.metadata.lastSignInTime).getTime() : undefined,
175
+ },
176
+ }
177
+ }
178
+
179
+ onAuthStateChanged(
180
+ callback: (user: AuthUser | null) => void,
181
+ onError?: (error: Error) => void
182
+ ): () => void {
183
+ return this.auth.onAuthStateChanged(
184
+ (user) => {
185
+ callback(
186
+ user
187
+ ? {
188
+ uid: user.uid,
189
+ email: user.email,
190
+ emailVerified: user.emailVerified,
191
+ displayName: user.displayName,
192
+ photoURL: user.photoURL,
193
+ phoneNumber: user.phoneNumber,
194
+ isAnonymous: user.isAnonymous,
195
+ tenantId: user.tenantId,
196
+ providerId: user.providerId,
197
+ metadata: {
198
+ creationTime: user.metadata.creationTime ? new Date(user.metadata.creationTime).getTime() : undefined,
199
+ lastSignInTime: user.metadata.lastSignInTime ? new Date(user.metadata.lastSignInTime).getTime() : undefined,
200
+ },
201
+ }
202
+ : null
203
+ )
204
+ },
205
+ (error) => {
206
+ onError?.(error as Error)
207
+ }
208
+ )
209
+ }
210
+
211
+ /**
212
+ * Handle Firebase Auth errors
213
+ */
214
+ private handleAuthError(error: unknown): Error {
215
+ if (error instanceof Error && 'code' in error) {
216
+ const code = (error as { code: string }).code
217
+
218
+ switch (code) {
219
+ case 'auth/user-not-found':
220
+ case 'auth/wrong-password':
221
+ case 'auth/invalid-credential':
222
+ return new Error('Invalid credentials')
223
+ case 'auth/email-already-in-use':
224
+ return new Error('Email already in use')
225
+ case 'auth/weak-password':
226
+ return new Error('Password is too weak')
227
+ case 'auth/invalid-email':
228
+ return new Error('Invalid email')
229
+ case 'auth/user-disabled':
230
+ return new Error('Account disabled')
231
+ case 'auth/too-many-requests':
232
+ return new Error('Too many requests')
233
+ case 'auth/popup-closed-by-user':
234
+ return new Error('Sign in cancelled')
235
+ default:
236
+ return new Error(`Auth error: ${code}`)
237
+ }
238
+ }
239
+
240
+ return new Error('Unknown auth error')
241
+ }
242
+ }
243
+
244
+ // Export class and singleton instance
245
+ export const authService = new AuthService()
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Auth Domain Services
3
+ * Subpath: @umituz/web-firebase/auth
4
+ */
5
+
6
+ export { authService } from './auth.service'
7
+ export type { IAuthService } from '../types'